blob: 7a612aaa2ba7ab35997df6b4fc21912456e9ecff [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 }
cristya30d9ba2011-07-23 21:00:48 +00001316 center=GetPixelChannels(image)*(image->columns+kernel_info->width)*
1317 (kernel_info->height/2L)+GetPixelChannels(image)*(kernel_info->width/2);
cristybb503372010-05-27 20:51:26 +00001318 for (x=0; x < (ssize_t) image->columns; x++)
cristyfccdab92009-11-30 16:43:57 +00001319 {
cristybb503372010-05-27 20:51:26 +00001320 register ssize_t
cristyed231572011-07-14 02:18:59 +00001321 i;
cristyfccdab92009-11-30 16:43:57 +00001322
cristya30d9ba2011-07-23 21:00:48 +00001323 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristyed231572011-07-14 02:18:59 +00001324 {
cristyed231572011-07-14 02:18:59 +00001325 MagickRealType
cristy4e154852011-07-14 13:28:53 +00001326 alpha,
1327 gamma,
cristyed231572011-07-14 02:18:59 +00001328 pixel;
1329
1330 PixelChannel
1331 channel;
1332
1333 PixelTrait
1334 convolve_traits,
1335 traits;
1336
1337 register const double
1338 *restrict k;
1339
1340 register const Quantum
cristyeb52cde2011-07-17 01:52:52 +00001341 *restrict pixels;
cristyed231572011-07-14 02:18:59 +00001342
1343 register ssize_t
1344 u;
1345
1346 ssize_t
1347 v;
1348
cristy30301712011-07-18 15:06:51 +00001349 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
cristy4e154852011-07-14 13:28:53 +00001350 if (traits == UndefinedPixelTrait)
cristyed231572011-07-14 02:18:59 +00001351 continue;
cristy30301712011-07-18 15:06:51 +00001352 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
cristy4e154852011-07-14 13:28:53 +00001353 convolve_traits=GetPixelChannelMapTraits(convolve_image,channel);
1354 if (convolve_traits == UndefinedPixelTrait)
1355 continue;
1356 if ((convolve_traits & CopyPixelTrait) != 0)
1357 {
cristyf7dc44c2011-07-20 14:41:15 +00001358 q[channel]=p[center+i];
cristy4e154852011-07-14 13:28:53 +00001359 continue;
1360 }
cristy5e6be1e2011-07-16 01:23:39 +00001361 k=kernel_info->values;
cristy105ba3c2011-07-18 02:28:38 +00001362 pixels=p;
cristy0a922382011-07-16 15:30:34 +00001363 pixel=kernel_info->bias;
cristy1ce96d02011-07-14 17:57:24 +00001364 if (((convolve_traits & BlendPixelTrait) == 0) ||
1365 (GetPixelAlphaTraits(image) == UndefinedPixelTrait) ||
cristyed231572011-07-14 02:18:59 +00001366 (image->matte == MagickFalse))
cristyfccdab92009-11-30 16:43:57 +00001367 {
cristyed231572011-07-14 02:18:59 +00001368 /*
cristy4e154852011-07-14 13:28:53 +00001369 No alpha blending.
cristyed231572011-07-14 02:18:59 +00001370 */
cristyeb52cde2011-07-17 01:52:52 +00001371 for (v=0; v < (ssize_t) kernel_info->height; v++)
cristyfccdab92009-11-30 16:43:57 +00001372 {
cristyeb52cde2011-07-17 01:52:52 +00001373 for (u=0; u < (ssize_t) kernel_info->width; u++)
cristy175653e2011-07-10 23:13:34 +00001374 {
cristyeb52cde2011-07-17 01:52:52 +00001375 pixel+=(*k)*pixels[i];
cristyed231572011-07-14 02:18:59 +00001376 k++;
cristya30d9ba2011-07-23 21:00:48 +00001377 pixels+=GetPixelChannels(image);
cristy175653e2011-07-10 23:13:34 +00001378 }
cristya30d9ba2011-07-23 21:00:48 +00001379 pixels+=image->columns*GetPixelChannels(image);
cristy175653e2011-07-10 23:13:34 +00001380 }
cristyf7dc44c2011-07-20 14:41:15 +00001381 q[channel]=ClampToQuantum(pixel);
cristy4e154852011-07-14 13:28:53 +00001382 continue;
cristyed231572011-07-14 02:18:59 +00001383 }
cristy4e154852011-07-14 13:28:53 +00001384 /*
1385 Alpha blending.
1386 */
1387 gamma=0.0;
cristyeb52cde2011-07-17 01:52:52 +00001388 for (v=0; v < (ssize_t) kernel_info->height; v++)
cristy4e154852011-07-14 13:28:53 +00001389 {
cristyeb52cde2011-07-17 01:52:52 +00001390 for (u=0; u < (ssize_t) kernel_info->width; u++)
cristy4e154852011-07-14 13:28:53 +00001391 {
cristyeb52cde2011-07-17 01:52:52 +00001392 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1393 pixel+=(*k)*alpha*pixels[i];
cristy4e154852011-07-14 13:28:53 +00001394 gamma+=(*k)*alpha;
1395 k++;
cristya30d9ba2011-07-23 21:00:48 +00001396 pixels+=GetPixelChannels(image);
cristy4e154852011-07-14 13:28:53 +00001397 }
cristya30d9ba2011-07-23 21:00:48 +00001398 pixels+=image->columns*GetPixelChannels(image);
cristy4e154852011-07-14 13:28:53 +00001399 }
cristy1ce96d02011-07-14 17:57:24 +00001400 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristye7a41c92011-07-20 21:31:01 +00001401 q[channel]=ClampToQuantum(gamma*pixel);
cristyed231572011-07-14 02:18:59 +00001402 }
cristya30d9ba2011-07-23 21:00:48 +00001403 p+=GetPixelChannels(image);
1404 q+=GetPixelChannels(convolve_image);
cristyfccdab92009-11-30 16:43:57 +00001405 }
cristyed231572011-07-14 02:18:59 +00001406 if (SyncCacheViewAuthenticPixels(convolve_view,exception) == MagickFalse)
cristyfccdab92009-11-30 16:43:57 +00001407 status=MagickFalse;
1408 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1409 {
1410 MagickBooleanType
1411 proceed;
1412
1413#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001414 #pragma omp critical (MagickCore_ConvolveImage)
cristyfccdab92009-11-30 16:43:57 +00001415#endif
1416 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1417 if (proceed == MagickFalse)
1418 status=MagickFalse;
1419 }
1420 }
1421 convolve_image->type=image->type;
1422 convolve_view=DestroyCacheView(convolve_view);
1423 image_view=DestroyCacheView(image_view);
cristyfccdab92009-11-30 16:43:57 +00001424 if (status == MagickFalse)
1425 convolve_image=DestroyImage(convolve_image);
1426 return(convolve_image);
1427}
1428
1429/*
1430%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1431% %
1432% %
1433% %
cristy3ed852e2009-09-05 21:47:34 +00001434% D e s p e c k l e I m a g e %
1435% %
1436% %
1437% %
1438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1439%
1440% DespeckleImage() reduces the speckle noise in an image while perserving the
1441% edges of the original image.
1442%
1443% The format of the DespeckleImage method is:
1444%
1445% Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1446%
1447% A description of each parameter follows:
1448%
1449% o image: the image.
1450%
1451% o exception: return any errors or warnings in this structure.
1452%
1453*/
1454
cristybb503372010-05-27 20:51:26 +00001455static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1456 const size_t columns,const size_t rows,Quantum *f,Quantum *g,
cristy3ed852e2009-09-05 21:47:34 +00001457 const int polarity)
1458{
cristy3ed852e2009-09-05 21:47:34 +00001459 MagickRealType
1460 v;
1461
cristy3ed852e2009-09-05 21:47:34 +00001462 register Quantum
1463 *p,
1464 *q,
1465 *r,
1466 *s;
1467
cristy117ff172010-08-15 21:35:32 +00001468 register ssize_t
1469 x;
1470
1471 ssize_t
1472 y;
1473
cristy3ed852e2009-09-05 21:47:34 +00001474 assert(f != (Quantum *) NULL);
1475 assert(g != (Quantum *) NULL);
1476 p=f+(columns+2);
1477 q=g+(columns+2);
cristybb503372010-05-27 20:51:26 +00001478 r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1479 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001480 {
1481 p++;
1482 q++;
1483 r++;
1484 if (polarity > 0)
cristybb503372010-05-27 20:51:26 +00001485 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001486 {
1487 v=(MagickRealType) (*p);
1488 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1489 v+=ScaleCharToQuantum(1);
1490 *q=(Quantum) v;
1491 p++;
1492 q++;
1493 r++;
1494 }
1495 else
cristybb503372010-05-27 20:51:26 +00001496 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001497 {
1498 v=(MagickRealType) (*p);
1499 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
cristybb503372010-05-27 20:51:26 +00001500 v-=(ssize_t) ScaleCharToQuantum(1);
cristy3ed852e2009-09-05 21:47:34 +00001501 *q=(Quantum) v;
1502 p++;
1503 q++;
1504 r++;
1505 }
1506 p++;
1507 q++;
1508 r++;
1509 }
1510 p=f+(columns+2);
1511 q=g+(columns+2);
cristybb503372010-05-27 20:51:26 +00001512 r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1513 s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1514 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001515 {
1516 p++;
1517 q++;
1518 r++;
1519 s++;
1520 if (polarity > 0)
cristybb503372010-05-27 20:51:26 +00001521 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001522 {
1523 v=(MagickRealType) (*q);
1524 if (((MagickRealType) *s >=
1525 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1526 ((MagickRealType) *r > v))
1527 v+=ScaleCharToQuantum(1);
1528 *p=(Quantum) v;
1529 p++;
1530 q++;
1531 r++;
1532 s++;
1533 }
1534 else
cristybb503372010-05-27 20:51:26 +00001535 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001536 {
1537 v=(MagickRealType) (*q);
1538 if (((MagickRealType) *s <=
1539 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1540 ((MagickRealType) *r < v))
1541 v-=(MagickRealType) ScaleCharToQuantum(1);
1542 *p=(Quantum) v;
1543 p++;
1544 q++;
1545 r++;
1546 s++;
1547 }
1548 p++;
1549 q++;
1550 r++;
1551 s++;
1552 }
1553}
1554
1555MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1556{
1557#define DespeckleImageTag "Despeckle/Image"
1558
cristy2407fc22009-09-11 00:55:25 +00001559 CacheView
1560 *despeckle_view,
1561 *image_view;
1562
cristy3ed852e2009-09-05 21:47:34 +00001563 Image
1564 *despeckle_image;
1565
cristy3ed852e2009-09-05 21:47:34 +00001566 MagickBooleanType
1567 status;
1568
cristya58c3172011-02-19 19:23:11 +00001569 register ssize_t
1570 i;
1571
cristy3ed852e2009-09-05 21:47:34 +00001572 Quantum
cristy65b9f392011-02-22 14:22:54 +00001573 *restrict buffers,
1574 *restrict pixels;
cristy3ed852e2009-09-05 21:47:34 +00001575
1576 size_t
cristya58c3172011-02-19 19:23:11 +00001577 length,
1578 number_channels;
cristy117ff172010-08-15 21:35:32 +00001579
cristybb503372010-05-27 20:51:26 +00001580 static const ssize_t
cristy691a29e2009-09-11 00:44:10 +00001581 X[4] = {0, 1, 1,-1},
1582 Y[4] = {1, 0, 1, 1};
cristy3ed852e2009-09-05 21:47:34 +00001583
cristy3ed852e2009-09-05 21:47:34 +00001584 /*
1585 Allocate despeckled image.
1586 */
1587 assert(image != (const Image *) NULL);
1588 assert(image->signature == MagickSignature);
1589 if (image->debug != MagickFalse)
1590 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1591 assert(exception != (ExceptionInfo *) NULL);
1592 assert(exception->signature == MagickSignature);
1593 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1594 exception);
1595 if (despeckle_image == (Image *) NULL)
1596 return((Image *) NULL);
1597 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1598 {
1599 InheritException(exception,&despeckle_image->exception);
1600 despeckle_image=DestroyImage(despeckle_image);
1601 return((Image *) NULL);
1602 }
1603 /*
1604 Allocate image buffers.
1605 */
1606 length=(size_t) ((image->columns+2)*(image->rows+2));
cristy65b9f392011-02-22 14:22:54 +00001607 pixels=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1608 buffers=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1609 if ((pixels == (Quantum *) NULL) || (buffers == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001610 {
cristy65b9f392011-02-22 14:22:54 +00001611 if (buffers != (Quantum *) NULL)
1612 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1613 if (pixels != (Quantum *) NULL)
1614 pixels=(Quantum *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001615 despeckle_image=DestroyImage(despeckle_image);
1616 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1617 }
1618 /*
1619 Reduce speckle in the image.
1620 */
1621 status=MagickTrue;
cristy109695a2011-02-19 19:38:14 +00001622 number_channels=(size_t) (image->colorspace == CMYKColorspace ? 5 : 4);
cristy3ed852e2009-09-05 21:47:34 +00001623 image_view=AcquireCacheView(image);
1624 despeckle_view=AcquireCacheView(despeckle_image);
cristy8df3d002011-02-19 19:40:59 +00001625 for (i=0; i < (ssize_t) number_channels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001626 {
cristy3ed852e2009-09-05 21:47:34 +00001627 register Quantum
1628 *buffer,
1629 *pixel;
1630
cristyc1488b52011-02-19 18:54:15 +00001631 register ssize_t
cristya58c3172011-02-19 19:23:11 +00001632 k,
cristyc1488b52011-02-19 18:54:15 +00001633 x;
1634
cristy117ff172010-08-15 21:35:32 +00001635 ssize_t
1636 j,
1637 y;
1638
cristy3ed852e2009-09-05 21:47:34 +00001639 if (status == MagickFalse)
1640 continue;
cristy65b9f392011-02-22 14:22:54 +00001641 pixel=pixels;
cristy3ed852e2009-09-05 21:47:34 +00001642 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
cristy65b9f392011-02-22 14:22:54 +00001643 buffer=buffers;
cristybb503372010-05-27 20:51:26 +00001644 j=(ssize_t) image->columns+2;
1645 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001646 {
cristy4c08aed2011-07-01 19:47:50 +00001647 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001648 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001649
1650 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001651 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001652 break;
1653 j++;
cristybb503372010-05-27 20:51:26 +00001654 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001655 {
cristya58c3172011-02-19 19:23:11 +00001656 switch (i)
cristy3ed852e2009-09-05 21:47:34 +00001657 {
cristy4c08aed2011-07-01 19:47:50 +00001658 case 0: pixel[j]=GetPixelRed(image,p); break;
1659 case 1: pixel[j]=GetPixelGreen(image,p); break;
1660 case 2: pixel[j]=GetPixelBlue(image,p); break;
1661 case 3: pixel[j]=GetPixelAlpha(image,p); break;
1662 case 4: pixel[j]=GetPixelBlack(image,p); break;
cristy3ed852e2009-09-05 21:47:34 +00001663 default: break;
1664 }
cristyed231572011-07-14 02:18:59 +00001665 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001666 j++;
1667 }
1668 j++;
1669 }
cristy3ed852e2009-09-05 21:47:34 +00001670 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
cristya58c3172011-02-19 19:23:11 +00001671 for (k=0; k < 4; k++)
cristy3ed852e2009-09-05 21:47:34 +00001672 {
cristya58c3172011-02-19 19:23:11 +00001673 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,1);
1674 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);
cristy3ed852e2009-09-05 21:47:34 +00001677 }
cristybb503372010-05-27 20:51:26 +00001678 j=(ssize_t) image->columns+2;
1679 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001680 {
1681 MagickBooleanType
1682 sync;
1683
cristy4c08aed2011-07-01 19:47:50 +00001684 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001685 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001686
1687 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1688 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001689 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001690 break;
1691 j++;
cristybb503372010-05-27 20:51:26 +00001692 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001693 {
cristya58c3172011-02-19 19:23:11 +00001694 switch (i)
cristy3ed852e2009-09-05 21:47:34 +00001695 {
cristy4c08aed2011-07-01 19:47:50 +00001696 case 0: SetPixelRed(despeckle_image,pixel[j],q); break;
1697 case 1: SetPixelGreen(despeckle_image,pixel[j],q); break;
1698 case 2: SetPixelBlue(despeckle_image,pixel[j],q); break;
1699 case 3: SetPixelAlpha(despeckle_image,pixel[j],q); break;
1700 case 4: SetPixelBlack(despeckle_image,pixel[j],q); break;
cristy3ed852e2009-09-05 21:47:34 +00001701 default: break;
1702 }
cristyed231572011-07-14 02:18:59 +00001703 q+=GetPixelChannels(despeckle_image);
cristy3ed852e2009-09-05 21:47:34 +00001704 j++;
1705 }
1706 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1707 if (sync == MagickFalse)
1708 {
1709 status=MagickFalse;
1710 break;
1711 }
1712 j++;
1713 }
1714 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1715 {
1716 MagickBooleanType
1717 proceed;
1718
cristya58c3172011-02-19 19:23:11 +00001719 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1720 number_channels);
cristy3ed852e2009-09-05 21:47:34 +00001721 if (proceed == MagickFalse)
1722 status=MagickFalse;
1723 }
1724 }
1725 despeckle_view=DestroyCacheView(despeckle_view);
1726 image_view=DestroyCacheView(image_view);
cristy65b9f392011-02-22 14:22:54 +00001727 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1728 pixels=(Quantum *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001729 despeckle_image->type=image->type;
1730 if (status == MagickFalse)
1731 despeckle_image=DestroyImage(despeckle_image);
1732 return(despeckle_image);
1733}
1734
1735/*
1736%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1737% %
1738% %
1739% %
1740% E d g e I m a g e %
1741% %
1742% %
1743% %
1744%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1745%
1746% EdgeImage() finds edges in an image. Radius defines the radius of the
1747% convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1748% radius for you.
1749%
1750% The format of the EdgeImage method is:
1751%
1752% Image *EdgeImage(const Image *image,const double radius,
1753% ExceptionInfo *exception)
1754%
1755% A description of each parameter follows:
1756%
1757% o image: the image.
1758%
1759% o radius: the radius of the pixel neighborhood.
1760%
1761% o exception: return any errors or warnings in this structure.
1762%
1763*/
1764MagickExport Image *EdgeImage(const Image *image,const double radius,
1765 ExceptionInfo *exception)
1766{
1767 Image
1768 *edge_image;
1769
cristy41cbe682011-07-15 19:12:37 +00001770 KernelInfo
1771 *kernel_info;
cristy3ed852e2009-09-05 21:47:34 +00001772
cristybb503372010-05-27 20:51:26 +00001773 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001774 i;
1775
cristybb503372010-05-27 20:51:26 +00001776 size_t
cristy3ed852e2009-09-05 21:47:34 +00001777 width;
1778
cristy41cbe682011-07-15 19:12:37 +00001779 ssize_t
1780 j,
1781 u,
1782 v;
1783
cristy3ed852e2009-09-05 21:47:34 +00001784 assert(image != (const Image *) NULL);
1785 assert(image->signature == MagickSignature);
1786 if (image->debug != MagickFalse)
1787 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1788 assert(exception != (ExceptionInfo *) NULL);
1789 assert(exception->signature == MagickSignature);
1790 width=GetOptimalKernelWidth1D(radius,0.5);
cristy5e6be1e2011-07-16 01:23:39 +00001791 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001792 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001793 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001794 kernel_info->width=width;
1795 kernel_info->height=width;
cristy41cbe682011-07-15 19:12:37 +00001796 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1797 kernel_info->width*sizeof(*kernel_info->values));
1798 if (kernel_info->values == (double *) NULL)
1799 {
1800 kernel_info=DestroyKernelInfo(kernel_info);
1801 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1802 }
1803 j=(ssize_t) kernel_info->width/2;
1804 i=0;
1805 for (v=(-j); v <= j; v++)
1806 {
1807 for (u=(-j); u <= j; u++)
1808 {
1809 kernel_info->values[i]=(-1.0);
1810 i++;
1811 }
1812 }
1813 kernel_info->values[i/2]=(double) (width*width-1.0);
cristy0a922382011-07-16 15:30:34 +00001814 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00001815 edge_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001816 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001817 return(edge_image);
1818}
1819
1820/*
1821%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1822% %
1823% %
1824% %
1825% E m b o s s I m a g e %
1826% %
1827% %
1828% %
1829%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1830%
1831% EmbossImage() returns a grayscale image with a three-dimensional effect.
1832% We convolve the image with a Gaussian operator of the given radius and
1833% standard deviation (sigma). For reasonable results, radius should be
1834% larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1835% radius for you.
1836%
1837% The format of the EmbossImage method is:
1838%
1839% Image *EmbossImage(const Image *image,const double radius,
1840% const double sigma,ExceptionInfo *exception)
1841%
1842% A description of each parameter follows:
1843%
1844% o image: the image.
1845%
1846% o radius: the radius of the pixel neighborhood.
1847%
1848% o sigma: the standard deviation of the Gaussian, in pixels.
1849%
1850% o exception: return any errors or warnings in this structure.
1851%
1852*/
1853MagickExport Image *EmbossImage(const Image *image,const double radius,
1854 const double sigma,ExceptionInfo *exception)
1855{
cristy3ed852e2009-09-05 21:47:34 +00001856 Image
1857 *emboss_image;
1858
cristy41cbe682011-07-15 19:12:37 +00001859 KernelInfo
1860 *kernel_info;
1861
cristybb503372010-05-27 20:51:26 +00001862 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001863 i;
1864
cristybb503372010-05-27 20:51:26 +00001865 size_t
cristy3ed852e2009-09-05 21:47:34 +00001866 width;
1867
cristy117ff172010-08-15 21:35:32 +00001868 ssize_t
1869 j,
1870 k,
1871 u,
1872 v;
1873
cristy41cbe682011-07-15 19:12:37 +00001874 assert(image != (const Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001875 assert(image->signature == MagickSignature);
1876 if (image->debug != MagickFalse)
1877 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1878 assert(exception != (ExceptionInfo *) NULL);
1879 assert(exception->signature == MagickSignature);
1880 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001881 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001882 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001883 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001884 kernel_info->width=width;
1885 kernel_info->height=width;
cristy41cbe682011-07-15 19:12:37 +00001886 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1887 kernel_info->width*sizeof(*kernel_info->values));
1888 if (kernel_info->values == (double *) NULL)
1889 {
1890 kernel_info=DestroyKernelInfo(kernel_info);
1891 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1892 }
1893 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00001894 k=j;
1895 i=0;
1896 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00001897 {
cristy47e00502009-12-17 19:19:57 +00001898 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00001899 {
cristy41cbe682011-07-15 19:12:37 +00001900 kernel_info->values[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
cristy47e00502009-12-17 19:19:57 +00001901 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
cristy4205a3c2010-09-12 20:19:59 +00001902 (2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +00001903 if (u != k)
cristy41cbe682011-07-15 19:12:37 +00001904 kernel_info->values[i]=0.0;
cristy3ed852e2009-09-05 21:47:34 +00001905 i++;
1906 }
cristy47e00502009-12-17 19:19:57 +00001907 k--;
cristy3ed852e2009-09-05 21:47:34 +00001908 }
cristy0a922382011-07-16 15:30:34 +00001909 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00001910 emboss_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001911 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001912 if (emboss_image != (Image *) NULL)
1913 (void) EqualizeImage(emboss_image);
cristy3ed852e2009-09-05 21:47:34 +00001914 return(emboss_image);
1915}
1916
1917/*
1918%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1919% %
1920% %
1921% %
1922% G a u s s i a n B l u r I m a g e %
1923% %
1924% %
1925% %
1926%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1927%
1928% GaussianBlurImage() blurs an image. We convolve the image with a
1929% Gaussian operator of the given radius and standard deviation (sigma).
1930% For reasonable results, the radius should be larger than sigma. Use a
1931% radius of 0 and GaussianBlurImage() selects a suitable radius for you
1932%
1933% The format of the GaussianBlurImage method is:
1934%
1935% Image *GaussianBlurImage(const Image *image,onst double radius,
1936% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001937%
1938% A description of each parameter follows:
1939%
1940% o image: the image.
1941%
cristy3ed852e2009-09-05 21:47:34 +00001942% o radius: the radius of the Gaussian, in pixels, not counting the center
1943% pixel.
1944%
1945% o sigma: the standard deviation of the Gaussian, in pixels.
1946%
1947% o exception: return any errors or warnings in this structure.
1948%
1949*/
cristy41cbe682011-07-15 19:12:37 +00001950MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1951 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001952{
cristy3ed852e2009-09-05 21:47:34 +00001953 Image
1954 *blur_image;
1955
cristy41cbe682011-07-15 19:12:37 +00001956 KernelInfo
1957 *kernel_info;
1958
cristybb503372010-05-27 20:51:26 +00001959 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001960 i;
1961
cristybb503372010-05-27 20:51:26 +00001962 size_t
cristy3ed852e2009-09-05 21:47:34 +00001963 width;
1964
cristy117ff172010-08-15 21:35:32 +00001965 ssize_t
1966 j,
1967 u,
1968 v;
1969
cristy3ed852e2009-09-05 21:47:34 +00001970 assert(image != (const Image *) NULL);
1971 assert(image->signature == MagickSignature);
1972 if (image->debug != MagickFalse)
1973 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1974 assert(exception != (ExceptionInfo *) NULL);
1975 assert(exception->signature == MagickSignature);
1976 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001977 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001978 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001979 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001980 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1981 kernel_info->width=width;
1982 kernel_info->height=width;
1983 kernel_info->signature=MagickSignature;
1984 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1985 kernel_info->width*sizeof(*kernel_info->values));
1986 if (kernel_info->values == (double *) NULL)
1987 {
1988 kernel_info=DestroyKernelInfo(kernel_info);
1989 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1990 }
1991 j=(ssize_t) kernel_info->width/2;
cristy3ed852e2009-09-05 21:47:34 +00001992 i=0;
cristy47e00502009-12-17 19:19:57 +00001993 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00001994 {
cristy47e00502009-12-17 19:19:57 +00001995 for (u=(-j); u <= j; u++)
cristy41cbe682011-07-15 19:12:37 +00001996 {
1997 kernel_info->values[i]=(double) (exp(-((double) u*u+v*v)/(2.0*
1998 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
1999 i++;
2000 }
cristy3ed852e2009-09-05 21:47:34 +00002001 }
cristy0a922382011-07-16 15:30:34 +00002002 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00002003 blur_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00002004 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00002005 return(blur_image);
2006}
2007
2008/*
2009%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2010% %
2011% %
2012% %
cristy3ed852e2009-09-05 21:47:34 +00002013% M o t i o n B l u r I m a g e %
2014% %
2015% %
2016% %
2017%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2018%
2019% MotionBlurImage() simulates motion blur. We convolve the image with a
2020% Gaussian operator of the given radius and standard deviation (sigma).
2021% For reasonable results, radius should be larger than sigma. Use a
2022% radius of 0 and MotionBlurImage() selects a suitable radius for you.
2023% Angle gives the angle of the blurring motion.
2024%
2025% Andrew Protano contributed this effect.
2026%
2027% The format of the MotionBlurImage method is:
2028%
2029% Image *MotionBlurImage(const Image *image,const double radius,
2030% const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002031%
2032% A description of each parameter follows:
2033%
2034% o image: the image.
2035%
cristy3ed852e2009-09-05 21:47:34 +00002036% o radius: the radius of the Gaussian, in pixels, not counting
2037% the center pixel.
2038%
2039% o sigma: the standard deviation of the Gaussian, in pixels.
2040%
cristycee97112010-05-28 00:44:52 +00002041% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00002042%
2043% o exception: return any errors or warnings in this structure.
2044%
2045*/
2046
cristybb503372010-05-27 20:51:26 +00002047static double *GetMotionBlurKernel(const size_t width,const double sigma)
cristy3ed852e2009-09-05 21:47:34 +00002048{
cristy3ed852e2009-09-05 21:47:34 +00002049 double
cristy47e00502009-12-17 19:19:57 +00002050 *kernel,
cristy3ed852e2009-09-05 21:47:34 +00002051 normalize;
2052
cristybb503372010-05-27 20:51:26 +00002053 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002054 i;
2055
2056 /*
cristy47e00502009-12-17 19:19:57 +00002057 Generate a 1-D convolution kernel.
cristy3ed852e2009-09-05 21:47:34 +00002058 */
2059 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2060 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2061 if (kernel == (double *) NULL)
2062 return(kernel);
cristy3ed852e2009-09-05 21:47:34 +00002063 normalize=0.0;
cristybb503372010-05-27 20:51:26 +00002064 for (i=0; i < (ssize_t) width; i++)
cristy47e00502009-12-17 19:19:57 +00002065 {
cristy4205a3c2010-09-12 20:19:59 +00002066 kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
2067 MagickSigma)))/(MagickSQ2PI*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00002068 normalize+=kernel[i];
cristy47e00502009-12-17 19:19:57 +00002069 }
cristybb503372010-05-27 20:51:26 +00002070 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002071 kernel[i]/=normalize;
2072 return(kernel);
2073}
2074
cristyf4ad9df2011-07-08 16:49:03 +00002075MagickExport Image *MotionBlurImage(const Image *image,
2076 const double radius,const double sigma,const double angle,
2077 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002078{
cristyc4c8d132010-01-07 01:58:38 +00002079 CacheView
2080 *blur_view,
2081 *image_view;
2082
cristy3ed852e2009-09-05 21:47:34 +00002083 double
2084 *kernel;
2085
2086 Image
2087 *blur_image;
2088
cristy3ed852e2009-09-05 21:47:34 +00002089 MagickBooleanType
2090 status;
2091
cristybb503372010-05-27 20:51:26 +00002092 MagickOffsetType
2093 progress;
2094
cristy4c08aed2011-07-01 19:47:50 +00002095 PixelInfo
cristyddd82202009-11-03 20:14:50 +00002096 bias;
cristy3ed852e2009-09-05 21:47:34 +00002097
2098 OffsetInfo
2099 *offset;
2100
2101 PointInfo
2102 point;
2103
cristybb503372010-05-27 20:51:26 +00002104 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002105 i;
2106
cristybb503372010-05-27 20:51:26 +00002107 size_t
cristy3ed852e2009-09-05 21:47:34 +00002108 width;
2109
cristybb503372010-05-27 20:51:26 +00002110 ssize_t
2111 y;
2112
cristy3ed852e2009-09-05 21:47:34 +00002113 assert(image != (Image *) NULL);
2114 assert(image->signature == MagickSignature);
2115 if (image->debug != MagickFalse)
2116 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2117 assert(exception != (ExceptionInfo *) NULL);
2118 width=GetOptimalKernelWidth1D(radius,sigma);
2119 kernel=GetMotionBlurKernel(width,sigma);
2120 if (kernel == (double *) NULL)
2121 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2122 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2123 if (offset == (OffsetInfo *) NULL)
2124 {
2125 kernel=(double *) RelinquishMagickMemory(kernel);
2126 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2127 }
2128 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2129 if (blur_image == (Image *) NULL)
2130 {
2131 kernel=(double *) RelinquishMagickMemory(kernel);
2132 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2133 return((Image *) NULL);
2134 }
2135 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2136 {
2137 kernel=(double *) RelinquishMagickMemory(kernel);
2138 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2139 InheritException(exception,&blur_image->exception);
2140 blur_image=DestroyImage(blur_image);
2141 return((Image *) NULL);
2142 }
2143 point.x=(double) width*sin(DegreesToRadians(angle));
2144 point.y=(double) width*cos(DegreesToRadians(angle));
cristybb503372010-05-27 20:51:26 +00002145 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002146 {
cristybb503372010-05-27 20:51:26 +00002147 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2148 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
cristy3ed852e2009-09-05 21:47:34 +00002149 }
2150 /*
2151 Motion blur image.
2152 */
2153 status=MagickTrue;
2154 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00002155 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00002156 image_view=AcquireCacheView(image);
2157 blur_view=AcquireCacheView(blur_image);
cristyb557a152011-02-22 12:14:30 +00002158#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +00002159 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +00002160#endif
cristybb503372010-05-27 20:51:26 +00002161 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002162 {
cristy4c08aed2011-07-01 19:47:50 +00002163 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002164 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002165
cristy117ff172010-08-15 21:35:32 +00002166 register ssize_t
2167 x;
2168
cristy3ed852e2009-09-05 21:47:34 +00002169 if (status == MagickFalse)
2170 continue;
2171 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2172 exception);
cristy4c08aed2011-07-01 19:47:50 +00002173 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002174 {
2175 status=MagickFalse;
2176 continue;
2177 }
cristybb503372010-05-27 20:51:26 +00002178 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002179 {
cristy4c08aed2011-07-01 19:47:50 +00002180 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002181 qixel;
2182
2183 PixelPacket
2184 pixel;
2185
2186 register double
cristyc47d1f82009-11-26 01:44:43 +00002187 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00002188
cristybb503372010-05-27 20:51:26 +00002189 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002190 i;
2191
cristy3ed852e2009-09-05 21:47:34 +00002192 k=kernel;
cristyddd82202009-11-03 20:14:50 +00002193 qixel=bias;
cristyed231572011-07-14 02:18:59 +00002194 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002195 {
cristybb503372010-05-27 20:51:26 +00002196 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002197 {
2198 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2199 offset[i].y,&pixel,exception);
2200 qixel.red+=(*k)*pixel.red;
2201 qixel.green+=(*k)*pixel.green;
2202 qixel.blue+=(*k)*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00002203 qixel.alpha+=(*k)*pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002204 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002205 qixel.black+=(*k)*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00002206 k++;
2207 }
cristyed231572011-07-14 02:18:59 +00002208 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002209 SetPixelRed(blur_image,
2210 ClampToQuantum(qixel.red),q);
cristyed231572011-07-14 02:18:59 +00002211 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002212 SetPixelGreen(blur_image,
2213 ClampToQuantum(qixel.green),q);
cristyed231572011-07-14 02:18:59 +00002214 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002215 SetPixelBlue(blur_image,
2216 ClampToQuantum(qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00002217 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00002218 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00002219 SetPixelBlack(blur_image,
2220 ClampToQuantum(qixel.black),q);
cristyed231572011-07-14 02:18:59 +00002221 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002222 SetPixelAlpha(blur_image,
2223 ClampToQuantum(qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00002224 }
2225 else
2226 {
2227 MagickRealType
2228 alpha,
2229 gamma;
2230
2231 alpha=0.0;
2232 gamma=0.0;
cristybb503372010-05-27 20:51:26 +00002233 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002234 {
2235 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2236 offset[i].y,&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00002237 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00002238 qixel.red+=(*k)*alpha*pixel.red;
2239 qixel.green+=(*k)*alpha*pixel.green;
2240 qixel.blue+=(*k)*alpha*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00002241 qixel.alpha+=(*k)*pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002242 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002243 qixel.black+=(*k)*alpha*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00002244 gamma+=(*k)*alpha;
2245 k++;
2246 }
2247 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00002248 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002249 SetPixelRed(blur_image,
2250 ClampToQuantum(gamma*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00002251 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002252 SetPixelGreen(blur_image,
2253 ClampToQuantum(gamma*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00002254 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002255 SetPixelBlue(blur_image,
2256 ClampToQuantum(gamma*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00002257 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00002258 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00002259 SetPixelBlack(blur_image,
2260 ClampToQuantum(gamma*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00002261 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002262 SetPixelAlpha(blur_image,
2263 ClampToQuantum(qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00002264 }
cristyed231572011-07-14 02:18:59 +00002265 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00002266 }
2267 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2268 status=MagickFalse;
2269 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2270 {
2271 MagickBooleanType
2272 proceed;
2273
cristyb557a152011-02-22 12:14:30 +00002274#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00002275 #pragma omp critical (MagickCore_MotionBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00002276#endif
2277 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2278 if (proceed == MagickFalse)
2279 status=MagickFalse;
2280 }
2281 }
2282 blur_view=DestroyCacheView(blur_view);
2283 image_view=DestroyCacheView(image_view);
2284 kernel=(double *) RelinquishMagickMemory(kernel);
2285 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2286 if (status == MagickFalse)
2287 blur_image=DestroyImage(blur_image);
2288 return(blur_image);
2289}
2290
2291/*
2292%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2293% %
2294% %
2295% %
2296% P r e v i e w I m a g e %
2297% %
2298% %
2299% %
2300%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2301%
2302% PreviewImage() tiles 9 thumbnails of the specified image with an image
2303% processing operation applied with varying parameters. This may be helpful
2304% pin-pointing an appropriate parameter for a particular image processing
2305% operation.
2306%
2307% The format of the PreviewImages method is:
2308%
2309% Image *PreviewImages(const Image *image,const PreviewType preview,
2310% ExceptionInfo *exception)
2311%
2312% A description of each parameter follows:
2313%
2314% o image: the image.
2315%
2316% o preview: the image processing operation.
2317%
2318% o exception: return any errors or warnings in this structure.
2319%
2320*/
2321MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2322 ExceptionInfo *exception)
2323{
2324#define NumberTiles 9
2325#define PreviewImageTag "Preview/Image"
2326#define DefaultPreviewGeometry "204x204+10+10"
2327
2328 char
2329 factor[MaxTextExtent],
2330 label[MaxTextExtent];
2331
2332 double
2333 degrees,
2334 gamma,
2335 percentage,
2336 radius,
2337 sigma,
2338 threshold;
2339
2340 Image
2341 *images,
2342 *montage_image,
2343 *preview_image,
2344 *thumbnail;
2345
2346 ImageInfo
2347 *preview_info;
2348
cristy3ed852e2009-09-05 21:47:34 +00002349 MagickBooleanType
2350 proceed;
2351
2352 MontageInfo
2353 *montage_info;
2354
2355 QuantizeInfo
2356 quantize_info;
2357
2358 RectangleInfo
2359 geometry;
2360
cristybb503372010-05-27 20:51:26 +00002361 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002362 i,
2363 x;
2364
cristybb503372010-05-27 20:51:26 +00002365 size_t
cristy3ed852e2009-09-05 21:47:34 +00002366 colors;
2367
cristy117ff172010-08-15 21:35:32 +00002368 ssize_t
2369 y;
2370
cristy3ed852e2009-09-05 21:47:34 +00002371 /*
2372 Open output image file.
2373 */
2374 assert(image != (Image *) NULL);
2375 assert(image->signature == MagickSignature);
2376 if (image->debug != MagickFalse)
2377 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2378 colors=2;
2379 degrees=0.0;
2380 gamma=(-0.2f);
2381 preview_info=AcquireImageInfo();
2382 SetGeometry(image,&geometry);
2383 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2384 &geometry.width,&geometry.height);
2385 images=NewImageList();
2386 percentage=12.5;
2387 GetQuantizeInfo(&quantize_info);
2388 radius=0.0;
2389 sigma=1.0;
2390 threshold=0.0;
2391 x=0;
2392 y=0;
2393 for (i=0; i < NumberTiles; i++)
2394 {
2395 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2396 if (thumbnail == (Image *) NULL)
2397 break;
2398 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2399 (void *) NULL);
2400 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2401 if (i == (NumberTiles/2))
2402 {
2403 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
2404 AppendImageToList(&images,thumbnail);
2405 continue;
2406 }
2407 switch (preview)
2408 {
2409 case RotatePreview:
2410 {
2411 degrees+=45.0;
2412 preview_image=RotateImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002413 (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002414 break;
2415 }
2416 case ShearPreview:
2417 {
2418 degrees+=5.0;
2419 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002420 (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002421 degrees,2.0*degrees);
2422 break;
2423 }
2424 case RollPreview:
2425 {
cristybb503372010-05-27 20:51:26 +00002426 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2427 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
cristy3ed852e2009-09-05 21:47:34 +00002428 preview_image=RollImage(thumbnail,x,y,exception);
cristyb51dff52011-05-19 16:55:47 +00002429 (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
cristye8c25f92010-06-03 00:53:06 +00002430 (double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00002431 break;
2432 }
2433 case HuePreview:
2434 {
2435 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2436 if (preview_image == (Image *) NULL)
2437 break;
cristyb51dff52011-05-19 16:55:47 +00002438 (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",
cristy3ed852e2009-09-05 21:47:34 +00002439 2.0*percentage);
2440 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002441 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002442 break;
2443 }
2444 case SaturationPreview:
2445 {
2446 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2447 if (preview_image == (Image *) NULL)
2448 break;
cristyb51dff52011-05-19 16:55:47 +00002449 (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",
cristy8cd5b312010-01-07 01:10:24 +00002450 2.0*percentage);
cristy3ed852e2009-09-05 21:47:34 +00002451 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002452 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002453 break;
2454 }
2455 case BrightnessPreview:
2456 {
2457 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2458 if (preview_image == (Image *) NULL)
2459 break;
cristyb51dff52011-05-19 16:55:47 +00002460 (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
cristy3ed852e2009-09-05 21:47:34 +00002461 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002462 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002463 break;
2464 }
2465 case GammaPreview:
2466 default:
2467 {
2468 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2469 if (preview_image == (Image *) NULL)
2470 break;
2471 gamma+=0.4f;
cristy50fbc382011-07-07 02:19:17 +00002472 (void) GammaImage(preview_image,gamma);
cristyb51dff52011-05-19 16:55:47 +00002473 (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
cristy3ed852e2009-09-05 21:47:34 +00002474 break;
2475 }
2476 case SpiffPreview:
2477 {
2478 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2479 if (preview_image != (Image *) NULL)
2480 for (x=0; x < i; x++)
2481 (void) ContrastImage(preview_image,MagickTrue);
cristyb51dff52011-05-19 16:55:47 +00002482 (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002483 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002484 break;
2485 }
2486 case DullPreview:
2487 {
2488 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2489 if (preview_image == (Image *) NULL)
2490 break;
2491 for (x=0; x < i; x++)
2492 (void) ContrastImage(preview_image,MagickFalse);
cristyb51dff52011-05-19 16:55:47 +00002493 (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002494 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002495 break;
2496 }
2497 case GrayscalePreview:
2498 {
2499 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2500 if (preview_image == (Image *) NULL)
2501 break;
2502 colors<<=1;
2503 quantize_info.number_colors=colors;
2504 quantize_info.colorspace=GRAYColorspace;
2505 (void) QuantizeImage(&quantize_info,preview_image);
cristyb51dff52011-05-19 16:55:47 +00002506 (void) FormatLocaleString(label,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002507 "-colorspace gray -colors %.20g",(double) colors);
cristy3ed852e2009-09-05 21:47:34 +00002508 break;
2509 }
2510 case QuantizePreview:
2511 {
2512 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2513 if (preview_image == (Image *) NULL)
2514 break;
2515 colors<<=1;
2516 quantize_info.number_colors=colors;
2517 (void) QuantizeImage(&quantize_info,preview_image);
cristyb51dff52011-05-19 16:55:47 +00002518 (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002519 colors);
cristy3ed852e2009-09-05 21:47:34 +00002520 break;
2521 }
2522 case DespecklePreview:
2523 {
2524 for (x=0; x < (i-1); x++)
2525 {
2526 preview_image=DespeckleImage(thumbnail,exception);
2527 if (preview_image == (Image *) NULL)
2528 break;
2529 thumbnail=DestroyImage(thumbnail);
2530 thumbnail=preview_image;
2531 }
2532 preview_image=DespeckleImage(thumbnail,exception);
2533 if (preview_image == (Image *) NULL)
2534 break;
cristyb51dff52011-05-19 16:55:47 +00002535 (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002536 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002537 break;
2538 }
2539 case ReduceNoisePreview:
2540 {
cristy95c38342011-03-18 22:39:51 +00002541 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2542 (size_t) radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002543 (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002544 break;
2545 }
2546 case AddNoisePreview:
2547 {
2548 switch ((int) i)
2549 {
2550 case 0:
2551 {
2552 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2553 break;
2554 }
2555 case 1:
2556 {
2557 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2558 break;
2559 }
2560 case 2:
2561 {
2562 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2563 break;
2564 }
2565 case 3:
2566 {
2567 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2568 break;
2569 }
2570 case 4:
2571 {
2572 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2573 break;
2574 }
2575 case 5:
2576 {
2577 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2578 break;
2579 }
2580 default:
2581 {
2582 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2583 break;
2584 }
2585 }
cristyd76c51e2011-03-26 00:21:26 +00002586 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2587 (size_t) i,exception);
cristyb51dff52011-05-19 16:55:47 +00002588 (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002589 break;
2590 }
2591 case SharpenPreview:
2592 {
2593 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002594 (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002595 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002596 break;
2597 }
2598 case BlurPreview:
2599 {
2600 preview_image=BlurImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002601 (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
cristy3ed852e2009-09-05 21:47:34 +00002602 sigma);
2603 break;
2604 }
2605 case ThresholdPreview:
2606 {
2607 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2608 if (preview_image == (Image *) NULL)
2609 break;
2610 (void) BilevelImage(thumbnail,
2611 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
cristyb51dff52011-05-19 16:55:47 +00002612 (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",
cristy3ed852e2009-09-05 21:47:34 +00002613 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2614 break;
2615 }
2616 case EdgeDetectPreview:
2617 {
2618 preview_image=EdgeImage(thumbnail,radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002619 (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002620 break;
2621 }
2622 case SpreadPreview:
2623 {
2624 preview_image=SpreadImage(thumbnail,radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002625 (void) FormatLocaleString(label,MaxTextExtent,"spread %g",
cristy8cd5b312010-01-07 01:10:24 +00002626 radius+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002627 break;
2628 }
2629 case SolarizePreview:
2630 {
2631 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2632 if (preview_image == (Image *) NULL)
2633 break;
2634 (void) SolarizeImage(preview_image,(double) QuantumRange*
2635 percentage/100.0);
cristyb51dff52011-05-19 16:55:47 +00002636 (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
cristy3ed852e2009-09-05 21:47:34 +00002637 (QuantumRange*percentage)/100.0);
2638 break;
2639 }
2640 case ShadePreview:
2641 {
2642 degrees+=10.0;
2643 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2644 exception);
cristyb51dff52011-05-19 16:55:47 +00002645 (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002646 degrees,degrees);
cristy3ed852e2009-09-05 21:47:34 +00002647 break;
2648 }
2649 case RaisePreview:
2650 {
2651 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2652 if (preview_image == (Image *) NULL)
2653 break;
cristybb503372010-05-27 20:51:26 +00002654 geometry.width=(size_t) (2*i+2);
2655 geometry.height=(size_t) (2*i+2);
cristy3ed852e2009-09-05 21:47:34 +00002656 geometry.x=i/2;
2657 geometry.y=i/2;
2658 (void) RaiseImage(preview_image,&geometry,MagickTrue);
cristyb51dff52011-05-19 16:55:47 +00002659 (void) FormatLocaleString(label,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00002660 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002661 geometry.height,(double) geometry.x,(double) geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00002662 break;
2663 }
2664 case SegmentPreview:
2665 {
2666 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2667 if (preview_image == (Image *) NULL)
2668 break;
2669 threshold+=0.4f;
2670 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
2671 threshold);
cristyb51dff52011-05-19 16:55:47 +00002672 (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002673 threshold,threshold);
2674 break;
2675 }
2676 case SwirlPreview:
2677 {
2678 preview_image=SwirlImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002679 (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002680 degrees+=45.0;
2681 break;
2682 }
2683 case ImplodePreview:
2684 {
2685 degrees+=0.1f;
2686 preview_image=ImplodeImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002687 (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002688 break;
2689 }
2690 case WavePreview:
2691 {
2692 degrees+=5.0f;
2693 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002694 (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002695 0.5*degrees,2.0*degrees);
cristy3ed852e2009-09-05 21:47:34 +00002696 break;
2697 }
2698 case OilPaintPreview:
2699 {
2700 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002701 (void) FormatLocaleString(label,MaxTextExtent,"paint %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002702 break;
2703 }
2704 case CharcoalDrawingPreview:
2705 {
2706 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2707 exception);
cristyb51dff52011-05-19 16:55:47 +00002708 (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002709 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002710 break;
2711 }
2712 case JPEGPreview:
2713 {
2714 char
2715 filename[MaxTextExtent];
2716
2717 int
2718 file;
2719
2720 MagickBooleanType
2721 status;
2722
2723 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2724 if (preview_image == (Image *) NULL)
2725 break;
cristybb503372010-05-27 20:51:26 +00002726 preview_info->quality=(size_t) percentage;
cristyb51dff52011-05-19 16:55:47 +00002727 (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002728 preview_info->quality);
cristy3ed852e2009-09-05 21:47:34 +00002729 file=AcquireUniqueFileResource(filename);
2730 if (file != -1)
2731 file=close(file)-1;
cristyb51dff52011-05-19 16:55:47 +00002732 (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00002733 "jpeg:%s",filename);
2734 status=WriteImage(preview_info,preview_image);
2735 if (status != MagickFalse)
2736 {
2737 Image
2738 *quality_image;
2739
2740 (void) CopyMagickString(preview_info->filename,
2741 preview_image->filename,MaxTextExtent);
2742 quality_image=ReadImage(preview_info,exception);
2743 if (quality_image != (Image *) NULL)
2744 {
2745 preview_image=DestroyImage(preview_image);
2746 preview_image=quality_image;
2747 }
2748 }
2749 (void) RelinquishUniqueFileResource(preview_image->filename);
2750 if ((GetBlobSize(preview_image)/1024) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002751 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
cristy3ed852e2009-09-05 21:47:34 +00002752 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2753 1024.0/1024.0);
2754 else
2755 if (GetBlobSize(preview_image) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002756 (void) FormatLocaleString(label,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002757 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
cristy8cd5b312010-01-07 01:10:24 +00002758 GetBlobSize(preview_image))/1024.0);
cristy3ed852e2009-09-05 21:47:34 +00002759 else
cristyb51dff52011-05-19 16:55:47 +00002760 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
cristy54ea5732011-06-10 12:39:53 +00002761 factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
cristy3ed852e2009-09-05 21:47:34 +00002762 break;
2763 }
2764 }
2765 thumbnail=DestroyImage(thumbnail);
2766 percentage+=12.5;
2767 radius+=0.5;
2768 sigma+=0.25;
2769 if (preview_image == (Image *) NULL)
2770 break;
2771 (void) DeleteImageProperty(preview_image,"label");
2772 (void) SetImageProperty(preview_image,"label",label);
2773 AppendImageToList(&images,preview_image);
cristybb503372010-05-27 20:51:26 +00002774 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2775 NumberTiles);
cristy3ed852e2009-09-05 21:47:34 +00002776 if (proceed == MagickFalse)
2777 break;
2778 }
2779 if (images == (Image *) NULL)
2780 {
2781 preview_info=DestroyImageInfo(preview_info);
2782 return((Image *) NULL);
2783 }
2784 /*
2785 Create the montage.
2786 */
2787 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2788 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2789 montage_info->shadow=MagickTrue;
2790 (void) CloneString(&montage_info->tile,"3x3");
2791 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2792 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2793 montage_image=MontageImages(images,montage_info,exception);
2794 montage_info=DestroyMontageInfo(montage_info);
2795 images=DestroyImageList(images);
2796 if (montage_image == (Image *) NULL)
2797 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2798 if (montage_image->montage != (char *) NULL)
2799 {
2800 /*
2801 Free image directory.
2802 */
2803 montage_image->montage=(char *) RelinquishMagickMemory(
2804 montage_image->montage);
2805 if (image->directory != (char *) NULL)
2806 montage_image->directory=(char *) RelinquishMagickMemory(
2807 montage_image->directory);
2808 }
2809 preview_info=DestroyImageInfo(preview_info);
2810 return(montage_image);
2811}
2812
2813/*
2814%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2815% %
2816% %
2817% %
2818% R a d i a l B l u r I m a g e %
2819% %
2820% %
2821% %
2822%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2823%
2824% RadialBlurImage() applies a radial blur to the image.
2825%
2826% Andrew Protano contributed this effect.
2827%
2828% The format of the RadialBlurImage method is:
2829%
2830% Image *RadialBlurImage(const Image *image,const double angle,
2831% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002832%
2833% A description of each parameter follows:
2834%
2835% o image: the image.
2836%
cristy3ed852e2009-09-05 21:47:34 +00002837% o angle: the angle of the radial blur.
2838%
2839% o exception: return any errors or warnings in this structure.
2840%
2841*/
cristyf4ad9df2011-07-08 16:49:03 +00002842MagickExport Image *RadialBlurImage(const Image *image,
2843 const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002844{
cristyc4c8d132010-01-07 01:58:38 +00002845 CacheView
2846 *blur_view,
2847 *image_view;
2848
cristy3ed852e2009-09-05 21:47:34 +00002849 Image
2850 *blur_image;
2851
cristy3ed852e2009-09-05 21:47:34 +00002852 MagickBooleanType
2853 status;
2854
cristybb503372010-05-27 20:51:26 +00002855 MagickOffsetType
2856 progress;
2857
cristy4c08aed2011-07-01 19:47:50 +00002858 PixelInfo
cristyddd82202009-11-03 20:14:50 +00002859 bias;
cristy3ed852e2009-09-05 21:47:34 +00002860
2861 MagickRealType
2862 blur_radius,
2863 *cos_theta,
2864 offset,
2865 *sin_theta,
2866 theta;
2867
2868 PointInfo
2869 blur_center;
2870
cristybb503372010-05-27 20:51:26 +00002871 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002872 i;
2873
cristybb503372010-05-27 20:51:26 +00002874 size_t
cristy3ed852e2009-09-05 21:47:34 +00002875 n;
2876
cristybb503372010-05-27 20:51:26 +00002877 ssize_t
2878 y;
2879
cristy3ed852e2009-09-05 21:47:34 +00002880 /*
2881 Allocate blur image.
2882 */
2883 assert(image != (Image *) NULL);
2884 assert(image->signature == MagickSignature);
2885 if (image->debug != MagickFalse)
2886 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2887 assert(exception != (ExceptionInfo *) NULL);
2888 assert(exception->signature == MagickSignature);
2889 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2890 if (blur_image == (Image *) NULL)
2891 return((Image *) NULL);
2892 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2893 {
2894 InheritException(exception,&blur_image->exception);
2895 blur_image=DestroyImage(blur_image);
2896 return((Image *) NULL);
2897 }
2898 blur_center.x=(double) image->columns/2.0;
2899 blur_center.y=(double) image->rows/2.0;
2900 blur_radius=hypot(blur_center.x,blur_center.y);
cristy117ff172010-08-15 21:35:32 +00002901 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
cristy3ed852e2009-09-05 21:47:34 +00002902 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
2903 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2904 sizeof(*cos_theta));
2905 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2906 sizeof(*sin_theta));
2907 if ((cos_theta == (MagickRealType *) NULL) ||
2908 (sin_theta == (MagickRealType *) NULL))
2909 {
2910 blur_image=DestroyImage(blur_image);
2911 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2912 }
2913 offset=theta*(MagickRealType) (n-1)/2.0;
cristybb503372010-05-27 20:51:26 +00002914 for (i=0; i < (ssize_t) n; i++)
cristy3ed852e2009-09-05 21:47:34 +00002915 {
2916 cos_theta[i]=cos((double) (theta*i-offset));
2917 sin_theta[i]=sin((double) (theta*i-offset));
2918 }
2919 /*
2920 Radial blur image.
2921 */
2922 status=MagickTrue;
2923 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00002924 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00002925 image_view=AcquireCacheView(image);
2926 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00002927#if defined(MAGICKCORE_OPENMP_SUPPORT)
2928 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002929#endif
cristybb503372010-05-27 20:51:26 +00002930 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002931 {
cristy4c08aed2011-07-01 19:47:50 +00002932 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002933 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002934
cristy117ff172010-08-15 21:35:32 +00002935 register ssize_t
2936 x;
2937
cristy3ed852e2009-09-05 21:47:34 +00002938 if (status == MagickFalse)
2939 continue;
2940 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2941 exception);
cristy4c08aed2011-07-01 19:47:50 +00002942 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002943 {
2944 status=MagickFalse;
2945 continue;
2946 }
cristybb503372010-05-27 20:51:26 +00002947 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002948 {
cristy4c08aed2011-07-01 19:47:50 +00002949 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002950 qixel;
2951
2952 MagickRealType
2953 normalize,
2954 radius;
2955
2956 PixelPacket
2957 pixel;
2958
2959 PointInfo
2960 center;
2961
cristybb503372010-05-27 20:51:26 +00002962 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002963 i;
2964
cristybb503372010-05-27 20:51:26 +00002965 size_t
cristy3ed852e2009-09-05 21:47:34 +00002966 step;
2967
2968 center.x=(double) x-blur_center.x;
2969 center.y=(double) y-blur_center.y;
2970 radius=hypot((double) center.x,center.y);
2971 if (radius == 0)
2972 step=1;
2973 else
2974 {
cristybb503372010-05-27 20:51:26 +00002975 step=(size_t) (blur_radius/radius);
cristy3ed852e2009-09-05 21:47:34 +00002976 if (step == 0)
2977 step=1;
2978 else
2979 if (step >= n)
2980 step=n-1;
2981 }
2982 normalize=0.0;
cristyddd82202009-11-03 20:14:50 +00002983 qixel=bias;
cristyed231572011-07-14 02:18:59 +00002984 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002985 {
cristyeaedf062010-05-29 22:36:02 +00002986 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
cristy3ed852e2009-09-05 21:47:34 +00002987 {
cristyeaedf062010-05-29 22:36:02 +00002988 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
2989 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
2990 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
2991 cos_theta[i]+0.5),&pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00002992 qixel.red+=pixel.red;
2993 qixel.green+=pixel.green;
2994 qixel.blue+=pixel.blue;
cristy3ed852e2009-09-05 21:47:34 +00002995 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002996 qixel.black+=pixel.black;
2997 qixel.alpha+=pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002998 normalize+=1.0;
2999 }
3000 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3001 normalize);
cristyed231572011-07-14 02:18:59 +00003002 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003003 SetPixelRed(blur_image,
3004 ClampToQuantum(normalize*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00003005 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003006 SetPixelGreen(blur_image,
3007 ClampToQuantum(normalize*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00003008 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003009 SetPixelBlue(blur_image,
3010 ClampToQuantum(normalize*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003011 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00003012 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00003013 SetPixelBlack(blur_image,
3014 ClampToQuantum(normalize*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00003015 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003016 SetPixelAlpha(blur_image,
3017 ClampToQuantum(normalize*qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003018 }
3019 else
3020 {
3021 MagickRealType
3022 alpha,
3023 gamma;
3024
3025 alpha=1.0;
3026 gamma=0.0;
cristyeaedf062010-05-29 22:36:02 +00003027 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
cristy3ed852e2009-09-05 21:47:34 +00003028 {
cristyeaedf062010-05-29 22:36:02 +00003029 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3030 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
3031 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
3032 cos_theta[i]+0.5),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00003033 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00003034 qixel.red+=alpha*pixel.red;
3035 qixel.green+=alpha*pixel.green;
3036 qixel.blue+=alpha*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00003037 qixel.alpha+=pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00003038 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00003039 qixel.black+=alpha*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00003040 gamma+=alpha;
3041 normalize+=1.0;
3042 }
3043 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3044 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3045 normalize);
cristyed231572011-07-14 02:18:59 +00003046 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003047 SetPixelRed(blur_image,
3048 ClampToQuantum(gamma*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00003049 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003050 SetPixelGreen(blur_image,
3051 ClampToQuantum(gamma*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00003052 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003053 SetPixelBlue(blur_image,
3054 ClampToQuantum(gamma*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003055 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00003056 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00003057 SetPixelBlack(blur_image,
3058 ClampToQuantum(gamma*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00003059 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003060 SetPixelAlpha(blur_image,
3061 ClampToQuantum(normalize*qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003062 }
cristyed231572011-07-14 02:18:59 +00003063 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003064 }
3065 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3066 status=MagickFalse;
3067 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3068 {
3069 MagickBooleanType
3070 proceed;
3071
cristyb5d5f722009-11-04 03:03:49 +00003072#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00003073 #pragma omp critical (MagickCore_RadialBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003074#endif
3075 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3076 if (proceed == MagickFalse)
3077 status=MagickFalse;
3078 }
3079 }
3080 blur_view=DestroyCacheView(blur_view);
3081 image_view=DestroyCacheView(image_view);
3082 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3083 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3084 if (status == MagickFalse)
3085 blur_image=DestroyImage(blur_image);
3086 return(blur_image);
3087}
3088
3089/*
3090%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3091% %
3092% %
3093% %
cristy3ed852e2009-09-05 21:47:34 +00003094% S e l e c t i v e B l u r I m a g e %
3095% %
3096% %
3097% %
3098%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3099%
3100% SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3101% It is similar to the unsharpen mask that sharpens everything with contrast
3102% above a certain threshold.
3103%
3104% The format of the SelectiveBlurImage method is:
3105%
3106% Image *SelectiveBlurImage(const Image *image,const double radius,
3107% const double sigma,const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003108%
3109% A description of each parameter follows:
3110%
3111% o image: the image.
3112%
cristy3ed852e2009-09-05 21:47:34 +00003113% o radius: the radius of the Gaussian, in pixels, not counting the center
3114% pixel.
3115%
3116% o sigma: the standard deviation of the Gaussian, in pixels.
3117%
3118% o threshold: only pixels within this contrast threshold are included
3119% in the blur operation.
3120%
3121% o exception: return any errors or warnings in this structure.
3122%
3123*/
cristyf4ad9df2011-07-08 16:49:03 +00003124MagickExport Image *SelectiveBlurImage(const Image *image,
3125 const double radius,const double sigma,const double threshold,
3126 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003127{
3128#define SelectiveBlurImageTag "SelectiveBlur/Image"
3129
cristy47e00502009-12-17 19:19:57 +00003130 CacheView
3131 *blur_view,
3132 *image_view;
3133
cristy3ed852e2009-09-05 21:47:34 +00003134 double
cristy3ed852e2009-09-05 21:47:34 +00003135 *kernel;
3136
3137 Image
3138 *blur_image;
3139
cristy3ed852e2009-09-05 21:47:34 +00003140 MagickBooleanType
3141 status;
3142
cristybb503372010-05-27 20:51:26 +00003143 MagickOffsetType
3144 progress;
3145
cristy4c08aed2011-07-01 19:47:50 +00003146 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003147 bias;
3148
cristybb503372010-05-27 20:51:26 +00003149 register ssize_t
cristy47e00502009-12-17 19:19:57 +00003150 i;
cristy3ed852e2009-09-05 21:47:34 +00003151
cristybb503372010-05-27 20:51:26 +00003152 size_t
cristy3ed852e2009-09-05 21:47:34 +00003153 width;
3154
cristybb503372010-05-27 20:51:26 +00003155 ssize_t
3156 j,
3157 u,
3158 v,
3159 y;
3160
cristy3ed852e2009-09-05 21:47:34 +00003161 /*
3162 Initialize blur image attributes.
3163 */
3164 assert(image != (Image *) NULL);
3165 assert(image->signature == MagickSignature);
3166 if (image->debug != MagickFalse)
3167 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3168 assert(exception != (ExceptionInfo *) NULL);
3169 assert(exception->signature == MagickSignature);
3170 width=GetOptimalKernelWidth1D(radius,sigma);
3171 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3172 if (kernel == (double *) NULL)
3173 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00003174 j=(ssize_t) width/2;
cristy3ed852e2009-09-05 21:47:34 +00003175 i=0;
cristy47e00502009-12-17 19:19:57 +00003176 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00003177 {
cristy47e00502009-12-17 19:19:57 +00003178 for (u=(-j); u <= j; u++)
cristy4205a3c2010-09-12 20:19:59 +00003179 kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3180 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00003181 }
3182 if (image->debug != MagickFalse)
3183 {
3184 char
3185 format[MaxTextExtent],
3186 *message;
3187
cristy117ff172010-08-15 21:35:32 +00003188 register const double
3189 *k;
3190
cristybb503372010-05-27 20:51:26 +00003191 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003192 u,
3193 v;
3194
cristy3ed852e2009-09-05 21:47:34 +00003195 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003196 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3197 width);
cristy3ed852e2009-09-05 21:47:34 +00003198 message=AcquireString("");
3199 k=kernel;
cristybb503372010-05-27 20:51:26 +00003200 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003201 {
3202 *message='\0';
cristyb51dff52011-05-19 16:55:47 +00003203 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristy3ed852e2009-09-05 21:47:34 +00003204 (void) ConcatenateString(&message,format);
cristybb503372010-05-27 20:51:26 +00003205 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003206 {
cristyb51dff52011-05-19 16:55:47 +00003207 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",*k++);
cristy3ed852e2009-09-05 21:47:34 +00003208 (void) ConcatenateString(&message,format);
3209 }
3210 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3211 }
3212 message=DestroyString(message);
3213 }
3214 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3215 if (blur_image == (Image *) NULL)
3216 return((Image *) NULL);
3217 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3218 {
3219 InheritException(exception,&blur_image->exception);
3220 blur_image=DestroyImage(blur_image);
3221 return((Image *) NULL);
3222 }
3223 /*
3224 Threshold blur image.
3225 */
3226 status=MagickTrue;
3227 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003228 GetPixelInfo(image,&bias);
3229 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00003230 image_view=AcquireCacheView(image);
3231 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00003232#if defined(MAGICKCORE_OPENMP_SUPPORT)
3233 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003234#endif
cristybb503372010-05-27 20:51:26 +00003235 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003236 {
cristy4c08aed2011-07-01 19:47:50 +00003237 double
3238 contrast;
3239
cristy3ed852e2009-09-05 21:47:34 +00003240 MagickBooleanType
3241 sync;
3242
3243 MagickRealType
3244 gamma;
3245
cristy4c08aed2011-07-01 19:47:50 +00003246 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003247 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003248
cristy4c08aed2011-07-01 19:47:50 +00003249 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003250 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003251
cristy117ff172010-08-15 21:35:32 +00003252 register ssize_t
3253 x;
3254
cristy3ed852e2009-09-05 21:47:34 +00003255 if (status == MagickFalse)
3256 continue;
cristy117ff172010-08-15 21:35:32 +00003257 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3258 (width/2L),image->columns+width,width,exception);
cristy3ed852e2009-09-05 21:47:34 +00003259 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3260 exception);
cristy4c08aed2011-07-01 19:47:50 +00003261 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003262 {
3263 status=MagickFalse;
3264 continue;
3265 }
cristybb503372010-05-27 20:51:26 +00003266 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003267 {
cristy4c08aed2011-07-01 19:47:50 +00003268 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003269 pixel;
3270
3271 register const double
cristyc47d1f82009-11-26 01:44:43 +00003272 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00003273
cristybb503372010-05-27 20:51:26 +00003274 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003275 u;
3276
cristy117ff172010-08-15 21:35:32 +00003277 ssize_t
3278 j,
3279 v;
3280
cristyddd82202009-11-03 20:14:50 +00003281 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +00003282 k=kernel;
3283 gamma=0.0;
3284 j=0;
cristyed231572011-07-14 02:18:59 +00003285 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00003286 {
cristybb503372010-05-27 20:51:26 +00003287 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003288 {
cristybb503372010-05-27 20:51:26 +00003289 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003290 {
cristyed231572011-07-14 02:18:59 +00003291 contrast=GetPixelIntensity(image,p+(u+j)*GetPixelChannels(image))-
cristy4c08aed2011-07-01 19:47:50 +00003292 (double) GetPixelIntensity(blur_image,q);
3293 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003294 {
cristy4c08aed2011-07-01 19:47:50 +00003295 pixel.red+=(*k)*
cristyed231572011-07-14 02:18:59 +00003296 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003297 pixel.green+=(*k)*
cristyed231572011-07-14 02:18:59 +00003298 GetPixelGreen(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003299 pixel.blue+=(*k)*
cristyed231572011-07-14 02:18:59 +00003300 GetPixelBlue(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003301 if (image->colorspace == CMYKColorspace)
3302 pixel.black+=(*k)*
cristyed231572011-07-14 02:18:59 +00003303 GetPixelBlack(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003304 gamma+=(*k);
3305 k++;
3306 }
3307 }
cristyd99b0962010-05-29 23:14:26 +00003308 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003309 }
3310 if (gamma != 0.0)
3311 {
3312 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00003313 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003314 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00003315 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003316 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00003317 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003318 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003319 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00003320 (image->colorspace == CMYKColorspace))
3321 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00003322 }
cristyed231572011-07-14 02:18:59 +00003323 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003324 {
3325 gamma=0.0;
3326 j=0;
cristybb503372010-05-27 20:51:26 +00003327 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003328 {
cristybb503372010-05-27 20:51:26 +00003329 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003330 {
cristy4c08aed2011-07-01 19:47:50 +00003331 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003332 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003333 GetPixelIntensity(blur_image,q);
3334 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003335 {
cristy4c08aed2011-07-01 19:47:50 +00003336 pixel.alpha+=(*k)*
cristyed231572011-07-14 02:18:59 +00003337 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003338 gamma+=(*k);
3339 k++;
3340 }
3341 }
cristyeaedf062010-05-29 22:36:02 +00003342 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003343 }
3344 if (gamma != 0.0)
3345 {
3346 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3347 gamma);
cristy4c08aed2011-07-01 19:47:50 +00003348 SetPixelAlpha(blur_image,ClampToQuantum(gamma*pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003349 }
3350 }
3351 }
3352 else
3353 {
3354 MagickRealType
3355 alpha;
3356
cristybb503372010-05-27 20:51:26 +00003357 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003358 {
cristybb503372010-05-27 20:51:26 +00003359 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003360 {
cristy4c08aed2011-07-01 19:47:50 +00003361 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003362 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003363 GetPixelIntensity(blur_image,q);
3364 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003365 {
cristy4c08aed2011-07-01 19:47:50 +00003366 alpha=(MagickRealType) (QuantumScale*
cristyed231572011-07-14 02:18:59 +00003367 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image)));
cristy4c08aed2011-07-01 19:47:50 +00003368 pixel.red+=(*k)*alpha*
cristyed231572011-07-14 02:18:59 +00003369 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003370 pixel.green+=(*k)*alpha*GetPixelGreen(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003371 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003372 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003373 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003374 pixel.alpha+=(*k)*GetPixelAlpha(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003375 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003376 if (image->colorspace == CMYKColorspace)
3377 pixel.black+=(*k)*GetPixelBlack(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003378 GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003379 gamma+=(*k)*alpha;
3380 k++;
3381 }
3382 }
cristyeaedf062010-05-29 22:36:02 +00003383 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003384 }
3385 if (gamma != 0.0)
3386 {
3387 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00003388 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003389 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00003390 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003391 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00003392 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003393 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003394 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00003395 (image->colorspace == CMYKColorspace))
3396 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00003397 }
cristyed231572011-07-14 02:18:59 +00003398 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003399 {
3400 gamma=0.0;
3401 j=0;
cristybb503372010-05-27 20:51:26 +00003402 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003403 {
cristybb503372010-05-27 20:51:26 +00003404 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003405 {
cristy4c08aed2011-07-01 19:47:50 +00003406 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003407 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003408 GetPixelIntensity(blur_image,q);
3409 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003410 {
cristy4c08aed2011-07-01 19:47:50 +00003411 pixel.alpha+=(*k)*
cristyed231572011-07-14 02:18:59 +00003412 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003413 gamma+=(*k);
3414 k++;
3415 }
3416 }
cristyeaedf062010-05-29 22:36:02 +00003417 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003418 }
3419 if (gamma != 0.0)
3420 {
3421 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3422 gamma);
cristy4c08aed2011-07-01 19:47:50 +00003423 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003424 }
3425 }
3426 }
cristyed231572011-07-14 02:18:59 +00003427 p+=GetPixelChannels(image);
3428 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003429 }
3430 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3431 if (sync == MagickFalse)
3432 status=MagickFalse;
3433 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3434 {
3435 MagickBooleanType
3436 proceed;
3437
cristyb5d5f722009-11-04 03:03:49 +00003438#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00003439 #pragma omp critical (MagickCore_SelectiveBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003440#endif
3441 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3442 image->rows);
3443 if (proceed == MagickFalse)
3444 status=MagickFalse;
3445 }
3446 }
3447 blur_image->type=image->type;
3448 blur_view=DestroyCacheView(blur_view);
3449 image_view=DestroyCacheView(image_view);
3450 kernel=(double *) RelinquishMagickMemory(kernel);
3451 if (status == MagickFalse)
3452 blur_image=DestroyImage(blur_image);
3453 return(blur_image);
3454}
3455
3456/*
3457%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3458% %
3459% %
3460% %
3461% S h a d e I m a g e %
3462% %
3463% %
3464% %
3465%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3466%
3467% ShadeImage() shines a distant light on an image to create a
3468% three-dimensional effect. You control the positioning of the light with
3469% azimuth and elevation; azimuth is measured in degrees off the x axis
3470% and elevation is measured in pixels above the Z axis.
3471%
3472% The format of the ShadeImage method is:
3473%
3474% Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3475% const double azimuth,const double elevation,ExceptionInfo *exception)
3476%
3477% A description of each parameter follows:
3478%
3479% o image: the image.
3480%
3481% o gray: A value other than zero shades the intensity of each pixel.
3482%
3483% o azimuth, elevation: Define the light source direction.
3484%
3485% o exception: return any errors or warnings in this structure.
3486%
3487*/
3488MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3489 const double azimuth,const double elevation,ExceptionInfo *exception)
3490{
3491#define ShadeImageTag "Shade/Image"
3492
cristyc4c8d132010-01-07 01:58:38 +00003493 CacheView
3494 *image_view,
3495 *shade_view;
3496
cristy3ed852e2009-09-05 21:47:34 +00003497 Image
3498 *shade_image;
3499
cristy3ed852e2009-09-05 21:47:34 +00003500 MagickBooleanType
3501 status;
3502
cristybb503372010-05-27 20:51:26 +00003503 MagickOffsetType
3504 progress;
3505
cristy3ed852e2009-09-05 21:47:34 +00003506 PrimaryInfo
3507 light;
3508
cristybb503372010-05-27 20:51:26 +00003509 ssize_t
3510 y;
3511
cristy3ed852e2009-09-05 21:47:34 +00003512 /*
3513 Initialize shaded image attributes.
3514 */
3515 assert(image != (const Image *) NULL);
3516 assert(image->signature == MagickSignature);
3517 if (image->debug != MagickFalse)
3518 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3519 assert(exception != (ExceptionInfo *) NULL);
3520 assert(exception->signature == MagickSignature);
3521 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3522 if (shade_image == (Image *) NULL)
3523 return((Image *) NULL);
3524 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
3525 {
3526 InheritException(exception,&shade_image->exception);
3527 shade_image=DestroyImage(shade_image);
3528 return((Image *) NULL);
3529 }
3530 /*
3531 Compute the light vector.
3532 */
3533 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3534 cos(DegreesToRadians(elevation));
3535 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3536 cos(DegreesToRadians(elevation));
3537 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3538 /*
3539 Shade image.
3540 */
3541 status=MagickTrue;
3542 progress=0;
3543 image_view=AcquireCacheView(image);
3544 shade_view=AcquireCacheView(shade_image);
cristyb5d5f722009-11-04 03:03:49 +00003545#if defined(MAGICKCORE_OPENMP_SUPPORT)
3546 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003547#endif
cristybb503372010-05-27 20:51:26 +00003548 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003549 {
3550 MagickRealType
3551 distance,
3552 normal_distance,
3553 shade;
3554
3555 PrimaryInfo
3556 normal;
3557
cristy4c08aed2011-07-01 19:47:50 +00003558 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003559 *restrict p,
3560 *restrict s0,
3561 *restrict s1,
3562 *restrict s2;
cristy3ed852e2009-09-05 21:47:34 +00003563
cristy4c08aed2011-07-01 19:47:50 +00003564 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003565 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003566
cristy117ff172010-08-15 21:35:32 +00003567 register ssize_t
3568 x;
3569
cristy3ed852e2009-09-05 21:47:34 +00003570 if (status == MagickFalse)
3571 continue;
3572 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
3573 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3574 exception);
cristy4c08aed2011-07-01 19:47:50 +00003575 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003576 {
3577 status=MagickFalse;
3578 continue;
3579 }
3580 /*
3581 Shade this row of pixels.
3582 */
3583 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
cristyed231572011-07-14 02:18:59 +00003584 s0=p+GetPixelChannels(image);
3585 s1=s0+(image->columns+2)*GetPixelChannels(image);
3586 s2=s1+(image->columns+2)*GetPixelChannels(image);
cristybb503372010-05-27 20:51:26 +00003587 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003588 {
3589 /*
3590 Determine the surface normal and compute shading.
3591 */
cristyed231572011-07-14 02:18:59 +00003592 normal.x=(double) (GetPixelIntensity(image,s0-GetPixelChannels(image))+
3593 GetPixelIntensity(image,s1-GetPixelChannels(image))+
3594 GetPixelIntensity(image,s2-GetPixelChannels(image))-
3595 GetPixelIntensity(image,s0+GetPixelChannels(image))-
3596 GetPixelIntensity(image,s1+GetPixelChannels(image))-
3597 GetPixelIntensity(image,s2+GetPixelChannels(image)));
3598 normal.y=(double) (GetPixelIntensity(image,s2-GetPixelChannels(image))+
cristy4c08aed2011-07-01 19:47:50 +00003599 GetPixelIntensity(image,s2)+
cristyed231572011-07-14 02:18:59 +00003600 GetPixelIntensity(image,s2+GetPixelChannels(image))-
3601 GetPixelIntensity(image,s0-GetPixelChannels(image))-
cristy4c08aed2011-07-01 19:47:50 +00003602 GetPixelIntensity(image,s0)-
cristyed231572011-07-14 02:18:59 +00003603 GetPixelIntensity(image,s0+GetPixelChannels(image)));
cristy3ed852e2009-09-05 21:47:34 +00003604 if ((normal.x == 0.0) && (normal.y == 0.0))
3605 shade=light.z;
3606 else
3607 {
3608 shade=0.0;
3609 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3610 if (distance > MagickEpsilon)
3611 {
3612 normal_distance=
3613 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3614 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3615 shade=distance/sqrt((double) normal_distance);
3616 }
3617 }
3618 if (gray != MagickFalse)
3619 {
cristy4c08aed2011-07-01 19:47:50 +00003620 SetPixelRed(shade_image,ClampToQuantum(shade),q);
3621 SetPixelGreen(shade_image,ClampToQuantum(shade),q);
3622 SetPixelBlue(shade_image,ClampToQuantum(shade),q);
cristy3ed852e2009-09-05 21:47:34 +00003623 }
3624 else
3625 {
cristy4c08aed2011-07-01 19:47:50 +00003626 SetPixelRed(shade_image,ClampToQuantum(QuantumScale*shade*
3627 GetPixelRed(image,s1)),q);
3628 SetPixelGreen(shade_image,ClampToQuantum(QuantumScale*shade*
3629 GetPixelGreen(image,s1)),q);
3630 SetPixelBlue(shade_image,ClampToQuantum(QuantumScale*shade*
3631 GetPixelBlue(image,s1)),q);
cristy3ed852e2009-09-05 21:47:34 +00003632 }
cristy4c08aed2011-07-01 19:47:50 +00003633 SetPixelAlpha(shade_image,GetPixelAlpha(image,s1),q);
cristyed231572011-07-14 02:18:59 +00003634 s0+=GetPixelChannels(image);
3635 s1+=GetPixelChannels(image);
3636 s2+=GetPixelChannels(image);
3637 q+=GetPixelChannels(shade_image);
cristy3ed852e2009-09-05 21:47:34 +00003638 }
3639 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3640 status=MagickFalse;
3641 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3642 {
3643 MagickBooleanType
3644 proceed;
3645
cristyb5d5f722009-11-04 03:03:49 +00003646#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003647 #pragma omp critical (MagickCore_ShadeImage)
3648#endif
3649 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3650 if (proceed == MagickFalse)
3651 status=MagickFalse;
3652 }
3653 }
3654 shade_view=DestroyCacheView(shade_view);
3655 image_view=DestroyCacheView(image_view);
3656 if (status == MagickFalse)
3657 shade_image=DestroyImage(shade_image);
3658 return(shade_image);
3659}
3660
3661/*
3662%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3663% %
3664% %
3665% %
3666% S h a r p e n I m a g e %
3667% %
3668% %
3669% %
3670%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3671%
3672% SharpenImage() sharpens the image. We convolve the image with a Gaussian
3673% operator of the given radius and standard deviation (sigma). For
3674% reasonable results, radius should be larger than sigma. Use a radius of 0
3675% and SharpenImage() selects a suitable radius for you.
3676%
3677% Using a separable kernel would be faster, but the negative weights cancel
3678% out on the corners of the kernel producing often undesirable ringing in the
3679% filtered result; this can be avoided by using a 2D gaussian shaped image
3680% sharpening kernel instead.
3681%
3682% The format of the SharpenImage method is:
3683%
3684% Image *SharpenImage(const Image *image,const double radius,
3685% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003686%
3687% A description of each parameter follows:
3688%
3689% o image: the image.
3690%
cristy3ed852e2009-09-05 21:47:34 +00003691% o radius: the radius of the Gaussian, in pixels, not counting the center
3692% pixel.
3693%
3694% o sigma: the standard deviation of the Laplacian, in pixels.
3695%
3696% o exception: return any errors or warnings in this structure.
3697%
3698*/
cristy3ed852e2009-09-05 21:47:34 +00003699MagickExport Image *SharpenImage(const Image *image,const double radius,
3700 const double sigma,ExceptionInfo *exception)
3701{
cristy3ed852e2009-09-05 21:47:34 +00003702 double
cristy47e00502009-12-17 19:19:57 +00003703 normalize;
cristy3ed852e2009-09-05 21:47:34 +00003704
3705 Image
3706 *sharp_image;
3707
cristy41cbe682011-07-15 19:12:37 +00003708 KernelInfo
3709 *kernel_info;
3710
cristybb503372010-05-27 20:51:26 +00003711 register ssize_t
cristy47e00502009-12-17 19:19:57 +00003712 i;
3713
cristybb503372010-05-27 20:51:26 +00003714 size_t
cristy3ed852e2009-09-05 21:47:34 +00003715 width;
3716
cristy117ff172010-08-15 21:35:32 +00003717 ssize_t
3718 j,
3719 u,
3720 v;
3721
cristy3ed852e2009-09-05 21:47:34 +00003722 assert(image != (const Image *) NULL);
3723 assert(image->signature == MagickSignature);
3724 if (image->debug != MagickFalse)
3725 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3726 assert(exception != (ExceptionInfo *) NULL);
3727 assert(exception->signature == MagickSignature);
3728 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00003729 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00003730 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003731 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00003732 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3733 kernel_info->width=width;
3734 kernel_info->height=width;
3735 kernel_info->signature=MagickSignature;
3736 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
3737 kernel_info->width*sizeof(*kernel_info->values));
3738 if (kernel_info->values == (double *) NULL)
3739 {
3740 kernel_info=DestroyKernelInfo(kernel_info);
3741 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3742 }
cristy3ed852e2009-09-05 21:47:34 +00003743 normalize=0.0;
cristy41cbe682011-07-15 19:12:37 +00003744 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00003745 i=0;
3746 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00003747 {
cristy47e00502009-12-17 19:19:57 +00003748 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00003749 {
cristy41cbe682011-07-15 19:12:37 +00003750 kernel_info->values[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*
3751 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3752 normalize+=kernel_info->values[i];
cristy3ed852e2009-09-05 21:47:34 +00003753 i++;
3754 }
3755 }
cristy41cbe682011-07-15 19:12:37 +00003756 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
cristy0a922382011-07-16 15:30:34 +00003757 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00003758 sharp_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00003759 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00003760 return(sharp_image);
3761}
3762
3763/*
3764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3765% %
3766% %
3767% %
3768% S p r e a d I m a g e %
3769% %
3770% %
3771% %
3772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3773%
3774% SpreadImage() is a special effects method that randomly displaces each
3775% pixel in a block defined by the radius parameter.
3776%
3777% The format of the SpreadImage method is:
3778%
3779% Image *SpreadImage(const Image *image,const double radius,
3780% ExceptionInfo *exception)
3781%
3782% A description of each parameter follows:
3783%
3784% o image: the image.
3785%
3786% o radius: Choose a random pixel in a neighborhood of this extent.
3787%
3788% o exception: return any errors or warnings in this structure.
3789%
3790*/
3791MagickExport Image *SpreadImage(const Image *image,const double radius,
3792 ExceptionInfo *exception)
3793{
3794#define SpreadImageTag "Spread/Image"
3795
cristyfa112112010-01-04 17:48:07 +00003796 CacheView
cristy9f7e7cb2011-03-26 00:49:57 +00003797 *image_view,
3798 *spread_view;
cristyfa112112010-01-04 17:48:07 +00003799
cristy3ed852e2009-09-05 21:47:34 +00003800 Image
3801 *spread_image;
3802
cristy3ed852e2009-09-05 21:47:34 +00003803 MagickBooleanType
3804 status;
3805
cristybb503372010-05-27 20:51:26 +00003806 MagickOffsetType
3807 progress;
3808
cristy4c08aed2011-07-01 19:47:50 +00003809 PixelInfo
cristyddd82202009-11-03 20:14:50 +00003810 bias;
cristy3ed852e2009-09-05 21:47:34 +00003811
3812 RandomInfo
cristyfa112112010-01-04 17:48:07 +00003813 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00003814
cristybb503372010-05-27 20:51:26 +00003815 size_t
cristy3ed852e2009-09-05 21:47:34 +00003816 width;
3817
cristybb503372010-05-27 20:51:26 +00003818 ssize_t
3819 y;
3820
cristy3ed852e2009-09-05 21:47:34 +00003821 /*
3822 Initialize spread image attributes.
3823 */
3824 assert(image != (Image *) NULL);
3825 assert(image->signature == MagickSignature);
3826 if (image->debug != MagickFalse)
3827 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3828 assert(exception != (ExceptionInfo *) NULL);
3829 assert(exception->signature == MagickSignature);
3830 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3831 exception);
3832 if (spread_image == (Image *) NULL)
3833 return((Image *) NULL);
3834 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
3835 {
3836 InheritException(exception,&spread_image->exception);
3837 spread_image=DestroyImage(spread_image);
3838 return((Image *) NULL);
3839 }
3840 /*
3841 Spread image.
3842 */
3843 status=MagickTrue;
3844 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003845 GetPixelInfo(spread_image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00003846 width=GetOptimalKernelWidth1D(radius,0.5);
cristy3ed852e2009-09-05 21:47:34 +00003847 random_info=AcquireRandomInfoThreadSet();
cristy9f7e7cb2011-03-26 00:49:57 +00003848 image_view=AcquireCacheView(image);
3849 spread_view=AcquireCacheView(spread_image);
cristyb557a152011-02-22 12:14:30 +00003850#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +00003851 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +00003852#endif
cristybb503372010-05-27 20:51:26 +00003853 for (y=0; y < (ssize_t) spread_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003854 {
cristy5c9e6f22010-09-17 17:31:01 +00003855 const int
3856 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003857
cristy4c08aed2011-07-01 19:47:50 +00003858 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003859 pixel;
3860
cristy4c08aed2011-07-01 19:47:50 +00003861 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003862 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003863
cristy117ff172010-08-15 21:35:32 +00003864 register ssize_t
3865 x;
3866
cristy3ed852e2009-09-05 21:47:34 +00003867 if (status == MagickFalse)
3868 continue;
cristy9f7e7cb2011-03-26 00:49:57 +00003869 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003870 exception);
cristy4c08aed2011-07-01 19:47:50 +00003871 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003872 {
3873 status=MagickFalse;
3874 continue;
3875 }
cristyddd82202009-11-03 20:14:50 +00003876 pixel=bias;
cristybb503372010-05-27 20:51:26 +00003877 for (x=0; x < (ssize_t) spread_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003878 {
cristy4c08aed2011-07-01 19:47:50 +00003879 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00003880 UndefinedInterpolatePixel,(double) x+width*(GetPseudoRandomValue(
3881 random_info[id])-0.5),(double) y+width*(GetPseudoRandomValue(
3882 random_info[id])-0.5),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00003883 SetPixelPixelInfo(spread_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00003884 q+=GetPixelChannels(spread_image);
cristy3ed852e2009-09-05 21:47:34 +00003885 }
cristy9f7e7cb2011-03-26 00:49:57 +00003886 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003887 status=MagickFalse;
3888 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3889 {
3890 MagickBooleanType
3891 proceed;
3892
cristyb557a152011-02-22 12:14:30 +00003893#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003894 #pragma omp critical (MagickCore_SpreadImage)
3895#endif
3896 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3897 if (proceed == MagickFalse)
3898 status=MagickFalse;
3899 }
3900 }
cristy9f7e7cb2011-03-26 00:49:57 +00003901 spread_view=DestroyCacheView(spread_view);
cristy3ed852e2009-09-05 21:47:34 +00003902 image_view=DestroyCacheView(image_view);
3903 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00003904 return(spread_image);
3905}
3906
3907/*
3908%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3909% %
3910% %
3911% %
cristy0834d642011-03-18 18:26:08 +00003912% S t a t i s t i c I m a g e %
3913% %
3914% %
3915% %
3916%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3917%
3918% StatisticImage() makes each pixel the min / max / median / mode / etc. of
cristy8d752042011-03-19 01:00:36 +00003919% the neighborhood of the specified width and height.
cristy0834d642011-03-18 18:26:08 +00003920%
3921% The format of the StatisticImage method is:
3922%
3923% Image *StatisticImage(const Image *image,const StatisticType type,
cristy95c38342011-03-18 22:39:51 +00003924% const size_t width,const size_t height,ExceptionInfo *exception)
cristy0834d642011-03-18 18:26:08 +00003925%
3926% A description of each parameter follows:
3927%
3928% o image: the image.
3929%
cristy0834d642011-03-18 18:26:08 +00003930% o type: the statistic type (median, mode, etc.).
3931%
cristy95c38342011-03-18 22:39:51 +00003932% o width: the width of the pixel neighborhood.
3933%
3934% o height: the height of the pixel neighborhood.
cristy0834d642011-03-18 18:26:08 +00003935%
3936% o exception: return any errors or warnings in this structure.
3937%
3938*/
3939
cristy733678d2011-03-18 21:29:28 +00003940#define ListChannels 5
3941
3942typedef struct _ListNode
3943{
3944 size_t
3945 next[9],
3946 count,
3947 signature;
3948} ListNode;
3949
3950typedef struct _SkipList
3951{
3952 ssize_t
3953 level;
3954
3955 ListNode
3956 *nodes;
3957} SkipList;
3958
3959typedef struct _PixelList
3960{
3961 size_t
cristy6fc86bb2011-03-18 23:45:16 +00003962 length,
cristy733678d2011-03-18 21:29:28 +00003963 seed,
3964 signature;
3965
3966 SkipList
3967 lists[ListChannels];
3968} PixelList;
3969
3970static PixelList *DestroyPixelList(PixelList *pixel_list)
3971{
3972 register ssize_t
3973 i;
3974
3975 if (pixel_list == (PixelList *) NULL)
3976 return((PixelList *) NULL);
3977 for (i=0; i < ListChannels; i++)
3978 if (pixel_list->lists[i].nodes != (ListNode *) NULL)
3979 pixel_list->lists[i].nodes=(ListNode *) RelinquishMagickMemory(
3980 pixel_list->lists[i].nodes);
3981 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
3982 return(pixel_list);
3983}
3984
3985static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
3986{
3987 register ssize_t
3988 i;
3989
3990 assert(pixel_list != (PixelList **) NULL);
3991 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3992 if (pixel_list[i] != (PixelList *) NULL)
3993 pixel_list[i]=DestroyPixelList(pixel_list[i]);
3994 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
3995 return(pixel_list);
3996}
3997
cristy6fc86bb2011-03-18 23:45:16 +00003998static PixelList *AcquirePixelList(const size_t width,const size_t height)
cristy733678d2011-03-18 21:29:28 +00003999{
4000 PixelList
4001 *pixel_list;
4002
4003 register ssize_t
4004 i;
4005
4006 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
4007 if (pixel_list == (PixelList *) NULL)
4008 return(pixel_list);
4009 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
cristy6fc86bb2011-03-18 23:45:16 +00004010 pixel_list->length=width*height;
cristy733678d2011-03-18 21:29:28 +00004011 for (i=0; i < ListChannels; i++)
4012 {
4013 pixel_list->lists[i].nodes=(ListNode *) AcquireQuantumMemory(65537UL,
4014 sizeof(*pixel_list->lists[i].nodes));
4015 if (pixel_list->lists[i].nodes == (ListNode *) NULL)
4016 return(DestroyPixelList(pixel_list));
4017 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
4018 sizeof(*pixel_list->lists[i].nodes));
4019 }
4020 pixel_list->signature=MagickSignature;
4021 return(pixel_list);
4022}
4023
cristy6fc86bb2011-03-18 23:45:16 +00004024static PixelList **AcquirePixelListThreadSet(const size_t width,
4025 const size_t height)
cristy733678d2011-03-18 21:29:28 +00004026{
4027 PixelList
4028 **pixel_list;
4029
4030 register ssize_t
4031 i;
4032
4033 size_t
4034 number_threads;
4035
4036 number_threads=GetOpenMPMaximumThreads();
4037 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
4038 sizeof(*pixel_list));
4039 if (pixel_list == (PixelList **) NULL)
4040 return((PixelList **) NULL);
4041 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
4042 for (i=0; i < (ssize_t) number_threads; i++)
4043 {
cristy6fc86bb2011-03-18 23:45:16 +00004044 pixel_list[i]=AcquirePixelList(width,height);
cristy733678d2011-03-18 21:29:28 +00004045 if (pixel_list[i] == (PixelList *) NULL)
4046 return(DestroyPixelListThreadSet(pixel_list));
4047 }
4048 return(pixel_list);
4049}
4050
4051static void AddNodePixelList(PixelList *pixel_list,const ssize_t channel,
4052 const size_t color)
4053{
4054 register SkipList
4055 *list;
4056
4057 register ssize_t
4058 level;
4059
4060 size_t
4061 search,
4062 update[9];
4063
4064 /*
4065 Initialize the node.
4066 */
4067 list=pixel_list->lists+channel;
4068 list->nodes[color].signature=pixel_list->signature;
4069 list->nodes[color].count=1;
4070 /*
4071 Determine where it belongs in the list.
4072 */
4073 search=65536UL;
4074 for (level=list->level; level >= 0; level--)
4075 {
4076 while (list->nodes[search].next[level] < color)
4077 search=list->nodes[search].next[level];
4078 update[level]=search;
4079 }
4080 /*
4081 Generate a pseudo-random level for this node.
4082 */
4083 for (level=0; ; level++)
4084 {
4085 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
4086 if ((pixel_list->seed & 0x300) != 0x300)
4087 break;
4088 }
4089 if (level > 8)
4090 level=8;
4091 if (level > (list->level+2))
4092 level=list->level+2;
4093 /*
4094 If we're raising the list's level, link back to the root node.
4095 */
4096 while (level > list->level)
4097 {
4098 list->level++;
4099 update[list->level]=65536UL;
4100 }
4101 /*
4102 Link the node into the skip-list.
4103 */
4104 do
4105 {
4106 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
4107 list->nodes[update[level]].next[level]=color;
cristy3cba8ca2011-03-19 01:29:12 +00004108 } while (level-- > 0);
cristy733678d2011-03-18 21:29:28 +00004109}
4110
cristy4c08aed2011-07-01 19:47:50 +00004111static PixelInfo GetMaximumPixelList(PixelList *pixel_list)
cristy6fc86bb2011-03-18 23:45:16 +00004112{
cristy4c08aed2011-07-01 19:47:50 +00004113 PixelInfo
cristy6fc86bb2011-03-18 23:45:16 +00004114 pixel;
4115
4116 register SkipList
4117 *list;
4118
4119 register ssize_t
4120 channel;
4121
4122 size_t
cristyd76c51e2011-03-26 00:21:26 +00004123 color,
4124 maximum;
cristy49f37242011-03-22 18:18:23 +00004125
4126 ssize_t
cristy6fc86bb2011-03-18 23:45:16 +00004127 count;
4128
4129 unsigned short
cristyd76c51e2011-03-26 00:21:26 +00004130 channels[ListChannels];
cristy6fc86bb2011-03-18 23:45:16 +00004131
4132 /*
4133 Find the maximum value for each of the color.
4134 */
4135 for (channel=0; channel < 5; channel++)
4136 {
4137 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004138 color=65536L;
cristy6fc86bb2011-03-18 23:45:16 +00004139 count=0;
cristy49f37242011-03-22 18:18:23 +00004140 maximum=list->nodes[color].next[0];
cristy6fc86bb2011-03-18 23:45:16 +00004141 do
4142 {
4143 color=list->nodes[color].next[0];
cristy49f37242011-03-22 18:18:23 +00004144 if (color > maximum)
4145 maximum=color;
cristy6fc86bb2011-03-18 23:45:16 +00004146 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004147 } while (count < (ssize_t) pixel_list->length);
cristy49f37242011-03-22 18:18:23 +00004148 channels[channel]=(unsigned short) maximum;
4149 }
cristy4c08aed2011-07-01 19:47:50 +00004150 GetPixelInfo((const Image *) NULL,&pixel);
cristy49f37242011-03-22 18:18:23 +00004151 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4152 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4153 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004154 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4155 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy49f37242011-03-22 18:18:23 +00004156 return(pixel);
4157}
4158
cristy4c08aed2011-07-01 19:47:50 +00004159static PixelInfo GetMeanPixelList(PixelList *pixel_list)
cristy49f37242011-03-22 18:18:23 +00004160{
cristy4c08aed2011-07-01 19:47:50 +00004161 PixelInfo
cristy49f37242011-03-22 18:18:23 +00004162 pixel;
4163
cristy80a99a32011-03-30 01:30:23 +00004164 MagickRealType
4165 sum;
4166
cristy49f37242011-03-22 18:18:23 +00004167 register SkipList
4168 *list;
4169
4170 register ssize_t
4171 channel;
4172
4173 size_t
cristy80a99a32011-03-30 01:30:23 +00004174 color;
cristy49f37242011-03-22 18:18:23 +00004175
4176 ssize_t
4177 count;
4178
4179 unsigned short
4180 channels[ListChannels];
4181
4182 /*
4183 Find the mean value for each of the color.
4184 */
4185 for (channel=0; channel < 5; channel++)
4186 {
4187 list=pixel_list->lists+channel;
4188 color=65536L;
4189 count=0;
cristy80a99a32011-03-30 01:30:23 +00004190 sum=0.0;
cristy49f37242011-03-22 18:18:23 +00004191 do
4192 {
4193 color=list->nodes[color].next[0];
cristy80a99a32011-03-30 01:30:23 +00004194 sum+=(MagickRealType) list->nodes[color].count*color;
cristy49f37242011-03-22 18:18:23 +00004195 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004196 } while (count < (ssize_t) pixel_list->length);
cristy80a99a32011-03-30 01:30:23 +00004197 sum/=pixel_list->length;
4198 channels[channel]=(unsigned short) sum;
cristy6fc86bb2011-03-18 23:45:16 +00004199 }
cristy4c08aed2011-07-01 19:47:50 +00004200 GetPixelInfo((const Image *) NULL,&pixel);
cristy6fc86bb2011-03-18 23:45:16 +00004201 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4202 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4203 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004204 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4205 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy6fc86bb2011-03-18 23:45:16 +00004206 return(pixel);
4207}
4208
cristy4c08aed2011-07-01 19:47:50 +00004209static PixelInfo GetMedianPixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004210{
cristy4c08aed2011-07-01 19:47:50 +00004211 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004212 pixel;
4213
4214 register SkipList
4215 *list;
4216
4217 register ssize_t
4218 channel;
4219
4220 size_t
cristy49f37242011-03-22 18:18:23 +00004221 color;
4222
4223 ssize_t
cristy733678d2011-03-18 21:29:28 +00004224 count;
4225
4226 unsigned short
4227 channels[ListChannels];
4228
4229 /*
4230 Find the median value for each of the color.
4231 */
cristy733678d2011-03-18 21:29:28 +00004232 for (channel=0; channel < 5; channel++)
4233 {
4234 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004235 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004236 count=0;
4237 do
4238 {
4239 color=list->nodes[color].next[0];
4240 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004241 } while (count <= (ssize_t) (pixel_list->length >> 1));
cristy6fc86bb2011-03-18 23:45:16 +00004242 channels[channel]=(unsigned short) color;
4243 }
cristy4c08aed2011-07-01 19:47:50 +00004244 GetPixelInfo((const Image *) NULL,&pixel);
cristy6fc86bb2011-03-18 23:45:16 +00004245 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4246 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4247 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004248 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4249 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy6fc86bb2011-03-18 23:45:16 +00004250 return(pixel);
4251}
4252
cristy4c08aed2011-07-01 19:47:50 +00004253static PixelInfo GetMinimumPixelList(PixelList *pixel_list)
cristy6fc86bb2011-03-18 23:45:16 +00004254{
cristy4c08aed2011-07-01 19:47:50 +00004255 PixelInfo
cristy6fc86bb2011-03-18 23:45:16 +00004256 pixel;
4257
4258 register SkipList
4259 *list;
4260
4261 register ssize_t
4262 channel;
4263
4264 size_t
cristyd76c51e2011-03-26 00:21:26 +00004265 color,
4266 minimum;
cristy6fc86bb2011-03-18 23:45:16 +00004267
cristy49f37242011-03-22 18:18:23 +00004268 ssize_t
4269 count;
4270
cristy6fc86bb2011-03-18 23:45:16 +00004271 unsigned short
cristyd76c51e2011-03-26 00:21:26 +00004272 channels[ListChannels];
cristy6fc86bb2011-03-18 23:45:16 +00004273
4274 /*
4275 Find the minimum value for each of the color.
4276 */
4277 for (channel=0; channel < 5; channel++)
4278 {
4279 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004280 count=0;
cristy6fc86bb2011-03-18 23:45:16 +00004281 color=65536UL;
cristy49f37242011-03-22 18:18:23 +00004282 minimum=list->nodes[color].next[0];
4283 do
4284 {
4285 color=list->nodes[color].next[0];
4286 if (color < minimum)
4287 minimum=color;
4288 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004289 } while (count < (ssize_t) pixel_list->length);
cristy49f37242011-03-22 18:18:23 +00004290 channels[channel]=(unsigned short) minimum;
cristy733678d2011-03-18 21:29:28 +00004291 }
cristy4c08aed2011-07-01 19:47:50 +00004292 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004293 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4294 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4295 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004296 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4297 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy733678d2011-03-18 21:29:28 +00004298 return(pixel);
4299}
4300
cristy4c08aed2011-07-01 19:47:50 +00004301static PixelInfo GetModePixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004302{
cristy4c08aed2011-07-01 19:47:50 +00004303 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004304 pixel;
4305
4306 register SkipList
4307 *list;
4308
4309 register ssize_t
4310 channel;
4311
4312 size_t
4313 color,
cristy733678d2011-03-18 21:29:28 +00004314 max_count,
cristy6fc86bb2011-03-18 23:45:16 +00004315 mode;
cristy733678d2011-03-18 21:29:28 +00004316
cristy49f37242011-03-22 18:18:23 +00004317 ssize_t
4318 count;
4319
cristy733678d2011-03-18 21:29:28 +00004320 unsigned short
4321 channels[5];
4322
4323 /*
glennrp30d2dc62011-06-25 03:17:16 +00004324 Make each pixel the 'predominant color' of the specified neighborhood.
cristy733678d2011-03-18 21:29:28 +00004325 */
cristy733678d2011-03-18 21:29:28 +00004326 for (channel=0; channel < 5; channel++)
4327 {
4328 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004329 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004330 mode=color;
4331 max_count=list->nodes[mode].count;
4332 count=0;
4333 do
4334 {
4335 color=list->nodes[color].next[0];
4336 if (list->nodes[color].count > max_count)
4337 {
4338 mode=color;
4339 max_count=list->nodes[mode].count;
4340 }
4341 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004342 } while (count < (ssize_t) pixel_list->length);
cristy733678d2011-03-18 21:29:28 +00004343 channels[channel]=(unsigned short) mode;
4344 }
cristy4c08aed2011-07-01 19:47:50 +00004345 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004346 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4347 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4348 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004349 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4350 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy733678d2011-03-18 21:29:28 +00004351 return(pixel);
4352}
4353
cristy4c08aed2011-07-01 19:47:50 +00004354static PixelInfo GetNonpeakPixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004355{
cristy4c08aed2011-07-01 19:47:50 +00004356 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004357 pixel;
4358
4359 register SkipList
4360 *list;
4361
4362 register ssize_t
4363 channel;
4364
4365 size_t
cristy733678d2011-03-18 21:29:28 +00004366 color,
cristy733678d2011-03-18 21:29:28 +00004367 next,
4368 previous;
4369
cristy49f37242011-03-22 18:18:23 +00004370 ssize_t
4371 count;
4372
cristy733678d2011-03-18 21:29:28 +00004373 unsigned short
4374 channels[5];
4375
4376 /*
cristy49f37242011-03-22 18:18:23 +00004377 Finds the non peak value for each of the colors.
cristy733678d2011-03-18 21:29:28 +00004378 */
cristy733678d2011-03-18 21:29:28 +00004379 for (channel=0; channel < 5; channel++)
4380 {
4381 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004382 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004383 next=list->nodes[color].next[0];
4384 count=0;
4385 do
4386 {
4387 previous=color;
4388 color=next;
4389 next=list->nodes[color].next[0];
4390 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004391 } while (count <= (ssize_t) (pixel_list->length >> 1));
cristy733678d2011-03-18 21:29:28 +00004392 if ((previous == 65536UL) && (next != 65536UL))
4393 color=next;
4394 else
4395 if ((previous != 65536UL) && (next == 65536UL))
4396 color=previous;
4397 channels[channel]=(unsigned short) color;
4398 }
cristy4c08aed2011-07-01 19:47:50 +00004399 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004400 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4401 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4402 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004403 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4404 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy733678d2011-03-18 21:29:28 +00004405 return(pixel);
4406}
4407
cristy4c08aed2011-07-01 19:47:50 +00004408static PixelInfo GetStandardDeviationPixelList(PixelList *pixel_list)
cristy9a68cbb2011-03-29 00:51:23 +00004409{
cristy4c08aed2011-07-01 19:47:50 +00004410 PixelInfo
cristy9a68cbb2011-03-29 00:51:23 +00004411 pixel;
4412
cristy80a99a32011-03-30 01:30:23 +00004413 MagickRealType
4414 sum,
4415 sum_squared;
4416
cristy9a68cbb2011-03-29 00:51:23 +00004417 register SkipList
4418 *list;
4419
4420 register ssize_t
4421 channel;
4422
4423 size_t
cristy80a99a32011-03-30 01:30:23 +00004424 color;
cristy9a68cbb2011-03-29 00:51:23 +00004425
4426 ssize_t
4427 count;
4428
4429 unsigned short
4430 channels[ListChannels];
4431
4432 /*
cristy80a99a32011-03-30 01:30:23 +00004433 Find the standard-deviation value for each of the color.
cristy9a68cbb2011-03-29 00:51:23 +00004434 */
4435 for (channel=0; channel < 5; channel++)
4436 {
4437 list=pixel_list->lists+channel;
4438 color=65536L;
4439 count=0;
cristy80a99a32011-03-30 01:30:23 +00004440 sum=0.0;
4441 sum_squared=0.0;
cristy9a68cbb2011-03-29 00:51:23 +00004442 do
4443 {
cristy80a99a32011-03-30 01:30:23 +00004444 register ssize_t
4445 i;
4446
cristy9a68cbb2011-03-29 00:51:23 +00004447 color=list->nodes[color].next[0];
cristy80a99a32011-03-30 01:30:23 +00004448 sum+=(MagickRealType) list->nodes[color].count*color;
4449 for (i=0; i < (ssize_t) list->nodes[color].count; i++)
4450 sum_squared+=((MagickRealType) color)*((MagickRealType) color);
cristy9a68cbb2011-03-29 00:51:23 +00004451 count+=list->nodes[color].count;
4452 } while (count < (ssize_t) pixel_list->length);
cristy80a99a32011-03-30 01:30:23 +00004453 sum/=pixel_list->length;
4454 sum_squared/=pixel_list->length;
4455 channels[channel]=(unsigned short) sqrt(sum_squared-(sum*sum));
cristy9a68cbb2011-03-29 00:51:23 +00004456 }
cristy4c08aed2011-07-01 19:47:50 +00004457 GetPixelInfo((const Image *) NULL,&pixel);
cristy9a68cbb2011-03-29 00:51:23 +00004458 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4459 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4460 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004461 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4462 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy9a68cbb2011-03-29 00:51:23 +00004463 return(pixel);
4464}
4465
cristy4c08aed2011-07-01 19:47:50 +00004466static inline void InsertPixelList(const Image *image,const Quantum *pixel,
4467 PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004468{
4469 size_t
4470 signature;
4471
4472 unsigned short
4473 index;
4474
cristy4c08aed2011-07-01 19:47:50 +00004475 index=ScaleQuantumToShort(GetPixelRed(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004476 signature=pixel_list->lists[0].nodes[index].signature;
4477 if (signature == pixel_list->signature)
4478 pixel_list->lists[0].nodes[index].count++;
4479 else
4480 AddNodePixelList(pixel_list,0,index);
cristy4c08aed2011-07-01 19:47:50 +00004481 index=ScaleQuantumToShort(GetPixelGreen(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004482 signature=pixel_list->lists[1].nodes[index].signature;
4483 if (signature == pixel_list->signature)
4484 pixel_list->lists[1].nodes[index].count++;
4485 else
4486 AddNodePixelList(pixel_list,1,index);
cristy4c08aed2011-07-01 19:47:50 +00004487 index=ScaleQuantumToShort(GetPixelBlue(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004488 signature=pixel_list->lists[2].nodes[index].signature;
4489 if (signature == pixel_list->signature)
4490 pixel_list->lists[2].nodes[index].count++;
4491 else
4492 AddNodePixelList(pixel_list,2,index);
cristy4c08aed2011-07-01 19:47:50 +00004493 index=ScaleQuantumToShort(GetPixelAlpha(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004494 signature=pixel_list->lists[3].nodes[index].signature;
4495 if (signature == pixel_list->signature)
4496 pixel_list->lists[3].nodes[index].count++;
4497 else
4498 AddNodePixelList(pixel_list,3,index);
4499 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004500 index=ScaleQuantumToShort(GetPixelBlack(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004501 signature=pixel_list->lists[4].nodes[index].signature;
4502 if (signature == pixel_list->signature)
4503 pixel_list->lists[4].nodes[index].count++;
4504 else
4505 AddNodePixelList(pixel_list,4,index);
4506}
4507
cristy80c99742011-04-04 14:46:39 +00004508static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
4509{
4510 if (x < 0)
4511 return(-x);
4512 return(x);
4513}
4514
cristy733678d2011-03-18 21:29:28 +00004515static void ResetPixelList(PixelList *pixel_list)
4516{
4517 int
4518 level;
4519
4520 register ListNode
4521 *root;
4522
4523 register SkipList
4524 *list;
4525
4526 register ssize_t
4527 channel;
4528
4529 /*
4530 Reset the skip-list.
4531 */
4532 for (channel=0; channel < 5; channel++)
4533 {
4534 list=pixel_list->lists+channel;
4535 root=list->nodes+65536UL;
4536 list->level=0;
4537 for (level=0; level < 9; level++)
4538 root->next[level]=65536UL;
4539 }
4540 pixel_list->seed=pixel_list->signature++;
4541}
4542
cristy0834d642011-03-18 18:26:08 +00004543MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
cristy95c38342011-03-18 22:39:51 +00004544 const size_t width,const size_t height,ExceptionInfo *exception)
cristy0834d642011-03-18 18:26:08 +00004545{
cristy3cba8ca2011-03-19 01:29:12 +00004546#define StatisticWidth \
cristyd76c51e2011-03-26 00:21:26 +00004547 (width == 0 ? GetOptimalKernelWidth2D((double) width,0.5) : width)
cristy3cba8ca2011-03-19 01:29:12 +00004548#define StatisticHeight \
cristyd76c51e2011-03-26 00:21:26 +00004549 (height == 0 ? GetOptimalKernelWidth2D((double) height,0.5) : height)
cristy0834d642011-03-18 18:26:08 +00004550#define StatisticImageTag "Statistic/Image"
4551
4552 CacheView
4553 *image_view,
4554 *statistic_view;
4555
4556 Image
4557 *statistic_image;
4558
4559 MagickBooleanType
4560 status;
4561
4562 MagickOffsetType
4563 progress;
4564
4565 PixelList
4566 **restrict pixel_list;
4567
cristy0834d642011-03-18 18:26:08 +00004568 ssize_t
4569 y;
4570
4571 /*
4572 Initialize statistics image attributes.
4573 */
4574 assert(image != (Image *) NULL);
4575 assert(image->signature == MagickSignature);
4576 if (image->debug != MagickFalse)
4577 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4578 assert(exception != (ExceptionInfo *) NULL);
4579 assert(exception->signature == MagickSignature);
cristy0834d642011-03-18 18:26:08 +00004580 statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4581 exception);
4582 if (statistic_image == (Image *) NULL)
4583 return((Image *) NULL);
4584 if (SetImageStorageClass(statistic_image,DirectClass) == MagickFalse)
4585 {
4586 InheritException(exception,&statistic_image->exception);
4587 statistic_image=DestroyImage(statistic_image);
4588 return((Image *) NULL);
4589 }
cristy6fc86bb2011-03-18 23:45:16 +00004590 pixel_list=AcquirePixelListThreadSet(StatisticWidth,StatisticHeight);
cristy0834d642011-03-18 18:26:08 +00004591 if (pixel_list == (PixelList **) NULL)
4592 {
4593 statistic_image=DestroyImage(statistic_image);
4594 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4595 }
4596 /*
cristy8d752042011-03-19 01:00:36 +00004597 Make each pixel the min / max / median / mode / etc. of the neighborhood.
cristy0834d642011-03-18 18:26:08 +00004598 */
4599 status=MagickTrue;
4600 progress=0;
4601 image_view=AcquireCacheView(image);
4602 statistic_view=AcquireCacheView(statistic_image);
4603#if defined(MAGICKCORE_OPENMP_SUPPORT)
4604 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4605#endif
4606 for (y=0; y < (ssize_t) statistic_image->rows; y++)
4607 {
4608 const int
4609 id = GetOpenMPThreadId();
4610
cristy4c08aed2011-07-01 19:47:50 +00004611 register const Quantum
cristy0834d642011-03-18 18:26:08 +00004612 *restrict p;
4613
cristy4c08aed2011-07-01 19:47:50 +00004614 register Quantum
cristy0834d642011-03-18 18:26:08 +00004615 *restrict q;
4616
4617 register ssize_t
4618 x;
4619
4620 if (status == MagickFalse)
4621 continue;
cristy6fc86bb2011-03-18 23:45:16 +00004622 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) StatisticWidth/2L),y-
4623 (ssize_t) (StatisticHeight/2L),image->columns+StatisticWidth,
4624 StatisticHeight,exception);
cristy3cba8ca2011-03-19 01:29:12 +00004625 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004626 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy0834d642011-03-18 18:26:08 +00004627 {
4628 status=MagickFalse;
4629 continue;
4630 }
cristy0834d642011-03-18 18:26:08 +00004631 for (x=0; x < (ssize_t) statistic_image->columns; x++)
4632 {
cristy4c08aed2011-07-01 19:47:50 +00004633 PixelInfo
cristy0834d642011-03-18 18:26:08 +00004634 pixel;
4635
cristy4c08aed2011-07-01 19:47:50 +00004636 register const Quantum
cristy6e3026a2011-03-19 00:54:38 +00004637 *restrict r;
4638
cristy0834d642011-03-18 18:26:08 +00004639 register ssize_t
4640 u,
4641 v;
4642
4643 r=p;
cristy0834d642011-03-18 18:26:08 +00004644 ResetPixelList(pixel_list[id]);
cristy6e4c3292011-03-19 00:53:55 +00004645 for (v=0; v < (ssize_t) StatisticHeight; v++)
cristy0834d642011-03-18 18:26:08 +00004646 {
cristy6e4c3292011-03-19 00:53:55 +00004647 for (u=0; u < (ssize_t) StatisticWidth; u++)
cristyed231572011-07-14 02:18:59 +00004648 InsertPixelList(image,r+u*GetPixelChannels(image),pixel_list[id]);
4649 r+=(image->columns+StatisticWidth)*GetPixelChannels(image);
cristy0834d642011-03-18 18:26:08 +00004650 }
cristy4c08aed2011-07-01 19:47:50 +00004651 GetPixelInfo(image,&pixel);
cristy490408a2011-07-07 14:42:05 +00004652 SetPixelInfo(image,p+(StatisticWidth*StatisticHeight/2)*
cristyed231572011-07-14 02:18:59 +00004653 GetPixelChannels(image),&pixel);
cristy0834d642011-03-18 18:26:08 +00004654 switch (type)
4655 {
cristy80c99742011-04-04 14:46:39 +00004656 case GradientStatistic:
4657 {
cristy4c08aed2011-07-01 19:47:50 +00004658 PixelInfo
cristy80c99742011-04-04 14:46:39 +00004659 maximum,
4660 minimum;
4661
4662 minimum=GetMinimumPixelList(pixel_list[id]);
4663 maximum=GetMaximumPixelList(pixel_list[id]);
4664 pixel.red=MagickAbsoluteValue(maximum.red-minimum.red);
4665 pixel.green=MagickAbsoluteValue(maximum.green-minimum.green);
4666 pixel.blue=MagickAbsoluteValue(maximum.blue-minimum.blue);
cristy4c08aed2011-07-01 19:47:50 +00004667 pixel.alpha=MagickAbsoluteValue(maximum.alpha-minimum.alpha);
cristy80c99742011-04-04 14:46:39 +00004668 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004669 pixel.black=MagickAbsoluteValue(maximum.black-minimum.black);
cristy80c99742011-04-04 14:46:39 +00004670 break;
4671 }
cristy6fc86bb2011-03-18 23:45:16 +00004672 case MaximumStatistic:
4673 {
4674 pixel=GetMaximumPixelList(pixel_list[id]);
4675 break;
4676 }
cristy49f37242011-03-22 18:18:23 +00004677 case MeanStatistic:
4678 {
4679 pixel=GetMeanPixelList(pixel_list[id]);
4680 break;
4681 }
cristyf2ad14a2011-03-18 18:57:25 +00004682 case MedianStatistic:
cristy6fc86bb2011-03-18 23:45:16 +00004683 default:
cristyf2ad14a2011-03-18 18:57:25 +00004684 {
4685 pixel=GetMedianPixelList(pixel_list[id]);
4686 break;
4687 }
cristy6fc86bb2011-03-18 23:45:16 +00004688 case MinimumStatistic:
4689 {
4690 pixel=GetMinimumPixelList(pixel_list[id]);
4691 break;
4692 }
cristyf2ad14a2011-03-18 18:57:25 +00004693 case ModeStatistic:
4694 {
4695 pixel=GetModePixelList(pixel_list[id]);
4696 break;
4697 }
4698 case NonpeakStatistic:
4699 {
4700 pixel=GetNonpeakPixelList(pixel_list[id]);
4701 break;
4702 }
cristy9a68cbb2011-03-29 00:51:23 +00004703 case StandardDeviationStatistic:
4704 {
4705 pixel=GetStandardDeviationPixelList(pixel_list[id]);
4706 break;
4707 }
cristy0834d642011-03-18 18:26:08 +00004708 }
cristyed231572011-07-14 02:18:59 +00004709 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004710 SetPixelRed(statistic_image,ClampToQuantum(pixel.red),q);
cristyed231572011-07-14 02:18:59 +00004711 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004712 SetPixelGreen(statistic_image,ClampToQuantum(pixel.green),q);
cristyed231572011-07-14 02:18:59 +00004713 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004714 SetPixelBlue(statistic_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00004715 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy0834d642011-03-18 18:26:08 +00004716 (image->colorspace == CMYKColorspace))
cristy490408a2011-07-07 14:42:05 +00004717 SetPixelBlack(statistic_image,ClampToQuantum(pixel.black),q);
cristyed231572011-07-14 02:18:59 +00004718 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00004719 (image->matte != MagickFalse))
cristy490408a2011-07-07 14:42:05 +00004720 SetPixelAlpha(statistic_image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004721 p+=GetPixelChannels(image);
4722 q+=GetPixelChannels(statistic_image);
cristy0834d642011-03-18 18:26:08 +00004723 }
4724 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
4725 status=MagickFalse;
4726 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4727 {
4728 MagickBooleanType
4729 proceed;
4730
4731#if defined(MAGICKCORE_OPENMP_SUPPORT)
4732 #pragma omp critical (MagickCore_StatisticImage)
4733#endif
4734 proceed=SetImageProgress(image,StatisticImageTag,progress++,
4735 image->rows);
4736 if (proceed == MagickFalse)
4737 status=MagickFalse;
4738 }
4739 }
4740 statistic_view=DestroyCacheView(statistic_view);
4741 image_view=DestroyCacheView(image_view);
4742 pixel_list=DestroyPixelListThreadSet(pixel_list);
4743 return(statistic_image);
4744}
4745
4746/*
4747%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4748% %
4749% %
4750% %
cristy3ed852e2009-09-05 21:47:34 +00004751% U n s h a r p M a s k I m a g e %
4752% %
4753% %
4754% %
4755%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4756%
4757% UnsharpMaskImage() sharpens one or more image channels. We convolve the
4758% image with a Gaussian operator of the given radius and standard deviation
4759% (sigma). For reasonable results, radius should be larger than sigma. Use a
4760% radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4761%
4762% The format of the UnsharpMaskImage method is:
4763%
4764% Image *UnsharpMaskImage(const Image *image,const double radius,
4765% const double sigma,const double amount,const double threshold,
4766% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004767%
4768% A description of each parameter follows:
4769%
4770% o image: the image.
4771%
cristy3ed852e2009-09-05 21:47:34 +00004772% o radius: the radius of the Gaussian, in pixels, not counting the center
4773% pixel.
4774%
4775% o sigma: the standard deviation of the Gaussian, in pixels.
4776%
4777% o amount: the percentage of the difference between the original and the
4778% blur image that is added back into the original.
4779%
4780% o threshold: the threshold in pixels needed to apply the diffence amount.
4781%
4782% o exception: return any errors or warnings in this structure.
4783%
4784*/
cristyf4ad9df2011-07-08 16:49:03 +00004785MagickExport Image *UnsharpMaskImage(const Image *image,
4786 const double radius,const double sigma,const double amount,
4787 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004788{
4789#define SharpenImageTag "Sharpen/Image"
4790
cristyc4c8d132010-01-07 01:58:38 +00004791 CacheView
4792 *image_view,
4793 *unsharp_view;
4794
cristy3ed852e2009-09-05 21:47:34 +00004795 Image
4796 *unsharp_image;
4797
cristy3ed852e2009-09-05 21:47:34 +00004798 MagickBooleanType
4799 status;
4800
cristybb503372010-05-27 20:51:26 +00004801 MagickOffsetType
4802 progress;
4803
cristy4c08aed2011-07-01 19:47:50 +00004804 PixelInfo
cristyddd82202009-11-03 20:14:50 +00004805 bias;
cristy3ed852e2009-09-05 21:47:34 +00004806
4807 MagickRealType
4808 quantum_threshold;
4809
cristybb503372010-05-27 20:51:26 +00004810 ssize_t
4811 y;
4812
cristy3ed852e2009-09-05 21:47:34 +00004813 assert(image != (const Image *) NULL);
4814 assert(image->signature == MagickSignature);
4815 if (image->debug != MagickFalse)
4816 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4817 assert(exception != (ExceptionInfo *) NULL);
cristyf4ad9df2011-07-08 16:49:03 +00004818 unsharp_image=BlurImage(image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00004819 if (unsharp_image == (Image *) NULL)
4820 return((Image *) NULL);
4821 quantum_threshold=(MagickRealType) QuantumRange*threshold;
4822 /*
4823 Unsharp-mask image.
4824 */
4825 status=MagickTrue;
4826 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00004827 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00004828 image_view=AcquireCacheView(image);
4829 unsharp_view=AcquireCacheView(unsharp_image);
cristyb5d5f722009-11-04 03:03:49 +00004830#if defined(MAGICKCORE_OPENMP_SUPPORT)
4831 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004832#endif
cristybb503372010-05-27 20:51:26 +00004833 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004834 {
cristy4c08aed2011-07-01 19:47:50 +00004835 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004836 pixel;
4837
cristy4c08aed2011-07-01 19:47:50 +00004838 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004839 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004840
cristy4c08aed2011-07-01 19:47:50 +00004841 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004842 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004843
cristy117ff172010-08-15 21:35:32 +00004844 register ssize_t
4845 x;
4846
cristy3ed852e2009-09-05 21:47:34 +00004847 if (status == MagickFalse)
4848 continue;
4849 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4850 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4851 exception);
cristy4c08aed2011-07-01 19:47:50 +00004852 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004853 {
4854 status=MagickFalse;
4855 continue;
4856 }
cristyddd82202009-11-03 20:14:50 +00004857 pixel=bias;
cristybb503372010-05-27 20:51:26 +00004858 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004859 {
cristyed231572011-07-14 02:18:59 +00004860 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004861 {
cristy4c08aed2011-07-01 19:47:50 +00004862 pixel.red=GetPixelRed(image,p)-(MagickRealType) GetPixelRed(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004863 if (fabs(2.0*pixel.red) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004864 pixel.red=(MagickRealType) GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004865 else
cristy4c08aed2011-07-01 19:47:50 +00004866 pixel.red=(MagickRealType) GetPixelRed(image,p)+(pixel.red*amount);
4867 SetPixelRed(unsharp_image,ClampToQuantum(pixel.red),q);
cristy3ed852e2009-09-05 21:47:34 +00004868 }
cristyed231572011-07-14 02:18:59 +00004869 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004870 {
cristy4c08aed2011-07-01 19:47:50 +00004871 pixel.green=GetPixelGreen(image,p)-
4872 (MagickRealType) GetPixelGreen(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004873 if (fabs(2.0*pixel.green) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004874 pixel.green=(MagickRealType)
4875 GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004876 else
cristy4c08aed2011-07-01 19:47:50 +00004877 pixel.green=(MagickRealType)
4878 GetPixelGreen(image,p)+
4879 (pixel.green*amount);
4880 SetPixelGreen(unsharp_image,
4881 ClampToQuantum(pixel.green),q);
cristy3ed852e2009-09-05 21:47:34 +00004882 }
cristyed231572011-07-14 02:18:59 +00004883 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004884 {
cristy4c08aed2011-07-01 19:47:50 +00004885 pixel.blue=GetPixelBlue(image,p)-
4886 (MagickRealType) GetPixelBlue(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004887 if (fabs(2.0*pixel.blue) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004888 pixel.blue=(MagickRealType)
4889 GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004890 else
cristy4c08aed2011-07-01 19:47:50 +00004891 pixel.blue=(MagickRealType)
4892 GetPixelBlue(image,p)+(pixel.blue*amount);
4893 SetPixelBlue(unsharp_image,
4894 ClampToQuantum(pixel.blue),q);
cristy3ed852e2009-09-05 21:47:34 +00004895 }
cristyed231572011-07-14 02:18:59 +00004896 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00004897 (image->colorspace == CMYKColorspace))
4898 {
cristy4c08aed2011-07-01 19:47:50 +00004899 pixel.black=GetPixelBlack(image,p)-
4900 (MagickRealType) GetPixelBlack(image,q);
4901 if (fabs(2.0*pixel.black) < quantum_threshold)
4902 pixel.black=(MagickRealType)
4903 GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004904 else
cristy4c08aed2011-07-01 19:47:50 +00004905 pixel.black=(MagickRealType)
4906 GetPixelBlack(image,p)+(pixel.black*
4907 amount);
4908 SetPixelBlack(unsharp_image,
4909 ClampToQuantum(pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00004910 }
cristyed231572011-07-14 02:18:59 +00004911 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004912 {
4913 pixel.alpha=GetPixelAlpha(image,p)-
4914 (MagickRealType) GetPixelAlpha(image,q);
4915 if (fabs(2.0*pixel.alpha) < quantum_threshold)
4916 pixel.alpha=(MagickRealType)
4917 GetPixelAlpha(image,p);
4918 else
4919 pixel.alpha=GetPixelAlpha(image,p)+
4920 (pixel.alpha*amount);
4921 SetPixelAlpha(unsharp_image,
4922 ClampToQuantum(pixel.alpha),q);
4923 }
cristyed231572011-07-14 02:18:59 +00004924 p+=GetPixelChannels(image);
4925 q+=GetPixelChannels(unsharp_image);
cristy3ed852e2009-09-05 21:47:34 +00004926 }
4927 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4928 status=MagickFalse;
4929 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4930 {
4931 MagickBooleanType
4932 proceed;
4933
cristyb5d5f722009-11-04 03:03:49 +00004934#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00004935 #pragma omp critical (MagickCore_UnsharpMaskImage)
cristy3ed852e2009-09-05 21:47:34 +00004936#endif
4937 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4938 if (proceed == MagickFalse)
4939 status=MagickFalse;
4940 }
4941 }
4942 unsharp_image->type=image->type;
4943 unsharp_view=DestroyCacheView(unsharp_view);
4944 image_view=DestroyCacheView(image_view);
4945 if (status == MagickFalse)
4946 unsharp_image=DestroyImage(unsharp_image);
4947 return(unsharp_image);
4948}