blob: ab794b4f48fe99f4d15c528ed73aaa88ead9b2cf [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% EEEEE FFFFF FFFFF EEEEE CCCC TTTTT %
7% E F F E C T %
8% EEE FFF FFF EEE C T %
9% E F F E C T %
10% EEEEE F F EEEEE CCCC T %
11% %
12% %
13% MagickCore Image Effects Methods %
14% %
15% Software Design %
16% John Cristy %
17% October 1996 %
18% %
19% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/accelerate.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/decorate.h"
52#include "MagickCore/draw.h"
53#include "MagickCore/enhance.h"
54#include "MagickCore/exception.h"
55#include "MagickCore/exception-private.h"
56#include "MagickCore/effect.h"
57#include "MagickCore/fx.h"
58#include "MagickCore/gem.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/image-private.h"
61#include "MagickCore/list.h"
62#include "MagickCore/log.h"
63#include "MagickCore/memory_.h"
64#include "MagickCore/monitor.h"
65#include "MagickCore/monitor-private.h"
66#include "MagickCore/montage.h"
67#include "MagickCore/morphology.h"
68#include "MagickCore/paint.h"
69#include "MagickCore/pixel-accessor.h"
70#include "MagickCore/property.h"
71#include "MagickCore/quantize.h"
72#include "MagickCore/quantum.h"
73#include "MagickCore/quantum-private.h"
74#include "MagickCore/random_.h"
75#include "MagickCore/random-private.h"
76#include "MagickCore/resample.h"
77#include "MagickCore/resample-private.h"
78#include "MagickCore/resize.h"
79#include "MagickCore/resource_.h"
80#include "MagickCore/segment.h"
81#include "MagickCore/shear.h"
82#include "MagickCore/signature-private.h"
83#include "MagickCore/string_.h"
84#include "MagickCore/thread-private.h"
85#include "MagickCore/transform.h"
86#include "MagickCore/threshold.h"
cristy3ed852e2009-09-05 21:47:34 +000087
88/*
89%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90% %
91% %
92% %
93% A d a p t i v e B l u r I m a g e %
94% %
95% %
96% %
97%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98%
99% AdaptiveBlurImage() adaptively blurs the image by blurring less
100% intensely near image edges and more intensely far from edges. We blur the
101% image with a Gaussian operator of the given radius and standard deviation
102% (sigma). For reasonable results, radius should be larger than sigma. Use a
103% radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
104%
105% The format of the AdaptiveBlurImage method is:
106%
107% Image *AdaptiveBlurImage(const Image *image,const double radius,
108% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000109%
110% A description of each parameter follows:
111%
112% o image: the image.
113%
cristy3ed852e2009-09-05 21:47:34 +0000114% o radius: the radius of the Gaussian, in pixels, not counting the center
115% pixel.
116%
117% o sigma: the standard deviation of the Laplacian, in pixels.
118%
119% o exception: return any errors or warnings in this structure.
120%
121*/
122
cristyf89cb1d2011-07-07 01:24:37 +0000123MagickExport MagickBooleanType AdaptiveLevelImage(Image *image,
124 const char *levels)
125{
126 double
127 black_point,
128 gamma,
129 white_point;
130
131 GeometryInfo
132 geometry_info;
133
134 MagickBooleanType
135 status;
136
137 MagickStatusType
138 flags;
139
140 /*
141 Parse levels.
142 */
143 if (levels == (char *) NULL)
144 return(MagickFalse);
145 flags=ParseGeometry(levels,&geometry_info);
146 black_point=geometry_info.rho;
147 white_point=(double) QuantumRange;
148 if ((flags & SigmaValue) != 0)
149 white_point=geometry_info.sigma;
150 gamma=1.0;
151 if ((flags & XiValue) != 0)
152 gamma=geometry_info.xi;
153 if ((flags & PercentValue) != 0)
154 {
155 black_point*=(double) image->columns*image->rows/100.0;
156 white_point*=(double) image->columns*image->rows/100.0;
157 }
158 if ((flags & SigmaValue) == 0)
159 white_point=(double) QuantumRange-black_point;
160 if ((flags & AspectValue ) == 0)
161 status=LevelImage(image,black_point,white_point,gamma);
162 else
163 status=LevelizeImage(image,black_point,white_point,gamma);
164 return(status);
165}
166
cristyf4ad9df2011-07-08 16:49:03 +0000167MagickExport Image *AdaptiveBlurImage(const Image *image,
168 const double radius,const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000169{
170#define AdaptiveBlurImageTag "Convolve/Image"
171#define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
172
cristyc4c8d132010-01-07 01:58:38 +0000173 CacheView
174 *blur_view,
175 *edge_view,
176 *image_view;
177
cristy3ed852e2009-09-05 21:47:34 +0000178 double
cristy47e00502009-12-17 19:19:57 +0000179 **kernel,
180 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000181
182 Image
183 *blur_image,
184 *edge_image,
185 *gaussian_image;
186
cristy3ed852e2009-09-05 21:47:34 +0000187 MagickBooleanType
188 status;
189
cristybb503372010-05-27 20:51:26 +0000190 MagickOffsetType
191 progress;
192
cristy4c08aed2011-07-01 19:47:50 +0000193 PixelInfo
cristyddd82202009-11-03 20:14:50 +0000194 bias;
cristy3ed852e2009-09-05 21:47:34 +0000195
cristybb503372010-05-27 20:51:26 +0000196 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000197 i;
cristy3ed852e2009-09-05 21:47:34 +0000198
cristybb503372010-05-27 20:51:26 +0000199 size_t
cristy3ed852e2009-09-05 21:47:34 +0000200 width;
201
cristybb503372010-05-27 20:51:26 +0000202 ssize_t
203 j,
204 k,
205 u,
206 v,
207 y;
208
cristy3ed852e2009-09-05 21:47:34 +0000209 assert(image != (const Image *) NULL);
210 assert(image->signature == MagickSignature);
211 if (image->debug != MagickFalse)
212 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
213 assert(exception != (ExceptionInfo *) NULL);
214 assert(exception->signature == MagickSignature);
215 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
216 if (blur_image == (Image *) NULL)
217 return((Image *) NULL);
218 if (fabs(sigma) <= MagickEpsilon)
219 return(blur_image);
220 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
221 {
222 InheritException(exception,&blur_image->exception);
223 blur_image=DestroyImage(blur_image);
224 return((Image *) NULL);
225 }
226 /*
227 Edge detect the image brighness channel, level, blur, and level again.
228 */
229 edge_image=EdgeImage(image,radius,exception);
230 if (edge_image == (Image *) NULL)
231 {
232 blur_image=DestroyImage(blur_image);
233 return((Image *) NULL);
234 }
cristyf89cb1d2011-07-07 01:24:37 +0000235 (void) AdaptiveLevelImage(edge_image,"20%,95%");
cristy3ed852e2009-09-05 21:47:34 +0000236 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
237 if (gaussian_image != (Image *) NULL)
238 {
239 edge_image=DestroyImage(edge_image);
240 edge_image=gaussian_image;
241 }
cristyf89cb1d2011-07-07 01:24:37 +0000242 (void) AdaptiveLevelImage(edge_image,"10%,95%");
cristy3ed852e2009-09-05 21:47:34 +0000243 /*
244 Create a set of kernels from maximum (radius,sigma) to minimum.
245 */
246 width=GetOptimalKernelWidth2D(radius,sigma);
247 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
248 if (kernel == (double **) NULL)
249 {
250 edge_image=DestroyImage(edge_image);
251 blur_image=DestroyImage(blur_image);
252 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
253 }
254 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
cristybb503372010-05-27 20:51:26 +0000255 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000256 {
257 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
258 sizeof(**kernel));
259 if (kernel[i] == (double *) NULL)
260 break;
cristy47e00502009-12-17 19:19:57 +0000261 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000262 j=(ssize_t) (width-i)/2;
cristy47e00502009-12-17 19:19:57 +0000263 k=0;
264 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +0000265 {
cristy47e00502009-12-17 19:19:57 +0000266 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +0000267 {
cristy4205a3c2010-09-12 20:19:59 +0000268 kernel[i][k]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
269 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +0000270 normalize+=kernel[i][k];
271 k++;
cristy3ed852e2009-09-05 21:47:34 +0000272 }
273 }
cristy3ed852e2009-09-05 21:47:34 +0000274 if (fabs(normalize) <= MagickEpsilon)
275 normalize=1.0;
276 normalize=1.0/normalize;
cristy47e00502009-12-17 19:19:57 +0000277 for (k=0; k < (j*j); k++)
278 kernel[i][k]=normalize*kernel[i][k];
cristy3ed852e2009-09-05 21:47:34 +0000279 }
cristybb503372010-05-27 20:51:26 +0000280 if (i < (ssize_t) width)
cristy3ed852e2009-09-05 21:47:34 +0000281 {
282 for (i-=2; i >= 0; i-=2)
283 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
284 kernel=(double **) RelinquishMagickMemory(kernel);
285 edge_image=DestroyImage(edge_image);
286 blur_image=DestroyImage(blur_image);
287 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
288 }
289 /*
290 Adaptively blur image.
291 */
292 status=MagickTrue;
293 progress=0;
cristy4c08aed2011-07-01 19:47:50 +0000294 GetPixelInfo(image,&bias);
295 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +0000296 image_view=AcquireCacheView(image);
297 edge_view=AcquireCacheView(edge_image);
298 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +0000299#if defined(MAGICKCORE_OPENMP_SUPPORT)
300 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000301#endif
cristybb503372010-05-27 20:51:26 +0000302 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000303 {
cristy4c08aed2011-07-01 19:47:50 +0000304 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000305 *restrict p,
306 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +0000307
cristy4c08aed2011-07-01 19:47:50 +0000308 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000309 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000310
cristy117ff172010-08-15 21:35:32 +0000311 register ssize_t
312 x;
313
cristy3ed852e2009-09-05 21:47:34 +0000314 if (status == MagickFalse)
315 continue;
316 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
317 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
318 exception);
cristy4c08aed2011-07-01 19:47:50 +0000319 if ((r == (const Quantum *) NULL) || (q == (const Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000320 {
321 status=MagickFalse;
322 continue;
323 }
cristybb503372010-05-27 20:51:26 +0000324 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000325 {
cristy4c08aed2011-07-01 19:47:50 +0000326 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000327 pixel;
328
329 MagickRealType
330 alpha,
331 gamma;
332
333 register const double
cristyc47d1f82009-11-26 01:44:43 +0000334 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +0000335
cristybb503372010-05-27 20:51:26 +0000336 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000337 i,
338 u,
339 v;
340
341 gamma=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000342 i=(ssize_t) ceil((double) width*QuantumScale*
343 GetPixelIntensity(edge_image,r)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000344 if (i < 0)
345 i=0;
346 else
cristybb503372010-05-27 20:51:26 +0000347 if (i > (ssize_t) width)
348 i=(ssize_t) width;
cristy3ed852e2009-09-05 21:47:34 +0000349 if ((i & 0x01) != 0)
350 i--;
cristya21afde2010-07-02 00:45:40 +0000351 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-i)/2L),y-
352 (ssize_t) ((width-i)/2L),width-i,width-i,exception);
cristy4c08aed2011-07-01 19:47:50 +0000353 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000354 break;
cristyddd82202009-11-03 20:14:50 +0000355 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +0000356 k=kernel[i];
cristybb503372010-05-27 20:51:26 +0000357 for (v=0; v < (ssize_t) (width-i); v++)
cristy3ed852e2009-09-05 21:47:34 +0000358 {
cristybb503372010-05-27 20:51:26 +0000359 for (u=0; u < (ssize_t) (width-i); u++)
cristy3ed852e2009-09-05 21:47:34 +0000360 {
361 alpha=1.0;
cristyed231572011-07-14 02:18:59 +0000362 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000363 (image->matte != MagickFalse))
cristy4c08aed2011-07-01 19:47:50 +0000364 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,p));
cristyed231572011-07-14 02:18:59 +0000365 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000366 pixel.red+=(*k)*alpha*GetPixelRed(image,p);
cristyed231572011-07-14 02:18:59 +0000367 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000368 pixel.green+=(*k)*alpha*GetPixelGreen(image,p);
cristyed231572011-07-14 02:18:59 +0000369 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000370 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p);
cristyed231572011-07-14 02:18:59 +0000371 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000372 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +0000373 pixel.black+=(*k)*alpha*GetPixelBlack(image,p);
cristyed231572011-07-14 02:18:59 +0000374 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000375 pixel.alpha+=(*k)*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000376 gamma+=(*k)*alpha;
377 k++;
cristyed231572011-07-14 02:18:59 +0000378 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000379 }
380 }
381 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +0000382 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000383 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +0000384 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000385 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +0000386 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000387 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000388 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000389 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +0000390 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristyed231572011-07-14 02:18:59 +0000391 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000392 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +0000393 q+=GetPixelChannels(blur_image);
394 r+=GetPixelChannels(edge_image);
cristy3ed852e2009-09-05 21:47:34 +0000395 }
396 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
397 status=MagickFalse;
398 if (image->progress_monitor != (MagickProgressMonitor) NULL)
399 {
400 MagickBooleanType
401 proceed;
402
cristyb5d5f722009-11-04 03:03:49 +0000403#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy9aa95be2011-07-20 21:56:45 +0000404 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
cristy3ed852e2009-09-05 21:47:34 +0000405#endif
406 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
407 image->rows);
408 if (proceed == MagickFalse)
409 status=MagickFalse;
410 }
411 }
412 blur_image->type=image->type;
413 blur_view=DestroyCacheView(blur_view);
414 edge_view=DestroyCacheView(edge_view);
415 image_view=DestroyCacheView(image_view);
416 edge_image=DestroyImage(edge_image);
cristybb503372010-05-27 20:51:26 +0000417 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000418 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
419 kernel=(double **) RelinquishMagickMemory(kernel);
420 if (status == MagickFalse)
421 blur_image=DestroyImage(blur_image);
422 return(blur_image);
423}
424
425/*
426%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
427% %
428% %
429% %
430% A d a p t i v e S h a r p e n I m a g e %
431% %
432% %
433% %
434%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
435%
436% AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
437% intensely near image edges and less intensely far from edges. We sharpen the
438% image with a Gaussian operator of the given radius and standard deviation
439% (sigma). For reasonable results, radius should be larger than sigma. Use a
440% radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
441%
442% The format of the AdaptiveSharpenImage method is:
443%
444% Image *AdaptiveSharpenImage(const Image *image,const double radius,
445% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000446%
447% A description of each parameter follows:
448%
449% o image: the image.
450%
cristy3ed852e2009-09-05 21:47:34 +0000451% o radius: the radius of the Gaussian, in pixels, not counting the center
452% pixel.
453%
454% o sigma: the standard deviation of the Laplacian, in pixels.
455%
456% o exception: return any errors or warnings in this structure.
457%
458*/
cristy3ed852e2009-09-05 21:47:34 +0000459MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
460 const double sigma,ExceptionInfo *exception)
461{
cristy3ed852e2009-09-05 21:47:34 +0000462#define AdaptiveSharpenImageTag "Convolve/Image"
463#define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
464
cristyc4c8d132010-01-07 01:58:38 +0000465 CacheView
466 *sharp_view,
467 *edge_view,
468 *image_view;
469
cristy3ed852e2009-09-05 21:47:34 +0000470 double
cristy47e00502009-12-17 19:19:57 +0000471 **kernel,
472 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000473
474 Image
475 *sharp_image,
476 *edge_image,
477 *gaussian_image;
478
cristy3ed852e2009-09-05 21:47:34 +0000479 MagickBooleanType
480 status;
481
cristybb503372010-05-27 20:51:26 +0000482 MagickOffsetType
483 progress;
484
cristy4c08aed2011-07-01 19:47:50 +0000485 PixelInfo
cristyddd82202009-11-03 20:14:50 +0000486 bias;
cristy3ed852e2009-09-05 21:47:34 +0000487
cristybb503372010-05-27 20:51:26 +0000488 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000489 i;
cristy3ed852e2009-09-05 21:47:34 +0000490
cristybb503372010-05-27 20:51:26 +0000491 size_t
cristy3ed852e2009-09-05 21:47:34 +0000492 width;
493
cristybb503372010-05-27 20:51:26 +0000494 ssize_t
495 j,
496 k,
497 u,
498 v,
499 y;
500
cristy3ed852e2009-09-05 21:47:34 +0000501 assert(image != (const Image *) NULL);
502 assert(image->signature == MagickSignature);
503 if (image->debug != MagickFalse)
504 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
505 assert(exception != (ExceptionInfo *) NULL);
506 assert(exception->signature == MagickSignature);
507 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
508 if (sharp_image == (Image *) NULL)
509 return((Image *) NULL);
510 if (fabs(sigma) <= MagickEpsilon)
511 return(sharp_image);
512 if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
513 {
514 InheritException(exception,&sharp_image->exception);
515 sharp_image=DestroyImage(sharp_image);
516 return((Image *) NULL);
517 }
518 /*
519 Edge detect the image brighness channel, level, sharp, and level again.
520 */
521 edge_image=EdgeImage(image,radius,exception);
522 if (edge_image == (Image *) NULL)
523 {
524 sharp_image=DestroyImage(sharp_image);
525 return((Image *) NULL);
526 }
cristyf89cb1d2011-07-07 01:24:37 +0000527 (void) AdaptiveLevelImage(edge_image,"20%,95%");
cristy3ed852e2009-09-05 21:47:34 +0000528 gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
529 if (gaussian_image != (Image *) NULL)
530 {
531 edge_image=DestroyImage(edge_image);
532 edge_image=gaussian_image;
533 }
cristyf89cb1d2011-07-07 01:24:37 +0000534 (void) AdaptiveLevelImage(edge_image,"10%,95%");
cristy3ed852e2009-09-05 21:47:34 +0000535 /*
536 Create a set of kernels from maximum (radius,sigma) to minimum.
537 */
538 width=GetOptimalKernelWidth2D(radius,sigma);
539 kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
540 if (kernel == (double **) NULL)
541 {
542 edge_image=DestroyImage(edge_image);
543 sharp_image=DestroyImage(sharp_image);
544 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
545 }
546 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
cristybb503372010-05-27 20:51:26 +0000547 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000548 {
549 kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
550 sizeof(**kernel));
551 if (kernel[i] == (double *) NULL)
552 break;
cristy47e00502009-12-17 19:19:57 +0000553 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000554 j=(ssize_t) (width-i)/2;
cristy47e00502009-12-17 19:19:57 +0000555 k=0;
556 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +0000557 {
cristy47e00502009-12-17 19:19:57 +0000558 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +0000559 {
cristy4205a3c2010-09-12 20:19:59 +0000560 kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
561 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +0000562 normalize+=kernel[i][k];
563 k++;
cristy3ed852e2009-09-05 21:47:34 +0000564 }
565 }
cristy3ed852e2009-09-05 21:47:34 +0000566 if (fabs(normalize) <= MagickEpsilon)
567 normalize=1.0;
568 normalize=1.0/normalize;
cristy47e00502009-12-17 19:19:57 +0000569 for (k=0; k < (j*j); k++)
570 kernel[i][k]=normalize*kernel[i][k];
cristy3ed852e2009-09-05 21:47:34 +0000571 }
cristybb503372010-05-27 20:51:26 +0000572 if (i < (ssize_t) width)
cristy3ed852e2009-09-05 21:47:34 +0000573 {
574 for (i-=2; i >= 0; i-=2)
575 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
576 kernel=(double **) RelinquishMagickMemory(kernel);
577 edge_image=DestroyImage(edge_image);
578 sharp_image=DestroyImage(sharp_image);
579 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
580 }
581 /*
582 Adaptively sharpen image.
583 */
584 status=MagickTrue;
585 progress=0;
cristy4c08aed2011-07-01 19:47:50 +0000586 GetPixelInfo(image,&bias);
587 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +0000588 image_view=AcquireCacheView(image);
589 edge_view=AcquireCacheView(edge_image);
590 sharp_view=AcquireCacheView(sharp_image);
cristyb5d5f722009-11-04 03:03:49 +0000591#if defined(MAGICKCORE_OPENMP_SUPPORT)
592 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000593#endif
cristybb503372010-05-27 20:51:26 +0000594 for (y=0; y < (ssize_t) sharp_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000595 {
cristy4c08aed2011-07-01 19:47:50 +0000596 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000597 *restrict p,
598 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +0000599
cristy4c08aed2011-07-01 19:47:50 +0000600 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000601 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000602
cristy117ff172010-08-15 21:35:32 +0000603 register ssize_t
604 x;
605
cristy3ed852e2009-09-05 21:47:34 +0000606 if (status == MagickFalse)
607 continue;
608 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
609 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
610 exception);
cristy4c08aed2011-07-01 19:47:50 +0000611 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000612 {
613 status=MagickFalse;
614 continue;
615 }
cristybb503372010-05-27 20:51:26 +0000616 for (x=0; x < (ssize_t) sharp_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000617 {
cristy4c08aed2011-07-01 19:47:50 +0000618 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000619 pixel;
620
621 MagickRealType
622 alpha,
623 gamma;
624
625 register const double
cristyc47d1f82009-11-26 01:44:43 +0000626 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +0000627
cristybb503372010-05-27 20:51:26 +0000628 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000629 i,
630 u,
631 v;
632
633 gamma=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000634 i=(ssize_t) ceil((double) width*QuantumScale*
635 GetPixelIntensity(edge_image,r)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000636 if (i < 0)
637 i=0;
638 else
cristybb503372010-05-27 20:51:26 +0000639 if (i > (ssize_t) width)
640 i=(ssize_t) width;
cristy3ed852e2009-09-05 21:47:34 +0000641 if ((i & 0x01) != 0)
642 i--;
cristy117ff172010-08-15 21:35:32 +0000643 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-i)/2L),y-
644 (ssize_t) ((width-i)/2L),width-i,width-i,exception);
cristy4c08aed2011-07-01 19:47:50 +0000645 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000646 break;
cristy3ed852e2009-09-05 21:47:34 +0000647 k=kernel[i];
cristyddd82202009-11-03 20:14:50 +0000648 pixel=bias;
cristybb503372010-05-27 20:51:26 +0000649 for (v=0; v < (ssize_t) (width-i); v++)
cristy3ed852e2009-09-05 21:47:34 +0000650 {
cristybb503372010-05-27 20:51:26 +0000651 for (u=0; u < (ssize_t) (width-i); u++)
cristy3ed852e2009-09-05 21:47:34 +0000652 {
653 alpha=1.0;
cristyed231572011-07-14 02:18:59 +0000654 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000655 (image->matte != MagickFalse))
cristy4c08aed2011-07-01 19:47:50 +0000656 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,p));
cristyed231572011-07-14 02:18:59 +0000657 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000658 pixel.red+=(*k)*alpha*GetPixelRed(image,p);
cristyed231572011-07-14 02:18:59 +0000659 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000660 pixel.green+=(*k)*alpha*GetPixelGreen(image,p);
cristyed231572011-07-14 02:18:59 +0000661 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000662 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p);
cristyed231572011-07-14 02:18:59 +0000663 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000664 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +0000665 pixel.black+=(*k)*alpha*GetPixelBlack(image,p);
cristyed231572011-07-14 02:18:59 +0000666 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000667 pixel.alpha+=(*k)*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000668 gamma+=(*k)*alpha;
669 k++;
cristyed231572011-07-14 02:18:59 +0000670 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000671 }
672 }
673 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +0000674 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000675 SetPixelRed(sharp_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +0000676 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000677 SetPixelGreen(sharp_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +0000678 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000679 SetPixelBlue(sharp_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000680 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +0000681 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +0000682 SetPixelBlack(sharp_image,ClampToQuantum(gamma*pixel.black),q);
cristyed231572011-07-14 02:18:59 +0000683 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000684 SetPixelAlpha(sharp_image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +0000685 q+=GetPixelChannels(sharp_image);
686 r+=GetPixelChannels(edge_image);
cristy3ed852e2009-09-05 21:47:34 +0000687 }
688 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
689 status=MagickFalse;
690 if (image->progress_monitor != (MagickProgressMonitor) NULL)
691 {
692 MagickBooleanType
693 proceed;
694
cristyb5d5f722009-11-04 03:03:49 +0000695#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000696 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
cristy3ed852e2009-09-05 21:47:34 +0000697#endif
698 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
699 image->rows);
700 if (proceed == MagickFalse)
701 status=MagickFalse;
702 }
703 }
704 sharp_image->type=image->type;
705 sharp_view=DestroyCacheView(sharp_view);
706 edge_view=DestroyCacheView(edge_view);
707 image_view=DestroyCacheView(image_view);
708 edge_image=DestroyImage(edge_image);
cristybb503372010-05-27 20:51:26 +0000709 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000710 kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
711 kernel=(double **) RelinquishMagickMemory(kernel);
712 if (status == MagickFalse)
713 sharp_image=DestroyImage(sharp_image);
714 return(sharp_image);
715}
716
717/*
718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719% %
720% %
721% %
722% B l u r I m a g e %
723% %
724% %
725% %
726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727%
728% BlurImage() blurs an image. We convolve the image with a Gaussian operator
729% of the given radius and standard deviation (sigma). For reasonable results,
730% the radius should be larger than sigma. Use a radius of 0 and BlurImage()
731% selects a suitable radius for you.
732%
733% BlurImage() differs from GaussianBlurImage() in that it uses a separable
734% kernel which is faster but mathematically equivalent to the non-separable
735% kernel.
736%
737% The format of the BlurImage method is:
738%
739% Image *BlurImage(const Image *image,const double radius,
740% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000741%
742% A description of each parameter follows:
743%
744% o image: the image.
745%
cristy3ed852e2009-09-05 21:47:34 +0000746% o radius: the radius of the Gaussian, in pixels, not counting the center
747% pixel.
748%
749% o sigma: the standard deviation of the Gaussian, in pixels.
750%
751% o exception: return any errors or warnings in this structure.
752%
753*/
754
cristybb503372010-05-27 20:51:26 +0000755static double *GetBlurKernel(const size_t width,const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000756{
cristy3ed852e2009-09-05 21:47:34 +0000757 double
cristy47e00502009-12-17 19:19:57 +0000758 *kernel,
759 normalize;
cristy3ed852e2009-09-05 21:47:34 +0000760
cristy117ff172010-08-15 21:35:32 +0000761 register ssize_t
762 i;
763
cristybb503372010-05-27 20:51:26 +0000764 ssize_t
cristy47e00502009-12-17 19:19:57 +0000765 j,
766 k;
cristy3ed852e2009-09-05 21:47:34 +0000767
cristy3ed852e2009-09-05 21:47:34 +0000768 /*
769 Generate a 1-D convolution kernel.
770 */
771 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
772 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
773 if (kernel == (double *) NULL)
774 return(0);
cristy3ed852e2009-09-05 21:47:34 +0000775 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000776 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000777 i=0;
778 for (k=(-j); k <= j; k++)
779 {
cristy4205a3c2010-09-12 20:19:59 +0000780 kernel[i]=(double) (exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
781 (MagickSQ2PI*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +0000782 normalize+=kernel[i];
cristy47e00502009-12-17 19:19:57 +0000783 i++;
784 }
cristybb503372010-05-27 20:51:26 +0000785 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000786 kernel[i]/=normalize;
787 return(kernel);
788}
789
cristyf4ad9df2011-07-08 16:49:03 +0000790MagickExport Image *BlurImage(const Image *image,const double radius,
791 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000792{
793#define BlurImageTag "Blur/Image"
794
cristyc4c8d132010-01-07 01:58:38 +0000795 CacheView
796 *blur_view,
797 *image_view;
798
cristy3ed852e2009-09-05 21:47:34 +0000799 double
800 *kernel;
801
802 Image
803 *blur_image;
804
cristy3ed852e2009-09-05 21:47:34 +0000805 MagickBooleanType
806 status;
807
cristybb503372010-05-27 20:51:26 +0000808 MagickOffsetType
809 progress;
810
cristy4c08aed2011-07-01 19:47:50 +0000811 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000812 bias;
813
cristybb503372010-05-27 20:51:26 +0000814 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000815 i;
816
cristybb503372010-05-27 20:51:26 +0000817 size_t
cristy3ed852e2009-09-05 21:47:34 +0000818 width;
819
cristybb503372010-05-27 20:51:26 +0000820 ssize_t
821 x,
822 y;
823
cristy3ed852e2009-09-05 21:47:34 +0000824 /*
825 Initialize blur image attributes.
826 */
827 assert(image != (Image *) NULL);
828 assert(image->signature == MagickSignature);
829 if (image->debug != MagickFalse)
830 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
831 assert(exception != (ExceptionInfo *) NULL);
832 assert(exception->signature == MagickSignature);
833 blur_image=CloneImage(image,0,0,MagickTrue,exception);
834 if (blur_image == (Image *) NULL)
835 return((Image *) NULL);
836 if (fabs(sigma) <= MagickEpsilon)
837 return(blur_image);
838 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
839 {
840 InheritException(exception,&blur_image->exception);
841 blur_image=DestroyImage(blur_image);
842 return((Image *) NULL);
843 }
844 width=GetOptimalKernelWidth1D(radius,sigma);
845 kernel=GetBlurKernel(width,sigma);
846 if (kernel == (double *) NULL)
847 {
848 blur_image=DestroyImage(blur_image);
849 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
850 }
851 if (image->debug != MagickFalse)
852 {
853 char
854 format[MaxTextExtent],
855 *message;
856
857 register const double
858 *k;
859
860 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000861 " BlurImage with %.20g kernel:",(double) width);
cristy3ed852e2009-09-05 21:47:34 +0000862 message=AcquireString("");
863 k=kernel;
cristybb503372010-05-27 20:51:26 +0000864 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000865 {
866 *message='\0';
cristyb51dff52011-05-19 16:55:47 +0000867 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) i);
cristy3ed852e2009-09-05 21:47:34 +0000868 (void) ConcatenateString(&message,format);
cristyb51dff52011-05-19 16:55:47 +0000869 (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
cristy3ed852e2009-09-05 21:47:34 +0000870 (void) ConcatenateString(&message,format);
871 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
872 }
873 message=DestroyString(message);
874 }
875 /*
876 Blur rows.
877 */
878 status=MagickTrue;
879 progress=0;
cristy4c08aed2011-07-01 19:47:50 +0000880 GetPixelInfo(image,&bias);
881 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +0000882 image_view=AcquireCacheView(image);
883 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +0000884#if defined(MAGICKCORE_OPENMP_SUPPORT)
885 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000886#endif
cristybb503372010-05-27 20:51:26 +0000887 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000888 {
cristy4c08aed2011-07-01 19:47:50 +0000889 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000890 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000891
cristy4c08aed2011-07-01 19:47:50 +0000892 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000893 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000894
cristy117ff172010-08-15 21:35:32 +0000895 register ssize_t
896 x;
897
cristy3ed852e2009-09-05 21:47:34 +0000898 if (status == MagickFalse)
899 continue;
cristy117ff172010-08-15 21:35:32 +0000900 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y,
901 image->columns+width,1,exception);
cristy3ed852e2009-09-05 21:47:34 +0000902 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
903 exception);
cristy4c08aed2011-07-01 19:47:50 +0000904 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000905 {
906 status=MagickFalse;
907 continue;
908 }
cristybb503372010-05-27 20:51:26 +0000909 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000910 {
cristy4c08aed2011-07-01 19:47:50 +0000911 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000912 pixel;
913
914 register const double
cristyc47d1f82009-11-26 01:44:43 +0000915 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +0000916
cristy4c08aed2011-07-01 19:47:50 +0000917 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000918 *restrict kernel_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000919
cristybb503372010-05-27 20:51:26 +0000920 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000921 i;
922
cristyddd82202009-11-03 20:14:50 +0000923 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +0000924 k=kernel;
925 kernel_pixels=p;
cristyed231572011-07-14 02:18:59 +0000926 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) ||
cristyf4ad9df2011-07-08 16:49:03 +0000927 (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +0000928 {
cristybb503372010-05-27 20:51:26 +0000929 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000930 {
cristy4c08aed2011-07-01 19:47:50 +0000931 pixel.red+=(*k)*GetPixelRed(image,kernel_pixels);
932 pixel.green+=(*k)*GetPixelGreen(image,kernel_pixels);
933 pixel.blue+=(*k)*GetPixelBlue(image,kernel_pixels);
934 if (image->colorspace == CMYKColorspace)
935 pixel.black+=(*k)*GetPixelBlack(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000936 k++;
cristyed231572011-07-14 02:18:59 +0000937 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000938 }
cristyed231572011-07-14 02:18:59 +0000939 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000940 SetPixelRed(blur_image,ClampToQuantum(pixel.red),q);
cristyed231572011-07-14 02:18:59 +0000941 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000942 SetPixelGreen(blur_image,ClampToQuantum(pixel.green),q);
cristyed231572011-07-14 02:18:59 +0000943 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000944 SetPixelBlue(blur_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000945 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000946 (blur_image->colorspace == CMYKColorspace))
947 SetPixelBlack(blur_image,ClampToQuantum(pixel.black),q);
cristyed231572011-07-14 02:18:59 +0000948 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +0000949 {
950 k=kernel;
951 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +0000952 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000953 {
cristy4c08aed2011-07-01 19:47:50 +0000954 pixel.alpha+=(*k)*GetPixelAlpha(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000955 k++;
cristyed231572011-07-14 02:18:59 +0000956 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000957 }
cristy4c08aed2011-07-01 19:47:50 +0000958 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +0000959 }
960 }
961 else
962 {
963 MagickRealType
964 alpha,
965 gamma;
966
967 gamma=0.0;
cristybb503372010-05-27 20:51:26 +0000968 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000969 {
cristy4c08aed2011-07-01 19:47:50 +0000970 alpha=(MagickRealType) (QuantumScale*
971 GetPixelAlpha(image,kernel_pixels));
972 pixel.red+=(*k)*alpha*GetPixelRed(image,kernel_pixels);
973 pixel.green+=(*k)*alpha*GetPixelGreen(image,kernel_pixels);
974 pixel.blue+=(*k)*alpha*GetPixelBlue(image,kernel_pixels);
975 if (image->colorspace == CMYKColorspace)
976 pixel.black+=(*k)*alpha*GetPixelBlack(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000977 gamma+=(*k)*alpha;
978 k++;
cristyed231572011-07-14 02:18:59 +0000979 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000980 }
981 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +0000982 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000983 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +0000984 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000985 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +0000986 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000987 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +0000988 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000989 (blur_image->colorspace == CMYKColorspace))
990 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristyed231572011-07-14 02:18:59 +0000991 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +0000992 {
993 k=kernel;
994 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +0000995 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +0000996 {
cristy4c08aed2011-07-01 19:47:50 +0000997 pixel.alpha+=(*k)*GetPixelAlpha(image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000998 k++;
cristyed231572011-07-14 02:18:59 +0000999 kernel_pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001000 }
cristy4c08aed2011-07-01 19:47:50 +00001001 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00001002 }
1003 }
cristyed231572011-07-14 02:18:59 +00001004 p+=GetPixelChannels(image);
1005 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001006 }
1007 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1008 status=MagickFalse;
1009 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1010 {
1011 MagickBooleanType
1012 proceed;
1013
cristyb5d5f722009-11-04 03:03:49 +00001014#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001015 #pragma omp critical (MagickCore_BlurImage)
cristy3ed852e2009-09-05 21:47:34 +00001016#endif
1017 proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1018 blur_image->columns);
1019 if (proceed == MagickFalse)
1020 status=MagickFalse;
1021 }
1022 }
1023 blur_view=DestroyCacheView(blur_view);
1024 image_view=DestroyCacheView(image_view);
1025 /*
1026 Blur columns.
1027 */
1028 image_view=AcquireCacheView(blur_image);
1029 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00001030#if defined(MAGICKCORE_OPENMP_SUPPORT)
1031 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001032#endif
cristybb503372010-05-27 20:51:26 +00001033 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001034 {
cristy4c08aed2011-07-01 19:47:50 +00001035 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001036 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001037
cristy4c08aed2011-07-01 19:47:50 +00001038 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001039 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001040
cristy117ff172010-08-15 21:35:32 +00001041 register ssize_t
1042 y;
1043
cristy3ed852e2009-09-05 21:47:34 +00001044 if (status == MagickFalse)
1045 continue;
cristy117ff172010-08-15 21:35:32 +00001046 p=GetCacheViewVirtualPixels(image_view,x,-((ssize_t) width/2L),1,
1047 image->rows+width,exception);
cristy3ed852e2009-09-05 21:47:34 +00001048 q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
cristy4c08aed2011-07-01 19:47:50 +00001049 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001050 {
1051 status=MagickFalse;
1052 continue;
1053 }
cristybb503372010-05-27 20:51:26 +00001054 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001055 {
cristy4c08aed2011-07-01 19:47:50 +00001056 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001057 pixel;
1058
1059 register const double
cristyc47d1f82009-11-26 01:44:43 +00001060 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00001061
cristy4c08aed2011-07-01 19:47:50 +00001062 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001063 *restrict kernel_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001064
cristybb503372010-05-27 20:51:26 +00001065 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001066 i;
1067
cristyddd82202009-11-03 20:14:50 +00001068 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +00001069 k=kernel;
1070 kernel_pixels=p;
cristyed231572011-07-14 02:18:59 +00001071 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) ||
cristyf4ad9df2011-07-08 16:49:03 +00001072 (blur_image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001073 {
cristybb503372010-05-27 20:51:26 +00001074 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001075 {
cristy4c08aed2011-07-01 19:47:50 +00001076 pixel.red+=(*k)*GetPixelRed(blur_image,kernel_pixels);
1077 pixel.green+=(*k)*GetPixelGreen(blur_image,kernel_pixels);
1078 pixel.blue+=(*k)*GetPixelBlue(blur_image,kernel_pixels);
1079 if (blur_image->colorspace == CMYKColorspace)
1080 pixel.black+=(*k)*GetPixelBlack(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001081 k++;
cristyed231572011-07-14 02:18:59 +00001082 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001083 }
cristyed231572011-07-14 02:18:59 +00001084 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001085 SetPixelRed(blur_image,ClampToQuantum(pixel.red),q);
cristyed231572011-07-14 02:18:59 +00001086 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001087 SetPixelGreen(blur_image,ClampToQuantum(pixel.green),q);
cristyed231572011-07-14 02:18:59 +00001088 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001089 SetPixelBlue(blur_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00001090 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001091 (blur_image->colorspace == CMYKColorspace))
1092 SetPixelBlack(blur_image,ClampToQuantum(pixel.black),q);
cristyed231572011-07-14 02:18:59 +00001093 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001094 {
1095 k=kernel;
1096 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +00001097 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001098 {
cristy4c08aed2011-07-01 19:47:50 +00001099 pixel.alpha+=(*k)*GetPixelAlpha(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001100 k++;
cristyed231572011-07-14 02:18:59 +00001101 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001102 }
cristy4c08aed2011-07-01 19:47:50 +00001103 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00001104 }
1105 }
1106 else
1107 {
1108 MagickRealType
1109 alpha,
1110 gamma;
1111
1112 gamma=0.0;
cristybb503372010-05-27 20:51:26 +00001113 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001114 {
cristy46f08202010-01-10 04:04:21 +00001115 alpha=(MagickRealType) (QuantumScale*
cristy4c08aed2011-07-01 19:47:50 +00001116 GetPixelAlpha(blur_image,kernel_pixels));
1117 pixel.red+=(*k)*alpha*GetPixelRed(blur_image,kernel_pixels);
1118 pixel.green+=(*k)*alpha*GetPixelGreen(blur_image,kernel_pixels);
1119 pixel.blue+=(*k)*alpha*GetPixelBlue(blur_image,kernel_pixels);
1120 if (blur_image->colorspace == CMYKColorspace)
1121 pixel.black+=(*k)*alpha*GetPixelBlack(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001122 gamma+=(*k)*alpha;
1123 k++;
cristyed231572011-07-14 02:18:59 +00001124 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001125 }
1126 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00001127 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001128 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00001129 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001130 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00001131 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001132 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00001133 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001134 (blur_image->colorspace == CMYKColorspace))
1135 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristyed231572011-07-14 02:18:59 +00001136 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001137 {
1138 k=kernel;
1139 kernel_pixels=p;
cristybb503372010-05-27 20:51:26 +00001140 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001141 {
cristy4c08aed2011-07-01 19:47:50 +00001142 pixel.alpha+=(*k)*GetPixelAlpha(blur_image,kernel_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001143 k++;
cristyed231572011-07-14 02:18:59 +00001144 kernel_pixels+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001145 }
cristy4c08aed2011-07-01 19:47:50 +00001146 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00001147 }
1148 }
cristyed231572011-07-14 02:18:59 +00001149 p+=GetPixelChannels(blur_image);
1150 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001151 }
1152 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1153 status=MagickFalse;
cristy4c08aed2011-07-01 19:47:50 +00001154 if (blur_image->progress_monitor != (MagickProgressMonitor) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001155 {
1156 MagickBooleanType
1157 proceed;
1158
cristyb5d5f722009-11-04 03:03:49 +00001159#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001160 #pragma omp critical (MagickCore_BlurImage)
cristy3ed852e2009-09-05 21:47:34 +00001161#endif
cristy4c08aed2011-07-01 19:47:50 +00001162 proceed=SetImageProgress(blur_image,BlurImageTag,progress++,
1163 blur_image->rows+blur_image->columns);
cristy3ed852e2009-09-05 21:47:34 +00001164 if (proceed == MagickFalse)
1165 status=MagickFalse;
1166 }
1167 }
1168 blur_view=DestroyCacheView(blur_view);
1169 image_view=DestroyCacheView(image_view);
1170 kernel=(double *) RelinquishMagickMemory(kernel);
1171 if (status == MagickFalse)
1172 blur_image=DestroyImage(blur_image);
1173 blur_image->type=image->type;
1174 return(blur_image);
1175}
1176
1177/*
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179% %
1180% %
1181% %
cristyfccdab92009-11-30 16:43:57 +00001182% C o n v o l v e I m a g e %
1183% %
1184% %
1185% %
1186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1187%
1188% ConvolveImage() applies a custom convolution kernel to the image.
1189%
1190% The format of the ConvolveImage method is:
1191%
cristy5e6be1e2011-07-16 01:23:39 +00001192% Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
1193% ExceptionInfo *exception)
1194%
cristyfccdab92009-11-30 16:43:57 +00001195% A description of each parameter follows:
1196%
1197% o image: the image.
1198%
cristy5e6be1e2011-07-16 01:23:39 +00001199% o kernel: the filtering kernel.
cristyfccdab92009-11-30 16:43:57 +00001200%
1201% o exception: return any errors or warnings in this structure.
1202%
1203*/
cristy5e6be1e2011-07-16 01:23:39 +00001204MagickExport Image *ConvolveImage(const Image *image,
1205 const KernelInfo *kernel_info,ExceptionInfo *exception)
cristyfccdab92009-11-30 16:43:57 +00001206{
cristyfccdab92009-11-30 16:43:57 +00001207#define ConvolveImageTag "Convolve/Image"
1208
cristyc4c8d132010-01-07 01:58:38 +00001209 CacheView
1210 *convolve_view,
cristy105ba3c2011-07-18 02:28:38 +00001211 *image_view;
cristyc4c8d132010-01-07 01:58:38 +00001212
cristyfccdab92009-11-30 16:43:57 +00001213 Image
1214 *convolve_image;
1215
cristyfccdab92009-11-30 16:43:57 +00001216 MagickBooleanType
1217 status;
1218
cristybb503372010-05-27 20:51:26 +00001219 MagickOffsetType
1220 progress;
1221
cristybb503372010-05-27 20:51:26 +00001222 ssize_t
1223 y;
1224
cristyfccdab92009-11-30 16:43:57 +00001225 /*
1226 Initialize convolve image attributes.
1227 */
1228 assert(image != (Image *) NULL);
1229 assert(image->signature == MagickSignature);
1230 if (image->debug != MagickFalse)
1231 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1232 assert(exception != (ExceptionInfo *) NULL);
1233 assert(exception->signature == MagickSignature);
cristy5e6be1e2011-07-16 01:23:39 +00001234 if ((kernel_info->width % 2) == 0)
cristyfccdab92009-11-30 16:43:57 +00001235 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
cristy08429172011-07-14 17:18:16 +00001236 convolve_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1237 exception);
cristyfccdab92009-11-30 16:43:57 +00001238 if (convolve_image == (Image *) NULL)
1239 return((Image *) NULL);
1240 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
1241 {
1242 InheritException(exception,&convolve_image->exception);
1243 convolve_image=DestroyImage(convolve_image);
1244 return((Image *) NULL);
1245 }
1246 if (image->debug != MagickFalse)
1247 {
1248 char
1249 format[MaxTextExtent],
1250 *message;
1251
cristy117ff172010-08-15 21:35:32 +00001252 register const double
1253 *k;
1254
cristy4e154852011-07-14 13:28:53 +00001255 register ssize_t
1256 u;
1257
cristybb503372010-05-27 20:51:26 +00001258 ssize_t
cristyfccdab92009-11-30 16:43:57 +00001259 v;
1260
cristyfccdab92009-11-30 16:43:57 +00001261 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristy5e6be1e2011-07-16 01:23:39 +00001262 " ConvolveImage with %.20gx%.20g kernel:",(double) kernel_info->width,
1263 (double) kernel_info->height);
cristyfccdab92009-11-30 16:43:57 +00001264 message=AcquireString("");
cristy5e6be1e2011-07-16 01:23:39 +00001265 k=kernel_info->values;
1266 for (v=0; v < (ssize_t) kernel_info->width; v++)
cristyfccdab92009-11-30 16:43:57 +00001267 {
1268 *message='\0';
cristyb51dff52011-05-19 16:55:47 +00001269 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristyfccdab92009-11-30 16:43:57 +00001270 (void) ConcatenateString(&message,format);
cristy5e6be1e2011-07-16 01:23:39 +00001271 for (u=0; u < (ssize_t) kernel_info->height; u++)
cristyfccdab92009-11-30 16:43:57 +00001272 {
cristyb51dff52011-05-19 16:55:47 +00001273 (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
cristyfccdab92009-11-30 16:43:57 +00001274 (void) ConcatenateString(&message,format);
1275 }
1276 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1277 }
1278 message=DestroyString(message);
1279 }
1280 /*
cristyfccdab92009-11-30 16:43:57 +00001281 Convolve image.
1282 */
1283 status=MagickTrue;
1284 progress=0;
cristyfccdab92009-11-30 16:43:57 +00001285 image_view=AcquireCacheView(image);
1286 convolve_view=AcquireCacheView(convolve_image);
1287#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy175653e2011-07-10 23:13:34 +00001288 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristyfccdab92009-11-30 16:43:57 +00001289#endif
cristybb503372010-05-27 20:51:26 +00001290 for (y=0; y < (ssize_t) image->rows; y++)
cristyfccdab92009-11-30 16:43:57 +00001291 {
cristy4c08aed2011-07-01 19:47:50 +00001292 register const Quantum
cristy105ba3c2011-07-18 02:28:38 +00001293 *restrict p;
cristyfccdab92009-11-30 16:43:57 +00001294
cristy4c08aed2011-07-01 19:47:50 +00001295 register Quantum
cristyfccdab92009-11-30 16:43:57 +00001296 *restrict q;
1297
cristy117ff172010-08-15 21:35:32 +00001298 register ssize_t
1299 x;
1300
cristy5af97fe2011-07-18 02:33:29 +00001301 ssize_t
1302 center;
1303
cristyfccdab92009-11-30 16:43:57 +00001304 if (status == MagickFalse)
1305 continue;
cristy105ba3c2011-07-18 02:28:38 +00001306 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
1307 (ssize_t) (kernel_info->height/2L),image->columns+kernel_info->width,
1308 kernel_info->height,exception);
cristy08429172011-07-14 17:18:16 +00001309 q=QueueCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
cristyfccdab92009-11-30 16:43:57 +00001310 exception);
cristy105ba3c2011-07-18 02:28:38 +00001311 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristyfccdab92009-11-30 16:43:57 +00001312 {
1313 status=MagickFalse;
1314 continue;
1315 }
cristy79cedc72011-07-25 00:41:15 +00001316 center=(ssize_t) GetPixelChannels(image)*(image->columns+
1317 kernel_info->width)*(kernel_info->height/2L)+GetPixelChannels(image)*
1318 (kernel_info->width/2);
cristybb503372010-05-27 20:51:26 +00001319 for (x=0; x < (ssize_t) image->columns; x++)
cristyfccdab92009-11-30 16:43:57 +00001320 {
cristybb503372010-05-27 20:51:26 +00001321 register ssize_t
cristyed231572011-07-14 02:18:59 +00001322 i;
cristyfccdab92009-11-30 16:43:57 +00001323
cristya30d9ba2011-07-23 21:00:48 +00001324 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristyed231572011-07-14 02:18:59 +00001325 {
cristyed231572011-07-14 02:18:59 +00001326 MagickRealType
cristy4e154852011-07-14 13:28:53 +00001327 alpha,
1328 gamma,
cristyed231572011-07-14 02:18:59 +00001329 pixel;
1330
1331 PixelChannel
1332 channel;
1333
1334 PixelTrait
1335 convolve_traits,
1336 traits;
1337
1338 register const double
1339 *restrict k;
1340
1341 register const Quantum
cristyeb52cde2011-07-17 01:52:52 +00001342 *restrict pixels;
cristyed231572011-07-14 02:18:59 +00001343
1344 register ssize_t
1345 u;
1346
1347 ssize_t
1348 v;
1349
cristy30301712011-07-18 15:06:51 +00001350 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
cristy4e154852011-07-14 13:28:53 +00001351 if (traits == UndefinedPixelTrait)
cristyed231572011-07-14 02:18:59 +00001352 continue;
cristy30301712011-07-18 15:06:51 +00001353 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
cristy4e154852011-07-14 13:28:53 +00001354 convolve_traits=GetPixelChannelMapTraits(convolve_image,channel);
1355 if (convolve_traits == UndefinedPixelTrait)
1356 continue;
1357 if ((convolve_traits & CopyPixelTrait) != 0)
1358 {
cristyf7dc44c2011-07-20 14:41:15 +00001359 q[channel]=p[center+i];
cristy4e154852011-07-14 13:28:53 +00001360 continue;
1361 }
cristy5e6be1e2011-07-16 01:23:39 +00001362 k=kernel_info->values;
cristy105ba3c2011-07-18 02:28:38 +00001363 pixels=p;
cristy0a922382011-07-16 15:30:34 +00001364 pixel=kernel_info->bias;
cristy222b19c2011-08-04 01:35:11 +00001365 if ((convolve_traits & BlendPixelTrait) == 0)
cristyfccdab92009-11-30 16:43:57 +00001366 {
cristyed231572011-07-14 02:18:59 +00001367 /*
cristy4e154852011-07-14 13:28:53 +00001368 No alpha blending.
cristyed231572011-07-14 02:18:59 +00001369 */
cristyeb52cde2011-07-17 01:52:52 +00001370 for (v=0; v < (ssize_t) kernel_info->height; v++)
cristyfccdab92009-11-30 16:43:57 +00001371 {
cristyeb52cde2011-07-17 01:52:52 +00001372 for (u=0; u < (ssize_t) kernel_info->width; u++)
cristy175653e2011-07-10 23:13:34 +00001373 {
cristyeb52cde2011-07-17 01:52:52 +00001374 pixel+=(*k)*pixels[i];
cristyed231572011-07-14 02:18:59 +00001375 k++;
cristya30d9ba2011-07-23 21:00:48 +00001376 pixels+=GetPixelChannels(image);
cristy175653e2011-07-10 23:13:34 +00001377 }
cristya30d9ba2011-07-23 21:00:48 +00001378 pixels+=image->columns*GetPixelChannels(image);
cristy175653e2011-07-10 23:13:34 +00001379 }
cristyf7dc44c2011-07-20 14:41:15 +00001380 q[channel]=ClampToQuantum(pixel);
cristy4e154852011-07-14 13:28:53 +00001381 continue;
cristyed231572011-07-14 02:18:59 +00001382 }
cristy4e154852011-07-14 13:28:53 +00001383 /*
1384 Alpha blending.
1385 */
1386 gamma=0.0;
cristyeb52cde2011-07-17 01:52:52 +00001387 for (v=0; v < (ssize_t) kernel_info->height; v++)
cristy4e154852011-07-14 13:28:53 +00001388 {
cristyeb52cde2011-07-17 01:52:52 +00001389 for (u=0; u < (ssize_t) kernel_info->width; u++)
cristy4e154852011-07-14 13:28:53 +00001390 {
cristyeb52cde2011-07-17 01:52:52 +00001391 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1392 pixel+=(*k)*alpha*pixels[i];
cristy4e154852011-07-14 13:28:53 +00001393 gamma+=(*k)*alpha;
1394 k++;
cristya30d9ba2011-07-23 21:00:48 +00001395 pixels+=GetPixelChannels(image);
cristy4e154852011-07-14 13:28:53 +00001396 }
cristya30d9ba2011-07-23 21:00:48 +00001397 pixels+=image->columns*GetPixelChannels(image);
cristy4e154852011-07-14 13:28:53 +00001398 }
cristy1ce96d02011-07-14 17:57:24 +00001399 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristye7a41c92011-07-20 21:31:01 +00001400 q[channel]=ClampToQuantum(gamma*pixel);
cristyed231572011-07-14 02:18:59 +00001401 }
cristya30d9ba2011-07-23 21:00:48 +00001402 p+=GetPixelChannels(image);
1403 q+=GetPixelChannels(convolve_image);
cristyfccdab92009-11-30 16:43:57 +00001404 }
cristyed231572011-07-14 02:18:59 +00001405 if (SyncCacheViewAuthenticPixels(convolve_view,exception) == MagickFalse)
cristyfccdab92009-11-30 16:43:57 +00001406 status=MagickFalse;
1407 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1408 {
1409 MagickBooleanType
1410 proceed;
1411
1412#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001413 #pragma omp critical (MagickCore_ConvolveImage)
cristyfccdab92009-11-30 16:43:57 +00001414#endif
1415 proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1416 if (proceed == MagickFalse)
1417 status=MagickFalse;
1418 }
1419 }
1420 convolve_image->type=image->type;
1421 convolve_view=DestroyCacheView(convolve_view);
1422 image_view=DestroyCacheView(image_view);
cristyfccdab92009-11-30 16:43:57 +00001423 if (status == MagickFalse)
1424 convolve_image=DestroyImage(convolve_image);
1425 return(convolve_image);
1426}
1427
1428/*
1429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430% %
1431% %
1432% %
cristy3ed852e2009-09-05 21:47:34 +00001433% D e s p e c k l e I m a g e %
1434% %
1435% %
1436% %
1437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438%
1439% DespeckleImage() reduces the speckle noise in an image while perserving the
1440% edges of the original image.
1441%
1442% The format of the DespeckleImage method is:
1443%
1444% Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1445%
1446% A description of each parameter follows:
1447%
1448% o image: the image.
1449%
1450% o exception: return any errors or warnings in this structure.
1451%
1452*/
1453
cristybb503372010-05-27 20:51:26 +00001454static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1455 const size_t columns,const size_t rows,Quantum *f,Quantum *g,
cristy3ed852e2009-09-05 21:47:34 +00001456 const int polarity)
1457{
cristy3ed852e2009-09-05 21:47:34 +00001458 MagickRealType
1459 v;
1460
cristy3ed852e2009-09-05 21:47:34 +00001461 register Quantum
1462 *p,
1463 *q,
1464 *r,
1465 *s;
1466
cristy117ff172010-08-15 21:35:32 +00001467 register ssize_t
1468 x;
1469
1470 ssize_t
1471 y;
1472
cristy3ed852e2009-09-05 21:47:34 +00001473 assert(f != (Quantum *) NULL);
1474 assert(g != (Quantum *) NULL);
1475 p=f+(columns+2);
1476 q=g+(columns+2);
cristybb503372010-05-27 20:51:26 +00001477 r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1478 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001479 {
1480 p++;
1481 q++;
1482 r++;
1483 if (polarity > 0)
cristybb503372010-05-27 20:51:26 +00001484 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001485 {
1486 v=(MagickRealType) (*p);
1487 if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1488 v+=ScaleCharToQuantum(1);
1489 *q=(Quantum) v;
1490 p++;
1491 q++;
1492 r++;
1493 }
1494 else
cristybb503372010-05-27 20:51:26 +00001495 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001496 {
1497 v=(MagickRealType) (*p);
1498 if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
cristybb503372010-05-27 20:51:26 +00001499 v-=(ssize_t) ScaleCharToQuantum(1);
cristy3ed852e2009-09-05 21:47:34 +00001500 *q=(Quantum) v;
1501 p++;
1502 q++;
1503 r++;
1504 }
1505 p++;
1506 q++;
1507 r++;
1508 }
1509 p=f+(columns+2);
1510 q=g+(columns+2);
cristybb503372010-05-27 20:51:26 +00001511 r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1512 s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1513 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001514 {
1515 p++;
1516 q++;
1517 r++;
1518 s++;
1519 if (polarity > 0)
cristybb503372010-05-27 20:51:26 +00001520 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001521 {
1522 v=(MagickRealType) (*q);
1523 if (((MagickRealType) *s >=
1524 (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1525 ((MagickRealType) *r > v))
1526 v+=ScaleCharToQuantum(1);
1527 *p=(Quantum) v;
1528 p++;
1529 q++;
1530 r++;
1531 s++;
1532 }
1533 else
cristybb503372010-05-27 20:51:26 +00001534 for (x=(ssize_t) columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001535 {
1536 v=(MagickRealType) (*q);
1537 if (((MagickRealType) *s <=
1538 (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1539 ((MagickRealType) *r < v))
1540 v-=(MagickRealType) ScaleCharToQuantum(1);
1541 *p=(Quantum) v;
1542 p++;
1543 q++;
1544 r++;
1545 s++;
1546 }
1547 p++;
1548 q++;
1549 r++;
1550 s++;
1551 }
1552}
1553
1554MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1555{
1556#define DespeckleImageTag "Despeckle/Image"
1557
cristy2407fc22009-09-11 00:55:25 +00001558 CacheView
1559 *despeckle_view,
1560 *image_view;
1561
cristy3ed852e2009-09-05 21:47:34 +00001562 Image
1563 *despeckle_image;
1564
cristy3ed852e2009-09-05 21:47:34 +00001565 MagickBooleanType
1566 status;
1567
cristya58c3172011-02-19 19:23:11 +00001568 register ssize_t
1569 i;
1570
cristy3ed852e2009-09-05 21:47:34 +00001571 Quantum
cristy65b9f392011-02-22 14:22:54 +00001572 *restrict buffers,
1573 *restrict pixels;
cristy3ed852e2009-09-05 21:47:34 +00001574
1575 size_t
cristya58c3172011-02-19 19:23:11 +00001576 length,
1577 number_channels;
cristy117ff172010-08-15 21:35:32 +00001578
cristybb503372010-05-27 20:51:26 +00001579 static const ssize_t
cristy691a29e2009-09-11 00:44:10 +00001580 X[4] = {0, 1, 1,-1},
1581 Y[4] = {1, 0, 1, 1};
cristy3ed852e2009-09-05 21:47:34 +00001582
cristy3ed852e2009-09-05 21:47:34 +00001583 /*
1584 Allocate despeckled image.
1585 */
1586 assert(image != (const Image *) NULL);
1587 assert(image->signature == MagickSignature);
1588 if (image->debug != MagickFalse)
1589 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1590 assert(exception != (ExceptionInfo *) NULL);
1591 assert(exception->signature == MagickSignature);
1592 despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1593 exception);
1594 if (despeckle_image == (Image *) NULL)
1595 return((Image *) NULL);
1596 if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
1597 {
1598 InheritException(exception,&despeckle_image->exception);
1599 despeckle_image=DestroyImage(despeckle_image);
1600 return((Image *) NULL);
1601 }
1602 /*
1603 Allocate image buffers.
1604 */
1605 length=(size_t) ((image->columns+2)*(image->rows+2));
cristy65b9f392011-02-22 14:22:54 +00001606 pixels=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1607 buffers=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1608 if ((pixels == (Quantum *) NULL) || (buffers == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001609 {
cristy65b9f392011-02-22 14:22:54 +00001610 if (buffers != (Quantum *) NULL)
1611 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1612 if (pixels != (Quantum *) NULL)
1613 pixels=(Quantum *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001614 despeckle_image=DestroyImage(despeckle_image);
1615 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1616 }
1617 /*
1618 Reduce speckle in the image.
1619 */
1620 status=MagickTrue;
cristy109695a2011-02-19 19:38:14 +00001621 number_channels=(size_t) (image->colorspace == CMYKColorspace ? 5 : 4);
cristy3ed852e2009-09-05 21:47:34 +00001622 image_view=AcquireCacheView(image);
1623 despeckle_view=AcquireCacheView(despeckle_image);
cristy8df3d002011-02-19 19:40:59 +00001624 for (i=0; i < (ssize_t) number_channels; i++)
cristy3ed852e2009-09-05 21:47:34 +00001625 {
cristy3ed852e2009-09-05 21:47:34 +00001626 register Quantum
1627 *buffer,
1628 *pixel;
1629
cristyc1488b52011-02-19 18:54:15 +00001630 register ssize_t
cristya58c3172011-02-19 19:23:11 +00001631 k,
cristyc1488b52011-02-19 18:54:15 +00001632 x;
1633
cristy117ff172010-08-15 21:35:32 +00001634 ssize_t
1635 j,
1636 y;
1637
cristy3ed852e2009-09-05 21:47:34 +00001638 if (status == MagickFalse)
1639 continue;
cristy65b9f392011-02-22 14:22:54 +00001640 pixel=pixels;
cristy3ed852e2009-09-05 21:47:34 +00001641 (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
cristy65b9f392011-02-22 14:22:54 +00001642 buffer=buffers;
cristybb503372010-05-27 20:51:26 +00001643 j=(ssize_t) image->columns+2;
1644 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001645 {
cristy4c08aed2011-07-01 19:47:50 +00001646 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001647 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001648
1649 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001650 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001651 break;
1652 j++;
cristybb503372010-05-27 20:51:26 +00001653 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001654 {
cristya58c3172011-02-19 19:23:11 +00001655 switch (i)
cristy3ed852e2009-09-05 21:47:34 +00001656 {
cristy4c08aed2011-07-01 19:47:50 +00001657 case 0: pixel[j]=GetPixelRed(image,p); break;
1658 case 1: pixel[j]=GetPixelGreen(image,p); break;
1659 case 2: pixel[j]=GetPixelBlue(image,p); break;
1660 case 3: pixel[j]=GetPixelAlpha(image,p); break;
1661 case 4: pixel[j]=GetPixelBlack(image,p); break;
cristy3ed852e2009-09-05 21:47:34 +00001662 default: break;
1663 }
cristyed231572011-07-14 02:18:59 +00001664 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001665 j++;
1666 }
1667 j++;
1668 }
cristy3ed852e2009-09-05 21:47:34 +00001669 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
cristya58c3172011-02-19 19:23:11 +00001670 for (k=0; k < 4; k++)
cristy3ed852e2009-09-05 21:47:34 +00001671 {
cristya58c3172011-02-19 19:23:11 +00001672 Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,1);
1673 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);
cristy3ed852e2009-09-05 21:47:34 +00001676 }
cristybb503372010-05-27 20:51:26 +00001677 j=(ssize_t) image->columns+2;
1678 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001679 {
1680 MagickBooleanType
1681 sync;
1682
cristy4c08aed2011-07-01 19:47:50 +00001683 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001684 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001685
1686 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1687 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001688 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001689 break;
1690 j++;
cristybb503372010-05-27 20:51:26 +00001691 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001692 {
cristya58c3172011-02-19 19:23:11 +00001693 switch (i)
cristy3ed852e2009-09-05 21:47:34 +00001694 {
cristy4c08aed2011-07-01 19:47:50 +00001695 case 0: SetPixelRed(despeckle_image,pixel[j],q); break;
1696 case 1: SetPixelGreen(despeckle_image,pixel[j],q); break;
1697 case 2: SetPixelBlue(despeckle_image,pixel[j],q); break;
1698 case 3: SetPixelAlpha(despeckle_image,pixel[j],q); break;
1699 case 4: SetPixelBlack(despeckle_image,pixel[j],q); break;
cristy3ed852e2009-09-05 21:47:34 +00001700 default: break;
1701 }
cristyed231572011-07-14 02:18:59 +00001702 q+=GetPixelChannels(despeckle_image);
cristy3ed852e2009-09-05 21:47:34 +00001703 j++;
1704 }
1705 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1706 if (sync == MagickFalse)
1707 {
1708 status=MagickFalse;
1709 break;
1710 }
1711 j++;
1712 }
1713 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1714 {
1715 MagickBooleanType
1716 proceed;
1717
cristya58c3172011-02-19 19:23:11 +00001718 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1719 number_channels);
cristy3ed852e2009-09-05 21:47:34 +00001720 if (proceed == MagickFalse)
1721 status=MagickFalse;
1722 }
1723 }
1724 despeckle_view=DestroyCacheView(despeckle_view);
1725 image_view=DestroyCacheView(image_view);
cristy65b9f392011-02-22 14:22:54 +00001726 buffers=(Quantum *) RelinquishMagickMemory(buffers);
1727 pixels=(Quantum *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00001728 despeckle_image->type=image->type;
1729 if (status == MagickFalse)
1730 despeckle_image=DestroyImage(despeckle_image);
1731 return(despeckle_image);
1732}
1733
1734/*
1735%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1736% %
1737% %
1738% %
1739% E d g e I m a g e %
1740% %
1741% %
1742% %
1743%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1744%
1745% EdgeImage() finds edges in an image. Radius defines the radius of the
1746% convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1747% radius for you.
1748%
1749% The format of the EdgeImage method is:
1750%
1751% Image *EdgeImage(const Image *image,const double radius,
1752% ExceptionInfo *exception)
1753%
1754% A description of each parameter follows:
1755%
1756% o image: the image.
1757%
1758% o radius: the radius of the pixel neighborhood.
1759%
1760% o exception: return any errors or warnings in this structure.
1761%
1762*/
1763MagickExport Image *EdgeImage(const Image *image,const double radius,
1764 ExceptionInfo *exception)
1765{
1766 Image
1767 *edge_image;
1768
cristy41cbe682011-07-15 19:12:37 +00001769 KernelInfo
1770 *kernel_info;
cristy3ed852e2009-09-05 21:47:34 +00001771
cristybb503372010-05-27 20:51:26 +00001772 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001773 i;
1774
cristybb503372010-05-27 20:51:26 +00001775 size_t
cristy3ed852e2009-09-05 21:47:34 +00001776 width;
1777
cristy41cbe682011-07-15 19:12:37 +00001778 ssize_t
1779 j,
1780 u,
1781 v;
1782
cristy3ed852e2009-09-05 21:47:34 +00001783 assert(image != (const Image *) NULL);
1784 assert(image->signature == MagickSignature);
1785 if (image->debug != MagickFalse)
1786 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1787 assert(exception != (ExceptionInfo *) NULL);
1788 assert(exception->signature == MagickSignature);
1789 width=GetOptimalKernelWidth1D(radius,0.5);
cristy5e6be1e2011-07-16 01:23:39 +00001790 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001791 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001792 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001793 kernel_info->width=width;
1794 kernel_info->height=width;
cristy41cbe682011-07-15 19:12:37 +00001795 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1796 kernel_info->width*sizeof(*kernel_info->values));
1797 if (kernel_info->values == (double *) NULL)
1798 {
1799 kernel_info=DestroyKernelInfo(kernel_info);
1800 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1801 }
1802 j=(ssize_t) kernel_info->width/2;
1803 i=0;
1804 for (v=(-j); v <= j; v++)
1805 {
1806 for (u=(-j); u <= j; u++)
1807 {
1808 kernel_info->values[i]=(-1.0);
1809 i++;
1810 }
1811 }
1812 kernel_info->values[i/2]=(double) (width*width-1.0);
cristy0a922382011-07-16 15:30:34 +00001813 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00001814 edge_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001815 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001816 return(edge_image);
1817}
1818
1819/*
1820%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1821% %
1822% %
1823% %
1824% E m b o s s I m a g e %
1825% %
1826% %
1827% %
1828%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1829%
1830% EmbossImage() returns a grayscale image with a three-dimensional effect.
1831% We convolve the image with a Gaussian operator of the given radius and
1832% standard deviation (sigma). For reasonable results, radius should be
1833% larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1834% radius for you.
1835%
1836% The format of the EmbossImage method is:
1837%
1838% Image *EmbossImage(const Image *image,const double radius,
1839% const double sigma,ExceptionInfo *exception)
1840%
1841% A description of each parameter follows:
1842%
1843% o image: the image.
1844%
1845% o radius: the radius of the pixel neighborhood.
1846%
1847% o sigma: the standard deviation of the Gaussian, in pixels.
1848%
1849% o exception: return any errors or warnings in this structure.
1850%
1851*/
1852MagickExport Image *EmbossImage(const Image *image,const double radius,
1853 const double sigma,ExceptionInfo *exception)
1854{
cristy3ed852e2009-09-05 21:47:34 +00001855 Image
1856 *emboss_image;
1857
cristy41cbe682011-07-15 19:12:37 +00001858 KernelInfo
1859 *kernel_info;
1860
cristybb503372010-05-27 20:51:26 +00001861 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001862 i;
1863
cristybb503372010-05-27 20:51:26 +00001864 size_t
cristy3ed852e2009-09-05 21:47:34 +00001865 width;
1866
cristy117ff172010-08-15 21:35:32 +00001867 ssize_t
1868 j,
1869 k,
1870 u,
1871 v;
1872
cristy41cbe682011-07-15 19:12:37 +00001873 assert(image != (const Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001874 assert(image->signature == MagickSignature);
1875 if (image->debug != MagickFalse)
1876 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1877 assert(exception != (ExceptionInfo *) NULL);
1878 assert(exception->signature == MagickSignature);
1879 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001880 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001881 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001882 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001883 kernel_info->width=width;
1884 kernel_info->height=width;
cristy41cbe682011-07-15 19:12:37 +00001885 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1886 kernel_info->width*sizeof(*kernel_info->values));
1887 if (kernel_info->values == (double *) NULL)
1888 {
1889 kernel_info=DestroyKernelInfo(kernel_info);
1890 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1891 }
1892 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00001893 k=j;
1894 i=0;
1895 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00001896 {
cristy47e00502009-12-17 19:19:57 +00001897 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00001898 {
cristy41cbe682011-07-15 19:12:37 +00001899 kernel_info->values[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
cristy47e00502009-12-17 19:19:57 +00001900 exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
cristy4205a3c2010-09-12 20:19:59 +00001901 (2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +00001902 if (u != k)
cristy41cbe682011-07-15 19:12:37 +00001903 kernel_info->values[i]=0.0;
cristy3ed852e2009-09-05 21:47:34 +00001904 i++;
1905 }
cristy47e00502009-12-17 19:19:57 +00001906 k--;
cristy3ed852e2009-09-05 21:47:34 +00001907 }
cristy0a922382011-07-16 15:30:34 +00001908 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00001909 emboss_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00001910 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001911 if (emboss_image != (Image *) NULL)
1912 (void) EqualizeImage(emboss_image);
cristy3ed852e2009-09-05 21:47:34 +00001913 return(emboss_image);
1914}
1915
1916/*
1917%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1918% %
1919% %
1920% %
1921% G a u s s i a n B l u r I m a g e %
1922% %
1923% %
1924% %
1925%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1926%
1927% GaussianBlurImage() blurs an image. We convolve the image with a
1928% Gaussian operator of the given radius and standard deviation (sigma).
1929% For reasonable results, the radius should be larger than sigma. Use a
1930% radius of 0 and GaussianBlurImage() selects a suitable radius for you
1931%
1932% The format of the GaussianBlurImage method is:
1933%
1934% Image *GaussianBlurImage(const Image *image,onst double radius,
1935% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001936%
1937% A description of each parameter follows:
1938%
1939% o image: the image.
1940%
cristy3ed852e2009-09-05 21:47:34 +00001941% o radius: the radius of the Gaussian, in pixels, not counting the center
1942% pixel.
1943%
1944% o sigma: the standard deviation of the Gaussian, in pixels.
1945%
1946% o exception: return any errors or warnings in this structure.
1947%
1948*/
cristy41cbe682011-07-15 19:12:37 +00001949MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1950 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001951{
cristy3ed852e2009-09-05 21:47:34 +00001952 Image
1953 *blur_image;
1954
cristy41cbe682011-07-15 19:12:37 +00001955 KernelInfo
1956 *kernel_info;
1957
cristybb503372010-05-27 20:51:26 +00001958 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001959 i;
1960
cristybb503372010-05-27 20:51:26 +00001961 size_t
cristy3ed852e2009-09-05 21:47:34 +00001962 width;
1963
cristy117ff172010-08-15 21:35:32 +00001964 ssize_t
1965 j,
1966 u,
1967 v;
1968
cristy3ed852e2009-09-05 21:47:34 +00001969 assert(image != (const Image *) NULL);
1970 assert(image->signature == MagickSignature);
1971 if (image->debug != MagickFalse)
1972 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1973 assert(exception != (ExceptionInfo *) NULL);
1974 assert(exception->signature == MagickSignature);
1975 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00001976 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00001977 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001978 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001979 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1980 kernel_info->width=width;
1981 kernel_info->height=width;
1982 kernel_info->signature=MagickSignature;
1983 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1984 kernel_info->width*sizeof(*kernel_info->values));
1985 if (kernel_info->values == (double *) NULL)
1986 {
1987 kernel_info=DestroyKernelInfo(kernel_info);
1988 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1989 }
1990 j=(ssize_t) kernel_info->width/2;
cristy3ed852e2009-09-05 21:47:34 +00001991 i=0;
cristy47e00502009-12-17 19:19:57 +00001992 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00001993 {
cristy47e00502009-12-17 19:19:57 +00001994 for (u=(-j); u <= j; u++)
cristy41cbe682011-07-15 19:12:37 +00001995 {
1996 kernel_info->values[i]=(double) (exp(-((double) u*u+v*v)/(2.0*
1997 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
1998 i++;
1999 }
cristy3ed852e2009-09-05 21:47:34 +00002000 }
cristy0a922382011-07-16 15:30:34 +00002001 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00002002 blur_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00002003 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00002004 return(blur_image);
2005}
2006
2007/*
2008%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2009% %
2010% %
2011% %
cristy3ed852e2009-09-05 21:47:34 +00002012% M o t i o n B l u r I m a g e %
2013% %
2014% %
2015% %
2016%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2017%
2018% MotionBlurImage() simulates motion blur. We convolve the image with a
2019% Gaussian operator of the given radius and standard deviation (sigma).
2020% For reasonable results, radius should be larger than sigma. Use a
2021% radius of 0 and MotionBlurImage() selects a suitable radius for you.
2022% Angle gives the angle of the blurring motion.
2023%
2024% Andrew Protano contributed this effect.
2025%
2026% The format of the MotionBlurImage method is:
2027%
2028% Image *MotionBlurImage(const Image *image,const double radius,
2029% const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002030%
2031% A description of each parameter follows:
2032%
2033% o image: the image.
2034%
cristy3ed852e2009-09-05 21:47:34 +00002035% o radius: the radius of the Gaussian, in pixels, not counting
2036% the center pixel.
2037%
2038% o sigma: the standard deviation of the Gaussian, in pixels.
2039%
cristycee97112010-05-28 00:44:52 +00002040% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00002041%
2042% o exception: return any errors or warnings in this structure.
2043%
2044*/
2045
cristybb503372010-05-27 20:51:26 +00002046static double *GetMotionBlurKernel(const size_t width,const double sigma)
cristy3ed852e2009-09-05 21:47:34 +00002047{
cristy3ed852e2009-09-05 21:47:34 +00002048 double
cristy47e00502009-12-17 19:19:57 +00002049 *kernel,
cristy3ed852e2009-09-05 21:47:34 +00002050 normalize;
2051
cristybb503372010-05-27 20:51:26 +00002052 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002053 i;
2054
2055 /*
cristy47e00502009-12-17 19:19:57 +00002056 Generate a 1-D convolution kernel.
cristy3ed852e2009-09-05 21:47:34 +00002057 */
2058 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2059 kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2060 if (kernel == (double *) NULL)
2061 return(kernel);
cristy3ed852e2009-09-05 21:47:34 +00002062 normalize=0.0;
cristybb503372010-05-27 20:51:26 +00002063 for (i=0; i < (ssize_t) width; i++)
cristy47e00502009-12-17 19:19:57 +00002064 {
cristy4205a3c2010-09-12 20:19:59 +00002065 kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
2066 MagickSigma)))/(MagickSQ2PI*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00002067 normalize+=kernel[i];
cristy47e00502009-12-17 19:19:57 +00002068 }
cristybb503372010-05-27 20:51:26 +00002069 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002070 kernel[i]/=normalize;
2071 return(kernel);
2072}
2073
cristyf4ad9df2011-07-08 16:49:03 +00002074MagickExport Image *MotionBlurImage(const Image *image,
2075 const double radius,const double sigma,const double angle,
2076 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002077{
cristyc4c8d132010-01-07 01:58:38 +00002078 CacheView
2079 *blur_view,
2080 *image_view;
2081
cristy3ed852e2009-09-05 21:47:34 +00002082 double
2083 *kernel;
2084
2085 Image
2086 *blur_image;
2087
cristy3ed852e2009-09-05 21:47:34 +00002088 MagickBooleanType
2089 status;
2090
cristybb503372010-05-27 20:51:26 +00002091 MagickOffsetType
2092 progress;
2093
cristy4c08aed2011-07-01 19:47:50 +00002094 PixelInfo
cristyddd82202009-11-03 20:14:50 +00002095 bias;
cristy3ed852e2009-09-05 21:47:34 +00002096
2097 OffsetInfo
2098 *offset;
2099
2100 PointInfo
2101 point;
2102
cristybb503372010-05-27 20:51:26 +00002103 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002104 i;
2105
cristybb503372010-05-27 20:51:26 +00002106 size_t
cristy3ed852e2009-09-05 21:47:34 +00002107 width;
2108
cristybb503372010-05-27 20:51:26 +00002109 ssize_t
2110 y;
2111
cristy3ed852e2009-09-05 21:47:34 +00002112 assert(image != (Image *) NULL);
2113 assert(image->signature == MagickSignature);
2114 if (image->debug != MagickFalse)
2115 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2116 assert(exception != (ExceptionInfo *) NULL);
2117 width=GetOptimalKernelWidth1D(radius,sigma);
2118 kernel=GetMotionBlurKernel(width,sigma);
2119 if (kernel == (double *) NULL)
2120 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2121 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2122 if (offset == (OffsetInfo *) NULL)
2123 {
2124 kernel=(double *) RelinquishMagickMemory(kernel);
2125 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2126 }
2127 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2128 if (blur_image == (Image *) NULL)
2129 {
2130 kernel=(double *) RelinquishMagickMemory(kernel);
2131 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2132 return((Image *) NULL);
2133 }
2134 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2135 {
2136 kernel=(double *) RelinquishMagickMemory(kernel);
2137 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2138 InheritException(exception,&blur_image->exception);
2139 blur_image=DestroyImage(blur_image);
2140 return((Image *) NULL);
2141 }
2142 point.x=(double) width*sin(DegreesToRadians(angle));
2143 point.y=(double) width*cos(DegreesToRadians(angle));
cristybb503372010-05-27 20:51:26 +00002144 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002145 {
cristybb503372010-05-27 20:51:26 +00002146 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2147 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
cristy3ed852e2009-09-05 21:47:34 +00002148 }
2149 /*
2150 Motion blur image.
2151 */
2152 status=MagickTrue;
2153 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00002154 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00002155 image_view=AcquireCacheView(image);
2156 blur_view=AcquireCacheView(blur_image);
cristyb557a152011-02-22 12:14:30 +00002157#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +00002158 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +00002159#endif
cristybb503372010-05-27 20:51:26 +00002160 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002161 {
cristy4c08aed2011-07-01 19:47:50 +00002162 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002163 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002164
cristy117ff172010-08-15 21:35:32 +00002165 register ssize_t
2166 x;
2167
cristy3ed852e2009-09-05 21:47:34 +00002168 if (status == MagickFalse)
2169 continue;
2170 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2171 exception);
cristy4c08aed2011-07-01 19:47:50 +00002172 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002173 {
2174 status=MagickFalse;
2175 continue;
2176 }
cristybb503372010-05-27 20:51:26 +00002177 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002178 {
cristy4c08aed2011-07-01 19:47:50 +00002179 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002180 qixel;
2181
2182 PixelPacket
2183 pixel;
2184
2185 register double
cristyc47d1f82009-11-26 01:44:43 +00002186 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00002187
cristybb503372010-05-27 20:51:26 +00002188 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002189 i;
2190
cristy3ed852e2009-09-05 21:47:34 +00002191 k=kernel;
cristyddd82202009-11-03 20:14:50 +00002192 qixel=bias;
cristyed231572011-07-14 02:18:59 +00002193 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002194 {
cristybb503372010-05-27 20:51:26 +00002195 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002196 {
2197 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2198 offset[i].y,&pixel,exception);
2199 qixel.red+=(*k)*pixel.red;
2200 qixel.green+=(*k)*pixel.green;
2201 qixel.blue+=(*k)*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00002202 qixel.alpha+=(*k)*pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002203 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002204 qixel.black+=(*k)*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00002205 k++;
2206 }
cristyed231572011-07-14 02:18:59 +00002207 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002208 SetPixelRed(blur_image,
2209 ClampToQuantum(qixel.red),q);
cristyed231572011-07-14 02:18:59 +00002210 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002211 SetPixelGreen(blur_image,
2212 ClampToQuantum(qixel.green),q);
cristyed231572011-07-14 02:18:59 +00002213 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002214 SetPixelBlue(blur_image,
2215 ClampToQuantum(qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00002216 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00002217 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00002218 SetPixelBlack(blur_image,
2219 ClampToQuantum(qixel.black),q);
cristyed231572011-07-14 02:18:59 +00002220 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002221 SetPixelAlpha(blur_image,
2222 ClampToQuantum(qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00002223 }
2224 else
2225 {
2226 MagickRealType
2227 alpha,
2228 gamma;
2229
2230 alpha=0.0;
2231 gamma=0.0;
cristybb503372010-05-27 20:51:26 +00002232 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00002233 {
2234 (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
2235 offset[i].y,&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00002236 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00002237 qixel.red+=(*k)*alpha*pixel.red;
2238 qixel.green+=(*k)*alpha*pixel.green;
2239 qixel.blue+=(*k)*alpha*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00002240 qixel.alpha+=(*k)*pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002241 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002242 qixel.black+=(*k)*alpha*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00002243 gamma+=(*k)*alpha;
2244 k++;
2245 }
2246 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00002247 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002248 SetPixelRed(blur_image,
2249 ClampToQuantum(gamma*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00002250 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002251 SetPixelGreen(blur_image,
2252 ClampToQuantum(gamma*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00002253 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002254 SetPixelBlue(blur_image,
2255 ClampToQuantum(gamma*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00002256 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00002257 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00002258 SetPixelBlack(blur_image,
2259 ClampToQuantum(gamma*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00002260 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00002261 SetPixelAlpha(blur_image,
2262 ClampToQuantum(qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00002263 }
cristyed231572011-07-14 02:18:59 +00002264 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00002265 }
2266 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2267 status=MagickFalse;
2268 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2269 {
2270 MagickBooleanType
2271 proceed;
2272
cristyb557a152011-02-22 12:14:30 +00002273#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00002274 #pragma omp critical (MagickCore_MotionBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00002275#endif
2276 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2277 if (proceed == MagickFalse)
2278 status=MagickFalse;
2279 }
2280 }
2281 blur_view=DestroyCacheView(blur_view);
2282 image_view=DestroyCacheView(image_view);
2283 kernel=(double *) RelinquishMagickMemory(kernel);
2284 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2285 if (status == MagickFalse)
2286 blur_image=DestroyImage(blur_image);
2287 return(blur_image);
2288}
2289
2290/*
2291%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2292% %
2293% %
2294% %
2295% P r e v i e w I m a g e %
2296% %
2297% %
2298% %
2299%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2300%
2301% PreviewImage() tiles 9 thumbnails of the specified image with an image
2302% processing operation applied with varying parameters. This may be helpful
2303% pin-pointing an appropriate parameter for a particular image processing
2304% operation.
2305%
2306% The format of the PreviewImages method is:
2307%
2308% Image *PreviewImages(const Image *image,const PreviewType preview,
2309% ExceptionInfo *exception)
2310%
2311% A description of each parameter follows:
2312%
2313% o image: the image.
2314%
2315% o preview: the image processing operation.
2316%
2317% o exception: return any errors or warnings in this structure.
2318%
2319*/
2320MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2321 ExceptionInfo *exception)
2322{
2323#define NumberTiles 9
2324#define PreviewImageTag "Preview/Image"
2325#define DefaultPreviewGeometry "204x204+10+10"
2326
2327 char
2328 factor[MaxTextExtent],
2329 label[MaxTextExtent];
2330
2331 double
2332 degrees,
2333 gamma,
2334 percentage,
2335 radius,
2336 sigma,
2337 threshold;
2338
2339 Image
2340 *images,
2341 *montage_image,
2342 *preview_image,
2343 *thumbnail;
2344
2345 ImageInfo
2346 *preview_info;
2347
cristy3ed852e2009-09-05 21:47:34 +00002348 MagickBooleanType
2349 proceed;
2350
2351 MontageInfo
2352 *montage_info;
2353
2354 QuantizeInfo
2355 quantize_info;
2356
2357 RectangleInfo
2358 geometry;
2359
cristybb503372010-05-27 20:51:26 +00002360 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002361 i,
2362 x;
2363
cristybb503372010-05-27 20:51:26 +00002364 size_t
cristy3ed852e2009-09-05 21:47:34 +00002365 colors;
2366
cristy117ff172010-08-15 21:35:32 +00002367 ssize_t
2368 y;
2369
cristy3ed852e2009-09-05 21:47:34 +00002370 /*
2371 Open output image file.
2372 */
2373 assert(image != (Image *) NULL);
2374 assert(image->signature == MagickSignature);
2375 if (image->debug != MagickFalse)
2376 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2377 colors=2;
2378 degrees=0.0;
2379 gamma=(-0.2f);
2380 preview_info=AcquireImageInfo();
2381 SetGeometry(image,&geometry);
2382 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2383 &geometry.width,&geometry.height);
2384 images=NewImageList();
2385 percentage=12.5;
2386 GetQuantizeInfo(&quantize_info);
2387 radius=0.0;
2388 sigma=1.0;
2389 threshold=0.0;
2390 x=0;
2391 y=0;
2392 for (i=0; i < NumberTiles; i++)
2393 {
2394 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2395 if (thumbnail == (Image *) NULL)
2396 break;
2397 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2398 (void *) NULL);
2399 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2400 if (i == (NumberTiles/2))
2401 {
2402 (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
2403 AppendImageToList(&images,thumbnail);
2404 continue;
2405 }
2406 switch (preview)
2407 {
2408 case RotatePreview:
2409 {
2410 degrees+=45.0;
2411 preview_image=RotateImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002412 (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002413 break;
2414 }
2415 case ShearPreview:
2416 {
2417 degrees+=5.0;
2418 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002419 (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002420 degrees,2.0*degrees);
2421 break;
2422 }
2423 case RollPreview:
2424 {
cristybb503372010-05-27 20:51:26 +00002425 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2426 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
cristy3ed852e2009-09-05 21:47:34 +00002427 preview_image=RollImage(thumbnail,x,y,exception);
cristyb51dff52011-05-19 16:55:47 +00002428 (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
cristye8c25f92010-06-03 00:53:06 +00002429 (double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00002430 break;
2431 }
2432 case HuePreview:
2433 {
2434 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2435 if (preview_image == (Image *) NULL)
2436 break;
cristyb51dff52011-05-19 16:55:47 +00002437 (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",
cristy3ed852e2009-09-05 21:47:34 +00002438 2.0*percentage);
2439 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002440 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002441 break;
2442 }
2443 case SaturationPreview:
2444 {
2445 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2446 if (preview_image == (Image *) NULL)
2447 break;
cristyb51dff52011-05-19 16:55:47 +00002448 (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",
cristy8cd5b312010-01-07 01:10:24 +00002449 2.0*percentage);
cristy3ed852e2009-09-05 21:47:34 +00002450 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002451 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002452 break;
2453 }
2454 case BrightnessPreview:
2455 {
2456 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2457 if (preview_image == (Image *) NULL)
2458 break;
cristyb51dff52011-05-19 16:55:47 +00002459 (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
cristy3ed852e2009-09-05 21:47:34 +00002460 (void) ModulateImage(preview_image,factor);
cristyb51dff52011-05-19 16:55:47 +00002461 (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002462 break;
2463 }
2464 case GammaPreview:
2465 default:
2466 {
2467 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2468 if (preview_image == (Image *) NULL)
2469 break;
2470 gamma+=0.4f;
cristyb3e7c6c2011-07-24 01:43:55 +00002471 (void) GammaImage(preview_image,gamma,exception);
cristyb51dff52011-05-19 16:55:47 +00002472 (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
cristy3ed852e2009-09-05 21:47:34 +00002473 break;
2474 }
2475 case SpiffPreview:
2476 {
2477 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2478 if (preview_image != (Image *) NULL)
2479 for (x=0; x < i; x++)
2480 (void) ContrastImage(preview_image,MagickTrue);
cristyb51dff52011-05-19 16:55:47 +00002481 (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002482 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002483 break;
2484 }
2485 case DullPreview:
2486 {
2487 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2488 if (preview_image == (Image *) NULL)
2489 break;
2490 for (x=0; x < i; x++)
2491 (void) ContrastImage(preview_image,MagickFalse);
cristyb51dff52011-05-19 16:55:47 +00002492 (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002493 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002494 break;
2495 }
2496 case GrayscalePreview:
2497 {
2498 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2499 if (preview_image == (Image *) NULL)
2500 break;
2501 colors<<=1;
2502 quantize_info.number_colors=colors;
2503 quantize_info.colorspace=GRAYColorspace;
2504 (void) QuantizeImage(&quantize_info,preview_image);
cristyb51dff52011-05-19 16:55:47 +00002505 (void) FormatLocaleString(label,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002506 "-colorspace gray -colors %.20g",(double) colors);
cristy3ed852e2009-09-05 21:47:34 +00002507 break;
2508 }
2509 case QuantizePreview:
2510 {
2511 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2512 if (preview_image == (Image *) NULL)
2513 break;
2514 colors<<=1;
2515 quantize_info.number_colors=colors;
2516 (void) QuantizeImage(&quantize_info,preview_image);
cristyb51dff52011-05-19 16:55:47 +00002517 (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002518 colors);
cristy3ed852e2009-09-05 21:47:34 +00002519 break;
2520 }
2521 case DespecklePreview:
2522 {
2523 for (x=0; x < (i-1); x++)
2524 {
2525 preview_image=DespeckleImage(thumbnail,exception);
2526 if (preview_image == (Image *) NULL)
2527 break;
2528 thumbnail=DestroyImage(thumbnail);
2529 thumbnail=preview_image;
2530 }
2531 preview_image=DespeckleImage(thumbnail,exception);
2532 if (preview_image == (Image *) NULL)
2533 break;
cristyb51dff52011-05-19 16:55:47 +00002534 (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002535 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002536 break;
2537 }
2538 case ReduceNoisePreview:
2539 {
cristy95c38342011-03-18 22:39:51 +00002540 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2541 (size_t) radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002542 (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002543 break;
2544 }
2545 case AddNoisePreview:
2546 {
2547 switch ((int) i)
2548 {
2549 case 0:
2550 {
2551 (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2552 break;
2553 }
2554 case 1:
2555 {
2556 (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2557 break;
2558 }
2559 case 2:
2560 {
2561 (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2562 break;
2563 }
2564 case 3:
2565 {
2566 (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2567 break;
2568 }
2569 case 4:
2570 {
2571 (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2572 break;
2573 }
2574 case 5:
2575 {
2576 (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2577 break;
2578 }
2579 default:
2580 {
2581 (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2582 break;
2583 }
2584 }
cristyd76c51e2011-03-26 00:21:26 +00002585 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2586 (size_t) i,exception);
cristyb51dff52011-05-19 16:55:47 +00002587 (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002588 break;
2589 }
2590 case SharpenPreview:
2591 {
2592 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002593 (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002594 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002595 break;
2596 }
2597 case BlurPreview:
2598 {
2599 preview_image=BlurImage(thumbnail,radius,sigma,exception);
cristyb51dff52011-05-19 16:55:47 +00002600 (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
cristy3ed852e2009-09-05 21:47:34 +00002601 sigma);
2602 break;
2603 }
2604 case ThresholdPreview:
2605 {
2606 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2607 if (preview_image == (Image *) NULL)
2608 break;
2609 (void) BilevelImage(thumbnail,
2610 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
cristyb51dff52011-05-19 16:55:47 +00002611 (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",
cristy3ed852e2009-09-05 21:47:34 +00002612 (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2613 break;
2614 }
2615 case EdgeDetectPreview:
2616 {
2617 preview_image=EdgeImage(thumbnail,radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002618 (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002619 break;
2620 }
2621 case SpreadPreview:
2622 {
2623 preview_image=SpreadImage(thumbnail,radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002624 (void) FormatLocaleString(label,MaxTextExtent,"spread %g",
cristy8cd5b312010-01-07 01:10:24 +00002625 radius+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002626 break;
2627 }
2628 case SolarizePreview:
2629 {
2630 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2631 if (preview_image == (Image *) NULL)
2632 break;
2633 (void) SolarizeImage(preview_image,(double) QuantumRange*
2634 percentage/100.0);
cristyb51dff52011-05-19 16:55:47 +00002635 (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
cristy3ed852e2009-09-05 21:47:34 +00002636 (QuantumRange*percentage)/100.0);
2637 break;
2638 }
2639 case ShadePreview:
2640 {
2641 degrees+=10.0;
2642 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2643 exception);
cristyb51dff52011-05-19 16:55:47 +00002644 (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002645 degrees,degrees);
cristy3ed852e2009-09-05 21:47:34 +00002646 break;
2647 }
2648 case RaisePreview:
2649 {
2650 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2651 if (preview_image == (Image *) NULL)
2652 break;
cristybb503372010-05-27 20:51:26 +00002653 geometry.width=(size_t) (2*i+2);
2654 geometry.height=(size_t) (2*i+2);
cristy3ed852e2009-09-05 21:47:34 +00002655 geometry.x=i/2;
2656 geometry.y=i/2;
2657 (void) RaiseImage(preview_image,&geometry,MagickTrue);
cristyb51dff52011-05-19 16:55:47 +00002658 (void) FormatLocaleString(label,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00002659 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002660 geometry.height,(double) geometry.x,(double) geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00002661 break;
2662 }
2663 case SegmentPreview:
2664 {
2665 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2666 if (preview_image == (Image *) NULL)
2667 break;
2668 threshold+=0.4f;
2669 (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
2670 threshold);
cristyb51dff52011-05-19 16:55:47 +00002671 (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002672 threshold,threshold);
2673 break;
2674 }
2675 case SwirlPreview:
2676 {
2677 preview_image=SwirlImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002678 (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002679 degrees+=45.0;
2680 break;
2681 }
2682 case ImplodePreview:
2683 {
2684 degrees+=0.1f;
2685 preview_image=ImplodeImage(thumbnail,degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002686 (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002687 break;
2688 }
2689 case WavePreview:
2690 {
2691 degrees+=5.0f;
2692 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
cristyb51dff52011-05-19 16:55:47 +00002693 (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002694 0.5*degrees,2.0*degrees);
cristy3ed852e2009-09-05 21:47:34 +00002695 break;
2696 }
2697 case OilPaintPreview:
2698 {
2699 preview_image=OilPaintImage(thumbnail,(double) radius,exception);
cristyb51dff52011-05-19 16:55:47 +00002700 (void) FormatLocaleString(label,MaxTextExtent,"paint %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002701 break;
2702 }
2703 case CharcoalDrawingPreview:
2704 {
2705 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2706 exception);
cristyb51dff52011-05-19 16:55:47 +00002707 (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
cristy8cd5b312010-01-07 01:10:24 +00002708 radius,sigma);
cristy3ed852e2009-09-05 21:47:34 +00002709 break;
2710 }
2711 case JPEGPreview:
2712 {
2713 char
2714 filename[MaxTextExtent];
2715
2716 int
2717 file;
2718
2719 MagickBooleanType
2720 status;
2721
2722 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2723 if (preview_image == (Image *) NULL)
2724 break;
cristybb503372010-05-27 20:51:26 +00002725 preview_info->quality=(size_t) percentage;
cristyb51dff52011-05-19 16:55:47 +00002726 (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002727 preview_info->quality);
cristy3ed852e2009-09-05 21:47:34 +00002728 file=AcquireUniqueFileResource(filename);
2729 if (file != -1)
2730 file=close(file)-1;
cristyb51dff52011-05-19 16:55:47 +00002731 (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00002732 "jpeg:%s",filename);
2733 status=WriteImage(preview_info,preview_image);
2734 if (status != MagickFalse)
2735 {
2736 Image
2737 *quality_image;
2738
2739 (void) CopyMagickString(preview_info->filename,
2740 preview_image->filename,MaxTextExtent);
2741 quality_image=ReadImage(preview_info,exception);
2742 if (quality_image != (Image *) NULL)
2743 {
2744 preview_image=DestroyImage(preview_image);
2745 preview_image=quality_image;
2746 }
2747 }
2748 (void) RelinquishUniqueFileResource(preview_image->filename);
2749 if ((GetBlobSize(preview_image)/1024) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002750 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
cristy3ed852e2009-09-05 21:47:34 +00002751 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2752 1024.0/1024.0);
2753 else
2754 if (GetBlobSize(preview_image) >= 1024)
cristyb51dff52011-05-19 16:55:47 +00002755 (void) FormatLocaleString(label,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002756 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
cristy8cd5b312010-01-07 01:10:24 +00002757 GetBlobSize(preview_image))/1024.0);
cristy3ed852e2009-09-05 21:47:34 +00002758 else
cristyb51dff52011-05-19 16:55:47 +00002759 (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
cristy54ea5732011-06-10 12:39:53 +00002760 factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
cristy3ed852e2009-09-05 21:47:34 +00002761 break;
2762 }
2763 }
2764 thumbnail=DestroyImage(thumbnail);
2765 percentage+=12.5;
2766 radius+=0.5;
2767 sigma+=0.25;
2768 if (preview_image == (Image *) NULL)
2769 break;
2770 (void) DeleteImageProperty(preview_image,"label");
2771 (void) SetImageProperty(preview_image,"label",label);
2772 AppendImageToList(&images,preview_image);
cristybb503372010-05-27 20:51:26 +00002773 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2774 NumberTiles);
cristy3ed852e2009-09-05 21:47:34 +00002775 if (proceed == MagickFalse)
2776 break;
2777 }
2778 if (images == (Image *) NULL)
2779 {
2780 preview_info=DestroyImageInfo(preview_info);
2781 return((Image *) NULL);
2782 }
2783 /*
2784 Create the montage.
2785 */
2786 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2787 (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2788 montage_info->shadow=MagickTrue;
2789 (void) CloneString(&montage_info->tile,"3x3");
2790 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2791 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2792 montage_image=MontageImages(images,montage_info,exception);
2793 montage_info=DestroyMontageInfo(montage_info);
2794 images=DestroyImageList(images);
2795 if (montage_image == (Image *) NULL)
2796 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2797 if (montage_image->montage != (char *) NULL)
2798 {
2799 /*
2800 Free image directory.
2801 */
2802 montage_image->montage=(char *) RelinquishMagickMemory(
2803 montage_image->montage);
2804 if (image->directory != (char *) NULL)
2805 montage_image->directory=(char *) RelinquishMagickMemory(
2806 montage_image->directory);
2807 }
2808 preview_info=DestroyImageInfo(preview_info);
2809 return(montage_image);
2810}
2811
2812/*
2813%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2814% %
2815% %
2816% %
2817% R a d i a l B l u r I m a g e %
2818% %
2819% %
2820% %
2821%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2822%
2823% RadialBlurImage() applies a radial blur to the image.
2824%
2825% Andrew Protano contributed this effect.
2826%
2827% The format of the RadialBlurImage method is:
2828%
2829% Image *RadialBlurImage(const Image *image,const double angle,
2830% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002831%
2832% A description of each parameter follows:
2833%
2834% o image: the image.
2835%
cristy3ed852e2009-09-05 21:47:34 +00002836% o angle: the angle of the radial blur.
2837%
2838% o exception: return any errors or warnings in this structure.
2839%
2840*/
cristyf4ad9df2011-07-08 16:49:03 +00002841MagickExport Image *RadialBlurImage(const Image *image,
2842 const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002843{
cristyc4c8d132010-01-07 01:58:38 +00002844 CacheView
2845 *blur_view,
2846 *image_view;
2847
cristy3ed852e2009-09-05 21:47:34 +00002848 Image
2849 *blur_image;
2850
cristy3ed852e2009-09-05 21:47:34 +00002851 MagickBooleanType
2852 status;
2853
cristybb503372010-05-27 20:51:26 +00002854 MagickOffsetType
2855 progress;
2856
cristy4c08aed2011-07-01 19:47:50 +00002857 PixelInfo
cristyddd82202009-11-03 20:14:50 +00002858 bias;
cristy3ed852e2009-09-05 21:47:34 +00002859
2860 MagickRealType
2861 blur_radius,
2862 *cos_theta,
2863 offset,
2864 *sin_theta,
2865 theta;
2866
2867 PointInfo
2868 blur_center;
2869
cristybb503372010-05-27 20:51:26 +00002870 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002871 i;
2872
cristybb503372010-05-27 20:51:26 +00002873 size_t
cristy3ed852e2009-09-05 21:47:34 +00002874 n;
2875
cristybb503372010-05-27 20:51:26 +00002876 ssize_t
2877 y;
2878
cristy3ed852e2009-09-05 21:47:34 +00002879 /*
2880 Allocate blur image.
2881 */
2882 assert(image != (Image *) NULL);
2883 assert(image->signature == MagickSignature);
2884 if (image->debug != MagickFalse)
2885 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2886 assert(exception != (ExceptionInfo *) NULL);
2887 assert(exception->signature == MagickSignature);
2888 blur_image=CloneImage(image,0,0,MagickTrue,exception);
2889 if (blur_image == (Image *) NULL)
2890 return((Image *) NULL);
2891 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
2892 {
2893 InheritException(exception,&blur_image->exception);
2894 blur_image=DestroyImage(blur_image);
2895 return((Image *) NULL);
2896 }
2897 blur_center.x=(double) image->columns/2.0;
2898 blur_center.y=(double) image->rows/2.0;
2899 blur_radius=hypot(blur_center.x,blur_center.y);
cristy117ff172010-08-15 21:35:32 +00002900 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
cristy3ed852e2009-09-05 21:47:34 +00002901 theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
2902 cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2903 sizeof(*cos_theta));
2904 sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2905 sizeof(*sin_theta));
2906 if ((cos_theta == (MagickRealType *) NULL) ||
2907 (sin_theta == (MagickRealType *) NULL))
2908 {
2909 blur_image=DestroyImage(blur_image);
2910 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2911 }
2912 offset=theta*(MagickRealType) (n-1)/2.0;
cristybb503372010-05-27 20:51:26 +00002913 for (i=0; i < (ssize_t) n; i++)
cristy3ed852e2009-09-05 21:47:34 +00002914 {
2915 cos_theta[i]=cos((double) (theta*i-offset));
2916 sin_theta[i]=sin((double) (theta*i-offset));
2917 }
2918 /*
2919 Radial blur image.
2920 */
2921 status=MagickTrue;
2922 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00002923 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00002924 image_view=AcquireCacheView(image);
2925 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00002926#if defined(MAGICKCORE_OPENMP_SUPPORT)
2927 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002928#endif
cristybb503372010-05-27 20:51:26 +00002929 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002930 {
cristy4c08aed2011-07-01 19:47:50 +00002931 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002932 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002933
cristy117ff172010-08-15 21:35:32 +00002934 register ssize_t
2935 x;
2936
cristy3ed852e2009-09-05 21:47:34 +00002937 if (status == MagickFalse)
2938 continue;
2939 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2940 exception);
cristy4c08aed2011-07-01 19:47:50 +00002941 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002942 {
2943 status=MagickFalse;
2944 continue;
2945 }
cristybb503372010-05-27 20:51:26 +00002946 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002947 {
cristy4c08aed2011-07-01 19:47:50 +00002948 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002949 qixel;
2950
2951 MagickRealType
2952 normalize,
2953 radius;
2954
2955 PixelPacket
2956 pixel;
2957
2958 PointInfo
2959 center;
2960
cristybb503372010-05-27 20:51:26 +00002961 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002962 i;
2963
cristybb503372010-05-27 20:51:26 +00002964 size_t
cristy3ed852e2009-09-05 21:47:34 +00002965 step;
2966
2967 center.x=(double) x-blur_center.x;
2968 center.y=(double) y-blur_center.y;
2969 radius=hypot((double) center.x,center.y);
2970 if (radius == 0)
2971 step=1;
2972 else
2973 {
cristybb503372010-05-27 20:51:26 +00002974 step=(size_t) (blur_radius/radius);
cristy3ed852e2009-09-05 21:47:34 +00002975 if (step == 0)
2976 step=1;
2977 else
2978 if (step >= n)
2979 step=n-1;
2980 }
2981 normalize=0.0;
cristyddd82202009-11-03 20:14:50 +00002982 qixel=bias;
cristyed231572011-07-14 02:18:59 +00002983 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002984 {
cristyeaedf062010-05-29 22:36:02 +00002985 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
cristy3ed852e2009-09-05 21:47:34 +00002986 {
cristyeaedf062010-05-29 22:36:02 +00002987 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
2988 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
2989 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
2990 cos_theta[i]+0.5),&pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00002991 qixel.red+=pixel.red;
2992 qixel.green+=pixel.green;
2993 qixel.blue+=pixel.blue;
cristy3ed852e2009-09-05 21:47:34 +00002994 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00002995 qixel.black+=pixel.black;
2996 qixel.alpha+=pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002997 normalize+=1.0;
2998 }
2999 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3000 normalize);
cristyed231572011-07-14 02:18:59 +00003001 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003002 SetPixelRed(blur_image,
3003 ClampToQuantum(normalize*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00003004 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003005 SetPixelGreen(blur_image,
3006 ClampToQuantum(normalize*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00003007 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003008 SetPixelBlue(blur_image,
3009 ClampToQuantum(normalize*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003010 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00003011 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00003012 SetPixelBlack(blur_image,
3013 ClampToQuantum(normalize*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00003014 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003015 SetPixelAlpha(blur_image,
3016 ClampToQuantum(normalize*qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003017 }
3018 else
3019 {
3020 MagickRealType
3021 alpha,
3022 gamma;
3023
3024 alpha=1.0;
3025 gamma=0.0;
cristyeaedf062010-05-29 22:36:02 +00003026 for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
cristy3ed852e2009-09-05 21:47:34 +00003027 {
cristyeaedf062010-05-29 22:36:02 +00003028 (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3029 (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
3030 (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
3031 cos_theta[i]+0.5),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00003032 alpha=(MagickRealType) (QuantumScale*pixel.alpha);
cristy3ed852e2009-09-05 21:47:34 +00003033 qixel.red+=alpha*pixel.red;
3034 qixel.green+=alpha*pixel.green;
3035 qixel.blue+=alpha*pixel.blue;
cristy4c08aed2011-07-01 19:47:50 +00003036 qixel.alpha+=pixel.alpha;
cristy3ed852e2009-09-05 21:47:34 +00003037 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00003038 qixel.black+=alpha*pixel.black;
cristy3ed852e2009-09-05 21:47:34 +00003039 gamma+=alpha;
3040 normalize+=1.0;
3041 }
3042 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3043 normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
3044 normalize);
cristyed231572011-07-14 02:18:59 +00003045 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003046 SetPixelRed(blur_image,
3047 ClampToQuantum(gamma*qixel.red),q);
cristyed231572011-07-14 02:18:59 +00003048 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003049 SetPixelGreen(blur_image,
3050 ClampToQuantum(gamma*qixel.green),q);
cristyed231572011-07-14 02:18:59 +00003051 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003052 SetPixelBlue(blur_image,
3053 ClampToQuantum(gamma*qixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003054 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00003055 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00003056 SetPixelBlack(blur_image,
3057 ClampToQuantum(gamma*qixel.black),q);
cristyed231572011-07-14 02:18:59 +00003058 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003059 SetPixelAlpha(blur_image,
3060 ClampToQuantum(normalize*qixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003061 }
cristyed231572011-07-14 02:18:59 +00003062 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003063 }
3064 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3065 status=MagickFalse;
3066 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3067 {
3068 MagickBooleanType
3069 proceed;
3070
cristyb5d5f722009-11-04 03:03:49 +00003071#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00003072 #pragma omp critical (MagickCore_RadialBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003073#endif
3074 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3075 if (proceed == MagickFalse)
3076 status=MagickFalse;
3077 }
3078 }
3079 blur_view=DestroyCacheView(blur_view);
3080 image_view=DestroyCacheView(image_view);
3081 cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3082 sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3083 if (status == MagickFalse)
3084 blur_image=DestroyImage(blur_image);
3085 return(blur_image);
3086}
3087
3088/*
3089%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3090% %
3091% %
3092% %
cristy3ed852e2009-09-05 21:47:34 +00003093% S e l e c t i v e B l u r I m a g e %
3094% %
3095% %
3096% %
3097%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3098%
3099% SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3100% It is similar to the unsharpen mask that sharpens everything with contrast
3101% above a certain threshold.
3102%
3103% The format of the SelectiveBlurImage method is:
3104%
3105% Image *SelectiveBlurImage(const Image *image,const double radius,
3106% const double sigma,const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003107%
3108% A description of each parameter follows:
3109%
3110% o image: the image.
3111%
cristy3ed852e2009-09-05 21:47:34 +00003112% o radius: the radius of the Gaussian, in pixels, not counting the center
3113% pixel.
3114%
3115% o sigma: the standard deviation of the Gaussian, in pixels.
3116%
3117% o threshold: only pixels within this contrast threshold are included
3118% in the blur operation.
3119%
3120% o exception: return any errors or warnings in this structure.
3121%
3122*/
cristyf4ad9df2011-07-08 16:49:03 +00003123MagickExport Image *SelectiveBlurImage(const Image *image,
3124 const double radius,const double sigma,const double threshold,
3125 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003126{
3127#define SelectiveBlurImageTag "SelectiveBlur/Image"
3128
cristy47e00502009-12-17 19:19:57 +00003129 CacheView
3130 *blur_view,
3131 *image_view;
3132
cristy3ed852e2009-09-05 21:47:34 +00003133 double
cristy3ed852e2009-09-05 21:47:34 +00003134 *kernel;
3135
3136 Image
3137 *blur_image;
3138
cristy3ed852e2009-09-05 21:47:34 +00003139 MagickBooleanType
3140 status;
3141
cristybb503372010-05-27 20:51:26 +00003142 MagickOffsetType
3143 progress;
3144
cristy4c08aed2011-07-01 19:47:50 +00003145 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003146 bias;
3147
cristybb503372010-05-27 20:51:26 +00003148 register ssize_t
cristy47e00502009-12-17 19:19:57 +00003149 i;
cristy3ed852e2009-09-05 21:47:34 +00003150
cristybb503372010-05-27 20:51:26 +00003151 size_t
cristy3ed852e2009-09-05 21:47:34 +00003152 width;
3153
cristybb503372010-05-27 20:51:26 +00003154 ssize_t
3155 j,
3156 u,
3157 v,
3158 y;
3159
cristy3ed852e2009-09-05 21:47:34 +00003160 /*
3161 Initialize blur image attributes.
3162 */
3163 assert(image != (Image *) NULL);
3164 assert(image->signature == MagickSignature);
3165 if (image->debug != MagickFalse)
3166 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3167 assert(exception != (ExceptionInfo *) NULL);
3168 assert(exception->signature == MagickSignature);
3169 width=GetOptimalKernelWidth1D(radius,sigma);
3170 kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3171 if (kernel == (double *) NULL)
3172 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00003173 j=(ssize_t) width/2;
cristy3ed852e2009-09-05 21:47:34 +00003174 i=0;
cristy47e00502009-12-17 19:19:57 +00003175 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00003176 {
cristy47e00502009-12-17 19:19:57 +00003177 for (u=(-j); u <= j; u++)
cristy4205a3c2010-09-12 20:19:59 +00003178 kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3179 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00003180 }
3181 if (image->debug != MagickFalse)
3182 {
3183 char
3184 format[MaxTextExtent],
3185 *message;
3186
cristy117ff172010-08-15 21:35:32 +00003187 register const double
3188 *k;
3189
cristybb503372010-05-27 20:51:26 +00003190 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003191 u,
3192 v;
3193
cristy3ed852e2009-09-05 21:47:34 +00003194 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003195 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3196 width);
cristy3ed852e2009-09-05 21:47:34 +00003197 message=AcquireString("");
3198 k=kernel;
cristybb503372010-05-27 20:51:26 +00003199 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003200 {
3201 *message='\0';
cristyb51dff52011-05-19 16:55:47 +00003202 (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
cristy3ed852e2009-09-05 21:47:34 +00003203 (void) ConcatenateString(&message,format);
cristybb503372010-05-27 20:51:26 +00003204 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003205 {
cristyb51dff52011-05-19 16:55:47 +00003206 (void) FormatLocaleString(format,MaxTextExtent,"%+f ",*k++);
cristy3ed852e2009-09-05 21:47:34 +00003207 (void) ConcatenateString(&message,format);
3208 }
3209 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3210 }
3211 message=DestroyString(message);
3212 }
3213 blur_image=CloneImage(image,0,0,MagickTrue,exception);
3214 if (blur_image == (Image *) NULL)
3215 return((Image *) NULL);
3216 if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
3217 {
3218 InheritException(exception,&blur_image->exception);
3219 blur_image=DestroyImage(blur_image);
3220 return((Image *) NULL);
3221 }
3222 /*
3223 Threshold blur image.
3224 */
3225 status=MagickTrue;
3226 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003227 GetPixelInfo(image,&bias);
3228 SetPixelInfoBias(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00003229 image_view=AcquireCacheView(image);
3230 blur_view=AcquireCacheView(blur_image);
cristyb5d5f722009-11-04 03:03:49 +00003231#if defined(MAGICKCORE_OPENMP_SUPPORT)
3232 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003233#endif
cristybb503372010-05-27 20:51:26 +00003234 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003235 {
cristy4c08aed2011-07-01 19:47:50 +00003236 double
3237 contrast;
3238
cristy3ed852e2009-09-05 21:47:34 +00003239 MagickBooleanType
3240 sync;
3241
3242 MagickRealType
3243 gamma;
3244
cristy4c08aed2011-07-01 19:47:50 +00003245 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003246 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00003247
cristy4c08aed2011-07-01 19:47:50 +00003248 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003249 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003250
cristy117ff172010-08-15 21:35:32 +00003251 register ssize_t
3252 x;
3253
cristy3ed852e2009-09-05 21:47:34 +00003254 if (status == MagickFalse)
3255 continue;
cristy117ff172010-08-15 21:35:32 +00003256 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3257 (width/2L),image->columns+width,width,exception);
cristy3ed852e2009-09-05 21:47:34 +00003258 q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3259 exception);
cristy4c08aed2011-07-01 19:47:50 +00003260 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003261 {
3262 status=MagickFalse;
3263 continue;
3264 }
cristybb503372010-05-27 20:51:26 +00003265 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003266 {
cristy4c08aed2011-07-01 19:47:50 +00003267 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003268 pixel;
3269
3270 register const double
cristyc47d1f82009-11-26 01:44:43 +00003271 *restrict k;
cristy3ed852e2009-09-05 21:47:34 +00003272
cristybb503372010-05-27 20:51:26 +00003273 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003274 u;
3275
cristy117ff172010-08-15 21:35:32 +00003276 ssize_t
3277 j,
3278 v;
3279
cristyddd82202009-11-03 20:14:50 +00003280 pixel=bias;
cristy3ed852e2009-09-05 21:47:34 +00003281 k=kernel;
3282 gamma=0.0;
3283 j=0;
cristyed231572011-07-14 02:18:59 +00003284 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) == 0) || (image->matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00003285 {
cristybb503372010-05-27 20:51:26 +00003286 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003287 {
cristybb503372010-05-27 20:51:26 +00003288 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003289 {
cristyed231572011-07-14 02:18:59 +00003290 contrast=GetPixelIntensity(image,p+(u+j)*GetPixelChannels(image))-
cristy4c08aed2011-07-01 19:47:50 +00003291 (double) GetPixelIntensity(blur_image,q);
3292 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003293 {
cristy4c08aed2011-07-01 19:47:50 +00003294 pixel.red+=(*k)*
cristyed231572011-07-14 02:18:59 +00003295 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003296 pixel.green+=(*k)*
cristyed231572011-07-14 02:18:59 +00003297 GetPixelGreen(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003298 pixel.blue+=(*k)*
cristyed231572011-07-14 02:18:59 +00003299 GetPixelBlue(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003300 if (image->colorspace == CMYKColorspace)
3301 pixel.black+=(*k)*
cristyed231572011-07-14 02:18:59 +00003302 GetPixelBlack(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003303 gamma+=(*k);
3304 k++;
3305 }
3306 }
cristyd99b0962010-05-29 23:14:26 +00003307 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003308 }
3309 if (gamma != 0.0)
3310 {
3311 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00003312 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003313 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00003314 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003315 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00003316 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003317 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003318 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00003319 (image->colorspace == CMYKColorspace))
3320 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00003321 }
cristyed231572011-07-14 02:18:59 +00003322 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003323 {
3324 gamma=0.0;
3325 j=0;
cristybb503372010-05-27 20:51:26 +00003326 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003327 {
cristybb503372010-05-27 20:51:26 +00003328 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003329 {
cristy4c08aed2011-07-01 19:47:50 +00003330 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003331 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003332 GetPixelIntensity(blur_image,q);
3333 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003334 {
cristy4c08aed2011-07-01 19:47:50 +00003335 pixel.alpha+=(*k)*
cristyed231572011-07-14 02:18:59 +00003336 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003337 gamma+=(*k);
3338 k++;
3339 }
3340 }
cristyeaedf062010-05-29 22:36:02 +00003341 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003342 }
3343 if (gamma != 0.0)
3344 {
3345 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3346 gamma);
cristy4c08aed2011-07-01 19:47:50 +00003347 SetPixelAlpha(blur_image,ClampToQuantum(gamma*pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003348 }
3349 }
3350 }
3351 else
3352 {
3353 MagickRealType
3354 alpha;
3355
cristybb503372010-05-27 20:51:26 +00003356 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003357 {
cristybb503372010-05-27 20:51:26 +00003358 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003359 {
cristy4c08aed2011-07-01 19:47:50 +00003360 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003361 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003362 GetPixelIntensity(blur_image,q);
3363 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003364 {
cristy4c08aed2011-07-01 19:47:50 +00003365 alpha=(MagickRealType) (QuantumScale*
cristyed231572011-07-14 02:18:59 +00003366 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image)));
cristy4c08aed2011-07-01 19:47:50 +00003367 pixel.red+=(*k)*alpha*
cristyed231572011-07-14 02:18:59 +00003368 GetPixelRed(image,p+(u+j)*GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003369 pixel.green+=(*k)*alpha*GetPixelGreen(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003370 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003371 pixel.blue+=(*k)*alpha*GetPixelBlue(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003372 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003373 pixel.alpha+=(*k)*GetPixelAlpha(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003374 GetPixelChannels(image));
cristy4c08aed2011-07-01 19:47:50 +00003375 if (image->colorspace == CMYKColorspace)
3376 pixel.black+=(*k)*GetPixelBlack(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003377 GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003378 gamma+=(*k)*alpha;
3379 k++;
3380 }
3381 }
cristyeaedf062010-05-29 22:36:02 +00003382 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003383 }
3384 if (gamma != 0.0)
3385 {
3386 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyed231572011-07-14 02:18:59 +00003387 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003388 SetPixelRed(blur_image,ClampToQuantum(gamma*pixel.red),q);
cristyed231572011-07-14 02:18:59 +00003389 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003390 SetPixelGreen(blur_image,ClampToQuantum(gamma*pixel.green),q);
cristyed231572011-07-14 02:18:59 +00003391 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00003392 SetPixelBlue(blur_image,ClampToQuantum(gamma*pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00003393 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00003394 (image->colorspace == CMYKColorspace))
3395 SetPixelBlack(blur_image,ClampToQuantum(gamma*pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00003396 }
cristyed231572011-07-14 02:18:59 +00003397 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003398 {
3399 gamma=0.0;
3400 j=0;
cristybb503372010-05-27 20:51:26 +00003401 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00003402 {
cristybb503372010-05-27 20:51:26 +00003403 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00003404 {
cristy4c08aed2011-07-01 19:47:50 +00003405 contrast=GetPixelIntensity(image,p+(u+j)*
cristyed231572011-07-14 02:18:59 +00003406 GetPixelChannels(image))-(double)
cristy4c08aed2011-07-01 19:47:50 +00003407 GetPixelIntensity(blur_image,q);
3408 if (fabs(contrast) < threshold)
cristy3ed852e2009-09-05 21:47:34 +00003409 {
cristy4c08aed2011-07-01 19:47:50 +00003410 pixel.alpha+=(*k)*
cristyed231572011-07-14 02:18:59 +00003411 GetPixelAlpha(image,p+(u+j)*GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00003412 gamma+=(*k);
3413 k++;
3414 }
3415 }
cristyeaedf062010-05-29 22:36:02 +00003416 j+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +00003417 }
3418 if (gamma != 0.0)
3419 {
3420 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
3421 gamma);
cristy4c08aed2011-07-01 19:47:50 +00003422 SetPixelAlpha(blur_image,ClampToQuantum(pixel.alpha),q);
cristy3ed852e2009-09-05 21:47:34 +00003423 }
3424 }
3425 }
cristyed231572011-07-14 02:18:59 +00003426 p+=GetPixelChannels(image);
3427 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003428 }
3429 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3430 if (sync == MagickFalse)
3431 status=MagickFalse;
3432 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3433 {
3434 MagickBooleanType
3435 proceed;
3436
cristyb5d5f722009-11-04 03:03:49 +00003437#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00003438 #pragma omp critical (MagickCore_SelectiveBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003439#endif
3440 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3441 image->rows);
3442 if (proceed == MagickFalse)
3443 status=MagickFalse;
3444 }
3445 }
3446 blur_image->type=image->type;
3447 blur_view=DestroyCacheView(blur_view);
3448 image_view=DestroyCacheView(image_view);
3449 kernel=(double *) RelinquishMagickMemory(kernel);
3450 if (status == MagickFalse)
3451 blur_image=DestroyImage(blur_image);
3452 return(blur_image);
3453}
3454
3455/*
3456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3457% %
3458% %
3459% %
3460% S h a d e I m a g e %
3461% %
3462% %
3463% %
3464%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3465%
3466% ShadeImage() shines a distant light on an image to create a
3467% three-dimensional effect. You control the positioning of the light with
3468% azimuth and elevation; azimuth is measured in degrees off the x axis
3469% and elevation is measured in pixels above the Z axis.
3470%
3471% The format of the ShadeImage method is:
3472%
3473% Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3474% const double azimuth,const double elevation,ExceptionInfo *exception)
3475%
3476% A description of each parameter follows:
3477%
3478% o image: the image.
3479%
3480% o gray: A value other than zero shades the intensity of each pixel.
3481%
3482% o azimuth, elevation: Define the light source direction.
3483%
3484% o exception: return any errors or warnings in this structure.
3485%
3486*/
3487MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3488 const double azimuth,const double elevation,ExceptionInfo *exception)
3489{
3490#define ShadeImageTag "Shade/Image"
3491
cristyc4c8d132010-01-07 01:58:38 +00003492 CacheView
3493 *image_view,
3494 *shade_view;
3495
cristy3ed852e2009-09-05 21:47:34 +00003496 Image
3497 *shade_image;
3498
cristy3ed852e2009-09-05 21:47:34 +00003499 MagickBooleanType
3500 status;
3501
cristybb503372010-05-27 20:51:26 +00003502 MagickOffsetType
3503 progress;
3504
cristy3ed852e2009-09-05 21:47:34 +00003505 PrimaryInfo
3506 light;
3507
cristybb503372010-05-27 20:51:26 +00003508 ssize_t
3509 y;
3510
cristy3ed852e2009-09-05 21:47:34 +00003511 /*
3512 Initialize shaded image attributes.
3513 */
3514 assert(image != (const Image *) NULL);
3515 assert(image->signature == MagickSignature);
3516 if (image->debug != MagickFalse)
3517 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3518 assert(exception != (ExceptionInfo *) NULL);
3519 assert(exception->signature == MagickSignature);
3520 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3521 if (shade_image == (Image *) NULL)
3522 return((Image *) NULL);
3523 if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
3524 {
3525 InheritException(exception,&shade_image->exception);
3526 shade_image=DestroyImage(shade_image);
3527 return((Image *) NULL);
3528 }
3529 /*
3530 Compute the light vector.
3531 */
3532 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3533 cos(DegreesToRadians(elevation));
3534 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3535 cos(DegreesToRadians(elevation));
3536 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3537 /*
3538 Shade image.
3539 */
3540 status=MagickTrue;
3541 progress=0;
3542 image_view=AcquireCacheView(image);
3543 shade_view=AcquireCacheView(shade_image);
cristyb5d5f722009-11-04 03:03:49 +00003544#if defined(MAGICKCORE_OPENMP_SUPPORT)
3545 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00003546#endif
cristybb503372010-05-27 20:51:26 +00003547 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003548 {
3549 MagickRealType
3550 distance,
3551 normal_distance,
3552 shade;
3553
3554 PrimaryInfo
3555 normal;
3556
cristy4c08aed2011-07-01 19:47:50 +00003557 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00003558 *restrict p,
3559 *restrict s0,
3560 *restrict s1,
3561 *restrict s2;
cristy3ed852e2009-09-05 21:47:34 +00003562
cristy4c08aed2011-07-01 19:47:50 +00003563 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003564 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003565
cristy117ff172010-08-15 21:35:32 +00003566 register ssize_t
3567 x;
3568
cristy3ed852e2009-09-05 21:47:34 +00003569 if (status == MagickFalse)
3570 continue;
3571 p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
3572 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3573 exception);
cristy4c08aed2011-07-01 19:47:50 +00003574 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003575 {
3576 status=MagickFalse;
3577 continue;
3578 }
3579 /*
3580 Shade this row of pixels.
3581 */
3582 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
cristyed231572011-07-14 02:18:59 +00003583 s0=p+GetPixelChannels(image);
3584 s1=s0+(image->columns+2)*GetPixelChannels(image);
3585 s2=s1+(image->columns+2)*GetPixelChannels(image);
cristybb503372010-05-27 20:51:26 +00003586 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003587 {
3588 /*
3589 Determine the surface normal and compute shading.
3590 */
cristyed231572011-07-14 02:18:59 +00003591 normal.x=(double) (GetPixelIntensity(image,s0-GetPixelChannels(image))+
3592 GetPixelIntensity(image,s1-GetPixelChannels(image))+
3593 GetPixelIntensity(image,s2-GetPixelChannels(image))-
3594 GetPixelIntensity(image,s0+GetPixelChannels(image))-
3595 GetPixelIntensity(image,s1+GetPixelChannels(image))-
3596 GetPixelIntensity(image,s2+GetPixelChannels(image)));
3597 normal.y=(double) (GetPixelIntensity(image,s2-GetPixelChannels(image))+
cristy4c08aed2011-07-01 19:47:50 +00003598 GetPixelIntensity(image,s2)+
cristyed231572011-07-14 02:18:59 +00003599 GetPixelIntensity(image,s2+GetPixelChannels(image))-
3600 GetPixelIntensity(image,s0-GetPixelChannels(image))-
cristy4c08aed2011-07-01 19:47:50 +00003601 GetPixelIntensity(image,s0)-
cristyed231572011-07-14 02:18:59 +00003602 GetPixelIntensity(image,s0+GetPixelChannels(image)));
cristy3ed852e2009-09-05 21:47:34 +00003603 if ((normal.x == 0.0) && (normal.y == 0.0))
3604 shade=light.z;
3605 else
3606 {
3607 shade=0.0;
3608 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3609 if (distance > MagickEpsilon)
3610 {
3611 normal_distance=
3612 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3613 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3614 shade=distance/sqrt((double) normal_distance);
3615 }
3616 }
3617 if (gray != MagickFalse)
3618 {
cristy4c08aed2011-07-01 19:47:50 +00003619 SetPixelRed(shade_image,ClampToQuantum(shade),q);
3620 SetPixelGreen(shade_image,ClampToQuantum(shade),q);
3621 SetPixelBlue(shade_image,ClampToQuantum(shade),q);
cristy3ed852e2009-09-05 21:47:34 +00003622 }
3623 else
3624 {
cristy4c08aed2011-07-01 19:47:50 +00003625 SetPixelRed(shade_image,ClampToQuantum(QuantumScale*shade*
3626 GetPixelRed(image,s1)),q);
3627 SetPixelGreen(shade_image,ClampToQuantum(QuantumScale*shade*
3628 GetPixelGreen(image,s1)),q);
3629 SetPixelBlue(shade_image,ClampToQuantum(QuantumScale*shade*
3630 GetPixelBlue(image,s1)),q);
cristy3ed852e2009-09-05 21:47:34 +00003631 }
cristy4c08aed2011-07-01 19:47:50 +00003632 SetPixelAlpha(shade_image,GetPixelAlpha(image,s1),q);
cristyed231572011-07-14 02:18:59 +00003633 s0+=GetPixelChannels(image);
3634 s1+=GetPixelChannels(image);
3635 s2+=GetPixelChannels(image);
3636 q+=GetPixelChannels(shade_image);
cristy3ed852e2009-09-05 21:47:34 +00003637 }
3638 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3639 status=MagickFalse;
3640 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3641 {
3642 MagickBooleanType
3643 proceed;
3644
cristyb5d5f722009-11-04 03:03:49 +00003645#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003646 #pragma omp critical (MagickCore_ShadeImage)
3647#endif
3648 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3649 if (proceed == MagickFalse)
3650 status=MagickFalse;
3651 }
3652 }
3653 shade_view=DestroyCacheView(shade_view);
3654 image_view=DestroyCacheView(image_view);
3655 if (status == MagickFalse)
3656 shade_image=DestroyImage(shade_image);
3657 return(shade_image);
3658}
3659
3660/*
3661%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3662% %
3663% %
3664% %
3665% S h a r p e n I m a g e %
3666% %
3667% %
3668% %
3669%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3670%
3671% SharpenImage() sharpens the image. We convolve the image with a Gaussian
3672% operator of the given radius and standard deviation (sigma). For
3673% reasonable results, radius should be larger than sigma. Use a radius of 0
3674% and SharpenImage() selects a suitable radius for you.
3675%
3676% Using a separable kernel would be faster, but the negative weights cancel
3677% out on the corners of the kernel producing often undesirable ringing in the
3678% filtered result; this can be avoided by using a 2D gaussian shaped image
3679% sharpening kernel instead.
3680%
3681% The format of the SharpenImage method is:
3682%
3683% Image *SharpenImage(const Image *image,const double radius,
3684% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003685%
3686% A description of each parameter follows:
3687%
3688% o image: the image.
3689%
cristy3ed852e2009-09-05 21:47:34 +00003690% o radius: the radius of the Gaussian, in pixels, not counting the center
3691% pixel.
3692%
3693% o sigma: the standard deviation of the Laplacian, in pixels.
3694%
3695% o exception: return any errors or warnings in this structure.
3696%
3697*/
cristy3ed852e2009-09-05 21:47:34 +00003698MagickExport Image *SharpenImage(const Image *image,const double radius,
3699 const double sigma,ExceptionInfo *exception)
3700{
cristy3ed852e2009-09-05 21:47:34 +00003701 double
cristy47e00502009-12-17 19:19:57 +00003702 normalize;
cristy3ed852e2009-09-05 21:47:34 +00003703
3704 Image
3705 *sharp_image;
3706
cristy41cbe682011-07-15 19:12:37 +00003707 KernelInfo
3708 *kernel_info;
3709
cristybb503372010-05-27 20:51:26 +00003710 register ssize_t
cristy47e00502009-12-17 19:19:57 +00003711 i;
3712
cristybb503372010-05-27 20:51:26 +00003713 size_t
cristy3ed852e2009-09-05 21:47:34 +00003714 width;
3715
cristy117ff172010-08-15 21:35:32 +00003716 ssize_t
3717 j,
3718 u,
3719 v;
3720
cristy3ed852e2009-09-05 21:47:34 +00003721 assert(image != (const Image *) NULL);
3722 assert(image->signature == MagickSignature);
3723 if (image->debug != MagickFalse)
3724 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3725 assert(exception != (ExceptionInfo *) NULL);
3726 assert(exception->signature == MagickSignature);
3727 width=GetOptimalKernelWidth2D(radius,sigma);
cristy5e6be1e2011-07-16 01:23:39 +00003728 kernel_info=AcquireKernelInfo((const char *) NULL);
cristy41cbe682011-07-15 19:12:37 +00003729 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003730 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00003731 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3732 kernel_info->width=width;
3733 kernel_info->height=width;
3734 kernel_info->signature=MagickSignature;
3735 kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
3736 kernel_info->width*sizeof(*kernel_info->values));
3737 if (kernel_info->values == (double *) NULL)
3738 {
3739 kernel_info=DestroyKernelInfo(kernel_info);
3740 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3741 }
cristy3ed852e2009-09-05 21:47:34 +00003742 normalize=0.0;
cristy41cbe682011-07-15 19:12:37 +00003743 j=(ssize_t) kernel_info->width/2;
cristy47e00502009-12-17 19:19:57 +00003744 i=0;
3745 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00003746 {
cristy47e00502009-12-17 19:19:57 +00003747 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00003748 {
cristy41cbe682011-07-15 19:12:37 +00003749 kernel_info->values[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*
3750 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3751 normalize+=kernel_info->values[i];
cristy3ed852e2009-09-05 21:47:34 +00003752 i++;
3753 }
3754 }
cristy41cbe682011-07-15 19:12:37 +00003755 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
cristy0a922382011-07-16 15:30:34 +00003756 kernel_info->bias=image->bias;
cristy5e6be1e2011-07-16 01:23:39 +00003757 sharp_image=ConvolveImage(image,kernel_info,exception);
cristy41cbe682011-07-15 19:12:37 +00003758 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00003759 return(sharp_image);
3760}
3761
3762/*
3763%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3764% %
3765% %
3766% %
3767% S p r e a d I m a g e %
3768% %
3769% %
3770% %
3771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3772%
3773% SpreadImage() is a special effects method that randomly displaces each
3774% pixel in a block defined by the radius parameter.
3775%
3776% The format of the SpreadImage method is:
3777%
3778% Image *SpreadImage(const Image *image,const double radius,
3779% ExceptionInfo *exception)
3780%
3781% A description of each parameter follows:
3782%
3783% o image: the image.
3784%
3785% o radius: Choose a random pixel in a neighborhood of this extent.
3786%
3787% o exception: return any errors or warnings in this structure.
3788%
3789*/
3790MagickExport Image *SpreadImage(const Image *image,const double radius,
3791 ExceptionInfo *exception)
3792{
3793#define SpreadImageTag "Spread/Image"
3794
cristyfa112112010-01-04 17:48:07 +00003795 CacheView
cristy9f7e7cb2011-03-26 00:49:57 +00003796 *image_view,
3797 *spread_view;
cristyfa112112010-01-04 17:48:07 +00003798
cristy3ed852e2009-09-05 21:47:34 +00003799 Image
3800 *spread_image;
3801
cristy3ed852e2009-09-05 21:47:34 +00003802 MagickBooleanType
3803 status;
3804
cristybb503372010-05-27 20:51:26 +00003805 MagickOffsetType
3806 progress;
3807
cristy4c08aed2011-07-01 19:47:50 +00003808 PixelInfo
cristyddd82202009-11-03 20:14:50 +00003809 bias;
cristy3ed852e2009-09-05 21:47:34 +00003810
3811 RandomInfo
cristyfa112112010-01-04 17:48:07 +00003812 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00003813
cristybb503372010-05-27 20:51:26 +00003814 size_t
cristy3ed852e2009-09-05 21:47:34 +00003815 width;
3816
cristybb503372010-05-27 20:51:26 +00003817 ssize_t
3818 y;
3819
cristy3ed852e2009-09-05 21:47:34 +00003820 /*
3821 Initialize spread image attributes.
3822 */
3823 assert(image != (Image *) NULL);
3824 assert(image->signature == MagickSignature);
3825 if (image->debug != MagickFalse)
3826 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3827 assert(exception != (ExceptionInfo *) NULL);
3828 assert(exception->signature == MagickSignature);
3829 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3830 exception);
3831 if (spread_image == (Image *) NULL)
3832 return((Image *) NULL);
3833 if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
3834 {
3835 InheritException(exception,&spread_image->exception);
3836 spread_image=DestroyImage(spread_image);
3837 return((Image *) NULL);
3838 }
3839 /*
3840 Spread image.
3841 */
3842 status=MagickTrue;
3843 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00003844 GetPixelInfo(spread_image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00003845 width=GetOptimalKernelWidth1D(radius,0.5);
cristy3ed852e2009-09-05 21:47:34 +00003846 random_info=AcquireRandomInfoThreadSet();
cristy9f7e7cb2011-03-26 00:49:57 +00003847 image_view=AcquireCacheView(image);
3848 spread_view=AcquireCacheView(spread_image);
cristyb557a152011-02-22 12:14:30 +00003849#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +00003850 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +00003851#endif
cristybb503372010-05-27 20:51:26 +00003852 for (y=0; y < (ssize_t) spread_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003853 {
cristy5c9e6f22010-09-17 17:31:01 +00003854 const int
3855 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00003856
cristy4c08aed2011-07-01 19:47:50 +00003857 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003858 pixel;
3859
cristy4c08aed2011-07-01 19:47:50 +00003860 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003861 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003862
cristy117ff172010-08-15 21:35:32 +00003863 register ssize_t
3864 x;
3865
cristy3ed852e2009-09-05 21:47:34 +00003866 if (status == MagickFalse)
3867 continue;
cristy9f7e7cb2011-03-26 00:49:57 +00003868 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00003869 exception);
cristy4c08aed2011-07-01 19:47:50 +00003870 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003871 {
3872 status=MagickFalse;
3873 continue;
3874 }
cristyddd82202009-11-03 20:14:50 +00003875 pixel=bias;
cristybb503372010-05-27 20:51:26 +00003876 for (x=0; x < (ssize_t) spread_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003877 {
cristy4c08aed2011-07-01 19:47:50 +00003878 (void) InterpolatePixelInfo(image,image_view,
cristy8a7c3e82011-03-26 02:10:53 +00003879 UndefinedInterpolatePixel,(double) x+width*(GetPseudoRandomValue(
3880 random_info[id])-0.5),(double) y+width*(GetPseudoRandomValue(
3881 random_info[id])-0.5),&pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00003882 SetPixelPixelInfo(spread_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00003883 q+=GetPixelChannels(spread_image);
cristy3ed852e2009-09-05 21:47:34 +00003884 }
cristy9f7e7cb2011-03-26 00:49:57 +00003885 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003886 status=MagickFalse;
3887 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3888 {
3889 MagickBooleanType
3890 proceed;
3891
cristyb557a152011-02-22 12:14:30 +00003892#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003893 #pragma omp critical (MagickCore_SpreadImage)
3894#endif
3895 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3896 if (proceed == MagickFalse)
3897 status=MagickFalse;
3898 }
3899 }
cristy9f7e7cb2011-03-26 00:49:57 +00003900 spread_view=DestroyCacheView(spread_view);
cristy3ed852e2009-09-05 21:47:34 +00003901 image_view=DestroyCacheView(image_view);
3902 random_info=DestroyRandomInfoThreadSet(random_info);
cristy3ed852e2009-09-05 21:47:34 +00003903 return(spread_image);
3904}
3905
3906/*
3907%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3908% %
3909% %
3910% %
cristy0834d642011-03-18 18:26:08 +00003911% S t a t i s t i c I m a g e %
3912% %
3913% %
3914% %
3915%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3916%
3917% StatisticImage() makes each pixel the min / max / median / mode / etc. of
cristy8d752042011-03-19 01:00:36 +00003918% the neighborhood of the specified width and height.
cristy0834d642011-03-18 18:26:08 +00003919%
3920% The format of the StatisticImage method is:
3921%
3922% Image *StatisticImage(const Image *image,const StatisticType type,
cristy95c38342011-03-18 22:39:51 +00003923% const size_t width,const size_t height,ExceptionInfo *exception)
cristy0834d642011-03-18 18:26:08 +00003924%
3925% A description of each parameter follows:
3926%
3927% o image: the image.
3928%
cristy0834d642011-03-18 18:26:08 +00003929% o type: the statistic type (median, mode, etc.).
3930%
cristy95c38342011-03-18 22:39:51 +00003931% o width: the width of the pixel neighborhood.
3932%
3933% o height: the height of the pixel neighborhood.
cristy0834d642011-03-18 18:26:08 +00003934%
3935% o exception: return any errors or warnings in this structure.
3936%
3937*/
3938
cristy733678d2011-03-18 21:29:28 +00003939#define ListChannels 5
3940
3941typedef struct _ListNode
3942{
3943 size_t
3944 next[9],
3945 count,
3946 signature;
3947} ListNode;
3948
3949typedef struct _SkipList
3950{
3951 ssize_t
3952 level;
3953
3954 ListNode
3955 *nodes;
3956} SkipList;
3957
3958typedef struct _PixelList
3959{
3960 size_t
cristy6fc86bb2011-03-18 23:45:16 +00003961 length,
cristy733678d2011-03-18 21:29:28 +00003962 seed,
3963 signature;
3964
3965 SkipList
3966 lists[ListChannels];
3967} PixelList;
3968
3969static PixelList *DestroyPixelList(PixelList *pixel_list)
3970{
3971 register ssize_t
3972 i;
3973
3974 if (pixel_list == (PixelList *) NULL)
3975 return((PixelList *) NULL);
3976 for (i=0; i < ListChannels; i++)
3977 if (pixel_list->lists[i].nodes != (ListNode *) NULL)
3978 pixel_list->lists[i].nodes=(ListNode *) RelinquishMagickMemory(
3979 pixel_list->lists[i].nodes);
3980 pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
3981 return(pixel_list);
3982}
3983
3984static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
3985{
3986 register ssize_t
3987 i;
3988
3989 assert(pixel_list != (PixelList **) NULL);
3990 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3991 if (pixel_list[i] != (PixelList *) NULL)
3992 pixel_list[i]=DestroyPixelList(pixel_list[i]);
3993 pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
3994 return(pixel_list);
3995}
3996
cristy6fc86bb2011-03-18 23:45:16 +00003997static PixelList *AcquirePixelList(const size_t width,const size_t height)
cristy733678d2011-03-18 21:29:28 +00003998{
3999 PixelList
4000 *pixel_list;
4001
4002 register ssize_t
4003 i;
4004
4005 pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
4006 if (pixel_list == (PixelList *) NULL)
4007 return(pixel_list);
4008 (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
cristy6fc86bb2011-03-18 23:45:16 +00004009 pixel_list->length=width*height;
cristy733678d2011-03-18 21:29:28 +00004010 for (i=0; i < ListChannels; i++)
4011 {
4012 pixel_list->lists[i].nodes=(ListNode *) AcquireQuantumMemory(65537UL,
4013 sizeof(*pixel_list->lists[i].nodes));
4014 if (pixel_list->lists[i].nodes == (ListNode *) NULL)
4015 return(DestroyPixelList(pixel_list));
4016 (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
4017 sizeof(*pixel_list->lists[i].nodes));
4018 }
4019 pixel_list->signature=MagickSignature;
4020 return(pixel_list);
4021}
4022
cristy6fc86bb2011-03-18 23:45:16 +00004023static PixelList **AcquirePixelListThreadSet(const size_t width,
4024 const size_t height)
cristy733678d2011-03-18 21:29:28 +00004025{
4026 PixelList
4027 **pixel_list;
4028
4029 register ssize_t
4030 i;
4031
4032 size_t
4033 number_threads;
4034
4035 number_threads=GetOpenMPMaximumThreads();
4036 pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
4037 sizeof(*pixel_list));
4038 if (pixel_list == (PixelList **) NULL)
4039 return((PixelList **) NULL);
4040 (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
4041 for (i=0; i < (ssize_t) number_threads; i++)
4042 {
cristy6fc86bb2011-03-18 23:45:16 +00004043 pixel_list[i]=AcquirePixelList(width,height);
cristy733678d2011-03-18 21:29:28 +00004044 if (pixel_list[i] == (PixelList *) NULL)
4045 return(DestroyPixelListThreadSet(pixel_list));
4046 }
4047 return(pixel_list);
4048}
4049
4050static void AddNodePixelList(PixelList *pixel_list,const ssize_t channel,
4051 const size_t color)
4052{
4053 register SkipList
4054 *list;
4055
4056 register ssize_t
4057 level;
4058
4059 size_t
4060 search,
4061 update[9];
4062
4063 /*
4064 Initialize the node.
4065 */
4066 list=pixel_list->lists+channel;
4067 list->nodes[color].signature=pixel_list->signature;
4068 list->nodes[color].count=1;
4069 /*
4070 Determine where it belongs in the list.
4071 */
4072 search=65536UL;
4073 for (level=list->level; level >= 0; level--)
4074 {
4075 while (list->nodes[search].next[level] < color)
4076 search=list->nodes[search].next[level];
4077 update[level]=search;
4078 }
4079 /*
4080 Generate a pseudo-random level for this node.
4081 */
4082 for (level=0; ; level++)
4083 {
4084 pixel_list->seed=(pixel_list->seed*42893621L)+1L;
4085 if ((pixel_list->seed & 0x300) != 0x300)
4086 break;
4087 }
4088 if (level > 8)
4089 level=8;
4090 if (level > (list->level+2))
4091 level=list->level+2;
4092 /*
4093 If we're raising the list's level, link back to the root node.
4094 */
4095 while (level > list->level)
4096 {
4097 list->level++;
4098 update[list->level]=65536UL;
4099 }
4100 /*
4101 Link the node into the skip-list.
4102 */
4103 do
4104 {
4105 list->nodes[color].next[level]=list->nodes[update[level]].next[level];
4106 list->nodes[update[level]].next[level]=color;
cristy3cba8ca2011-03-19 01:29:12 +00004107 } while (level-- > 0);
cristy733678d2011-03-18 21:29:28 +00004108}
4109
cristy4c08aed2011-07-01 19:47:50 +00004110static PixelInfo GetMaximumPixelList(PixelList *pixel_list)
cristy6fc86bb2011-03-18 23:45:16 +00004111{
cristy4c08aed2011-07-01 19:47:50 +00004112 PixelInfo
cristy6fc86bb2011-03-18 23:45:16 +00004113 pixel;
4114
4115 register SkipList
4116 *list;
4117
4118 register ssize_t
4119 channel;
4120
4121 size_t
cristyd76c51e2011-03-26 00:21:26 +00004122 color,
4123 maximum;
cristy49f37242011-03-22 18:18:23 +00004124
4125 ssize_t
cristy6fc86bb2011-03-18 23:45:16 +00004126 count;
4127
4128 unsigned short
cristyd76c51e2011-03-26 00:21:26 +00004129 channels[ListChannels];
cristy6fc86bb2011-03-18 23:45:16 +00004130
4131 /*
4132 Find the maximum value for each of the color.
4133 */
4134 for (channel=0; channel < 5; channel++)
4135 {
4136 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004137 color=65536L;
cristy6fc86bb2011-03-18 23:45:16 +00004138 count=0;
cristy49f37242011-03-22 18:18:23 +00004139 maximum=list->nodes[color].next[0];
cristy6fc86bb2011-03-18 23:45:16 +00004140 do
4141 {
4142 color=list->nodes[color].next[0];
cristy49f37242011-03-22 18:18:23 +00004143 if (color > maximum)
4144 maximum=color;
cristy6fc86bb2011-03-18 23:45:16 +00004145 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004146 } while (count < (ssize_t) pixel_list->length);
cristy49f37242011-03-22 18:18:23 +00004147 channels[channel]=(unsigned short) maximum;
4148 }
cristy4c08aed2011-07-01 19:47:50 +00004149 GetPixelInfo((const Image *) NULL,&pixel);
cristy49f37242011-03-22 18:18:23 +00004150 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4151 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4152 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004153 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4154 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy49f37242011-03-22 18:18:23 +00004155 return(pixel);
4156}
4157
cristy4c08aed2011-07-01 19:47:50 +00004158static PixelInfo GetMeanPixelList(PixelList *pixel_list)
cristy49f37242011-03-22 18:18:23 +00004159{
cristy4c08aed2011-07-01 19:47:50 +00004160 PixelInfo
cristy49f37242011-03-22 18:18:23 +00004161 pixel;
4162
cristy80a99a32011-03-30 01:30:23 +00004163 MagickRealType
4164 sum;
4165
cristy49f37242011-03-22 18:18:23 +00004166 register SkipList
4167 *list;
4168
4169 register ssize_t
4170 channel;
4171
4172 size_t
cristy80a99a32011-03-30 01:30:23 +00004173 color;
cristy49f37242011-03-22 18:18:23 +00004174
4175 ssize_t
4176 count;
4177
4178 unsigned short
4179 channels[ListChannels];
4180
4181 /*
4182 Find the mean value for each of the color.
4183 */
4184 for (channel=0; channel < 5; channel++)
4185 {
4186 list=pixel_list->lists+channel;
4187 color=65536L;
4188 count=0;
cristy80a99a32011-03-30 01:30:23 +00004189 sum=0.0;
cristy49f37242011-03-22 18:18:23 +00004190 do
4191 {
4192 color=list->nodes[color].next[0];
cristy80a99a32011-03-30 01:30:23 +00004193 sum+=(MagickRealType) list->nodes[color].count*color;
cristy49f37242011-03-22 18:18:23 +00004194 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004195 } while (count < (ssize_t) pixel_list->length);
cristy80a99a32011-03-30 01:30:23 +00004196 sum/=pixel_list->length;
4197 channels[channel]=(unsigned short) sum;
cristy6fc86bb2011-03-18 23:45:16 +00004198 }
cristy4c08aed2011-07-01 19:47:50 +00004199 GetPixelInfo((const Image *) NULL,&pixel);
cristy6fc86bb2011-03-18 23:45:16 +00004200 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4201 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4202 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004203 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4204 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy6fc86bb2011-03-18 23:45:16 +00004205 return(pixel);
4206}
4207
cristy4c08aed2011-07-01 19:47:50 +00004208static PixelInfo GetMedianPixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004209{
cristy4c08aed2011-07-01 19:47:50 +00004210 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004211 pixel;
4212
4213 register SkipList
4214 *list;
4215
4216 register ssize_t
4217 channel;
4218
4219 size_t
cristy49f37242011-03-22 18:18:23 +00004220 color;
4221
4222 ssize_t
cristy733678d2011-03-18 21:29:28 +00004223 count;
4224
4225 unsigned short
4226 channels[ListChannels];
4227
4228 /*
4229 Find the median value for each of the color.
4230 */
cristy733678d2011-03-18 21:29:28 +00004231 for (channel=0; channel < 5; channel++)
4232 {
4233 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004234 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004235 count=0;
4236 do
4237 {
4238 color=list->nodes[color].next[0];
4239 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004240 } while (count <= (ssize_t) (pixel_list->length >> 1));
cristy6fc86bb2011-03-18 23:45:16 +00004241 channels[channel]=(unsigned short) color;
4242 }
cristy4c08aed2011-07-01 19:47:50 +00004243 GetPixelInfo((const Image *) NULL,&pixel);
cristy6fc86bb2011-03-18 23:45:16 +00004244 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4245 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4246 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004247 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4248 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy6fc86bb2011-03-18 23:45:16 +00004249 return(pixel);
4250}
4251
cristy4c08aed2011-07-01 19:47:50 +00004252static PixelInfo GetMinimumPixelList(PixelList *pixel_list)
cristy6fc86bb2011-03-18 23:45:16 +00004253{
cristy4c08aed2011-07-01 19:47:50 +00004254 PixelInfo
cristy6fc86bb2011-03-18 23:45:16 +00004255 pixel;
4256
4257 register SkipList
4258 *list;
4259
4260 register ssize_t
4261 channel;
4262
4263 size_t
cristyd76c51e2011-03-26 00:21:26 +00004264 color,
4265 minimum;
cristy6fc86bb2011-03-18 23:45:16 +00004266
cristy49f37242011-03-22 18:18:23 +00004267 ssize_t
4268 count;
4269
cristy6fc86bb2011-03-18 23:45:16 +00004270 unsigned short
cristyd76c51e2011-03-26 00:21:26 +00004271 channels[ListChannels];
cristy6fc86bb2011-03-18 23:45:16 +00004272
4273 /*
4274 Find the minimum value for each of the color.
4275 */
4276 for (channel=0; channel < 5; channel++)
4277 {
4278 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004279 count=0;
cristy6fc86bb2011-03-18 23:45:16 +00004280 color=65536UL;
cristy49f37242011-03-22 18:18:23 +00004281 minimum=list->nodes[color].next[0];
4282 do
4283 {
4284 color=list->nodes[color].next[0];
4285 if (color < minimum)
4286 minimum=color;
4287 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004288 } while (count < (ssize_t) pixel_list->length);
cristy49f37242011-03-22 18:18:23 +00004289 channels[channel]=(unsigned short) minimum;
cristy733678d2011-03-18 21:29:28 +00004290 }
cristy4c08aed2011-07-01 19:47:50 +00004291 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004292 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4293 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4294 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004295 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4296 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy733678d2011-03-18 21:29:28 +00004297 return(pixel);
4298}
4299
cristy4c08aed2011-07-01 19:47:50 +00004300static PixelInfo GetModePixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004301{
cristy4c08aed2011-07-01 19:47:50 +00004302 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004303 pixel;
4304
4305 register SkipList
4306 *list;
4307
4308 register ssize_t
4309 channel;
4310
4311 size_t
4312 color,
cristy733678d2011-03-18 21:29:28 +00004313 max_count,
cristy6fc86bb2011-03-18 23:45:16 +00004314 mode;
cristy733678d2011-03-18 21:29:28 +00004315
cristy49f37242011-03-22 18:18:23 +00004316 ssize_t
4317 count;
4318
cristy733678d2011-03-18 21:29:28 +00004319 unsigned short
4320 channels[5];
4321
4322 /*
glennrp30d2dc62011-06-25 03:17:16 +00004323 Make each pixel the 'predominant color' of the specified neighborhood.
cristy733678d2011-03-18 21:29:28 +00004324 */
cristy733678d2011-03-18 21:29:28 +00004325 for (channel=0; channel < 5; channel++)
4326 {
4327 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004328 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004329 mode=color;
4330 max_count=list->nodes[mode].count;
4331 count=0;
4332 do
4333 {
4334 color=list->nodes[color].next[0];
4335 if (list->nodes[color].count > max_count)
4336 {
4337 mode=color;
4338 max_count=list->nodes[mode].count;
4339 }
4340 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004341 } while (count < (ssize_t) pixel_list->length);
cristy733678d2011-03-18 21:29:28 +00004342 channels[channel]=(unsigned short) mode;
4343 }
cristy4c08aed2011-07-01 19:47:50 +00004344 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004345 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4346 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4347 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004348 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
4349 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
cristy733678d2011-03-18 21:29:28 +00004350 return(pixel);
4351}
4352
cristy4c08aed2011-07-01 19:47:50 +00004353static PixelInfo GetNonpeakPixelList(PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004354{
cristy4c08aed2011-07-01 19:47:50 +00004355 PixelInfo
cristy733678d2011-03-18 21:29:28 +00004356 pixel;
4357
4358 register SkipList
4359 *list;
4360
4361 register ssize_t
4362 channel;
4363
4364 size_t
cristy733678d2011-03-18 21:29:28 +00004365 color,
cristy733678d2011-03-18 21:29:28 +00004366 next,
4367 previous;
4368
cristy49f37242011-03-22 18:18:23 +00004369 ssize_t
4370 count;
4371
cristy733678d2011-03-18 21:29:28 +00004372 unsigned short
4373 channels[5];
4374
4375 /*
cristy49f37242011-03-22 18:18:23 +00004376 Finds the non peak value for each of the colors.
cristy733678d2011-03-18 21:29:28 +00004377 */
cristy733678d2011-03-18 21:29:28 +00004378 for (channel=0; channel < 5; channel++)
4379 {
4380 list=pixel_list->lists+channel;
cristy49f37242011-03-22 18:18:23 +00004381 color=65536L;
cristy733678d2011-03-18 21:29:28 +00004382 next=list->nodes[color].next[0];
4383 count=0;
4384 do
4385 {
4386 previous=color;
4387 color=next;
4388 next=list->nodes[color].next[0];
4389 count+=list->nodes[color].count;
cristyd76c51e2011-03-26 00:21:26 +00004390 } while (count <= (ssize_t) (pixel_list->length >> 1));
cristy733678d2011-03-18 21:29:28 +00004391 if ((previous == 65536UL) && (next != 65536UL))
4392 color=next;
4393 else
4394 if ((previous != 65536UL) && (next == 65536UL))
4395 color=previous;
4396 channels[channel]=(unsigned short) color;
4397 }
cristy4c08aed2011-07-01 19:47:50 +00004398 GetPixelInfo((const Image *) NULL,&pixel);
cristy733678d2011-03-18 21:29:28 +00004399 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4400 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4401 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004402 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4403 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy733678d2011-03-18 21:29:28 +00004404 return(pixel);
4405}
4406
cristy4c08aed2011-07-01 19:47:50 +00004407static PixelInfo GetStandardDeviationPixelList(PixelList *pixel_list)
cristy9a68cbb2011-03-29 00:51:23 +00004408{
cristy4c08aed2011-07-01 19:47:50 +00004409 PixelInfo
cristy9a68cbb2011-03-29 00:51:23 +00004410 pixel;
4411
cristy80a99a32011-03-30 01:30:23 +00004412 MagickRealType
4413 sum,
4414 sum_squared;
4415
cristy9a68cbb2011-03-29 00:51:23 +00004416 register SkipList
4417 *list;
4418
4419 register ssize_t
4420 channel;
4421
4422 size_t
cristy80a99a32011-03-30 01:30:23 +00004423 color;
cristy9a68cbb2011-03-29 00:51:23 +00004424
4425 ssize_t
4426 count;
4427
4428 unsigned short
4429 channels[ListChannels];
4430
4431 /*
cristy80a99a32011-03-30 01:30:23 +00004432 Find the standard-deviation value for each of the color.
cristy9a68cbb2011-03-29 00:51:23 +00004433 */
4434 for (channel=0; channel < 5; channel++)
4435 {
4436 list=pixel_list->lists+channel;
4437 color=65536L;
4438 count=0;
cristy80a99a32011-03-30 01:30:23 +00004439 sum=0.0;
4440 sum_squared=0.0;
cristy9a68cbb2011-03-29 00:51:23 +00004441 do
4442 {
cristy80a99a32011-03-30 01:30:23 +00004443 register ssize_t
4444 i;
4445
cristy9a68cbb2011-03-29 00:51:23 +00004446 color=list->nodes[color].next[0];
cristy80a99a32011-03-30 01:30:23 +00004447 sum+=(MagickRealType) list->nodes[color].count*color;
4448 for (i=0; i < (ssize_t) list->nodes[color].count; i++)
4449 sum_squared+=((MagickRealType) color)*((MagickRealType) color);
cristy9a68cbb2011-03-29 00:51:23 +00004450 count+=list->nodes[color].count;
4451 } while (count < (ssize_t) pixel_list->length);
cristy80a99a32011-03-30 01:30:23 +00004452 sum/=pixel_list->length;
4453 sum_squared/=pixel_list->length;
4454 channels[channel]=(unsigned short) sqrt(sum_squared-(sum*sum));
cristy9a68cbb2011-03-29 00:51:23 +00004455 }
cristy4c08aed2011-07-01 19:47:50 +00004456 GetPixelInfo((const Image *) NULL,&pixel);
cristy9a68cbb2011-03-29 00:51:23 +00004457 pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
4458 pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
4459 pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
cristy4c08aed2011-07-01 19:47:50 +00004460 pixel.alpha=(MagickRealType) ScaleShortToQuantum(channels[3]);
4461 pixel.black=(MagickRealType) ScaleShortToQuantum(channels[4]);
cristy9a68cbb2011-03-29 00:51:23 +00004462 return(pixel);
4463}
4464
cristy4c08aed2011-07-01 19:47:50 +00004465static inline void InsertPixelList(const Image *image,const Quantum *pixel,
4466 PixelList *pixel_list)
cristy733678d2011-03-18 21:29:28 +00004467{
4468 size_t
4469 signature;
4470
4471 unsigned short
4472 index;
4473
cristy4c08aed2011-07-01 19:47:50 +00004474 index=ScaleQuantumToShort(GetPixelRed(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004475 signature=pixel_list->lists[0].nodes[index].signature;
4476 if (signature == pixel_list->signature)
4477 pixel_list->lists[0].nodes[index].count++;
4478 else
4479 AddNodePixelList(pixel_list,0,index);
cristy4c08aed2011-07-01 19:47:50 +00004480 index=ScaleQuantumToShort(GetPixelGreen(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004481 signature=pixel_list->lists[1].nodes[index].signature;
4482 if (signature == pixel_list->signature)
4483 pixel_list->lists[1].nodes[index].count++;
4484 else
4485 AddNodePixelList(pixel_list,1,index);
cristy4c08aed2011-07-01 19:47:50 +00004486 index=ScaleQuantumToShort(GetPixelBlue(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004487 signature=pixel_list->lists[2].nodes[index].signature;
4488 if (signature == pixel_list->signature)
4489 pixel_list->lists[2].nodes[index].count++;
4490 else
4491 AddNodePixelList(pixel_list,2,index);
cristy4c08aed2011-07-01 19:47:50 +00004492 index=ScaleQuantumToShort(GetPixelAlpha(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004493 signature=pixel_list->lists[3].nodes[index].signature;
4494 if (signature == pixel_list->signature)
4495 pixel_list->lists[3].nodes[index].count++;
4496 else
4497 AddNodePixelList(pixel_list,3,index);
4498 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004499 index=ScaleQuantumToShort(GetPixelBlack(image,pixel));
cristy733678d2011-03-18 21:29:28 +00004500 signature=pixel_list->lists[4].nodes[index].signature;
4501 if (signature == pixel_list->signature)
4502 pixel_list->lists[4].nodes[index].count++;
4503 else
4504 AddNodePixelList(pixel_list,4,index);
4505}
4506
cristy80c99742011-04-04 14:46:39 +00004507static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
4508{
4509 if (x < 0)
4510 return(-x);
4511 return(x);
4512}
4513
cristy733678d2011-03-18 21:29:28 +00004514static void ResetPixelList(PixelList *pixel_list)
4515{
4516 int
4517 level;
4518
4519 register ListNode
4520 *root;
4521
4522 register SkipList
4523 *list;
4524
4525 register ssize_t
4526 channel;
4527
4528 /*
4529 Reset the skip-list.
4530 */
4531 for (channel=0; channel < 5; channel++)
4532 {
4533 list=pixel_list->lists+channel;
4534 root=list->nodes+65536UL;
4535 list->level=0;
4536 for (level=0; level < 9; level++)
4537 root->next[level]=65536UL;
4538 }
4539 pixel_list->seed=pixel_list->signature++;
4540}
4541
cristy0834d642011-03-18 18:26:08 +00004542MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
cristy95c38342011-03-18 22:39:51 +00004543 const size_t width,const size_t height,ExceptionInfo *exception)
cristy0834d642011-03-18 18:26:08 +00004544{
cristy3cba8ca2011-03-19 01:29:12 +00004545#define StatisticWidth \
cristyd76c51e2011-03-26 00:21:26 +00004546 (width == 0 ? GetOptimalKernelWidth2D((double) width,0.5) : width)
cristy3cba8ca2011-03-19 01:29:12 +00004547#define StatisticHeight \
cristyd76c51e2011-03-26 00:21:26 +00004548 (height == 0 ? GetOptimalKernelWidth2D((double) height,0.5) : height)
cristy0834d642011-03-18 18:26:08 +00004549#define StatisticImageTag "Statistic/Image"
4550
4551 CacheView
4552 *image_view,
4553 *statistic_view;
4554
4555 Image
4556 *statistic_image;
4557
4558 MagickBooleanType
4559 status;
4560
4561 MagickOffsetType
4562 progress;
4563
4564 PixelList
4565 **restrict pixel_list;
4566
cristy0834d642011-03-18 18:26:08 +00004567 ssize_t
4568 y;
4569
4570 /*
4571 Initialize statistics image attributes.
4572 */
4573 assert(image != (Image *) NULL);
4574 assert(image->signature == MagickSignature);
4575 if (image->debug != MagickFalse)
4576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4577 assert(exception != (ExceptionInfo *) NULL);
4578 assert(exception->signature == MagickSignature);
cristy0834d642011-03-18 18:26:08 +00004579 statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4580 exception);
4581 if (statistic_image == (Image *) NULL)
4582 return((Image *) NULL);
4583 if (SetImageStorageClass(statistic_image,DirectClass) == MagickFalse)
4584 {
4585 InheritException(exception,&statistic_image->exception);
4586 statistic_image=DestroyImage(statistic_image);
4587 return((Image *) NULL);
4588 }
cristy6fc86bb2011-03-18 23:45:16 +00004589 pixel_list=AcquirePixelListThreadSet(StatisticWidth,StatisticHeight);
cristy0834d642011-03-18 18:26:08 +00004590 if (pixel_list == (PixelList **) NULL)
4591 {
4592 statistic_image=DestroyImage(statistic_image);
4593 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4594 }
4595 /*
cristy8d752042011-03-19 01:00:36 +00004596 Make each pixel the min / max / median / mode / etc. of the neighborhood.
cristy0834d642011-03-18 18:26:08 +00004597 */
4598 status=MagickTrue;
4599 progress=0;
4600 image_view=AcquireCacheView(image);
4601 statistic_view=AcquireCacheView(statistic_image);
4602#if defined(MAGICKCORE_OPENMP_SUPPORT)
4603 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4604#endif
4605 for (y=0; y < (ssize_t) statistic_image->rows; y++)
4606 {
4607 const int
4608 id = GetOpenMPThreadId();
4609
cristy4c08aed2011-07-01 19:47:50 +00004610 register const Quantum
cristy0834d642011-03-18 18:26:08 +00004611 *restrict p;
4612
cristy4c08aed2011-07-01 19:47:50 +00004613 register Quantum
cristy0834d642011-03-18 18:26:08 +00004614 *restrict q;
4615
4616 register ssize_t
4617 x;
4618
4619 if (status == MagickFalse)
4620 continue;
cristy6fc86bb2011-03-18 23:45:16 +00004621 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) StatisticWidth/2L),y-
4622 (ssize_t) (StatisticHeight/2L),image->columns+StatisticWidth,
4623 StatisticHeight,exception);
cristy3cba8ca2011-03-19 01:29:12 +00004624 q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004625 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy0834d642011-03-18 18:26:08 +00004626 {
4627 status=MagickFalse;
4628 continue;
4629 }
cristy0834d642011-03-18 18:26:08 +00004630 for (x=0; x < (ssize_t) statistic_image->columns; x++)
4631 {
cristy4c08aed2011-07-01 19:47:50 +00004632 PixelInfo
cristy0834d642011-03-18 18:26:08 +00004633 pixel;
4634
cristy4c08aed2011-07-01 19:47:50 +00004635 register const Quantum
cristy6e3026a2011-03-19 00:54:38 +00004636 *restrict r;
4637
cristy0834d642011-03-18 18:26:08 +00004638 register ssize_t
4639 u,
4640 v;
4641
4642 r=p;
cristy0834d642011-03-18 18:26:08 +00004643 ResetPixelList(pixel_list[id]);
cristy6e4c3292011-03-19 00:53:55 +00004644 for (v=0; v < (ssize_t) StatisticHeight; v++)
cristy0834d642011-03-18 18:26:08 +00004645 {
cristy6e4c3292011-03-19 00:53:55 +00004646 for (u=0; u < (ssize_t) StatisticWidth; u++)
cristyed231572011-07-14 02:18:59 +00004647 InsertPixelList(image,r+u*GetPixelChannels(image),pixel_list[id]);
4648 r+=(image->columns+StatisticWidth)*GetPixelChannels(image);
cristy0834d642011-03-18 18:26:08 +00004649 }
cristy4c08aed2011-07-01 19:47:50 +00004650 GetPixelInfo(image,&pixel);
cristy490408a2011-07-07 14:42:05 +00004651 SetPixelInfo(image,p+(StatisticWidth*StatisticHeight/2)*
cristyed231572011-07-14 02:18:59 +00004652 GetPixelChannels(image),&pixel);
cristy0834d642011-03-18 18:26:08 +00004653 switch (type)
4654 {
cristy80c99742011-04-04 14:46:39 +00004655 case GradientStatistic:
4656 {
cristy4c08aed2011-07-01 19:47:50 +00004657 PixelInfo
cristy80c99742011-04-04 14:46:39 +00004658 maximum,
4659 minimum;
4660
4661 minimum=GetMinimumPixelList(pixel_list[id]);
4662 maximum=GetMaximumPixelList(pixel_list[id]);
4663 pixel.red=MagickAbsoluteValue(maximum.red-minimum.red);
4664 pixel.green=MagickAbsoluteValue(maximum.green-minimum.green);
4665 pixel.blue=MagickAbsoluteValue(maximum.blue-minimum.blue);
cristy4c08aed2011-07-01 19:47:50 +00004666 pixel.alpha=MagickAbsoluteValue(maximum.alpha-minimum.alpha);
cristy80c99742011-04-04 14:46:39 +00004667 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00004668 pixel.black=MagickAbsoluteValue(maximum.black-minimum.black);
cristy80c99742011-04-04 14:46:39 +00004669 break;
4670 }
cristy6fc86bb2011-03-18 23:45:16 +00004671 case MaximumStatistic:
4672 {
4673 pixel=GetMaximumPixelList(pixel_list[id]);
4674 break;
4675 }
cristy49f37242011-03-22 18:18:23 +00004676 case MeanStatistic:
4677 {
4678 pixel=GetMeanPixelList(pixel_list[id]);
4679 break;
4680 }
cristyf2ad14a2011-03-18 18:57:25 +00004681 case MedianStatistic:
cristy6fc86bb2011-03-18 23:45:16 +00004682 default:
cristyf2ad14a2011-03-18 18:57:25 +00004683 {
4684 pixel=GetMedianPixelList(pixel_list[id]);
4685 break;
4686 }
cristy6fc86bb2011-03-18 23:45:16 +00004687 case MinimumStatistic:
4688 {
4689 pixel=GetMinimumPixelList(pixel_list[id]);
4690 break;
4691 }
cristyf2ad14a2011-03-18 18:57:25 +00004692 case ModeStatistic:
4693 {
4694 pixel=GetModePixelList(pixel_list[id]);
4695 break;
4696 }
4697 case NonpeakStatistic:
4698 {
4699 pixel=GetNonpeakPixelList(pixel_list[id]);
4700 break;
4701 }
cristy9a68cbb2011-03-29 00:51:23 +00004702 case StandardDeviationStatistic:
4703 {
4704 pixel=GetStandardDeviationPixelList(pixel_list[id]);
4705 break;
4706 }
cristy0834d642011-03-18 18:26:08 +00004707 }
cristyed231572011-07-14 02:18:59 +00004708 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004709 SetPixelRed(statistic_image,ClampToQuantum(pixel.red),q);
cristyed231572011-07-14 02:18:59 +00004710 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004711 SetPixelGreen(statistic_image,ClampToQuantum(pixel.green),q);
cristyed231572011-07-14 02:18:59 +00004712 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy490408a2011-07-07 14:42:05 +00004713 SetPixelBlue(statistic_image,ClampToQuantum(pixel.blue),q);
cristyed231572011-07-14 02:18:59 +00004714 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy0834d642011-03-18 18:26:08 +00004715 (image->colorspace == CMYKColorspace))
cristy490408a2011-07-07 14:42:05 +00004716 SetPixelBlack(statistic_image,ClampToQuantum(pixel.black),q);
cristyed231572011-07-14 02:18:59 +00004717 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00004718 (image->matte != MagickFalse))
cristy490408a2011-07-07 14:42:05 +00004719 SetPixelAlpha(statistic_image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +00004720 p+=GetPixelChannels(image);
4721 q+=GetPixelChannels(statistic_image);
cristy0834d642011-03-18 18:26:08 +00004722 }
4723 if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
4724 status=MagickFalse;
4725 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4726 {
4727 MagickBooleanType
4728 proceed;
4729
4730#if defined(MAGICKCORE_OPENMP_SUPPORT)
4731 #pragma omp critical (MagickCore_StatisticImage)
4732#endif
4733 proceed=SetImageProgress(image,StatisticImageTag,progress++,
4734 image->rows);
4735 if (proceed == MagickFalse)
4736 status=MagickFalse;
4737 }
4738 }
4739 statistic_view=DestroyCacheView(statistic_view);
4740 image_view=DestroyCacheView(image_view);
4741 pixel_list=DestroyPixelListThreadSet(pixel_list);
4742 return(statistic_image);
4743}
4744
4745/*
4746%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4747% %
4748% %
4749% %
cristy3ed852e2009-09-05 21:47:34 +00004750% U n s h a r p M a s k I m a g e %
4751% %
4752% %
4753% %
4754%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4755%
4756% UnsharpMaskImage() sharpens one or more image channels. We convolve the
4757% image with a Gaussian operator of the given radius and standard deviation
4758% (sigma). For reasonable results, radius should be larger than sigma. Use a
4759% radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4760%
4761% The format of the UnsharpMaskImage method is:
4762%
4763% Image *UnsharpMaskImage(const Image *image,const double radius,
4764% const double sigma,const double amount,const double threshold,
4765% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004766%
4767% A description of each parameter follows:
4768%
4769% o image: the image.
4770%
cristy3ed852e2009-09-05 21:47:34 +00004771% o radius: the radius of the Gaussian, in pixels, not counting the center
4772% pixel.
4773%
4774% o sigma: the standard deviation of the Gaussian, in pixels.
4775%
4776% o amount: the percentage of the difference between the original and the
4777% blur image that is added back into the original.
4778%
4779% o threshold: the threshold in pixels needed to apply the diffence amount.
4780%
4781% o exception: return any errors or warnings in this structure.
4782%
4783*/
cristyf4ad9df2011-07-08 16:49:03 +00004784MagickExport Image *UnsharpMaskImage(const Image *image,
4785 const double radius,const double sigma,const double amount,
4786 const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00004787{
4788#define SharpenImageTag "Sharpen/Image"
4789
cristyc4c8d132010-01-07 01:58:38 +00004790 CacheView
4791 *image_view,
4792 *unsharp_view;
4793
cristy3ed852e2009-09-05 21:47:34 +00004794 Image
4795 *unsharp_image;
4796
cristy3ed852e2009-09-05 21:47:34 +00004797 MagickBooleanType
4798 status;
4799
cristybb503372010-05-27 20:51:26 +00004800 MagickOffsetType
4801 progress;
4802
cristy4c08aed2011-07-01 19:47:50 +00004803 PixelInfo
cristyddd82202009-11-03 20:14:50 +00004804 bias;
cristy3ed852e2009-09-05 21:47:34 +00004805
4806 MagickRealType
4807 quantum_threshold;
4808
cristybb503372010-05-27 20:51:26 +00004809 ssize_t
4810 y;
4811
cristy3ed852e2009-09-05 21:47:34 +00004812 assert(image != (const Image *) NULL);
4813 assert(image->signature == MagickSignature);
4814 if (image->debug != MagickFalse)
4815 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4816 assert(exception != (ExceptionInfo *) NULL);
cristyf4ad9df2011-07-08 16:49:03 +00004817 unsharp_image=BlurImage(image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +00004818 if (unsharp_image == (Image *) NULL)
4819 return((Image *) NULL);
4820 quantum_threshold=(MagickRealType) QuantumRange*threshold;
4821 /*
4822 Unsharp-mask image.
4823 */
4824 status=MagickTrue;
4825 progress=0;
cristy4c08aed2011-07-01 19:47:50 +00004826 GetPixelInfo(image,&bias);
cristy3ed852e2009-09-05 21:47:34 +00004827 image_view=AcquireCacheView(image);
4828 unsharp_view=AcquireCacheView(unsharp_image);
cristyb5d5f722009-11-04 03:03:49 +00004829#if defined(MAGICKCORE_OPENMP_SUPPORT)
4830 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00004831#endif
cristybb503372010-05-27 20:51:26 +00004832 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004833 {
cristy4c08aed2011-07-01 19:47:50 +00004834 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004835 pixel;
4836
cristy4c08aed2011-07-01 19:47:50 +00004837 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00004838 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00004839
cristy4c08aed2011-07-01 19:47:50 +00004840 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00004841 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00004842
cristy117ff172010-08-15 21:35:32 +00004843 register ssize_t
4844 x;
4845
cristy3ed852e2009-09-05 21:47:34 +00004846 if (status == MagickFalse)
4847 continue;
4848 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4849 q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4850 exception);
cristy4c08aed2011-07-01 19:47:50 +00004851 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00004852 {
4853 status=MagickFalse;
4854 continue;
4855 }
cristyddd82202009-11-03 20:14:50 +00004856 pixel=bias;
cristybb503372010-05-27 20:51:26 +00004857 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00004858 {
cristyed231572011-07-14 02:18:59 +00004859 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004860 {
cristy4c08aed2011-07-01 19:47:50 +00004861 pixel.red=GetPixelRed(image,p)-(MagickRealType) GetPixelRed(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004862 if (fabs(2.0*pixel.red) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004863 pixel.red=(MagickRealType) GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004864 else
cristy4c08aed2011-07-01 19:47:50 +00004865 pixel.red=(MagickRealType) GetPixelRed(image,p)+(pixel.red*amount);
4866 SetPixelRed(unsharp_image,ClampToQuantum(pixel.red),q);
cristy3ed852e2009-09-05 21:47:34 +00004867 }
cristyed231572011-07-14 02:18:59 +00004868 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004869 {
cristy4c08aed2011-07-01 19:47:50 +00004870 pixel.green=GetPixelGreen(image,p)-
4871 (MagickRealType) GetPixelGreen(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004872 if (fabs(2.0*pixel.green) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004873 pixel.green=(MagickRealType)
4874 GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004875 else
cristy4c08aed2011-07-01 19:47:50 +00004876 pixel.green=(MagickRealType)
4877 GetPixelGreen(image,p)+
4878 (pixel.green*amount);
4879 SetPixelGreen(unsharp_image,
4880 ClampToQuantum(pixel.green),q);
cristy3ed852e2009-09-05 21:47:34 +00004881 }
cristyed231572011-07-14 02:18:59 +00004882 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004883 {
cristy4c08aed2011-07-01 19:47:50 +00004884 pixel.blue=GetPixelBlue(image,p)-
4885 (MagickRealType) GetPixelBlue(image,q);
cristy3ed852e2009-09-05 21:47:34 +00004886 if (fabs(2.0*pixel.blue) < quantum_threshold)
cristy4c08aed2011-07-01 19:47:50 +00004887 pixel.blue=(MagickRealType)
4888 GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004889 else
cristy4c08aed2011-07-01 19:47:50 +00004890 pixel.blue=(MagickRealType)
4891 GetPixelBlue(image,p)+(pixel.blue*amount);
4892 SetPixelBlue(unsharp_image,
4893 ClampToQuantum(pixel.blue),q);
cristy3ed852e2009-09-05 21:47:34 +00004894 }
cristyed231572011-07-14 02:18:59 +00004895 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00004896 (image->colorspace == CMYKColorspace))
4897 {
cristy4c08aed2011-07-01 19:47:50 +00004898 pixel.black=GetPixelBlack(image,p)-
4899 (MagickRealType) GetPixelBlack(image,q);
4900 if (fabs(2.0*pixel.black) < quantum_threshold)
4901 pixel.black=(MagickRealType)
4902 GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +00004903 else
cristy4c08aed2011-07-01 19:47:50 +00004904 pixel.black=(MagickRealType)
4905 GetPixelBlack(image,p)+(pixel.black*
4906 amount);
4907 SetPixelBlack(unsharp_image,
4908 ClampToQuantum(pixel.black),q);
cristy3ed852e2009-09-05 21:47:34 +00004909 }
cristyed231572011-07-14 02:18:59 +00004910 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00004911 {
4912 pixel.alpha=GetPixelAlpha(image,p)-
4913 (MagickRealType) GetPixelAlpha(image,q);
4914 if (fabs(2.0*pixel.alpha) < quantum_threshold)
4915 pixel.alpha=(MagickRealType)
4916 GetPixelAlpha(image,p);
4917 else
4918 pixel.alpha=GetPixelAlpha(image,p)+
4919 (pixel.alpha*amount);
4920 SetPixelAlpha(unsharp_image,
4921 ClampToQuantum(pixel.alpha),q);
4922 }
cristyed231572011-07-14 02:18:59 +00004923 p+=GetPixelChannels(image);
4924 q+=GetPixelChannels(unsharp_image);
cristy3ed852e2009-09-05 21:47:34 +00004925 }
4926 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4927 status=MagickFalse;
4928 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4929 {
4930 MagickBooleanType
4931 proceed;
4932
cristyb5d5f722009-11-04 03:03:49 +00004933#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00004934 #pragma omp critical (MagickCore_UnsharpMaskImage)
cristy3ed852e2009-09-05 21:47:34 +00004935#endif
4936 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4937 if (proceed == MagickFalse)
4938 status=MagickFalse;
4939 }
4940 }
4941 unsharp_image->type=image->type;
4942 unsharp_view=DestroyCacheView(unsharp_view);
4943 image_view=DestroyCacheView(image_view);
4944 if (status == MagickFalse)
4945 unsharp_image=DestroyImage(unsharp_image);
4946 return(unsharp_image);
4947}