blob: dd4e08a192fe54eb393ec2d6ec80e50e1c11c2ac [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% EEEEE FFFFF FFFFF EEEEE CCCC TTTTT %
7% E F F E C T %
8% EEE FFF FFF EEE C T %
9% E F F E C T %
10% EEEEE F F EEEEE CCCC T %
11% %
12% %
13% MagickCore Image Effects Methods %
14% %
15% Software Design %
16% John Cristy %
17% October 1996 %
18% %
19% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/accelerate.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/decorate.h"
52#include "MagickCore/draw.h"
53#include "MagickCore/enhance.h"
54#include "MagickCore/exception.h"
55#include "MagickCore/exception-private.h"
56#include "MagickCore/effect.h"
57#include "MagickCore/fx.h"
58#include "MagickCore/gem.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/image-private.h"
61#include "MagickCore/list.h"
62#include "MagickCore/log.h"
63#include "MagickCore/memory_.h"
64#include "MagickCore/monitor.h"
65#include "MagickCore/monitor-private.h"
66#include "MagickCore/montage.h"
67#include "MagickCore/morphology.h"
68#include "MagickCore/paint.h"
69#include "MagickCore/pixel-accessor.h"
70#include "MagickCore/property.h"
71#include "MagickCore/quantize.h"
72#include "MagickCore/quantum.h"
73#include "MagickCore/quantum-private.h"
74#include "MagickCore/random_.h"
75#include "MagickCore/random-private.h"
76#include "MagickCore/resample.h"
77#include "MagickCore/resample-private.h"
78#include "MagickCore/resize.h"
79#include "MagickCore/resource_.h"
80#include "MagickCore/segment.h"
81#include "MagickCore/shear.h"
82#include "MagickCore/signature-private.h"
83#include "MagickCore/string_.h"
84#include "MagickCore/thread-private.h"
85#include "MagickCore/transform.h"
86#include "MagickCore/threshold.h"
cristy3ed852e2009-09-05 21:47:34 +000087
88/*
89%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90% %
91% %
92% %
93% A d a p t i v e B l u r I m a g e %
94% %
95% %
96% %
97%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98%
99% AdaptiveBlurImage() adaptively blurs the image by blurring less
100% intensely near image edges and more intensely far from edges. We blur the
101% image with a Gaussian operator of the given radius and standard deviation
102% (sigma). For reasonable results, radius should be larger than sigma. Use a
103% radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
104%
105% The format of the AdaptiveBlurImage method is:
106%
107% Image *AdaptiveBlurImage(const Image *image,const double radius,
108% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000109%
110% A description of each parameter follows:
111%
112% o image: the image.
113%
cristy3ed852e2009-09-05 21:47:34 +0000114% o radius: the radius of the Gaussian, in pixels, not counting the center
115% pixel.
116%
117% o sigma: the standard deviation of the Laplacian, in pixels.
118%
119% o exception: return any errors or warnings in this structure.
120%
121*/
122
cristyf89cb1d2011-07-07 01:24:37 +0000123MagickExport MagickBooleanType AdaptiveLevelImage(Image *image,
124 const char *levels)
125{
126 double
127 black_point,
128 gamma,
129 white_point;
130
131 GeometryInfo
132 geometry_info;
133
134 MagickBooleanType
135 status;
136
137 MagickStatusType
138 flags;
139
140 /*
141 Parse levels.
142 */
143 if (levels == (char *) NULL)
144 return(MagickFalse);
145 flags=ParseGeometry(levels,&geometry_info);
146 black_point=geometry_info.rho;
147 white_point=(double) QuantumRange;
148 if ((flags & SigmaValue) != 0)
149 white_point=geometry_info.sigma;
150 gamma=1.0;
151 if ((flags & XiValue) != 0)
152 gamma=geometry_info.xi;
153 if ((flags & PercentValue) != 0)
154 {
155 black_point*=(double) image->columns*image->rows/100.0;
156 white_point*=(double) image->columns*image->rows/100.0;
157 }
158 if ((flags & SigmaValue) == 0)
159 white_point=(double) QuantumRange-black_point;
160 if ((flags & AspectValue ) == 0)
161 status=LevelImage(image,black_point,white_point,gamma);
162 else
163 status=LevelizeImage(image,black_point,white_point,gamma);
164 return(status);
165}
166
cristyf4ad9df2011-07-08 16:49:03 +0000167MagickExport Image *AdaptiveBlurImage(const Image *image,
168 const double radius,const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000169{
170#define AdaptiveBlurImageTag "Convolve/Image"
171#define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
172
cristyc4c8d132010-01-07 01:58:38 +0000173 CacheView
174 *blur_view,
175 *edge_view,
176 *image_view;
177
cristy3ed852e2009-09-05 21:47:34 +0000178 double
cristy47e00502009-12-17 19:19:57 +0000179 **kernel,
180 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000181
182 Image
183 *blur_image,
184 *edge_image,
185 *gaussian_image;
186
cristy3ed852e2009-09-05 21:47:34 +0000187 MagickBooleanType
188 status;
189
cristybb503372010-05-27 20:51:26 +0000190 MagickOffsetType
191 progress;
192
cristy4c08aed2011-07-01 19:47:50 +0000193 PixelInfo
cristyddd82202009-11-03 20:14:50 +0000194 bias;
cristy3ed852e2009-09-05 21:47:34 +0000195
cristybb503372010-05-27 20:51:26 +0000196 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000197 i;
cristy3ed852e2009-09-05 21:47:34 +0000198
cristybb503372010-05-27 20:51:26 +0000199 size_t
cristy3ed852e2009-09-05 21:47:34 +0000200 width;
201
cristybb503372010-05-27 20:51:26 +0000202 ssize_t
203 j,
204 k,
205 u,
206 v,
207 y;
208
cristy3ed852e2009-09-05 21:47:34 +0000209 assert(image != (const Image *) NULL);
210 assert(image->signature == MagickSignature);
211 if (image->debug != MagickFalse)
212 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
213 assert(exception != (ExceptionInfo *) NULL);
214 assert(exception->signature == MagickSignature);
215 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
216 if (blur_image == (Image *) NULL)
217 return((Image *) NULL);
218 if (fabs(sigma) <= MagickEpsilon)
219 return(blur_image);
220 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
221 {
222 InheritException(exception,&blur_image->exception);
223 blur_image=DestroyImage(blur_image);
224 return((Image *) NULL);
225 }
226 /*
227 Edge detect the image brighness channel, level, blur, and level again.
228 */
229 edge_image=EdgeImage(image,radius,exception);
230 if (edge_image == (Image *) NULL)
231 {
232 blur_image=DestroyImage(blur_image);
233 return((Image *) NULL);
234 }
cristyf89cb1d2011-07-07 01:24:37 +0000235 (void) AdaptiveLevelImage(edge_image,"20%,95%");
cristy3ed852e2009-09-05 21:47:34 +0000236 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
237 if (gaussian_image != (Image *) NULL)
238 {
239 edge_image=DestroyImage(edge_image);
240 edge_image=gaussian_image;
241 }
cristyf89cb1d2011-07-07 01:24:37 +0000242 (void) AdaptiveLevelImage(edge_image,"10%,95%");
cristy3ed852e2009-09-05 21:47:34 +0000243 /*
244 Create a set of kernels from maximum (radius,sigma) to minimum.
245 */
246 width=GetOptimalKernelWidth2D(radius,sigma);
247 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
248 if (kernel == (double **) NULL)
249 {
250 edge_image=DestroyImage(edge_image);
251 blur_image=DestroyImage(blur_image);
252 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
253 }
254 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
cristybb503372010-05-27 20:51:26 +0000255 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000256 {
257 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
258 sizeof(**kernel));
259 if (kernel[i] == (double *) NULL)
260 break;
cristy47e00502009-12-17 19:19:57 +0000261 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000262 j=(ssize_t) (width-i)/2;
cristy47e00502009-12-17 19:19:57 +0000263 k=0;
264 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +0000265 {
cristy47e00502009-12-17 19:19:57 +0000266 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +0000267 {
cristy4205a3c2010-09-12 20:19:59 +0000268 kernel[i][k]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
269 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +0000270 normalize+=kernel[i][k];
271 k++;
cristy3ed852e2009-09-05 21:47:34 +0000272 }
273 }
cristy3ed852e2009-09-05 21:47:34 +0000274 if (fabs(normalize) <= MagickEpsilon)
275 normalize=1.0;
276 normalize=1.0/normalize;
cristy47e00502009-12-17 19:19:57 +0000277 for (k=0; k < (j*j); k++)
278 kernel[i][k]=normalize*kernel[i][k];
cristy3ed852e2009-09-05 21:47:34 +0000279 }
cristybb503372010-05-27 20:51:26 +0000280 if (i < (ssize_t) width)
cristy3ed852e2009-09-05 21:47:34 +0000281 {
282 for (i-=2; i >= 0; i-=2)
283 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
284 kernel=(double **) RelinquishMagickMemory(kernel);
285 edge_image=DestroyImage(edge_image);
286 blur_image=DestroyImage(blur_image);
287 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
288 }
289 /*
290 Adaptively blur image.
291 */
292 status=MagickTrue;
293 progress=0;
cristy4c08aed2011-07-01 19:47:50 +0000294 GetPixelInfo(image,&bias);
295 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +0000296 image_view=AcquireCacheView(image);
297 edge_view=AcquireCacheView(edge_image);
298 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +0000299#if defined(MAGICKCORE_OPENMP_SUPPORT)
300 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000301#endif
cristybb503372010-05-27 20:51:26 +0000302 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000303 {
cristy4c08aed2011-07-01 19:47:50 +0000304 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000305 *restrict p,
306 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +0000307
cristy4c08aed2011-07-01 19:47:50 +0000308 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000309 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000310
cristy117ff172010-08-15 21:35:32 +0000311 register ssize_t
312 x;
313
cristy3ed852e2009-09-05 21:47:34 +0000314 if (status == MagickFalse)
315 continue;
316 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
317 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
318 exception);
cristy4c08aed2011-07-01 19:47:50 +0000319 if ((r == (const Quantum *) NULL) || (q == (const Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000320 {
321 status=MagickFalse;
322 continue;
323 }
cristybb503372010-05-27 20:51:26 +0000324 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000325 {
cristy4c08aed2011-07-01 19:47:50 +0000326 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000327 pixel;
328
329 MagickRealType
330 alpha,
331 gamma;
332
333 register const double
cristyc47d1f82009-11-26 01:44:43 +0000334 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +0000335
cristybb503372010-05-27 20:51:26 +0000336 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000337 i,
338 u,
339 v;
340
341 gamma=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000342 i=(ssize_t) ceil((double) width*QuantumScale*
343 GetPixelIntensity(edge_image,r)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000344 if (i < 0)
345 i=0;
346 else
cristybb503372010-05-27 20:51:26 +0000347 if (i > (ssize_t) width)
348 i=(ssize_t) width;
cristy3ed852e2009-09-05 21:47:34 +0000349 if ((i & 0x01) != 0)
350 i--;
cristya21afde2010-07-02 00:45:40 +0000351 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-i)/2L),y-
352 (ssize_t) ((width-i)/2L),width-i,width-i,exception);
cristy4c08aed2011-07-01 19:47:50 +0000353 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000354 break;
cristyddd82202009-11-03 20:14:50 +0000355 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +0000356 k=kernel[i];
cristybb503372010-05-27 20:51:26 +0000357 for (v=0; v < (ssize_t) (width-i); v++)
cristy3ed852e2009-09-05 21:47:34 +0000358 {
cristybb503372010-05-27 20:51:26 +0000359 for (u=0; u < (ssize_t) (width-i); u++)
cristy3ed852e2009-09-05 21:47:34 +0000360 {
361 alpha=1.0;
cristyed231572011-07-14 02:18:59 +0000362 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000363 (image->matte != MagickFalse))
cristy4c08aed2011-07-01 19:47:50 +0000364 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,p));
cristyed231572011-07-14 02:18:59 +0000365 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000366 pixel.red+=(*k)*alpha*GetPixelRed(image,p);
cristyed231572011-07-14 02:18:59 +0000367 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000368 pixel.green+=(*k)*alpha*GetPixelGreen(image,p);
cristyed231572011-07-14 02:18:59 +0000369 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000370 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p);
cristyed231572011-07-14 02:18:59 +0000371 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000372 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +0000373 pixel.black+=(*k)*alpha*GetPixelBlack(image,p);
cristyed231572011-07-14 02:18:59 +0000374 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000375 pixel.alpha+=(*k)*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000376 gamma+=(*k)*alpha;
377 k++;
cristyed231572011-07-14 02:18:59 +0000378 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000379 }
380 }
381 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +0000382 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000383 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +0000384 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000385 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +0000386 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000387 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000388 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000389 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +0000390 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristyed231572011-07-14 02:18:59 +0000391 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000392 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +0000393 q+=GetPixelChannels(blur_image);
394 r+=GetPixelChannels(edge_image);
cristy3ed852e2009-09-05 21:47:34 +0000395 }
396 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
397 status=MagickFalse;
398 if (image->progress_monitor != (MagickProgressMonitor) NULL)
399 {
400 MagickBooleanType
401 proceed;
402
cristyb5d5f722009-11-04 03:03:49 +0000403#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000404 #pragma omp critical (MagickCore_AdaptiveBlurImage)
cristy3ed852e2009-09-05 21:47:34 +0000405#endif
406 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
407 image->rows);
408 if (proceed == MagickFalse)
409 status=MagickFalse;
410 }
411 }
412 blur_image->type=image->type;
413 blur_view=DestroyCacheView(blur_view);
414 edge_view=DestroyCacheView(edge_view);
415 image_view=DestroyCacheView(image_view);
416 edge_image=DestroyImage(edge_image);
cristybb503372010-05-27 20:51:26 +0000417 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000418 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
419 kernel=(double **) RelinquishMagickMemory(kernel);
420 if (status == MagickFalse)
421 blur_image=DestroyImage(blur_image);
422 return(blur_image);
423}
424
425/*
426%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
427% %
428% %
429% %
430% A d a p t i v e S h a r p e n I m a g e %
431% %
432% %
433% %
434%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
435%
436% AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
437% intensely near image edges and less intensely far from edges. We sharpen the
438% image with a Gaussian operator of the given radius and standard deviation
439% (sigma). For reasonable results, radius should be larger than sigma. Use a
440% radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
441%
442% The format of the AdaptiveSharpenImage method is:
443%
444% Image *AdaptiveSharpenImage(const Image *image,const double radius,
445% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000446%
447% A description of each parameter follows:
448%
449% o image: the image.
450%
cristy3ed852e2009-09-05 21:47:34 +0000451% o radius: the radius of the Gaussian, in pixels, not counting the center
452% pixel.
453%
454% o sigma: the standard deviation of the Laplacian, in pixels.
455%
456% o exception: return any errors or warnings in this structure.
457%
458*/
cristy3ed852e2009-09-05 21:47:34 +0000459MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
460 const double sigma,ExceptionInfo *exception)
461{
cristy3ed852e2009-09-05 21:47:34 +0000462#define AdaptiveSharpenImageTag "Convolve/Image"
463#define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
464
cristyc4c8d132010-01-07 01:58:38 +0000465 CacheView
466 *sharp_view,
467 *edge_view,
468 *image_view;
469
cristy3ed852e2009-09-05 21:47:34 +0000470 double
cristy47e00502009-12-17 19:19:57 +0000471 **kernel,
472 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000473
474 Image
475 *sharp_image,
476 *edge_image,
477 *gaussian_image;
478
cristy3ed852e2009-09-05 21:47:34 +0000479 MagickBooleanType
480 status;
481
cristybb503372010-05-27 20:51:26 +0000482 MagickOffsetType
483 progress;
484
cristy4c08aed2011-07-01 19:47:50 +0000485 PixelInfo
cristyddd82202009-11-03 20:14:50 +0000486 bias;
cristy3ed852e2009-09-05 21:47:34 +0000487
cristybb503372010-05-27 20:51:26 +0000488 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000489 i;
cristy3ed852e2009-09-05 21:47:34 +0000490
cristybb503372010-05-27 20:51:26 +0000491 size_t
cristy3ed852e2009-09-05 21:47:34 +0000492 width;
493
cristybb503372010-05-27 20:51:26 +0000494 ssize_t
495 j,
496 k,
497 u,
498 v,
499 y;
500
cristy3ed852e2009-09-05 21:47:34 +0000501 assert(image != (const Image *) NULL);
502 assert(image->signature == MagickSignature);
503 if (image->debug != MagickFalse)
504 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
505 assert(exception != (ExceptionInfo *) NULL);
506 assert(exception->signature == MagickSignature);
507 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
508 if (sharp_image == (Image *) NULL)
509 return((Image *) NULL);
510 if (fabs(sigma) <= MagickEpsilon)
511 return(sharp_image);
512 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
513 {
514 InheritException(exception,&sharp_image->exception);
515 sharp_image=DestroyImage(sharp_image);
516 return((Image *) NULL);
517 }
518 /*
519 Edge detect the image brighness channel, level, sharp, and level again.
520 */
521 edge_image=EdgeImage(image,radius,exception);
522 if (edge_image == (Image *) NULL)
523 {
524 sharp_image=DestroyImage(sharp_image);
525 return((Image *) NULL);
526 }
cristyf89cb1d2011-07-07 01:24:37 +0000527 (void) AdaptiveLevelImage(edge_image,"20%,95%");
cristy3ed852e2009-09-05 21:47:34 +0000528 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
529 if (gaussian_image != (Image *) NULL)
530 {
531 edge_image=DestroyImage(edge_image);
532 edge_image=gaussian_image;
533 }
cristyf89cb1d2011-07-07 01:24:37 +0000534 (void) AdaptiveLevelImage(edge_image,"10%,95%");
cristy3ed852e2009-09-05 21:47:34 +0000535 /*
536 Create a set of kernels from maximum (radius,sigma) to minimum.
537 */
538 width=GetOptimalKernelWidth2D(radius,sigma);
539 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
540 if (kernel == (double **) NULL)
541 {
542 edge_image=DestroyImage(edge_image);
543 sharp_image=DestroyImage(sharp_image);
544 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
545 }
546 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
cristybb503372010-05-27 20:51:26 +0000547 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000548 {
549 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
550 sizeof(**kernel));
551 if (kernel[i] == (double *) NULL)
552 break;
cristy47e00502009-12-17 19:19:57 +0000553 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000554 j=(ssize_t) (width-i)/2;
cristy47e00502009-12-17 19:19:57 +0000555 k=0;
556 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +0000557 {
cristy47e00502009-12-17 19:19:57 +0000558 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +0000559 {
cristy4205a3c2010-09-12 20:19:59 +0000560 kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
561 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +0000562 normalize+=kernel[i][k];
563 k++;
cristy3ed852e2009-09-05 21:47:34 +0000564 }
565 }
cristy3ed852e2009-09-05 21:47:34 +0000566 if (fabs(normalize) <= MagickEpsilon)
567 normalize=1.0;
568 normalize=1.0/normalize;
cristy47e00502009-12-17 19:19:57 +0000569 for (k=0; k < (j*j); k++)
570 kernel[i][k]=normalize*kernel[i][k];
cristy3ed852e2009-09-05 21:47:34 +0000571 }
cristybb503372010-05-27 20:51:26 +0000572 if (i < (ssize_t) width)
cristy3ed852e2009-09-05 21:47:34 +0000573 {
574 for (i-=2; i >= 0; i-=2)
575 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
576 kernel=(double **) RelinquishMagickMemory(kernel);
577 edge_image=DestroyImage(edge_image);
578 sharp_image=DestroyImage(sharp_image);
579 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
580 }
581 /*
582 Adaptively sharpen image.
583 */
584 status=MagickTrue;
585 progress=0;
cristy4c08aed2011-07-01 19:47:50 +0000586 GetPixelInfo(image,&bias);
587 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +0000588 image_view=AcquireCacheView(image);
589 edge_view=AcquireCacheView(edge_image);
590 sharp_view=AcquireCacheView(sharp_image);
cristyb5d5f722009-11-04 03:03:49 +0000591#if defined(MAGICKCORE_OPENMP_SUPPORT)
592 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000593#endif
cristybb503372010-05-27 20:51:26 +0000594 for (y=0; y < (ssize_t) sharp_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000595 {
cristy4c08aed2011-07-01 19:47:50 +0000596 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000597 *restrict p,
598 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +0000599
cristy4c08aed2011-07-01 19:47:50 +0000600 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000601 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000602
cristy117ff172010-08-15 21:35:32 +0000603 register ssize_t
604 x;
605
cristy3ed852e2009-09-05 21:47:34 +0000606 if (status == MagickFalse)
607 continue;
608 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
609 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
610 exception);
cristy4c08aed2011-07-01 19:47:50 +0000611 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000612 {
613 status=MagickFalse;
614 continue;
615 }
cristybb503372010-05-27 20:51:26 +0000616 for (x=0; x < (ssize_t) sharp_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000617 {
cristy4c08aed2011-07-01 19:47:50 +0000618 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000619 pixel;
620
621 MagickRealType
622 alpha,
623 gamma;
624
625 register const double
cristyc47d1f82009-11-26 01:44:43 +0000626 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +0000627
cristybb503372010-05-27 20:51:26 +0000628 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000629 i,
630 u,
631 v;
632
633 gamma=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000634 i=(ssize_t) ceil((double) width*QuantumScale*
635 GetPixelIntensity(edge_image,r)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000636 if (i < 0)
637 i=0;
638 else
cristybb503372010-05-27 20:51:26 +0000639 if (i > (ssize_t) width)
640 i=(ssize_t) width;
cristy3ed852e2009-09-05 21:47:34 +0000641 if ((i & 0x01) != 0)
642 i--;
cristy117ff172010-08-15 21:35:32 +0000643 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-i)/2L),y-
644 (ssize_t) ((width-i)/2L),width-i,width-i,exception);
cristy4c08aed2011-07-01 19:47:50 +0000645 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000646 break;
cristy3ed852e2009-09-05 21:47:34 +0000647 k=kernel[i];
cristyddd82202009-11-03 20:14:50 +0000648 pixel=bias;
cristybb503372010-05-27 20:51:26 +0000649 for (v=0; v < (ssize_t) (width-i); v++)
cristy3ed852e2009-09-05 21:47:34 +0000650 {
cristybb503372010-05-27 20:51:26 +0000651 for (u=0; u < (ssize_t) (width-i); u++)
cristy3ed852e2009-09-05 21:47:34 +0000652 {
653 alpha=1.0;
cristyed231572011-07-14 02:18:59 +0000654 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000655 (image->matte != MagickFalse))
cristy4c08aed2011-07-01 19:47:50 +0000656 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,p));
cristyed231572011-07-14 02:18:59 +0000657 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000658 pixel.red+=(*k)*alpha*GetPixelRed(image,p);
cristyed231572011-07-14 02:18:59 +0000659 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000660 pixel.green+=(*k)*alpha*GetPixelGreen(image,p);
cristyed231572011-07-14 02:18:59 +0000661 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000662 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p);
cristyed231572011-07-14 02:18:59 +0000663 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000664 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +0000665 pixel.black+=(*k)*alpha*GetPixelBlack(image,p);
cristyed231572011-07-14 02:18:59 +0000666 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000667 pixel.alpha+=(*k)*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000668 gamma+=(*k)*alpha;
669 k++;
cristyed231572011-07-14 02:18:59 +0000670 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000671 }
672 }
673 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +0000674 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000675 SetPixelRed(sharp_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +0000676 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000677 SetPixelGreen(sharp_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +0000678 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000679 SetPixelBlue(sharp_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000680 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000681 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +0000682 SetPixelBlack(sharp_image,ClampToQuantum(gamma*pixel.black),q);
cristyed231572011-07-14 02:18:59 +0000683 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000684 SetPixelAlpha(sharp_image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +0000685 q+=GetPixelChannels(sharp_image);
686 r+=GetPixelChannels(edge_image);
cristy3ed852e2009-09-05 21:47:34 +0000687 }
688 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
689 status=MagickFalse;
690 if (image->progress_monitor != (MagickProgressMonitor) NULL)
691 {
692 MagickBooleanType
693 proceed;
694
cristyb5d5f722009-11-04 03:03:49 +0000695#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000696 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
cristy3ed852e2009-09-05 21:47:34 +0000697#endif
698 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
699 image->rows);
700 if (proceed == MagickFalse)
701 status=MagickFalse;
702 }
703 }
704 sharp_image->type=image->type;
705 sharp_view=DestroyCacheView(sharp_view);
706 edge_view=DestroyCacheView(edge_view);
707 image_view=DestroyCacheView(image_view);
708 edge_image=DestroyImage(edge_image);
cristybb503372010-05-27 20:51:26 +0000709 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000710 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
711 kernel=(double **) RelinquishMagickMemory(kernel);
712 if (status == MagickFalse)
713 sharp_image=DestroyImage(sharp_image);
714 return(sharp_image);
715}
716
717/*
718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719% %
720% %
721% %
722% B l u r I m a g e %
723% %
724% %
725% %
726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727%
728% BlurImage() blurs an image. We convolve the image with a Gaussian operator
729% of the given radius and standard deviation (sigma). For reasonable results,
730% the radius should be larger than sigma. Use a radius of 0 and BlurImage()
731% selects a suitable radius for you.
732%
733% BlurImage() differs from GaussianBlurImage() in that it uses a separable
734% kernel which is faster but mathematically equivalent to the non-separable
735% kernel.
736%
737% The format of the BlurImage method is:
738%
739% Image *BlurImage(const Image *image,const double radius,
740% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000741%
742% A description of each parameter follows:
743%
744% o image: the image.
745%
cristy3ed852e2009-09-05 21:47:34 +0000746% o radius: the radius of the Gaussian, in pixels, not counting the center
747% pixel.
748%
749% o sigma: the standard deviation of the Gaussian, in pixels.
750%
751% o exception: return any errors or warnings in this structure.
752%
753*/
754
cristybb503372010-05-27 20:51:26 +0000755static double *GetBlurKernel(const size_t width,const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000756{
cristy3ed852e2009-09-05 21:47:34 +0000757 double
cristy47e00502009-12-17 19:19:57 +0000758 *kernel,
759 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000760
cristy117ff172010-08-15 21:35:32 +0000761 register ssize_t
762 i;
763
cristybb503372010-05-27 20:51:26 +0000764 ssize_t
cristy47e00502009-12-17 19:19:57 +0000765 j,
766 k;
cristy3ed852e2009-09-05 21:47:34 +0000767
cristy3ed852e2009-09-05 21:47:34 +0000768 /*
769 Generate a 1-D convolution kernel.
770 */
771 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
772 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
773 if (kernel == (double *) NULL)
774 return(0);
cristy3ed852e2009-09-05 21:47:34 +0000775 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000776 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000777 i=0;
778 for (k=(-j); k <= j; k++)
779 {
cristy4205a3c2010-09-12 20:19:59 +0000780 kernel[i]=(double) (exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
781 (MagickSQ2PI*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +0000782 normalize+=kernel[i];
cristy47e00502009-12-17 19:19:57 +0000783 i++;
784 }
cristybb503372010-05-27 20:51:26 +0000785 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000786 kernel[i]/=normalize;
787 return(kernel);
788}
789
cristyf4ad9df2011-07-08 16:49:03 +0000790MagickExport Image *BlurImage(const Image *image,const double radius,
791 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000792{
793#define BlurImageTag "Blur/Image"
794
cristyc4c8d132010-01-07 01:58:38 +0000795 CacheView
796 *blur_view,
797 *image_view;
798
cristy3ed852e2009-09-05 21:47:34 +0000799 double
800 *kernel;
801
802 Image
803 *blur_image;
804
cristy3ed852e2009-09-05 21:47:34 +0000805 MagickBooleanType
806 status;
807
cristybb503372010-05-27 20:51:26 +0000808 MagickOffsetType
809 progress;
810
cristy4c08aed2011-07-01 19:47:50 +0000811 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000812 bias;
813
cristybb503372010-05-27 20:51:26 +0000814 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000815 i;
816
cristybb503372010-05-27 20:51:26 +0000817 size_t
cristy3ed852e2009-09-05 21:47:34 +0000818 width;
819
cristybb503372010-05-27 20:51:26 +0000820 ssize_t
821 x,
822 y;
823
cristy3ed852e2009-09-05 21:47:34 +0000824 /*
825 Initialize blur image attributes.
826 */
827 assert(image != (Image *) NULL);
828 assert(image->signature == MagickSignature);
829 if (image->debug != MagickFalse)
830 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
831 assert(exception != (ExceptionInfo *) NULL);
832 assert(exception->signature == MagickSignature);
833 blur_image=CloneImage(image,0,0,MagickTrue,exception);
834 if (blur_image == (Image *) NULL)
835 return((Image *) NULL);
836 if (fabs(sigma) <= MagickEpsilon)
837 return(blur_image);
838 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
839 {
840 InheritException(exception,&blur_image->exception);
841 blur_image=DestroyImage(blur_image);
842 return((Image *) NULL);
843 }
844 width=GetOptimalKernelWidth1D(radius,sigma);
845 kernel=GetBlurKernel(width,sigma);
846 if (kernel == (double *) NULL)
847 {
848 blur_image=DestroyImage(blur_image);
849 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
850 }
851 if (image->debug != MagickFalse)
852 {
853 char
854 format[MaxTextExtent],
855 *message;
856
857 register const double
858 *k;
859
860 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000861 " BlurImage with %.20g kernel:",(double) width);
cristy3ed852e2009-09-05 21:47:34 +0000862 message=AcquireString("");
863 k=kernel;
cristybb503372010-05-27 20:51:26 +0000864 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000865 {
866 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000867 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) i);
cristy3ed852e2009-09-05 21:47:34 +0000868 (void) ConcatenateString(&message,format);
cristyb51dff52011-05-19 16:55:47 +0000869 (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
cristy3ed852e2009-09-05 21:47:34 +0000870 (void) ConcatenateString(&message,format);
871 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
872 }
873 message=DestroyString(message);
874 }
875 /*
876 Blur rows.
877 */
878 status=MagickTrue;
879 progress=0;
cristy4c08aed2011-07-01 19:47:50 +0000880 GetPixelInfo(image,&bias);
881 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +0000882 image_view=AcquireCacheView(image);
883 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +0000884#if defined(MAGICKCORE_OPENMP_SUPPORT)
885 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000886#endif
cristybb503372010-05-27 20:51:26 +0000887 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000888 {
cristy4c08aed2011-07-01 19:47:50 +0000889 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000890 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000891
cristy4c08aed2011-07-01 19:47:50 +0000892 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000893 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000894
cristy117ff172010-08-15 21:35:32 +0000895 register ssize_t
896 x;
897
cristy3ed852e2009-09-05 21:47:34 +0000898 if (status == MagickFalse)
899 continue;
cristy117ff172010-08-15 21:35:32 +0000900 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y,
901 image->columns+width,1,exception);
cristy3ed852e2009-09-05 21:47:34 +0000902 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
903 exception);
cristy4c08aed2011-07-01 19:47:50 +0000904 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000905 {
906 status=MagickFalse;
907 continue;
908 }
cristybb503372010-05-27 20:51:26 +0000909 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000910 {
cristy4c08aed2011-07-01 19:47:50 +0000911 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000912 pixel;
913
914 register const double
cristyc47d1f82009-11-26 01:44:43 +0000915 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +0000916
cristy4c08aed2011-07-01 19:47:50 +0000917 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000918 *restrict kernel_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000919
cristybb503372010-05-27 20:51:26 +0000920 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000921 i;
922
cristyddd82202009-11-03 20:14:50 +0000923 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +0000924 k=kernel;
925 kernel_pixels=p;
cristyed231572011-07-14 02:18:59 +0000926 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) ||
cristyf4ad9df2011-07-08 16:49:03 +0000927 (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +0000928 {
cristybb503372010-05-27 20:51:26 +0000929 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000930 {
cristy4c08aed2011-07-01 19:47:50 +0000931 pixel.red+=(*k)*GetPixelRed(image,kernel_pixels);
932 pixel.green+=(*k)*GetPixelGreen(image,kernel_pixels);
933 pixel.blue+=(*k)*GetPixelBlue(image,kernel_pixels);
934 if (image->colorspace == CMYKColorspace)
935 pixel.black+=(*k)*GetPixelBlack(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000936 k++;
cristyed231572011-07-14 02:18:59 +0000937 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000938 }
cristyed231572011-07-14 02:18:59 +0000939 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000940 SetPixelRed(blur_image,ClampToQuantum(pixel.red),q);
cristyed231572011-07-14 02:18:59 +0000941 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000942 SetPixelGreen(blur_image,ClampToQuantum(pixel.green),q);
cristyed231572011-07-14 02:18:59 +0000943 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000944 SetPixelBlue(blur_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000945 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000946 (blur_image->colorspace == CMYKColorspace))
947 SetPixelBlack(blur_image,ClampToQuantum(pixel.black),q);
cristyed231572011-07-14 02:18:59 +0000948 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +0000949 {
950 k=kernel;
951 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +0000952 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000953 {
cristy4c08aed2011-07-01 19:47:50 +0000954 pixel.alpha+=(*k)*GetPixelAlpha(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000955 k++;
cristyed231572011-07-14 02:18:59 +0000956 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000957 }
cristy4c08aed2011-07-01 19:47:50 +0000958 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +0000959 }
960 }
961 else
962 {
963 MagickRealType
964 alpha,
965 gamma;
966
967 gamma=0.0;
cristybb503372010-05-27 20:51:26 +0000968 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000969 {
cristy4c08aed2011-07-01 19:47:50 +0000970 alpha=(MagickRealType) (QuantumScale*
971 GetPixelAlpha(image,kernel_pixels));
972 pixel.red+=(*k)*alpha*GetPixelRed(image,kernel_pixels);
973 pixel.green+=(*k)*alpha*GetPixelGreen(image,kernel_pixels);
974 pixel.blue+=(*k)*alpha*GetPixelBlue(image,kernel_pixels);
975 if (image->colorspace == CMYKColorspace)
976 pixel.black+=(*k)*alpha*GetPixelBlack(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000977 gamma+=(*k)*alpha;
978 k++;
cristyed231572011-07-14 02:18:59 +0000979 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000980 }
981 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +0000982 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000983 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +0000984 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000985 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +0000986 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000987 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000988 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000989 (blur_image->colorspace == CMYKColorspace))
990 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristyed231572011-07-14 02:18:59 +0000991 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +0000992 {
993 k=kernel;
994 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +0000995 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000996 {
cristy4c08aed2011-07-01 19:47:50 +0000997 pixel.alpha+=(*k)*GetPixelAlpha(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000998 k++;
cristyed231572011-07-14 02:18:59 +0000999 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001000 }
cristy4c08aed2011-07-01 19:47:50 +00001001 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00001002 }
1003 }
cristyed231572011-07-14 02:18:59 +00001004 p+=GetPixelChannels(image);
1005 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001006 }
1007 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1008 status=MagickFalse;
1009 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1010 {
1011 MagickBooleanType
1012 proceed;
1013
cristyb5d5f722009-11-04 03:03:49 +00001014#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001015 #pragma omp critical (MagickCore_BlurImage)
cristy3ed852e2009-09-05 21:47:34 +00001016#endif
1017 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1018 blur_image->columns);
1019 if (proceed == MagickFalse)
1020 status=MagickFalse;
1021 }
1022 }
1023 blur_view=DestroyCacheView(blur_view);
1024 image_view=DestroyCacheView(image_view);
1025 /*
1026 Blur columns.
1027 */
1028 image_view=AcquireCacheView(blur_image);
1029 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00001030#if defined(MAGICKCORE_OPENMP_SUPPORT)
1031 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001032#endif
cristybb503372010-05-27 20:51:26 +00001033 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001034 {
cristy4c08aed2011-07-01 19:47:50 +00001035 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001036 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001037
cristy4c08aed2011-07-01 19:47:50 +00001038 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001039 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001040
cristy117ff172010-08-15 21:35:32 +00001041 register ssize_t
1042 y;
1043
cristy3ed852e2009-09-05 21:47:34 +00001044 if (status == MagickFalse)
1045 continue;
cristy117ff172010-08-15 21:35:32 +00001046 p=GetCacheViewVirtualPixels(image_view,x,-((ssize_t) width/2L),1,
1047 image->rows+width,exception);
cristy3ed852e2009-09-05 21:47:34 +00001048 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
cristy4c08aed2011-07-01 19:47:50 +00001049 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001050 {
1051 status=MagickFalse;
1052 continue;
1053 }
cristybb503372010-05-27 20:51:26 +00001054 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001055 {
cristy4c08aed2011-07-01 19:47:50 +00001056 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001057 pixel;
1058
1059 register const double
cristyc47d1f82009-11-26 01:44:43 +00001060 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00001061
cristy4c08aed2011-07-01 19:47:50 +00001062 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001063 *restrict kernel_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001064
cristybb503372010-05-27 20:51:26 +00001065 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001066 i;
1067
cristyddd82202009-11-03 20:14:50 +00001068 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +00001069 k=kernel;
1070 kernel_pixels=p;
cristyed231572011-07-14 02:18:59 +00001071 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) ||
cristyf4ad9df2011-07-08 16:49:03 +00001072 (blur_image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001073 {
cristybb503372010-05-27 20:51:26 +00001074 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001075 {
cristy4c08aed2011-07-01 19:47:50 +00001076 pixel.red+=(*k)*GetPixelRed(blur_image,kernel_pixels);
1077 pixel.green+=(*k)*GetPixelGreen(blur_image,kernel_pixels);
1078 pixel.blue+=(*k)*GetPixelBlue(blur_image,kernel_pixels);
1079 if (blur_image->colorspace == CMYKColorspace)
1080 pixel.black+=(*k)*GetPixelBlack(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001081 k++;
cristyed231572011-07-14 02:18:59 +00001082 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001083 }
cristyed231572011-07-14 02:18:59 +00001084 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001085 SetPixelRed(blur_image,ClampToQuantum(pixel.red),q);
cristyed231572011-07-14 02:18:59 +00001086 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001087 SetPixelGreen(blur_image,ClampToQuantum(pixel.green),q);
cristyed231572011-07-14 02:18:59 +00001088 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001089 SetPixelBlue(blur_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00001090 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001091 (blur_image->colorspace == CMYKColorspace))
1092 SetPixelBlack(blur_image,ClampToQuantum(pixel.black),q);
cristyed231572011-07-14 02:18:59 +00001093 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001094 {
1095 k=kernel;
1096 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +00001097 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001098 {
cristy4c08aed2011-07-01 19:47:50 +00001099 pixel.alpha+=(*k)*GetPixelAlpha(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001100 k++;
cristyed231572011-07-14 02:18:59 +00001101 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001102 }
cristy4c08aed2011-07-01 19:47:50 +00001103 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00001104 }
1105 }
1106 else
1107 {
1108 MagickRealType
1109 alpha,
1110 gamma;
1111
1112 gamma=0.0;
cristybb503372010-05-27 20:51:26 +00001113 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001114 {
cristy46f08202010-01-10 04:04:21 +00001115 alpha=(MagickRealType) (QuantumScale*
cristy4c08aed2011-07-01 19:47:50 +00001116 GetPixelAlpha(blur_image,kernel_pixels));
1117 pixel.red+=(*k)*alpha*GetPixelRed(blur_image,kernel_pixels);
1118 pixel.green+=(*k)*alpha*GetPixelGreen(blur_image,kernel_pixels);
1119 pixel.blue+=(*k)*alpha*GetPixelBlue(blur_image,kernel_pixels);
1120 if (blur_image->colorspace == CMYKColorspace)
1121 pixel.black+=(*k)*alpha*GetPixelBlack(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001122 gamma+=(*k)*alpha;
1123 k++;
cristyed231572011-07-14 02:18:59 +00001124 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001125 }
1126 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00001127 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001128 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00001129 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001130 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00001131 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001132 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00001133 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001134 (blur_image->colorspace == CMYKColorspace))
1135 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristyed231572011-07-14 02:18:59 +00001136 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001137 {
1138 k=kernel;
1139 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +00001140 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001141 {
cristy4c08aed2011-07-01 19:47:50 +00001142 pixel.alpha+=(*k)*GetPixelAlpha(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001143 k++;
cristyed231572011-07-14 02:18:59 +00001144 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001145 }
cristy4c08aed2011-07-01 19:47:50 +00001146 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00001147 }
1148 }
cristyed231572011-07-14 02:18:59 +00001149 p+=GetPixelChannels(blur_image);
1150 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001151 }
1152 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1153 status=MagickFalse;
cristy4c08aed2011-07-01 19:47:50 +00001154 if (blur_image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001155 {
1156 MagickBooleanType
1157 proceed;
1158
cristyb5d5f722009-11-04 03:03:49 +00001159#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001160 #pragma omp critical (MagickCore_BlurImage)
cristy3ed852e2009-09-05 21:47:34 +00001161#endif
cristy4c08aed2011-07-01 19:47:50 +00001162 proceed=SetImageProgress(blur_image,BlurImageTag,progress++,
1163 blur_image->rows+blur_image->columns);
cristy3ed852e2009-09-05 21:47:34 +00001164 if (proceed == MagickFalse)
1165 status=MagickFalse;
1166 }
1167 }
1168 blur_view=DestroyCacheView(blur_view);
1169 image_view=DestroyCacheView(image_view);
1170 kernel=(double *) RelinquishMagickMemory(kernel);
1171 if (status == MagickFalse)
1172 blur_image=DestroyImage(blur_image);
1173 blur_image->type=image->type;
1174 return(blur_image);
1175}
1176
1177/*
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179% %
1180% %
1181% %
cristyfccdab92009-11-30 16:43:57 +00001182% C o n v o l v e I m a g e %
1183% %
1184% %
1185% %
1186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1187%
1188% ConvolveImage() applies a custom convolution kernel to the image.
1189%
1190% The format of the ConvolveImage method is:
1191%
cristy5e6be1e2011-07-16 01:23:39 +00001192% Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
1193% ExceptionInfo *exception)
1194%
cristyfccdab92009-11-30 16:43:57 +00001195% A description of each parameter follows:
1196%
1197% o image: the image.
1198%
cristy5e6be1e2011-07-16 01:23:39 +00001199% o kernel: the filtering kernel.
cristyfccdab92009-11-30 16:43:57 +00001200%
1201% o exception: return any errors or warnings in this structure.
1202%
1203*/
cristy5e6be1e2011-07-16 01:23:39 +00001204MagickExport Image *ConvolveImage(const Image *image,
1205 const KernelInfo *kernel_info,ExceptionInfo *exception)
cristyfccdab92009-11-30 16:43:57 +00001206{
cristyfccdab92009-11-30 16:43:57 +00001207#define ConvolveImageTag "Convolve/Image"
1208
cristyc4c8d132010-01-07 01:58:38 +00001209 CacheView
1210 *convolve_view,
cristy105ba3c2011-07-18 02:28:38 +00001211 *image_view;
cristyc4c8d132010-01-07 01:58:38 +00001212
cristyfccdab92009-11-30 16:43:57 +00001213 Image
1214 *convolve_image;
1215
cristyfccdab92009-11-30 16:43:57 +00001216 MagickBooleanType
1217 status;
1218
cristybb503372010-05-27 20:51:26 +00001219 MagickOffsetType
1220 progress;
1221
cristybb503372010-05-27 20:51:26 +00001222 ssize_t
1223 y;
1224
cristyfccdab92009-11-30 16:43:57 +00001225 /*
1226 Initialize convolve image attributes.
1227 */
1228 assert(image != (Image *) NULL);
1229 assert(image->signature == MagickSignature);
1230 if (image->debug != MagickFalse)
1231 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1232 assert(exception != (ExceptionInfo *) NULL);
1233 assert(exception->signature == MagickSignature);
cristy5e6be1e2011-07-16 01:23:39 +00001234 if ((kernel_info->width % 2) == 0)
cristyfccdab92009-11-30 16:43:57 +00001235 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
cristy08429172011-07-14 17:18:16 +00001236 convolve_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1237 exception);
cristyfccdab92009-11-30 16:43:57 +00001238 if (convolve_image == (Image *) NULL)
1239 return((Image *) NULL);
1240 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
1241 {
1242 InheritException(exception,&convolve_image->exception);
1243 convolve_image=DestroyImage(convolve_image);
1244 return((Image *) NULL);
1245 }
1246 if (image->debug != MagickFalse)
1247 {
1248 char
1249 format[MaxTextExtent],
1250 *message;
1251
cristy117ff172010-08-15 21:35:32 +00001252 register const double
1253 *k;
1254
cristy4e154852011-07-14 13:28:53 +00001255 register ssize_t
1256 u;
1257
cristybb503372010-05-27 20:51:26 +00001258 ssize_t
cristyfccdab92009-11-30 16:43:57 +00001259 v;
1260
cristyfccdab92009-11-30 16:43:57 +00001261 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristy5e6be1e2011-07-16 01:23:39 +00001262 " ConvolveImage with %.20gx%.20g kernel:",(double) kernel_info->width,
1263 (double) kernel_info->height);
cristyfccdab92009-11-30 16:43:57 +00001264 message=AcquireString("");
cristy5e6be1e2011-07-16 01:23:39 +00001265 k=kernel_info->values;
1266 for (v=0; v < (ssize_t) kernel_info->width; v++)
cristyfccdab92009-11-30 16:43:57 +00001267 {
1268 *message='\0';
cristyb51dff52011-05-19 16:55:47 +00001269 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristyfccdab92009-11-30 16:43:57 +00001270 (void) ConcatenateString(&message,format);
cristy5e6be1e2011-07-16 01:23:39 +00001271 for (u=0; u < (ssize_t) kernel_info->height; u++)
cristyfccdab92009-11-30 16:43:57 +00001272 {
cristyb51dff52011-05-19 16:55:47 +00001273 (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
cristyfccdab92009-11-30 16:43:57 +00001274 (void) ConcatenateString(&message,format);
1275 }
1276 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1277 }
1278 message=DestroyString(message);
1279 }
1280 /*
cristyfccdab92009-11-30 16:43:57 +00001281 Convolve image.
1282 */
1283 status=MagickTrue;
1284 progress=0;
cristyfccdab92009-11-30 16:43:57 +00001285 image_view=AcquireCacheView(image);
1286 convolve_view=AcquireCacheView(convolve_image);
1287#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy175653e2011-07-10 23:13:34 +00001288 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristyfccdab92009-11-30 16:43:57 +00001289#endif
cristybb503372010-05-27 20:51:26 +00001290 for (y=0; y < (ssize_t) image->rows; y++)
cristyfccdab92009-11-30 16:43:57 +00001291 {
cristy4c08aed2011-07-01 19:47:50 +00001292 register const Quantum
cristy105ba3c2011-07-18 02:28:38 +00001293 *restrict p;
cristyfccdab92009-11-30 16:43:57 +00001294
cristy4c08aed2011-07-01 19:47:50 +00001295 register Quantum
cristyfccdab92009-11-30 16:43:57 +00001296 *restrict q;
1297
cristy117ff172010-08-15 21:35:32 +00001298 register ssize_t
1299 x;
1300
cristy63852c12011-07-14 23:47:58 +00001301 size_t
cristyed231572011-07-14 02:18:59 +00001302 channels,
1303 convolve_channels;
1304
cristy5af97fe2011-07-18 02:33:29 +00001305 ssize_t
1306 center;
1307
cristyfccdab92009-11-30 16:43:57 +00001308 if (status == MagickFalse)
1309 continue;
cristy105ba3c2011-07-18 02:28:38 +00001310 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
1311 (ssize_t) (kernel_info->height/2L),image->columns+kernel_info->width,
1312 kernel_info->height,exception);
cristy08429172011-07-14 17:18:16 +00001313 q=QueueCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
cristyfccdab92009-11-30 16:43:57 +00001314 exception);
cristy105ba3c2011-07-18 02:28:38 +00001315 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristyfccdab92009-11-30 16:43:57 +00001316 {
1317 status=MagickFalse;
1318 continue;
1319 }
cristyed231572011-07-14 02:18:59 +00001320 channels=GetPixelChannels(image);
1321 convolve_channels=GetPixelChannels(convolve_image);
cristy5af97fe2011-07-18 02:33:29 +00001322 center=channels*(image->columns+kernel_info->width)*(kernel_info->height/
1323 2L)+channels*(kernel_info->width/2);
cristybb503372010-05-27 20:51:26 +00001324 for (x=0; x < (ssize_t) image->columns; x++)
cristyfccdab92009-11-30 16:43:57 +00001325 {
cristybb503372010-05-27 20:51:26 +00001326 register ssize_t
cristyed231572011-07-14 02:18:59 +00001327 i;
cristyfccdab92009-11-30 16:43:57 +00001328
cristyed231572011-07-14 02:18:59 +00001329 for (i=0; i < (ssize_t) channels; i++)
1330 {
cristyed231572011-07-14 02:18:59 +00001331 MagickRealType
cristy4e154852011-07-14 13:28:53 +00001332 alpha,
1333 gamma,
cristyed231572011-07-14 02:18:59 +00001334 pixel;
1335
1336 PixelChannel
1337 channel;
1338
1339 PixelTrait
1340 convolve_traits,
1341 traits;
1342
1343 register const double
1344 *restrict k;
1345
1346 register const Quantum
cristyeb52cde2011-07-17 01:52:52 +00001347 *restrict pixels;
cristyed231572011-07-14 02:18:59 +00001348
1349 register ssize_t
1350 u;
1351
1352 ssize_t
1353 v;
1354
cristy30301712011-07-18 15:06:51 +00001355 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
cristy4e154852011-07-14 13:28:53 +00001356 if (traits == UndefinedPixelTrait)
cristyed231572011-07-14 02:18:59 +00001357 continue;
cristy30301712011-07-18 15:06:51 +00001358 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
cristy4e154852011-07-14 13:28:53 +00001359 convolve_traits=GetPixelChannelMapTraits(convolve_image,channel);
1360 if (convolve_traits == UndefinedPixelTrait)
1361 continue;
1362 if ((convolve_traits & CopyPixelTrait) != 0)
1363 {
cristyf7dc44c2011-07-20 14:41:15 +00001364 q[channel]=p[center+i];
cristy4e154852011-07-14 13:28:53 +00001365 continue;
1366 }
cristy5e6be1e2011-07-16 01:23:39 +00001367 k=kernel_info->values;
cristy105ba3c2011-07-18 02:28:38 +00001368 pixels=p;
cristy0a922382011-07-16 15:30:34 +00001369 pixel=kernel_info->bias;
cristy1ce96d02011-07-14 17:57:24 +00001370 if (((convolve_traits & BlendPixelTrait) == 0) ||
1371 (GetPixelAlphaTraits(image) == UndefinedPixelTrait) ||
cristyed231572011-07-14 02:18:59 +00001372 (image->matte == MagickFalse))
cristyfccdab92009-11-30 16:43:57 +00001373 {
cristyed231572011-07-14 02:18:59 +00001374 /*
cristy4e154852011-07-14 13:28:53 +00001375 No alpha blending.
cristyed231572011-07-14 02:18:59 +00001376 */
cristyeb52cde2011-07-17 01:52:52 +00001377 for (v=0; v < (ssize_t) kernel_info->height; v++)
cristyfccdab92009-11-30 16:43:57 +00001378 {
cristyeb52cde2011-07-17 01:52:52 +00001379 for (u=0; u < (ssize_t) kernel_info->width; u++)
cristy175653e2011-07-10 23:13:34 +00001380 {
cristyeb52cde2011-07-17 01:52:52 +00001381 pixel+=(*k)*pixels[i];
cristyed231572011-07-14 02:18:59 +00001382 k++;
cristyeb52cde2011-07-17 01:52:52 +00001383 pixels+=channels;
cristy175653e2011-07-10 23:13:34 +00001384 }
cristyeb52cde2011-07-17 01:52:52 +00001385 pixels+=image->columns*channels;
cristy175653e2011-07-10 23:13:34 +00001386 }
cristyf7dc44c2011-07-20 14:41:15 +00001387 q[channel]=ClampToQuantum(pixel);
cristy4e154852011-07-14 13:28:53 +00001388 continue;
cristyed231572011-07-14 02:18:59 +00001389 }
cristy4e154852011-07-14 13:28:53 +00001390 /*
1391 Alpha blending.
1392 */
1393 gamma=0.0;
cristyeb52cde2011-07-17 01:52:52 +00001394 for (v=0; v < (ssize_t) kernel_info->height; v++)
cristy4e154852011-07-14 13:28:53 +00001395 {
cristyeb52cde2011-07-17 01:52:52 +00001396 for (u=0; u < (ssize_t) kernel_info->width; u++)
cristy4e154852011-07-14 13:28:53 +00001397 {
cristyeb52cde2011-07-17 01:52:52 +00001398 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1399 pixel+=(*k)*alpha*pixels[i];
cristy4e154852011-07-14 13:28:53 +00001400 gamma+=(*k)*alpha;
1401 k++;
cristyeb52cde2011-07-17 01:52:52 +00001402 pixels+=channels;
cristy4e154852011-07-14 13:28:53 +00001403 }
cristyeb52cde2011-07-17 01:52:52 +00001404 pixels+=image->columns*channels;
cristy4e154852011-07-14 13:28:53 +00001405 }
cristy1ce96d02011-07-14 17:57:24 +00001406 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1407 SetPixelChannel(convolve_image,channel,ClampToQuantum(gamma*pixel),q);
cristyed231572011-07-14 02:18:59 +00001408 }
1409 p+=channels;
1410 q+=convolve_channels;
cristyfccdab92009-11-30 16:43:57 +00001411 }
cristyed231572011-07-14 02:18:59 +00001412 if (SyncCacheViewAuthenticPixels(convolve_view,exception) == MagickFalse)
cristyfccdab92009-11-30 16:43:57 +00001413 status=MagickFalse;
1414 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1415 {
1416 MagickBooleanType
1417 proceed;
1418
1419#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001420 #pragma omp critical (MagickCore_ConvolveImage)
cristyfccdab92009-11-30 16:43:57 +00001421#endif
1422 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1423 if (proceed == MagickFalse)
1424 status=MagickFalse;
1425 }
1426 }
1427 convolve_image->type=image->type;
1428 convolve_view=DestroyCacheView(convolve_view);
1429 image_view=DestroyCacheView(image_view);
cristyfccdab92009-11-30 16:43:57 +00001430 if (status == MagickFalse)
1431 convolve_image=DestroyImage(convolve_image);
1432 return(convolve_image);
1433}
1434
1435/*
1436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1437% %
1438% %
1439% %
cristy3ed852e2009-09-05 21:47:34 +00001440% D e s p e c k l e I m a g e %
1441% %
1442% %
1443% %
1444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1445%
1446% DespeckleImage() reduces the speckle noise in an image while perserving the
1447% edges of the original image.
1448%
1449% The format of the DespeckleImage method is:
1450%
1451% Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1452%
1453% A description of each parameter follows:
1454%
1455% o image: the image.
1456%
1457% o exception: return any errors or warnings in this structure.
1458%
1459*/
1460
cristybb503372010-05-27 20:51:26 +00001461static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1462 const size_t columns,const size_t rows,Quantum *f,Quantum *g,
cristy3ed852e2009-09-05 21:47:34 +00001463 const int polarity)
1464{
cristy3ed852e2009-09-05 21:47:34 +00001465 MagickRealType
1466 v;
1467
cristy3ed852e2009-09-05 21:47:34 +00001468 register Quantum
1469 *p,
1470 *q,
1471 *r,
1472 *s;
1473
cristy117ff172010-08-15 21:35:32 +00001474 register ssize_t
1475 x;
1476
1477 ssize_t
1478 y;
1479
cristy3ed852e2009-09-05 21:47:34 +00001480 assert(f != (Quantum *) NULL);
1481 assert(g != (Quantum *) NULL);
1482 p=f+(columns+2);
1483 q=g+(columns+2);
cristybb503372010-05-27 20:51:26 +00001484 r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1485 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001486 {
1487 p++;
1488 q++;
1489 r++;
1490 if (polarity > 0)
cristybb503372010-05-27 20:51:26 +00001491 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001492 {
1493 v=(MagickRealType) (*p);
1494 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1495 v+=ScaleCharToQuantum(1);
1496 *q=(Quantum) v;
1497 p++;
1498 q++;
1499 r++;
1500 }
1501 else
cristybb503372010-05-27 20:51:26 +00001502 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001503 {
1504 v=(MagickRealType) (*p);
1505 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
cristybb503372010-05-27 20:51:26 +00001506 v-=(ssize_t) ScaleCharToQuantum(1);
cristy3ed852e2009-09-05 21:47:34 +00001507 *q=(Quantum) v;
1508 p++;
1509 q++;
1510 r++;
1511 }
1512 p++;
1513 q++;
1514 r++;
1515 }
1516 p=f+(columns+2);
1517 q=g+(columns+2);
cristybb503372010-05-27 20:51:26 +00001518 r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1519 s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1520 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001521 {
1522 p++;
1523 q++;
1524 r++;
1525 s++;
1526 if (polarity > 0)
cristybb503372010-05-27 20:51:26 +00001527 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001528 {
1529 v=(MagickRealType) (*q);
1530 if (((MagickRealType) *s >=
1531 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1532 ((MagickRealType) *r > v))
1533 v+=ScaleCharToQuantum(1);
1534 *p=(Quantum) v;
1535 p++;
1536 q++;
1537 r++;
1538 s++;
1539 }
1540 else
cristybb503372010-05-27 20:51:26 +00001541 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001542 {
1543 v=(MagickRealType) (*q);
1544 if (((MagickRealType) *s <=
1545 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1546 ((MagickRealType) *r < v))
1547 v-=(MagickRealType) ScaleCharToQuantum(1);
1548 *p=(Quantum) v;
1549 p++;
1550 q++;
1551 r++;
1552 s++;
1553 }
1554 p++;
1555 q++;
1556 r++;
1557 s++;
1558 }
1559}
1560
1561MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1562{
1563#define DespeckleImageTag "Despeckle/Image"
1564
cristy2407fc22009-09-11 00:55:25 +00001565 CacheView
1566 *despeckle_view,
1567 *image_view;
1568
cristy3ed852e2009-09-05 21:47:34 +00001569 Image
1570 *despeckle_image;
1571
cristy3ed852e2009-09-05 21:47:34 +00001572 MagickBooleanType
1573 status;
1574
cristya58c3172011-02-19 19:23:11 +00001575 register ssize_t
1576 i;
1577
cristy3ed852e2009-09-05 21:47:34 +00001578 Quantum
cristy65b9f392011-02-22 14:22:54 +00001579 *restrict buffers,
1580 *restrict pixels;
cristy3ed852e2009-09-05 21:47:34 +00001581
1582 size_t
cristya58c3172011-02-19 19:23:11 +00001583 length,
1584 number_channels;
cristy117ff172010-08-15 21:35:32 +00001585
cristybb503372010-05-27 20:51:26 +00001586 static const ssize_t
cristy691a29e2009-09-11 00:44:10 +00001587 X[4] = {0, 1, 1,-1},
1588 Y[4] = {1, 0, 1, 1};
cristy3ed852e2009-09-05 21:47:34 +00001589
cristy3ed852e2009-09-05 21:47:34 +00001590 /*
1591 Allocate despeckled image.
1592 */
1593 assert(image != (const Image *) NULL);
1594 assert(image->signature == MagickSignature);
1595 if (image->debug != MagickFalse)
1596 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1597 assert(exception != (ExceptionInfo *) NULL);
1598 assert(exception->signature == MagickSignature);
1599 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1600 exception);
1601 if (despeckle_image == (Image *) NULL)
1602 return((Image *) NULL);
1603 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1604 {
1605 InheritException(exception,&despeckle_image->exception);
1606 despeckle_image=DestroyImage(despeckle_image);
1607 return((Image *) NULL);
1608 }
1609 /*
1610 Allocate image buffers.
1611 */
1612 length=(size_t) ((image->columns+2)*(image->rows+2));
cristy65b9f392011-02-22 14:22:54 +00001613 pixels=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1614 buffers=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1615 if ((pixels == (Quantum *) NULL) || (buffers == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001616 {
cristy65b9f392011-02-22 14:22:54 +00001617 if (buffers != (Quantum *) NULL)
1618 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1619 if (pixels != (Quantum *) NULL)
1620 pixels=(Quantum *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001621 despeckle_image=DestroyImage(despeckle_image);
1622 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1623 }
1624 /*
1625 Reduce speckle in the image.
1626 */
1627 status=MagickTrue;
cristy109695a2011-02-19 19:38:14 +00001628 number_channels=(size_t) (image->colorspace == CMYKColorspace ? 5 : 4);
cristy3ed852e2009-09-05 21:47:34 +00001629 image_view=AcquireCacheView(image);
1630 despeckle_view=AcquireCacheView(despeckle_image);
cristy8df3d002011-02-19 19:40:59 +00001631 for (i=0; i < (ssize_t) number_channels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001632 {
cristy3ed852e2009-09-05 21:47:34 +00001633 register Quantum
1634 *buffer,
1635 *pixel;
1636
cristyc1488b52011-02-19 18:54:15 +00001637 register ssize_t
cristya58c3172011-02-19 19:23:11 +00001638 k,
cristyc1488b52011-02-19 18:54:15 +00001639 x;
1640
cristy117ff172010-08-15 21:35:32 +00001641 ssize_t
1642 j,
1643 y;
1644
cristy3ed852e2009-09-05 21:47:34 +00001645 if (status == MagickFalse)
1646 continue;
cristy65b9f392011-02-22 14:22:54 +00001647 pixel=pixels;
cristy3ed852e2009-09-05 21:47:34 +00001648 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
cristy65b9f392011-02-22 14:22:54 +00001649 buffer=buffers;
cristybb503372010-05-27 20:51:26 +00001650 j=(ssize_t) image->columns+2;
1651 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001652 {
cristy4c08aed2011-07-01 19:47:50 +00001653 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001654 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001655
1656 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001657 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001658 break;
1659 j++;
cristybb503372010-05-27 20:51:26 +00001660 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001661 {
cristya58c3172011-02-19 19:23:11 +00001662 switch (i)
cristy3ed852e2009-09-05 21:47:34 +00001663 {
cristy4c08aed2011-07-01 19:47:50 +00001664 case 0: pixel[j]=GetPixelRed(image,p); break;
1665 case 1: pixel[j]=GetPixelGreen(image,p); break;
1666 case 2: pixel[j]=GetPixelBlue(image,p); break;
1667 case 3: pixel[j]=GetPixelAlpha(image,p); break;
1668 case 4: pixel[j]=GetPixelBlack(image,p); break;
cristy3ed852e2009-09-05 21:47:34 +00001669 default: break;
1670 }
cristyed231572011-07-14 02:18:59 +00001671 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001672 j++;
1673 }
1674 j++;
1675 }
cristy3ed852e2009-09-05 21:47:34 +00001676 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
cristya58c3172011-02-19 19:23:11 +00001677 for (k=0; k < 4; k++)
cristy3ed852e2009-09-05 21:47:34 +00001678 {
cristya58c3172011-02-19 19:23:11 +00001679 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,1);
1680 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,1);
1681 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,-1);
1682 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,-1);
cristy3ed852e2009-09-05 21:47:34 +00001683 }
cristybb503372010-05-27 20:51:26 +00001684 j=(ssize_t) image->columns+2;
1685 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001686 {
1687 MagickBooleanType
1688 sync;
1689
cristy4c08aed2011-07-01 19:47:50 +00001690 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001691 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001692
1693 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1694 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001695 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001696 break;
1697 j++;
cristybb503372010-05-27 20:51:26 +00001698 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001699 {
cristya58c3172011-02-19 19:23:11 +00001700 switch (i)
cristy3ed852e2009-09-05 21:47:34 +00001701 {
cristy4c08aed2011-07-01 19:47:50 +00001702 case 0: SetPixelRed(despeckle_image,pixel[j],q); break;
1703 case 1: SetPixelGreen(despeckle_image,pixel[j],q); break;
1704 case 2: SetPixelBlue(despeckle_image,pixel[j],q); break;
1705 case 3: SetPixelAlpha(despeckle_image,pixel[j],q); break;
1706 case 4: SetPixelBlack(despeckle_image,pixel[j],q); break;
cristy3ed852e2009-09-05 21:47:34 +00001707 default: break;
1708 }
cristyed231572011-07-14 02:18:59 +00001709 q+=GetPixelChannels(despeckle_image);
cristy3ed852e2009-09-05 21:47:34 +00001710 j++;
1711 }
1712 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1713 if (sync == MagickFalse)
1714 {
1715 status=MagickFalse;
1716 break;
1717 }
1718 j++;
1719 }
1720 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1721 {
1722 MagickBooleanType
1723 proceed;
1724
cristya58c3172011-02-19 19:23:11 +00001725 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1726 number_channels);
cristy3ed852e2009-09-05 21:47:34 +00001727 if (proceed == MagickFalse)
1728 status=MagickFalse;
1729 }
1730 }
1731 despeckle_view=DestroyCacheView(despeckle_view);
1732 image_view=DestroyCacheView(image_view);
cristy65b9f392011-02-22 14:22:54 +00001733 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1734 pixels=(Quantum *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001735 despeckle_image->type=image->type;
1736 if (status == MagickFalse)
1737 despeckle_image=DestroyImage(despeckle_image);
1738 return(despeckle_image);
1739}
1740
1741/*
1742%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1743% %
1744% %
1745% %
1746% E d g e I m a g e %
1747% %
1748% %
1749% %
1750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1751%
1752% EdgeImage() finds edges in an image. Radius defines the radius of the
1753% convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1754% radius for you.
1755%
1756% The format of the EdgeImage method is:
1757%
1758% Image *EdgeImage(const Image *image,const double radius,
1759% ExceptionInfo *exception)
1760%
1761% A description of each parameter follows:
1762%
1763% o image: the image.
1764%
1765% o radius: the radius of the pixel neighborhood.
1766%
1767% o exception: return any errors or warnings in this structure.
1768%
1769*/
1770MagickExport Image *EdgeImage(const Image *image,const double radius,
1771 ExceptionInfo *exception)
1772{
1773 Image
1774 *edge_image;
1775
cristy41cbe682011-07-15 19:12:37 +00001776 KernelInfo
1777 *kernel_info;
cristy3ed852e2009-09-05 21:47:34 +00001778
cristybb503372010-05-27 20:51:26 +00001779 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001780 i;
1781
cristybb503372010-05-27 20:51:26 +00001782 size_t
cristy3ed852e2009-09-05 21:47:34 +00001783 width;
1784
cristy41cbe682011-07-15 19:12:37 +00001785 ssize_t
1786 j,
1787 u,
1788 v;
1789
cristy3ed852e2009-09-05 21:47:34 +00001790 assert(image != (const Image *) NULL);
1791 assert(image->signature == MagickSignature);
1792 if (image->debug != MagickFalse)
1793 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1794 assert(exception != (ExceptionInfo *) NULL);
1795 assert(exception->signature == MagickSignature);
1796 width=GetOptimalKernelWidth1D(radius,0.5);
cristy5e6be1e2011-07-16 01:23:39 +00001797 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001798 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001799 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001800 kernel_info->width=width;
1801 kernel_info->height=width;
cristy41cbe682011-07-15 19:12:37 +00001802 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1803 kernel_info->width*sizeof(*kernel_info->values));
1804 if (kernel_info->values == (double *) NULL)
1805 {
1806 kernel_info=DestroyKernelInfo(kernel_info);
1807 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1808 }
1809 j=(ssize_t) kernel_info->width/2;
1810 i=0;
1811 for (v=(-j); v <= j; v++)
1812 {
1813 for (u=(-j); u <= j; u++)
1814 {
1815 kernel_info->values[i]=(-1.0);
1816 i++;
1817 }
1818 }
1819 kernel_info->values[i/2]=(double) (width*width-1.0);
cristy0a922382011-07-16 15:30:34 +00001820 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00001821 edge_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001822 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001823 return(edge_image);
1824}
1825
1826/*
1827%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1828% %
1829% %
1830% %
1831% E m b o s s I m a g e %
1832% %
1833% %
1834% %
1835%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1836%
1837% EmbossImage() returns a grayscale image with a three-dimensional effect.
1838% We convolve the image with a Gaussian operator of the given radius and
1839% standard deviation (sigma). For reasonable results, radius should be
1840% larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1841% radius for you.
1842%
1843% The format of the EmbossImage method is:
1844%
1845% Image *EmbossImage(const Image *image,const double radius,
1846% const double sigma,ExceptionInfo *exception)
1847%
1848% A description of each parameter follows:
1849%
1850% o image: the image.
1851%
1852% o radius: the radius of the pixel neighborhood.
1853%
1854% o sigma: the standard deviation of the Gaussian, in pixels.
1855%
1856% o exception: return any errors or warnings in this structure.
1857%
1858*/
1859MagickExport Image *EmbossImage(const Image *image,const double radius,
1860 const double sigma,ExceptionInfo *exception)
1861{
cristy3ed852e2009-09-05 21:47:34 +00001862 Image
1863 *emboss_image;
1864
cristy41cbe682011-07-15 19:12:37 +00001865 KernelInfo
1866 *kernel_info;
1867
cristybb503372010-05-27 20:51:26 +00001868 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001869 i;
1870
cristybb503372010-05-27 20:51:26 +00001871 size_t
cristy3ed852e2009-09-05 21:47:34 +00001872 width;
1873
cristy117ff172010-08-15 21:35:32 +00001874 ssize_t
1875 j,
1876 k,
1877 u,
1878 v;
1879
cristy41cbe682011-07-15 19:12:37 +00001880 assert(image != (const Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001881 assert(image->signature == MagickSignature);
1882 if (image->debug != MagickFalse)
1883 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1884 assert(exception != (ExceptionInfo *) NULL);
1885 assert(exception->signature == MagickSignature);
1886 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001887 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001888 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001889 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001890 kernel_info->width=width;
1891 kernel_info->height=width;
cristy41cbe682011-07-15 19:12:37 +00001892 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1893 kernel_info->width*sizeof(*kernel_info->values));
1894 if (kernel_info->values == (double *) NULL)
1895 {
1896 kernel_info=DestroyKernelInfo(kernel_info);
1897 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1898 }
1899 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00001900 k=j;
1901 i=0;
1902 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00001903 {
cristy47e00502009-12-17 19:19:57 +00001904 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00001905 {
cristy41cbe682011-07-15 19:12:37 +00001906 kernel_info->values[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
cristy47e00502009-12-17 19:19:57 +00001907 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
cristy4205a3c2010-09-12 20:19:59 +00001908 (2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +00001909 if (u != k)
cristy41cbe682011-07-15 19:12:37 +00001910 kernel_info->values[i]=0.0;
cristy3ed852e2009-09-05 21:47:34 +00001911 i++;
1912 }
cristy47e00502009-12-17 19:19:57 +00001913 k--;
cristy3ed852e2009-09-05 21:47:34 +00001914 }
cristy0a922382011-07-16 15:30:34 +00001915 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00001916 emboss_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001917 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001918 if (emboss_image != (Image *) NULL)
1919 (void) EqualizeImage(emboss_image);
cristy3ed852e2009-09-05 21:47:34 +00001920 return(emboss_image);
1921}
1922
1923/*
1924%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1925% %
1926% %
1927% %
1928% G a u s s i a n B l u r I m a g e %
1929% %
1930% %
1931% %
1932%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1933%
1934% GaussianBlurImage() blurs an image. We convolve the image with a
1935% Gaussian operator of the given radius and standard deviation (sigma).
1936% For reasonable results, the radius should be larger than sigma. Use a
1937% radius of 0 and GaussianBlurImage() selects a suitable radius for you
1938%
1939% The format of the GaussianBlurImage method is:
1940%
1941% Image *GaussianBlurImage(const Image *image,onst double radius,
1942% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001943%
1944% A description of each parameter follows:
1945%
1946% o image: the image.
1947%
cristy3ed852e2009-09-05 21:47:34 +00001948% o radius: the radius of the Gaussian, in pixels, not counting the center
1949% pixel.
1950%
1951% o sigma: the standard deviation of the Gaussian, in pixels.
1952%
1953% o exception: return any errors or warnings in this structure.
1954%
1955*/
cristy41cbe682011-07-15 19:12:37 +00001956MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1957 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001958{
cristy3ed852e2009-09-05 21:47:34 +00001959 Image
1960 *blur_image;
1961
cristy41cbe682011-07-15 19:12:37 +00001962 KernelInfo
1963 *kernel_info;
1964
cristybb503372010-05-27 20:51:26 +00001965 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001966 i;
1967
cristybb503372010-05-27 20:51:26 +00001968 size_t
cristy3ed852e2009-09-05 21:47:34 +00001969 width;
1970
cristy117ff172010-08-15 21:35:32 +00001971 ssize_t
1972 j,
1973 u,
1974 v;
1975
cristy3ed852e2009-09-05 21:47:34 +00001976 assert(image != (const Image *) NULL);
1977 assert(image->signature == MagickSignature);
1978 if (image->debug != MagickFalse)
1979 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1980 assert(exception != (ExceptionInfo *) NULL);
1981 assert(exception->signature == MagickSignature);
1982 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001983 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001984 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001985 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001986 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1987 kernel_info->width=width;
1988 kernel_info->height=width;
1989 kernel_info->signature=MagickSignature;
1990 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1991 kernel_info->width*sizeof(*kernel_info->values));
1992 if (kernel_info->values == (double *) NULL)
1993 {
1994 kernel_info=DestroyKernelInfo(kernel_info);
1995 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1996 }
1997 j=(ssize_t) kernel_info->width/2;
cristy3ed852e2009-09-05 21:47:34 +00001998 i=0;
cristy47e00502009-12-17 19:19:57 +00001999 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00002000 {
cristy47e00502009-12-17 19:19:57 +00002001 for (u=(-j); u <= j; u++)
cristy41cbe682011-07-15 19:12:37 +00002002 {
2003 kernel_info->values[i]=(double) (exp(-((double) u*u+v*v)/(2.0*
2004 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
2005 i++;
2006 }
cristy3ed852e2009-09-05 21:47:34 +00002007 }
cristy0a922382011-07-16 15:30:34 +00002008 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00002009 blur_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00002010 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00002011 return(blur_image);
2012}
2013
2014/*
2015%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2016% %
2017% %
2018% %
cristy3ed852e2009-09-05 21:47:34 +00002019% M o t i o n B l u r I m a g e %
2020% %
2021% %
2022% %
2023%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2024%
2025% MotionBlurImage() simulates motion blur. We convolve the image with a
2026% Gaussian operator of the given radius and standard deviation (sigma).
2027% For reasonable results, radius should be larger than sigma. Use a
2028% radius of 0 and MotionBlurImage() selects a suitable radius for you.
2029% Angle gives the angle of the blurring motion.
2030%
2031% Andrew Protano contributed this effect.
2032%
2033% The format of the MotionBlurImage method is:
2034%
2035% Image *MotionBlurImage(const Image *image,const double radius,
2036% const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002037%
2038% A description of each parameter follows:
2039%
2040% o image: the image.
2041%
cristy3ed852e2009-09-05 21:47:34 +00002042% o radius: the radius of the Gaussian, in pixels, not counting
2043% the center pixel.
2044%
2045% o sigma: the standard deviation of the Gaussian, in pixels.
2046%
cristycee97112010-05-28 00:44:52 +00002047% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00002048%
2049% o exception: return any errors or warnings in this structure.
2050%
2051*/
2052
cristybb503372010-05-27 20:51:26 +00002053static double *GetMotionBlurKernel(const size_t width,const double sigma)
cristy3ed852e2009-09-05 21:47:34 +00002054{
cristy3ed852e2009-09-05 21:47:34 +00002055 double
cristy47e00502009-12-17 19:19:57 +00002056 *kernel,
cristy3ed852e2009-09-05 21:47:34 +00002057 normalize;
2058
cristybb503372010-05-27 20:51:26 +00002059 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002060 i;
2061
2062 /*
cristy47e00502009-12-17 19:19:57 +00002063 Generate a 1-D convolution kernel.
cristy3ed852e2009-09-05 21:47:34 +00002064 */
2065 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2066 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2067 if (kernel == (double *) NULL)
2068 return(kernel);
cristy3ed852e2009-09-05 21:47:34 +00002069 normalize=0.0;
cristybb503372010-05-27 20:51:26 +00002070 for (i=0; i < (ssize_t) width; i++)
cristy47e00502009-12-17 19:19:57 +00002071 {
cristy4205a3c2010-09-12 20:19:59 +00002072 kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
2073 MagickSigma)))/(MagickSQ2PI*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00002074 normalize+=kernel[i];
cristy47e00502009-12-17 19:19:57 +00002075 }
cristybb503372010-05-27 20:51:26 +00002076 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002077 kernel[i]/=normalize;
2078 return(kernel);
2079}
2080
cristyf4ad9df2011-07-08 16:49:03 +00002081MagickExport Image *MotionBlurImage(const Image *image,
2082 const double radius,const double sigma,const double angle,
2083 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002084{
cristyc4c8d132010-01-07 01:58:38 +00002085 CacheView
2086 *blur_view,
2087 *image_view;
2088
cristy3ed852e2009-09-05 21:47:34 +00002089 double
2090 *kernel;
2091
2092 Image
2093 *blur_image;
2094
cristy3ed852e2009-09-05 21:47:34 +00002095 MagickBooleanType
2096 status;
2097
cristybb503372010-05-27 20:51:26 +00002098 MagickOffsetType
2099 progress;
2100
cristy4c08aed2011-07-01 19:47:50 +00002101 PixelInfo
cristyddd82202009-11-03 20:14:50 +00002102 bias;
cristy3ed852e2009-09-05 21:47:34 +00002103
2104 OffsetInfo
2105 *offset;
2106
2107 PointInfo
2108 point;
2109
cristybb503372010-05-27 20:51:26 +00002110 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002111 i;
2112
cristybb503372010-05-27 20:51:26 +00002113 size_t
cristy3ed852e2009-09-05 21:47:34 +00002114 width;
2115
cristybb503372010-05-27 20:51:26 +00002116 ssize_t
2117 y;
2118
cristy3ed852e2009-09-05 21:47:34 +00002119 assert(image != (Image *) NULL);
2120 assert(image->signature == MagickSignature);
2121 if (image->debug != MagickFalse)
2122 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2123 assert(exception != (ExceptionInfo *) NULL);
2124 width=GetOptimalKernelWidth1D(radius,sigma);
2125 kernel=GetMotionBlurKernel(width,sigma);
2126 if (kernel == (double *) NULL)
2127 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2128 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2129 if (offset == (OffsetInfo *) NULL)
2130 {
2131 kernel=(double *) RelinquishMagickMemory(kernel);
2132 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2133 }
2134 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2135 if (blur_image == (Image *) NULL)
2136 {
2137 kernel=(double *) RelinquishMagickMemory(kernel);
2138 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2139 return((Image *) NULL);
2140 }
2141 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2142 {
2143 kernel=(double *) RelinquishMagickMemory(kernel);
2144 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2145 InheritException(exception,&blur_image->exception);
2146 blur_image=DestroyImage(blur_image);
2147 return((Image *) NULL);
2148 }
2149 point.x=(double) width*sin(DegreesToRadians(angle));
2150 point.y=(double) width*cos(DegreesToRadians(angle));
cristybb503372010-05-27 20:51:26 +00002151 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002152 {
cristybb503372010-05-27 20:51:26 +00002153 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2154 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
cristy3ed852e2009-09-05 21:47:34 +00002155 }
2156 /*
2157 Motion blur image.
2158 */
2159 status=MagickTrue;
2160 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00002161 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00002162 image_view=AcquireCacheView(image);
2163 blur_view=AcquireCacheView(blur_image);
cristyb557a152011-02-22 12:14:30 +00002164#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +00002165 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +00002166#endif
cristybb503372010-05-27 20:51:26 +00002167 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002168 {
cristy4c08aed2011-07-01 19:47:50 +00002169 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002170 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002171
cristy117ff172010-08-15 21:35:32 +00002172 register ssize_t
2173 x;
2174
cristy3ed852e2009-09-05 21:47:34 +00002175 if (status == MagickFalse)
2176 continue;
2177 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2178 exception);
cristy4c08aed2011-07-01 19:47:50 +00002179 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002180 {
2181 status=MagickFalse;
2182 continue;
2183 }
cristybb503372010-05-27 20:51:26 +00002184 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002185 {
cristy4c08aed2011-07-01 19:47:50 +00002186 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002187 qixel;
2188
2189 PixelPacket
2190 pixel;
2191
2192 register double
cristyc47d1f82009-11-26 01:44:43 +00002193 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00002194
cristybb503372010-05-27 20:51:26 +00002195 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002196 i;
2197
cristy3ed852e2009-09-05 21:47:34 +00002198 k=kernel;
cristyddd82202009-11-03 20:14:50 +00002199 qixel=bias;
cristyed231572011-07-14 02:18:59 +00002200 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002201 {
cristybb503372010-05-27 20:51:26 +00002202 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002203 {
2204 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2205 offset[i].y,&pixel,exception);
2206 qixel.red+=(*k)*pixel.red;
2207 qixel.green+=(*k)*pixel.green;
2208 qixel.blue+=(*k)*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00002209 qixel.alpha+=(*k)*pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002210 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002211 qixel.black+=(*k)*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00002212 k++;
2213 }
cristyed231572011-07-14 02:18:59 +00002214 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002215 SetPixelRed(blur_image,
2216 ClampToQuantum(qixel.red),q);
cristyed231572011-07-14 02:18:59 +00002217 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002218 SetPixelGreen(blur_image,
2219 ClampToQuantum(qixel.green),q);
cristyed231572011-07-14 02:18:59 +00002220 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002221 SetPixelBlue(blur_image,
2222 ClampToQuantum(qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00002223 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00002224 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00002225 SetPixelBlack(blur_image,
2226 ClampToQuantum(qixel.black),q);
cristyed231572011-07-14 02:18:59 +00002227 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002228 SetPixelAlpha(blur_image,
2229 ClampToQuantum(qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00002230 }
2231 else
2232 {
2233 MagickRealType
2234 alpha,
2235 gamma;
2236
2237 alpha=0.0;
2238 gamma=0.0;
cristybb503372010-05-27 20:51:26 +00002239 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002240 {
2241 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2242 offset[i].y,&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00002243 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00002244 qixel.red+=(*k)*alpha*pixel.red;
2245 qixel.green+=(*k)*alpha*pixel.green;
2246 qixel.blue+=(*k)*alpha*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00002247 qixel.alpha+=(*k)*pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002248 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002249 qixel.black+=(*k)*alpha*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00002250 gamma+=(*k)*alpha;
2251 k++;
2252 }
2253 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00002254 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002255 SetPixelRed(blur_image,
2256 ClampToQuantum(gamma*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00002257 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002258 SetPixelGreen(blur_image,
2259 ClampToQuantum(gamma*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00002260 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002261 SetPixelBlue(blur_image,
2262 ClampToQuantum(gamma*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00002263 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00002264 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00002265 SetPixelBlack(blur_image,
2266 ClampToQuantum(gamma*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00002267 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002268 SetPixelAlpha(blur_image,
2269 ClampToQuantum(qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00002270 }
cristyed231572011-07-14 02:18:59 +00002271 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00002272 }
2273 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2274 status=MagickFalse;
2275 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2276 {
2277 MagickBooleanType
2278 proceed;
2279
cristyb557a152011-02-22 12:14:30 +00002280#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00002281 #pragma omp critical (MagickCore_MotionBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00002282#endif
2283 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2284 if (proceed == MagickFalse)
2285 status=MagickFalse;
2286 }
2287 }
2288 blur_view=DestroyCacheView(blur_view);
2289 image_view=DestroyCacheView(image_view);
2290 kernel=(double *) RelinquishMagickMemory(kernel);
2291 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2292 if (status == MagickFalse)
2293 blur_image=DestroyImage(blur_image);
2294 return(blur_image);
2295}
2296
2297/*
2298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2299% %
2300% %
2301% %
2302% P r e v i e w I m a g e %
2303% %
2304% %
2305% %
2306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2307%
2308% PreviewImage() tiles 9 thumbnails of the specified image with an image
2309% processing operation applied with varying parameters. This may be helpful
2310% pin-pointing an appropriate parameter for a particular image processing
2311% operation.
2312%
2313% The format of the PreviewImages method is:
2314%
2315% Image *PreviewImages(const Image *image,const PreviewType preview,
2316% ExceptionInfo *exception)
2317%
2318% A description of each parameter follows:
2319%
2320% o image: the image.
2321%
2322% o preview: the image processing operation.
2323%
2324% o exception: return any errors or warnings in this structure.
2325%
2326*/
2327MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2328 ExceptionInfo *exception)
2329{
2330#define NumberTiles 9
2331#define PreviewImageTag "Preview/Image"
2332#define DefaultPreviewGeometry "204x204+10+10"
2333
2334 char
2335 factor[MaxTextExtent],
2336 label[MaxTextExtent];
2337
2338 double
2339 degrees,
2340 gamma,
2341 percentage,
2342 radius,
2343 sigma,
2344 threshold;
2345
2346 Image
2347 *images,
2348 *montage_image,
2349 *preview_image,
2350 *thumbnail;
2351
2352 ImageInfo
2353 *preview_info;
2354
cristy3ed852e2009-09-05 21:47:34 +00002355 MagickBooleanType
2356 proceed;
2357
2358 MontageInfo
2359 *montage_info;
2360
2361 QuantizeInfo
2362 quantize_info;
2363
2364 RectangleInfo
2365 geometry;
2366
cristybb503372010-05-27 20:51:26 +00002367 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002368 i,
2369 x;
2370
cristybb503372010-05-27 20:51:26 +00002371 size_t
cristy3ed852e2009-09-05 21:47:34 +00002372 colors;
2373
cristy117ff172010-08-15 21:35:32 +00002374 ssize_t
2375 y;
2376
cristy3ed852e2009-09-05 21:47:34 +00002377 /*
2378 Open output image file.
2379 */
2380 assert(image != (Image *) NULL);
2381 assert(image->signature == MagickSignature);
2382 if (image->debug != MagickFalse)
2383 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2384 colors=2;
2385 degrees=0.0;
2386 gamma=(-0.2f);
2387 preview_info=AcquireImageInfo();
2388 SetGeometry(image,&geometry);
2389 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2390 &geometry.width,&geometry.height);
2391 images=NewImageList();
2392 percentage=12.5;
2393 GetQuantizeInfo(&quantize_info);
2394 radius=0.0;
2395 sigma=1.0;
2396 threshold=0.0;
2397 x=0;
2398 y=0;
2399 for (i=0; i < NumberTiles; i++)
2400 {
2401 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2402 if (thumbnail == (Image *) NULL)
2403 break;
2404 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2405 (void *) NULL);
2406 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2407 if (i == (NumberTiles/2))
2408 {
2409 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
2410 AppendImageToList(&images,thumbnail);
2411 continue;
2412 }
2413 switch (preview)
2414 {
2415 case RotatePreview:
2416 {
2417 degrees+=45.0;
2418 preview_image=RotateImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002419 (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002420 break;
2421 }
2422 case ShearPreview:
2423 {
2424 degrees+=5.0;
2425 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002426 (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002427 degrees,2.0*degrees);
2428 break;
2429 }
2430 case RollPreview:
2431 {
cristybb503372010-05-27 20:51:26 +00002432 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2433 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
cristy3ed852e2009-09-05 21:47:34 +00002434 preview_image=RollImage(thumbnail,x,y,exception);
cristyb51dff52011-05-19 16:55:47 +00002435 (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
cristye8c25f92010-06-03 00:53:06 +00002436 (double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00002437 break;
2438 }
2439 case HuePreview:
2440 {
2441 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2442 if (preview_image == (Image *) NULL)
2443 break;
cristyb51dff52011-05-19 16:55:47 +00002444 (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",
cristy3ed852e2009-09-05 21:47:34 +00002445 2.0*percentage);
2446 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002447 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002448 break;
2449 }
2450 case SaturationPreview:
2451 {
2452 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2453 if (preview_image == (Image *) NULL)
2454 break;
cristyb51dff52011-05-19 16:55:47 +00002455 (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",
cristy8cd5b312010-01-07 01:10:24 +00002456 2.0*percentage);
cristy3ed852e2009-09-05 21:47:34 +00002457 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002458 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002459 break;
2460 }
2461 case BrightnessPreview:
2462 {
2463 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2464 if (preview_image == (Image *) NULL)
2465 break;
cristyb51dff52011-05-19 16:55:47 +00002466 (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
cristy3ed852e2009-09-05 21:47:34 +00002467 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002468 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002469 break;
2470 }
2471 case GammaPreview:
2472 default:
2473 {
2474 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2475 if (preview_image == (Image *) NULL)
2476 break;
2477 gamma+=0.4f;
cristy50fbc382011-07-07 02:19:17 +00002478 (void) GammaImage(preview_image,gamma);
cristyb51dff52011-05-19 16:55:47 +00002479 (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
cristy3ed852e2009-09-05 21:47:34 +00002480 break;
2481 }
2482 case SpiffPreview:
2483 {
2484 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2485 if (preview_image != (Image *) NULL)
2486 for (x=0; x < i; x++)
2487 (void) ContrastImage(preview_image,MagickTrue);
cristyb51dff52011-05-19 16:55:47 +00002488 (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002489 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002490 break;
2491 }
2492 case DullPreview:
2493 {
2494 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2495 if (preview_image == (Image *) NULL)
2496 break;
2497 for (x=0; x < i; x++)
2498 (void) ContrastImage(preview_image,MagickFalse);
cristyb51dff52011-05-19 16:55:47 +00002499 (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002500 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002501 break;
2502 }
2503 case GrayscalePreview:
2504 {
2505 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2506 if (preview_image == (Image *) NULL)
2507 break;
2508 colors<<=1;
2509 quantize_info.number_colors=colors;
2510 quantize_info.colorspace=GRAYColorspace;
2511 (void) QuantizeImage(&quantize_info,preview_image);
cristyb51dff52011-05-19 16:55:47 +00002512 (void) FormatLocaleString(label,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002513 "-colorspace gray -colors %.20g",(double) colors);
cristy3ed852e2009-09-05 21:47:34 +00002514 break;
2515 }
2516 case QuantizePreview:
2517 {
2518 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2519 if (preview_image == (Image *) NULL)
2520 break;
2521 colors<<=1;
2522 quantize_info.number_colors=colors;
2523 (void) QuantizeImage(&quantize_info,preview_image);
cristyb51dff52011-05-19 16:55:47 +00002524 (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002525 colors);
cristy3ed852e2009-09-05 21:47:34 +00002526 break;
2527 }
2528 case DespecklePreview:
2529 {
2530 for (x=0; x < (i-1); x++)
2531 {
2532 preview_image=DespeckleImage(thumbnail,exception);
2533 if (preview_image == (Image *) NULL)
2534 break;
2535 thumbnail=DestroyImage(thumbnail);
2536 thumbnail=preview_image;
2537 }
2538 preview_image=DespeckleImage(thumbnail,exception);
2539 if (preview_image == (Image *) NULL)
2540 break;
cristyb51dff52011-05-19 16:55:47 +00002541 (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002542 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002543 break;
2544 }
2545 case ReduceNoisePreview:
2546 {
cristy95c38342011-03-18 22:39:51 +00002547 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2548 (size_t) radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002549 (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002550 break;
2551 }
2552 case AddNoisePreview:
2553 {
2554 switch ((int) i)
2555 {
2556 case 0:
2557 {
2558 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2559 break;
2560 }
2561 case 1:
2562 {
2563 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2564 break;
2565 }
2566 case 2:
2567 {
2568 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2569 break;
2570 }
2571 case 3:
2572 {
2573 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2574 break;
2575 }
2576 case 4:
2577 {
2578 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2579 break;
2580 }
2581 case 5:
2582 {
2583 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2584 break;
2585 }
2586 default:
2587 {
2588 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2589 break;
2590 }
2591 }
cristyd76c51e2011-03-26 00:21:26 +00002592 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2593 (size_t) i,exception);
cristyb51dff52011-05-19 16:55:47 +00002594 (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002595 break;
2596 }
2597 case SharpenPreview:
2598 {
2599 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002600 (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002601 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002602 break;
2603 }
2604 case BlurPreview:
2605 {
2606 preview_image=BlurImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002607 (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
cristy3ed852e2009-09-05 21:47:34 +00002608 sigma);
2609 break;
2610 }
2611 case ThresholdPreview:
2612 {
2613 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2614 if (preview_image == (Image *) NULL)
2615 break;
2616 (void) BilevelImage(thumbnail,
2617 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
cristyb51dff52011-05-19 16:55:47 +00002618 (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",
cristy3ed852e2009-09-05 21:47:34 +00002619 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2620 break;
2621 }
2622 case EdgeDetectPreview:
2623 {
2624 preview_image=EdgeImage(thumbnail,radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002625 (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002626 break;
2627 }
2628 case SpreadPreview:
2629 {
2630 preview_image=SpreadImage(thumbnail,radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002631 (void) FormatLocaleString(label,MaxTextExtent,"spread %g",
cristy8cd5b312010-01-07 01:10:24 +00002632 radius+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002633 break;
2634 }
2635 case SolarizePreview:
2636 {
2637 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2638 if (preview_image == (Image *) NULL)
2639 break;
2640 (void) SolarizeImage(preview_image,(double) QuantumRange*
2641 percentage/100.0);
cristyb51dff52011-05-19 16:55:47 +00002642 (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
cristy3ed852e2009-09-05 21:47:34 +00002643 (QuantumRange*percentage)/100.0);
2644 break;
2645 }
2646 case ShadePreview:
2647 {
2648 degrees+=10.0;
2649 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2650 exception);
cristyb51dff52011-05-19 16:55:47 +00002651 (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002652 degrees,degrees);
cristy3ed852e2009-09-05 21:47:34 +00002653 break;
2654 }
2655 case RaisePreview:
2656 {
2657 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2658 if (preview_image == (Image *) NULL)
2659 break;
cristybb503372010-05-27 20:51:26 +00002660 geometry.width=(size_t) (2*i+2);
2661 geometry.height=(size_t) (2*i+2);
cristy3ed852e2009-09-05 21:47:34 +00002662 geometry.x=i/2;
2663 geometry.y=i/2;
2664 (void) RaiseImage(preview_image,&geometry,MagickTrue);
cristyb51dff52011-05-19 16:55:47 +00002665 (void) FormatLocaleString(label,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00002666 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002667 geometry.height,(double) geometry.x,(double) geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00002668 break;
2669 }
2670 case SegmentPreview:
2671 {
2672 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2673 if (preview_image == (Image *) NULL)
2674 break;
2675 threshold+=0.4f;
2676 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
2677 threshold);
cristyb51dff52011-05-19 16:55:47 +00002678 (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002679 threshold,threshold);
2680 break;
2681 }
2682 case SwirlPreview:
2683 {
2684 preview_image=SwirlImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002685 (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002686 degrees+=45.0;
2687 break;
2688 }
2689 case ImplodePreview:
2690 {
2691 degrees+=0.1f;
2692 preview_image=ImplodeImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002693 (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002694 break;
2695 }
2696 case WavePreview:
2697 {
2698 degrees+=5.0f;
2699 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002700 (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002701 0.5*degrees,2.0*degrees);
cristy3ed852e2009-09-05 21:47:34 +00002702 break;
2703 }
2704 case OilPaintPreview:
2705 {
2706 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002707 (void) FormatLocaleString(label,MaxTextExtent,"paint %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002708 break;
2709 }
2710 case CharcoalDrawingPreview:
2711 {
2712 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2713 exception);
cristyb51dff52011-05-19 16:55:47 +00002714 (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002715 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002716 break;
2717 }
2718 case JPEGPreview:
2719 {
2720 char
2721 filename[MaxTextExtent];
2722
2723 int
2724 file;
2725
2726 MagickBooleanType
2727 status;
2728
2729 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2730 if (preview_image == (Image *) NULL)
2731 break;
cristybb503372010-05-27 20:51:26 +00002732 preview_info->quality=(size_t) percentage;
cristyb51dff52011-05-19 16:55:47 +00002733 (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002734 preview_info->quality);
cristy3ed852e2009-09-05 21:47:34 +00002735 file=AcquireUniqueFileResource(filename);
2736 if (file != -1)
2737 file=close(file)-1;
cristyb51dff52011-05-19 16:55:47 +00002738 (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00002739 "jpeg:%s",filename);
2740 status=WriteImage(preview_info,preview_image);
2741 if (status != MagickFalse)
2742 {
2743 Image
2744 *quality_image;
2745
2746 (void) CopyMagickString(preview_info->filename,
2747 preview_image->filename,MaxTextExtent);
2748 quality_image=ReadImage(preview_info,exception);
2749 if (quality_image != (Image *) NULL)
2750 {
2751 preview_image=DestroyImage(preview_image);
2752 preview_image=quality_image;
2753 }
2754 }
2755 (void) RelinquishUniqueFileResource(preview_image->filename);
2756 if ((GetBlobSize(preview_image)/1024) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002757 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
cristy3ed852e2009-09-05 21:47:34 +00002758 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2759 1024.0/1024.0);
2760 else
2761 if (GetBlobSize(preview_image) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002762 (void) FormatLocaleString(label,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002763 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
cristy8cd5b312010-01-07 01:10:24 +00002764 GetBlobSize(preview_image))/1024.0);
cristy3ed852e2009-09-05 21:47:34 +00002765 else
cristyb51dff52011-05-19 16:55:47 +00002766 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
cristy54ea5732011-06-10 12:39:53 +00002767 factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
cristy3ed852e2009-09-05 21:47:34 +00002768 break;
2769 }
2770 }
2771 thumbnail=DestroyImage(thumbnail);
2772 percentage+=12.5;
2773 radius+=0.5;
2774 sigma+=0.25;
2775 if (preview_image == (Image *) NULL)
2776 break;
2777 (void) DeleteImageProperty(preview_image,"label");
2778 (void) SetImageProperty(preview_image,"label",label);
2779 AppendImageToList(&images,preview_image);
cristybb503372010-05-27 20:51:26 +00002780 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2781 NumberTiles);
cristy3ed852e2009-09-05 21:47:34 +00002782 if (proceed == MagickFalse)
2783 break;
2784 }
2785 if (images == (Image *) NULL)
2786 {
2787 preview_info=DestroyImageInfo(preview_info);
2788 return((Image *) NULL);
2789 }
2790 /*
2791 Create the montage.
2792 */
2793 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2794 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2795 montage_info->shadow=MagickTrue;
2796 (void) CloneString(&montage_info->tile,"3x3");
2797 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2798 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2799 montage_image=MontageImages(images,montage_info,exception);
2800 montage_info=DestroyMontageInfo(montage_info);
2801 images=DestroyImageList(images);
2802 if (montage_image == (Image *) NULL)
2803 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2804 if (montage_image->montage != (char *) NULL)
2805 {
2806 /*
2807 Free image directory.
2808 */
2809 montage_image->montage=(char *) RelinquishMagickMemory(
2810 montage_image->montage);
2811 if (image->directory != (char *) NULL)
2812 montage_image->directory=(char *) RelinquishMagickMemory(
2813 montage_image->directory);
2814 }
2815 preview_info=DestroyImageInfo(preview_info);
2816 return(montage_image);
2817}
2818
2819/*
2820%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2821% %
2822% %
2823% %
2824% R a d i a l B l u r I m a g e %
2825% %
2826% %
2827% %
2828%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2829%
2830% RadialBlurImage() applies a radial blur to the image.
2831%
2832% Andrew Protano contributed this effect.
2833%
2834% The format of the RadialBlurImage method is:
2835%
2836% Image *RadialBlurImage(const Image *image,const double angle,
2837% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002838%
2839% A description of each parameter follows:
2840%
2841% o image: the image.
2842%
cristy3ed852e2009-09-05 21:47:34 +00002843% o angle: the angle of the radial blur.
2844%
2845% o exception: return any errors or warnings in this structure.
2846%
2847*/
cristyf4ad9df2011-07-08 16:49:03 +00002848MagickExport Image *RadialBlurImage(const Image *image,
2849 const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002850{
cristyc4c8d132010-01-07 01:58:38 +00002851 CacheView
2852 *blur_view,
2853 *image_view;
2854
cristy3ed852e2009-09-05 21:47:34 +00002855 Image
2856 *blur_image;
2857
cristy3ed852e2009-09-05 21:47:34 +00002858 MagickBooleanType
2859 status;
2860
cristybb503372010-05-27 20:51:26 +00002861 MagickOffsetType
2862 progress;
2863
cristy4c08aed2011-07-01 19:47:50 +00002864 PixelInfo
cristyddd82202009-11-03 20:14:50 +00002865 bias;
cristy3ed852e2009-09-05 21:47:34 +00002866
2867 MagickRealType
2868 blur_radius,
2869 *cos_theta,
2870 offset,
2871 *sin_theta,
2872 theta;
2873
2874 PointInfo
2875 blur_center;
2876
cristybb503372010-05-27 20:51:26 +00002877 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002878 i;
2879
cristybb503372010-05-27 20:51:26 +00002880 size_t
cristy3ed852e2009-09-05 21:47:34 +00002881 n;
2882
cristybb503372010-05-27 20:51:26 +00002883 ssize_t
2884 y;
2885
cristy3ed852e2009-09-05 21:47:34 +00002886 /*
2887 Allocate blur image.
2888 */
2889 assert(image != (Image *) NULL);
2890 assert(image->signature == MagickSignature);
2891 if (image->debug != MagickFalse)
2892 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2893 assert(exception != (ExceptionInfo *) NULL);
2894 assert(exception->signature == MagickSignature);
2895 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2896 if (blur_image == (Image *) NULL)
2897 return((Image *) NULL);
2898 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2899 {
2900 InheritException(exception,&blur_image->exception);
2901 blur_image=DestroyImage(blur_image);
2902 return((Image *) NULL);
2903 }
2904 blur_center.x=(double) image->columns/2.0;
2905 blur_center.y=(double) image->rows/2.0;
2906 blur_radius=hypot(blur_center.x,blur_center.y);
cristy117ff172010-08-15 21:35:32 +00002907 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
cristy3ed852e2009-09-05 21:47:34 +00002908 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
2909 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2910 sizeof(*cos_theta));
2911 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2912 sizeof(*sin_theta));
2913 if ((cos_theta == (MagickRealType *) NULL) ||
2914 (sin_theta == (MagickRealType *) NULL))
2915 {
2916 blur_image=DestroyImage(blur_image);
2917 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2918 }
2919 offset=theta*(MagickRealType) (n-1)/2.0;
cristybb503372010-05-27 20:51:26 +00002920 for (i=0; i < (ssize_t) n; i++)
cristy3ed852e2009-09-05 21:47:34 +00002921 {
2922 cos_theta[i]=cos((double) (theta*i-offset));
2923 sin_theta[i]=sin((double) (theta*i-offset));
2924 }
2925 /*
2926 Radial blur image.
2927 */
2928 status=MagickTrue;
2929 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00002930 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00002931 image_view=AcquireCacheView(image);
2932 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00002933#if defined(MAGICKCORE_OPENMP_SUPPORT)
2934 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002935#endif
cristybb503372010-05-27 20:51:26 +00002936 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002937 {
cristy4c08aed2011-07-01 19:47:50 +00002938 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002939 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002940
cristy117ff172010-08-15 21:35:32 +00002941 register ssize_t
2942 x;
2943
cristy3ed852e2009-09-05 21:47:34 +00002944 if (status == MagickFalse)
2945 continue;
2946 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2947 exception);
cristy4c08aed2011-07-01 19:47:50 +00002948 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002949 {
2950 status=MagickFalse;
2951 continue;
2952 }
cristybb503372010-05-27 20:51:26 +00002953 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002954 {
cristy4c08aed2011-07-01 19:47:50 +00002955 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002956 qixel;
2957
2958 MagickRealType
2959 normalize,
2960 radius;
2961
2962 PixelPacket
2963 pixel;
2964
2965 PointInfo
2966 center;
2967
cristybb503372010-05-27 20:51:26 +00002968 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002969 i;
2970
cristybb503372010-05-27 20:51:26 +00002971 size_t
cristy3ed852e2009-09-05 21:47:34 +00002972 step;
2973
2974 center.x=(double) x-blur_center.x;
2975 center.y=(double) y-blur_center.y;
2976 radius=hypot((double) center.x,center.y);
2977 if (radius == 0)
2978 step=1;
2979 else
2980 {
cristybb503372010-05-27 20:51:26 +00002981 step=(size_t) (blur_radius/radius);
cristy3ed852e2009-09-05 21:47:34 +00002982 if (step == 0)
2983 step=1;
2984 else
2985 if (step >= n)
2986 step=n-1;
2987 }
2988 normalize=0.0;
cristyddd82202009-11-03 20:14:50 +00002989 qixel=bias;
cristyed231572011-07-14 02:18:59 +00002990 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002991 {
cristyeaedf062010-05-29 22:36:02 +00002992 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
cristy3ed852e2009-09-05 21:47:34 +00002993 {
cristyeaedf062010-05-29 22:36:02 +00002994 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
2995 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
2996 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
2997 cos_theta[i]+0.5),&pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00002998 qixel.red+=pixel.red;
2999 qixel.green+=pixel.green;
3000 qixel.blue+=pixel.blue;
cristy3ed852e2009-09-05 21:47:34 +00003001 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00003002 qixel.black+=pixel.black;
3003 qixel.alpha+=pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00003004 normalize+=1.0;
3005 }
3006 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3007 normalize);
cristyed231572011-07-14 02:18:59 +00003008 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003009 SetPixelRed(blur_image,
3010 ClampToQuantum(normalize*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00003011 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003012 SetPixelGreen(blur_image,
3013 ClampToQuantum(normalize*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00003014 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003015 SetPixelBlue(blur_image,
3016 ClampToQuantum(normalize*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003017 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00003018 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00003019 SetPixelBlack(blur_image,
3020 ClampToQuantum(normalize*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00003021 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003022 SetPixelAlpha(blur_image,
3023 ClampToQuantum(normalize*qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003024 }
3025 else
3026 {
3027 MagickRealType
3028 alpha,
3029 gamma;
3030
3031 alpha=1.0;
3032 gamma=0.0;
cristyeaedf062010-05-29 22:36:02 +00003033 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
cristy3ed852e2009-09-05 21:47:34 +00003034 {
cristyeaedf062010-05-29 22:36:02 +00003035 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3036 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
3037 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
3038 cos_theta[i]+0.5),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00003039 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00003040 qixel.red+=alpha*pixel.red;
3041 qixel.green+=alpha*pixel.green;
3042 qixel.blue+=alpha*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00003043 qixel.alpha+=pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00003044 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00003045 qixel.black+=alpha*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00003046 gamma+=alpha;
3047 normalize+=1.0;
3048 }
3049 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3050 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3051 normalize);
cristyed231572011-07-14 02:18:59 +00003052 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003053 SetPixelRed(blur_image,
3054 ClampToQuantum(gamma*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00003055 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003056 SetPixelGreen(blur_image,
3057 ClampToQuantum(gamma*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00003058 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003059 SetPixelBlue(blur_image,
3060 ClampToQuantum(gamma*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003061 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00003062 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00003063 SetPixelBlack(blur_image,
3064 ClampToQuantum(gamma*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00003065 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003066 SetPixelAlpha(blur_image,
3067 ClampToQuantum(normalize*qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003068 }
cristyed231572011-07-14 02:18:59 +00003069 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003070 }
3071 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3072 status=MagickFalse;
3073 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3074 {
3075 MagickBooleanType
3076 proceed;
3077
cristyb5d5f722009-11-04 03:03:49 +00003078#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00003079 #pragma omp critical (MagickCore_RadialBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003080#endif
3081 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3082 if (proceed == MagickFalse)
3083 status=MagickFalse;
3084 }
3085 }
3086 blur_view=DestroyCacheView(blur_view);
3087 image_view=DestroyCacheView(image_view);
3088 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3089 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3090 if (status == MagickFalse)
3091 blur_image=DestroyImage(blur_image);
3092 return(blur_image);
3093}
3094
3095/*
3096%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3097% %
3098% %
3099% %
cristy3ed852e2009-09-05 21:47:34 +00003100% S e l e c t i v e B l u r I m a g e %
3101% %
3102% %
3103% %
3104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3105%
3106% SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3107% It is similar to the unsharpen mask that sharpens everything with contrast
3108% above a certain threshold.
3109%
3110% The format of the SelectiveBlurImage method is:
3111%
3112% Image *SelectiveBlurImage(const Image *image,const double radius,
3113% const double sigma,const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003114%
3115% A description of each parameter follows:
3116%
3117% o image: the image.
3118%
cristy3ed852e2009-09-05 21:47:34 +00003119% o radius: the radius of the Gaussian, in pixels, not counting the center
3120% pixel.
3121%
3122% o sigma: the standard deviation of the Gaussian, in pixels.
3123%
3124% o threshold: only pixels within this contrast threshold are included
3125% in the blur operation.
3126%
3127% o exception: return any errors or warnings in this structure.
3128%
3129*/
cristyf4ad9df2011-07-08 16:49:03 +00003130MagickExport Image *SelectiveBlurImage(const Image *image,
3131 const double radius,const double sigma,const double threshold,
3132 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003133{
3134#define SelectiveBlurImageTag "SelectiveBlur/Image"
3135
cristy47e00502009-12-17 19:19:57 +00003136 CacheView
3137 *blur_view,
3138 *image_view;
3139
cristy3ed852e2009-09-05 21:47:34 +00003140 double
cristy3ed852e2009-09-05 21:47:34 +00003141 *kernel;
3142
3143 Image
3144 *blur_image;
3145
cristy3ed852e2009-09-05 21:47:34 +00003146 MagickBooleanType
3147 status;
3148
cristybb503372010-05-27 20:51:26 +00003149 MagickOffsetType
3150 progress;
3151
cristy4c08aed2011-07-01 19:47:50 +00003152 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003153 bias;
3154
cristybb503372010-05-27 20:51:26 +00003155 register ssize_t
cristy47e00502009-12-17 19:19:57 +00003156 i;
cristy3ed852e2009-09-05 21:47:34 +00003157
cristybb503372010-05-27 20:51:26 +00003158 size_t
cristy3ed852e2009-09-05 21:47:34 +00003159 width;
3160
cristybb503372010-05-27 20:51:26 +00003161 ssize_t
3162 j,
3163 u,
3164 v,
3165 y;
3166
cristy3ed852e2009-09-05 21:47:34 +00003167 /*
3168 Initialize blur image attributes.
3169 */
3170 assert(image != (Image *) NULL);
3171 assert(image->signature == MagickSignature);
3172 if (image->debug != MagickFalse)
3173 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3174 assert(exception != (ExceptionInfo *) NULL);
3175 assert(exception->signature == MagickSignature);
3176 width=GetOptimalKernelWidth1D(radius,sigma);
3177 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3178 if (kernel == (double *) NULL)
3179 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00003180 j=(ssize_t) width/2;
cristy3ed852e2009-09-05 21:47:34 +00003181 i=0;
cristy47e00502009-12-17 19:19:57 +00003182 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00003183 {
cristy47e00502009-12-17 19:19:57 +00003184 for (u=(-j); u <= j; u++)
cristy4205a3c2010-09-12 20:19:59 +00003185 kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3186 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00003187 }
3188 if (image->debug != MagickFalse)
3189 {
3190 char
3191 format[MaxTextExtent],
3192 *message;
3193
cristy117ff172010-08-15 21:35:32 +00003194 register const double
3195 *k;
3196
cristybb503372010-05-27 20:51:26 +00003197 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003198 u,
3199 v;
3200
cristy3ed852e2009-09-05 21:47:34 +00003201 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003202 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3203 width);
cristy3ed852e2009-09-05 21:47:34 +00003204 message=AcquireString("");
3205 k=kernel;
cristybb503372010-05-27 20:51:26 +00003206 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003207 {
3208 *message='\0';
cristyb51dff52011-05-19 16:55:47 +00003209 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristy3ed852e2009-09-05 21:47:34 +00003210 (void) ConcatenateString(&message,format);
cristybb503372010-05-27 20:51:26 +00003211 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003212 {
cristyb51dff52011-05-19 16:55:47 +00003213 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",*k++);
cristy3ed852e2009-09-05 21:47:34 +00003214 (void) ConcatenateString(&message,format);
3215 }
3216 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3217 }
3218 message=DestroyString(message);
3219 }
3220 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3221 if (blur_image == (Image *) NULL)
3222 return((Image *) NULL);
3223 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3224 {
3225 InheritException(exception,&blur_image->exception);
3226 blur_image=DestroyImage(blur_image);
3227 return((Image *) NULL);
3228 }
3229 /*
3230 Threshold blur image.
3231 */
3232 status=MagickTrue;
3233 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003234 GetPixelInfo(image,&bias);
3235 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00003236 image_view=AcquireCacheView(image);
3237 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00003238#if defined(MAGICKCORE_OPENMP_SUPPORT)
3239 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003240#endif
cristybb503372010-05-27 20:51:26 +00003241 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003242 {
cristy4c08aed2011-07-01 19:47:50 +00003243 double
3244 contrast;
3245
cristy3ed852e2009-09-05 21:47:34 +00003246 MagickBooleanType
3247 sync;
3248
3249 MagickRealType
3250 gamma;
3251
cristy4c08aed2011-07-01 19:47:50 +00003252 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003253 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003254
cristy4c08aed2011-07-01 19:47:50 +00003255 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003256 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003257
cristy117ff172010-08-15 21:35:32 +00003258 register ssize_t
3259 x;
3260
cristy3ed852e2009-09-05 21:47:34 +00003261 if (status == MagickFalse)
3262 continue;
cristy117ff172010-08-15 21:35:32 +00003263 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3264 (width/2L),image->columns+width,width,exception);
cristy3ed852e2009-09-05 21:47:34 +00003265 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3266 exception);
cristy4c08aed2011-07-01 19:47:50 +00003267 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003268 {
3269 status=MagickFalse;
3270 continue;
3271 }
cristybb503372010-05-27 20:51:26 +00003272 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003273 {
cristy4c08aed2011-07-01 19:47:50 +00003274 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003275 pixel;
3276
3277 register const double
cristyc47d1f82009-11-26 01:44:43 +00003278 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00003279
cristybb503372010-05-27 20:51:26 +00003280 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003281 u;
3282
cristy117ff172010-08-15 21:35:32 +00003283 ssize_t
3284 j,
3285 v;
3286
cristyddd82202009-11-03 20:14:50 +00003287 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +00003288 k=kernel;
3289 gamma=0.0;
3290 j=0;
cristyed231572011-07-14 02:18:59 +00003291 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00003292 {
cristybb503372010-05-27 20:51:26 +00003293 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003294 {
cristybb503372010-05-27 20:51:26 +00003295 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003296 {
cristyed231572011-07-14 02:18:59 +00003297 contrast=GetPixelIntensity(image,p+(u+j)*GetPixelChannels(image))-
cristy4c08aed2011-07-01 19:47:50 +00003298 (double) GetPixelIntensity(blur_image,q);
3299 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003300 {
cristy4c08aed2011-07-01 19:47:50 +00003301 pixel.red+=(*k)*
cristyed231572011-07-14 02:18:59 +00003302 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003303 pixel.green+=(*k)*
cristyed231572011-07-14 02:18:59 +00003304 GetPixelGreen(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003305 pixel.blue+=(*k)*
cristyed231572011-07-14 02:18:59 +00003306 GetPixelBlue(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003307 if (image->colorspace == CMYKColorspace)
3308 pixel.black+=(*k)*
cristyed231572011-07-14 02:18:59 +00003309 GetPixelBlack(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003310 gamma+=(*k);
3311 k++;
3312 }
3313 }
cristyd99b0962010-05-29 23:14:26 +00003314 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003315 }
3316 if (gamma != 0.0)
3317 {
3318 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00003319 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003320 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00003321 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003322 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00003323 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003324 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003325 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00003326 (image->colorspace == CMYKColorspace))
3327 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00003328 }
cristyed231572011-07-14 02:18:59 +00003329 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003330 {
3331 gamma=0.0;
3332 j=0;
cristybb503372010-05-27 20:51:26 +00003333 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003334 {
cristybb503372010-05-27 20:51:26 +00003335 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003336 {
cristy4c08aed2011-07-01 19:47:50 +00003337 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003338 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003339 GetPixelIntensity(blur_image,q);
3340 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003341 {
cristy4c08aed2011-07-01 19:47:50 +00003342 pixel.alpha+=(*k)*
cristyed231572011-07-14 02:18:59 +00003343 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003344 gamma+=(*k);
3345 k++;
3346 }
3347 }
cristyeaedf062010-05-29 22:36:02 +00003348 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003349 }
3350 if (gamma != 0.0)
3351 {
3352 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3353 gamma);
cristy4c08aed2011-07-01 19:47:50 +00003354 SetPixelAlpha(blur_image,ClampToQuantum(gamma*pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003355 }
3356 }
3357 }
3358 else
3359 {
3360 MagickRealType
3361 alpha;
3362
cristybb503372010-05-27 20:51:26 +00003363 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003364 {
cristybb503372010-05-27 20:51:26 +00003365 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003366 {
cristy4c08aed2011-07-01 19:47:50 +00003367 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003368 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003369 GetPixelIntensity(blur_image,q);
3370 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003371 {
cristy4c08aed2011-07-01 19:47:50 +00003372 alpha=(MagickRealType) (QuantumScale*
cristyed231572011-07-14 02:18:59 +00003373 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image)));
cristy4c08aed2011-07-01 19:47:50 +00003374 pixel.red+=(*k)*alpha*
cristyed231572011-07-14 02:18:59 +00003375 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003376 pixel.green+=(*k)*alpha*GetPixelGreen(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003377 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003378 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003379 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003380 pixel.alpha+=(*k)*GetPixelAlpha(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003381 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003382 if (image->colorspace == CMYKColorspace)
3383 pixel.black+=(*k)*GetPixelBlack(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003384 GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003385 gamma+=(*k)*alpha;
3386 k++;
3387 }
3388 }
cristyeaedf062010-05-29 22:36:02 +00003389 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003390 }
3391 if (gamma != 0.0)
3392 {
3393 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00003394 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003395 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00003396 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003397 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00003398 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003399 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003400 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00003401 (image->colorspace == CMYKColorspace))
3402 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00003403 }
cristyed231572011-07-14 02:18:59 +00003404 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003405 {
3406 gamma=0.0;
3407 j=0;
cristybb503372010-05-27 20:51:26 +00003408 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003409 {
cristybb503372010-05-27 20:51:26 +00003410 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003411 {
cristy4c08aed2011-07-01 19:47:50 +00003412 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003413 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003414 GetPixelIntensity(blur_image,q);
3415 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003416 {
cristy4c08aed2011-07-01 19:47:50 +00003417 pixel.alpha+=(*k)*
cristyed231572011-07-14 02:18:59 +00003418 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003419 gamma+=(*k);
3420 k++;
3421 }
3422 }
cristyeaedf062010-05-29 22:36:02 +00003423 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003424 }
3425 if (gamma != 0.0)
3426 {
3427 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3428 gamma);
cristy4c08aed2011-07-01 19:47:50 +00003429 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003430 }
3431 }
3432 }
cristyed231572011-07-14 02:18:59 +00003433 p+=GetPixelChannels(image);
3434 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003435 }
3436 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3437 if (sync == MagickFalse)
3438 status=MagickFalse;
3439 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3440 {
3441 MagickBooleanType
3442 proceed;
3443
cristyb5d5f722009-11-04 03:03:49 +00003444#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00003445 #pragma omp critical (MagickCore_SelectiveBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003446#endif
3447 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3448 image->rows);
3449 if (proceed == MagickFalse)
3450 status=MagickFalse;
3451 }
3452 }
3453 blur_image->type=image->type;
3454 blur_view=DestroyCacheView(blur_view);
3455 image_view=DestroyCacheView(image_view);
3456 kernel=(double *) RelinquishMagickMemory(kernel);
3457 if (status == MagickFalse)
3458 blur_image=DestroyImage(blur_image);
3459 return(blur_image);
3460}
3461
3462/*
3463%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3464% %
3465% %
3466% %
3467% S h a d e I m a g e %
3468% %
3469% %
3470% %
3471%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3472%
3473% ShadeImage() shines a distant light on an image to create a
3474% three-dimensional effect. You control the positioning of the light with
3475% azimuth and elevation; azimuth is measured in degrees off the x axis
3476% and elevation is measured in pixels above the Z axis.
3477%
3478% The format of the ShadeImage method is:
3479%
3480% Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3481% const double azimuth,const double elevation,ExceptionInfo *exception)
3482%
3483% A description of each parameter follows:
3484%
3485% o image: the image.
3486%
3487% o gray: A value other than zero shades the intensity of each pixel.
3488%
3489% o azimuth, elevation: Define the light source direction.
3490%
3491% o exception: return any errors or warnings in this structure.
3492%
3493*/
3494MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3495 const double azimuth,const double elevation,ExceptionInfo *exception)
3496{
3497#define ShadeImageTag "Shade/Image"
3498
cristyc4c8d132010-01-07 01:58:38 +00003499 CacheView
3500 *image_view,
3501 *shade_view;
3502
cristy3ed852e2009-09-05 21:47:34 +00003503 Image
3504 *shade_image;
3505
cristy3ed852e2009-09-05 21:47:34 +00003506 MagickBooleanType
3507 status;
3508
cristybb503372010-05-27 20:51:26 +00003509 MagickOffsetType
3510 progress;
3511
cristy3ed852e2009-09-05 21:47:34 +00003512 PrimaryInfo
3513 light;
3514
cristybb503372010-05-27 20:51:26 +00003515 ssize_t
3516 y;
3517
cristy3ed852e2009-09-05 21:47:34 +00003518 /*
3519 Initialize shaded image attributes.
3520 */
3521 assert(image != (const Image *) NULL);
3522 assert(image->signature == MagickSignature);
3523 if (image->debug != MagickFalse)
3524 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3525 assert(exception != (ExceptionInfo *) NULL);
3526 assert(exception->signature == MagickSignature);
3527 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3528 if (shade_image == (Image *) NULL)
3529 return((Image *) NULL);
3530 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
3531 {
3532 InheritException(exception,&shade_image->exception);
3533 shade_image=DestroyImage(shade_image);
3534 return((Image *) NULL);
3535 }
3536 /*
3537 Compute the light vector.
3538 */
3539 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3540 cos(DegreesToRadians(elevation));
3541 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3542 cos(DegreesToRadians(elevation));
3543 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3544 /*
3545 Shade image.
3546 */
3547 status=MagickTrue;
3548 progress=0;
3549 image_view=AcquireCacheView(image);
3550 shade_view=AcquireCacheView(shade_image);
cristyb5d5f722009-11-04 03:03:49 +00003551#if defined(MAGICKCORE_OPENMP_SUPPORT)
3552 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003553#endif
cristybb503372010-05-27 20:51:26 +00003554 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003555 {
3556 MagickRealType
3557 distance,
3558 normal_distance,
3559 shade;
3560
3561 PrimaryInfo
3562 normal;
3563
cristy4c08aed2011-07-01 19:47:50 +00003564 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003565 *restrict p,
3566 *restrict s0,
3567 *restrict s1,
3568 *restrict s2;
cristy3ed852e2009-09-05 21:47:34 +00003569
cristy4c08aed2011-07-01 19:47:50 +00003570 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003571 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003572
cristy117ff172010-08-15 21:35:32 +00003573 register ssize_t
3574 x;
3575
cristy3ed852e2009-09-05 21:47:34 +00003576 if (status == MagickFalse)
3577 continue;
3578 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
3579 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3580 exception);
cristy4c08aed2011-07-01 19:47:50 +00003581 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003582 {
3583 status=MagickFalse;
3584 continue;
3585 }
3586 /*
3587 Shade this row of pixels.
3588 */
3589 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
cristyed231572011-07-14 02:18:59 +00003590 s0=p+GetPixelChannels(image);
3591 s1=s0+(image->columns+2)*GetPixelChannels(image);
3592 s2=s1+(image->columns+2)*GetPixelChannels(image);
cristybb503372010-05-27 20:51:26 +00003593 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003594 {
3595 /*
3596 Determine the surface normal and compute shading.
3597 */
cristyed231572011-07-14 02:18:59 +00003598 normal.x=(double) (GetPixelIntensity(image,s0-GetPixelChannels(image))+
3599 GetPixelIntensity(image,s1-GetPixelChannels(image))+
3600 GetPixelIntensity(image,s2-GetPixelChannels(image))-
3601 GetPixelIntensity(image,s0+GetPixelChannels(image))-
3602 GetPixelIntensity(image,s1+GetPixelChannels(image))-
3603 GetPixelIntensity(image,s2+GetPixelChannels(image)));
3604 normal.y=(double) (GetPixelIntensity(image,s2-GetPixelChannels(image))+
cristy4c08aed2011-07-01 19:47:50 +00003605 GetPixelIntensity(image,s2)+
cristyed231572011-07-14 02:18:59 +00003606 GetPixelIntensity(image,s2+GetPixelChannels(image))-
3607 GetPixelIntensity(image,s0-GetPixelChannels(image))-
cristy4c08aed2011-07-01 19:47:50 +00003608 GetPixelIntensity(image,s0)-
cristyed231572011-07-14 02:18:59 +00003609 GetPixelIntensity(image,s0+GetPixelChannels(image)));
cristy3ed852e2009-09-05 21:47:34 +00003610 if ((normal.x == 0.0) && (normal.y == 0.0))
3611 shade=light.z;
3612 else
3613 {
3614 shade=0.0;
3615 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3616 if (distance > MagickEpsilon)
3617 {
3618 normal_distance=
3619 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3620 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3621 shade=distance/sqrt((double) normal_distance);
3622 }
3623 }
3624 if (gray != MagickFalse)
3625 {
cristy4c08aed2011-07-01 19:47:50 +00003626 SetPixelRed(shade_image,ClampToQuantum(shade),q);
3627 SetPixelGreen(shade_image,ClampToQuantum(shade),q);
3628 SetPixelBlue(shade_image,ClampToQuantum(shade),q);
cristy3ed852e2009-09-05 21:47:34 +00003629 }
3630 else
3631 {
cristy4c08aed2011-07-01 19:47:50 +00003632 SetPixelRed(shade_image,ClampToQuantum(QuantumScale*shade*
3633 GetPixelRed(image,s1)),q);
3634 SetPixelGreen(shade_image,ClampToQuantum(QuantumScale*shade*
3635 GetPixelGreen(image,s1)),q);
3636 SetPixelBlue(shade_image,ClampToQuantum(QuantumScale*shade*
3637 GetPixelBlue(image,s1)),q);
cristy3ed852e2009-09-05 21:47:34 +00003638 }
cristy4c08aed2011-07-01 19:47:50 +00003639 SetPixelAlpha(shade_image,GetPixelAlpha(image,s1),q);
cristyed231572011-07-14 02:18:59 +00003640 s0+=GetPixelChannels(image);
3641 s1+=GetPixelChannels(image);
3642 s2+=GetPixelChannels(image);
3643 q+=GetPixelChannels(shade_image);
cristy3ed852e2009-09-05 21:47:34 +00003644 }
3645 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3646 status=MagickFalse;
3647 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3648 {
3649 MagickBooleanType
3650 proceed;
3651
cristyb5d5f722009-11-04 03:03:49 +00003652#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003653 #pragma omp critical (MagickCore_ShadeImage)
3654#endif
3655 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3656 if (proceed == MagickFalse)
3657 status=MagickFalse;
3658 }
3659 }
3660 shade_view=DestroyCacheView(shade_view);
3661 image_view=DestroyCacheView(image_view);
3662 if (status == MagickFalse)
3663 shade_image=DestroyImage(shade_image);
3664 return(shade_image);
3665}
3666
3667/*
3668%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3669% %
3670% %
3671% %
3672% S h a r p e n I m a g e %
3673% %
3674% %
3675% %
3676%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3677%
3678% SharpenImage() sharpens the image. We convolve the image with a Gaussian
3679% operator of the given radius and standard deviation (sigma). For
3680% reasonable results, radius should be larger than sigma. Use a radius of 0
3681% and SharpenImage() selects a suitable radius for you.
3682%
3683% Using a separable kernel would be faster, but the negative weights cancel
3684% out on the corners of the kernel producing often undesirable ringing in the
3685% filtered result; this can be avoided by using a 2D gaussian shaped image
3686% sharpening kernel instead.
3687%
3688% The format of the SharpenImage method is:
3689%
3690% Image *SharpenImage(const Image *image,const double radius,
3691% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003692%
3693% A description of each parameter follows:
3694%
3695% o image: the image.
3696%
cristy3ed852e2009-09-05 21:47:34 +00003697% o radius: the radius of the Gaussian, in pixels, not counting the center
3698% pixel.
3699%
3700% o sigma: the standard deviation of the Laplacian, in pixels.
3701%
3702% o exception: return any errors or warnings in this structure.
3703%
3704*/
cristy3ed852e2009-09-05 21:47:34 +00003705MagickExport Image *SharpenImage(const Image *image,const double radius,
3706 const double sigma,ExceptionInfo *exception)
3707{
cristy3ed852e2009-09-05 21:47:34 +00003708 double
cristy47e00502009-12-17 19:19:57 +00003709 normalize;
cristy3ed852e2009-09-05 21:47:34 +00003710
3711 Image
3712 *sharp_image;
3713
cristy41cbe682011-07-15 19:12:37 +00003714 KernelInfo
3715 *kernel_info;
3716
cristybb503372010-05-27 20:51:26 +00003717 register ssize_t
cristy47e00502009-12-17 19:19:57 +00003718 i;
3719
cristybb503372010-05-27 20:51:26 +00003720 size_t
cristy3ed852e2009-09-05 21:47:34 +00003721 width;
3722
cristy117ff172010-08-15 21:35:32 +00003723 ssize_t
3724 j,
3725 u,
3726 v;
3727
cristy3ed852e2009-09-05 21:47:34 +00003728 assert(image != (const Image *) NULL);
3729 assert(image->signature == MagickSignature);
3730 if (image->debug != MagickFalse)
3731 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3732 assert(exception != (ExceptionInfo *) NULL);
3733 assert(exception->signature == MagickSignature);
3734 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00003735 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00003736 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003737 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00003738 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3739 kernel_info->width=width;
3740 kernel_info->height=width;
3741 kernel_info->signature=MagickSignature;
3742 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
3743 kernel_info->width*sizeof(*kernel_info->values));
3744 if (kernel_info->values == (double *) NULL)
3745 {
3746 kernel_info=DestroyKernelInfo(kernel_info);
3747 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3748 }
cristy3ed852e2009-09-05 21:47:34 +00003749 normalize=0.0;
cristy41cbe682011-07-15 19:12:37 +00003750 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00003751 i=0;
3752 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00003753 {
cristy47e00502009-12-17 19:19:57 +00003754 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00003755 {
cristy41cbe682011-07-15 19:12:37 +00003756 kernel_info->values[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*
3757 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3758 normalize+=kernel_info->values[i];
cristy3ed852e2009-09-05 21:47:34 +00003759 i++;
3760 }
3761 }
cristy41cbe682011-07-15 19:12:37 +00003762 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
cristy0a922382011-07-16 15:30:34 +00003763 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00003764 sharp_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00003765 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00003766 return(sharp_image);
3767}
3768
3769/*
3770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3771% %
3772% %
3773% %
3774% S p r e a d I m a g e %
3775% %
3776% %
3777% %
3778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3779%
3780% SpreadImage() is a special effects method that randomly displaces each
3781% pixel in a block defined by the radius parameter.
3782%
3783% The format of the SpreadImage method is:
3784%
3785% Image *SpreadImage(const Image *image,const double radius,
3786% ExceptionInfo *exception)
3787%
3788% A description of each parameter follows:
3789%
3790% o image: the image.
3791%
3792% o radius: Choose a random pixel in a neighborhood of this extent.
3793%
3794% o exception: return any errors or warnings in this structure.
3795%
3796*/
3797MagickExport Image *SpreadImage(const Image *image,const double radius,
3798 ExceptionInfo *exception)
3799{
3800#define SpreadImageTag "Spread/Image"
3801
cristyfa112112010-01-04 17:48:07 +00003802 CacheView
cristy9f7e7cb2011-03-26 00:49:57 +00003803 *image_view,
3804 *spread_view;
cristyfa112112010-01-04 17:48:07 +00003805
cristy3ed852e2009-09-05 21:47:34 +00003806 Image
3807 *spread_image;
3808
cristy3ed852e2009-09-05 21:47:34 +00003809 MagickBooleanType
3810 status;
3811
cristybb503372010-05-27 20:51:26 +00003812 MagickOffsetType
3813 progress;
3814
cristy4c08aed2011-07-01 19:47:50 +00003815 PixelInfo
cristyddd82202009-11-03 20:14:50 +00003816 bias;
cristy3ed852e2009-09-05 21:47:34 +00003817
3818 RandomInfo
cristyfa112112010-01-04 17:48:07 +00003819 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00003820
cristybb503372010-05-27 20:51:26 +00003821 size_t
cristy3ed852e2009-09-05 21:47:34 +00003822 width;
3823
cristybb503372010-05-27 20:51:26 +00003824 ssize_t
3825 y;
3826
cristy3ed852e2009-09-05 21:47:34 +00003827 /*
3828 Initialize spread image attributes.
3829 */
3830 assert(image != (Image *) NULL);
3831 assert(image->signature == MagickSignature);
3832 if (image->debug != MagickFalse)
3833 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3834 assert(exception != (ExceptionInfo *) NULL);
3835 assert(exception->signature == MagickSignature);
3836 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3837 exception);
3838 if (spread_image == (Image *) NULL)
3839 return((Image *) NULL);
3840 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
3841 {
3842 InheritException(exception,&spread_image->exception);
3843 spread_image=DestroyImage(spread_image);
3844 return((Image *) NULL);
3845 }
3846 /*
3847 Spread image.
3848 */
3849 status=MagickTrue;
3850 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003851 GetPixelInfo(spread_image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00003852 width=GetOptimalKernelWidth1D(radius,0.5);
cristy3ed852e2009-09-05 21:47:34 +00003853 random_info=AcquireRandomInfoThreadSet();
cristy9f7e7cb2011-03-26 00:49:57 +00003854 image_view=AcquireCacheView(image);
3855 spread_view=AcquireCacheView(spread_image);
cristyb557a152011-02-22 12:14:30 +00003856#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +00003857 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +00003858#endif
cristybb503372010-05-27 20:51:26 +00003859 for (y=0; y < (ssize_t) spread_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003860 {
cristy5c9e6f22010-09-17 17:31:01 +00003861 const int
3862 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003863
cristy4c08aed2011-07-01 19:47:50 +00003864 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003865 pixel;
3866
cristy4c08aed2011-07-01 19:47:50 +00003867 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003868 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003869
cristy117ff172010-08-15 21:35:32 +00003870 register ssize_t
3871 x;
3872
cristy3ed852e2009-09-05 21:47:34 +00003873 if (status == MagickFalse)
3874 continue;
cristy9f7e7cb2011-03-26 00:49:57 +00003875 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003876 exception);
cristy4c08aed2011-07-01 19:47:50 +00003877 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003878 {
3879 status=MagickFalse;
3880 continue;
3881 }
cristyddd82202009-11-03 20:14:50 +00003882 pixel=bias;
cristybb503372010-05-27 20:51:26 +00003883 for (x=0; x < (ssize_t) spread_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003884 {
cristy4c08aed2011-07-01 19:47:50 +00003885 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00003886 UndefinedInterpolatePixel,(double) x+width*(GetPseudoRandomValue(
3887 random_info[id])-0.5),(double) y+width*(GetPseudoRandomValue(
3888 random_info[id])-0.5),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00003889 SetPixelPixelInfo(spread_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00003890 q+=GetPixelChannels(spread_image);
cristy3ed852e2009-09-05 21:47:34 +00003891 }
cristy9f7e7cb2011-03-26 00:49:57 +00003892 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003893 status=MagickFalse;
3894 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3895 {
3896 MagickBooleanType
3897 proceed;
3898
cristyb557a152011-02-22 12:14:30 +00003899#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003900 #pragma omp critical (MagickCore_SpreadImage)
3901#endif
3902 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3903 if (proceed == MagickFalse)
3904 status=MagickFalse;
3905 }
3906 }
cristy9f7e7cb2011-03-26 00:49:57 +00003907 spread_view=DestroyCacheView(spread_view);
cristy3ed852e2009-09-05 21:47:34 +00003908 image_view=DestroyCacheView(image_view);
3909 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00003910 return(spread_image);
3911}
3912
3913/*
3914%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3915% %
3916% %
3917% %
cristy0834d642011-03-18 18:26:08 +00003918% S t a t i s t i c I m a g e %
3919% %
3920% %
3921% %
3922%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3923%
3924% StatisticImage() makes each pixel the min / max / median / mode / etc. of
cristy8d752042011-03-19 01:00:36 +00003925% the neighborhood of the specified width and height.
cristy0834d642011-03-18 18:26:08 +00003926%
3927% The format of the StatisticImage method is:
3928%
3929% Image *StatisticImage(const Image *image,const StatisticType type,
cristy95c38342011-03-18 22:39:51 +00003930% const size_t width,const size_t height,ExceptionInfo *exception)
cristy0834d642011-03-18 18:26:08 +00003931%
3932% A description of each parameter follows:
3933%
3934% o image: the image.
3935%
cristy0834d642011-03-18 18:26:08 +00003936% o type: the statistic type (median, mode, etc.).
3937%
cristy95c38342011-03-18 22:39:51 +00003938% o width: the width of the pixel neighborhood.
3939%
3940% o height: the height of the pixel neighborhood.
cristy0834d642011-03-18 18:26:08 +00003941%
3942% o exception: return any errors or warnings in this structure.
3943%
3944*/
3945
cristy733678d2011-03-18 21:29:28 +00003946#define ListChannels 5
3947
3948typedef struct _ListNode
3949{
3950 size_t
3951 next[9],
3952 count,
3953 signature;
3954} ListNode;
3955
3956typedef struct _SkipList
3957{
3958 ssize_t
3959 level;
3960
3961 ListNode
3962 *nodes;
3963} SkipList;
3964
3965typedef struct _PixelList
3966{
3967 size_t
cristy6fc86bb2011-03-18 23:45:16 +00003968 length,
cristy733678d2011-03-18 21:29:28 +00003969 seed,
3970 signature;
3971
3972 SkipList
3973 lists[ListChannels];
3974} PixelList;
3975
3976static PixelList *DestroyPixelList(PixelList *pixel_list)
3977{
3978 register ssize_t
3979 i;
3980
3981 if (pixel_list == (PixelList *) NULL)
3982 return((PixelList *) NULL);
3983 for (i=0; i < ListChannels; i++)
3984 if (pixel_list->lists[i].nodes != (ListNode *) NULL)
3985 pixel_list->lists[i].nodes=(ListNode *) RelinquishMagickMemory(
3986 pixel_list->lists[i].nodes);
3987 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
3988 return(pixel_list);
3989}
3990
3991static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
3992{
3993 register ssize_t
3994 i;
3995
3996 assert(pixel_list != (PixelList **) NULL);
3997 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3998 if (pixel_list[i] != (PixelList *) NULL)
3999 pixel_list[i]=DestroyPixelList(pixel_list[i]);
4000 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
4001 return(pixel_list);
4002}
4003
cristy6fc86bb2011-03-18 23:45:16 +00004004static PixelList *AcquirePixelList(const size_t width,const size_t height)
cristy733678d2011-03-18 21:29:28 +00004005{
4006 PixelList
4007 *pixel_list;
4008
4009 register ssize_t
4010 i;
4011
4012 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
4013 if (pixel_list == (PixelList *) NULL)
4014 return(pixel_list);
4015 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
cristy6fc86bb2011-03-18 23:45:16 +00004016 pixel_list->length=width*height;
cristy733678d2011-03-18 21:29:28 +00004017 for (i=0; i < ListChannels; i++)
4018 {
4019 pixel_list->lists[i].nodes=(ListNode *) AcquireQuantumMemory(65537UL,
4020 sizeof(*pixel_list->lists[i].nodes));
4021 if (pixel_list->lists[i].nodes == (ListNode *) NULL)
4022 return(DestroyPixelList(pixel_list));
4023 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
4024 sizeof(*pixel_list->lists[i].nodes));
4025 }
4026 pixel_list->signature=MagickSignature;
4027 return(pixel_list);
4028}
4029
cristy6fc86bb2011-03-18 23:45:16 +00004030static PixelList **AcquirePixelListThreadSet(const size_t width,
4031 const size_t height)
cristy733678d2011-03-18 21:29:28 +00004032{
4033 PixelList
4034 **pixel_list;
4035
4036 register ssize_t
4037 i;
4038
4039 size_t
4040 number_threads;
4041
4042 number_threads=GetOpenMPMaximumThreads();
4043 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
4044 sizeof(*pixel_list));
4045 if (pixel_list == (PixelList **) NULL)
4046 return((PixelList **) NULL);
4047 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
4048 for (i=0; i < (ssize_t) number_threads; i++)
4049 {
cristy6fc86bb2011-03-18 23:45:16 +00004050 pixel_list[i]=AcquirePixelList(width,height);
cristy733678d2011-03-18 21:29:28 +00004051 if (pixel_list[i] == (PixelList *) NULL)
4052 return(DestroyPixelListThreadSet(pixel_list));
4053 }
4054 return(pixel_list);
4055}
4056
4057static void AddNodePixelList(PixelList *pixel_list,const ssize_t channel,
4058 const size_t color)
4059{
4060 register SkipList
4061 *list;
4062
4063 register ssize_t
4064 level;
4065
4066 size_t
4067 search,
4068 update[9];
4069
4070 /*
4071 Initialize the node.
4072 */
4073 list=pixel_list->lists+channel;
4074 list->nodes[color].signature=pixel_list->signature;
4075 list->nodes[color].count=1;
4076 /*
4077 Determine where it belongs in the list.
4078 */
4079 search=65536UL;
4080 for (level=list->level; level >= 0; level--)
4081 {
4082 while (list->nodes[search].next[level] < color)
4083 search=list->nodes[search].next[level];
4084 update[level]=search;
4085 }
4086 /*
4087 Generate a pseudo-random level for this node.
4088 */
4089 for (level=0; ; level++)
4090 {
4091 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
4092 if ((pixel_list->seed & 0x300) != 0x300)
4093 break;
4094 }
4095 if (level > 8)
4096 level=8;
4097 if (level > (list->level+2))
4098 level=list->level+2;
4099 /*
4100 If we're raising the list's level, link back to the root node.
4101 */
4102 while (level > list->level)
4103 {
4104 list->level++;
4105 update[list->level]=65536UL;
4106 }
4107 /*
4108 Link the node into the skip-list.
4109 */
4110 do
4111 {
4112 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
4113 list->nodes[update[level]].next[level]=color;
cristy3cba8ca2011-03-19 01:29:12 +00004114 } while (level-- > 0);
cristy733678d2011-03-18 21:29:28 +00004115}
4116
cristy4c08aed2011-07-01 19:47:50 +00004117static PixelInfo GetMaximumPixelList(PixelList *pixel_list)
cristy6fc86bb2011-03-18 23:45:16 +00004118{
cristy4c08aed2011-07-01 19:47:50 +00004119 PixelInfo
cristy6fc86bb2011-03-18 23:45:16 +00004120 pixel;
4121
4122 register SkipList
4123 *list;
4124
4125 register ssize_t
4126 channel;
4127
4128 size_t
cristyd76c51e2011-03-26 00:21:26 +00004129 color,
4130 maximum;
cristy49f37242011-03-22 18:18:23 +00004131
4132 ssize_t
cristy6fc86bb2011-03-18 23:45:16 +00004133 count;
4134
4135 unsigned short
cristyd76c51e2011-03-26 00:21:26 +00004136 channels[ListChannels];
cristy6fc86bb2011-03-18 23:45:16 +00004137
4138 /*
4139 Find the maximum value for each of the color.
4140 */
4141 for (channel=0; channel < 5; channel++)
4142 {
4143 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004144 color=65536L;
cristy6fc86bb2011-03-18 23:45:16 +00004145 count=0;
cristy49f37242011-03-22 18:18:23 +00004146 maximum=list->nodes[color].next[0];
cristy6fc86bb2011-03-18 23:45:16 +00004147 do
4148 {
4149 color=list->nodes[color].next[0];
cristy49f37242011-03-22 18:18:23 +00004150 if (color > maximum)
4151 maximum=color;
cristy6fc86bb2011-03-18 23:45:16 +00004152 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004153 } while (count < (ssize_t) pixel_list->length);
cristy49f37242011-03-22 18:18:23 +00004154 channels[channel]=(unsigned short) maximum;
4155 }
cristy4c08aed2011-07-01 19:47:50 +00004156 GetPixelInfo((const Image *) NULL,&pixel);
cristy49f37242011-03-22 18:18:23 +00004157 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4158 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4159 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004160 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4161 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy49f37242011-03-22 18:18:23 +00004162 return(pixel);
4163}
4164
cristy4c08aed2011-07-01 19:47:50 +00004165static PixelInfo GetMeanPixelList(PixelList *pixel_list)
cristy49f37242011-03-22 18:18:23 +00004166{
cristy4c08aed2011-07-01 19:47:50 +00004167 PixelInfo
cristy49f37242011-03-22 18:18:23 +00004168 pixel;
4169
cristy80a99a32011-03-30 01:30:23 +00004170 MagickRealType
4171 sum;
4172
cristy49f37242011-03-22 18:18:23 +00004173 register SkipList
4174 *list;
4175
4176 register ssize_t
4177 channel;
4178
4179 size_t
cristy80a99a32011-03-30 01:30:23 +00004180 color;
cristy49f37242011-03-22 18:18:23 +00004181
4182 ssize_t
4183 count;
4184
4185 unsigned short
4186 channels[ListChannels];
4187
4188 /*
4189 Find the mean value for each of the color.
4190 */
4191 for (channel=0; channel < 5; channel++)
4192 {
4193 list=pixel_list->lists+channel;
4194 color=65536L;
4195 count=0;
cristy80a99a32011-03-30 01:30:23 +00004196 sum=0.0;
cristy49f37242011-03-22 18:18:23 +00004197 do
4198 {
4199 color=list->nodes[color].next[0];
cristy80a99a32011-03-30 01:30:23 +00004200 sum+=(MagickRealType) list->nodes[color].count*color;
cristy49f37242011-03-22 18:18:23 +00004201 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004202 } while (count < (ssize_t) pixel_list->length);
cristy80a99a32011-03-30 01:30:23 +00004203 sum/=pixel_list->length;
4204 channels[channel]=(unsigned short) sum;
cristy6fc86bb2011-03-18 23:45:16 +00004205 }
cristy4c08aed2011-07-01 19:47:50 +00004206 GetPixelInfo((const Image *) NULL,&pixel);
cristy6fc86bb2011-03-18 23:45:16 +00004207 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4208 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4209 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004210 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4211 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy6fc86bb2011-03-18 23:45:16 +00004212 return(pixel);
4213}
4214
cristy4c08aed2011-07-01 19:47:50 +00004215static PixelInfo GetMedianPixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004216{
cristy4c08aed2011-07-01 19:47:50 +00004217 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004218 pixel;
4219
4220 register SkipList
4221 *list;
4222
4223 register ssize_t
4224 channel;
4225
4226 size_t
cristy49f37242011-03-22 18:18:23 +00004227 color;
4228
4229 ssize_t
cristy733678d2011-03-18 21:29:28 +00004230 count;
4231
4232 unsigned short
4233 channels[ListChannels];
4234
4235 /*
4236 Find the median value for each of the color.
4237 */
cristy733678d2011-03-18 21:29:28 +00004238 for (channel=0; channel < 5; channel++)
4239 {
4240 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004241 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004242 count=0;
4243 do
4244 {
4245 color=list->nodes[color].next[0];
4246 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004247 } while (count <= (ssize_t) (pixel_list->length >> 1));
cristy6fc86bb2011-03-18 23:45:16 +00004248 channels[channel]=(unsigned short) color;
4249 }
cristy4c08aed2011-07-01 19:47:50 +00004250 GetPixelInfo((const Image *) NULL,&pixel);
cristy6fc86bb2011-03-18 23:45:16 +00004251 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4252 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4253 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004254 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4255 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy6fc86bb2011-03-18 23:45:16 +00004256 return(pixel);
4257}
4258
cristy4c08aed2011-07-01 19:47:50 +00004259static PixelInfo GetMinimumPixelList(PixelList *pixel_list)
cristy6fc86bb2011-03-18 23:45:16 +00004260{
cristy4c08aed2011-07-01 19:47:50 +00004261 PixelInfo
cristy6fc86bb2011-03-18 23:45:16 +00004262 pixel;
4263
4264 register SkipList
4265 *list;
4266
4267 register ssize_t
4268 channel;
4269
4270 size_t
cristyd76c51e2011-03-26 00:21:26 +00004271 color,
4272 minimum;
cristy6fc86bb2011-03-18 23:45:16 +00004273
cristy49f37242011-03-22 18:18:23 +00004274 ssize_t
4275 count;
4276
cristy6fc86bb2011-03-18 23:45:16 +00004277 unsigned short
cristyd76c51e2011-03-26 00:21:26 +00004278 channels[ListChannels];
cristy6fc86bb2011-03-18 23:45:16 +00004279
4280 /*
4281 Find the minimum value for each of the color.
4282 */
4283 for (channel=0; channel < 5; channel++)
4284 {
4285 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004286 count=0;
cristy6fc86bb2011-03-18 23:45:16 +00004287 color=65536UL;
cristy49f37242011-03-22 18:18:23 +00004288 minimum=list->nodes[color].next[0];
4289 do
4290 {
4291 color=list->nodes[color].next[0];
4292 if (color < minimum)
4293 minimum=color;
4294 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004295 } while (count < (ssize_t) pixel_list->length);
cristy49f37242011-03-22 18:18:23 +00004296 channels[channel]=(unsigned short) minimum;
cristy733678d2011-03-18 21:29:28 +00004297 }
cristy4c08aed2011-07-01 19:47:50 +00004298 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004299 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4300 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4301 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004302 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4303 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy733678d2011-03-18 21:29:28 +00004304 return(pixel);
4305}
4306
cristy4c08aed2011-07-01 19:47:50 +00004307static PixelInfo GetModePixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004308{
cristy4c08aed2011-07-01 19:47:50 +00004309 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004310 pixel;
4311
4312 register SkipList
4313 *list;
4314
4315 register ssize_t
4316 channel;
4317
4318 size_t
4319 color,
cristy733678d2011-03-18 21:29:28 +00004320 max_count,
cristy6fc86bb2011-03-18 23:45:16 +00004321 mode;
cristy733678d2011-03-18 21:29:28 +00004322
cristy49f37242011-03-22 18:18:23 +00004323 ssize_t
4324 count;
4325
cristy733678d2011-03-18 21:29:28 +00004326 unsigned short
4327 channels[5];
4328
4329 /*
glennrp30d2dc62011-06-25 03:17:16 +00004330 Make each pixel the 'predominant color' of the specified neighborhood.
cristy733678d2011-03-18 21:29:28 +00004331 */
cristy733678d2011-03-18 21:29:28 +00004332 for (channel=0; channel < 5; channel++)
4333 {
4334 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004335 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004336 mode=color;
4337 max_count=list->nodes[mode].count;
4338 count=0;
4339 do
4340 {
4341 color=list->nodes[color].next[0];
4342 if (list->nodes[color].count > max_count)
4343 {
4344 mode=color;
4345 max_count=list->nodes[mode].count;
4346 }
4347 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004348 } while (count < (ssize_t) pixel_list->length);
cristy733678d2011-03-18 21:29:28 +00004349 channels[channel]=(unsigned short) mode;
4350 }
cristy4c08aed2011-07-01 19:47:50 +00004351 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004352 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4353 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4354 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004355 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4356 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy733678d2011-03-18 21:29:28 +00004357 return(pixel);
4358}
4359
cristy4c08aed2011-07-01 19:47:50 +00004360static PixelInfo GetNonpeakPixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004361{
cristy4c08aed2011-07-01 19:47:50 +00004362 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004363 pixel;
4364
4365 register SkipList
4366 *list;
4367
4368 register ssize_t
4369 channel;
4370
4371 size_t
cristy733678d2011-03-18 21:29:28 +00004372 color,
cristy733678d2011-03-18 21:29:28 +00004373 next,
4374 previous;
4375
cristy49f37242011-03-22 18:18:23 +00004376 ssize_t
4377 count;
4378
cristy733678d2011-03-18 21:29:28 +00004379 unsigned short
4380 channels[5];
4381
4382 /*
cristy49f37242011-03-22 18:18:23 +00004383 Finds the non peak value for each of the colors.
cristy733678d2011-03-18 21:29:28 +00004384 */
cristy733678d2011-03-18 21:29:28 +00004385 for (channel=0; channel < 5; channel++)
4386 {
4387 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004388 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004389 next=list->nodes[color].next[0];
4390 count=0;
4391 do
4392 {
4393 previous=color;
4394 color=next;
4395 next=list->nodes[color].next[0];
4396 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004397 } while (count <= (ssize_t) (pixel_list->length >> 1));
cristy733678d2011-03-18 21:29:28 +00004398 if ((previous == 65536UL) && (next != 65536UL))
4399 color=next;
4400 else
4401 if ((previous != 65536UL) && (next == 65536UL))
4402 color=previous;
4403 channels[channel]=(unsigned short) color;
4404 }
cristy4c08aed2011-07-01 19:47:50 +00004405 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004406 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4407 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4408 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004409 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4410 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy733678d2011-03-18 21:29:28 +00004411 return(pixel);
4412}
4413
cristy4c08aed2011-07-01 19:47:50 +00004414static PixelInfo GetStandardDeviationPixelList(PixelList *pixel_list)
cristy9a68cbb2011-03-29 00:51:23 +00004415{
cristy4c08aed2011-07-01 19:47:50 +00004416 PixelInfo
cristy9a68cbb2011-03-29 00:51:23 +00004417 pixel;
4418
cristy80a99a32011-03-30 01:30:23 +00004419 MagickRealType
4420 sum,
4421 sum_squared;
4422
cristy9a68cbb2011-03-29 00:51:23 +00004423 register SkipList
4424 *list;
4425
4426 register ssize_t
4427 channel;
4428
4429 size_t
cristy80a99a32011-03-30 01:30:23 +00004430 color;
cristy9a68cbb2011-03-29 00:51:23 +00004431
4432 ssize_t
4433 count;
4434
4435 unsigned short
4436 channels[ListChannels];
4437
4438 /*
cristy80a99a32011-03-30 01:30:23 +00004439 Find the standard-deviation value for each of the color.
cristy9a68cbb2011-03-29 00:51:23 +00004440 */
4441 for (channel=0; channel < 5; channel++)
4442 {
4443 list=pixel_list->lists+channel;
4444 color=65536L;
4445 count=0;
cristy80a99a32011-03-30 01:30:23 +00004446 sum=0.0;
4447 sum_squared=0.0;
cristy9a68cbb2011-03-29 00:51:23 +00004448 do
4449 {
cristy80a99a32011-03-30 01:30:23 +00004450 register ssize_t
4451 i;
4452
cristy9a68cbb2011-03-29 00:51:23 +00004453 color=list->nodes[color].next[0];
cristy80a99a32011-03-30 01:30:23 +00004454 sum+=(MagickRealType) list->nodes[color].count*color;
4455 for (i=0; i < (ssize_t) list->nodes[color].count; i++)
4456 sum_squared+=((MagickRealType) color)*((MagickRealType) color);
cristy9a68cbb2011-03-29 00:51:23 +00004457 count+=list->nodes[color].count;
4458 } while (count < (ssize_t) pixel_list->length);
cristy80a99a32011-03-30 01:30:23 +00004459 sum/=pixel_list->length;
4460 sum_squared/=pixel_list->length;
4461 channels[channel]=(unsigned short) sqrt(sum_squared-(sum*sum));
cristy9a68cbb2011-03-29 00:51:23 +00004462 }
cristy4c08aed2011-07-01 19:47:50 +00004463 GetPixelInfo((const Image *) NULL,&pixel);
cristy9a68cbb2011-03-29 00:51:23 +00004464 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4465 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4466 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004467 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4468 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy9a68cbb2011-03-29 00:51:23 +00004469 return(pixel);
4470}
4471
cristy4c08aed2011-07-01 19:47:50 +00004472static inline void InsertPixelList(const Image *image,const Quantum *pixel,
4473 PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004474{
4475 size_t
4476 signature;
4477
4478 unsigned short
4479 index;
4480
cristy4c08aed2011-07-01 19:47:50 +00004481 index=ScaleQuantumToShort(GetPixelRed(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004482 signature=pixel_list->lists[0].nodes[index].signature;
4483 if (signature == pixel_list->signature)
4484 pixel_list->lists[0].nodes[index].count++;
4485 else
4486 AddNodePixelList(pixel_list,0,index);
cristy4c08aed2011-07-01 19:47:50 +00004487 index=ScaleQuantumToShort(GetPixelGreen(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004488 signature=pixel_list->lists[1].nodes[index].signature;
4489 if (signature == pixel_list->signature)
4490 pixel_list->lists[1].nodes[index].count++;
4491 else
4492 AddNodePixelList(pixel_list,1,index);
cristy4c08aed2011-07-01 19:47:50 +00004493 index=ScaleQuantumToShort(GetPixelBlue(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004494 signature=pixel_list->lists[2].nodes[index].signature;
4495 if (signature == pixel_list->signature)
4496 pixel_list->lists[2].nodes[index].count++;
4497 else
4498 AddNodePixelList(pixel_list,2,index);
cristy4c08aed2011-07-01 19:47:50 +00004499 index=ScaleQuantumToShort(GetPixelAlpha(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004500 signature=pixel_list->lists[3].nodes[index].signature;
4501 if (signature == pixel_list->signature)
4502 pixel_list->lists[3].nodes[index].count++;
4503 else
4504 AddNodePixelList(pixel_list,3,index);
4505 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004506 index=ScaleQuantumToShort(GetPixelBlack(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004507 signature=pixel_list->lists[4].nodes[index].signature;
4508 if (signature == pixel_list->signature)
4509 pixel_list->lists[4].nodes[index].count++;
4510 else
4511 AddNodePixelList(pixel_list,4,index);
4512}
4513
cristy80c99742011-04-04 14:46:39 +00004514static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
4515{
4516 if (x < 0)
4517 return(-x);
4518 return(x);
4519}
4520
cristy733678d2011-03-18 21:29:28 +00004521static void ResetPixelList(PixelList *pixel_list)
4522{
4523 int
4524 level;
4525
4526 register ListNode
4527 *root;
4528
4529 register SkipList
4530 *list;
4531
4532 register ssize_t
4533 channel;
4534
4535 /*
4536 Reset the skip-list.
4537 */
4538 for (channel=0; channel < 5; channel++)
4539 {
4540 list=pixel_list->lists+channel;
4541 root=list->nodes+65536UL;
4542 list->level=0;
4543 for (level=0; level < 9; level++)
4544 root->next[level]=65536UL;
4545 }
4546 pixel_list->seed=pixel_list->signature++;
4547}
4548
cristy0834d642011-03-18 18:26:08 +00004549MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
cristy95c38342011-03-18 22:39:51 +00004550 const size_t width,const size_t height,ExceptionInfo *exception)
cristy0834d642011-03-18 18:26:08 +00004551{
cristy3cba8ca2011-03-19 01:29:12 +00004552#define StatisticWidth \
cristyd76c51e2011-03-26 00:21:26 +00004553 (width == 0 ? GetOptimalKernelWidth2D((double) width,0.5) : width)
cristy3cba8ca2011-03-19 01:29:12 +00004554#define StatisticHeight \
cristyd76c51e2011-03-26 00:21:26 +00004555 (height == 0 ? GetOptimalKernelWidth2D((double) height,0.5) : height)
cristy0834d642011-03-18 18:26:08 +00004556#define StatisticImageTag "Statistic/Image"
4557
4558 CacheView
4559 *image_view,
4560 *statistic_view;
4561
4562 Image
4563 *statistic_image;
4564
4565 MagickBooleanType
4566 status;
4567
4568 MagickOffsetType
4569 progress;
4570
4571 PixelList
4572 **restrict pixel_list;
4573
cristy0834d642011-03-18 18:26:08 +00004574 ssize_t
4575 y;
4576
4577 /*
4578 Initialize statistics image attributes.
4579 */
4580 assert(image != (Image *) NULL);
4581 assert(image->signature == MagickSignature);
4582 if (image->debug != MagickFalse)
4583 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4584 assert(exception != (ExceptionInfo *) NULL);
4585 assert(exception->signature == MagickSignature);
cristy0834d642011-03-18 18:26:08 +00004586 statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4587 exception);
4588 if (statistic_image == (Image *) NULL)
4589 return((Image *) NULL);
4590 if (SetImageStorageClass(statistic_image,DirectClass) == MagickFalse)
4591 {
4592 InheritException(exception,&statistic_image->exception);
4593 statistic_image=DestroyImage(statistic_image);
4594 return((Image *) NULL);
4595 }
cristy6fc86bb2011-03-18 23:45:16 +00004596 pixel_list=AcquirePixelListThreadSet(StatisticWidth,StatisticHeight);
cristy0834d642011-03-18 18:26:08 +00004597 if (pixel_list == (PixelList **) NULL)
4598 {
4599 statistic_image=DestroyImage(statistic_image);
4600 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4601 }
4602 /*
cristy8d752042011-03-19 01:00:36 +00004603 Make each pixel the min / max / median / mode / etc. of the neighborhood.
cristy0834d642011-03-18 18:26:08 +00004604 */
4605 status=MagickTrue;
4606 progress=0;
4607 image_view=AcquireCacheView(image);
4608 statistic_view=AcquireCacheView(statistic_image);
4609#if defined(MAGICKCORE_OPENMP_SUPPORT)
4610 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4611#endif
4612 for (y=0; y < (ssize_t) statistic_image->rows; y++)
4613 {
4614 const int
4615 id = GetOpenMPThreadId();
4616
cristy4c08aed2011-07-01 19:47:50 +00004617 register const Quantum
cristy0834d642011-03-18 18:26:08 +00004618 *restrict p;
4619
cristy4c08aed2011-07-01 19:47:50 +00004620 register Quantum
cristy0834d642011-03-18 18:26:08 +00004621 *restrict q;
4622
4623 register ssize_t
4624 x;
4625
4626 if (status == MagickFalse)
4627 continue;
cristy6fc86bb2011-03-18 23:45:16 +00004628 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) StatisticWidth/2L),y-
4629 (ssize_t) (StatisticHeight/2L),image->columns+StatisticWidth,
4630 StatisticHeight,exception);
cristy3cba8ca2011-03-19 01:29:12 +00004631 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004632 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy0834d642011-03-18 18:26:08 +00004633 {
4634 status=MagickFalse;
4635 continue;
4636 }
cristy0834d642011-03-18 18:26:08 +00004637 for (x=0; x < (ssize_t) statistic_image->columns; x++)
4638 {
cristy4c08aed2011-07-01 19:47:50 +00004639 PixelInfo
cristy0834d642011-03-18 18:26:08 +00004640 pixel;
4641
cristy4c08aed2011-07-01 19:47:50 +00004642 register const Quantum
cristy6e3026a2011-03-19 00:54:38 +00004643 *restrict r;
4644
cristy0834d642011-03-18 18:26:08 +00004645 register ssize_t
4646 u,
4647 v;
4648
4649 r=p;
cristy0834d642011-03-18 18:26:08 +00004650 ResetPixelList(pixel_list[id]);
cristy6e4c3292011-03-19 00:53:55 +00004651 for (v=0; v < (ssize_t) StatisticHeight; v++)
cristy0834d642011-03-18 18:26:08 +00004652 {
cristy6e4c3292011-03-19 00:53:55 +00004653 for (u=0; u < (ssize_t) StatisticWidth; u++)
cristyed231572011-07-14 02:18:59 +00004654 InsertPixelList(image,r+u*GetPixelChannels(image),pixel_list[id]);
4655 r+=(image->columns+StatisticWidth)*GetPixelChannels(image);
cristy0834d642011-03-18 18:26:08 +00004656 }
cristy4c08aed2011-07-01 19:47:50 +00004657 GetPixelInfo(image,&pixel);
cristy490408a2011-07-07 14:42:05 +00004658 SetPixelInfo(image,p+(StatisticWidth*StatisticHeight/2)*
cristyed231572011-07-14 02:18:59 +00004659 GetPixelChannels(image),&pixel);
cristy0834d642011-03-18 18:26:08 +00004660 switch (type)
4661 {
cristy80c99742011-04-04 14:46:39 +00004662 case GradientStatistic:
4663 {
cristy4c08aed2011-07-01 19:47:50 +00004664 PixelInfo
cristy80c99742011-04-04 14:46:39 +00004665 maximum,
4666 minimum;
4667
4668 minimum=GetMinimumPixelList(pixel_list[id]);
4669 maximum=GetMaximumPixelList(pixel_list[id]);
4670 pixel.red=MagickAbsoluteValue(maximum.red-minimum.red);
4671 pixel.green=MagickAbsoluteValue(maximum.green-minimum.green);
4672 pixel.blue=MagickAbsoluteValue(maximum.blue-minimum.blue);
cristy4c08aed2011-07-01 19:47:50 +00004673 pixel.alpha=MagickAbsoluteValue(maximum.alpha-minimum.alpha);
cristy80c99742011-04-04 14:46:39 +00004674 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004675 pixel.black=MagickAbsoluteValue(maximum.black-minimum.black);
cristy80c99742011-04-04 14:46:39 +00004676 break;
4677 }
cristy6fc86bb2011-03-18 23:45:16 +00004678 case MaximumStatistic:
4679 {
4680 pixel=GetMaximumPixelList(pixel_list[id]);
4681 break;
4682 }
cristy49f37242011-03-22 18:18:23 +00004683 case MeanStatistic:
4684 {
4685 pixel=GetMeanPixelList(pixel_list[id]);
4686 break;
4687 }
cristyf2ad14a2011-03-18 18:57:25 +00004688 case MedianStatistic:
cristy6fc86bb2011-03-18 23:45:16 +00004689 default:
cristyf2ad14a2011-03-18 18:57:25 +00004690 {
4691 pixel=GetMedianPixelList(pixel_list[id]);
4692 break;
4693 }
cristy6fc86bb2011-03-18 23:45:16 +00004694 case MinimumStatistic:
4695 {
4696 pixel=GetMinimumPixelList(pixel_list[id]);
4697 break;
4698 }
cristyf2ad14a2011-03-18 18:57:25 +00004699 case ModeStatistic:
4700 {
4701 pixel=GetModePixelList(pixel_list[id]);
4702 break;
4703 }
4704 case NonpeakStatistic:
4705 {
4706 pixel=GetNonpeakPixelList(pixel_list[id]);
4707 break;
4708 }
cristy9a68cbb2011-03-29 00:51:23 +00004709 case StandardDeviationStatistic:
4710 {
4711 pixel=GetStandardDeviationPixelList(pixel_list[id]);
4712 break;
4713 }
cristy0834d642011-03-18 18:26:08 +00004714 }
cristyed231572011-07-14 02:18:59 +00004715 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004716 SetPixelRed(statistic_image,ClampToQuantum(pixel.red),q);
cristyed231572011-07-14 02:18:59 +00004717 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004718 SetPixelGreen(statistic_image,ClampToQuantum(pixel.green),q);
cristyed231572011-07-14 02:18:59 +00004719 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004720 SetPixelBlue(statistic_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00004721 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy0834d642011-03-18 18:26:08 +00004722 (image->colorspace == CMYKColorspace))
cristy490408a2011-07-07 14:42:05 +00004723 SetPixelBlack(statistic_image,ClampToQuantum(pixel.black),q);
cristyed231572011-07-14 02:18:59 +00004724 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00004725 (image->matte != MagickFalse))
cristy490408a2011-07-07 14:42:05 +00004726 SetPixelAlpha(statistic_image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004727 p+=GetPixelChannels(image);
4728 q+=GetPixelChannels(statistic_image);
cristy0834d642011-03-18 18:26:08 +00004729 }
4730 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
4731 status=MagickFalse;
4732 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4733 {
4734 MagickBooleanType
4735 proceed;
4736
4737#if defined(MAGICKCORE_OPENMP_SUPPORT)
4738 #pragma omp critical (MagickCore_StatisticImage)
4739#endif
4740 proceed=SetImageProgress(image,StatisticImageTag,progress++,
4741 image->rows);
4742 if (proceed == MagickFalse)
4743 status=MagickFalse;
4744 }
4745 }
4746 statistic_view=DestroyCacheView(statistic_view);
4747 image_view=DestroyCacheView(image_view);
4748 pixel_list=DestroyPixelListThreadSet(pixel_list);
4749 return(statistic_image);
4750}
4751
4752/*
4753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4754% %
4755% %
4756% %
cristy3ed852e2009-09-05 21:47:34 +00004757% U n s h a r p M a s k I m a g e %
4758% %
4759% %
4760% %
4761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4762%
4763% UnsharpMaskImage() sharpens one or more image channels. We convolve the
4764% image with a Gaussian operator of the given radius and standard deviation
4765% (sigma). For reasonable results, radius should be larger than sigma. Use a
4766% radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4767%
4768% The format of the UnsharpMaskImage method is:
4769%
4770% Image *UnsharpMaskImage(const Image *image,const double radius,
4771% const double sigma,const double amount,const double threshold,
4772% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004773%
4774% A description of each parameter follows:
4775%
4776% o image: the image.
4777%
cristy3ed852e2009-09-05 21:47:34 +00004778% o radius: the radius of the Gaussian, in pixels, not counting the center
4779% pixel.
4780%
4781% o sigma: the standard deviation of the Gaussian, in pixels.
4782%
4783% o amount: the percentage of the difference between the original and the
4784% blur image that is added back into the original.
4785%
4786% o threshold: the threshold in pixels needed to apply the diffence amount.
4787%
4788% o exception: return any errors or warnings in this structure.
4789%
4790*/
cristyf4ad9df2011-07-08 16:49:03 +00004791MagickExport Image *UnsharpMaskImage(const Image *image,
4792 const double radius,const double sigma,const double amount,
4793 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004794{
4795#define SharpenImageTag "Sharpen/Image"
4796
cristyc4c8d132010-01-07 01:58:38 +00004797 CacheView
4798 *image_view,
4799 *unsharp_view;
4800
cristy3ed852e2009-09-05 21:47:34 +00004801 Image
4802 *unsharp_image;
4803
cristy3ed852e2009-09-05 21:47:34 +00004804 MagickBooleanType
4805 status;
4806
cristybb503372010-05-27 20:51:26 +00004807 MagickOffsetType
4808 progress;
4809
cristy4c08aed2011-07-01 19:47:50 +00004810 PixelInfo
cristyddd82202009-11-03 20:14:50 +00004811 bias;
cristy3ed852e2009-09-05 21:47:34 +00004812
4813 MagickRealType
4814 quantum_threshold;
4815
cristybb503372010-05-27 20:51:26 +00004816 ssize_t
4817 y;
4818
cristy3ed852e2009-09-05 21:47:34 +00004819 assert(image != (const Image *) NULL);
4820 assert(image->signature == MagickSignature);
4821 if (image->debug != MagickFalse)
4822 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4823 assert(exception != (ExceptionInfo *) NULL);
cristyf4ad9df2011-07-08 16:49:03 +00004824 unsharp_image=BlurImage(image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00004825 if (unsharp_image == (Image *) NULL)
4826 return((Image *) NULL);
4827 quantum_threshold=(MagickRealType) QuantumRange*threshold;
4828 /*
4829 Unsharp-mask image.
4830 */
4831 status=MagickTrue;
4832 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00004833 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00004834 image_view=AcquireCacheView(image);
4835 unsharp_view=AcquireCacheView(unsharp_image);
cristyb5d5f722009-11-04 03:03:49 +00004836#if defined(MAGICKCORE_OPENMP_SUPPORT)
4837 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004838#endif
cristybb503372010-05-27 20:51:26 +00004839 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004840 {
cristy4c08aed2011-07-01 19:47:50 +00004841 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004842 pixel;
4843
cristy4c08aed2011-07-01 19:47:50 +00004844 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004845 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004846
cristy4c08aed2011-07-01 19:47:50 +00004847 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004848 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004849
cristy117ff172010-08-15 21:35:32 +00004850 register ssize_t
4851 x;
4852
cristy3ed852e2009-09-05 21:47:34 +00004853 if (status == MagickFalse)
4854 continue;
4855 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4856 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4857 exception);
cristy4c08aed2011-07-01 19:47:50 +00004858 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004859 {
4860 status=MagickFalse;
4861 continue;
4862 }
cristyddd82202009-11-03 20:14:50 +00004863 pixel=bias;
cristybb503372010-05-27 20:51:26 +00004864 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004865 {
cristyed231572011-07-14 02:18:59 +00004866 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004867 {
cristy4c08aed2011-07-01 19:47:50 +00004868 pixel.red=GetPixelRed(image,p)-(MagickRealType) GetPixelRed(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004869 if (fabs(2.0*pixel.red) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004870 pixel.red=(MagickRealType) GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004871 else
cristy4c08aed2011-07-01 19:47:50 +00004872 pixel.red=(MagickRealType) GetPixelRed(image,p)+(pixel.red*amount);
4873 SetPixelRed(unsharp_image,ClampToQuantum(pixel.red),q);
cristy3ed852e2009-09-05 21:47:34 +00004874 }
cristyed231572011-07-14 02:18:59 +00004875 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004876 {
cristy4c08aed2011-07-01 19:47:50 +00004877 pixel.green=GetPixelGreen(image,p)-
4878 (MagickRealType) GetPixelGreen(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004879 if (fabs(2.0*pixel.green) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004880 pixel.green=(MagickRealType)
4881 GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004882 else
cristy4c08aed2011-07-01 19:47:50 +00004883 pixel.green=(MagickRealType)
4884 GetPixelGreen(image,p)+
4885 (pixel.green*amount);
4886 SetPixelGreen(unsharp_image,
4887 ClampToQuantum(pixel.green),q);
cristy3ed852e2009-09-05 21:47:34 +00004888 }
cristyed231572011-07-14 02:18:59 +00004889 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004890 {
cristy4c08aed2011-07-01 19:47:50 +00004891 pixel.blue=GetPixelBlue(image,p)-
4892 (MagickRealType) GetPixelBlue(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004893 if (fabs(2.0*pixel.blue) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004894 pixel.blue=(MagickRealType)
4895 GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004896 else
cristy4c08aed2011-07-01 19:47:50 +00004897 pixel.blue=(MagickRealType)
4898 GetPixelBlue(image,p)+(pixel.blue*amount);
4899 SetPixelBlue(unsharp_image,
4900 ClampToQuantum(pixel.blue),q);
cristy3ed852e2009-09-05 21:47:34 +00004901 }
cristyed231572011-07-14 02:18:59 +00004902 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00004903 (image->colorspace == CMYKColorspace))
4904 {
cristy4c08aed2011-07-01 19:47:50 +00004905 pixel.black=GetPixelBlack(image,p)-
4906 (MagickRealType) GetPixelBlack(image,q);
4907 if (fabs(2.0*pixel.black) < quantum_threshold)
4908 pixel.black=(MagickRealType)
4909 GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004910 else
cristy4c08aed2011-07-01 19:47:50 +00004911 pixel.black=(MagickRealType)
4912 GetPixelBlack(image,p)+(pixel.black*
4913 amount);
4914 SetPixelBlack(unsharp_image,
4915 ClampToQuantum(pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00004916 }
cristyed231572011-07-14 02:18:59 +00004917 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004918 {
4919 pixel.alpha=GetPixelAlpha(image,p)-
4920 (MagickRealType) GetPixelAlpha(image,q);
4921 if (fabs(2.0*pixel.alpha) < quantum_threshold)
4922 pixel.alpha=(MagickRealType)
4923 GetPixelAlpha(image,p);
4924 else
4925 pixel.alpha=GetPixelAlpha(image,p)+
4926 (pixel.alpha*amount);
4927 SetPixelAlpha(unsharp_image,
4928 ClampToQuantum(pixel.alpha),q);
4929 }
cristyed231572011-07-14 02:18:59 +00004930 p+=GetPixelChannels(image);
4931 q+=GetPixelChannels(unsharp_image);
cristy3ed852e2009-09-05 21:47:34 +00004932 }
4933 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4934 status=MagickFalse;
4935 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4936 {
4937 MagickBooleanType
4938 proceed;
4939
cristyb5d5f722009-11-04 03:03:49 +00004940#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00004941 #pragma omp critical (MagickCore_UnsharpMaskImage)
cristy3ed852e2009-09-05 21:47:34 +00004942#endif
4943 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4944 if (proceed == MagickFalse)
4945 status=MagickFalse;
4946 }
4947 }
4948 unsharp_image->type=image->type;
4949 unsharp_view=DestroyCacheView(unsharp_view);
4950 image_view=DestroyCacheView(image_view);
4951 if (status == MagickFalse)
4952 unsharp_image=DestroyImage(unsharp_image);
4953 return(unsharp_image);
4954}