blob: b9f296df844eca9755ed7fbba89fdc325fee96a3 [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)
cristy9aa95be2011-07-20 21:56:45 +0000404 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
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
cristy5af97fe2011-07-18 02:33:29 +00001301 ssize_t
1302 center;
1303
cristyfccdab92009-11-30 16:43:57 +00001304 if (status == MagickFalse)
1305 continue;
cristy105ba3c2011-07-18 02:28:38 +00001306 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
1307 (ssize_t) (kernel_info->height/2L),image->columns+kernel_info->width,
1308 kernel_info->height,exception);
cristy08429172011-07-14 17:18:16 +00001309 q=QueueCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
cristyfccdab92009-11-30 16:43:57 +00001310 exception);
cristy105ba3c2011-07-18 02:28:38 +00001311 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristyfccdab92009-11-30 16:43:57 +00001312 {
1313 status=MagickFalse;
1314 continue;
1315 }
cristy79cedc72011-07-25 00:41:15 +00001316 center=(ssize_t) GetPixelChannels(image)*(image->columns+
1317 kernel_info->width)*(kernel_info->height/2L)+GetPixelChannels(image)*
1318 (kernel_info->width/2);
cristybb503372010-05-27 20:51:26 +00001319 for (x=0; x < (ssize_t) image->columns; x++)
cristyfccdab92009-11-30 16:43:57 +00001320 {
cristybb503372010-05-27 20:51:26 +00001321 register ssize_t
cristyed231572011-07-14 02:18:59 +00001322 i;
cristyfccdab92009-11-30 16:43:57 +00001323
cristya30d9ba2011-07-23 21:00:48 +00001324 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristyed231572011-07-14 02:18:59 +00001325 {
cristyed231572011-07-14 02:18:59 +00001326 MagickRealType
cristy4e154852011-07-14 13:28:53 +00001327 alpha,
1328 gamma,
cristyed231572011-07-14 02:18:59 +00001329 pixel;
1330
1331 PixelChannel
1332 channel;
1333
1334 PixelTrait
1335 convolve_traits,
1336 traits;
1337
1338 register const double
1339 *restrict k;
1340
1341 register const Quantum
cristyeb52cde2011-07-17 01:52:52 +00001342 *restrict pixels;
cristyed231572011-07-14 02:18:59 +00001343
1344 register ssize_t
1345 u;
1346
1347 ssize_t
1348 v;
1349
cristy30301712011-07-18 15:06:51 +00001350 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
cristy4e154852011-07-14 13:28:53 +00001351 if (traits == UndefinedPixelTrait)
cristyed231572011-07-14 02:18:59 +00001352 continue;
cristy30301712011-07-18 15:06:51 +00001353 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
cristy4e154852011-07-14 13:28:53 +00001354 convolve_traits=GetPixelChannelMapTraits(convolve_image,channel);
1355 if (convolve_traits == UndefinedPixelTrait)
1356 continue;
1357 if ((convolve_traits & CopyPixelTrait) != 0)
1358 {
cristyf7dc44c2011-07-20 14:41:15 +00001359 q[channel]=p[center+i];
cristy4e154852011-07-14 13:28:53 +00001360 continue;
1361 }
cristy5e6be1e2011-07-16 01:23:39 +00001362 k=kernel_info->values;
cristy105ba3c2011-07-18 02:28:38 +00001363 pixels=p;
cristy0a922382011-07-16 15:30:34 +00001364 pixel=kernel_info->bias;
cristy42497c52011-08-03 18:36:30 +00001365 if (((convolve_traits & BlendPixelTrait) == 0) ||
1366 (GetPixelAlphaTraits(image) == UndefinedPixelTrait) ||
1367 (image->matte == MagickFalse))
cristyfccdab92009-11-30 16:43:57 +00001368 {
cristyed231572011-07-14 02:18:59 +00001369 /*
cristy4e154852011-07-14 13:28:53 +00001370 No alpha blending.
cristyed231572011-07-14 02:18:59 +00001371 */
cristyeb52cde2011-07-17 01:52:52 +00001372 for (v=0; v < (ssize_t) kernel_info->height; v++)
cristyfccdab92009-11-30 16:43:57 +00001373 {
cristyeb52cde2011-07-17 01:52:52 +00001374 for (u=0; u < (ssize_t) kernel_info->width; u++)
cristy175653e2011-07-10 23:13:34 +00001375 {
cristyeb52cde2011-07-17 01:52:52 +00001376 pixel+=(*k)*pixels[i];
cristyed231572011-07-14 02:18:59 +00001377 k++;
cristya30d9ba2011-07-23 21:00:48 +00001378 pixels+=GetPixelChannels(image);
cristy175653e2011-07-10 23:13:34 +00001379 }
cristya30d9ba2011-07-23 21:00:48 +00001380 pixels+=image->columns*GetPixelChannels(image);
cristy175653e2011-07-10 23:13:34 +00001381 }
cristyf7dc44c2011-07-20 14:41:15 +00001382 q[channel]=ClampToQuantum(pixel);
cristy4e154852011-07-14 13:28:53 +00001383 continue;
cristyed231572011-07-14 02:18:59 +00001384 }
cristy4e154852011-07-14 13:28:53 +00001385 /*
1386 Alpha blending.
1387 */
1388 gamma=0.0;
cristyeb52cde2011-07-17 01:52:52 +00001389 for (v=0; v < (ssize_t) kernel_info->height; v++)
cristy4e154852011-07-14 13:28:53 +00001390 {
cristyeb52cde2011-07-17 01:52:52 +00001391 for (u=0; u < (ssize_t) kernel_info->width; u++)
cristy4e154852011-07-14 13:28:53 +00001392 {
cristyeb52cde2011-07-17 01:52:52 +00001393 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1394 pixel+=(*k)*alpha*pixels[i];
cristy4e154852011-07-14 13:28:53 +00001395 gamma+=(*k)*alpha;
1396 k++;
cristya30d9ba2011-07-23 21:00:48 +00001397 pixels+=GetPixelChannels(image);
cristy4e154852011-07-14 13:28:53 +00001398 }
cristya30d9ba2011-07-23 21:00:48 +00001399 pixels+=image->columns*GetPixelChannels(image);
cristy4e154852011-07-14 13:28:53 +00001400 }
cristy1ce96d02011-07-14 17:57:24 +00001401 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristye7a41c92011-07-20 21:31:01 +00001402 q[channel]=ClampToQuantum(gamma*pixel);
cristyed231572011-07-14 02:18:59 +00001403 }
cristya30d9ba2011-07-23 21:00:48 +00001404 p+=GetPixelChannels(image);
1405 q+=GetPixelChannels(convolve_image);
cristyfccdab92009-11-30 16:43:57 +00001406 }
cristyed231572011-07-14 02:18:59 +00001407 if (SyncCacheViewAuthenticPixels(convolve_view,exception) == MagickFalse)
cristyfccdab92009-11-30 16:43:57 +00001408 status=MagickFalse;
1409 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1410 {
1411 MagickBooleanType
1412 proceed;
1413
1414#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001415 #pragma omp critical (MagickCore_ConvolveImage)
cristyfccdab92009-11-30 16:43:57 +00001416#endif
1417 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1418 if (proceed == MagickFalse)
1419 status=MagickFalse;
1420 }
1421 }
1422 convolve_image->type=image->type;
1423 convolve_view=DestroyCacheView(convolve_view);
1424 image_view=DestroyCacheView(image_view);
cristyfccdab92009-11-30 16:43:57 +00001425 if (status == MagickFalse)
1426 convolve_image=DestroyImage(convolve_image);
1427 return(convolve_image);
1428}
1429
1430/*
1431%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1432% %
1433% %
1434% %
cristy3ed852e2009-09-05 21:47:34 +00001435% D e s p e c k l e I m a g e %
1436% %
1437% %
1438% %
1439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1440%
1441% DespeckleImage() reduces the speckle noise in an image while perserving the
1442% edges of the original image.
1443%
1444% The format of the DespeckleImage method is:
1445%
1446% Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1447%
1448% A description of each parameter follows:
1449%
1450% o image: the image.
1451%
1452% o exception: return any errors or warnings in this structure.
1453%
1454*/
1455
cristybb503372010-05-27 20:51:26 +00001456static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1457 const size_t columns,const size_t rows,Quantum *f,Quantum *g,
cristy3ed852e2009-09-05 21:47:34 +00001458 const int polarity)
1459{
cristy3ed852e2009-09-05 21:47:34 +00001460 MagickRealType
1461 v;
1462
cristy3ed852e2009-09-05 21:47:34 +00001463 register Quantum
1464 *p,
1465 *q,
1466 *r,
1467 *s;
1468
cristy117ff172010-08-15 21:35:32 +00001469 register ssize_t
1470 x;
1471
1472 ssize_t
1473 y;
1474
cristy3ed852e2009-09-05 21:47:34 +00001475 assert(f != (Quantum *) NULL);
1476 assert(g != (Quantum *) NULL);
1477 p=f+(columns+2);
1478 q=g+(columns+2);
cristybb503372010-05-27 20:51:26 +00001479 r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1480 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001481 {
1482 p++;
1483 q++;
1484 r++;
1485 if (polarity > 0)
cristybb503372010-05-27 20:51:26 +00001486 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001487 {
1488 v=(MagickRealType) (*p);
1489 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1490 v+=ScaleCharToQuantum(1);
1491 *q=(Quantum) v;
1492 p++;
1493 q++;
1494 r++;
1495 }
1496 else
cristybb503372010-05-27 20:51:26 +00001497 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001498 {
1499 v=(MagickRealType) (*p);
1500 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
cristybb503372010-05-27 20:51:26 +00001501 v-=(ssize_t) ScaleCharToQuantum(1);
cristy3ed852e2009-09-05 21:47:34 +00001502 *q=(Quantum) v;
1503 p++;
1504 q++;
1505 r++;
1506 }
1507 p++;
1508 q++;
1509 r++;
1510 }
1511 p=f+(columns+2);
1512 q=g+(columns+2);
cristybb503372010-05-27 20:51:26 +00001513 r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1514 s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1515 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001516 {
1517 p++;
1518 q++;
1519 r++;
1520 s++;
1521 if (polarity > 0)
cristybb503372010-05-27 20:51:26 +00001522 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001523 {
1524 v=(MagickRealType) (*q);
1525 if (((MagickRealType) *s >=
1526 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1527 ((MagickRealType) *r > v))
1528 v+=ScaleCharToQuantum(1);
1529 *p=(Quantum) v;
1530 p++;
1531 q++;
1532 r++;
1533 s++;
1534 }
1535 else
cristybb503372010-05-27 20:51:26 +00001536 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001537 {
1538 v=(MagickRealType) (*q);
1539 if (((MagickRealType) *s <=
1540 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1541 ((MagickRealType) *r < v))
1542 v-=(MagickRealType) ScaleCharToQuantum(1);
1543 *p=(Quantum) v;
1544 p++;
1545 q++;
1546 r++;
1547 s++;
1548 }
1549 p++;
1550 q++;
1551 r++;
1552 s++;
1553 }
1554}
1555
1556MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1557{
1558#define DespeckleImageTag "Despeckle/Image"
1559
cristy2407fc22009-09-11 00:55:25 +00001560 CacheView
1561 *despeckle_view,
1562 *image_view;
1563
cristy3ed852e2009-09-05 21:47:34 +00001564 Image
1565 *despeckle_image;
1566
cristy3ed852e2009-09-05 21:47:34 +00001567 MagickBooleanType
1568 status;
1569
cristya58c3172011-02-19 19:23:11 +00001570 register ssize_t
1571 i;
1572
cristy3ed852e2009-09-05 21:47:34 +00001573 Quantum
cristy65b9f392011-02-22 14:22:54 +00001574 *restrict buffers,
1575 *restrict pixels;
cristy3ed852e2009-09-05 21:47:34 +00001576
1577 size_t
cristya58c3172011-02-19 19:23:11 +00001578 length,
1579 number_channels;
cristy117ff172010-08-15 21:35:32 +00001580
cristybb503372010-05-27 20:51:26 +00001581 static const ssize_t
cristy691a29e2009-09-11 00:44:10 +00001582 X[4] = {0, 1, 1,-1},
1583 Y[4] = {1, 0, 1, 1};
cristy3ed852e2009-09-05 21:47:34 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 /*
1586 Allocate despeckled image.
1587 */
1588 assert(image != (const Image *) NULL);
1589 assert(image->signature == MagickSignature);
1590 if (image->debug != MagickFalse)
1591 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1592 assert(exception != (ExceptionInfo *) NULL);
1593 assert(exception->signature == MagickSignature);
1594 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1595 exception);
1596 if (despeckle_image == (Image *) NULL)
1597 return((Image *) NULL);
1598 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1599 {
1600 InheritException(exception,&despeckle_image->exception);
1601 despeckle_image=DestroyImage(despeckle_image);
1602 return((Image *) NULL);
1603 }
1604 /*
1605 Allocate image buffers.
1606 */
1607 length=(size_t) ((image->columns+2)*(image->rows+2));
cristy65b9f392011-02-22 14:22:54 +00001608 pixels=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1609 buffers=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1610 if ((pixels == (Quantum *) NULL) || (buffers == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001611 {
cristy65b9f392011-02-22 14:22:54 +00001612 if (buffers != (Quantum *) NULL)
1613 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1614 if (pixels != (Quantum *) NULL)
1615 pixels=(Quantum *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001616 despeckle_image=DestroyImage(despeckle_image);
1617 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1618 }
1619 /*
1620 Reduce speckle in the image.
1621 */
1622 status=MagickTrue;
cristy109695a2011-02-19 19:38:14 +00001623 number_channels=(size_t) (image->colorspace == CMYKColorspace ? 5 : 4);
cristy3ed852e2009-09-05 21:47:34 +00001624 image_view=AcquireCacheView(image);
1625 despeckle_view=AcquireCacheView(despeckle_image);
cristy8df3d002011-02-19 19:40:59 +00001626 for (i=0; i < (ssize_t) number_channels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001627 {
cristy3ed852e2009-09-05 21:47:34 +00001628 register Quantum
1629 *buffer,
1630 *pixel;
1631
cristyc1488b52011-02-19 18:54:15 +00001632 register ssize_t
cristya58c3172011-02-19 19:23:11 +00001633 k,
cristyc1488b52011-02-19 18:54:15 +00001634 x;
1635
cristy117ff172010-08-15 21:35:32 +00001636 ssize_t
1637 j,
1638 y;
1639
cristy3ed852e2009-09-05 21:47:34 +00001640 if (status == MagickFalse)
1641 continue;
cristy65b9f392011-02-22 14:22:54 +00001642 pixel=pixels;
cristy3ed852e2009-09-05 21:47:34 +00001643 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
cristy65b9f392011-02-22 14:22:54 +00001644 buffer=buffers;
cristybb503372010-05-27 20:51:26 +00001645 j=(ssize_t) image->columns+2;
1646 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001647 {
cristy4c08aed2011-07-01 19:47:50 +00001648 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001649 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001650
1651 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001652 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001653 break;
1654 j++;
cristybb503372010-05-27 20:51:26 +00001655 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001656 {
cristya58c3172011-02-19 19:23:11 +00001657 switch (i)
cristy3ed852e2009-09-05 21:47:34 +00001658 {
cristy4c08aed2011-07-01 19:47:50 +00001659 case 0: pixel[j]=GetPixelRed(image,p); break;
1660 case 1: pixel[j]=GetPixelGreen(image,p); break;
1661 case 2: pixel[j]=GetPixelBlue(image,p); break;
1662 case 3: pixel[j]=GetPixelAlpha(image,p); break;
1663 case 4: pixel[j]=GetPixelBlack(image,p); break;
cristy3ed852e2009-09-05 21:47:34 +00001664 default: break;
1665 }
cristyed231572011-07-14 02:18:59 +00001666 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001667 j++;
1668 }
1669 j++;
1670 }
cristy3ed852e2009-09-05 21:47:34 +00001671 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
cristya58c3172011-02-19 19:23:11 +00001672 for (k=0; k < 4; k++)
cristy3ed852e2009-09-05 21:47:34 +00001673 {
cristya58c3172011-02-19 19:23:11 +00001674 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,1);
1675 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,1);
1676 Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,-1);
1677 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,-1);
cristy3ed852e2009-09-05 21:47:34 +00001678 }
cristybb503372010-05-27 20:51:26 +00001679 j=(ssize_t) image->columns+2;
1680 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001681 {
1682 MagickBooleanType
1683 sync;
1684
cristy4c08aed2011-07-01 19:47:50 +00001685 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001686 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001687
1688 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1689 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001690 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001691 break;
1692 j++;
cristybb503372010-05-27 20:51:26 +00001693 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001694 {
cristya58c3172011-02-19 19:23:11 +00001695 switch (i)
cristy3ed852e2009-09-05 21:47:34 +00001696 {
cristy4c08aed2011-07-01 19:47:50 +00001697 case 0: SetPixelRed(despeckle_image,pixel[j],q); break;
1698 case 1: SetPixelGreen(despeckle_image,pixel[j],q); break;
1699 case 2: SetPixelBlue(despeckle_image,pixel[j],q); break;
1700 case 3: SetPixelAlpha(despeckle_image,pixel[j],q); break;
1701 case 4: SetPixelBlack(despeckle_image,pixel[j],q); break;
cristy3ed852e2009-09-05 21:47:34 +00001702 default: break;
1703 }
cristyed231572011-07-14 02:18:59 +00001704 q+=GetPixelChannels(despeckle_image);
cristy3ed852e2009-09-05 21:47:34 +00001705 j++;
1706 }
1707 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1708 if (sync == MagickFalse)
1709 {
1710 status=MagickFalse;
1711 break;
1712 }
1713 j++;
1714 }
1715 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1716 {
1717 MagickBooleanType
1718 proceed;
1719
cristya58c3172011-02-19 19:23:11 +00001720 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1721 number_channels);
cristy3ed852e2009-09-05 21:47:34 +00001722 if (proceed == MagickFalse)
1723 status=MagickFalse;
1724 }
1725 }
1726 despeckle_view=DestroyCacheView(despeckle_view);
1727 image_view=DestroyCacheView(image_view);
cristy65b9f392011-02-22 14:22:54 +00001728 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1729 pixels=(Quantum *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001730 despeckle_image->type=image->type;
1731 if (status == MagickFalse)
1732 despeckle_image=DestroyImage(despeckle_image);
1733 return(despeckle_image);
1734}
1735
1736/*
1737%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1738% %
1739% %
1740% %
1741% E d g e I m a g e %
1742% %
1743% %
1744% %
1745%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1746%
1747% EdgeImage() finds edges in an image. Radius defines the radius of the
1748% convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1749% radius for you.
1750%
1751% The format of the EdgeImage method is:
1752%
1753% Image *EdgeImage(const Image *image,const double radius,
1754% ExceptionInfo *exception)
1755%
1756% A description of each parameter follows:
1757%
1758% o image: the image.
1759%
1760% o radius: the radius of the pixel neighborhood.
1761%
1762% o exception: return any errors or warnings in this structure.
1763%
1764*/
1765MagickExport Image *EdgeImage(const Image *image,const double radius,
1766 ExceptionInfo *exception)
1767{
1768 Image
1769 *edge_image;
1770
cristy41cbe682011-07-15 19:12:37 +00001771 KernelInfo
1772 *kernel_info;
cristy3ed852e2009-09-05 21:47:34 +00001773
cristybb503372010-05-27 20:51:26 +00001774 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001775 i;
1776
cristybb503372010-05-27 20:51:26 +00001777 size_t
cristy3ed852e2009-09-05 21:47:34 +00001778 width;
1779
cristy41cbe682011-07-15 19:12:37 +00001780 ssize_t
1781 j,
1782 u,
1783 v;
1784
cristy3ed852e2009-09-05 21:47:34 +00001785 assert(image != (const Image *) NULL);
1786 assert(image->signature == MagickSignature);
1787 if (image->debug != MagickFalse)
1788 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1789 assert(exception != (ExceptionInfo *) NULL);
1790 assert(exception->signature == MagickSignature);
1791 width=GetOptimalKernelWidth1D(radius,0.5);
cristy5e6be1e2011-07-16 01:23:39 +00001792 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001793 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001794 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001795 kernel_info->width=width;
1796 kernel_info->height=width;
cristy41cbe682011-07-15 19:12:37 +00001797 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1798 kernel_info->width*sizeof(*kernel_info->values));
1799 if (kernel_info->values == (double *) NULL)
1800 {
1801 kernel_info=DestroyKernelInfo(kernel_info);
1802 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1803 }
1804 j=(ssize_t) kernel_info->width/2;
1805 i=0;
1806 for (v=(-j); v <= j; v++)
1807 {
1808 for (u=(-j); u <= j; u++)
1809 {
1810 kernel_info->values[i]=(-1.0);
1811 i++;
1812 }
1813 }
1814 kernel_info->values[i/2]=(double) (width*width-1.0);
cristy0a922382011-07-16 15:30:34 +00001815 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00001816 edge_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001817 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001818 return(edge_image);
1819}
1820
1821/*
1822%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1823% %
1824% %
1825% %
1826% E m b o s s I m a g e %
1827% %
1828% %
1829% %
1830%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1831%
1832% EmbossImage() returns a grayscale image with a three-dimensional effect.
1833% We convolve the image with a Gaussian operator of the given radius and
1834% standard deviation (sigma). For reasonable results, radius should be
1835% larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1836% radius for you.
1837%
1838% The format of the EmbossImage method is:
1839%
1840% Image *EmbossImage(const Image *image,const double radius,
1841% const double sigma,ExceptionInfo *exception)
1842%
1843% A description of each parameter follows:
1844%
1845% o image: the image.
1846%
1847% o radius: the radius of the pixel neighborhood.
1848%
1849% o sigma: the standard deviation of the Gaussian, in pixels.
1850%
1851% o exception: return any errors or warnings in this structure.
1852%
1853*/
1854MagickExport Image *EmbossImage(const Image *image,const double radius,
1855 const double sigma,ExceptionInfo *exception)
1856{
cristy3ed852e2009-09-05 21:47:34 +00001857 Image
1858 *emboss_image;
1859
cristy41cbe682011-07-15 19:12:37 +00001860 KernelInfo
1861 *kernel_info;
1862
cristybb503372010-05-27 20:51:26 +00001863 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001864 i;
1865
cristybb503372010-05-27 20:51:26 +00001866 size_t
cristy3ed852e2009-09-05 21:47:34 +00001867 width;
1868
cristy117ff172010-08-15 21:35:32 +00001869 ssize_t
1870 j,
1871 k,
1872 u,
1873 v;
1874
cristy41cbe682011-07-15 19:12:37 +00001875 assert(image != (const Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001876 assert(image->signature == MagickSignature);
1877 if (image->debug != MagickFalse)
1878 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1879 assert(exception != (ExceptionInfo *) NULL);
1880 assert(exception->signature == MagickSignature);
1881 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001882 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001883 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001884 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001885 kernel_info->width=width;
1886 kernel_info->height=width;
cristy41cbe682011-07-15 19:12:37 +00001887 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1888 kernel_info->width*sizeof(*kernel_info->values));
1889 if (kernel_info->values == (double *) NULL)
1890 {
1891 kernel_info=DestroyKernelInfo(kernel_info);
1892 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1893 }
1894 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00001895 k=j;
1896 i=0;
1897 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00001898 {
cristy47e00502009-12-17 19:19:57 +00001899 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00001900 {
cristy41cbe682011-07-15 19:12:37 +00001901 kernel_info->values[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
cristy47e00502009-12-17 19:19:57 +00001902 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
cristy4205a3c2010-09-12 20:19:59 +00001903 (2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +00001904 if (u != k)
cristy41cbe682011-07-15 19:12:37 +00001905 kernel_info->values[i]=0.0;
cristy3ed852e2009-09-05 21:47:34 +00001906 i++;
1907 }
cristy47e00502009-12-17 19:19:57 +00001908 k--;
cristy3ed852e2009-09-05 21:47:34 +00001909 }
cristy0a922382011-07-16 15:30:34 +00001910 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00001911 emboss_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001912 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001913 if (emboss_image != (Image *) NULL)
1914 (void) EqualizeImage(emboss_image);
cristy3ed852e2009-09-05 21:47:34 +00001915 return(emboss_image);
1916}
1917
1918/*
1919%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1920% %
1921% %
1922% %
1923% G a u s s i a n B l u r I m a g e %
1924% %
1925% %
1926% %
1927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1928%
1929% GaussianBlurImage() blurs an image. We convolve the image with a
1930% Gaussian operator of the given radius and standard deviation (sigma).
1931% For reasonable results, the radius should be larger than sigma. Use a
1932% radius of 0 and GaussianBlurImage() selects a suitable radius for you
1933%
1934% The format of the GaussianBlurImage method is:
1935%
1936% Image *GaussianBlurImage(const Image *image,onst double radius,
1937% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001938%
1939% A description of each parameter follows:
1940%
1941% o image: the image.
1942%
cristy3ed852e2009-09-05 21:47:34 +00001943% o radius: the radius of the Gaussian, in pixels, not counting the center
1944% pixel.
1945%
1946% o sigma: the standard deviation of the Gaussian, in pixels.
1947%
1948% o exception: return any errors or warnings in this structure.
1949%
1950*/
cristy41cbe682011-07-15 19:12:37 +00001951MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1952 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001953{
cristy3ed852e2009-09-05 21:47:34 +00001954 Image
1955 *blur_image;
1956
cristy41cbe682011-07-15 19:12:37 +00001957 KernelInfo
1958 *kernel_info;
1959
cristybb503372010-05-27 20:51:26 +00001960 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001961 i;
1962
cristybb503372010-05-27 20:51:26 +00001963 size_t
cristy3ed852e2009-09-05 21:47:34 +00001964 width;
1965
cristy117ff172010-08-15 21:35:32 +00001966 ssize_t
1967 j,
1968 u,
1969 v;
1970
cristy3ed852e2009-09-05 21:47:34 +00001971 assert(image != (const Image *) NULL);
1972 assert(image->signature == MagickSignature);
1973 if (image->debug != MagickFalse)
1974 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1975 assert(exception != (ExceptionInfo *) NULL);
1976 assert(exception->signature == MagickSignature);
1977 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001978 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001979 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001980 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001981 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1982 kernel_info->width=width;
1983 kernel_info->height=width;
1984 kernel_info->signature=MagickSignature;
1985 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1986 kernel_info->width*sizeof(*kernel_info->values));
1987 if (kernel_info->values == (double *) NULL)
1988 {
1989 kernel_info=DestroyKernelInfo(kernel_info);
1990 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1991 }
1992 j=(ssize_t) kernel_info->width/2;
cristy3ed852e2009-09-05 21:47:34 +00001993 i=0;
cristy47e00502009-12-17 19:19:57 +00001994 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00001995 {
cristy47e00502009-12-17 19:19:57 +00001996 for (u=(-j); u <= j; u++)
cristy41cbe682011-07-15 19:12:37 +00001997 {
1998 kernel_info->values[i]=(double) (exp(-((double) u*u+v*v)/(2.0*
1999 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
2000 i++;
2001 }
cristy3ed852e2009-09-05 21:47:34 +00002002 }
cristy0a922382011-07-16 15:30:34 +00002003 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00002004 blur_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00002005 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00002006 return(blur_image);
2007}
2008
2009/*
2010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2011% %
2012% %
2013% %
cristy3ed852e2009-09-05 21:47:34 +00002014% M o t i o n B l u r I m a g e %
2015% %
2016% %
2017% %
2018%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2019%
2020% MotionBlurImage() simulates motion blur. We convolve the image with a
2021% Gaussian operator of the given radius and standard deviation (sigma).
2022% For reasonable results, radius should be larger than sigma. Use a
2023% radius of 0 and MotionBlurImage() selects a suitable radius for you.
2024% Angle gives the angle of the blurring motion.
2025%
2026% Andrew Protano contributed this effect.
2027%
2028% The format of the MotionBlurImage method is:
2029%
2030% Image *MotionBlurImage(const Image *image,const double radius,
2031% const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002032%
2033% A description of each parameter follows:
2034%
2035% o image: the image.
2036%
cristy3ed852e2009-09-05 21:47:34 +00002037% o radius: the radius of the Gaussian, in pixels, not counting
2038% the center pixel.
2039%
2040% o sigma: the standard deviation of the Gaussian, in pixels.
2041%
cristycee97112010-05-28 00:44:52 +00002042% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00002043%
2044% o exception: return any errors or warnings in this structure.
2045%
2046*/
2047
cristybb503372010-05-27 20:51:26 +00002048static double *GetMotionBlurKernel(const size_t width,const double sigma)
cristy3ed852e2009-09-05 21:47:34 +00002049{
cristy3ed852e2009-09-05 21:47:34 +00002050 double
cristy47e00502009-12-17 19:19:57 +00002051 *kernel,
cristy3ed852e2009-09-05 21:47:34 +00002052 normalize;
2053
cristybb503372010-05-27 20:51:26 +00002054 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002055 i;
2056
2057 /*
cristy47e00502009-12-17 19:19:57 +00002058 Generate a 1-D convolution kernel.
cristy3ed852e2009-09-05 21:47:34 +00002059 */
2060 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2061 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2062 if (kernel == (double *) NULL)
2063 return(kernel);
cristy3ed852e2009-09-05 21:47:34 +00002064 normalize=0.0;
cristybb503372010-05-27 20:51:26 +00002065 for (i=0; i < (ssize_t) width; i++)
cristy47e00502009-12-17 19:19:57 +00002066 {
cristy4205a3c2010-09-12 20:19:59 +00002067 kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
2068 MagickSigma)))/(MagickSQ2PI*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00002069 normalize+=kernel[i];
cristy47e00502009-12-17 19:19:57 +00002070 }
cristybb503372010-05-27 20:51:26 +00002071 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002072 kernel[i]/=normalize;
2073 return(kernel);
2074}
2075
cristyf4ad9df2011-07-08 16:49:03 +00002076MagickExport Image *MotionBlurImage(const Image *image,
2077 const double radius,const double sigma,const double angle,
2078 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002079{
cristyc4c8d132010-01-07 01:58:38 +00002080 CacheView
2081 *blur_view,
2082 *image_view;
2083
cristy3ed852e2009-09-05 21:47:34 +00002084 double
2085 *kernel;
2086
2087 Image
2088 *blur_image;
2089
cristy3ed852e2009-09-05 21:47:34 +00002090 MagickBooleanType
2091 status;
2092
cristybb503372010-05-27 20:51:26 +00002093 MagickOffsetType
2094 progress;
2095
cristy4c08aed2011-07-01 19:47:50 +00002096 PixelInfo
cristyddd82202009-11-03 20:14:50 +00002097 bias;
cristy3ed852e2009-09-05 21:47:34 +00002098
2099 OffsetInfo
2100 *offset;
2101
2102 PointInfo
2103 point;
2104
cristybb503372010-05-27 20:51:26 +00002105 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002106 i;
2107
cristybb503372010-05-27 20:51:26 +00002108 size_t
cristy3ed852e2009-09-05 21:47:34 +00002109 width;
2110
cristybb503372010-05-27 20:51:26 +00002111 ssize_t
2112 y;
2113
cristy3ed852e2009-09-05 21:47:34 +00002114 assert(image != (Image *) NULL);
2115 assert(image->signature == MagickSignature);
2116 if (image->debug != MagickFalse)
2117 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2118 assert(exception != (ExceptionInfo *) NULL);
2119 width=GetOptimalKernelWidth1D(radius,sigma);
2120 kernel=GetMotionBlurKernel(width,sigma);
2121 if (kernel == (double *) NULL)
2122 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2123 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2124 if (offset == (OffsetInfo *) NULL)
2125 {
2126 kernel=(double *) RelinquishMagickMemory(kernel);
2127 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2128 }
2129 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2130 if (blur_image == (Image *) NULL)
2131 {
2132 kernel=(double *) RelinquishMagickMemory(kernel);
2133 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2134 return((Image *) NULL);
2135 }
2136 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2137 {
2138 kernel=(double *) RelinquishMagickMemory(kernel);
2139 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2140 InheritException(exception,&blur_image->exception);
2141 blur_image=DestroyImage(blur_image);
2142 return((Image *) NULL);
2143 }
2144 point.x=(double) width*sin(DegreesToRadians(angle));
2145 point.y=(double) width*cos(DegreesToRadians(angle));
cristybb503372010-05-27 20:51:26 +00002146 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002147 {
cristybb503372010-05-27 20:51:26 +00002148 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2149 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
cristy3ed852e2009-09-05 21:47:34 +00002150 }
2151 /*
2152 Motion blur image.
2153 */
2154 status=MagickTrue;
2155 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00002156 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00002157 image_view=AcquireCacheView(image);
2158 blur_view=AcquireCacheView(blur_image);
cristyb557a152011-02-22 12:14:30 +00002159#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +00002160 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +00002161#endif
cristybb503372010-05-27 20:51:26 +00002162 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002163 {
cristy4c08aed2011-07-01 19:47:50 +00002164 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002165 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002166
cristy117ff172010-08-15 21:35:32 +00002167 register ssize_t
2168 x;
2169
cristy3ed852e2009-09-05 21:47:34 +00002170 if (status == MagickFalse)
2171 continue;
2172 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2173 exception);
cristy4c08aed2011-07-01 19:47:50 +00002174 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002175 {
2176 status=MagickFalse;
2177 continue;
2178 }
cristybb503372010-05-27 20:51:26 +00002179 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002180 {
cristy4c08aed2011-07-01 19:47:50 +00002181 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002182 qixel;
2183
2184 PixelPacket
2185 pixel;
2186
2187 register double
cristyc47d1f82009-11-26 01:44:43 +00002188 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00002189
cristybb503372010-05-27 20:51:26 +00002190 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002191 i;
2192
cristy3ed852e2009-09-05 21:47:34 +00002193 k=kernel;
cristyddd82202009-11-03 20:14:50 +00002194 qixel=bias;
cristyed231572011-07-14 02:18:59 +00002195 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002196 {
cristybb503372010-05-27 20:51:26 +00002197 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002198 {
2199 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2200 offset[i].y,&pixel,exception);
2201 qixel.red+=(*k)*pixel.red;
2202 qixel.green+=(*k)*pixel.green;
2203 qixel.blue+=(*k)*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00002204 qixel.alpha+=(*k)*pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002205 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002206 qixel.black+=(*k)*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00002207 k++;
2208 }
cristyed231572011-07-14 02:18:59 +00002209 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002210 SetPixelRed(blur_image,
2211 ClampToQuantum(qixel.red),q);
cristyed231572011-07-14 02:18:59 +00002212 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002213 SetPixelGreen(blur_image,
2214 ClampToQuantum(qixel.green),q);
cristyed231572011-07-14 02:18:59 +00002215 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002216 SetPixelBlue(blur_image,
2217 ClampToQuantum(qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00002218 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00002219 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00002220 SetPixelBlack(blur_image,
2221 ClampToQuantum(qixel.black),q);
cristyed231572011-07-14 02:18:59 +00002222 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002223 SetPixelAlpha(blur_image,
2224 ClampToQuantum(qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00002225 }
2226 else
2227 {
2228 MagickRealType
2229 alpha,
2230 gamma;
2231
2232 alpha=0.0;
2233 gamma=0.0;
cristybb503372010-05-27 20:51:26 +00002234 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002235 {
2236 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2237 offset[i].y,&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00002238 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00002239 qixel.red+=(*k)*alpha*pixel.red;
2240 qixel.green+=(*k)*alpha*pixel.green;
2241 qixel.blue+=(*k)*alpha*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00002242 qixel.alpha+=(*k)*pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002243 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002244 qixel.black+=(*k)*alpha*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00002245 gamma+=(*k)*alpha;
2246 k++;
2247 }
2248 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00002249 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002250 SetPixelRed(blur_image,
2251 ClampToQuantum(gamma*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00002252 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002253 SetPixelGreen(blur_image,
2254 ClampToQuantum(gamma*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00002255 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002256 SetPixelBlue(blur_image,
2257 ClampToQuantum(gamma*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00002258 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00002259 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00002260 SetPixelBlack(blur_image,
2261 ClampToQuantum(gamma*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00002262 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002263 SetPixelAlpha(blur_image,
2264 ClampToQuantum(qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00002265 }
cristyed231572011-07-14 02:18:59 +00002266 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00002267 }
2268 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2269 status=MagickFalse;
2270 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2271 {
2272 MagickBooleanType
2273 proceed;
2274
cristyb557a152011-02-22 12:14:30 +00002275#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00002276 #pragma omp critical (MagickCore_MotionBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00002277#endif
2278 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2279 if (proceed == MagickFalse)
2280 status=MagickFalse;
2281 }
2282 }
2283 blur_view=DestroyCacheView(blur_view);
2284 image_view=DestroyCacheView(image_view);
2285 kernel=(double *) RelinquishMagickMemory(kernel);
2286 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2287 if (status == MagickFalse)
2288 blur_image=DestroyImage(blur_image);
2289 return(blur_image);
2290}
2291
2292/*
2293%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2294% %
2295% %
2296% %
2297% P r e v i e w I m a g e %
2298% %
2299% %
2300% %
2301%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2302%
2303% PreviewImage() tiles 9 thumbnails of the specified image with an image
2304% processing operation applied with varying parameters. This may be helpful
2305% pin-pointing an appropriate parameter for a particular image processing
2306% operation.
2307%
2308% The format of the PreviewImages method is:
2309%
2310% Image *PreviewImages(const Image *image,const PreviewType preview,
2311% ExceptionInfo *exception)
2312%
2313% A description of each parameter follows:
2314%
2315% o image: the image.
2316%
2317% o preview: the image processing operation.
2318%
2319% o exception: return any errors or warnings in this structure.
2320%
2321*/
2322MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2323 ExceptionInfo *exception)
2324{
2325#define NumberTiles 9
2326#define PreviewImageTag "Preview/Image"
2327#define DefaultPreviewGeometry "204x204+10+10"
2328
2329 char
2330 factor[MaxTextExtent],
2331 label[MaxTextExtent];
2332
2333 double
2334 degrees,
2335 gamma,
2336 percentage,
2337 radius,
2338 sigma,
2339 threshold;
2340
2341 Image
2342 *images,
2343 *montage_image,
2344 *preview_image,
2345 *thumbnail;
2346
2347 ImageInfo
2348 *preview_info;
2349
cristy3ed852e2009-09-05 21:47:34 +00002350 MagickBooleanType
2351 proceed;
2352
2353 MontageInfo
2354 *montage_info;
2355
2356 QuantizeInfo
2357 quantize_info;
2358
2359 RectangleInfo
2360 geometry;
2361
cristybb503372010-05-27 20:51:26 +00002362 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002363 i,
2364 x;
2365
cristybb503372010-05-27 20:51:26 +00002366 size_t
cristy3ed852e2009-09-05 21:47:34 +00002367 colors;
2368
cristy117ff172010-08-15 21:35:32 +00002369 ssize_t
2370 y;
2371
cristy3ed852e2009-09-05 21:47:34 +00002372 /*
2373 Open output image file.
2374 */
2375 assert(image != (Image *) NULL);
2376 assert(image->signature == MagickSignature);
2377 if (image->debug != MagickFalse)
2378 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2379 colors=2;
2380 degrees=0.0;
2381 gamma=(-0.2f);
2382 preview_info=AcquireImageInfo();
2383 SetGeometry(image,&geometry);
2384 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2385 &geometry.width,&geometry.height);
2386 images=NewImageList();
2387 percentage=12.5;
2388 GetQuantizeInfo(&quantize_info);
2389 radius=0.0;
2390 sigma=1.0;
2391 threshold=0.0;
2392 x=0;
2393 y=0;
2394 for (i=0; i < NumberTiles; i++)
2395 {
2396 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2397 if (thumbnail == (Image *) NULL)
2398 break;
2399 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2400 (void *) NULL);
2401 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2402 if (i == (NumberTiles/2))
2403 {
2404 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
2405 AppendImageToList(&images,thumbnail);
2406 continue;
2407 }
2408 switch (preview)
2409 {
2410 case RotatePreview:
2411 {
2412 degrees+=45.0;
2413 preview_image=RotateImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002414 (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002415 break;
2416 }
2417 case ShearPreview:
2418 {
2419 degrees+=5.0;
2420 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002421 (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002422 degrees,2.0*degrees);
2423 break;
2424 }
2425 case RollPreview:
2426 {
cristybb503372010-05-27 20:51:26 +00002427 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2428 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
cristy3ed852e2009-09-05 21:47:34 +00002429 preview_image=RollImage(thumbnail,x,y,exception);
cristyb51dff52011-05-19 16:55:47 +00002430 (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
cristye8c25f92010-06-03 00:53:06 +00002431 (double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00002432 break;
2433 }
2434 case HuePreview:
2435 {
2436 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2437 if (preview_image == (Image *) NULL)
2438 break;
cristyb51dff52011-05-19 16:55:47 +00002439 (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",
cristy3ed852e2009-09-05 21:47:34 +00002440 2.0*percentage);
2441 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002442 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002443 break;
2444 }
2445 case SaturationPreview:
2446 {
2447 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2448 if (preview_image == (Image *) NULL)
2449 break;
cristyb51dff52011-05-19 16:55:47 +00002450 (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",
cristy8cd5b312010-01-07 01:10:24 +00002451 2.0*percentage);
cristy3ed852e2009-09-05 21:47:34 +00002452 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002453 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002454 break;
2455 }
2456 case BrightnessPreview:
2457 {
2458 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2459 if (preview_image == (Image *) NULL)
2460 break;
cristyb51dff52011-05-19 16:55:47 +00002461 (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
cristy3ed852e2009-09-05 21:47:34 +00002462 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002463 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002464 break;
2465 }
2466 case GammaPreview:
2467 default:
2468 {
2469 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2470 if (preview_image == (Image *) NULL)
2471 break;
2472 gamma+=0.4f;
cristyb3e7c6c2011-07-24 01:43:55 +00002473 (void) GammaImage(preview_image,gamma,exception);
cristyb51dff52011-05-19 16:55:47 +00002474 (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
cristy3ed852e2009-09-05 21:47:34 +00002475 break;
2476 }
2477 case SpiffPreview:
2478 {
2479 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2480 if (preview_image != (Image *) NULL)
2481 for (x=0; x < i; x++)
2482 (void) ContrastImage(preview_image,MagickTrue);
cristyb51dff52011-05-19 16:55:47 +00002483 (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002484 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002485 break;
2486 }
2487 case DullPreview:
2488 {
2489 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2490 if (preview_image == (Image *) NULL)
2491 break;
2492 for (x=0; x < i; x++)
2493 (void) ContrastImage(preview_image,MagickFalse);
cristyb51dff52011-05-19 16:55:47 +00002494 (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002495 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002496 break;
2497 }
2498 case GrayscalePreview:
2499 {
2500 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2501 if (preview_image == (Image *) NULL)
2502 break;
2503 colors<<=1;
2504 quantize_info.number_colors=colors;
2505 quantize_info.colorspace=GRAYColorspace;
2506 (void) QuantizeImage(&quantize_info,preview_image);
cristyb51dff52011-05-19 16:55:47 +00002507 (void) FormatLocaleString(label,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002508 "-colorspace gray -colors %.20g",(double) colors);
cristy3ed852e2009-09-05 21:47:34 +00002509 break;
2510 }
2511 case QuantizePreview:
2512 {
2513 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2514 if (preview_image == (Image *) NULL)
2515 break;
2516 colors<<=1;
2517 quantize_info.number_colors=colors;
2518 (void) QuantizeImage(&quantize_info,preview_image);
cristyb51dff52011-05-19 16:55:47 +00002519 (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002520 colors);
cristy3ed852e2009-09-05 21:47:34 +00002521 break;
2522 }
2523 case DespecklePreview:
2524 {
2525 for (x=0; x < (i-1); x++)
2526 {
2527 preview_image=DespeckleImage(thumbnail,exception);
2528 if (preview_image == (Image *) NULL)
2529 break;
2530 thumbnail=DestroyImage(thumbnail);
2531 thumbnail=preview_image;
2532 }
2533 preview_image=DespeckleImage(thumbnail,exception);
2534 if (preview_image == (Image *) NULL)
2535 break;
cristyb51dff52011-05-19 16:55:47 +00002536 (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002537 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002538 break;
2539 }
2540 case ReduceNoisePreview:
2541 {
cristy95c38342011-03-18 22:39:51 +00002542 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2543 (size_t) radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002544 (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002545 break;
2546 }
2547 case AddNoisePreview:
2548 {
2549 switch ((int) i)
2550 {
2551 case 0:
2552 {
2553 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2554 break;
2555 }
2556 case 1:
2557 {
2558 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2559 break;
2560 }
2561 case 2:
2562 {
2563 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2564 break;
2565 }
2566 case 3:
2567 {
2568 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2569 break;
2570 }
2571 case 4:
2572 {
2573 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2574 break;
2575 }
2576 case 5:
2577 {
2578 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2579 break;
2580 }
2581 default:
2582 {
2583 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2584 break;
2585 }
2586 }
cristyd76c51e2011-03-26 00:21:26 +00002587 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2588 (size_t) i,exception);
cristyb51dff52011-05-19 16:55:47 +00002589 (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002590 break;
2591 }
2592 case SharpenPreview:
2593 {
2594 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002595 (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002596 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002597 break;
2598 }
2599 case BlurPreview:
2600 {
2601 preview_image=BlurImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002602 (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
cristy3ed852e2009-09-05 21:47:34 +00002603 sigma);
2604 break;
2605 }
2606 case ThresholdPreview:
2607 {
2608 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2609 if (preview_image == (Image *) NULL)
2610 break;
2611 (void) BilevelImage(thumbnail,
2612 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
cristyb51dff52011-05-19 16:55:47 +00002613 (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",
cristy3ed852e2009-09-05 21:47:34 +00002614 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2615 break;
2616 }
2617 case EdgeDetectPreview:
2618 {
2619 preview_image=EdgeImage(thumbnail,radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002620 (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002621 break;
2622 }
2623 case SpreadPreview:
2624 {
2625 preview_image=SpreadImage(thumbnail,radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002626 (void) FormatLocaleString(label,MaxTextExtent,"spread %g",
cristy8cd5b312010-01-07 01:10:24 +00002627 radius+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002628 break;
2629 }
2630 case SolarizePreview:
2631 {
2632 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2633 if (preview_image == (Image *) NULL)
2634 break;
2635 (void) SolarizeImage(preview_image,(double) QuantumRange*
2636 percentage/100.0);
cristyb51dff52011-05-19 16:55:47 +00002637 (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
cristy3ed852e2009-09-05 21:47:34 +00002638 (QuantumRange*percentage)/100.0);
2639 break;
2640 }
2641 case ShadePreview:
2642 {
2643 degrees+=10.0;
2644 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2645 exception);
cristyb51dff52011-05-19 16:55:47 +00002646 (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002647 degrees,degrees);
cristy3ed852e2009-09-05 21:47:34 +00002648 break;
2649 }
2650 case RaisePreview:
2651 {
2652 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2653 if (preview_image == (Image *) NULL)
2654 break;
cristybb503372010-05-27 20:51:26 +00002655 geometry.width=(size_t) (2*i+2);
2656 geometry.height=(size_t) (2*i+2);
cristy3ed852e2009-09-05 21:47:34 +00002657 geometry.x=i/2;
2658 geometry.y=i/2;
2659 (void) RaiseImage(preview_image,&geometry,MagickTrue);
cristyb51dff52011-05-19 16:55:47 +00002660 (void) FormatLocaleString(label,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00002661 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002662 geometry.height,(double) geometry.x,(double) geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00002663 break;
2664 }
2665 case SegmentPreview:
2666 {
2667 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2668 if (preview_image == (Image *) NULL)
2669 break;
2670 threshold+=0.4f;
2671 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
2672 threshold);
cristyb51dff52011-05-19 16:55:47 +00002673 (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002674 threshold,threshold);
2675 break;
2676 }
2677 case SwirlPreview:
2678 {
2679 preview_image=SwirlImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002680 (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002681 degrees+=45.0;
2682 break;
2683 }
2684 case ImplodePreview:
2685 {
2686 degrees+=0.1f;
2687 preview_image=ImplodeImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002688 (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002689 break;
2690 }
2691 case WavePreview:
2692 {
2693 degrees+=5.0f;
2694 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002695 (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002696 0.5*degrees,2.0*degrees);
cristy3ed852e2009-09-05 21:47:34 +00002697 break;
2698 }
2699 case OilPaintPreview:
2700 {
2701 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002702 (void) FormatLocaleString(label,MaxTextExtent,"paint %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002703 break;
2704 }
2705 case CharcoalDrawingPreview:
2706 {
2707 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2708 exception);
cristyb51dff52011-05-19 16:55:47 +00002709 (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002710 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002711 break;
2712 }
2713 case JPEGPreview:
2714 {
2715 char
2716 filename[MaxTextExtent];
2717
2718 int
2719 file;
2720
2721 MagickBooleanType
2722 status;
2723
2724 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2725 if (preview_image == (Image *) NULL)
2726 break;
cristybb503372010-05-27 20:51:26 +00002727 preview_info->quality=(size_t) percentage;
cristyb51dff52011-05-19 16:55:47 +00002728 (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002729 preview_info->quality);
cristy3ed852e2009-09-05 21:47:34 +00002730 file=AcquireUniqueFileResource(filename);
2731 if (file != -1)
2732 file=close(file)-1;
cristyb51dff52011-05-19 16:55:47 +00002733 (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00002734 "jpeg:%s",filename);
2735 status=WriteImage(preview_info,preview_image);
2736 if (status != MagickFalse)
2737 {
2738 Image
2739 *quality_image;
2740
2741 (void) CopyMagickString(preview_info->filename,
2742 preview_image->filename,MaxTextExtent);
2743 quality_image=ReadImage(preview_info,exception);
2744 if (quality_image != (Image *) NULL)
2745 {
2746 preview_image=DestroyImage(preview_image);
2747 preview_image=quality_image;
2748 }
2749 }
2750 (void) RelinquishUniqueFileResource(preview_image->filename);
2751 if ((GetBlobSize(preview_image)/1024) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002752 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
cristy3ed852e2009-09-05 21:47:34 +00002753 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2754 1024.0/1024.0);
2755 else
2756 if (GetBlobSize(preview_image) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002757 (void) FormatLocaleString(label,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002758 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
cristy8cd5b312010-01-07 01:10:24 +00002759 GetBlobSize(preview_image))/1024.0);
cristy3ed852e2009-09-05 21:47:34 +00002760 else
cristyb51dff52011-05-19 16:55:47 +00002761 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
cristy54ea5732011-06-10 12:39:53 +00002762 factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
cristy3ed852e2009-09-05 21:47:34 +00002763 break;
2764 }
2765 }
2766 thumbnail=DestroyImage(thumbnail);
2767 percentage+=12.5;
2768 radius+=0.5;
2769 sigma+=0.25;
2770 if (preview_image == (Image *) NULL)
2771 break;
2772 (void) DeleteImageProperty(preview_image,"label");
2773 (void) SetImageProperty(preview_image,"label",label);
2774 AppendImageToList(&images,preview_image);
cristybb503372010-05-27 20:51:26 +00002775 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2776 NumberTiles);
cristy3ed852e2009-09-05 21:47:34 +00002777 if (proceed == MagickFalse)
2778 break;
2779 }
2780 if (images == (Image *) NULL)
2781 {
2782 preview_info=DestroyImageInfo(preview_info);
2783 return((Image *) NULL);
2784 }
2785 /*
2786 Create the montage.
2787 */
2788 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2789 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2790 montage_info->shadow=MagickTrue;
2791 (void) CloneString(&montage_info->tile,"3x3");
2792 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2793 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2794 montage_image=MontageImages(images,montage_info,exception);
2795 montage_info=DestroyMontageInfo(montage_info);
2796 images=DestroyImageList(images);
2797 if (montage_image == (Image *) NULL)
2798 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2799 if (montage_image->montage != (char *) NULL)
2800 {
2801 /*
2802 Free image directory.
2803 */
2804 montage_image->montage=(char *) RelinquishMagickMemory(
2805 montage_image->montage);
2806 if (image->directory != (char *) NULL)
2807 montage_image->directory=(char *) RelinquishMagickMemory(
2808 montage_image->directory);
2809 }
2810 preview_info=DestroyImageInfo(preview_info);
2811 return(montage_image);
2812}
2813
2814/*
2815%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2816% %
2817% %
2818% %
2819% R a d i a l B l u r I m a g e %
2820% %
2821% %
2822% %
2823%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2824%
2825% RadialBlurImage() applies a radial blur to the image.
2826%
2827% Andrew Protano contributed this effect.
2828%
2829% The format of the RadialBlurImage method is:
2830%
2831% Image *RadialBlurImage(const Image *image,const double angle,
2832% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002833%
2834% A description of each parameter follows:
2835%
2836% o image: the image.
2837%
cristy3ed852e2009-09-05 21:47:34 +00002838% o angle: the angle of the radial blur.
2839%
2840% o exception: return any errors or warnings in this structure.
2841%
2842*/
cristyf4ad9df2011-07-08 16:49:03 +00002843MagickExport Image *RadialBlurImage(const Image *image,
2844 const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002845{
cristyc4c8d132010-01-07 01:58:38 +00002846 CacheView
2847 *blur_view,
2848 *image_view;
2849
cristy3ed852e2009-09-05 21:47:34 +00002850 Image
2851 *blur_image;
2852
cristy3ed852e2009-09-05 21:47:34 +00002853 MagickBooleanType
2854 status;
2855
cristybb503372010-05-27 20:51:26 +00002856 MagickOffsetType
2857 progress;
2858
cristy4c08aed2011-07-01 19:47:50 +00002859 PixelInfo
cristyddd82202009-11-03 20:14:50 +00002860 bias;
cristy3ed852e2009-09-05 21:47:34 +00002861
2862 MagickRealType
2863 blur_radius,
2864 *cos_theta,
2865 offset,
2866 *sin_theta,
2867 theta;
2868
2869 PointInfo
2870 blur_center;
2871
cristybb503372010-05-27 20:51:26 +00002872 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002873 i;
2874
cristybb503372010-05-27 20:51:26 +00002875 size_t
cristy3ed852e2009-09-05 21:47:34 +00002876 n;
2877
cristybb503372010-05-27 20:51:26 +00002878 ssize_t
2879 y;
2880
cristy3ed852e2009-09-05 21:47:34 +00002881 /*
2882 Allocate blur image.
2883 */
2884 assert(image != (Image *) NULL);
2885 assert(image->signature == MagickSignature);
2886 if (image->debug != MagickFalse)
2887 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2888 assert(exception != (ExceptionInfo *) NULL);
2889 assert(exception->signature == MagickSignature);
2890 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2891 if (blur_image == (Image *) NULL)
2892 return((Image *) NULL);
2893 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2894 {
2895 InheritException(exception,&blur_image->exception);
2896 blur_image=DestroyImage(blur_image);
2897 return((Image *) NULL);
2898 }
2899 blur_center.x=(double) image->columns/2.0;
2900 blur_center.y=(double) image->rows/2.0;
2901 blur_radius=hypot(blur_center.x,blur_center.y);
cristy117ff172010-08-15 21:35:32 +00002902 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
cristy3ed852e2009-09-05 21:47:34 +00002903 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
2904 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2905 sizeof(*cos_theta));
2906 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2907 sizeof(*sin_theta));
2908 if ((cos_theta == (MagickRealType *) NULL) ||
2909 (sin_theta == (MagickRealType *) NULL))
2910 {
2911 blur_image=DestroyImage(blur_image);
2912 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2913 }
2914 offset=theta*(MagickRealType) (n-1)/2.0;
cristybb503372010-05-27 20:51:26 +00002915 for (i=0; i < (ssize_t) n; i++)
cristy3ed852e2009-09-05 21:47:34 +00002916 {
2917 cos_theta[i]=cos((double) (theta*i-offset));
2918 sin_theta[i]=sin((double) (theta*i-offset));
2919 }
2920 /*
2921 Radial blur image.
2922 */
2923 status=MagickTrue;
2924 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00002925 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00002926 image_view=AcquireCacheView(image);
2927 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00002928#if defined(MAGICKCORE_OPENMP_SUPPORT)
2929 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002930#endif
cristybb503372010-05-27 20:51:26 +00002931 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002932 {
cristy4c08aed2011-07-01 19:47:50 +00002933 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002934 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002935
cristy117ff172010-08-15 21:35:32 +00002936 register ssize_t
2937 x;
2938
cristy3ed852e2009-09-05 21:47:34 +00002939 if (status == MagickFalse)
2940 continue;
2941 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2942 exception);
cristy4c08aed2011-07-01 19:47:50 +00002943 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002944 {
2945 status=MagickFalse;
2946 continue;
2947 }
cristybb503372010-05-27 20:51:26 +00002948 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002949 {
cristy4c08aed2011-07-01 19:47:50 +00002950 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002951 qixel;
2952
2953 MagickRealType
2954 normalize,
2955 radius;
2956
2957 PixelPacket
2958 pixel;
2959
2960 PointInfo
2961 center;
2962
cristybb503372010-05-27 20:51:26 +00002963 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002964 i;
2965
cristybb503372010-05-27 20:51:26 +00002966 size_t
cristy3ed852e2009-09-05 21:47:34 +00002967 step;
2968
2969 center.x=(double) x-blur_center.x;
2970 center.y=(double) y-blur_center.y;
2971 radius=hypot((double) center.x,center.y);
2972 if (radius == 0)
2973 step=1;
2974 else
2975 {
cristybb503372010-05-27 20:51:26 +00002976 step=(size_t) (blur_radius/radius);
cristy3ed852e2009-09-05 21:47:34 +00002977 if (step == 0)
2978 step=1;
2979 else
2980 if (step >= n)
2981 step=n-1;
2982 }
2983 normalize=0.0;
cristyddd82202009-11-03 20:14:50 +00002984 qixel=bias;
cristyed231572011-07-14 02:18:59 +00002985 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002986 {
cristyeaedf062010-05-29 22:36:02 +00002987 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
cristy3ed852e2009-09-05 21:47:34 +00002988 {
cristyeaedf062010-05-29 22:36:02 +00002989 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
2990 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
2991 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
2992 cos_theta[i]+0.5),&pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00002993 qixel.red+=pixel.red;
2994 qixel.green+=pixel.green;
2995 qixel.blue+=pixel.blue;
cristy3ed852e2009-09-05 21:47:34 +00002996 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002997 qixel.black+=pixel.black;
2998 qixel.alpha+=pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002999 normalize+=1.0;
3000 }
3001 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3002 normalize);
cristyed231572011-07-14 02:18:59 +00003003 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003004 SetPixelRed(blur_image,
3005 ClampToQuantum(normalize*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00003006 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003007 SetPixelGreen(blur_image,
3008 ClampToQuantum(normalize*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00003009 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003010 SetPixelBlue(blur_image,
3011 ClampToQuantum(normalize*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003012 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00003013 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00003014 SetPixelBlack(blur_image,
3015 ClampToQuantum(normalize*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00003016 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003017 SetPixelAlpha(blur_image,
3018 ClampToQuantum(normalize*qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003019 }
3020 else
3021 {
3022 MagickRealType
3023 alpha,
3024 gamma;
3025
3026 alpha=1.0;
3027 gamma=0.0;
cristyeaedf062010-05-29 22:36:02 +00003028 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
cristy3ed852e2009-09-05 21:47:34 +00003029 {
cristyeaedf062010-05-29 22:36:02 +00003030 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3031 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
3032 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
3033 cos_theta[i]+0.5),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00003034 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00003035 qixel.red+=alpha*pixel.red;
3036 qixel.green+=alpha*pixel.green;
3037 qixel.blue+=alpha*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00003038 qixel.alpha+=pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00003039 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00003040 qixel.black+=alpha*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00003041 gamma+=alpha;
3042 normalize+=1.0;
3043 }
3044 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3045 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3046 normalize);
cristyed231572011-07-14 02:18:59 +00003047 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003048 SetPixelRed(blur_image,
3049 ClampToQuantum(gamma*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00003050 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003051 SetPixelGreen(blur_image,
3052 ClampToQuantum(gamma*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00003053 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003054 SetPixelBlue(blur_image,
3055 ClampToQuantum(gamma*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003056 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00003057 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00003058 SetPixelBlack(blur_image,
3059 ClampToQuantum(gamma*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00003060 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003061 SetPixelAlpha(blur_image,
3062 ClampToQuantum(normalize*qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003063 }
cristyed231572011-07-14 02:18:59 +00003064 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003065 }
3066 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3067 status=MagickFalse;
3068 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3069 {
3070 MagickBooleanType
3071 proceed;
3072
cristyb5d5f722009-11-04 03:03:49 +00003073#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00003074 #pragma omp critical (MagickCore_RadialBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003075#endif
3076 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3077 if (proceed == MagickFalse)
3078 status=MagickFalse;
3079 }
3080 }
3081 blur_view=DestroyCacheView(blur_view);
3082 image_view=DestroyCacheView(image_view);
3083 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3084 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3085 if (status == MagickFalse)
3086 blur_image=DestroyImage(blur_image);
3087 return(blur_image);
3088}
3089
3090/*
3091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3092% %
3093% %
3094% %
cristy3ed852e2009-09-05 21:47:34 +00003095% S e l e c t i v e B l u r I m a g e %
3096% %
3097% %
3098% %
3099%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3100%
3101% SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3102% It is similar to the unsharpen mask that sharpens everything with contrast
3103% above a certain threshold.
3104%
3105% The format of the SelectiveBlurImage method is:
3106%
3107% Image *SelectiveBlurImage(const Image *image,const double radius,
3108% const double sigma,const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003109%
3110% A description of each parameter follows:
3111%
3112% o image: the image.
3113%
cristy3ed852e2009-09-05 21:47:34 +00003114% o radius: the radius of the Gaussian, in pixels, not counting the center
3115% pixel.
3116%
3117% o sigma: the standard deviation of the Gaussian, in pixels.
3118%
3119% o threshold: only pixels within this contrast threshold are included
3120% in the blur operation.
3121%
3122% o exception: return any errors or warnings in this structure.
3123%
3124*/
cristyf4ad9df2011-07-08 16:49:03 +00003125MagickExport Image *SelectiveBlurImage(const Image *image,
3126 const double radius,const double sigma,const double threshold,
3127 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003128{
3129#define SelectiveBlurImageTag "SelectiveBlur/Image"
3130
cristy47e00502009-12-17 19:19:57 +00003131 CacheView
3132 *blur_view,
3133 *image_view;
3134
cristy3ed852e2009-09-05 21:47:34 +00003135 double
cristy3ed852e2009-09-05 21:47:34 +00003136 *kernel;
3137
3138 Image
3139 *blur_image;
3140
cristy3ed852e2009-09-05 21:47:34 +00003141 MagickBooleanType
3142 status;
3143
cristybb503372010-05-27 20:51:26 +00003144 MagickOffsetType
3145 progress;
3146
cristy4c08aed2011-07-01 19:47:50 +00003147 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003148 bias;
3149
cristybb503372010-05-27 20:51:26 +00003150 register ssize_t
cristy47e00502009-12-17 19:19:57 +00003151 i;
cristy3ed852e2009-09-05 21:47:34 +00003152
cristybb503372010-05-27 20:51:26 +00003153 size_t
cristy3ed852e2009-09-05 21:47:34 +00003154 width;
3155
cristybb503372010-05-27 20:51:26 +00003156 ssize_t
3157 j,
3158 u,
3159 v,
3160 y;
3161
cristy3ed852e2009-09-05 21:47:34 +00003162 /*
3163 Initialize blur image attributes.
3164 */
3165 assert(image != (Image *) NULL);
3166 assert(image->signature == MagickSignature);
3167 if (image->debug != MagickFalse)
3168 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3169 assert(exception != (ExceptionInfo *) NULL);
3170 assert(exception->signature == MagickSignature);
3171 width=GetOptimalKernelWidth1D(radius,sigma);
3172 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3173 if (kernel == (double *) NULL)
3174 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00003175 j=(ssize_t) width/2;
cristy3ed852e2009-09-05 21:47:34 +00003176 i=0;
cristy47e00502009-12-17 19:19:57 +00003177 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00003178 {
cristy47e00502009-12-17 19:19:57 +00003179 for (u=(-j); u <= j; u++)
cristy4205a3c2010-09-12 20:19:59 +00003180 kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3181 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00003182 }
3183 if (image->debug != MagickFalse)
3184 {
3185 char
3186 format[MaxTextExtent],
3187 *message;
3188
cristy117ff172010-08-15 21:35:32 +00003189 register const double
3190 *k;
3191
cristybb503372010-05-27 20:51:26 +00003192 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003193 u,
3194 v;
3195
cristy3ed852e2009-09-05 21:47:34 +00003196 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003197 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3198 width);
cristy3ed852e2009-09-05 21:47:34 +00003199 message=AcquireString("");
3200 k=kernel;
cristybb503372010-05-27 20:51:26 +00003201 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003202 {
3203 *message='\0';
cristyb51dff52011-05-19 16:55:47 +00003204 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristy3ed852e2009-09-05 21:47:34 +00003205 (void) ConcatenateString(&message,format);
cristybb503372010-05-27 20:51:26 +00003206 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003207 {
cristyb51dff52011-05-19 16:55:47 +00003208 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",*k++);
cristy3ed852e2009-09-05 21:47:34 +00003209 (void) ConcatenateString(&message,format);
3210 }
3211 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3212 }
3213 message=DestroyString(message);
3214 }
3215 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3216 if (blur_image == (Image *) NULL)
3217 return((Image *) NULL);
3218 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3219 {
3220 InheritException(exception,&blur_image->exception);
3221 blur_image=DestroyImage(blur_image);
3222 return((Image *) NULL);
3223 }
3224 /*
3225 Threshold blur image.
3226 */
3227 status=MagickTrue;
3228 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003229 GetPixelInfo(image,&bias);
3230 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00003231 image_view=AcquireCacheView(image);
3232 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00003233#if defined(MAGICKCORE_OPENMP_SUPPORT)
3234 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003235#endif
cristybb503372010-05-27 20:51:26 +00003236 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003237 {
cristy4c08aed2011-07-01 19:47:50 +00003238 double
3239 contrast;
3240
cristy3ed852e2009-09-05 21:47:34 +00003241 MagickBooleanType
3242 sync;
3243
3244 MagickRealType
3245 gamma;
3246
cristy4c08aed2011-07-01 19:47:50 +00003247 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003248 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003249
cristy4c08aed2011-07-01 19:47:50 +00003250 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003251 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003252
cristy117ff172010-08-15 21:35:32 +00003253 register ssize_t
3254 x;
3255
cristy3ed852e2009-09-05 21:47:34 +00003256 if (status == MagickFalse)
3257 continue;
cristy117ff172010-08-15 21:35:32 +00003258 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3259 (width/2L),image->columns+width,width,exception);
cristy3ed852e2009-09-05 21:47:34 +00003260 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3261 exception);
cristy4c08aed2011-07-01 19:47:50 +00003262 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003263 {
3264 status=MagickFalse;
3265 continue;
3266 }
cristybb503372010-05-27 20:51:26 +00003267 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003268 {
cristy4c08aed2011-07-01 19:47:50 +00003269 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003270 pixel;
3271
3272 register const double
cristyc47d1f82009-11-26 01:44:43 +00003273 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00003274
cristybb503372010-05-27 20:51:26 +00003275 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003276 u;
3277
cristy117ff172010-08-15 21:35:32 +00003278 ssize_t
3279 j,
3280 v;
3281
cristyddd82202009-11-03 20:14:50 +00003282 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +00003283 k=kernel;
3284 gamma=0.0;
3285 j=0;
cristyed231572011-07-14 02:18:59 +00003286 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00003287 {
cristybb503372010-05-27 20:51:26 +00003288 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003289 {
cristybb503372010-05-27 20:51:26 +00003290 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003291 {
cristyed231572011-07-14 02:18:59 +00003292 contrast=GetPixelIntensity(image,p+(u+j)*GetPixelChannels(image))-
cristy4c08aed2011-07-01 19:47:50 +00003293 (double) GetPixelIntensity(blur_image,q);
3294 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003295 {
cristy4c08aed2011-07-01 19:47:50 +00003296 pixel.red+=(*k)*
cristyed231572011-07-14 02:18:59 +00003297 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003298 pixel.green+=(*k)*
cristyed231572011-07-14 02:18:59 +00003299 GetPixelGreen(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003300 pixel.blue+=(*k)*
cristyed231572011-07-14 02:18:59 +00003301 GetPixelBlue(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003302 if (image->colorspace == CMYKColorspace)
3303 pixel.black+=(*k)*
cristyed231572011-07-14 02:18:59 +00003304 GetPixelBlack(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003305 gamma+=(*k);
3306 k++;
3307 }
3308 }
cristyd99b0962010-05-29 23:14:26 +00003309 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003310 }
3311 if (gamma != 0.0)
3312 {
3313 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00003314 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003315 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00003316 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003317 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00003318 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003319 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003320 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00003321 (image->colorspace == CMYKColorspace))
3322 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00003323 }
cristyed231572011-07-14 02:18:59 +00003324 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003325 {
3326 gamma=0.0;
3327 j=0;
cristybb503372010-05-27 20:51:26 +00003328 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003329 {
cristybb503372010-05-27 20:51:26 +00003330 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003331 {
cristy4c08aed2011-07-01 19:47:50 +00003332 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003333 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003334 GetPixelIntensity(blur_image,q);
3335 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003336 {
cristy4c08aed2011-07-01 19:47:50 +00003337 pixel.alpha+=(*k)*
cristyed231572011-07-14 02:18:59 +00003338 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003339 gamma+=(*k);
3340 k++;
3341 }
3342 }
cristyeaedf062010-05-29 22:36:02 +00003343 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003344 }
3345 if (gamma != 0.0)
3346 {
3347 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3348 gamma);
cristy4c08aed2011-07-01 19:47:50 +00003349 SetPixelAlpha(blur_image,ClampToQuantum(gamma*pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003350 }
3351 }
3352 }
3353 else
3354 {
3355 MagickRealType
3356 alpha;
3357
cristybb503372010-05-27 20:51:26 +00003358 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003359 {
cristybb503372010-05-27 20:51:26 +00003360 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003361 {
cristy4c08aed2011-07-01 19:47:50 +00003362 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003363 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003364 GetPixelIntensity(blur_image,q);
3365 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003366 {
cristy4c08aed2011-07-01 19:47:50 +00003367 alpha=(MagickRealType) (QuantumScale*
cristyed231572011-07-14 02:18:59 +00003368 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image)));
cristy4c08aed2011-07-01 19:47:50 +00003369 pixel.red+=(*k)*alpha*
cristyed231572011-07-14 02:18:59 +00003370 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003371 pixel.green+=(*k)*alpha*GetPixelGreen(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003372 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003373 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003374 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003375 pixel.alpha+=(*k)*GetPixelAlpha(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003376 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003377 if (image->colorspace == CMYKColorspace)
3378 pixel.black+=(*k)*GetPixelBlack(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003379 GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003380 gamma+=(*k)*alpha;
3381 k++;
3382 }
3383 }
cristyeaedf062010-05-29 22:36:02 +00003384 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003385 }
3386 if (gamma != 0.0)
3387 {
3388 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00003389 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003390 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00003391 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003392 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00003393 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003394 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003395 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00003396 (image->colorspace == CMYKColorspace))
3397 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00003398 }
cristyed231572011-07-14 02:18:59 +00003399 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003400 {
3401 gamma=0.0;
3402 j=0;
cristybb503372010-05-27 20:51:26 +00003403 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003404 {
cristybb503372010-05-27 20:51:26 +00003405 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003406 {
cristy4c08aed2011-07-01 19:47:50 +00003407 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003408 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003409 GetPixelIntensity(blur_image,q);
3410 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003411 {
cristy4c08aed2011-07-01 19:47:50 +00003412 pixel.alpha+=(*k)*
cristyed231572011-07-14 02:18:59 +00003413 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003414 gamma+=(*k);
3415 k++;
3416 }
3417 }
cristyeaedf062010-05-29 22:36:02 +00003418 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003419 }
3420 if (gamma != 0.0)
3421 {
3422 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3423 gamma);
cristy4c08aed2011-07-01 19:47:50 +00003424 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003425 }
3426 }
3427 }
cristyed231572011-07-14 02:18:59 +00003428 p+=GetPixelChannels(image);
3429 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003430 }
3431 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3432 if (sync == MagickFalse)
3433 status=MagickFalse;
3434 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3435 {
3436 MagickBooleanType
3437 proceed;
3438
cristyb5d5f722009-11-04 03:03:49 +00003439#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00003440 #pragma omp critical (MagickCore_SelectiveBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003441#endif
3442 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3443 image->rows);
3444 if (proceed == MagickFalse)
3445 status=MagickFalse;
3446 }
3447 }
3448 blur_image->type=image->type;
3449 blur_view=DestroyCacheView(blur_view);
3450 image_view=DestroyCacheView(image_view);
3451 kernel=(double *) RelinquishMagickMemory(kernel);
3452 if (status == MagickFalse)
3453 blur_image=DestroyImage(blur_image);
3454 return(blur_image);
3455}
3456
3457/*
3458%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3459% %
3460% %
3461% %
3462% S h a d e I m a g e %
3463% %
3464% %
3465% %
3466%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3467%
3468% ShadeImage() shines a distant light on an image to create a
3469% three-dimensional effect. You control the positioning of the light with
3470% azimuth and elevation; azimuth is measured in degrees off the x axis
3471% and elevation is measured in pixels above the Z axis.
3472%
3473% The format of the ShadeImage method is:
3474%
3475% Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3476% const double azimuth,const double elevation,ExceptionInfo *exception)
3477%
3478% A description of each parameter follows:
3479%
3480% o image: the image.
3481%
3482% o gray: A value other than zero shades the intensity of each pixel.
3483%
3484% o azimuth, elevation: Define the light source direction.
3485%
3486% o exception: return any errors or warnings in this structure.
3487%
3488*/
3489MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3490 const double azimuth,const double elevation,ExceptionInfo *exception)
3491{
3492#define ShadeImageTag "Shade/Image"
3493
cristyc4c8d132010-01-07 01:58:38 +00003494 CacheView
3495 *image_view,
3496 *shade_view;
3497
cristy3ed852e2009-09-05 21:47:34 +00003498 Image
3499 *shade_image;
3500
cristy3ed852e2009-09-05 21:47:34 +00003501 MagickBooleanType
3502 status;
3503
cristybb503372010-05-27 20:51:26 +00003504 MagickOffsetType
3505 progress;
3506
cristy3ed852e2009-09-05 21:47:34 +00003507 PrimaryInfo
3508 light;
3509
cristybb503372010-05-27 20:51:26 +00003510 ssize_t
3511 y;
3512
cristy3ed852e2009-09-05 21:47:34 +00003513 /*
3514 Initialize shaded image attributes.
3515 */
3516 assert(image != (const Image *) NULL);
3517 assert(image->signature == MagickSignature);
3518 if (image->debug != MagickFalse)
3519 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3520 assert(exception != (ExceptionInfo *) NULL);
3521 assert(exception->signature == MagickSignature);
3522 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3523 if (shade_image == (Image *) NULL)
3524 return((Image *) NULL);
3525 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
3526 {
3527 InheritException(exception,&shade_image->exception);
3528 shade_image=DestroyImage(shade_image);
3529 return((Image *) NULL);
3530 }
3531 /*
3532 Compute the light vector.
3533 */
3534 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3535 cos(DegreesToRadians(elevation));
3536 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3537 cos(DegreesToRadians(elevation));
3538 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3539 /*
3540 Shade image.
3541 */
3542 status=MagickTrue;
3543 progress=0;
3544 image_view=AcquireCacheView(image);
3545 shade_view=AcquireCacheView(shade_image);
cristyb5d5f722009-11-04 03:03:49 +00003546#if defined(MAGICKCORE_OPENMP_SUPPORT)
3547 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003548#endif
cristybb503372010-05-27 20:51:26 +00003549 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003550 {
3551 MagickRealType
3552 distance,
3553 normal_distance,
3554 shade;
3555
3556 PrimaryInfo
3557 normal;
3558
cristy4c08aed2011-07-01 19:47:50 +00003559 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003560 *restrict p,
3561 *restrict s0,
3562 *restrict s1,
3563 *restrict s2;
cristy3ed852e2009-09-05 21:47:34 +00003564
cristy4c08aed2011-07-01 19:47:50 +00003565 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003566 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003567
cristy117ff172010-08-15 21:35:32 +00003568 register ssize_t
3569 x;
3570
cristy3ed852e2009-09-05 21:47:34 +00003571 if (status == MagickFalse)
3572 continue;
3573 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
3574 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3575 exception);
cristy4c08aed2011-07-01 19:47:50 +00003576 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003577 {
3578 status=MagickFalse;
3579 continue;
3580 }
3581 /*
3582 Shade this row of pixels.
3583 */
3584 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
cristyed231572011-07-14 02:18:59 +00003585 s0=p+GetPixelChannels(image);
3586 s1=s0+(image->columns+2)*GetPixelChannels(image);
3587 s2=s1+(image->columns+2)*GetPixelChannels(image);
cristybb503372010-05-27 20:51:26 +00003588 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003589 {
3590 /*
3591 Determine the surface normal and compute shading.
3592 */
cristyed231572011-07-14 02:18:59 +00003593 normal.x=(double) (GetPixelIntensity(image,s0-GetPixelChannels(image))+
3594 GetPixelIntensity(image,s1-GetPixelChannels(image))+
3595 GetPixelIntensity(image,s2-GetPixelChannels(image))-
3596 GetPixelIntensity(image,s0+GetPixelChannels(image))-
3597 GetPixelIntensity(image,s1+GetPixelChannels(image))-
3598 GetPixelIntensity(image,s2+GetPixelChannels(image)));
3599 normal.y=(double) (GetPixelIntensity(image,s2-GetPixelChannels(image))+
cristy4c08aed2011-07-01 19:47:50 +00003600 GetPixelIntensity(image,s2)+
cristyed231572011-07-14 02:18:59 +00003601 GetPixelIntensity(image,s2+GetPixelChannels(image))-
3602 GetPixelIntensity(image,s0-GetPixelChannels(image))-
cristy4c08aed2011-07-01 19:47:50 +00003603 GetPixelIntensity(image,s0)-
cristyed231572011-07-14 02:18:59 +00003604 GetPixelIntensity(image,s0+GetPixelChannels(image)));
cristy3ed852e2009-09-05 21:47:34 +00003605 if ((normal.x == 0.0) && (normal.y == 0.0))
3606 shade=light.z;
3607 else
3608 {
3609 shade=0.0;
3610 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3611 if (distance > MagickEpsilon)
3612 {
3613 normal_distance=
3614 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3615 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3616 shade=distance/sqrt((double) normal_distance);
3617 }
3618 }
3619 if (gray != MagickFalse)
3620 {
cristy4c08aed2011-07-01 19:47:50 +00003621 SetPixelRed(shade_image,ClampToQuantum(shade),q);
3622 SetPixelGreen(shade_image,ClampToQuantum(shade),q);
3623 SetPixelBlue(shade_image,ClampToQuantum(shade),q);
cristy3ed852e2009-09-05 21:47:34 +00003624 }
3625 else
3626 {
cristy4c08aed2011-07-01 19:47:50 +00003627 SetPixelRed(shade_image,ClampToQuantum(QuantumScale*shade*
3628 GetPixelRed(image,s1)),q);
3629 SetPixelGreen(shade_image,ClampToQuantum(QuantumScale*shade*
3630 GetPixelGreen(image,s1)),q);
3631 SetPixelBlue(shade_image,ClampToQuantum(QuantumScale*shade*
3632 GetPixelBlue(image,s1)),q);
cristy3ed852e2009-09-05 21:47:34 +00003633 }
cristy4c08aed2011-07-01 19:47:50 +00003634 SetPixelAlpha(shade_image,GetPixelAlpha(image,s1),q);
cristyed231572011-07-14 02:18:59 +00003635 s0+=GetPixelChannels(image);
3636 s1+=GetPixelChannels(image);
3637 s2+=GetPixelChannels(image);
3638 q+=GetPixelChannels(shade_image);
cristy3ed852e2009-09-05 21:47:34 +00003639 }
3640 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3641 status=MagickFalse;
3642 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3643 {
3644 MagickBooleanType
3645 proceed;
3646
cristyb5d5f722009-11-04 03:03:49 +00003647#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003648 #pragma omp critical (MagickCore_ShadeImage)
3649#endif
3650 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3651 if (proceed == MagickFalse)
3652 status=MagickFalse;
3653 }
3654 }
3655 shade_view=DestroyCacheView(shade_view);
3656 image_view=DestroyCacheView(image_view);
3657 if (status == MagickFalse)
3658 shade_image=DestroyImage(shade_image);
3659 return(shade_image);
3660}
3661
3662/*
3663%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3664% %
3665% %
3666% %
3667% S h a r p e n I m a g e %
3668% %
3669% %
3670% %
3671%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3672%
3673% SharpenImage() sharpens the image. We convolve the image with a Gaussian
3674% operator of the given radius and standard deviation (sigma). For
3675% reasonable results, radius should be larger than sigma. Use a radius of 0
3676% and SharpenImage() selects a suitable radius for you.
3677%
3678% Using a separable kernel would be faster, but the negative weights cancel
3679% out on the corners of the kernel producing often undesirable ringing in the
3680% filtered result; this can be avoided by using a 2D gaussian shaped image
3681% sharpening kernel instead.
3682%
3683% The format of the SharpenImage method is:
3684%
3685% Image *SharpenImage(const Image *image,const double radius,
3686% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003687%
3688% A description of each parameter follows:
3689%
3690% o image: the image.
3691%
cristy3ed852e2009-09-05 21:47:34 +00003692% o radius: the radius of the Gaussian, in pixels, not counting the center
3693% pixel.
3694%
3695% o sigma: the standard deviation of the Laplacian, in pixels.
3696%
3697% o exception: return any errors or warnings in this structure.
3698%
3699*/
cristy3ed852e2009-09-05 21:47:34 +00003700MagickExport Image *SharpenImage(const Image *image,const double radius,
3701 const double sigma,ExceptionInfo *exception)
3702{
cristy3ed852e2009-09-05 21:47:34 +00003703 double
cristy47e00502009-12-17 19:19:57 +00003704 normalize;
cristy3ed852e2009-09-05 21:47:34 +00003705
3706 Image
3707 *sharp_image;
3708
cristy41cbe682011-07-15 19:12:37 +00003709 KernelInfo
3710 *kernel_info;
3711
cristybb503372010-05-27 20:51:26 +00003712 register ssize_t
cristy47e00502009-12-17 19:19:57 +00003713 i;
3714
cristybb503372010-05-27 20:51:26 +00003715 size_t
cristy3ed852e2009-09-05 21:47:34 +00003716 width;
3717
cristy117ff172010-08-15 21:35:32 +00003718 ssize_t
3719 j,
3720 u,
3721 v;
3722
cristy3ed852e2009-09-05 21:47:34 +00003723 assert(image != (const Image *) NULL);
3724 assert(image->signature == MagickSignature);
3725 if (image->debug != MagickFalse)
3726 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3727 assert(exception != (ExceptionInfo *) NULL);
3728 assert(exception->signature == MagickSignature);
3729 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00003730 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00003731 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003732 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00003733 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3734 kernel_info->width=width;
3735 kernel_info->height=width;
3736 kernel_info->signature=MagickSignature;
3737 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
3738 kernel_info->width*sizeof(*kernel_info->values));
3739 if (kernel_info->values == (double *) NULL)
3740 {
3741 kernel_info=DestroyKernelInfo(kernel_info);
3742 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3743 }
cristy3ed852e2009-09-05 21:47:34 +00003744 normalize=0.0;
cristy41cbe682011-07-15 19:12:37 +00003745 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00003746 i=0;
3747 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00003748 {
cristy47e00502009-12-17 19:19:57 +00003749 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00003750 {
cristy41cbe682011-07-15 19:12:37 +00003751 kernel_info->values[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*
3752 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3753 normalize+=kernel_info->values[i];
cristy3ed852e2009-09-05 21:47:34 +00003754 i++;
3755 }
3756 }
cristy41cbe682011-07-15 19:12:37 +00003757 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
cristy0a922382011-07-16 15:30:34 +00003758 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00003759 sharp_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00003760 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00003761 return(sharp_image);
3762}
3763
3764/*
3765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3766% %
3767% %
3768% %
3769% S p r e a d I m a g e %
3770% %
3771% %
3772% %
3773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3774%
3775% SpreadImage() is a special effects method that randomly displaces each
3776% pixel in a block defined by the radius parameter.
3777%
3778% The format of the SpreadImage method is:
3779%
3780% Image *SpreadImage(const Image *image,const double radius,
3781% ExceptionInfo *exception)
3782%
3783% A description of each parameter follows:
3784%
3785% o image: the image.
3786%
3787% o radius: Choose a random pixel in a neighborhood of this extent.
3788%
3789% o exception: return any errors or warnings in this structure.
3790%
3791*/
3792MagickExport Image *SpreadImage(const Image *image,const double radius,
3793 ExceptionInfo *exception)
3794{
3795#define SpreadImageTag "Spread/Image"
3796
cristyfa112112010-01-04 17:48:07 +00003797 CacheView
cristy9f7e7cb2011-03-26 00:49:57 +00003798 *image_view,
3799 *spread_view;
cristyfa112112010-01-04 17:48:07 +00003800
cristy3ed852e2009-09-05 21:47:34 +00003801 Image
3802 *spread_image;
3803
cristy3ed852e2009-09-05 21:47:34 +00003804 MagickBooleanType
3805 status;
3806
cristybb503372010-05-27 20:51:26 +00003807 MagickOffsetType
3808 progress;
3809
cristy4c08aed2011-07-01 19:47:50 +00003810 PixelInfo
cristyddd82202009-11-03 20:14:50 +00003811 bias;
cristy3ed852e2009-09-05 21:47:34 +00003812
3813 RandomInfo
cristyfa112112010-01-04 17:48:07 +00003814 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00003815
cristybb503372010-05-27 20:51:26 +00003816 size_t
cristy3ed852e2009-09-05 21:47:34 +00003817 width;
3818
cristybb503372010-05-27 20:51:26 +00003819 ssize_t
3820 y;
3821
cristy3ed852e2009-09-05 21:47:34 +00003822 /*
3823 Initialize spread image attributes.
3824 */
3825 assert(image != (Image *) NULL);
3826 assert(image->signature == MagickSignature);
3827 if (image->debug != MagickFalse)
3828 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3829 assert(exception != (ExceptionInfo *) NULL);
3830 assert(exception->signature == MagickSignature);
3831 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3832 exception);
3833 if (spread_image == (Image *) NULL)
3834 return((Image *) NULL);
3835 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
3836 {
3837 InheritException(exception,&spread_image->exception);
3838 spread_image=DestroyImage(spread_image);
3839 return((Image *) NULL);
3840 }
3841 /*
3842 Spread image.
3843 */
3844 status=MagickTrue;
3845 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003846 GetPixelInfo(spread_image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00003847 width=GetOptimalKernelWidth1D(radius,0.5);
cristy3ed852e2009-09-05 21:47:34 +00003848 random_info=AcquireRandomInfoThreadSet();
cristy9f7e7cb2011-03-26 00:49:57 +00003849 image_view=AcquireCacheView(image);
3850 spread_view=AcquireCacheView(spread_image);
cristyb557a152011-02-22 12:14:30 +00003851#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +00003852 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +00003853#endif
cristybb503372010-05-27 20:51:26 +00003854 for (y=0; y < (ssize_t) spread_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003855 {
cristy5c9e6f22010-09-17 17:31:01 +00003856 const int
3857 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003858
cristy4c08aed2011-07-01 19:47:50 +00003859 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003860 pixel;
3861
cristy4c08aed2011-07-01 19:47:50 +00003862 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003863 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003864
cristy117ff172010-08-15 21:35:32 +00003865 register ssize_t
3866 x;
3867
cristy3ed852e2009-09-05 21:47:34 +00003868 if (status == MagickFalse)
3869 continue;
cristy9f7e7cb2011-03-26 00:49:57 +00003870 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003871 exception);
cristy4c08aed2011-07-01 19:47:50 +00003872 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003873 {
3874 status=MagickFalse;
3875 continue;
3876 }
cristyddd82202009-11-03 20:14:50 +00003877 pixel=bias;
cristybb503372010-05-27 20:51:26 +00003878 for (x=0; x < (ssize_t) spread_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003879 {
cristy4c08aed2011-07-01 19:47:50 +00003880 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00003881 UndefinedInterpolatePixel,(double) x+width*(GetPseudoRandomValue(
3882 random_info[id])-0.5),(double) y+width*(GetPseudoRandomValue(
3883 random_info[id])-0.5),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00003884 SetPixelPixelInfo(spread_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00003885 q+=GetPixelChannels(spread_image);
cristy3ed852e2009-09-05 21:47:34 +00003886 }
cristy9f7e7cb2011-03-26 00:49:57 +00003887 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003888 status=MagickFalse;
3889 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3890 {
3891 MagickBooleanType
3892 proceed;
3893
cristyb557a152011-02-22 12:14:30 +00003894#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003895 #pragma omp critical (MagickCore_SpreadImage)
3896#endif
3897 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3898 if (proceed == MagickFalse)
3899 status=MagickFalse;
3900 }
3901 }
cristy9f7e7cb2011-03-26 00:49:57 +00003902 spread_view=DestroyCacheView(spread_view);
cristy3ed852e2009-09-05 21:47:34 +00003903 image_view=DestroyCacheView(image_view);
3904 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00003905 return(spread_image);
3906}
3907
3908/*
3909%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3910% %
3911% %
3912% %
cristy0834d642011-03-18 18:26:08 +00003913% S t a t i s t i c I m a g e %
3914% %
3915% %
3916% %
3917%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3918%
3919% StatisticImage() makes each pixel the min / max / median / mode / etc. of
cristy8d752042011-03-19 01:00:36 +00003920% the neighborhood of the specified width and height.
cristy0834d642011-03-18 18:26:08 +00003921%
3922% The format of the StatisticImage method is:
3923%
3924% Image *StatisticImage(const Image *image,const StatisticType type,
cristy95c38342011-03-18 22:39:51 +00003925% const size_t width,const size_t height,ExceptionInfo *exception)
cristy0834d642011-03-18 18:26:08 +00003926%
3927% A description of each parameter follows:
3928%
3929% o image: the image.
3930%
cristy0834d642011-03-18 18:26:08 +00003931% o type: the statistic type (median, mode, etc.).
3932%
cristy95c38342011-03-18 22:39:51 +00003933% o width: the width of the pixel neighborhood.
3934%
3935% o height: the height of the pixel neighborhood.
cristy0834d642011-03-18 18:26:08 +00003936%
3937% o exception: return any errors or warnings in this structure.
3938%
3939*/
3940
cristy733678d2011-03-18 21:29:28 +00003941#define ListChannels 5
3942
3943typedef struct _ListNode
3944{
3945 size_t
3946 next[9],
3947 count,
3948 signature;
3949} ListNode;
3950
3951typedef struct _SkipList
3952{
3953 ssize_t
3954 level;
3955
3956 ListNode
3957 *nodes;
3958} SkipList;
3959
3960typedef struct _PixelList
3961{
3962 size_t
cristy6fc86bb2011-03-18 23:45:16 +00003963 length,
cristy733678d2011-03-18 21:29:28 +00003964 seed,
3965 signature;
3966
3967 SkipList
3968 lists[ListChannels];
3969} PixelList;
3970
3971static PixelList *DestroyPixelList(PixelList *pixel_list)
3972{
3973 register ssize_t
3974 i;
3975
3976 if (pixel_list == (PixelList *) NULL)
3977 return((PixelList *) NULL);
3978 for (i=0; i < ListChannels; i++)
3979 if (pixel_list->lists[i].nodes != (ListNode *) NULL)
3980 pixel_list->lists[i].nodes=(ListNode *) RelinquishMagickMemory(
3981 pixel_list->lists[i].nodes);
3982 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
3983 return(pixel_list);
3984}
3985
3986static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
3987{
3988 register ssize_t
3989 i;
3990
3991 assert(pixel_list != (PixelList **) NULL);
3992 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3993 if (pixel_list[i] != (PixelList *) NULL)
3994 pixel_list[i]=DestroyPixelList(pixel_list[i]);
3995 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
3996 return(pixel_list);
3997}
3998
cristy6fc86bb2011-03-18 23:45:16 +00003999static PixelList *AcquirePixelList(const size_t width,const size_t height)
cristy733678d2011-03-18 21:29:28 +00004000{
4001 PixelList
4002 *pixel_list;
4003
4004 register ssize_t
4005 i;
4006
4007 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
4008 if (pixel_list == (PixelList *) NULL)
4009 return(pixel_list);
4010 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
cristy6fc86bb2011-03-18 23:45:16 +00004011 pixel_list->length=width*height;
cristy733678d2011-03-18 21:29:28 +00004012 for (i=0; i < ListChannels; i++)
4013 {
4014 pixel_list->lists[i].nodes=(ListNode *) AcquireQuantumMemory(65537UL,
4015 sizeof(*pixel_list->lists[i].nodes));
4016 if (pixel_list->lists[i].nodes == (ListNode *) NULL)
4017 return(DestroyPixelList(pixel_list));
4018 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
4019 sizeof(*pixel_list->lists[i].nodes));
4020 }
4021 pixel_list->signature=MagickSignature;
4022 return(pixel_list);
4023}
4024
cristy6fc86bb2011-03-18 23:45:16 +00004025static PixelList **AcquirePixelListThreadSet(const size_t width,
4026 const size_t height)
cristy733678d2011-03-18 21:29:28 +00004027{
4028 PixelList
4029 **pixel_list;
4030
4031 register ssize_t
4032 i;
4033
4034 size_t
4035 number_threads;
4036
4037 number_threads=GetOpenMPMaximumThreads();
4038 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
4039 sizeof(*pixel_list));
4040 if (pixel_list == (PixelList **) NULL)
4041 return((PixelList **) NULL);
4042 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
4043 for (i=0; i < (ssize_t) number_threads; i++)
4044 {
cristy6fc86bb2011-03-18 23:45:16 +00004045 pixel_list[i]=AcquirePixelList(width,height);
cristy733678d2011-03-18 21:29:28 +00004046 if (pixel_list[i] == (PixelList *) NULL)
4047 return(DestroyPixelListThreadSet(pixel_list));
4048 }
4049 return(pixel_list);
4050}
4051
4052static void AddNodePixelList(PixelList *pixel_list,const ssize_t channel,
4053 const size_t color)
4054{
4055 register SkipList
4056 *list;
4057
4058 register ssize_t
4059 level;
4060
4061 size_t
4062 search,
4063 update[9];
4064
4065 /*
4066 Initialize the node.
4067 */
4068 list=pixel_list->lists+channel;
4069 list->nodes[color].signature=pixel_list->signature;
4070 list->nodes[color].count=1;
4071 /*
4072 Determine where it belongs in the list.
4073 */
4074 search=65536UL;
4075 for (level=list->level; level >= 0; level--)
4076 {
4077 while (list->nodes[search].next[level] < color)
4078 search=list->nodes[search].next[level];
4079 update[level]=search;
4080 }
4081 /*
4082 Generate a pseudo-random level for this node.
4083 */
4084 for (level=0; ; level++)
4085 {
4086 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
4087 if ((pixel_list->seed & 0x300) != 0x300)
4088 break;
4089 }
4090 if (level > 8)
4091 level=8;
4092 if (level > (list->level+2))
4093 level=list->level+2;
4094 /*
4095 If we're raising the list's level, link back to the root node.
4096 */
4097 while (level > list->level)
4098 {
4099 list->level++;
4100 update[list->level]=65536UL;
4101 }
4102 /*
4103 Link the node into the skip-list.
4104 */
4105 do
4106 {
4107 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
4108 list->nodes[update[level]].next[level]=color;
cristy3cba8ca2011-03-19 01:29:12 +00004109 } while (level-- > 0);
cristy733678d2011-03-18 21:29:28 +00004110}
4111
cristy4c08aed2011-07-01 19:47:50 +00004112static PixelInfo GetMaximumPixelList(PixelList *pixel_list)
cristy6fc86bb2011-03-18 23:45:16 +00004113{
cristy4c08aed2011-07-01 19:47:50 +00004114 PixelInfo
cristy6fc86bb2011-03-18 23:45:16 +00004115 pixel;
4116
4117 register SkipList
4118 *list;
4119
4120 register ssize_t
4121 channel;
4122
4123 size_t
cristyd76c51e2011-03-26 00:21:26 +00004124 color,
4125 maximum;
cristy49f37242011-03-22 18:18:23 +00004126
4127 ssize_t
cristy6fc86bb2011-03-18 23:45:16 +00004128 count;
4129
4130 unsigned short
cristyd76c51e2011-03-26 00:21:26 +00004131 channels[ListChannels];
cristy6fc86bb2011-03-18 23:45:16 +00004132
4133 /*
4134 Find the maximum value for each of the color.
4135 */
4136 for (channel=0; channel < 5; channel++)
4137 {
4138 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004139 color=65536L;
cristy6fc86bb2011-03-18 23:45:16 +00004140 count=0;
cristy49f37242011-03-22 18:18:23 +00004141 maximum=list->nodes[color].next[0];
cristy6fc86bb2011-03-18 23:45:16 +00004142 do
4143 {
4144 color=list->nodes[color].next[0];
cristy49f37242011-03-22 18:18:23 +00004145 if (color > maximum)
4146 maximum=color;
cristy6fc86bb2011-03-18 23:45:16 +00004147 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004148 } while (count < (ssize_t) pixel_list->length);
cristy49f37242011-03-22 18:18:23 +00004149 channels[channel]=(unsigned short) maximum;
4150 }
cristy4c08aed2011-07-01 19:47:50 +00004151 GetPixelInfo((const Image *) NULL,&pixel);
cristy49f37242011-03-22 18:18:23 +00004152 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4153 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4154 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004155 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4156 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy49f37242011-03-22 18:18:23 +00004157 return(pixel);
4158}
4159
cristy4c08aed2011-07-01 19:47:50 +00004160static PixelInfo GetMeanPixelList(PixelList *pixel_list)
cristy49f37242011-03-22 18:18:23 +00004161{
cristy4c08aed2011-07-01 19:47:50 +00004162 PixelInfo
cristy49f37242011-03-22 18:18:23 +00004163 pixel;
4164
cristy80a99a32011-03-30 01:30:23 +00004165 MagickRealType
4166 sum;
4167
cristy49f37242011-03-22 18:18:23 +00004168 register SkipList
4169 *list;
4170
4171 register ssize_t
4172 channel;
4173
4174 size_t
cristy80a99a32011-03-30 01:30:23 +00004175 color;
cristy49f37242011-03-22 18:18:23 +00004176
4177 ssize_t
4178 count;
4179
4180 unsigned short
4181 channels[ListChannels];
4182
4183 /*
4184 Find the mean value for each of the color.
4185 */
4186 for (channel=0; channel < 5; channel++)
4187 {
4188 list=pixel_list->lists+channel;
4189 color=65536L;
4190 count=0;
cristy80a99a32011-03-30 01:30:23 +00004191 sum=0.0;
cristy49f37242011-03-22 18:18:23 +00004192 do
4193 {
4194 color=list->nodes[color].next[0];
cristy80a99a32011-03-30 01:30:23 +00004195 sum+=(MagickRealType) list->nodes[color].count*color;
cristy49f37242011-03-22 18:18:23 +00004196 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004197 } while (count < (ssize_t) pixel_list->length);
cristy80a99a32011-03-30 01:30:23 +00004198 sum/=pixel_list->length;
4199 channels[channel]=(unsigned short) sum;
cristy6fc86bb2011-03-18 23:45:16 +00004200 }
cristy4c08aed2011-07-01 19:47:50 +00004201 GetPixelInfo((const Image *) NULL,&pixel);
cristy6fc86bb2011-03-18 23:45:16 +00004202 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4203 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4204 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004205 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4206 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy6fc86bb2011-03-18 23:45:16 +00004207 return(pixel);
4208}
4209
cristy4c08aed2011-07-01 19:47:50 +00004210static PixelInfo GetMedianPixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004211{
cristy4c08aed2011-07-01 19:47:50 +00004212 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004213 pixel;
4214
4215 register SkipList
4216 *list;
4217
4218 register ssize_t
4219 channel;
4220
4221 size_t
cristy49f37242011-03-22 18:18:23 +00004222 color;
4223
4224 ssize_t
cristy733678d2011-03-18 21:29:28 +00004225 count;
4226
4227 unsigned short
4228 channels[ListChannels];
4229
4230 /*
4231 Find the median value for each of the color.
4232 */
cristy733678d2011-03-18 21:29:28 +00004233 for (channel=0; channel < 5; channel++)
4234 {
4235 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004236 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004237 count=0;
4238 do
4239 {
4240 color=list->nodes[color].next[0];
4241 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004242 } while (count <= (ssize_t) (pixel_list->length >> 1));
cristy6fc86bb2011-03-18 23:45:16 +00004243 channels[channel]=(unsigned short) color;
4244 }
cristy4c08aed2011-07-01 19:47:50 +00004245 GetPixelInfo((const Image *) NULL,&pixel);
cristy6fc86bb2011-03-18 23:45:16 +00004246 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4247 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4248 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004249 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4250 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy6fc86bb2011-03-18 23:45:16 +00004251 return(pixel);
4252}
4253
cristy4c08aed2011-07-01 19:47:50 +00004254static PixelInfo GetMinimumPixelList(PixelList *pixel_list)
cristy6fc86bb2011-03-18 23:45:16 +00004255{
cristy4c08aed2011-07-01 19:47:50 +00004256 PixelInfo
cristy6fc86bb2011-03-18 23:45:16 +00004257 pixel;
4258
4259 register SkipList
4260 *list;
4261
4262 register ssize_t
4263 channel;
4264
4265 size_t
cristyd76c51e2011-03-26 00:21:26 +00004266 color,
4267 minimum;
cristy6fc86bb2011-03-18 23:45:16 +00004268
cristy49f37242011-03-22 18:18:23 +00004269 ssize_t
4270 count;
4271
cristy6fc86bb2011-03-18 23:45:16 +00004272 unsigned short
cristyd76c51e2011-03-26 00:21:26 +00004273 channels[ListChannels];
cristy6fc86bb2011-03-18 23:45:16 +00004274
4275 /*
4276 Find the minimum value for each of the color.
4277 */
4278 for (channel=0; channel < 5; channel++)
4279 {
4280 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004281 count=0;
cristy6fc86bb2011-03-18 23:45:16 +00004282 color=65536UL;
cristy49f37242011-03-22 18:18:23 +00004283 minimum=list->nodes[color].next[0];
4284 do
4285 {
4286 color=list->nodes[color].next[0];
4287 if (color < minimum)
4288 minimum=color;
4289 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004290 } while (count < (ssize_t) pixel_list->length);
cristy49f37242011-03-22 18:18:23 +00004291 channels[channel]=(unsigned short) minimum;
cristy733678d2011-03-18 21:29:28 +00004292 }
cristy4c08aed2011-07-01 19:47:50 +00004293 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004294 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4295 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4296 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004297 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4298 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy733678d2011-03-18 21:29:28 +00004299 return(pixel);
4300}
4301
cristy4c08aed2011-07-01 19:47:50 +00004302static PixelInfo GetModePixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004303{
cristy4c08aed2011-07-01 19:47:50 +00004304 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004305 pixel;
4306
4307 register SkipList
4308 *list;
4309
4310 register ssize_t
4311 channel;
4312
4313 size_t
4314 color,
cristy733678d2011-03-18 21:29:28 +00004315 max_count,
cristy6fc86bb2011-03-18 23:45:16 +00004316 mode;
cristy733678d2011-03-18 21:29:28 +00004317
cristy49f37242011-03-22 18:18:23 +00004318 ssize_t
4319 count;
4320
cristy733678d2011-03-18 21:29:28 +00004321 unsigned short
4322 channels[5];
4323
4324 /*
glennrp30d2dc62011-06-25 03:17:16 +00004325 Make each pixel the 'predominant color' of the specified neighborhood.
cristy733678d2011-03-18 21:29:28 +00004326 */
cristy733678d2011-03-18 21:29:28 +00004327 for (channel=0; channel < 5; channel++)
4328 {
4329 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004330 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004331 mode=color;
4332 max_count=list->nodes[mode].count;
4333 count=0;
4334 do
4335 {
4336 color=list->nodes[color].next[0];
4337 if (list->nodes[color].count > max_count)
4338 {
4339 mode=color;
4340 max_count=list->nodes[mode].count;
4341 }
4342 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004343 } while (count < (ssize_t) pixel_list->length);
cristy733678d2011-03-18 21:29:28 +00004344 channels[channel]=(unsigned short) mode;
4345 }
cristy4c08aed2011-07-01 19:47:50 +00004346 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004347 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4348 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4349 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004350 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4351 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy733678d2011-03-18 21:29:28 +00004352 return(pixel);
4353}
4354
cristy4c08aed2011-07-01 19:47:50 +00004355static PixelInfo GetNonpeakPixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004356{
cristy4c08aed2011-07-01 19:47:50 +00004357 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004358 pixel;
4359
4360 register SkipList
4361 *list;
4362
4363 register ssize_t
4364 channel;
4365
4366 size_t
cristy733678d2011-03-18 21:29:28 +00004367 color,
cristy733678d2011-03-18 21:29:28 +00004368 next,
4369 previous;
4370
cristy49f37242011-03-22 18:18:23 +00004371 ssize_t
4372 count;
4373
cristy733678d2011-03-18 21:29:28 +00004374 unsigned short
4375 channels[5];
4376
4377 /*
cristy49f37242011-03-22 18:18:23 +00004378 Finds the non peak value for each of the colors.
cristy733678d2011-03-18 21:29:28 +00004379 */
cristy733678d2011-03-18 21:29:28 +00004380 for (channel=0; channel < 5; channel++)
4381 {
4382 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004383 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004384 next=list->nodes[color].next[0];
4385 count=0;
4386 do
4387 {
4388 previous=color;
4389 color=next;
4390 next=list->nodes[color].next[0];
4391 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004392 } while (count <= (ssize_t) (pixel_list->length >> 1));
cristy733678d2011-03-18 21:29:28 +00004393 if ((previous == 65536UL) && (next != 65536UL))
4394 color=next;
4395 else
4396 if ((previous != 65536UL) && (next == 65536UL))
4397 color=previous;
4398 channels[channel]=(unsigned short) color;
4399 }
cristy4c08aed2011-07-01 19:47:50 +00004400 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004401 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4402 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4403 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004404 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4405 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy733678d2011-03-18 21:29:28 +00004406 return(pixel);
4407}
4408
cristy4c08aed2011-07-01 19:47:50 +00004409static PixelInfo GetStandardDeviationPixelList(PixelList *pixel_list)
cristy9a68cbb2011-03-29 00:51:23 +00004410{
cristy4c08aed2011-07-01 19:47:50 +00004411 PixelInfo
cristy9a68cbb2011-03-29 00:51:23 +00004412 pixel;
4413
cristy80a99a32011-03-30 01:30:23 +00004414 MagickRealType
4415 sum,
4416 sum_squared;
4417
cristy9a68cbb2011-03-29 00:51:23 +00004418 register SkipList
4419 *list;
4420
4421 register ssize_t
4422 channel;
4423
4424 size_t
cristy80a99a32011-03-30 01:30:23 +00004425 color;
cristy9a68cbb2011-03-29 00:51:23 +00004426
4427 ssize_t
4428 count;
4429
4430 unsigned short
4431 channels[ListChannels];
4432
4433 /*
cristy80a99a32011-03-30 01:30:23 +00004434 Find the standard-deviation value for each of the color.
cristy9a68cbb2011-03-29 00:51:23 +00004435 */
4436 for (channel=0; channel < 5; channel++)
4437 {
4438 list=pixel_list->lists+channel;
4439 color=65536L;
4440 count=0;
cristy80a99a32011-03-30 01:30:23 +00004441 sum=0.0;
4442 sum_squared=0.0;
cristy9a68cbb2011-03-29 00:51:23 +00004443 do
4444 {
cristy80a99a32011-03-30 01:30:23 +00004445 register ssize_t
4446 i;
4447
cristy9a68cbb2011-03-29 00:51:23 +00004448 color=list->nodes[color].next[0];
cristy80a99a32011-03-30 01:30:23 +00004449 sum+=(MagickRealType) list->nodes[color].count*color;
4450 for (i=0; i < (ssize_t) list->nodes[color].count; i++)
4451 sum_squared+=((MagickRealType) color)*((MagickRealType) color);
cristy9a68cbb2011-03-29 00:51:23 +00004452 count+=list->nodes[color].count;
4453 } while (count < (ssize_t) pixel_list->length);
cristy80a99a32011-03-30 01:30:23 +00004454 sum/=pixel_list->length;
4455 sum_squared/=pixel_list->length;
4456 channels[channel]=(unsigned short) sqrt(sum_squared-(sum*sum));
cristy9a68cbb2011-03-29 00:51:23 +00004457 }
cristy4c08aed2011-07-01 19:47:50 +00004458 GetPixelInfo((const Image *) NULL,&pixel);
cristy9a68cbb2011-03-29 00:51:23 +00004459 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4460 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4461 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004462 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4463 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy9a68cbb2011-03-29 00:51:23 +00004464 return(pixel);
4465}
4466
cristy4c08aed2011-07-01 19:47:50 +00004467static inline void InsertPixelList(const Image *image,const Quantum *pixel,
4468 PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004469{
4470 size_t
4471 signature;
4472
4473 unsigned short
4474 index;
4475
cristy4c08aed2011-07-01 19:47:50 +00004476 index=ScaleQuantumToShort(GetPixelRed(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004477 signature=pixel_list->lists[0].nodes[index].signature;
4478 if (signature == pixel_list->signature)
4479 pixel_list->lists[0].nodes[index].count++;
4480 else
4481 AddNodePixelList(pixel_list,0,index);
cristy4c08aed2011-07-01 19:47:50 +00004482 index=ScaleQuantumToShort(GetPixelGreen(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004483 signature=pixel_list->lists[1].nodes[index].signature;
4484 if (signature == pixel_list->signature)
4485 pixel_list->lists[1].nodes[index].count++;
4486 else
4487 AddNodePixelList(pixel_list,1,index);
cristy4c08aed2011-07-01 19:47:50 +00004488 index=ScaleQuantumToShort(GetPixelBlue(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004489 signature=pixel_list->lists[2].nodes[index].signature;
4490 if (signature == pixel_list->signature)
4491 pixel_list->lists[2].nodes[index].count++;
4492 else
4493 AddNodePixelList(pixel_list,2,index);
cristy4c08aed2011-07-01 19:47:50 +00004494 index=ScaleQuantumToShort(GetPixelAlpha(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004495 signature=pixel_list->lists[3].nodes[index].signature;
4496 if (signature == pixel_list->signature)
4497 pixel_list->lists[3].nodes[index].count++;
4498 else
4499 AddNodePixelList(pixel_list,3,index);
4500 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004501 index=ScaleQuantumToShort(GetPixelBlack(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004502 signature=pixel_list->lists[4].nodes[index].signature;
4503 if (signature == pixel_list->signature)
4504 pixel_list->lists[4].nodes[index].count++;
4505 else
4506 AddNodePixelList(pixel_list,4,index);
4507}
4508
cristy80c99742011-04-04 14:46:39 +00004509static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
4510{
4511 if (x < 0)
4512 return(-x);
4513 return(x);
4514}
4515
cristy733678d2011-03-18 21:29:28 +00004516static void ResetPixelList(PixelList *pixel_list)
4517{
4518 int
4519 level;
4520
4521 register ListNode
4522 *root;
4523
4524 register SkipList
4525 *list;
4526
4527 register ssize_t
4528 channel;
4529
4530 /*
4531 Reset the skip-list.
4532 */
4533 for (channel=0; channel < 5; channel++)
4534 {
4535 list=pixel_list->lists+channel;
4536 root=list->nodes+65536UL;
4537 list->level=0;
4538 for (level=0; level < 9; level++)
4539 root->next[level]=65536UL;
4540 }
4541 pixel_list->seed=pixel_list->signature++;
4542}
4543
cristy0834d642011-03-18 18:26:08 +00004544MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
cristy95c38342011-03-18 22:39:51 +00004545 const size_t width,const size_t height,ExceptionInfo *exception)
cristy0834d642011-03-18 18:26:08 +00004546{
cristy3cba8ca2011-03-19 01:29:12 +00004547#define StatisticWidth \
cristyd76c51e2011-03-26 00:21:26 +00004548 (width == 0 ? GetOptimalKernelWidth2D((double) width,0.5) : width)
cristy3cba8ca2011-03-19 01:29:12 +00004549#define StatisticHeight \
cristyd76c51e2011-03-26 00:21:26 +00004550 (height == 0 ? GetOptimalKernelWidth2D((double) height,0.5) : height)
cristy0834d642011-03-18 18:26:08 +00004551#define StatisticImageTag "Statistic/Image"
4552
4553 CacheView
4554 *image_view,
4555 *statistic_view;
4556
4557 Image
4558 *statistic_image;
4559
4560 MagickBooleanType
4561 status;
4562
4563 MagickOffsetType
4564 progress;
4565
4566 PixelList
4567 **restrict pixel_list;
4568
cristy0834d642011-03-18 18:26:08 +00004569 ssize_t
4570 y;
4571
4572 /*
4573 Initialize statistics image attributes.
4574 */
4575 assert(image != (Image *) NULL);
4576 assert(image->signature == MagickSignature);
4577 if (image->debug != MagickFalse)
4578 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4579 assert(exception != (ExceptionInfo *) NULL);
4580 assert(exception->signature == MagickSignature);
cristy0834d642011-03-18 18:26:08 +00004581 statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4582 exception);
4583 if (statistic_image == (Image *) NULL)
4584 return((Image *) NULL);
4585 if (SetImageStorageClass(statistic_image,DirectClass) == MagickFalse)
4586 {
4587 InheritException(exception,&statistic_image->exception);
4588 statistic_image=DestroyImage(statistic_image);
4589 return((Image *) NULL);
4590 }
cristy6fc86bb2011-03-18 23:45:16 +00004591 pixel_list=AcquirePixelListThreadSet(StatisticWidth,StatisticHeight);
cristy0834d642011-03-18 18:26:08 +00004592 if (pixel_list == (PixelList **) NULL)
4593 {
4594 statistic_image=DestroyImage(statistic_image);
4595 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4596 }
4597 /*
cristy8d752042011-03-19 01:00:36 +00004598 Make each pixel the min / max / median / mode / etc. of the neighborhood.
cristy0834d642011-03-18 18:26:08 +00004599 */
4600 status=MagickTrue;
4601 progress=0;
4602 image_view=AcquireCacheView(image);
4603 statistic_view=AcquireCacheView(statistic_image);
4604#if defined(MAGICKCORE_OPENMP_SUPPORT)
4605 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4606#endif
4607 for (y=0; y < (ssize_t) statistic_image->rows; y++)
4608 {
4609 const int
4610 id = GetOpenMPThreadId();
4611
cristy4c08aed2011-07-01 19:47:50 +00004612 register const Quantum
cristy0834d642011-03-18 18:26:08 +00004613 *restrict p;
4614
cristy4c08aed2011-07-01 19:47:50 +00004615 register Quantum
cristy0834d642011-03-18 18:26:08 +00004616 *restrict q;
4617
4618 register ssize_t
4619 x;
4620
4621 if (status == MagickFalse)
4622 continue;
cristy6fc86bb2011-03-18 23:45:16 +00004623 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) StatisticWidth/2L),y-
4624 (ssize_t) (StatisticHeight/2L),image->columns+StatisticWidth,
4625 StatisticHeight,exception);
cristy3cba8ca2011-03-19 01:29:12 +00004626 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004627 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy0834d642011-03-18 18:26:08 +00004628 {
4629 status=MagickFalse;
4630 continue;
4631 }
cristy0834d642011-03-18 18:26:08 +00004632 for (x=0; x < (ssize_t) statistic_image->columns; x++)
4633 {
cristy4c08aed2011-07-01 19:47:50 +00004634 PixelInfo
cristy0834d642011-03-18 18:26:08 +00004635 pixel;
4636
cristy4c08aed2011-07-01 19:47:50 +00004637 register const Quantum
cristy6e3026a2011-03-19 00:54:38 +00004638 *restrict r;
4639
cristy0834d642011-03-18 18:26:08 +00004640 register ssize_t
4641 u,
4642 v;
4643
4644 r=p;
cristy0834d642011-03-18 18:26:08 +00004645 ResetPixelList(pixel_list[id]);
cristy6e4c3292011-03-19 00:53:55 +00004646 for (v=0; v < (ssize_t) StatisticHeight; v++)
cristy0834d642011-03-18 18:26:08 +00004647 {
cristy6e4c3292011-03-19 00:53:55 +00004648 for (u=0; u < (ssize_t) StatisticWidth; u++)
cristyed231572011-07-14 02:18:59 +00004649 InsertPixelList(image,r+u*GetPixelChannels(image),pixel_list[id]);
4650 r+=(image->columns+StatisticWidth)*GetPixelChannels(image);
cristy0834d642011-03-18 18:26:08 +00004651 }
cristy4c08aed2011-07-01 19:47:50 +00004652 GetPixelInfo(image,&pixel);
cristy490408a2011-07-07 14:42:05 +00004653 SetPixelInfo(image,p+(StatisticWidth*StatisticHeight/2)*
cristyed231572011-07-14 02:18:59 +00004654 GetPixelChannels(image),&pixel);
cristy0834d642011-03-18 18:26:08 +00004655 switch (type)
4656 {
cristy80c99742011-04-04 14:46:39 +00004657 case GradientStatistic:
4658 {
cristy4c08aed2011-07-01 19:47:50 +00004659 PixelInfo
cristy80c99742011-04-04 14:46:39 +00004660 maximum,
4661 minimum;
4662
4663 minimum=GetMinimumPixelList(pixel_list[id]);
4664 maximum=GetMaximumPixelList(pixel_list[id]);
4665 pixel.red=MagickAbsoluteValue(maximum.red-minimum.red);
4666 pixel.green=MagickAbsoluteValue(maximum.green-minimum.green);
4667 pixel.blue=MagickAbsoluteValue(maximum.blue-minimum.blue);
cristy4c08aed2011-07-01 19:47:50 +00004668 pixel.alpha=MagickAbsoluteValue(maximum.alpha-minimum.alpha);
cristy80c99742011-04-04 14:46:39 +00004669 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004670 pixel.black=MagickAbsoluteValue(maximum.black-minimum.black);
cristy80c99742011-04-04 14:46:39 +00004671 break;
4672 }
cristy6fc86bb2011-03-18 23:45:16 +00004673 case MaximumStatistic:
4674 {
4675 pixel=GetMaximumPixelList(pixel_list[id]);
4676 break;
4677 }
cristy49f37242011-03-22 18:18:23 +00004678 case MeanStatistic:
4679 {
4680 pixel=GetMeanPixelList(pixel_list[id]);
4681 break;
4682 }
cristyf2ad14a2011-03-18 18:57:25 +00004683 case MedianStatistic:
cristy6fc86bb2011-03-18 23:45:16 +00004684 default:
cristyf2ad14a2011-03-18 18:57:25 +00004685 {
4686 pixel=GetMedianPixelList(pixel_list[id]);
4687 break;
4688 }
cristy6fc86bb2011-03-18 23:45:16 +00004689 case MinimumStatistic:
4690 {
4691 pixel=GetMinimumPixelList(pixel_list[id]);
4692 break;
4693 }
cristyf2ad14a2011-03-18 18:57:25 +00004694 case ModeStatistic:
4695 {
4696 pixel=GetModePixelList(pixel_list[id]);
4697 break;
4698 }
4699 case NonpeakStatistic:
4700 {
4701 pixel=GetNonpeakPixelList(pixel_list[id]);
4702 break;
4703 }
cristy9a68cbb2011-03-29 00:51:23 +00004704 case StandardDeviationStatistic:
4705 {
4706 pixel=GetStandardDeviationPixelList(pixel_list[id]);
4707 break;
4708 }
cristy0834d642011-03-18 18:26:08 +00004709 }
cristyed231572011-07-14 02:18:59 +00004710 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004711 SetPixelRed(statistic_image,ClampToQuantum(pixel.red),q);
cristyed231572011-07-14 02:18:59 +00004712 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004713 SetPixelGreen(statistic_image,ClampToQuantum(pixel.green),q);
cristyed231572011-07-14 02:18:59 +00004714 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004715 SetPixelBlue(statistic_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00004716 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy0834d642011-03-18 18:26:08 +00004717 (image->colorspace == CMYKColorspace))
cristy490408a2011-07-07 14:42:05 +00004718 SetPixelBlack(statistic_image,ClampToQuantum(pixel.black),q);
cristyed231572011-07-14 02:18:59 +00004719 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00004720 (image->matte != MagickFalse))
cristy490408a2011-07-07 14:42:05 +00004721 SetPixelAlpha(statistic_image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004722 p+=GetPixelChannels(image);
4723 q+=GetPixelChannels(statistic_image);
cristy0834d642011-03-18 18:26:08 +00004724 }
4725 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
4726 status=MagickFalse;
4727 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4728 {
4729 MagickBooleanType
4730 proceed;
4731
4732#if defined(MAGICKCORE_OPENMP_SUPPORT)
4733 #pragma omp critical (MagickCore_StatisticImage)
4734#endif
4735 proceed=SetImageProgress(image,StatisticImageTag,progress++,
4736 image->rows);
4737 if (proceed == MagickFalse)
4738 status=MagickFalse;
4739 }
4740 }
4741 statistic_view=DestroyCacheView(statistic_view);
4742 image_view=DestroyCacheView(image_view);
4743 pixel_list=DestroyPixelListThreadSet(pixel_list);
4744 return(statistic_image);
4745}
4746
4747/*
4748%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4749% %
4750% %
4751% %
cristy3ed852e2009-09-05 21:47:34 +00004752% U n s h a r p M a s k I m a g e %
4753% %
4754% %
4755% %
4756%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4757%
4758% UnsharpMaskImage() sharpens one or more image channels. We convolve the
4759% image with a Gaussian operator of the given radius and standard deviation
4760% (sigma). For reasonable results, radius should be larger than sigma. Use a
4761% radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4762%
4763% The format of the UnsharpMaskImage method is:
4764%
4765% Image *UnsharpMaskImage(const Image *image,const double radius,
4766% const double sigma,const double amount,const double threshold,
4767% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004768%
4769% A description of each parameter follows:
4770%
4771% o image: the image.
4772%
cristy3ed852e2009-09-05 21:47:34 +00004773% o radius: the radius of the Gaussian, in pixels, not counting the center
4774% pixel.
4775%
4776% o sigma: the standard deviation of the Gaussian, in pixels.
4777%
4778% o amount: the percentage of the difference between the original and the
4779% blur image that is added back into the original.
4780%
4781% o threshold: the threshold in pixels needed to apply the diffence amount.
4782%
4783% o exception: return any errors or warnings in this structure.
4784%
4785*/
cristyf4ad9df2011-07-08 16:49:03 +00004786MagickExport Image *UnsharpMaskImage(const Image *image,
4787 const double radius,const double sigma,const double amount,
4788 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004789{
4790#define SharpenImageTag "Sharpen/Image"
4791
cristyc4c8d132010-01-07 01:58:38 +00004792 CacheView
4793 *image_view,
4794 *unsharp_view;
4795
cristy3ed852e2009-09-05 21:47:34 +00004796 Image
4797 *unsharp_image;
4798
cristy3ed852e2009-09-05 21:47:34 +00004799 MagickBooleanType
4800 status;
4801
cristybb503372010-05-27 20:51:26 +00004802 MagickOffsetType
4803 progress;
4804
cristy4c08aed2011-07-01 19:47:50 +00004805 PixelInfo
cristyddd82202009-11-03 20:14:50 +00004806 bias;
cristy3ed852e2009-09-05 21:47:34 +00004807
4808 MagickRealType
4809 quantum_threshold;
4810
cristybb503372010-05-27 20:51:26 +00004811 ssize_t
4812 y;
4813
cristy3ed852e2009-09-05 21:47:34 +00004814 assert(image != (const Image *) NULL);
4815 assert(image->signature == MagickSignature);
4816 if (image->debug != MagickFalse)
4817 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4818 assert(exception != (ExceptionInfo *) NULL);
cristyf4ad9df2011-07-08 16:49:03 +00004819 unsharp_image=BlurImage(image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00004820 if (unsharp_image == (Image *) NULL)
4821 return((Image *) NULL);
4822 quantum_threshold=(MagickRealType) QuantumRange*threshold;
4823 /*
4824 Unsharp-mask image.
4825 */
4826 status=MagickTrue;
4827 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00004828 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00004829 image_view=AcquireCacheView(image);
4830 unsharp_view=AcquireCacheView(unsharp_image);
cristyb5d5f722009-11-04 03:03:49 +00004831#if defined(MAGICKCORE_OPENMP_SUPPORT)
4832 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004833#endif
cristybb503372010-05-27 20:51:26 +00004834 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004835 {
cristy4c08aed2011-07-01 19:47:50 +00004836 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004837 pixel;
4838
cristy4c08aed2011-07-01 19:47:50 +00004839 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004840 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004841
cristy4c08aed2011-07-01 19:47:50 +00004842 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004843 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004844
cristy117ff172010-08-15 21:35:32 +00004845 register ssize_t
4846 x;
4847
cristy3ed852e2009-09-05 21:47:34 +00004848 if (status == MagickFalse)
4849 continue;
4850 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4851 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4852 exception);
cristy4c08aed2011-07-01 19:47:50 +00004853 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004854 {
4855 status=MagickFalse;
4856 continue;
4857 }
cristyddd82202009-11-03 20:14:50 +00004858 pixel=bias;
cristybb503372010-05-27 20:51:26 +00004859 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004860 {
cristyed231572011-07-14 02:18:59 +00004861 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004862 {
cristy4c08aed2011-07-01 19:47:50 +00004863 pixel.red=GetPixelRed(image,p)-(MagickRealType) GetPixelRed(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004864 if (fabs(2.0*pixel.red) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004865 pixel.red=(MagickRealType) GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004866 else
cristy4c08aed2011-07-01 19:47:50 +00004867 pixel.red=(MagickRealType) GetPixelRed(image,p)+(pixel.red*amount);
4868 SetPixelRed(unsharp_image,ClampToQuantum(pixel.red),q);
cristy3ed852e2009-09-05 21:47:34 +00004869 }
cristyed231572011-07-14 02:18:59 +00004870 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004871 {
cristy4c08aed2011-07-01 19:47:50 +00004872 pixel.green=GetPixelGreen(image,p)-
4873 (MagickRealType) GetPixelGreen(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004874 if (fabs(2.0*pixel.green) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004875 pixel.green=(MagickRealType)
4876 GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004877 else
cristy4c08aed2011-07-01 19:47:50 +00004878 pixel.green=(MagickRealType)
4879 GetPixelGreen(image,p)+
4880 (pixel.green*amount);
4881 SetPixelGreen(unsharp_image,
4882 ClampToQuantum(pixel.green),q);
cristy3ed852e2009-09-05 21:47:34 +00004883 }
cristyed231572011-07-14 02:18:59 +00004884 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004885 {
cristy4c08aed2011-07-01 19:47:50 +00004886 pixel.blue=GetPixelBlue(image,p)-
4887 (MagickRealType) GetPixelBlue(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004888 if (fabs(2.0*pixel.blue) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004889 pixel.blue=(MagickRealType)
4890 GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004891 else
cristy4c08aed2011-07-01 19:47:50 +00004892 pixel.blue=(MagickRealType)
4893 GetPixelBlue(image,p)+(pixel.blue*amount);
4894 SetPixelBlue(unsharp_image,
4895 ClampToQuantum(pixel.blue),q);
cristy3ed852e2009-09-05 21:47:34 +00004896 }
cristyed231572011-07-14 02:18:59 +00004897 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00004898 (image->colorspace == CMYKColorspace))
4899 {
cristy4c08aed2011-07-01 19:47:50 +00004900 pixel.black=GetPixelBlack(image,p)-
4901 (MagickRealType) GetPixelBlack(image,q);
4902 if (fabs(2.0*pixel.black) < quantum_threshold)
4903 pixel.black=(MagickRealType)
4904 GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004905 else
cristy4c08aed2011-07-01 19:47:50 +00004906 pixel.black=(MagickRealType)
4907 GetPixelBlack(image,p)+(pixel.black*
4908 amount);
4909 SetPixelBlack(unsharp_image,
4910 ClampToQuantum(pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00004911 }
cristyed231572011-07-14 02:18:59 +00004912 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004913 {
4914 pixel.alpha=GetPixelAlpha(image,p)-
4915 (MagickRealType) GetPixelAlpha(image,q);
4916 if (fabs(2.0*pixel.alpha) < quantum_threshold)
4917 pixel.alpha=(MagickRealType)
4918 GetPixelAlpha(image,p);
4919 else
4920 pixel.alpha=GetPixelAlpha(image,p)+
4921 (pixel.alpha*amount);
4922 SetPixelAlpha(unsharp_image,
4923 ClampToQuantum(pixel.alpha),q);
4924 }
cristyed231572011-07-14 02:18:59 +00004925 p+=GetPixelChannels(image);
4926 q+=GetPixelChannels(unsharp_image);
cristy3ed852e2009-09-05 21:47:34 +00004927 }
4928 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4929 status=MagickFalse;
4930 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4931 {
4932 MagickBooleanType
4933 proceed;
4934
cristyb5d5f722009-11-04 03:03:49 +00004935#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00004936 #pragma omp critical (MagickCore_UnsharpMaskImage)
cristy3ed852e2009-09-05 21:47:34 +00004937#endif
4938 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4939 if (proceed == MagickFalse)
4940 status=MagickFalse;
4941 }
4942 }
4943 unsharp_image->type=image->type;
4944 unsharp_view=DestroyCacheView(unsharp_view);
4945 image_view=DestroyCacheView(image_view);
4946 if (status == MagickFalse)
4947 unsharp_image=DestroyImage(unsharp_image);
4948 return(unsharp_image);
4949}