blob: 6e49520bb2af7276018feceb92f35325b866e027 [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% October 1996 %
18% %
19% %
cristyb56bb242014-11-25 17:12:48 +000020% Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/accelerate.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/decorate.h"
cristyc53413d2011-11-17 13:04:26 +000052#include "MagickCore/distort.h"
cristy4c08aed2011-07-01 19:47:50 +000053#include "MagickCore/draw.h"
54#include "MagickCore/enhance.h"
55#include "MagickCore/exception.h"
56#include "MagickCore/exception-private.h"
57#include "MagickCore/effect.h"
58#include "MagickCore/fx.h"
59#include "MagickCore/gem.h"
cristy8ea81222011-09-04 10:33:32 +000060#include "MagickCore/gem-private.h"
cristy4c08aed2011-07-01 19:47:50 +000061#include "MagickCore/geometry.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
cristydde3f7a2014-04-04 00:55:24 +000065#include "MagickCore/matrix.h"
cristy4c08aed2011-07-01 19:47:50 +000066#include "MagickCore/memory_.h"
cristye42639a2012-08-23 01:53:24 +000067#include "MagickCore/memory-private.h"
cristy4c08aed2011-07-01 19:47:50 +000068#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/montage.h"
71#include "MagickCore/morphology.h"
cristy7f391ce2013-03-27 11:03:11 +000072#include "MagickCore/morphology-private.h"
cristy4c08aed2011-07-01 19:47:50 +000073#include "MagickCore/paint.h"
74#include "MagickCore/pixel-accessor.h"
cristy1d1b10f2012-06-02 20:02:55 +000075#include "MagickCore/pixel-private.h"
cristy4c08aed2011-07-01 19:47:50 +000076#include "MagickCore/property.h"
77#include "MagickCore/quantize.h"
78#include "MagickCore/quantum.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/random_.h"
81#include "MagickCore/random-private.h"
82#include "MagickCore/resample.h"
83#include "MagickCore/resample-private.h"
84#include "MagickCore/resize.h"
85#include "MagickCore/resource_.h"
86#include "MagickCore/segment.h"
cristy31bbf2f2011-11-17 13:19:37 +000087#include "MagickCore/shear.h"
cristy4c08aed2011-07-01 19:47:50 +000088#include "MagickCore/signature-private.h"
cristy99bd5232011-12-07 14:38:20 +000089#include "MagickCore/statistic.h"
cristy4c08aed2011-07-01 19:47:50 +000090#include "MagickCore/string_.h"
91#include "MagickCore/thread-private.h"
92#include "MagickCore/transform.h"
93#include "MagickCore/threshold.h"
cristy3ed852e2009-09-05 21:47:34 +000094
95/*
96%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97% %
98% %
99% %
100% A d a p t i v e B l u r I m a g e %
101% %
102% %
103% %
104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105%
106% AdaptiveBlurImage() adaptively blurs the image by blurring less
107% intensely near image edges and more intensely far from edges. We blur the
108% image with a Gaussian operator of the given radius and standard deviation
109% (sigma). For reasonable results, radius should be larger than sigma. Use a
110% radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
111%
112% The format of the AdaptiveBlurImage method is:
113%
114% Image *AdaptiveBlurImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +0000115% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000116%
117% A description of each parameter follows:
118%
119% o image: the image.
120%
cristy3ed852e2009-09-05 21:47:34 +0000121% o radius: the radius of the Gaussian, in pixels, not counting the center
122% pixel.
123%
124% o sigma: the standard deviation of the Laplacian, in pixels.
125%
126% o exception: return any errors or warnings in this structure.
127%
128*/
cristy4282c702011-11-21 00:01:06 +0000129MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +0000130 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000131{
132#define AdaptiveBlurImageTag "Convolve/Image"
cristy9b528342012-06-02 00:59:20 +0000133#define MagickSigma (fabs(sigma) < MagickEpsilon ? MagickEpsilon : sigma)
cristy3ed852e2009-09-05 21:47:34 +0000134
cristyc4c8d132010-01-07 01:58:38 +0000135 CacheView
136 *blur_view,
137 *edge_view,
138 *image_view;
139
cristyf4bae2c2012-10-17 12:19:38 +0000140 double
141 normalize;
142
cristy3ed852e2009-09-05 21:47:34 +0000143 Image
144 *blur_image,
145 *edge_image,
146 *gaussian_image;
147
cristy3ed852e2009-09-05 21:47:34 +0000148 MagickBooleanType
149 status;
150
cristybb503372010-05-27 20:51:26 +0000151 MagickOffsetType
152 progress;
153
cristy0a887dc2012-08-15 22:58:36 +0000154 MagickRealType
cristyf4bae2c2012-10-17 12:19:38 +0000155 **kernel;
cristy0a887dc2012-08-15 22:58:36 +0000156
cristybb503372010-05-27 20:51:26 +0000157 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000158 i;
cristy3ed852e2009-09-05 21:47:34 +0000159
cristybb503372010-05-27 20:51:26 +0000160 size_t
cristy3ed852e2009-09-05 21:47:34 +0000161 width;
162
cristybb503372010-05-27 20:51:26 +0000163 ssize_t
164 j,
165 k,
166 u,
167 v,
168 y;
169
cristy3ed852e2009-09-05 21:47:34 +0000170 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000171 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000172 if (image->debug != MagickFalse)
173 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
174 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000175 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000176 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
177 if (blur_image == (Image *) NULL)
178 return((Image *) NULL);
cristy3d2287c2012-06-04 21:26:19 +0000179 if (fabs(sigma) < MagickEpsilon)
cristy3ed852e2009-09-05 21:47:34 +0000180 return(blur_image);
cristy574cc262011-08-05 01:23:58 +0000181 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000182 {
cristy3ed852e2009-09-05 21:47:34 +0000183 blur_image=DestroyImage(blur_image);
184 return((Image *) NULL);
185 }
186 /*
187 Edge detect the image brighness channel, level, blur, and level again.
188 */
cristy9dc4c512013-03-24 01:38:00 +0000189 edge_image=EdgeImage(image,radius,exception);
cristy3ed852e2009-09-05 21:47:34 +0000190 if (edge_image == (Image *) NULL)
191 {
192 blur_image=DestroyImage(blur_image);
193 return((Image *) NULL);
194 }
cristyd8dd86c2014-05-28 10:58:46 +0000195 (void) AutoLevelImage(edge_image,exception);
cristyb202ff32013-03-24 15:16:46 +0000196 gaussian_image=BlurImage(edge_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000197 if (gaussian_image != (Image *) NULL)
198 {
199 edge_image=DestroyImage(edge_image);
200 edge_image=gaussian_image;
201 }
cristyd8dd86c2014-05-28 10:58:46 +0000202 (void) AutoLevelImage(edge_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000203 /*
204 Create a set of kernels from maximum (radius,sigma) to minimum.
205 */
206 width=GetOptimalKernelWidth2D(radius,sigma);
cristye42639a2012-08-23 01:53:24 +0000207 kernel=(MagickRealType **) MagickAssumeAligned(AcquireAlignedMemory((size_t)
208 width,sizeof(*kernel)));
cristy0a887dc2012-08-15 22:58:36 +0000209 if (kernel == (MagickRealType **) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000210 {
211 edge_image=DestroyImage(edge_image);
212 blur_image=DestroyImage(blur_image);
213 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
214 }
215 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
cristybb503372010-05-27 20:51:26 +0000216 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000217 {
cristye42639a2012-08-23 01:53:24 +0000218 kernel[i]=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
219 (size_t) (width-i),(width-i)*sizeof(**kernel)));
cristy0a887dc2012-08-15 22:58:36 +0000220 if (kernel[i] == (MagickRealType *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000221 break;
cristy47e00502009-12-17 19:19:57 +0000222 normalize=0.0;
cristy6eec5202013-04-16 23:33:08 +0000223 j=(ssize_t) (width-i-1)/2;
cristy47e00502009-12-17 19:19:57 +0000224 k=0;
225 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +0000226 {
cristy47e00502009-12-17 19:19:57 +0000227 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +0000228 {
cristy0a887dc2012-08-15 22:58:36 +0000229 kernel[i][k]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
cristy4205a3c2010-09-12 20:19:59 +0000230 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +0000231 normalize+=kernel[i][k];
232 k++;
cristy3ed852e2009-09-05 21:47:34 +0000233 }
234 }
cristy6eec5202013-04-16 23:33:08 +0000235 kernel[i][(j-1)/2]+=(1.0-normalize);
236 if (sigma < MagickEpsilon)
237 kernel[i][(j-1)/2]=1.0;
cristy3ed852e2009-09-05 21:47:34 +0000238 }
cristybb503372010-05-27 20:51:26 +0000239 if (i < (ssize_t) width)
cristy3ed852e2009-09-05 21:47:34 +0000240 {
241 for (i-=2; i >= 0; i-=2)
cristy0a887dc2012-08-15 22:58:36 +0000242 kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
243 kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +0000244 edge_image=DestroyImage(edge_image);
245 blur_image=DestroyImage(blur_image);
246 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
247 }
248 /*
249 Adaptively blur image.
250 */
251 status=MagickTrue;
252 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000253 image_view=AcquireVirtualCacheView(image,exception);
254 edge_view=AcquireVirtualCacheView(edge_image,exception);
255 blur_view=AcquireAuthenticCacheView(blur_image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000256#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000257 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000258 magick_threads(image,blur_image,blur_image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000259#endif
cristybb503372010-05-27 20:51:26 +0000260 for (y=0; y < (ssize_t) blur_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000261 {
cristy4c08aed2011-07-01 19:47:50 +0000262 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000263 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +0000264
cristy4c08aed2011-07-01 19:47:50 +0000265 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000266 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000267
cristy117ff172010-08-15 21:35:32 +0000268 register ssize_t
269 x;
270
cristy3ed852e2009-09-05 21:47:34 +0000271 if (status == MagickFalse)
272 continue;
273 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
274 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
275 exception);
cristyacd2ed22011-08-30 01:44:23 +0000276 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000277 {
278 status=MagickFalse;
279 continue;
280 }
cristybb503372010-05-27 20:51:26 +0000281 for (x=0; x < (ssize_t) blur_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000282 {
cristy4c11c2b2011-09-05 20:17:07 +0000283 register const Quantum
284 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000285
cristybb503372010-05-27 20:51:26 +0000286 register ssize_t
cristy4c11c2b2011-09-05 20:17:07 +0000287 i;
cristy3ed852e2009-09-05 21:47:34 +0000288
cristy4c11c2b2011-09-05 20:17:07 +0000289 ssize_t
290 center,
291 j;
292
293 j=(ssize_t) ceil((double) width*QuantumScale*
cristy4c08aed2011-07-01 19:47:50 +0000294 GetPixelIntensity(edge_image,r)-0.5);
cristy4c11c2b2011-09-05 20:17:07 +0000295 if (j < 0)
296 j=0;
cristy3ed852e2009-09-05 21:47:34 +0000297 else
cristy4c11c2b2011-09-05 20:17:07 +0000298 if (j > (ssize_t) width)
299 j=(ssize_t) width;
300 if ((j & 0x01) != 0)
301 j--;
302 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
303 (ssize_t) ((width-j)/2L),width-j,width-j,exception);
cristy4c08aed2011-07-01 19:47:50 +0000304 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000305 break;
cristyd09f8802012-02-04 16:44:10 +0000306 center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
307 GetPixelChannels(image)*((width-j)/2L);
cristy4c11c2b2011-09-05 20:17:07 +0000308 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000309 {
cristya19f1d72012-08-07 18:24:38 +0000310 double
cristy4c11c2b2011-09-05 20:17:07 +0000311 alpha,
312 gamma,
313 pixel;
314
cristy633067b2013-02-21 01:15:12 +0000315 PixelChannel
316 channel;
317
318 PixelTrait
319 blur_traits,
320 traits;
321
cristy0a887dc2012-08-15 22:58:36 +0000322 register const MagickRealType
cristy4c11c2b2011-09-05 20:17:07 +0000323 *restrict k;
324
325 register const Quantum
326 *restrict pixels;
327
328 register ssize_t
329 u;
330
331 ssize_t
332 v;
333
cristy633067b2013-02-21 01:15:12 +0000334 channel=GetPixelChannelChannel(image,i);
335 traits=GetPixelChannelTraits(image,channel);
336 blur_traits=GetPixelChannelTraits(blur_image,channel);
cristy4c11c2b2011-09-05 20:17:07 +0000337 if ((traits == UndefinedPixelTrait) ||
338 (blur_traits == UndefinedPixelTrait))
339 continue;
cristy1eced092012-08-10 23:10:56 +0000340 if (((blur_traits & CopyPixelTrait) != 0) ||
cristy883fde12013-04-08 00:50:13 +0000341 (GetPixelReadMask(image,p+center) == 0))
cristy4c11c2b2011-09-05 20:17:07 +0000342 {
cristy0beccfa2011-09-25 20:47:53 +0000343 SetPixelChannel(blur_image,channel,p[center+i],q);
cristy4c11c2b2011-09-05 20:17:07 +0000344 continue;
345 }
346 k=kernel[j];
347 pixels=p;
cristyaa2c16c2012-03-25 22:21:35 +0000348 pixel=0.0;
cristy4c11c2b2011-09-05 20:17:07 +0000349 gamma=0.0;
cristy633067b2013-02-21 01:15:12 +0000350 if ((blur_traits & BlendPixelTrait) == 0)
cristy4c11c2b2011-09-05 20:17:07 +0000351 {
352 /*
353 No alpha blending.
354 */
355 for (v=0; v < (ssize_t) (width-j); v++)
356 {
357 for (u=0; u < (ssize_t) (width-j); u++)
358 {
359 pixel+=(*k)*pixels[i];
360 gamma+=(*k);
361 k++;
362 pixels+=GetPixelChannels(image);
363 }
364 }
cristy3e3ec3a2012-11-03 23:11:06 +0000365 gamma=PerceptibleReciprocal(gamma);
cristy0beccfa2011-09-25 20:47:53 +0000366 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy4c11c2b2011-09-05 20:17:07 +0000367 continue;
368 }
369 /*
370 Alpha blending.
371 */
372 for (v=0; v < (ssize_t) (width-j); v++)
cristy3ed852e2009-09-05 21:47:34 +0000373 {
cristy4c11c2b2011-09-05 20:17:07 +0000374 for (u=0; u < (ssize_t) (width-j); u++)
375 {
cristya19f1d72012-08-07 18:24:38 +0000376 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
cristy4c11c2b2011-09-05 20:17:07 +0000377 pixel+=(*k)*alpha*pixels[i];
378 gamma+=(*k)*alpha;
379 k++;
380 pixels+=GetPixelChannels(image);
381 }
cristy3ed852e2009-09-05 21:47:34 +0000382 }
cristy3e3ec3a2012-11-03 23:11:06 +0000383 gamma=PerceptibleReciprocal(gamma);
cristy0beccfa2011-09-25 20:47:53 +0000384 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy3ed852e2009-09-05 21:47:34 +0000385 }
cristyed231572011-07-14 02:18:59 +0000386 q+=GetPixelChannels(blur_image);
387 r+=GetPixelChannels(edge_image);
cristy3ed852e2009-09-05 21:47:34 +0000388 }
389 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
390 status=MagickFalse;
391 if (image->progress_monitor != (MagickProgressMonitor) NULL)
392 {
393 MagickBooleanType
394 proceed;
395
cristyb5d5f722009-11-04 03:03:49 +0000396#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +0000397 #pragma omp critical (MagickCore_AdaptiveBlurImage)
cristy3ed852e2009-09-05 21:47:34 +0000398#endif
399 proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
400 image->rows);
401 if (proceed == MagickFalse)
402 status=MagickFalse;
403 }
404 }
405 blur_image->type=image->type;
406 blur_view=DestroyCacheView(blur_view);
407 edge_view=DestroyCacheView(edge_view);
408 image_view=DestroyCacheView(image_view);
409 edge_image=DestroyImage(edge_image);
cristybb503372010-05-27 20:51:26 +0000410 for (i=0; i < (ssize_t) width; i+=2)
cristy0a887dc2012-08-15 22:58:36 +0000411 kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
412 kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +0000413 if (status == MagickFalse)
414 blur_image=DestroyImage(blur_image);
415 return(blur_image);
416}
417
418/*
419%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420% %
421% %
422% %
423% A d a p t i v e S h a r p e n I m a g e %
424% %
425% %
426% %
427%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
428%
429% AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
430% intensely near image edges and less intensely far from edges. We sharpen the
431% image with a Gaussian operator of the given radius and standard deviation
432% (sigma). For reasonable results, radius should be larger than sigma. Use a
433% radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
434%
435% The format of the AdaptiveSharpenImage method is:
436%
437% Image *AdaptiveSharpenImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +0000438% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000439%
440% A description of each parameter follows:
441%
442% o image: the image.
443%
cristy3ed852e2009-09-05 21:47:34 +0000444% o radius: the radius of the Gaussian, in pixels, not counting the center
445% pixel.
446%
447% o sigma: the standard deviation of the Laplacian, in pixels.
448%
449% o exception: return any errors or warnings in this structure.
450%
451*/
cristy3ed852e2009-09-05 21:47:34 +0000452MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +0000453 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000454{
cristy3ed852e2009-09-05 21:47:34 +0000455#define AdaptiveSharpenImageTag "Convolve/Image"
cristy9b528342012-06-02 00:59:20 +0000456#define MagickSigma (fabs(sigma) < MagickEpsilon ? MagickEpsilon : sigma)
cristy3ed852e2009-09-05 21:47:34 +0000457
cristyc4c8d132010-01-07 01:58:38 +0000458 CacheView
459 *sharp_view,
460 *edge_view,
461 *image_view;
462
cristyf4bae2c2012-10-17 12:19:38 +0000463 double
464 normalize;
465
cristy3ed852e2009-09-05 21:47:34 +0000466 Image
467 *sharp_image,
468 *edge_image,
469 *gaussian_image;
470
cristy3ed852e2009-09-05 21:47:34 +0000471 MagickBooleanType
472 status;
473
cristybb503372010-05-27 20:51:26 +0000474 MagickOffsetType
475 progress;
476
cristy0a887dc2012-08-15 22:58:36 +0000477 MagickRealType
cristyf4bae2c2012-10-17 12:19:38 +0000478 **kernel;
cristy0a887dc2012-08-15 22:58:36 +0000479
cristybb503372010-05-27 20:51:26 +0000480 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000481 i;
cristy3ed852e2009-09-05 21:47:34 +0000482
cristybb503372010-05-27 20:51:26 +0000483 size_t
cristy3ed852e2009-09-05 21:47:34 +0000484 width;
485
cristybb503372010-05-27 20:51:26 +0000486 ssize_t
487 j,
488 k,
489 u,
490 v,
491 y;
492
cristy3ed852e2009-09-05 21:47:34 +0000493 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000494 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000495 if (image->debug != MagickFalse)
496 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
497 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000498 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000499 sharp_image=CloneImage(image,0,0,MagickTrue,exception);
500 if (sharp_image == (Image *) NULL)
501 return((Image *) NULL);
cristye6180ff2012-06-04 22:19:16 +0000502 if (fabs(sigma) < MagickEpsilon)
cristy3ed852e2009-09-05 21:47:34 +0000503 return(sharp_image);
cristy574cc262011-08-05 01:23:58 +0000504 if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000505 {
cristy3ed852e2009-09-05 21:47:34 +0000506 sharp_image=DestroyImage(sharp_image);
507 return((Image *) NULL);
508 }
509 /*
cristyd8dd86c2014-05-28 10:58:46 +0000510 Edge detect the image brightness channel, level, sharp, and level again.
cristy3ed852e2009-09-05 21:47:34 +0000511 */
cristy9dc4c512013-03-24 01:38:00 +0000512 edge_image=EdgeImage(image,radius,exception);
cristy3ed852e2009-09-05 21:47:34 +0000513 if (edge_image == (Image *) NULL)
514 {
515 sharp_image=DestroyImage(sharp_image);
516 return((Image *) NULL);
517 }
cristyd8dd86c2014-05-28 10:58:46 +0000518 (void) AutoLevelImage(edge_image,exception);
cristyb202ff32013-03-24 15:16:46 +0000519 gaussian_image=BlurImage(edge_image,radius,sigma,exception);
cristy3ed852e2009-09-05 21:47:34 +0000520 if (gaussian_image != (Image *) NULL)
521 {
522 edge_image=DestroyImage(edge_image);
523 edge_image=gaussian_image;
524 }
cristyd8dd86c2014-05-28 10:58:46 +0000525 (void) AutoLevelImage(edge_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000526 /*
527 Create a set of kernels from maximum (radius,sigma) to minimum.
528 */
529 width=GetOptimalKernelWidth2D(radius,sigma);
cristye42639a2012-08-23 01:53:24 +0000530 kernel=(MagickRealType **) MagickAssumeAligned(AcquireAlignedMemory((size_t)
531 width,sizeof(*kernel)));
cristy0a887dc2012-08-15 22:58:36 +0000532 if (kernel == (MagickRealType **) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000533 {
534 edge_image=DestroyImage(edge_image);
535 sharp_image=DestroyImage(sharp_image);
536 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
537 }
538 (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
cristybb503372010-05-27 20:51:26 +0000539 for (i=0; i < (ssize_t) width; i+=2)
cristy3ed852e2009-09-05 21:47:34 +0000540 {
cristye42639a2012-08-23 01:53:24 +0000541 kernel[i]=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
542 (size_t) (width-i),(width-i)*sizeof(**kernel)));
cristy97559b62012-08-23 12:00:40 +0000543 if (kernel[i] == (MagickRealType *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000544 break;
cristy47e00502009-12-17 19:19:57 +0000545 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000546 j=(ssize_t) (width-i)/2;
cristy47e00502009-12-17 19:19:57 +0000547 k=0;
548 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +0000549 {
cristy47e00502009-12-17 19:19:57 +0000550 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +0000551 {
cristy0a887dc2012-08-15 22:58:36 +0000552 kernel[i][k]=(MagickRealType) (-exp(-((double) u*u+v*v)/(2.0*
553 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy47e00502009-12-17 19:19:57 +0000554 normalize+=kernel[i][k];
555 k++;
cristy3ed852e2009-09-05 21:47:34 +0000556 }
557 }
cristy6eec5202013-04-16 23:33:08 +0000558 kernel[i][(k-1)/2]=(double) ((-2.0)*normalize);
559 if (sigma < MagickEpsilon)
560 kernel[i][(k-1)/2]=1.0;
cristy3ed852e2009-09-05 21:47:34 +0000561 }
cristybb503372010-05-27 20:51:26 +0000562 if (i < (ssize_t) width)
cristy3ed852e2009-09-05 21:47:34 +0000563 {
564 for (i-=2; i >= 0; i-=2)
cristy0a887dc2012-08-15 22:58:36 +0000565 kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
566 kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +0000567 edge_image=DestroyImage(edge_image);
568 sharp_image=DestroyImage(sharp_image);
569 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
570 }
571 /*
572 Adaptively sharpen image.
573 */
574 status=MagickTrue;
575 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000576 image_view=AcquireVirtualCacheView(image,exception);
577 edge_view=AcquireVirtualCacheView(edge_image,exception);
578 sharp_view=AcquireAuthenticCacheView(sharp_image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000579#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000580 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000581 magick_threads(image,sharp_image,sharp_image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000582#endif
cristybb503372010-05-27 20:51:26 +0000583 for (y=0; y < (ssize_t) sharp_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000584 {
cristy4c08aed2011-07-01 19:47:50 +0000585 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000586 *restrict r;
cristy3ed852e2009-09-05 21:47:34 +0000587
cristy4c08aed2011-07-01 19:47:50 +0000588 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000589 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000590
cristy117ff172010-08-15 21:35:32 +0000591 register ssize_t
592 x;
593
cristy3ed852e2009-09-05 21:47:34 +0000594 if (status == MagickFalse)
595 continue;
596 r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
597 q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
598 exception);
cristy4c08aed2011-07-01 19:47:50 +0000599 if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000600 {
601 status=MagickFalse;
602 continue;
603 }
cristybb503372010-05-27 20:51:26 +0000604 for (x=0; x < (ssize_t) sharp_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000605 {
cristy4c11c2b2011-09-05 20:17:07 +0000606 register const Quantum
607 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000608
cristybb503372010-05-27 20:51:26 +0000609 register ssize_t
cristy4c11c2b2011-09-05 20:17:07 +0000610 i;
cristy3ed852e2009-09-05 21:47:34 +0000611
cristy4c11c2b2011-09-05 20:17:07 +0000612 ssize_t
613 center,
614 j;
615
cristy6eec5202013-04-16 23:33:08 +0000616 j=(ssize_t) ceil((double) width*(1.0-QuantumScale*
617 GetPixelIntensity(edge_image,r))-0.5);
cristy4c11c2b2011-09-05 20:17:07 +0000618 if (j < 0)
619 j=0;
cristy3ed852e2009-09-05 21:47:34 +0000620 else
cristy4c11c2b2011-09-05 20:17:07 +0000621 if (j > (ssize_t) width)
622 j=(ssize_t) width;
623 if ((j & 0x01) != 0)
624 j--;
625 p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
626 (ssize_t) ((width-j)/2L),width-j,width-j,exception);
cristy4c08aed2011-07-01 19:47:50 +0000627 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000628 break;
cristyd09f8802012-02-04 16:44:10 +0000629 center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
630 GetPixelChannels(image)*((width-j)/2);
cristyc94ba6f2012-01-29 23:19:58 +0000631 for (i=0; i < (ssize_t) GetPixelChannels(sharp_image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000632 {
cristya19f1d72012-08-07 18:24:38 +0000633 double
cristy4c11c2b2011-09-05 20:17:07 +0000634 alpha,
635 gamma,
636 pixel;
637
cristy633067b2013-02-21 01:15:12 +0000638 PixelChannel
639 channel;
640
641 PixelTrait
642 sharp_traits,
643 traits;
644
cristy0a887dc2012-08-15 22:58:36 +0000645 register const MagickRealType
cristy4c11c2b2011-09-05 20:17:07 +0000646 *restrict k;
647
648 register const Quantum
649 *restrict pixels;
650
651 register ssize_t
652 u;
653
654 ssize_t
655 v;
656
cristy633067b2013-02-21 01:15:12 +0000657 channel=GetPixelChannelChannel(image,i);
658 traits=GetPixelChannelTraits(image,channel);
659 sharp_traits=GetPixelChannelTraits(sharp_image,channel);
cristy4c11c2b2011-09-05 20:17:07 +0000660 if ((traits == UndefinedPixelTrait) ||
661 (sharp_traits == UndefinedPixelTrait))
662 continue;
cristy1eced092012-08-10 23:10:56 +0000663 if (((sharp_traits & CopyPixelTrait) != 0) ||
cristy883fde12013-04-08 00:50:13 +0000664 (GetPixelReadMask(image,p+center) == 0))
cristy4c11c2b2011-09-05 20:17:07 +0000665 {
cristy0beccfa2011-09-25 20:47:53 +0000666 SetPixelChannel(sharp_image,channel,p[center+i],q);
cristy4c11c2b2011-09-05 20:17:07 +0000667 continue;
668 }
669 k=kernel[j];
670 pixels=p;
cristyaa2c16c2012-03-25 22:21:35 +0000671 pixel=0.0;
cristy4c11c2b2011-09-05 20:17:07 +0000672 gamma=0.0;
cristy633067b2013-02-21 01:15:12 +0000673 if ((sharp_traits & BlendPixelTrait) == 0)
cristy4c11c2b2011-09-05 20:17:07 +0000674 {
675 /*
676 No alpha blending.
677 */
678 for (v=0; v < (ssize_t) (width-j); v++)
679 {
680 for (u=0; u < (ssize_t) (width-j); u++)
681 {
682 pixel+=(*k)*pixels[i];
683 gamma+=(*k);
684 k++;
685 pixels+=GetPixelChannels(image);
686 }
687 }
cristy3e3ec3a2012-11-03 23:11:06 +0000688 gamma=PerceptibleReciprocal(gamma);
cristy0beccfa2011-09-25 20:47:53 +0000689 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
cristy4c11c2b2011-09-05 20:17:07 +0000690 continue;
691 }
692 /*
693 Alpha blending.
694 */
695 for (v=0; v < (ssize_t) (width-j); v++)
cristy3ed852e2009-09-05 21:47:34 +0000696 {
cristy4c11c2b2011-09-05 20:17:07 +0000697 for (u=0; u < (ssize_t) (width-j); u++)
698 {
cristya19f1d72012-08-07 18:24:38 +0000699 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
cristy4c11c2b2011-09-05 20:17:07 +0000700 pixel+=(*k)*alpha*pixels[i];
701 gamma+=(*k)*alpha;
702 k++;
703 pixels+=GetPixelChannels(image);
704 }
cristy3ed852e2009-09-05 21:47:34 +0000705 }
cristy3e3ec3a2012-11-03 23:11:06 +0000706 gamma=PerceptibleReciprocal(gamma);
cristy0beccfa2011-09-25 20:47:53 +0000707 SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
cristy3ed852e2009-09-05 21:47:34 +0000708 }
cristyed231572011-07-14 02:18:59 +0000709 q+=GetPixelChannels(sharp_image);
710 r+=GetPixelChannels(edge_image);
cristy3ed852e2009-09-05 21:47:34 +0000711 }
712 if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
713 status=MagickFalse;
714 if (image->progress_monitor != (MagickProgressMonitor) NULL)
715 {
716 MagickBooleanType
717 proceed;
718
cristyb5d5f722009-11-04 03:03:49 +0000719#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +0000720 #pragma omp critical (MagickCore_AdaptiveSharpenImage)
cristy3ed852e2009-09-05 21:47:34 +0000721#endif
722 proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
723 image->rows);
724 if (proceed == MagickFalse)
725 status=MagickFalse;
726 }
727 }
728 sharp_image->type=image->type;
729 sharp_view=DestroyCacheView(sharp_view);
730 edge_view=DestroyCacheView(edge_view);
731 image_view=DestroyCacheView(image_view);
732 edge_image=DestroyImage(edge_image);
cristybb503372010-05-27 20:51:26 +0000733 for (i=0; i < (ssize_t) width; i+=2)
cristy0a887dc2012-08-15 22:58:36 +0000734 kernel[i]=(MagickRealType *) RelinquishAlignedMemory(kernel[i]);
735 kernel=(MagickRealType **) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +0000736 if (status == MagickFalse)
737 sharp_image=DestroyImage(sharp_image);
738 return(sharp_image);
739}
740
741/*
742%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
743% %
744% %
745% %
746% B l u r I m a g e %
747% %
748% %
749% %
750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
751%
752% BlurImage() blurs an image. We convolve the image with a Gaussian operator
753% of the given radius and standard deviation (sigma). For reasonable results,
754% the radius should be larger than sigma. Use a radius of 0 and BlurImage()
755% selects a suitable radius for you.
756%
cristy3ed852e2009-09-05 21:47:34 +0000757% The format of the BlurImage method is:
758%
759% Image *BlurImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +0000760% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000761%
762% A description of each parameter follows:
763%
764% o image: the image.
765%
cristy3ed852e2009-09-05 21:47:34 +0000766% o radius: the radius of the Gaussian, in pixels, not counting the center
767% pixel.
768%
769% o sigma: the standard deviation of the Gaussian, in pixels.
770%
771% o exception: return any errors or warnings in this structure.
772%
773*/
cristyf4ad9df2011-07-08 16:49:03 +0000774MagickExport Image *BlurImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +0000775 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000776{
cristye6bdb5e2013-03-23 18:39:24 +0000777 char
cristy151b66d2015-04-15 10:50:31 +0000778 geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000779
cristy590c8292013-02-21 01:37:36 +0000780 KernelInfo
781 *kernel_info;
cristy0a887dc2012-08-15 22:58:36 +0000782
cristye6bdb5e2013-03-23 18:39:24 +0000783 Image
cristyd42fab62013-03-26 16:34:20 +0000784 *blur_image;
cristybb503372010-05-27 20:51:26 +0000785
cristy590c8292013-02-21 01:37:36 +0000786 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000787 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000788 if (image->debug != MagickFalse)
789 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
790 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000791 assert(exception->signature == MagickCoreSignature);
cristy151b66d2015-04-15 10:50:31 +0000792 (void) FormatLocaleString(geometry,MagickPathExtent,
cristyd42fab62013-03-26 16:34:20 +0000793 "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
cristy2c57b742014-10-31 00:40:34 +0000794 kernel_info=AcquireKernelInfo(geometry,exception);
cristy590c8292013-02-21 01:37:36 +0000795 if (kernel_info == (KernelInfo *) NULL)
796 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy7f391ce2013-03-27 11:03:11 +0000797 blur_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
798 UndefinedCompositeOp,0.0,exception);
cristyda1231c2013-03-24 19:11:49 +0000799 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +0000800 return(blur_image);
801}
802
803/*
804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
805% %
806% %
807% %
cristyfccdab92009-11-30 16:43:57 +0000808% C o n v o l v e I m a g e %
809% %
810% %
811% %
812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813%
814% ConvolveImage() applies a custom convolution kernel to the image.
815%
816% The format of the ConvolveImage method is:
817%
cristy5e6be1e2011-07-16 01:23:39 +0000818% Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
819% ExceptionInfo *exception)
820%
cristyfccdab92009-11-30 16:43:57 +0000821% A description of each parameter follows:
822%
823% o image: the image.
824%
cristy5e6be1e2011-07-16 01:23:39 +0000825% o kernel: the filtering kernel.
cristyfccdab92009-11-30 16:43:57 +0000826%
827% o exception: return any errors or warnings in this structure.
828%
829*/
cristy5e6be1e2011-07-16 01:23:39 +0000830MagickExport Image *ConvolveImage(const Image *image,
831 const KernelInfo *kernel_info,ExceptionInfo *exception)
cristyfccdab92009-11-30 16:43:57 +0000832{
cristy7f391ce2013-03-27 11:03:11 +0000833 Image
834 *convolve_image;
835
836 convolve_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
837 UndefinedCompositeOp,0.0,exception);
838 return(convolve_image);
cristyfccdab92009-11-30 16:43:57 +0000839}
840
841/*
842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
843% %
844% %
845% %
cristy3ed852e2009-09-05 21:47:34 +0000846% D e s p e c k l e I m a g e %
847% %
848% %
849% %
850%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851%
852% DespeckleImage() reduces the speckle noise in an image while perserving the
cristyf0ae7762012-01-15 22:04:03 +0000853% edges of the original image. A speckle removing filter uses a complementary % hulling technique (raising pixels that are darker than their surrounding
854% neighbors, then complementarily lowering pixels that are brighter than their
855% surrounding neighbors) to reduce the speckle index of that image (reference
856% Crimmins speckle removal).
cristy3ed852e2009-09-05 21:47:34 +0000857%
858% The format of the DespeckleImage method is:
859%
860% Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
861%
862% A description of each parameter follows:
863%
864% o image: the image.
865%
866% o exception: return any errors or warnings in this structure.
867%
868*/
869
cristy4ee2b0c2012-05-15 00:30:35 +0000870static void Hull(const Image *image,const ssize_t x_offset,
871 const ssize_t y_offset,const size_t columns,const size_t rows,
872 const int polarity,Quantum *restrict f,Quantum *restrict g)
cristy3ed852e2009-09-05 21:47:34 +0000873{
cristy7b4b8862012-01-16 19:52:19 +0000874 register Quantum
cristy5473bf02012-01-16 19:15:08 +0000875 *p,
876 *q,
877 *r,
878 *s;
cristy78f3de22012-01-16 00:43:18 +0000879
cristy5473bf02012-01-16 19:15:08 +0000880 ssize_t
881 y;
882
cristyb0de93f2013-05-03 13:39:25 +0000883 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000884 assert(image->signature == MagickCoreSignature);
cristyb0de93f2013-05-03 13:39:25 +0000885 if (image->debug != MagickFalse)
886 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy5473bf02012-01-16 19:15:08 +0000887 assert(f != (Quantum *) NULL);
888 assert(g != (Quantum *) NULL);
889 p=f+(columns+2);
890 q=g+(columns+2);
891 r=p+(y_offset*(columns+2)+x_offset);
cristyff8e85a2012-01-18 13:36:20 +0000892#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +0000893 #pragma omp parallel for schedule(static,4) \
cristy64c10cd2013-01-06 18:40:43 +0000894 magick_threads(image,image,1,1)
cristy5473bf02012-01-16 19:15:08 +0000895#endif
896 for (y=0; y < (ssize_t) rows; y++)
897 {
cristyc058afd2012-10-31 11:26:53 +0000898 MagickRealType
899 v;
900
cristy7b4b8862012-01-16 19:52:19 +0000901 register ssize_t
cristy5473bf02012-01-16 19:15:08 +0000902 i,
903 x;
904
905 i=(2*y+1)+y*columns;
906 if (polarity > 0)
907 for (x=0; x < (ssize_t) columns; x++)
908 {
cristyc058afd2012-10-31 11:26:53 +0000909 v=(MagickRealType) p[i];
910 if ((MagickRealType) r[i] >= (v+ScaleCharToQuantum(2)))
cristy5473bf02012-01-16 19:15:08 +0000911 v+=ScaleCharToQuantum(1);
912 q[i]=(Quantum) v;
913 i++;
914 }
915 else
916 for (x=0; x < (ssize_t) columns; x++)
917 {
cristyc058afd2012-10-31 11:26:53 +0000918 v=(MagickRealType) p[i];
919 if ((MagickRealType) r[i] <= (v-ScaleCharToQuantum(2)))
cristy5473bf02012-01-16 19:15:08 +0000920 v-=ScaleCharToQuantum(1);
921 q[i]=(Quantum) v;
922 i++;
923 }
924 }
925 p=f+(columns+2);
926 q=g+(columns+2);
927 r=q+(y_offset*(columns+2)+x_offset);
928 s=q-(y_offset*(columns+2)+x_offset);
cristyff8e85a2012-01-18 13:36:20 +0000929#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +0000930 #pragma omp parallel for schedule(static,4) \
cristy64c10cd2013-01-06 18:40:43 +0000931 magick_threads(image,image,1,1)
cristy5473bf02012-01-16 19:15:08 +0000932#endif
933 for (y=0; y < (ssize_t) rows; y++)
934 {
cristy7b4b8862012-01-16 19:52:19 +0000935 register ssize_t
cristy5473bf02012-01-16 19:15:08 +0000936 i,
937 x;
938
cristyc058afd2012-10-31 11:26:53 +0000939 MagickRealType
cristy7b4b8862012-01-16 19:52:19 +0000940 v;
941
cristy5473bf02012-01-16 19:15:08 +0000942 i=(2*y+1)+y*columns;
943 if (polarity > 0)
944 for (x=0; x < (ssize_t) columns; x++)
945 {
cristyc058afd2012-10-31 11:26:53 +0000946 v=(MagickRealType) q[i];
947 if (((MagickRealType) s[i] >= (v+ScaleCharToQuantum(2))) &&
948 ((MagickRealType) r[i] > v))
cristy5473bf02012-01-16 19:15:08 +0000949 v+=ScaleCharToQuantum(1);
950 p[i]=(Quantum) v;
951 i++;
952 }
953 else
954 for (x=0; x < (ssize_t) columns; x++)
955 {
cristyc058afd2012-10-31 11:26:53 +0000956 v=(MagickRealType) q[i];
957 if (((MagickRealType) s[i] <= (v-ScaleCharToQuantum(2))) &&
958 ((MagickRealType) r[i] < v))
cristy5473bf02012-01-16 19:15:08 +0000959 v-=ScaleCharToQuantum(1);
960 p[i]=(Quantum) v;
961 i++;
962 }
963 }
cristy3ed852e2009-09-05 21:47:34 +0000964}
965
966MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
967{
968#define DespeckleImageTag "Despeckle/Image"
969
cristy2407fc22009-09-11 00:55:25 +0000970 CacheView
971 *despeckle_view,
972 *image_view;
973
cristy3ed852e2009-09-05 21:47:34 +0000974 Image
975 *despeckle_image;
976
cristy3ed852e2009-09-05 21:47:34 +0000977 MagickBooleanType
978 status;
979
cristy7d4aa382013-06-30 01:59:39 +0000980 MemoryInfo
981 *buffer_info,
982 *pixel_info;
983
cristy5473bf02012-01-16 19:15:08 +0000984 Quantum
985 *restrict buffer,
986 *restrict pixels;
cristya63e4a92011-09-09 00:47:59 +0000987
cristy5473bf02012-01-16 19:15:08 +0000988 register ssize_t
989 i;
990
991 size_t
992 length;
cristy117ff172010-08-15 21:35:32 +0000993
cristybb503372010-05-27 20:51:26 +0000994 static const ssize_t
cristy691a29e2009-09-11 00:44:10 +0000995 X[4] = {0, 1, 1,-1},
996 Y[4] = {1, 0, 1, 1};
cristy3ed852e2009-09-05 21:47:34 +0000997
cristy3ed852e2009-09-05 21:47:34 +0000998 /*
999 Allocate despeckled image.
1000 */
1001 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001002 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001003 if (image->debug != MagickFalse)
1004 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1005 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001006 assert(exception->signature == MagickCoreSignature);
cristy5473bf02012-01-16 19:15:08 +00001007 despeckle_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001008 if (despeckle_image == (Image *) NULL)
1009 return((Image *) NULL);
cristya63e4a92011-09-09 00:47:59 +00001010 status=SetImageStorageClass(despeckle_image,DirectClass,exception);
1011 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001012 {
cristy3ed852e2009-09-05 21:47:34 +00001013 despeckle_image=DestroyImage(despeckle_image);
1014 return((Image *) NULL);
1015 }
1016 /*
cristy5473bf02012-01-16 19:15:08 +00001017 Allocate image buffer.
1018 */
1019 length=(size_t) ((image->columns+2)*(image->rows+2));
cristy7d4aa382013-06-30 01:59:39 +00001020 pixel_info=AcquireVirtualMemory(length,sizeof(*pixels));
1021 buffer_info=AcquireVirtualMemory(length,sizeof(*buffer));
1022 if ((pixel_info == (MemoryInfo *) NULL) ||
1023 (buffer_info == (MemoryInfo *) NULL))
cristy5473bf02012-01-16 19:15:08 +00001024 {
cristy7d4aa382013-06-30 01:59:39 +00001025 if (buffer_info != (MemoryInfo *) NULL)
1026 buffer_info=RelinquishVirtualMemory(buffer_info);
1027 if (pixel_info != (MemoryInfo *) NULL)
1028 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy5473bf02012-01-16 19:15:08 +00001029 despeckle_image=DestroyImage(despeckle_image);
1030 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1031 }
cristy7d4aa382013-06-30 01:59:39 +00001032 pixels=(Quantum *) GetVirtualMemoryBlob(pixel_info);
1033 buffer=(Quantum *) GetVirtualMemoryBlob(buffer_info);
cristy5473bf02012-01-16 19:15:08 +00001034 /*
1035 Reduce speckle in the image.
cristy3ed852e2009-09-05 21:47:34 +00001036 */
1037 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00001038 image_view=AcquireVirtualCacheView(image,exception);
1039 despeckle_view=AcquireAuthenticCacheView(despeckle_image,exception);
cristy5473bf02012-01-16 19:15:08 +00001040 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +00001041 {
cristy633067b2013-02-21 01:15:12 +00001042 PixelChannel
1043 channel;
1044
1045 PixelTrait
1046 despeckle_traits,
1047 traits;
1048
cristyc1488b52011-02-19 18:54:15 +00001049 register ssize_t
cristy5473bf02012-01-16 19:15:08 +00001050 k,
cristyf0ae7762012-01-15 22:04:03 +00001051 x;
cristyc1488b52011-02-19 18:54:15 +00001052
cristy5473bf02012-01-16 19:15:08 +00001053 ssize_t
1054 j,
1055 y;
1056
cristy633067b2013-02-21 01:15:12 +00001057 if (status == MagickFalse)
1058 continue;
1059 channel=GetPixelChannelChannel(image,i);
1060 traits=GetPixelChannelTraits(image,channel);
1061 despeckle_traits=GetPixelChannelTraits(despeckle_image,channel);
cristy5473bf02012-01-16 19:15:08 +00001062 if ((traits == UndefinedPixelTrait) ||
1063 (despeckle_traits == UndefinedPixelTrait))
1064 continue;
1065 if ((despeckle_traits & CopyPixelTrait) != 0)
1066 continue;
1067 (void) ResetMagickMemory(pixels,0,length*sizeof(*pixels));
1068 j=(ssize_t) image->columns+2;
1069 for (y=0; y < (ssize_t) image->rows; y++)
cristyfc830f42012-01-15 02:45:06 +00001070 {
cristy5473bf02012-01-16 19:15:08 +00001071 register const Quantum
1072 *restrict p;
cristyfc830f42012-01-15 02:45:06 +00001073
cristy5473bf02012-01-16 19:15:08 +00001074 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1075 if (p == (const Quantum *) NULL)
cristyf0ae7762012-01-15 22:04:03 +00001076 {
cristy5473bf02012-01-16 19:15:08 +00001077 status=MagickFalse;
1078 continue;
cristyf0ae7762012-01-15 22:04:03 +00001079 }
cristy5473bf02012-01-16 19:15:08 +00001080 j++;
1081 for (x=0; x < (ssize_t) image->columns; x++)
1082 {
1083 pixels[j++]=p[i];
1084 p+=GetPixelChannels(image);
cristyfc830f42012-01-15 02:45:06 +00001085 }
cristy5473bf02012-01-16 19:15:08 +00001086 j++;
cristy3ed852e2009-09-05 21:47:34 +00001087 }
cristy5473bf02012-01-16 19:15:08 +00001088 (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1089 for (k=0; k < 4; k++)
1090 {
cristy4ee2b0c2012-05-15 00:30:35 +00001091 Hull(image,X[k],Y[k],image->columns,image->rows,1,pixels,buffer);
1092 Hull(image,-X[k],-Y[k],image->columns,image->rows,1,pixels,buffer);
1093 Hull(image,-X[k],-Y[k],image->columns,image->rows,-1,pixels,buffer);
1094 Hull(image,X[k],Y[k],image->columns,image->rows,-1,pixels,buffer);
cristy5473bf02012-01-16 19:15:08 +00001095 }
1096 j=(ssize_t) image->columns+2;
1097 for (y=0; y < (ssize_t) image->rows; y++)
1098 {
1099 MagickBooleanType
1100 sync;
1101
1102 register Quantum
1103 *restrict q;
1104
cristyf7ba7732013-01-22 22:16:21 +00001105 q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1106 1,exception);
cristy5473bf02012-01-16 19:15:08 +00001107 if (q == (Quantum *) NULL)
1108 {
1109 status=MagickFalse;
1110 continue;
1111 }
1112 j++;
1113 for (x=0; x < (ssize_t) image->columns; x++)
1114 {
1115 SetPixelChannel(despeckle_image,channel,pixels[j++],q);
1116 q+=GetPixelChannels(despeckle_image);
1117 }
1118 sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1119 if (sync == MagickFalse)
1120 status=MagickFalse;
1121 j++;
1122 }
cristy3ed852e2009-09-05 21:47:34 +00001123 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1124 {
1125 MagickBooleanType
1126 proceed;
1127
cristy5473bf02012-01-16 19:15:08 +00001128 proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1129 GetPixelChannels(image));
cristy3ed852e2009-09-05 21:47:34 +00001130 if (proceed == MagickFalse)
1131 status=MagickFalse;
1132 }
1133 }
1134 despeckle_view=DestroyCacheView(despeckle_view);
1135 image_view=DestroyCacheView(image_view);
cristy7d4aa382013-06-30 01:59:39 +00001136 buffer_info=RelinquishVirtualMemory(buffer_info);
1137 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00001138 despeckle_image->type=image->type;
1139 if (status == MagickFalse)
1140 despeckle_image=DestroyImage(despeckle_image);
1141 return(despeckle_image);
1142}
1143
1144/*
1145%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1146% %
1147% %
1148% %
1149% E d g e I m a g e %
1150% %
1151% %
1152% %
1153%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1154%
1155% EdgeImage() finds edges in an image. Radius defines the radius of the
1156% convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
1157% radius for you.
1158%
1159% The format of the EdgeImage method is:
1160%
1161% Image *EdgeImage(const Image *image,const double radius,
cristy9dc4c512013-03-24 01:38:00 +00001162% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001163%
1164% A description of each parameter follows:
1165%
1166% o image: the image.
1167%
1168% o radius: the radius of the pixel neighborhood.
1169%
1170% o exception: return any errors or warnings in this structure.
1171%
1172*/
1173MagickExport Image *EdgeImage(const Image *image,const double radius,
cristy9dc4c512013-03-24 01:38:00 +00001174 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001175{
cristy9ab5d042013-04-28 02:38:26 +00001176 Image
1177 *edge_image;
cristy3ed852e2009-09-05 21:47:34 +00001178
cristy41cbe682011-07-15 19:12:37 +00001179 KernelInfo
1180 *kernel_info;
cristy3ed852e2009-09-05 21:47:34 +00001181
cristy9ab5d042013-04-28 02:38:26 +00001182 register ssize_t
1183 i;
1184
1185 size_t
1186 width;
cristy41cbe682011-07-15 19:12:37 +00001187
cristy3ed852e2009-09-05 21:47:34 +00001188 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001189 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001190 if (image->debug != MagickFalse)
1191 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1192 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001193 assert(exception->signature == MagickCoreSignature);
cristy9ab5d042013-04-28 02:38:26 +00001194 width=GetOptimalKernelWidth1D(radius,0.5);
cristy2c57b742014-10-31 00:40:34 +00001195 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
cristy41cbe682011-07-15 19:12:37 +00001196 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001197 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy9ab5d042013-04-28 02:38:26 +00001198 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1199 kernel_info->width=width;
1200 kernel_info->height=width;
1201 kernel_info->x=(ssize_t) (kernel_info->width-1)/2;
1202 kernel_info->y=(ssize_t) (kernel_info->height-1)/2;
cristye1c94d92015-06-28 12:16:33 +00001203 kernel_info->signature=MagickCoreSignature;
cristy9ab5d042013-04-28 02:38:26 +00001204 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
1205 AcquireAlignedMemory(kernel_info->width,kernel_info->height*
1206 sizeof(*kernel_info->values)));
1207 if (kernel_info->values == (MagickRealType *) NULL)
1208 {
1209 kernel_info=DestroyKernelInfo(kernel_info);
1210 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1211 }
1212 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1213 kernel_info->values[i]=(-1.0);
1214 kernel_info->values[i/2]=(double) kernel_info->width*kernel_info->height-1.0;
1215 edge_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
cristy7f391ce2013-03-27 11:03:11 +00001216 UndefinedCompositeOp,0.0,exception);
cristy41cbe682011-07-15 19:12:37 +00001217 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001218 return(edge_image);
1219}
1220
1221/*
1222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1223% %
1224% %
1225% %
1226% E m b o s s I m a g e %
1227% %
1228% %
1229% %
1230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1231%
1232% EmbossImage() returns a grayscale image with a three-dimensional effect.
1233% We convolve the image with a Gaussian operator of the given radius and
1234% standard deviation (sigma). For reasonable results, radius should be
1235% larger than sigma. Use a radius of 0 and Emboss() selects a suitable
1236% radius for you.
1237%
1238% The format of the EmbossImage method is:
1239%
1240% Image *EmbossImage(const Image *image,const double radius,
1241% const double sigma,ExceptionInfo *exception)
1242%
1243% A description of each parameter follows:
1244%
1245% o image: the image.
1246%
1247% o radius: the radius of the pixel neighborhood.
1248%
1249% o sigma: the standard deviation of the Gaussian, in pixels.
1250%
1251% o exception: return any errors or warnings in this structure.
1252%
1253*/
1254MagickExport Image *EmbossImage(const Image *image,const double radius,
1255 const double sigma,ExceptionInfo *exception)
1256{
cristy0cfd5672013-03-26 14:34:25 +00001257 double
1258 gamma,
1259 normalize;
1260
cristy3ed852e2009-09-05 21:47:34 +00001261 Image
1262 *emboss_image;
1263
cristy41cbe682011-07-15 19:12:37 +00001264 KernelInfo
1265 *kernel_info;
1266
cristybb503372010-05-27 20:51:26 +00001267 register ssize_t
cristy47e00502009-12-17 19:19:57 +00001268 i;
1269
cristybb503372010-05-27 20:51:26 +00001270 size_t
cristy3ed852e2009-09-05 21:47:34 +00001271 width;
1272
cristy117ff172010-08-15 21:35:32 +00001273 ssize_t
1274 j,
1275 k,
1276 u,
1277 v;
1278
cristy41cbe682011-07-15 19:12:37 +00001279 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001280 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001281 if (image->debug != MagickFalse)
1282 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1283 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001284 assert(exception->signature == MagickCoreSignature);
cristyfc0ae532011-12-06 15:14:45 +00001285 width=GetOptimalKernelWidth1D(radius,sigma);
cristy2c57b742014-10-31 00:40:34 +00001286 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
cristy41cbe682011-07-15 19:12:37 +00001287 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001288 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy41cbe682011-07-15 19:12:37 +00001289 kernel_info->width=width;
1290 kernel_info->height=width;
cristy6b516212013-03-22 19:20:32 +00001291 kernel_info->x=(ssize_t) (width-1)/2;
1292 kernel_info->y=(ssize_t) (width-1)/2;
cristye42639a2012-08-23 01:53:24 +00001293 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
1294 AcquireAlignedMemory(kernel_info->width,kernel_info->width*
1295 sizeof(*kernel_info->values)));
cristyd1e1c222012-08-13 01:13:28 +00001296 if (kernel_info->values == (MagickRealType *) NULL)
cristy41cbe682011-07-15 19:12:37 +00001297 {
1298 kernel_info=DestroyKernelInfo(kernel_info);
1299 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1300 }
cristy35faaa72013-03-08 12:33:42 +00001301 j=(ssize_t) (kernel_info->width-1)/2;
cristy47e00502009-12-17 19:19:57 +00001302 k=j;
1303 i=0;
1304 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00001305 {
cristy47e00502009-12-17 19:19:57 +00001306 for (u=(-j); u <= j; u++)
cristy3ed852e2009-09-05 21:47:34 +00001307 {
cristyd1e1c222012-08-13 01:13:28 +00001308 kernel_info->values[i]=(MagickRealType) (((u < 0) || (v < 0) ? -8.0 :
1309 8.0)*exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
cristy4205a3c2010-09-12 20:19:59 +00001310 (2.0*MagickPI*MagickSigma*MagickSigma));
cristy08ee72f2013-07-08 19:31:14 +00001311 if (u != k)
1312 kernel_info->values[i]=0.0;
cristy3ed852e2009-09-05 21:47:34 +00001313 i++;
1314 }
cristy47e00502009-12-17 19:19:57 +00001315 k--;
cristy3ed852e2009-09-05 21:47:34 +00001316 }
cristy0cfd5672013-03-26 14:34:25 +00001317 normalize=0.0;
1318 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1319 normalize+=kernel_info->values[i];
1320 gamma=PerceptibleReciprocal(normalize);
1321 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1322 kernel_info->values[i]*=gamma;
cristy7f391ce2013-03-27 11:03:11 +00001323 emboss_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
1324 UndefinedCompositeOp,0.0,exception);
cristy41cbe682011-07-15 19:12:37 +00001325 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001326 if (emboss_image != (Image *) NULL)
cristy6d8c3d72011-08-22 01:20:01 +00001327 (void) EqualizeImage(emboss_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001328 return(emboss_image);
1329}
1330
1331/*
1332%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1333% %
1334% %
1335% %
1336% G a u s s i a n B l u r I m a g e %
1337% %
1338% %
1339% %
1340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1341%
1342% GaussianBlurImage() blurs an image. We convolve the image with a
1343% Gaussian operator of the given radius and standard deviation (sigma).
1344% For reasonable results, the radius should be larger than sigma. Use a
1345% radius of 0 and GaussianBlurImage() selects a suitable radius for you
1346%
1347% The format of the GaussianBlurImage method is:
1348%
1349% Image *GaussianBlurImage(const Image *image,onst double radius,
cristyd89705a2012-01-20 02:52:24 +00001350% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001351%
1352% A description of each parameter follows:
1353%
1354% o image: the image.
1355%
cristy3ed852e2009-09-05 21:47:34 +00001356% o radius: the radius of the Gaussian, in pixels, not counting the center
1357% pixel.
1358%
1359% o sigma: the standard deviation of the Gaussian, in pixels.
1360%
1361% o exception: return any errors or warnings in this structure.
1362%
1363*/
cristy41cbe682011-07-15 19:12:37 +00001364MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
cristyd89705a2012-01-20 02:52:24 +00001365 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001366{
cristye6bdb5e2013-03-23 18:39:24 +00001367 char
cristy151b66d2015-04-15 10:50:31 +00001368 geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001369
cristy41cbe682011-07-15 19:12:37 +00001370 KernelInfo
1371 *kernel_info;
1372
cristye6bdb5e2013-03-23 18:39:24 +00001373 Image
1374 *blur_image;
cristy117ff172010-08-15 21:35:32 +00001375
cristy3ed852e2009-09-05 21:47:34 +00001376 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001377 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001378 if (image->debug != MagickFalse)
1379 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1380 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001381 assert(exception->signature == MagickCoreSignature);
cristy151b66d2015-04-15 10:50:31 +00001382 (void) FormatLocaleString(geometry,MagickPathExtent,"gaussian:%.20gx%.20g",
cristye6bdb5e2013-03-23 18:39:24 +00001383 radius,sigma);
cristy2c57b742014-10-31 00:40:34 +00001384 kernel_info=AcquireKernelInfo(geometry,exception);
cristy41cbe682011-07-15 19:12:37 +00001385 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001386 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy7f391ce2013-03-27 11:03:11 +00001387 blur_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
1388 UndefinedCompositeOp,0.0,exception);
cristy41cbe682011-07-15 19:12:37 +00001389 kernel_info=DestroyKernelInfo(kernel_info);
cristy3ed852e2009-09-05 21:47:34 +00001390 return(blur_image);
1391}
1392
1393/*
1394%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1395% %
1396% %
1397% %
cristy3b207f82014-09-27 14:21:20 +00001398% K u w a h a r a I m a g e %
1399% %
1400% %
1401% %
1402%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1403%
cristya7c76222014-09-30 09:53:07 +00001404% KuwaharaImage() is an edge preserving noise reduction filter.
cristy3b207f82014-09-27 14:21:20 +00001405%
1406% The format of the KuwaharaImage method is:
1407%
cristydea96f32014-10-05 23:27:21 +00001408% Image *KuwaharaImage(const Image *image,const double radius,
cristy3b207f82014-09-27 14:21:20 +00001409% const double sigma,ExceptionInfo *exception)
1410%
1411% A description of each parameter follows:
1412%
1413% o image: the image.
1414%
cristydea96f32014-10-05 23:27:21 +00001415% o radius: the square window radius.
cristy3b207f82014-09-27 14:21:20 +00001416%
1417% o sigma: the standard deviation of the Gaussian, in pixels.
1418%
1419% o exception: return any errors or warnings in this structure.
1420%
1421*/
cristye67edc32014-10-02 13:39:48 +00001422
1423static inline MagickRealType GetMeanLuma(const Image *restrict image,
1424 const double *restrict pixel)
1425{
1426 if (image->colorspace == GRAYColorspace)
1427 return((MagickRealType) pixel[image->channel_map[GrayPixelChannel].offset]);
1428 return(0.212656f*pixel[image->channel_map[RedPixelChannel].offset]+
1429 0.715158f*pixel[image->channel_map[GreenPixelChannel].offset]+
1430 0.072186f*pixel[image->channel_map[BluePixelChannel].offset]); /* Rec709 */
1431}
1432
cristydea96f32014-10-05 23:27:21 +00001433MagickExport Image *KuwaharaImage(const Image *image,const double radius,
cristy3b207f82014-09-27 14:21:20 +00001434 const double sigma,ExceptionInfo *exception)
1435{
cristy51b20df2014-09-29 18:52:28 +00001436#define KuwaharaImageTag "Kuwahara/Image"
cristy3b207f82014-09-27 14:21:20 +00001437
cristy51b20df2014-09-29 18:52:28 +00001438 CacheView
cristyee31ab82014-10-06 11:35:27 +00001439 *image_view,
cristy51b20df2014-09-29 18:52:28 +00001440 *kuwahara_view;
cristy3b207f82014-09-27 14:21:20 +00001441
1442 Image
cristy0562d772014-10-01 12:33:56 +00001443 *gaussian_image,
cristy3b207f82014-09-27 14:21:20 +00001444 *kuwahara_image;
1445
cristy51b20df2014-09-29 18:52:28 +00001446 MagickBooleanType
1447 status;
1448
1449 MagickOffsetType
1450 progress;
1451
cristya68a3bd2014-10-05 20:57:45 +00001452 size_t
cristydea96f32014-10-05 23:27:21 +00001453 width;
cristya68a3bd2014-10-05 20:57:45 +00001454
cristy51b20df2014-09-29 18:52:28 +00001455 ssize_t
1456 y;
1457
1458 /*
cristyb4a9d382014-10-04 14:06:13 +00001459 Initialize Kuwahara image attributes.
cristy51b20df2014-09-29 18:52:28 +00001460 */
1461 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001462 assert(image->signature == MagickCoreSignature);
cristy3b207f82014-09-27 14:21:20 +00001463 if (image->debug != MagickFalse)
1464 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1465 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001466 assert(exception->signature == MagickCoreSignature);
cristydea96f32014-10-05 23:27:21 +00001467 width=(size_t) radius+1;
1468 gaussian_image=BlurImage(image,radius,sigma,exception);
cristyc70ee662014-10-01 14:55:15 +00001469 if (gaussian_image == (Image *) NULL)
cristy0562d772014-10-01 12:33:56 +00001470 return((Image *) NULL);
cristy51b20df2014-09-29 18:52:28 +00001471 kuwahara_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1472 exception);
1473 if (kuwahara_image == (Image *) NULL)
cristy0562d772014-10-01 12:33:56 +00001474 {
1475 gaussian_image=DestroyImage(gaussian_image);
1476 return((Image *) NULL);
1477 }
cristy51b20df2014-09-29 18:52:28 +00001478 if (SetImageStorageClass(kuwahara_image,DirectClass,exception) == MagickFalse)
1479 {
cristy0562d772014-10-01 12:33:56 +00001480 gaussian_image=DestroyImage(gaussian_image);
cristy51b20df2014-09-29 18:52:28 +00001481 kuwahara_image=DestroyImage(kuwahara_image);
1482 return((Image *) NULL);
1483 }
1484 /*
cristyd1bc64d2014-09-30 09:52:12 +00001485 Edge preserving noise reduction filter.
cristy51b20df2014-09-29 18:52:28 +00001486 */
1487 status=MagickTrue;
1488 progress=0;
cristyee31ab82014-10-06 11:35:27 +00001489 image_view=AcquireVirtualCacheView(gaussian_image,exception);
cristy51b20df2014-09-29 18:52:28 +00001490 kuwahara_view=AcquireAuthenticCacheView(kuwahara_image,exception);
1491#if defined(MAGICKCORE_OPENMP_SUPPORT)
1492 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1493 magick_threads(image,kuwahara_image,image->rows,1)
1494#endif
1495 for (y=0; y < (ssize_t) image->rows; y++)
1496 {
1497 register Quantum
1498 *restrict q;
1499
1500 register ssize_t
1501 x;
1502
1503 if (status == MagickFalse)
1504 continue;
1505 q=QueueCacheViewAuthenticPixels(kuwahara_view,0,y,kuwahara_image->columns,1,
1506 exception);
1507 if (q == (Quantum *) NULL)
1508 {
1509 status=MagickFalse;
1510 continue;
1511 }
1512 for (x=0; x < (ssize_t) image->columns; x++)
1513 {
cristybc8db772014-09-29 20:57:24 +00001514 const Quantum
cristyee31ab82014-10-06 11:35:27 +00001515 *restrict p;
cristybc8db772014-09-29 20:57:24 +00001516
cristyb4800612014-09-29 22:30:01 +00001517 double
cristybfcd1be2014-10-03 22:39:11 +00001518 min_variance;
cristy70965022014-09-29 23:26:43 +00001519
cristybfcd1be2014-10-03 22:39:11 +00001520 RectangleInfo
cristyee31ab82014-10-06 11:35:27 +00001521 quadrant,
1522 target;
cristyb4800612014-09-29 22:30:01 +00001523
cristyee31ab82014-10-06 11:35:27 +00001524 register size_t
1525 i;
1526
cristyea715492014-10-03 16:27:53 +00001527 min_variance=MagickMaximumValue;
cristyee31ab82014-10-06 11:35:27 +00001528 SetGeometry(gaussian_image,&target);
1529 quadrant.width=width;
1530 quadrant.height=width;
cristyb4800612014-09-29 22:30:01 +00001531 for (i=0; i < 4; i++)
1532 {
cristye67edc32014-10-02 13:39:48 +00001533 const Quantum
1534 *restrict k;
1535
cristyb4800612014-09-29 22:30:01 +00001536 double
cristyb4800612014-09-29 22:30:01 +00001537 mean[MaxPixelChannels],
cristy9e366d32014-09-30 18:45:56 +00001538 variance;
cristyb4800612014-09-29 22:30:01 +00001539
cristyee31ab82014-10-06 11:35:27 +00001540 register ssize_t
cristyfa971252014-10-05 23:55:56 +00001541 n;
cristyb4800612014-09-29 22:30:01 +00001542
cristyee31ab82014-10-06 11:35:27 +00001543 ssize_t
1544 j;
1545
1546 quadrant.x=x;
1547 quadrant.y=y;
1548 switch (i)
1549 {
1550 case 0:
1551 {
1552 quadrant.x=x-(ssize_t) (width-1);
1553 quadrant.y=y-(ssize_t) (width-1);
1554 break;
1555 }
1556 case 1:
1557 {
1558 quadrant.y=y-(ssize_t) (width-1);
1559 break;
1560 }
1561 case 2:
1562 {
1563 quadrant.x=x-(ssize_t) (width-1);
1564 break;
1565 }
1566 case 3:
1567 default:
1568 break;
1569 }
1570 p=GetCacheViewVirtualPixels(image_view,quadrant.x,quadrant.y,
1571 quadrant.width,quadrant.height,exception);
1572 if (p == (const Quantum *) NULL)
1573 break;
1574 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
cristyb4800612014-09-29 22:30:01 +00001575 mean[j]=0.0;
cristyee31ab82014-10-06 11:35:27 +00001576 k=p;
cristyfa971252014-10-05 23:55:56 +00001577 for (n=0; n < (ssize_t) (width*width); n++)
cristye67edc32014-10-02 13:39:48 +00001578 {
cristyee31ab82014-10-06 11:35:27 +00001579 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
cristye67edc32014-10-02 13:39:48 +00001580 mean[j]+=(double) k[j];
1581 k+=GetPixelChannels(image);
1582 }
cristyee31ab82014-10-06 11:35:27 +00001583 for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
cristydea96f32014-10-05 23:27:21 +00001584 mean[j]/=(double) (width*width);
cristyee31ab82014-10-06 11:35:27 +00001585 k=p;
cristye67edc32014-10-02 13:39:48 +00001586 variance=0.0;
cristyfa971252014-10-05 23:55:56 +00001587 for (n=0; n < (ssize_t) (width*width); n++)
cristyb4800612014-09-29 22:30:01 +00001588 {
cristy9e366d32014-09-30 18:45:56 +00001589 double
1590 luma;
1591
cristyee31ab82014-10-06 11:35:27 +00001592 luma=GetPixelLuma(gaussian_image,k);
cristye67edc32014-10-02 13:39:48 +00001593 variance+=(luma-GetMeanLuma(gaussian_image,mean))*
1594 (luma-GetMeanLuma(gaussian_image,mean));
1595 k+=GetPixelChannels(image);
cristy70965022014-09-29 23:26:43 +00001596 }
cristy9e366d32014-09-30 18:45:56 +00001597 if (variance < min_variance)
1598 {
1599 min_variance=variance;
cristyee31ab82014-10-06 11:35:27 +00001600 target=quadrant;
cristy9e366d32014-09-30 18:45:56 +00001601 }
cristyb4800612014-09-29 22:30:01 +00001602 }
cristyee31ab82014-10-06 11:35:27 +00001603 if (i < 4)
1604 {
1605 status=MagickFalse;
1606 break;
1607 }
1608 status=InterpolatePixelChannels(gaussian_image,image_view,kuwahara_image,
1609 UndefinedInterpolatePixel,(double) target.x+target.width/2.0,(double)
1610 target.y+target.height/2.0,q,exception);
cristy51b20df2014-09-29 18:52:28 +00001611 q+=GetPixelChannels(kuwahara_image);
1612 }
1613 if (SyncCacheViewAuthenticPixels(kuwahara_view,exception) == MagickFalse)
1614 status=MagickFalse;
1615 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1616 {
1617 MagickBooleanType
1618 proceed;
1619
1620#if defined(MAGICKCORE_OPENMP_SUPPORT)
1621 #pragma omp critical (MagickCore_KuwaharaImage)
1622#endif
1623 proceed=SetImageProgress(image,KuwaharaImageTag,progress++,image->rows);
1624 if (proceed == MagickFalse)
1625 status=MagickFalse;
1626 }
1627 }
1628 kuwahara_view=DestroyCacheView(kuwahara_view);
cristyee31ab82014-10-06 11:35:27 +00001629 image_view=DestroyCacheView(image_view);
cristy0562d772014-10-01 12:33:56 +00001630 gaussian_image=DestroyImage(gaussian_image);
cristy51b20df2014-09-29 18:52:28 +00001631 if (status == MagickFalse)
1632 kuwahara_image=DestroyImage(kuwahara_image);
cristy3b207f82014-09-27 14:21:20 +00001633 return(kuwahara_image);
1634}
1635
1636/*
1637%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1638% %
1639% %
1640% %
cristy3ed852e2009-09-05 21:47:34 +00001641% M o t i o n B l u r I m a g e %
1642% %
1643% %
1644% %
1645%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1646%
1647% MotionBlurImage() simulates motion blur. We convolve the image with a
1648% Gaussian operator of the given radius and standard deviation (sigma).
1649% For reasonable results, radius should be larger than sigma. Use a
1650% radius of 0 and MotionBlurImage() selects a suitable radius for you.
1651% Angle gives the angle of the blurring motion.
1652%
1653% Andrew Protano contributed this effect.
1654%
1655% The format of the MotionBlurImage method is:
1656%
1657% Image *MotionBlurImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00001658% const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001659%
1660% A description of each parameter follows:
1661%
1662% o image: the image.
1663%
cristy3ed852e2009-09-05 21:47:34 +00001664% o radius: the radius of the Gaussian, in pixels, not counting
1665% the center pixel.
1666%
1667% o sigma: the standard deviation of the Gaussian, in pixels.
1668%
cristycee97112010-05-28 00:44:52 +00001669% o angle: Apply the effect along this angle.
cristy3ed852e2009-09-05 21:47:34 +00001670%
1671% o exception: return any errors or warnings in this structure.
1672%
1673*/
1674
cristy0a887dc2012-08-15 22:58:36 +00001675static MagickRealType *GetMotionBlurKernel(const size_t width,
1676 const double sigma)
cristy3ed852e2009-09-05 21:47:34 +00001677{
cristy0a887dc2012-08-15 22:58:36 +00001678 MagickRealType
cristy47e00502009-12-17 19:19:57 +00001679 *kernel,
cristy3ed852e2009-09-05 21:47:34 +00001680 normalize;
1681
cristybb503372010-05-27 20:51:26 +00001682 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001683 i;
1684
1685 /*
cristy47e00502009-12-17 19:19:57 +00001686 Generate a 1-D convolution kernel.
cristy3ed852e2009-09-05 21:47:34 +00001687 */
1688 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristye42639a2012-08-23 01:53:24 +00001689 kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
1690 width,sizeof(*kernel)));
cristy0a887dc2012-08-15 22:58:36 +00001691 if (kernel == (MagickRealType *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001692 return(kernel);
cristy3ed852e2009-09-05 21:47:34 +00001693 normalize=0.0;
cristybb503372010-05-27 20:51:26 +00001694 for (i=0; i < (ssize_t) width; i++)
cristy47e00502009-12-17 19:19:57 +00001695 {
cristy0a887dc2012-08-15 22:58:36 +00001696 kernel[i]=(MagickRealType) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
cristy4205a3c2010-09-12 20:19:59 +00001697 MagickSigma)))/(MagickSQ2PI*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00001698 normalize+=kernel[i];
cristy47e00502009-12-17 19:19:57 +00001699 }
cristybb503372010-05-27 20:51:26 +00001700 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001701 kernel[i]/=normalize;
1702 return(kernel);
1703}
1704
cristya63e4a92011-09-09 00:47:59 +00001705MagickExport Image *MotionBlurImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00001706 const double sigma,const double angle,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001707{
cristy590c8292013-02-21 01:37:36 +00001708#define BlurImageTag "Blur/Image"
1709
cristyc4c8d132010-01-07 01:58:38 +00001710 CacheView
1711 *blur_view,
cristy8b49f382012-01-17 03:11:03 +00001712 *image_view,
1713 *motion_view;
cristyc4c8d132010-01-07 01:58:38 +00001714
cristy3ed852e2009-09-05 21:47:34 +00001715 Image
1716 *blur_image;
1717
cristy3ed852e2009-09-05 21:47:34 +00001718 MagickBooleanType
1719 status;
1720
cristybb503372010-05-27 20:51:26 +00001721 MagickOffsetType
1722 progress;
1723
cristy0a887dc2012-08-15 22:58:36 +00001724 MagickRealType
1725 *kernel;
1726
cristy3ed852e2009-09-05 21:47:34 +00001727 OffsetInfo
1728 *offset;
1729
1730 PointInfo
1731 point;
1732
cristybb503372010-05-27 20:51:26 +00001733 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001734 i;
1735
cristybb503372010-05-27 20:51:26 +00001736 size_t
cristy3ed852e2009-09-05 21:47:34 +00001737 width;
1738
cristybb503372010-05-27 20:51:26 +00001739 ssize_t
1740 y;
1741
cristy3ed852e2009-09-05 21:47:34 +00001742 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001743 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001744 if (image->debug != MagickFalse)
1745 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1746 assert(exception != (ExceptionInfo *) NULL);
1747 width=GetOptimalKernelWidth1D(radius,sigma);
1748 kernel=GetMotionBlurKernel(width,sigma);
cristy0a887dc2012-08-15 22:58:36 +00001749 if (kernel == (MagickRealType *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001750 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1751 offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
1752 if (offset == (OffsetInfo *) NULL)
1753 {
cristy0a887dc2012-08-15 22:58:36 +00001754 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +00001755 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1756 }
cristy1e7aa312011-09-10 20:01:36 +00001757 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001758 if (blur_image == (Image *) NULL)
1759 {
cristy0a887dc2012-08-15 22:58:36 +00001760 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +00001761 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1762 return((Image *) NULL);
1763 }
cristy574cc262011-08-05 01:23:58 +00001764 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001765 {
cristy0a887dc2012-08-15 22:58:36 +00001766 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +00001767 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
cristy3ed852e2009-09-05 21:47:34 +00001768 blur_image=DestroyImage(blur_image);
1769 return((Image *) NULL);
1770 }
1771 point.x=(double) width*sin(DegreesToRadians(angle));
1772 point.y=(double) width*cos(DegreesToRadians(angle));
cristybb503372010-05-27 20:51:26 +00001773 for (i=0; i < (ssize_t) width; i++)
cristy3ed852e2009-09-05 21:47:34 +00001774 {
cristybb503372010-05-27 20:51:26 +00001775 offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
1776 offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
cristy3ed852e2009-09-05 21:47:34 +00001777 }
1778 /*
1779 Motion blur image.
1780 */
1781 status=MagickTrue;
1782 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001783 image_view=AcquireVirtualCacheView(image,exception);
1784 motion_view=AcquireVirtualCacheView(image,exception);
1785 blur_view=AcquireAuthenticCacheView(blur_image,exception);
cristyb557a152011-02-22 12:14:30 +00001786#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001787 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001788 magick_threads(image,blur_image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00001789#endif
cristybb503372010-05-27 20:51:26 +00001790 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001791 {
cristyf7ef0252011-09-09 14:50:06 +00001792 register const Quantum
1793 *restrict p;
1794
cristy4c08aed2011-07-01 19:47:50 +00001795 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001796 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001797
cristy117ff172010-08-15 21:35:32 +00001798 register ssize_t
1799 x;
1800
cristy3ed852e2009-09-05 21:47:34 +00001801 if (status == MagickFalse)
1802 continue;
cristy8b49f382012-01-17 03:11:03 +00001803 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1804 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00001805 exception);
cristyf7ef0252011-09-09 14:50:06 +00001806 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001807 {
1808 status=MagickFalse;
1809 continue;
1810 }
cristybb503372010-05-27 20:51:26 +00001811 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001812 {
cristybb503372010-05-27 20:51:26 +00001813 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001814 i;
1815
cristyf7ef0252011-09-09 14:50:06 +00001816 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1817 {
cristya19f1d72012-08-07 18:24:38 +00001818 double
cristyf7ef0252011-09-09 14:50:06 +00001819 alpha,
1820 gamma,
1821 pixel;
cristy3ed852e2009-09-05 21:47:34 +00001822
cristy633067b2013-02-21 01:15:12 +00001823 PixelChannel
1824 channel;
1825
1826 PixelTrait
1827 blur_traits,
1828 traits;
1829
cristyf7ef0252011-09-09 14:50:06 +00001830 register const Quantum
1831 *restrict r;
1832
cristy0a887dc2012-08-15 22:58:36 +00001833 register MagickRealType
cristyf7ef0252011-09-09 14:50:06 +00001834 *restrict k;
1835
1836 register ssize_t
1837 j;
1838
cristy633067b2013-02-21 01:15:12 +00001839 channel=GetPixelChannelChannel(image,i);
1840 traits=GetPixelChannelTraits(image,channel);
1841 blur_traits=GetPixelChannelTraits(blur_image,channel);
cristyf7ef0252011-09-09 14:50:06 +00001842 if ((traits == UndefinedPixelTrait) ||
1843 (blur_traits == UndefinedPixelTrait))
1844 continue;
cristy1eced092012-08-10 23:10:56 +00001845 if (((blur_traits & CopyPixelTrait) != 0) ||
cristy883fde12013-04-08 00:50:13 +00001846 (GetPixelReadMask(image,p) == 0))
cristy3ed852e2009-09-05 21:47:34 +00001847 {
cristy0beccfa2011-09-25 20:47:53 +00001848 SetPixelChannel(blur_image,channel,p[i],q);
cristyf7ef0252011-09-09 14:50:06 +00001849 continue;
cristy3ed852e2009-09-05 21:47:34 +00001850 }
cristyf7ef0252011-09-09 14:50:06 +00001851 k=kernel;
cristyaa2c16c2012-03-25 22:21:35 +00001852 pixel=0.0;
cristyf7ef0252011-09-09 14:50:06 +00001853 if ((blur_traits & BlendPixelTrait) == 0)
1854 {
1855 for (j=0; j < (ssize_t) width; j++)
1856 {
cristy8b49f382012-01-17 03:11:03 +00001857 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+
cristyf7ef0252011-09-09 14:50:06 +00001858 offset[j].y,1,1,exception);
1859 if (r == (const Quantum *) NULL)
1860 {
1861 status=MagickFalse;
1862 continue;
1863 }
1864 pixel+=(*k)*r[i];
1865 k++;
1866 }
cristy0beccfa2011-09-25 20:47:53 +00001867 SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
cristyf7ef0252011-09-09 14:50:06 +00001868 continue;
1869 }
1870 alpha=0.0;
1871 gamma=0.0;
1872 for (j=0; j < (ssize_t) width; j++)
1873 {
cristy8b49f382012-01-17 03:11:03 +00001874 r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+offset[j].y,1,
cristyf7ef0252011-09-09 14:50:06 +00001875 1,exception);
1876 if (r == (const Quantum *) NULL)
1877 {
1878 status=MagickFalse;
1879 continue;
1880 }
cristya19f1d72012-08-07 18:24:38 +00001881 alpha=(double) (QuantumScale*GetPixelAlpha(image,r));
cristyf7ef0252011-09-09 14:50:06 +00001882 pixel+=(*k)*alpha*r[i];
1883 gamma+=(*k)*alpha;
1884 k++;
cristy3ed852e2009-09-05 21:47:34 +00001885 }
cristy3e3ec3a2012-11-03 23:11:06 +00001886 gamma=PerceptibleReciprocal(gamma);
cristy0beccfa2011-09-25 20:47:53 +00001887 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristyf7ef0252011-09-09 14:50:06 +00001888 }
1889 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00001890 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00001891 }
1892 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1893 status=MagickFalse;
1894 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1895 {
1896 MagickBooleanType
1897 proceed;
1898
cristyb557a152011-02-22 12:14:30 +00001899#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +00001900 #pragma omp critical (MagickCore_MotionBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00001901#endif
1902 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
1903 if (proceed == MagickFalse)
1904 status=MagickFalse;
1905 }
1906 }
1907 blur_view=DestroyCacheView(blur_view);
cristy8b49f382012-01-17 03:11:03 +00001908 motion_view=DestroyCacheView(motion_view);
cristy3ed852e2009-09-05 21:47:34 +00001909 image_view=DestroyCacheView(image_view);
cristy0a887dc2012-08-15 22:58:36 +00001910 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +00001911 offset=(OffsetInfo *) RelinquishMagickMemory(offset);
1912 if (status == MagickFalse)
1913 blur_image=DestroyImage(blur_image);
1914 return(blur_image);
1915}
1916
1917/*
1918%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1919% %
1920% %
1921% %
1922% P r e v i e w I m a g e %
1923% %
1924% %
1925% %
1926%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1927%
1928% PreviewImage() tiles 9 thumbnails of the specified image with an image
1929% processing operation applied with varying parameters. This may be helpful
1930% pin-pointing an appropriate parameter for a particular image processing
1931% operation.
1932%
1933% The format of the PreviewImages method is:
1934%
1935% Image *PreviewImages(const Image *image,const PreviewType preview,
1936% ExceptionInfo *exception)
1937%
1938% A description of each parameter follows:
1939%
1940% o image: the image.
1941%
1942% o preview: the image processing operation.
1943%
1944% o exception: return any errors or warnings in this structure.
1945%
1946*/
1947MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
1948 ExceptionInfo *exception)
1949{
1950#define NumberTiles 9
1951#define PreviewImageTag "Preview/Image"
1952#define DefaultPreviewGeometry "204x204+10+10"
1953
1954 char
cristy151b66d2015-04-15 10:50:31 +00001955 factor[MagickPathExtent],
1956 label[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001957
1958 double
1959 degrees,
1960 gamma,
1961 percentage,
1962 radius,
1963 sigma,
1964 threshold;
1965
cristybcdf5672012-05-24 22:58:54 +00001966 extern const char
1967 DefaultTileFrame[];
1968
cristy3ed852e2009-09-05 21:47:34 +00001969 Image
1970 *images,
1971 *montage_image,
1972 *preview_image,
1973 *thumbnail;
1974
1975 ImageInfo
1976 *preview_info;
1977
cristy3ed852e2009-09-05 21:47:34 +00001978 MagickBooleanType
1979 proceed;
1980
1981 MontageInfo
1982 *montage_info;
1983
1984 QuantizeInfo
1985 quantize_info;
1986
1987 RectangleInfo
1988 geometry;
1989
cristybb503372010-05-27 20:51:26 +00001990 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001991 i,
1992 x;
1993
cristybb503372010-05-27 20:51:26 +00001994 size_t
cristy3ed852e2009-09-05 21:47:34 +00001995 colors;
1996
cristy117ff172010-08-15 21:35:32 +00001997 ssize_t
1998 y;
1999
cristy3ed852e2009-09-05 21:47:34 +00002000 /*
2001 Open output image file.
2002 */
2003 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002004 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002005 if (image->debug != MagickFalse)
2006 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2007 colors=2;
2008 degrees=0.0;
2009 gamma=(-0.2f);
2010 preview_info=AcquireImageInfo();
2011 SetGeometry(image,&geometry);
2012 (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2013 &geometry.width,&geometry.height);
2014 images=NewImageList();
2015 percentage=12.5;
2016 GetQuantizeInfo(&quantize_info);
2017 radius=0.0;
2018 sigma=1.0;
2019 threshold=0.0;
2020 x=0;
2021 y=0;
2022 for (i=0; i < NumberTiles; i++)
2023 {
2024 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2025 if (thumbnail == (Image *) NULL)
2026 break;
2027 (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2028 (void *) NULL);
cristyd15e6592011-10-15 00:13:06 +00002029 (void) SetImageProperty(thumbnail,"label",DefaultTileLabel,exception);
cristy3ed852e2009-09-05 21:47:34 +00002030 if (i == (NumberTiles/2))
2031 {
cristy9950d572011-10-01 18:22:35 +00002032 (void) QueryColorCompliance("#dfdfdf",AllCompliance,
2033 &thumbnail->matte_color,exception);
cristy3ed852e2009-09-05 21:47:34 +00002034 AppendImageToList(&images,thumbnail);
2035 continue;
2036 }
2037 switch (preview)
2038 {
2039 case RotatePreview:
2040 {
2041 degrees+=45.0;
2042 preview_image=RotateImage(thumbnail,degrees,exception);
cristy151b66d2015-04-15 10:50:31 +00002043 (void) FormatLocaleString(label,MagickPathExtent,"rotate %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002044 break;
2045 }
2046 case ShearPreview:
2047 {
2048 degrees+=5.0;
2049 preview_image=ShearImage(thumbnail,degrees,degrees,exception);
cristy151b66d2015-04-15 10:50:31 +00002050 (void) FormatLocaleString(label,MagickPathExtent,"shear %gx%g",degrees,
cristyd9fdd232013-01-19 00:14:59 +00002051 2.0*degrees);
cristy3ed852e2009-09-05 21:47:34 +00002052 break;
2053 }
2054 case RollPreview:
2055 {
cristybb503372010-05-27 20:51:26 +00002056 x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2057 y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
cristy3ed852e2009-09-05 21:47:34 +00002058 preview_image=RollImage(thumbnail,x,y,exception);
cristy151b66d2015-04-15 10:50:31 +00002059 (void) FormatLocaleString(label,MagickPathExtent,"roll %+.20gx%+.20g",
cristye8c25f92010-06-03 00:53:06 +00002060 (double) x,(double) y);
cristy3ed852e2009-09-05 21:47:34 +00002061 break;
2062 }
2063 case HuePreview:
2064 {
2065 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2066 if (preview_image == (Image *) NULL)
2067 break;
cristy151b66d2015-04-15 10:50:31 +00002068 (void) FormatLocaleString(factor,MagickPathExtent,"100,100,%g",2.0*
cristyd9fdd232013-01-19 00:14:59 +00002069 percentage);
cristy33bd5152011-08-24 01:42:24 +00002070 (void) ModulateImage(preview_image,factor,exception);
cristy151b66d2015-04-15 10:50:31 +00002071 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002072 break;
2073 }
2074 case SaturationPreview:
2075 {
2076 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2077 if (preview_image == (Image *) NULL)
2078 break;
cristy151b66d2015-04-15 10:50:31 +00002079 (void) FormatLocaleString(factor,MagickPathExtent,"100,%g",2.0*percentage);
cristy33bd5152011-08-24 01:42:24 +00002080 (void) ModulateImage(preview_image,factor,exception);
cristy151b66d2015-04-15 10:50:31 +00002081 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002082 break;
2083 }
2084 case BrightnessPreview:
2085 {
2086 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2087 if (preview_image == (Image *) NULL)
2088 break;
cristy151b66d2015-04-15 10:50:31 +00002089 (void) FormatLocaleString(factor,MagickPathExtent,"%g",2.0*percentage);
cristy33bd5152011-08-24 01:42:24 +00002090 (void) ModulateImage(preview_image,factor,exception);
cristy151b66d2015-04-15 10:50:31 +00002091 (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002092 break;
2093 }
2094 case GammaPreview:
2095 default:
2096 {
2097 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2098 if (preview_image == (Image *) NULL)
2099 break;
2100 gamma+=0.4f;
cristyb3e7c6c2011-07-24 01:43:55 +00002101 (void) GammaImage(preview_image,gamma,exception);
cristy151b66d2015-04-15 10:50:31 +00002102 (void) FormatLocaleString(label,MagickPathExtent,"gamma %g",gamma);
cristy3ed852e2009-09-05 21:47:34 +00002103 break;
2104 }
2105 case SpiffPreview:
2106 {
2107 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2108 if (preview_image != (Image *) NULL)
2109 for (x=0; x < i; x++)
cristye23ec9d2011-08-16 18:15:40 +00002110 (void) ContrastImage(preview_image,MagickTrue,exception);
cristy151b66d2015-04-15 10:50:31 +00002111 (void) FormatLocaleString(label,MagickPathExtent,"contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002112 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002113 break;
2114 }
2115 case DullPreview:
2116 {
2117 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2118 if (preview_image == (Image *) NULL)
2119 break;
2120 for (x=0; x < i; x++)
cristye23ec9d2011-08-16 18:15:40 +00002121 (void) ContrastImage(preview_image,MagickFalse,exception);
cristy151b66d2015-04-15 10:50:31 +00002122 (void) FormatLocaleString(label,MagickPathExtent,"+contrast (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002123 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002124 break;
2125 }
2126 case GrayscalePreview:
2127 {
2128 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2129 if (preview_image == (Image *) NULL)
2130 break;
2131 colors<<=1;
2132 quantize_info.number_colors=colors;
2133 quantize_info.colorspace=GRAYColorspace;
cristy018f07f2011-09-04 21:15:19 +00002134 (void) QuantizeImage(&quantize_info,preview_image,exception);
cristy151b66d2015-04-15 10:50:31 +00002135 (void) FormatLocaleString(label,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00002136 "-colorspace gray -colors %.20g",(double) colors);
cristy3ed852e2009-09-05 21:47:34 +00002137 break;
2138 }
2139 case QuantizePreview:
2140 {
2141 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2142 if (preview_image == (Image *) NULL)
2143 break;
2144 colors<<=1;
2145 quantize_info.number_colors=colors;
cristy018f07f2011-09-04 21:15:19 +00002146 (void) QuantizeImage(&quantize_info,preview_image,exception);
cristy151b66d2015-04-15 10:50:31 +00002147 (void) FormatLocaleString(label,MagickPathExtent,"colors %.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002148 colors);
cristy3ed852e2009-09-05 21:47:34 +00002149 break;
2150 }
2151 case DespecklePreview:
2152 {
2153 for (x=0; x < (i-1); x++)
2154 {
2155 preview_image=DespeckleImage(thumbnail,exception);
2156 if (preview_image == (Image *) NULL)
2157 break;
2158 thumbnail=DestroyImage(thumbnail);
2159 thumbnail=preview_image;
2160 }
2161 preview_image=DespeckleImage(thumbnail,exception);
2162 if (preview_image == (Image *) NULL)
2163 break;
cristy151b66d2015-04-15 10:50:31 +00002164 (void) FormatLocaleString(label,MagickPathExtent,"despeckle (%.20g)",
cristye8c25f92010-06-03 00:53:06 +00002165 (double) i+1);
cristy3ed852e2009-09-05 21:47:34 +00002166 break;
2167 }
2168 case ReduceNoisePreview:
2169 {
cristy95c38342011-03-18 22:39:51 +00002170 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2171 (size_t) radius,exception);
cristy151b66d2015-04-15 10:50:31 +00002172 (void) FormatLocaleString(label,MagickPathExtent,"noise %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002173 break;
2174 }
2175 case AddNoisePreview:
2176 {
2177 switch ((int) i)
2178 {
2179 case 0:
2180 {
cristy151b66d2015-04-15 10:50:31 +00002181 (void) CopyMagickString(factor,"uniform",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00002182 break;
2183 }
2184 case 1:
2185 {
cristy151b66d2015-04-15 10:50:31 +00002186 (void) CopyMagickString(factor,"gaussian",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00002187 break;
2188 }
2189 case 2:
2190 {
cristy151b66d2015-04-15 10:50:31 +00002191 (void) CopyMagickString(factor,"multiplicative",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00002192 break;
2193 }
2194 case 3:
2195 {
cristy151b66d2015-04-15 10:50:31 +00002196 (void) CopyMagickString(factor,"impulse",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00002197 break;
2198 }
cristyf2a82ee2014-05-26 17:49:54 +00002199 case 5:
cristy3ed852e2009-09-05 21:47:34 +00002200 {
cristy151b66d2015-04-15 10:50:31 +00002201 (void) CopyMagickString(factor,"laplacian",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00002202 break;
2203 }
cristyf2a82ee2014-05-26 17:49:54 +00002204 case 6:
cristy3ed852e2009-09-05 21:47:34 +00002205 {
cristy151b66d2015-04-15 10:50:31 +00002206 (void) CopyMagickString(factor,"Poisson",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00002207 break;
2208 }
2209 default:
2210 {
cristy151b66d2015-04-15 10:50:31 +00002211 (void) CopyMagickString(thumbnail->magick,"NULL",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00002212 break;
2213 }
2214 }
cristyd76c51e2011-03-26 00:21:26 +00002215 preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2216 (size_t) i,exception);
cristy151b66d2015-04-15 10:50:31 +00002217 (void) FormatLocaleString(label,MagickPathExtent,"+noise %s",factor);
cristy3ed852e2009-09-05 21:47:34 +00002218 break;
2219 }
2220 case SharpenPreview:
2221 {
cristyaa2c16c2012-03-25 22:21:35 +00002222 preview_image=SharpenImage(thumbnail,radius,sigma,exception);
cristy151b66d2015-04-15 10:50:31 +00002223 (void) FormatLocaleString(label,MagickPathExtent,"sharpen %gx%g",radius,
cristyd9fdd232013-01-19 00:14:59 +00002224 sigma);
cristy3ed852e2009-09-05 21:47:34 +00002225 break;
2226 }
2227 case BlurPreview:
2228 {
cristyaa2c16c2012-03-25 22:21:35 +00002229 preview_image=BlurImage(thumbnail,radius,sigma,exception);
cristy151b66d2015-04-15 10:50:31 +00002230 (void) FormatLocaleString(label,MagickPathExtent,"blur %gx%g",radius,
cristy3ed852e2009-09-05 21:47:34 +00002231 sigma);
2232 break;
2233 }
2234 case ThresholdPreview:
2235 {
2236 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2237 if (preview_image == (Image *) NULL)
2238 break;
cristya19f1d72012-08-07 18:24:38 +00002239 (void) BilevelImage(thumbnail,(double) (percentage*((double)
cristye941a752011-10-15 01:52:48 +00002240 QuantumRange+1.0))/100.0,exception);
cristy151b66d2015-04-15 10:50:31 +00002241 (void) FormatLocaleString(label,MagickPathExtent,"threshold %g",(double)
cristyd9fdd232013-01-19 00:14:59 +00002242 (percentage*((double) QuantumRange+1.0))/100.0);
cristy3ed852e2009-09-05 21:47:34 +00002243 break;
2244 }
2245 case EdgeDetectPreview:
2246 {
cristy9dc4c512013-03-24 01:38:00 +00002247 preview_image=EdgeImage(thumbnail,radius,exception);
cristy151b66d2015-04-15 10:50:31 +00002248 (void) FormatLocaleString(label,MagickPathExtent,"edge %g",radius);
cristy3ed852e2009-09-05 21:47:34 +00002249 break;
2250 }
2251 case SpreadPreview:
2252 {
Cristycc332e32015-08-09 12:15:50 -04002253 preview_image=SpreadImage(thumbnail,radius,exception);
2254 (void) FormatLocaleString(label,MagickPathExtent,"spread %g",
2255 radius+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002256 break;
2257 }
2258 case SolarizePreview:
2259 {
2260 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2261 if (preview_image == (Image *) NULL)
2262 break;
cristyd9fdd232013-01-19 00:14:59 +00002263 (void) SolarizeImage(preview_image,(double) QuantumRange*percentage/
2264 100.0,exception);
cristy151b66d2015-04-15 10:50:31 +00002265 (void) FormatLocaleString(label,MagickPathExtent,"solarize %g",
cristy3ed852e2009-09-05 21:47:34 +00002266 (QuantumRange*percentage)/100.0);
2267 break;
2268 }
2269 case ShadePreview:
2270 {
2271 degrees+=10.0;
2272 preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2273 exception);
cristy151b66d2015-04-15 10:50:31 +00002274 (void) FormatLocaleString(label,MagickPathExtent,"shade %gx%g",degrees,
cristyd9fdd232013-01-19 00:14:59 +00002275 degrees);
cristy3ed852e2009-09-05 21:47:34 +00002276 break;
2277 }
2278 case RaisePreview:
2279 {
2280 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2281 if (preview_image == (Image *) NULL)
2282 break;
cristybb503372010-05-27 20:51:26 +00002283 geometry.width=(size_t) (2*i+2);
2284 geometry.height=(size_t) (2*i+2);
cristy6b516212013-03-22 19:20:32 +00002285 geometry.x=(i-1)/2;
2286 geometry.y=(i-1)/2;
cristy6170ac32011-08-28 14:15:37 +00002287 (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
cristy151b66d2015-04-15 10:50:31 +00002288 (void) FormatLocaleString(label,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +00002289 "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
cristye8c25f92010-06-03 00:53:06 +00002290 geometry.height,(double) geometry.x,(double) geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00002291 break;
2292 }
2293 case SegmentPreview:
2294 {
2295 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2296 if (preview_image == (Image *) NULL)
2297 break;
2298 threshold+=0.4f;
cristyc511e882012-04-16 21:11:14 +00002299 (void) SegmentImage(preview_image,sRGBColorspace,MagickFalse,threshold,
cristy018f07f2011-09-04 21:15:19 +00002300 threshold,exception);
cristy151b66d2015-04-15 10:50:31 +00002301 (void) FormatLocaleString(label,MagickPathExtent,"segment %gx%g",
cristy3ed852e2009-09-05 21:47:34 +00002302 threshold,threshold);
2303 break;
2304 }
2305 case SwirlPreview:
2306 {
cristy76f512e2011-09-12 01:26:56 +00002307 preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2308 exception);
cristy151b66d2015-04-15 10:50:31 +00002309 (void) FormatLocaleString(label,MagickPathExtent,"swirl %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002310 degrees+=45.0;
2311 break;
2312 }
2313 case ImplodePreview:
2314 {
2315 degrees+=0.1f;
cristy76f512e2011-09-12 01:26:56 +00002316 preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2317 exception);
cristy151b66d2015-04-15 10:50:31 +00002318 (void) FormatLocaleString(label,MagickPathExtent,"implode %g",degrees);
cristy3ed852e2009-09-05 21:47:34 +00002319 break;
2320 }
2321 case WavePreview:
2322 {
2323 degrees+=5.0f;
cristy5c4e2582011-09-11 19:21:03 +00002324 preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2325 image->interpolate,exception);
cristy151b66d2015-04-15 10:50:31 +00002326 (void) FormatLocaleString(label,MagickPathExtent,"wave %gx%g",0.5*degrees,
cristyd9fdd232013-01-19 00:14:59 +00002327 2.0*degrees);
cristy3ed852e2009-09-05 21:47:34 +00002328 break;
2329 }
2330 case OilPaintPreview:
2331 {
cristy14973ba2011-08-27 23:48:07 +00002332 preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2333 exception);
cristy151b66d2015-04-15 10:50:31 +00002334 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",radius,
cristyd9fdd232013-01-19 00:14:59 +00002335 sigma);
cristy3ed852e2009-09-05 21:47:34 +00002336 break;
2337 }
2338 case CharcoalDrawingPreview:
2339 {
2340 preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
cristyaa2c16c2012-03-25 22:21:35 +00002341 exception);
cristy151b66d2015-04-15 10:50:31 +00002342 (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",radius,
cristyd9fdd232013-01-19 00:14:59 +00002343 sigma);
cristy3ed852e2009-09-05 21:47:34 +00002344 break;
2345 }
2346 case JPEGPreview:
2347 {
2348 char
cristy151b66d2015-04-15 10:50:31 +00002349 filename[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00002350
2351 int
2352 file;
2353
2354 MagickBooleanType
2355 status;
2356
2357 preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2358 if (preview_image == (Image *) NULL)
2359 break;
cristybb503372010-05-27 20:51:26 +00002360 preview_info->quality=(size_t) percentage;
cristy151b66d2015-04-15 10:50:31 +00002361 (void) FormatLocaleString(factor,MagickPathExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002362 preview_info->quality);
cristy3ed852e2009-09-05 21:47:34 +00002363 file=AcquireUniqueFileResource(filename);
2364 if (file != -1)
2365 file=close(file)-1;
cristy151b66d2015-04-15 10:50:31 +00002366 (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00002367 "jpeg:%s",filename);
cristy6f9e0d32011-08-28 16:32:09 +00002368 status=WriteImage(preview_info,preview_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00002369 if (status != MagickFalse)
2370 {
2371 Image
2372 *quality_image;
2373
2374 (void) CopyMagickString(preview_info->filename,
cristy151b66d2015-04-15 10:50:31 +00002375 preview_image->filename,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00002376 quality_image=ReadImage(preview_info,exception);
2377 if (quality_image != (Image *) NULL)
2378 {
2379 preview_image=DestroyImage(preview_image);
2380 preview_image=quality_image;
2381 }
2382 }
2383 (void) RelinquishUniqueFileResource(preview_image->filename);
2384 if ((GetBlobSize(preview_image)/1024) >= 1024)
cristy151b66d2015-04-15 10:50:31 +00002385 (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%gmb ",
cristy3ed852e2009-09-05 21:47:34 +00002386 factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2387 1024.0/1024.0);
2388 else
2389 if (GetBlobSize(preview_image) >= 1024)
cristy151b66d2015-04-15 10:50:31 +00002390 (void) FormatLocaleString(label,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00002391 "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
cristy8cd5b312010-01-07 01:10:24 +00002392 GetBlobSize(preview_image))/1024.0);
cristy3ed852e2009-09-05 21:47:34 +00002393 else
cristy151b66d2015-04-15 10:50:31 +00002394 (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%.20gb ",
cristy54ea5732011-06-10 12:39:53 +00002395 factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
cristy3ed852e2009-09-05 21:47:34 +00002396 break;
2397 }
2398 }
2399 thumbnail=DestroyImage(thumbnail);
2400 percentage+=12.5;
2401 radius+=0.5;
2402 sigma+=0.25;
2403 if (preview_image == (Image *) NULL)
2404 break;
2405 (void) DeleteImageProperty(preview_image,"label");
cristyd15e6592011-10-15 00:13:06 +00002406 (void) SetImageProperty(preview_image,"label",label,exception);
cristy3ed852e2009-09-05 21:47:34 +00002407 AppendImageToList(&images,preview_image);
cristybb503372010-05-27 20:51:26 +00002408 proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2409 NumberTiles);
cristy3ed852e2009-09-05 21:47:34 +00002410 if (proceed == MagickFalse)
2411 break;
2412 }
2413 if (images == (Image *) NULL)
2414 {
2415 preview_info=DestroyImageInfo(preview_info);
2416 return((Image *) NULL);
2417 }
2418 /*
2419 Create the montage.
2420 */
2421 montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
cristy151b66d2015-04-15 10:50:31 +00002422 (void) CopyMagickString(montage_info->filename,image->filename,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00002423 montage_info->shadow=MagickTrue;
2424 (void) CloneString(&montage_info->tile,"3x3");
2425 (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2426 (void) CloneString(&montage_info->frame,DefaultTileFrame);
2427 montage_image=MontageImages(images,montage_info,exception);
2428 montage_info=DestroyMontageInfo(montage_info);
2429 images=DestroyImageList(images);
2430 if (montage_image == (Image *) NULL)
2431 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2432 if (montage_image->montage != (char *) NULL)
2433 {
2434 /*
2435 Free image directory.
2436 */
2437 montage_image->montage=(char *) RelinquishMagickMemory(
2438 montage_image->montage);
2439 if (image->directory != (char *) NULL)
2440 montage_image->directory=(char *) RelinquishMagickMemory(
2441 montage_image->directory);
2442 }
2443 preview_info=DestroyImageInfo(preview_info);
2444 return(montage_image);
2445}
2446
2447/*
2448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2449% %
2450% %
2451% %
dirk6d612cf2014-03-13 21:17:23 +00002452% R o t a t i o n a l B l u r I m a g e %
cristy3ed852e2009-09-05 21:47:34 +00002453% %
2454% %
2455% %
2456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2457%
dirk6d612cf2014-03-13 21:17:23 +00002458% RotationalBlurImage() applies a radial blur to the image.
cristy3ed852e2009-09-05 21:47:34 +00002459%
2460% Andrew Protano contributed this effect.
2461%
dirk6d612cf2014-03-13 21:17:23 +00002462% The format of the RotationalBlurImage method is:
cristy3ed852e2009-09-05 21:47:34 +00002463%
dirk6d612cf2014-03-13 21:17:23 +00002464% Image *RotationalBlurImage(const Image *image,const double angle,
cristyaa2c16c2012-03-25 22:21:35 +00002465% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002466%
2467% A description of each parameter follows:
2468%
2469% o image: the image.
2470%
cristy3ed852e2009-09-05 21:47:34 +00002471% o angle: the angle of the radial blur.
2472%
cristy6435bd92011-09-10 02:10:07 +00002473% o blur: the blur.
2474%
cristy3ed852e2009-09-05 21:47:34 +00002475% o exception: return any errors or warnings in this structure.
2476%
2477*/
dirk6d612cf2014-03-13 21:17:23 +00002478MagickExport Image *RotationalBlurImage(const Image *image,const double angle,
cristyaa2c16c2012-03-25 22:21:35 +00002479 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002480{
cristyc4c8d132010-01-07 01:58:38 +00002481 CacheView
2482 *blur_view,
cristy8b49f382012-01-17 03:11:03 +00002483 *image_view,
2484 *radial_view;
cristyc4c8d132010-01-07 01:58:38 +00002485
cristyadc349b2014-12-06 21:40:39 +00002486 double
2487 blur_radius,
2488 *cos_theta,
2489 offset,
2490 *sin_theta,
2491 theta;
2492
cristy3ed852e2009-09-05 21:47:34 +00002493 Image
2494 *blur_image;
2495
cristy3ed852e2009-09-05 21:47:34 +00002496 MagickBooleanType
2497 status;
2498
cristybb503372010-05-27 20:51:26 +00002499 MagickOffsetType
2500 progress;
2501
cristy3ed852e2009-09-05 21:47:34 +00002502 PointInfo
2503 blur_center;
2504
cristybb503372010-05-27 20:51:26 +00002505 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002506 i;
2507
cristybb503372010-05-27 20:51:26 +00002508 size_t
cristy3ed852e2009-09-05 21:47:34 +00002509 n;
2510
cristybb503372010-05-27 20:51:26 +00002511 ssize_t
2512 y;
2513
cristy3ed852e2009-09-05 21:47:34 +00002514 /*
2515 Allocate blur image.
2516 */
2517 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002518 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002519 if (image->debug != MagickFalse)
2520 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2521 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002522 assert(exception->signature == MagickCoreSignature);
cristy1e7aa312011-09-10 20:01:36 +00002523 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00002524 if (blur_image == (Image *) NULL)
2525 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00002526 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002527 {
cristy3ed852e2009-09-05 21:47:34 +00002528 blur_image=DestroyImage(blur_image);
2529 return((Image *) NULL);
2530 }
cristy6b516212013-03-22 19:20:32 +00002531 blur_center.x=(double) (image->columns-1)/2.0;
2532 blur_center.y=(double) (image->rows-1)/2.0;
cristy3ed852e2009-09-05 21:47:34 +00002533 blur_radius=hypot(blur_center.x,blur_center.y);
cristy117ff172010-08-15 21:35:32 +00002534 n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
cristya19f1d72012-08-07 18:24:38 +00002535 theta=DegreesToRadians(angle)/(double) (n-1);
2536 cos_theta=(double *) AcquireQuantumMemory((size_t) n,
cristy3ed852e2009-09-05 21:47:34 +00002537 sizeof(*cos_theta));
cristya19f1d72012-08-07 18:24:38 +00002538 sin_theta=(double *) AcquireQuantumMemory((size_t) n,
cristy3ed852e2009-09-05 21:47:34 +00002539 sizeof(*sin_theta));
cristya19f1d72012-08-07 18:24:38 +00002540 if ((cos_theta == (double *) NULL) ||
2541 (sin_theta == (double *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002542 {
2543 blur_image=DestroyImage(blur_image);
2544 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2545 }
cristya19f1d72012-08-07 18:24:38 +00002546 offset=theta*(double) (n-1)/2.0;
cristybb503372010-05-27 20:51:26 +00002547 for (i=0; i < (ssize_t) n; i++)
cristy3ed852e2009-09-05 21:47:34 +00002548 {
2549 cos_theta[i]=cos((double) (theta*i-offset));
2550 sin_theta[i]=sin((double) (theta*i-offset));
2551 }
2552 /*
2553 Radial blur image.
2554 */
2555 status=MagickTrue;
2556 progress=0;
cristy46ff2672012-12-14 15:32:26 +00002557 image_view=AcquireVirtualCacheView(image,exception);
2558 radial_view=AcquireVirtualCacheView(image,exception);
2559 blur_view=AcquireAuthenticCacheView(blur_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00002560#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002561 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00002562 magick_threads(image,blur_image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00002563#endif
cristy1e7aa312011-09-10 20:01:36 +00002564 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002565 {
cristy1e7aa312011-09-10 20:01:36 +00002566 register const Quantum
2567 *restrict p;
2568
cristy4c08aed2011-07-01 19:47:50 +00002569 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002570 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002571
cristy117ff172010-08-15 21:35:32 +00002572 register ssize_t
2573 x;
2574
cristy3ed852e2009-09-05 21:47:34 +00002575 if (status == MagickFalse)
2576 continue;
cristy8b49f382012-01-17 03:11:03 +00002577 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2578 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00002579 exception);
cristy1e7aa312011-09-10 20:01:36 +00002580 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002581 {
2582 status=MagickFalse;
2583 continue;
2584 }
cristy1e7aa312011-09-10 20:01:36 +00002585 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002586 {
cristya19f1d72012-08-07 18:24:38 +00002587 double
cristy3ed852e2009-09-05 21:47:34 +00002588 radius;
2589
cristy3ed852e2009-09-05 21:47:34 +00002590 PointInfo
2591 center;
2592
cristybb503372010-05-27 20:51:26 +00002593 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002594 i;
2595
cristybb503372010-05-27 20:51:26 +00002596 size_t
cristy3ed852e2009-09-05 21:47:34 +00002597 step;
2598
2599 center.x=(double) x-blur_center.x;
2600 center.y=(double) y-blur_center.y;
2601 radius=hypot((double) center.x,center.y);
2602 if (radius == 0)
2603 step=1;
2604 else
2605 {
cristybb503372010-05-27 20:51:26 +00002606 step=(size_t) (blur_radius/radius);
cristy3ed852e2009-09-05 21:47:34 +00002607 if (step == 0)
2608 step=1;
2609 else
2610 if (step >= n)
2611 step=n-1;
2612 }
cristy1e7aa312011-09-10 20:01:36 +00002613 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2614 {
cristya19f1d72012-08-07 18:24:38 +00002615 double
cristy1e7aa312011-09-10 20:01:36 +00002616 gamma,
2617 pixel;
cristy3ed852e2009-09-05 21:47:34 +00002618
cristy633067b2013-02-21 01:15:12 +00002619 PixelChannel
2620 channel;
2621
2622 PixelTrait
2623 blur_traits,
2624 traits;
2625
cristy1e7aa312011-09-10 20:01:36 +00002626 register const Quantum
2627 *restrict r;
2628
2629 register ssize_t
2630 j;
2631
cristy633067b2013-02-21 01:15:12 +00002632 channel=GetPixelChannelChannel(image,i);
2633 traits=GetPixelChannelTraits(image,channel);
2634 blur_traits=GetPixelChannelTraits(blur_image,channel);
cristy1e7aa312011-09-10 20:01:36 +00002635 if ((traits == UndefinedPixelTrait) ||
2636 (blur_traits == UndefinedPixelTrait))
2637 continue;
cristy1eced092012-08-10 23:10:56 +00002638 if (((blur_traits & CopyPixelTrait) != 0) ||
cristy883fde12013-04-08 00:50:13 +00002639 (GetPixelReadMask(image,p) == 0))
cristy3ed852e2009-09-05 21:47:34 +00002640 {
cristy0beccfa2011-09-25 20:47:53 +00002641 SetPixelChannel(blur_image,channel,p[i],q);
cristy1e7aa312011-09-10 20:01:36 +00002642 continue;
cristy3ed852e2009-09-05 21:47:34 +00002643 }
cristy1e7aa312011-09-10 20:01:36 +00002644 gamma=0.0;
cristyaa2c16c2012-03-25 22:21:35 +00002645 pixel=0.0;
cristy1e7aa312011-09-10 20:01:36 +00002646 if ((blur_traits & BlendPixelTrait) == 0)
2647 {
2648 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2649 {
cristy8b49f382012-01-17 03:11:03 +00002650 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
cristy1e7aa312011-09-10 20:01:36 +00002651 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2652 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2653 1,1,exception);
2654 if (r == (const Quantum *) NULL)
2655 {
2656 status=MagickFalse;
2657 continue;
2658 }
2659 pixel+=r[i];
2660 gamma++;
2661 }
cristy3e3ec3a2012-11-03 23:11:06 +00002662 gamma=PerceptibleReciprocal(gamma);
cristy0beccfa2011-09-25 20:47:53 +00002663 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy1e7aa312011-09-10 20:01:36 +00002664 continue;
2665 }
2666 for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2667 {
cristy8b49f382012-01-17 03:11:03 +00002668 r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
cristy1e7aa312011-09-10 20:01:36 +00002669 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2670 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2671 1,1,exception);
2672 if (r == (const Quantum *) NULL)
2673 {
2674 status=MagickFalse;
2675 continue;
2676 }
2677 pixel+=GetPixelAlpha(image,r)*r[i];
2678 gamma+=GetPixelAlpha(image,r);
cristy3ed852e2009-09-05 21:47:34 +00002679 }
cristy3e3ec3a2012-11-03 23:11:06 +00002680 gamma=PerceptibleReciprocal(gamma);
cristy0beccfa2011-09-25 20:47:53 +00002681 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy1e7aa312011-09-10 20:01:36 +00002682 }
2683 p+=GetPixelChannels(image);
cristyed231572011-07-14 02:18:59 +00002684 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00002685 }
2686 if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2687 status=MagickFalse;
2688 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2689 {
2690 MagickBooleanType
2691 proceed;
2692
cristyb5d5f722009-11-04 03:03:49 +00002693#if defined(MAGICKCORE_OPENMP_SUPPORT)
dirk6d612cf2014-03-13 21:17:23 +00002694 #pragma omp critical (MagickCore_RotationalBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00002695#endif
2696 proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2697 if (proceed == MagickFalse)
2698 status=MagickFalse;
2699 }
2700 }
2701 blur_view=DestroyCacheView(blur_view);
cristy8b49f382012-01-17 03:11:03 +00002702 radial_view=DestroyCacheView(radial_view);
cristy3ed852e2009-09-05 21:47:34 +00002703 image_view=DestroyCacheView(image_view);
cristya19f1d72012-08-07 18:24:38 +00002704 cos_theta=(double *) RelinquishMagickMemory(cos_theta);
2705 sin_theta=(double *) RelinquishMagickMemory(sin_theta);
cristy3ed852e2009-09-05 21:47:34 +00002706 if (status == MagickFalse)
2707 blur_image=DestroyImage(blur_image);
2708 return(blur_image);
2709}
2710
2711/*
2712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2713% %
2714% %
2715% %
cristy3ed852e2009-09-05 21:47:34 +00002716% S e l e c t i v e B l u r I m a g e %
2717% %
2718% %
2719% %
2720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2721%
2722% SelectiveBlurImage() selectively blur pixels within a contrast threshold.
2723% It is similar to the unsharpen mask that sharpens everything with contrast
2724% above a certain threshold.
2725%
2726% The format of the SelectiveBlurImage method is:
2727%
2728% Image *SelectiveBlurImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00002729% const double sigma,const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002730%
2731% A description of each parameter follows:
2732%
2733% o image: the image.
2734%
cristy3ed852e2009-09-05 21:47:34 +00002735% o radius: the radius of the Gaussian, in pixels, not counting the center
2736% pixel.
2737%
2738% o sigma: the standard deviation of the Gaussian, in pixels.
2739%
2740% o threshold: only pixels within this contrast threshold are included
2741% in the blur operation.
2742%
2743% o exception: return any errors or warnings in this structure.
2744%
2745*/
cristy4282c702011-11-21 00:01:06 +00002746MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00002747 const double sigma,const double threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002748{
2749#define SelectiveBlurImageTag "SelectiveBlur/Image"
2750
cristy47e00502009-12-17 19:19:57 +00002751 CacheView
2752 *blur_view,
cristy9c7cf372012-08-08 22:58:27 +00002753 *image_view,
2754 *luminance_view;
cristy47e00502009-12-17 19:19:57 +00002755
cristy3ed852e2009-09-05 21:47:34 +00002756 Image
cristy9c7cf372012-08-08 22:58:27 +00002757 *blur_image,
2758 *luminance_image;
cristy3ed852e2009-09-05 21:47:34 +00002759
cristy3ed852e2009-09-05 21:47:34 +00002760 MagickBooleanType
2761 status;
2762
cristybb503372010-05-27 20:51:26 +00002763 MagickOffsetType
2764 progress;
2765
cristy0a887dc2012-08-15 22:58:36 +00002766 MagickRealType
2767 *kernel;
2768
cristybb503372010-05-27 20:51:26 +00002769 register ssize_t
cristy47e00502009-12-17 19:19:57 +00002770 i;
cristy3ed852e2009-09-05 21:47:34 +00002771
cristybb503372010-05-27 20:51:26 +00002772 size_t
cristy3ed852e2009-09-05 21:47:34 +00002773 width;
2774
cristybb503372010-05-27 20:51:26 +00002775 ssize_t
cristyc8523c12011-09-13 00:02:53 +00002776 center,
cristybb503372010-05-27 20:51:26 +00002777 j,
2778 u,
2779 v,
2780 y;
2781
cristy3ed852e2009-09-05 21:47:34 +00002782 /*
2783 Initialize blur image attributes.
2784 */
2785 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002786 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002787 if (image->debug != MagickFalse)
2788 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2789 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002790 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002791 width=GetOptimalKernelWidth1D(radius,sigma);
cristye42639a2012-08-23 01:53:24 +00002792 kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
2793 width,width*sizeof(*kernel)));
cristy0a887dc2012-08-15 22:58:36 +00002794 if (kernel == (MagickRealType *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002795 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy6b516212013-03-22 19:20:32 +00002796 j=(ssize_t) (width-1)/2;
cristy3ed852e2009-09-05 21:47:34 +00002797 i=0;
cristy47e00502009-12-17 19:19:57 +00002798 for (v=(-j); v <= j; v++)
cristy3ed852e2009-09-05 21:47:34 +00002799 {
cristy47e00502009-12-17 19:19:57 +00002800 for (u=(-j); u <= j; u++)
cristy0a887dc2012-08-15 22:58:36 +00002801 kernel[i++]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
cristy4205a3c2010-09-12 20:19:59 +00002802 MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
cristy3ed852e2009-09-05 21:47:34 +00002803 }
2804 if (image->debug != MagickFalse)
2805 {
2806 char
cristy151b66d2015-04-15 10:50:31 +00002807 format[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +00002808 *message;
2809
cristy0a887dc2012-08-15 22:58:36 +00002810 register const MagickRealType
cristy117ff172010-08-15 21:35:32 +00002811 *k;
2812
cristybb503372010-05-27 20:51:26 +00002813 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002814 u,
2815 v;
2816
cristy3ed852e2009-09-05 21:47:34 +00002817 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002818 " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
2819 width);
cristy3ed852e2009-09-05 21:47:34 +00002820 message=AcquireString("");
2821 k=kernel;
cristybb503372010-05-27 20:51:26 +00002822 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00002823 {
2824 *message='\0';
cristy151b66d2015-04-15 10:50:31 +00002825 (void) FormatLocaleString(format,MagickPathExtent,"%.20g: ",(double) v);
cristy3ed852e2009-09-05 21:47:34 +00002826 (void) ConcatenateString(&message,format);
cristybb503372010-05-27 20:51:26 +00002827 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00002828 {
cristy151b66d2015-04-15 10:50:31 +00002829 (void) FormatLocaleString(format,MagickPathExtent,"%+f ",(double) *k++);
cristy3ed852e2009-09-05 21:47:34 +00002830 (void) ConcatenateString(&message,format);
2831 }
2832 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
2833 }
2834 message=DestroyString(message);
2835 }
cristy1e7aa312011-09-10 20:01:36 +00002836 blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00002837 if (blur_image == (Image *) NULL)
2838 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00002839 if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002840 {
cristy3ed852e2009-09-05 21:47:34 +00002841 blur_image=DestroyImage(blur_image);
cristy0a887dc2012-08-15 22:58:36 +00002842 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
cristy9c7cf372012-08-08 22:58:27 +00002843 return((Image *) NULL);
2844 }
2845 luminance_image=CloneImage(image,0,0,MagickTrue,exception);
2846 if (luminance_image == (Image *) NULL)
2847 {
2848 blur_image=DestroyImage(blur_image);
cristy0a887dc2012-08-15 22:58:36 +00002849 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
cristy9c7cf372012-08-08 22:58:27 +00002850 return((Image *) NULL);
2851 }
2852 status=TransformImageColorspace(luminance_image,GRAYColorspace,exception);
2853 if (status == MagickFalse)
2854 {
2855 luminance_image=DestroyImage(luminance_image);
2856 blur_image=DestroyImage(blur_image);
cristy0a887dc2012-08-15 22:58:36 +00002857 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +00002858 return((Image *) NULL);
2859 }
2860 /*
2861 Threshold blur image.
2862 */
2863 status=MagickTrue;
2864 progress=0;
cristy6b516212013-03-22 19:20:32 +00002865 center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*
2866 ((width-1)/2L)+GetPixelChannels(image)*((width-1)/2L));
cristy46ff2672012-12-14 15:32:26 +00002867 image_view=AcquireVirtualCacheView(image,exception);
2868 luminance_view=AcquireVirtualCacheView(luminance_image,exception);
2869 blur_view=AcquireAuthenticCacheView(blur_image,exception);
cristy5a691dc2012-08-23 22:56:49 +00002870#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002871 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00002872 magick_threads(image,blur_image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00002873#endif
cristybb503372010-05-27 20:51:26 +00002874 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002875 {
cristy4c08aed2011-07-01 19:47:50 +00002876 double
2877 contrast;
2878
cristy3ed852e2009-09-05 21:47:34 +00002879 MagickBooleanType
2880 sync;
2881
cristy4c08aed2011-07-01 19:47:50 +00002882 register const Quantum
cristy9c7cf372012-08-08 22:58:27 +00002883 *restrict l,
cristyc47d1f82009-11-26 01:44:43 +00002884 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002885
cristy4c08aed2011-07-01 19:47:50 +00002886 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002887 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002888
cristy117ff172010-08-15 21:35:32 +00002889 register ssize_t
2890 x;
2891
cristy3ed852e2009-09-05 21:47:34 +00002892 if (status == MagickFalse)
2893 continue;
cristy6b516212013-03-22 19:20:32 +00002894 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) (width-1)/2L),y-(ssize_t)
2895 ((width-1)/2L),image->columns+width,width,exception);
2896 l=GetCacheViewVirtualPixels(luminance_view,-((ssize_t) (width-1)/2L),y-
2897 (ssize_t) ((width-1)/2L),luminance_image->columns+width,width,exception);
cristy8b49f382012-01-17 03:11:03 +00002898 q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +00002899 exception);
cristy4c08aed2011-07-01 19:47:50 +00002900 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002901 {
2902 status=MagickFalse;
2903 continue;
2904 }
cristybb503372010-05-27 20:51:26 +00002905 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002906 {
cristy9c7cf372012-08-08 22:58:27 +00002907 double
2908 intensity;
2909
cristybb503372010-05-27 20:51:26 +00002910 register ssize_t
cristy1e7aa312011-09-10 20:01:36 +00002911 i;
cristy3ed852e2009-09-05 21:47:34 +00002912
cristyf13c5942012-08-08 23:50:11 +00002913 intensity=GetPixelIntensity(image,p+center);
cristy1e7aa312011-09-10 20:01:36 +00002914 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2915 {
cristya19f1d72012-08-07 18:24:38 +00002916 double
cristy1e7aa312011-09-10 20:01:36 +00002917 alpha,
2918 gamma,
cristy1e7aa312011-09-10 20:01:36 +00002919 pixel;
cristy117ff172010-08-15 21:35:32 +00002920
cristy633067b2013-02-21 01:15:12 +00002921 PixelChannel
2922 channel;
2923
2924 PixelTrait
2925 blur_traits,
2926 traits;
2927
cristy0a887dc2012-08-15 22:58:36 +00002928 register const MagickRealType
cristy1e7aa312011-09-10 20:01:36 +00002929 *restrict k;
2930
2931 register const Quantum
cristy9c7cf372012-08-08 22:58:27 +00002932 *restrict luminance_pixels,
cristy1e7aa312011-09-10 20:01:36 +00002933 *restrict pixels;
2934
2935 register ssize_t
2936 u;
2937
2938 ssize_t
2939 v;
2940
cristy633067b2013-02-21 01:15:12 +00002941 channel=GetPixelChannelChannel(image,i);
2942 traits=GetPixelChannelTraits(image,channel);
2943 blur_traits=GetPixelChannelTraits(blur_image,channel);
cristy1e7aa312011-09-10 20:01:36 +00002944 if ((traits == UndefinedPixelTrait) ||
2945 (blur_traits == UndefinedPixelTrait))
2946 continue;
cristy1eced092012-08-10 23:10:56 +00002947 if (((blur_traits & CopyPixelTrait) != 0) ||
cristy883fde12013-04-08 00:50:13 +00002948 (GetPixelReadMask(image,p+center) == 0))
cristy3ed852e2009-09-05 21:47:34 +00002949 {
cristy0beccfa2011-09-25 20:47:53 +00002950 SetPixelChannel(blur_image,channel,p[center+i],q);
cristy1e7aa312011-09-10 20:01:36 +00002951 continue;
cristy3ed852e2009-09-05 21:47:34 +00002952 }
cristy1e7aa312011-09-10 20:01:36 +00002953 k=kernel;
cristyaa2c16c2012-03-25 22:21:35 +00002954 pixel=0.0;
cristy1e7aa312011-09-10 20:01:36 +00002955 pixels=p;
cristy9c7cf372012-08-08 22:58:27 +00002956 luminance_pixels=l;
cristy1e7aa312011-09-10 20:01:36 +00002957 gamma=0.0;
2958 if ((blur_traits & BlendPixelTrait) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002959 {
cristy1e7aa312011-09-10 20:01:36 +00002960 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +00002961 {
cristy1e7aa312011-09-10 20:01:36 +00002962 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +00002963 {
cristyf13c5942012-08-08 23:50:11 +00002964 contrast=GetPixelIntensity(luminance_image,luminance_pixels)-
2965 intensity;
cristy1e7aa312011-09-10 20:01:36 +00002966 if (fabs(contrast) < threshold)
2967 {
2968 pixel+=(*k)*pixels[i];
2969 gamma+=(*k);
2970 }
2971 k++;
2972 pixels+=GetPixelChannels(image);
cristy9c7cf372012-08-08 22:58:27 +00002973 luminance_pixels+=GetPixelChannels(luminance_image);
cristy3ed852e2009-09-05 21:47:34 +00002974 }
cristyadc349b2014-12-06 21:40:39 +00002975 pixels+=GetPixelChannels(image)*image->columns;
2976 luminance_pixels+=GetPixelChannels(luminance_image)*
2977 luminance_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00002978 }
cristy1e7aa312011-09-10 20:01:36 +00002979 if (fabs((double) gamma) < MagickEpsilon)
2980 {
cristy0beccfa2011-09-25 20:47:53 +00002981 SetPixelChannel(blur_image,channel,p[center+i],q);
cristy1e7aa312011-09-10 20:01:36 +00002982 continue;
2983 }
cristy3e3ec3a2012-11-03 23:11:06 +00002984 gamma=PerceptibleReciprocal(gamma);
cristy0beccfa2011-09-25 20:47:53 +00002985 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy1e7aa312011-09-10 20:01:36 +00002986 continue;
2987 }
2988 for (v=0; v < (ssize_t) width; v++)
2989 {
2990 for (u=0; u < (ssize_t) width; u++)
2991 {
2992 contrast=GetPixelIntensity(image,pixels)-intensity;
2993 if (fabs(contrast) < threshold)
2994 {
cristy60dc90c2013-02-09 18:33:21 +00002995 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
cristy1e7aa312011-09-10 20:01:36 +00002996 pixel+=(*k)*alpha*pixels[i];
2997 gamma+=(*k)*alpha;
2998 }
2999 k++;
3000 pixels+=GetPixelChannels(image);
cristy9c7cf372012-08-08 22:58:27 +00003001 luminance_pixels+=GetPixelChannels(luminance_image);
cristy1e7aa312011-09-10 20:01:36 +00003002 }
cristyadc349b2014-12-06 21:40:39 +00003003 pixels+=GetPixelChannels(image)*image->columns;
3004 luminance_pixels+=GetPixelChannels(luminance_image)*
3005 luminance_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00003006 }
cristy1e7aa312011-09-10 20:01:36 +00003007 if (fabs((double) gamma) < MagickEpsilon)
3008 {
cristy0beccfa2011-09-25 20:47:53 +00003009 SetPixelChannel(blur_image,channel,p[center+i],q);
cristy1e7aa312011-09-10 20:01:36 +00003010 continue;
3011 }
cristy3e3ec3a2012-11-03 23:11:06 +00003012 gamma=PerceptibleReciprocal(gamma);
cristy0beccfa2011-09-25 20:47:53 +00003013 SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
cristy1e7aa312011-09-10 20:01:36 +00003014 }
cristyed231572011-07-14 02:18:59 +00003015 p+=GetPixelChannels(image);
cristy9c7cf372012-08-08 22:58:27 +00003016 l+=GetPixelChannels(luminance_image);
cristyed231572011-07-14 02:18:59 +00003017 q+=GetPixelChannels(blur_image);
cristy3ed852e2009-09-05 21:47:34 +00003018 }
3019 sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3020 if (sync == MagickFalse)
3021 status=MagickFalse;
3022 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3023 {
3024 MagickBooleanType
3025 proceed;
3026
cristyb5d5f722009-11-04 03:03:49 +00003027#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +00003028 #pragma omp critical (MagickCore_SelectiveBlurImage)
cristy3ed852e2009-09-05 21:47:34 +00003029#endif
3030 proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3031 image->rows);
3032 if (proceed == MagickFalse)
3033 status=MagickFalse;
3034 }
3035 }
3036 blur_image->type=image->type;
3037 blur_view=DestroyCacheView(blur_view);
3038 image_view=DestroyCacheView(image_view);
cristy9c7cf372012-08-08 22:58:27 +00003039 luminance_image=DestroyImage(luminance_image);
cristy0a887dc2012-08-15 22:58:36 +00003040 kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
cristy3ed852e2009-09-05 21:47:34 +00003041 if (status == MagickFalse)
3042 blur_image=DestroyImage(blur_image);
3043 return(blur_image);
3044}
3045
3046/*
3047%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3048% %
3049% %
3050% %
3051% S h a d e I m a g e %
3052% %
3053% %
3054% %
3055%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3056%
3057% ShadeImage() shines a distant light on an image to create a
3058% three-dimensional effect. You control the positioning of the light with
3059% azimuth and elevation; azimuth is measured in degrees off the x axis
3060% and elevation is measured in pixels above the Z axis.
3061%
3062% The format of the ShadeImage method is:
3063%
3064% Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3065% const double azimuth,const double elevation,ExceptionInfo *exception)
3066%
3067% A description of each parameter follows:
3068%
3069% o image: the image.
3070%
3071% o gray: A value other than zero shades the intensity of each pixel.
3072%
3073% o azimuth, elevation: Define the light source direction.
3074%
3075% o exception: return any errors or warnings in this structure.
3076%
3077*/
3078MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3079 const double azimuth,const double elevation,ExceptionInfo *exception)
3080{
3081#define ShadeImageTag "Shade/Image"
3082
cristyc4c8d132010-01-07 01:58:38 +00003083 CacheView
3084 *image_view,
3085 *shade_view;
3086
cristy3ed852e2009-09-05 21:47:34 +00003087 Image
cristy7d9d8ca2012-12-20 17:11:08 +00003088 *linear_image,
cristy3ed852e2009-09-05 21:47:34 +00003089 *shade_image;
3090
cristy3ed852e2009-09-05 21:47:34 +00003091 MagickBooleanType
3092 status;
3093
cristybb503372010-05-27 20:51:26 +00003094 MagickOffsetType
3095 progress;
3096
cristy3ed852e2009-09-05 21:47:34 +00003097 PrimaryInfo
3098 light;
3099
cristybb503372010-05-27 20:51:26 +00003100 ssize_t
3101 y;
3102
cristy3ed852e2009-09-05 21:47:34 +00003103 /*
3104 Initialize shaded image attributes.
3105 */
3106 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003107 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00003108 if (image->debug != MagickFalse)
3109 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3110 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003111 assert(exception->signature == MagickCoreSignature);
cristy7d9d8ca2012-12-20 17:11:08 +00003112 linear_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003113 shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
cristy7d9d8ca2012-12-20 17:11:08 +00003114 if ((linear_image == (Image *) NULL) || (shade_image == (Image *) NULL))
3115 {
3116 if (linear_image != (Image *) NULL)
3117 linear_image=DestroyImage(linear_image);
3118 if (shade_image != (Image *) NULL)
3119 shade_image=DestroyImage(shade_image);
3120 return((Image *) NULL);
3121 }
cristy574cc262011-08-05 01:23:58 +00003122 if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003123 {
cristy7d9d8ca2012-12-20 17:11:08 +00003124 linear_image=DestroyImage(linear_image);
cristy3ed852e2009-09-05 21:47:34 +00003125 shade_image=DestroyImage(shade_image);
3126 return((Image *) NULL);
3127 }
3128 /*
3129 Compute the light vector.
3130 */
3131 light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3132 cos(DegreesToRadians(elevation));
3133 light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3134 cos(DegreesToRadians(elevation));
3135 light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3136 /*
3137 Shade image.
3138 */
3139 status=MagickTrue;
3140 progress=0;
cristy7d9d8ca2012-12-20 17:11:08 +00003141 image_view=AcquireVirtualCacheView(linear_image,exception);
cristy46ff2672012-12-14 15:32:26 +00003142 shade_view=AcquireAuthenticCacheView(shade_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00003143#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00003144 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy7d9d8ca2012-12-20 17:11:08 +00003145 magick_threads(linear_image,shade_image,linear_image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00003146#endif
cristy7d9d8ca2012-12-20 17:11:08 +00003147 for (y=0; y < (ssize_t) linear_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003148 {
cristya19f1d72012-08-07 18:24:38 +00003149 double
cristy3ed852e2009-09-05 21:47:34 +00003150 distance,
3151 normal_distance,
3152 shade;
3153
3154 PrimaryInfo
3155 normal;
3156
cristy4c08aed2011-07-01 19:47:50 +00003157 register const Quantum
cristy1e7aa312011-09-10 20:01:36 +00003158 *restrict center,
cristyc47d1f82009-11-26 01:44:43 +00003159 *restrict p,
cristy1e7aa312011-09-10 20:01:36 +00003160 *restrict post,
3161 *restrict pre;
cristy3ed852e2009-09-05 21:47:34 +00003162
cristy4c08aed2011-07-01 19:47:50 +00003163 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00003164 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003165
cristy117ff172010-08-15 21:35:32 +00003166 register ssize_t
3167 x;
3168
cristy3ed852e2009-09-05 21:47:34 +00003169 if (status == MagickFalse)
3170 continue;
cristy7d9d8ca2012-12-20 17:11:08 +00003171 p=GetCacheViewVirtualPixels(image_view,-1,y-1,linear_image->columns+2,3,
3172 exception);
cristy3ed852e2009-09-05 21:47:34 +00003173 q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3174 exception);
cristy4c08aed2011-07-01 19:47:50 +00003175 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00003176 {
3177 status=MagickFalse;
3178 continue;
3179 }
3180 /*
3181 Shade this row of pixels.
3182 */
3183 normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
cristy7d9d8ca2012-12-20 17:11:08 +00003184 pre=p+GetPixelChannels(linear_image);
3185 center=pre+(linear_image->columns+2)*GetPixelChannels(linear_image);
3186 post=center+(linear_image->columns+2)*GetPixelChannels(linear_image);
3187 for (x=0; x < (ssize_t) linear_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003188 {
cristy1e7aa312011-09-10 20:01:36 +00003189 register ssize_t
3190 i;
3191
cristy3ed852e2009-09-05 21:47:34 +00003192 /*
3193 Determine the surface normal and compute shading.
3194 */
cristy7d9d8ca2012-12-20 17:11:08 +00003195 normal.x=(double) (
3196 GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))+
3197 GetPixelIntensity(linear_image,center-GetPixelChannels(linear_image))+
3198 GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))-
3199 GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image))-
3200 GetPixelIntensity(linear_image,center+GetPixelChannels(linear_image))-
3201 GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image)));
3202 normal.y=(double) (
3203 GetPixelIntensity(linear_image,post-GetPixelChannels(linear_image))+
3204 GetPixelIntensity(linear_image,post)+
3205 GetPixelIntensity(linear_image,post+GetPixelChannels(linear_image))-
3206 GetPixelIntensity(linear_image,pre-GetPixelChannels(linear_image))-
3207 GetPixelIntensity(linear_image,pre)-
3208 GetPixelIntensity(linear_image,pre+GetPixelChannels(linear_image)));
cristy3ed852e2009-09-05 21:47:34 +00003209 if ((normal.x == 0.0) && (normal.y == 0.0))
3210 shade=light.z;
3211 else
3212 {
3213 shade=0.0;
3214 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3215 if (distance > MagickEpsilon)
3216 {
cristy7d9d8ca2012-12-20 17:11:08 +00003217 normal_distance=normal.x*normal.x+normal.y*normal.y+
3218 normal.z*normal.z;
cristy3ed852e2009-09-05 21:47:34 +00003219 if (normal_distance > (MagickEpsilon*MagickEpsilon))
3220 shade=distance/sqrt((double) normal_distance);
3221 }
3222 }
cristy7d9d8ca2012-12-20 17:11:08 +00003223 for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
cristy1e7aa312011-09-10 20:01:36 +00003224 {
cristy633067b2013-02-21 01:15:12 +00003225 PixelChannel
3226 channel;
3227
3228 PixelTrait
3229 shade_traits,
3230 traits;
3231
3232 channel=GetPixelChannelChannel(linear_image,i);
3233 traits=GetPixelChannelTraits(linear_image,channel);
3234 shade_traits=GetPixelChannelTraits(shade_image,channel);
cristy1e7aa312011-09-10 20:01:36 +00003235 if ((traits == UndefinedPixelTrait) ||
3236 (shade_traits == UndefinedPixelTrait))
3237 continue;
cristy1eced092012-08-10 23:10:56 +00003238 if (((shade_traits & CopyPixelTrait) != 0) ||
cristy883fde12013-04-08 00:50:13 +00003239 (GetPixelReadMask(linear_image,center) == 0))
cristy1e7aa312011-09-10 20:01:36 +00003240 {
cristy0beccfa2011-09-25 20:47:53 +00003241 SetPixelChannel(shade_image,channel,center[i],q);
cristy1e7aa312011-09-10 20:01:36 +00003242 continue;
3243 }
cristy28350032014-11-16 01:19:51 +00003244 if ((traits & UpdatePixelTrait) == 0)
3245 {
3246 SetPixelChannel(shade_image,channel,center[i],q);
3247 continue;
3248 }
cristy1e7aa312011-09-10 20:01:36 +00003249 if (gray != MagickFalse)
3250 {
cristy0beccfa2011-09-25 20:47:53 +00003251 SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
cristy1e7aa312011-09-10 20:01:36 +00003252 continue;
3253 }
cristy0beccfa2011-09-25 20:47:53 +00003254 SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3255 center[i]),q);
cristy1e7aa312011-09-10 20:01:36 +00003256 }
cristy7d9d8ca2012-12-20 17:11:08 +00003257 pre+=GetPixelChannels(linear_image);
3258 center+=GetPixelChannels(linear_image);
3259 post+=GetPixelChannels(linear_image);
cristyed231572011-07-14 02:18:59 +00003260 q+=GetPixelChannels(shade_image);
cristy3ed852e2009-09-05 21:47:34 +00003261 }
3262 if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3263 status=MagickFalse;
3264 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3265 {
3266 MagickBooleanType
3267 proceed;
3268
cristyb5d5f722009-11-04 03:03:49 +00003269#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf8561542012-01-24 00:26:46 +00003270 #pragma omp critical (MagickCore_ShadeImage)
cristy3ed852e2009-09-05 21:47:34 +00003271#endif
3272 proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3273 if (proceed == MagickFalse)
3274 status=MagickFalse;
3275 }
3276 }
3277 shade_view=DestroyCacheView(shade_view);
3278 image_view=DestroyCacheView(image_view);
cristy7d9d8ca2012-12-20 17:11:08 +00003279 linear_image=DestroyImage(linear_image);
cristy3ed852e2009-09-05 21:47:34 +00003280 if (status == MagickFalse)
3281 shade_image=DestroyImage(shade_image);
3282 return(shade_image);
3283}
3284
3285/*
3286%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3287% %
3288% %
3289% %
3290% S h a r p e n I m a g e %
3291% %
3292% %
3293% %
3294%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3295%
3296% SharpenImage() sharpens the image. We convolve the image with a Gaussian
3297% operator of the given radius and standard deviation (sigma). For
3298% reasonable results, radius should be larger than sigma. Use a radius of 0
3299% and SharpenImage() selects a suitable radius for you.
3300%
3301% Using a separable kernel would be faster, but the negative weights cancel
3302% out on the corners of the kernel producing often undesirable ringing in the
3303% filtered result; this can be avoided by using a 2D gaussian shaped image
3304% sharpening kernel instead.
3305%
3306% The format of the SharpenImage method is:
3307%
3308% Image *SharpenImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00003309% const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003310%
3311% A description of each parameter follows:
3312%
3313% o image: the image.
3314%
cristy3ed852e2009-09-05 21:47:34 +00003315% o radius: the radius of the Gaussian, in pixels, not counting the center
3316% pixel.
3317%
3318% o sigma: the standard deviation of the Laplacian, in pixels.
3319%
3320% o exception: return any errors or warnings in this structure.
3321%
3322*/
cristy3ed852e2009-09-05 21:47:34 +00003323MagickExport Image *SharpenImage(const Image *image,const double radius,
cristyaa2c16c2012-03-25 22:21:35 +00003324 const double sigma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003325{
cristy09dbb822013-03-24 23:25:21 +00003326 double
cristy0cfd5672013-03-26 14:34:25 +00003327 gamma,
cristy09dbb822013-03-24 23:25:21 +00003328 normalize;
3329
3330 Image
3331 *sharp_image;
cristy3ed852e2009-09-05 21:47:34 +00003332
cristy41cbe682011-07-15 19:12:37 +00003333 KernelInfo
3334 *kernel_info;
3335
cristy09dbb822013-03-24 23:25:21 +00003336 register ssize_t
3337 i;
3338
3339 size_t
3340 width;
3341
3342 ssize_t
3343 j,
3344 u,
3345 v;
cristy117ff172010-08-15 21:35:32 +00003346
cristy3ed852e2009-09-05 21:47:34 +00003347 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003348 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00003349 if (image->debug != MagickFalse)
3350 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3351 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003352 assert(exception->signature == MagickCoreSignature);
cristy09dbb822013-03-24 23:25:21 +00003353 width=GetOptimalKernelWidth2D(radius,sigma);
cristy2c57b742014-10-31 00:40:34 +00003354 kernel_info=AcquireKernelInfo((const char *) NULL,exception);
cristy41cbe682011-07-15 19:12:37 +00003355 if (kernel_info == (KernelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003356 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
cristy09dbb822013-03-24 23:25:21 +00003357 (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3358 kernel_info->width=width;
3359 kernel_info->height=width;
cristyf1307112013-03-25 22:16:57 +00003360 kernel_info->x=(ssize_t) (width-1)/2;
3361 kernel_info->y=(ssize_t) (width-1)/2;
cristye1c94d92015-06-28 12:16:33 +00003362 kernel_info->signature=MagickCoreSignature;
cristy09dbb822013-03-24 23:25:21 +00003363 kernel_info->values=(MagickRealType *) MagickAssumeAligned(
cristy39a5afb2013-03-26 19:09:41 +00003364 AcquireAlignedMemory(kernel_info->width,kernel_info->height*
cristy09dbb822013-03-24 23:25:21 +00003365 sizeof(*kernel_info->values)));
3366 if (kernel_info->values == (MagickRealType *) NULL)
3367 {
3368 kernel_info=DestroyKernelInfo(kernel_info);
3369 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3370 }
3371 normalize=0.0;
3372 j=(ssize_t) (kernel_info->width-1)/2;
3373 i=0;
3374 for (v=(-j); v <= j; v++)
3375 {
3376 for (u=(-j); u <= j; u++)
3377 {
3378 kernel_info->values[i]=(MagickRealType) (-exp(-((double) u*u+v*v)/(2.0*
3379 MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3380 normalize+=kernel_info->values[i];
3381 i++;
3382 }
3383 }
3384 kernel_info->values[i/2]=(double) ((-2.0)*normalize);
cristy78bfb0f2013-03-26 13:18:48 +00003385 normalize=0.0;
cristy0cfd5672013-03-26 14:34:25 +00003386 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
cristy78bfb0f2013-03-26 13:18:48 +00003387 normalize+=kernel_info->values[i];
cristy0cfd5672013-03-26 14:34:25 +00003388 gamma=PerceptibleReciprocal(normalize);
3389 for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3390 kernel_info->values[i]*=gamma;
cristy7f391ce2013-03-27 11:03:11 +00003391 sharp_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
3392 UndefinedCompositeOp,0.0,exception);
cristy41cbe682011-07-15 19:12:37 +00003393 kernel_info=DestroyKernelInfo(kernel_info);
cristy9dc4c512013-03-24 01:38:00 +00003394 return(sharp_image);
cristy3ed852e2009-09-05 21:47:34 +00003395}
3396
3397/*
3398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3399% %
3400% %
3401% %
3402% S p r e a d I m a g e %
3403% %
3404% %
3405% %
3406%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3407%
3408% SpreadImage() is a special effects method that randomly displaces each
3409% pixel in a block defined by the radius parameter.
3410%
3411% The format of the SpreadImage method is:
3412%
3413% Image *SpreadImage(const Image *image,const double radius,
Cristycc332e32015-08-09 12:15:50 -04003414% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003415%
3416% A description of each parameter follows:
3417%
3418% o image: the image.
3419%
cristy5c4e2582011-09-11 19:21:03 +00003420% o radius: choose a random pixel in a neighborhood of this extent.
3421%
cristy3ed852e2009-09-05 21:47:34 +00003422% o exception: return any errors or warnings in this structure.
3423%
3424*/
Cristycc332e32015-08-09 12:15:50 -04003425
3426static void inline SwapPixelComponent(Quantum *p,Quantum *q)
3427{
3428 Quantum
3429 pixel;
3430
3431 pixel=(*p);
3432 (*p)=(*q);
3433 (*q)=pixel;
3434}
3435
Cristy1c03a242015-08-11 06:22:22 -04003436static Image *InterpolateSpreadImage(const Image *image,
3437 const PixelInterpolateMethod method,const double radius,
3438 ExceptionInfo *exception)
3439{
3440#define SpreadImageTag "Spread/Image"
3441
3442 CacheView
3443 *image_view,
3444 *spread_view;
3445
3446 Image
3447 *spread_image;
3448
3449 MagickBooleanType
3450 status;
3451
3452 MagickOffsetType
3453 progress;
3454
3455 RandomInfo
3456 **restrict random_info;
3457
3458 size_t
3459 width;
3460
3461 ssize_t
3462 y;
3463
3464#if defined(MAGICKCORE_OPENMP_SUPPORT)
3465 unsigned long
3466 key;
3467#endif
3468
3469 /*
3470 Initialize spread image attributes.
3471 */
3472 assert(image != (Image *) NULL);
3473 assert(image->signature == MagickCoreSignature);
3474 if (image->debug != MagickFalse)
3475 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3476 assert(exception != (ExceptionInfo *) NULL);
3477 assert(exception->signature == MagickCoreSignature);
3478 spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3479 exception);
3480 if (spread_image == (Image *) NULL)
3481 return((Image *) NULL);
3482 if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3483 {
3484 spread_image=DestroyImage(spread_image);
3485 return((Image *) NULL);
3486 }
3487 /*
3488 Spread image.
3489 */
3490 status=MagickTrue;
3491 progress=0;
3492 width=GetOptimalKernelWidth1D(radius,0.5);
3493 random_info=AcquireRandomInfoThreadSet();
3494 image_view=AcquireVirtualCacheView(image,exception);
3495 spread_view=AcquireAuthenticCacheView(spread_image,exception);
3496#if defined(MAGICKCORE_OPENMP_SUPPORT)
3497 key=GetRandomSecretKey(random_info[0]);
3498 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3499 magick_threads(image,spread_image,image->rows,key == ~0UL)
3500#endif
3501 for (y=0; y < (ssize_t) image->rows; y++)
3502 {
3503 const int
3504 id = GetOpenMPThreadId();
3505
3506 register Quantum
3507 *restrict q;
3508
3509 register ssize_t
3510 x;
3511
3512 if (status == MagickFalse)
3513 continue;
3514 q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3515 exception);
3516 if (q == (Quantum *) NULL)
3517 {
3518 status=MagickFalse;
3519 continue;
3520 }
3521 for (x=0; x < (ssize_t) image->columns; x++)
3522 {
3523 PointInfo
3524 point;
3525
3526 point.x=GetPseudoRandomValue(random_info[id]);
3527 point.y=GetPseudoRandomValue(random_info[id]);
3528 status=InterpolatePixelChannels(image,image_view,spread_image,method,
3529 (double) x+width*(point.x-0.5),(double) y+width*(point.y-0.5),q,
3530 exception);
3531 q+=GetPixelChannels(spread_image);
3532 }
3533 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3534 status=MagickFalse;
3535 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3536 {
3537 MagickBooleanType
3538 proceed;
3539
3540#if defined(MAGICKCORE_OPENMP_SUPPORT)
3541 #pragma omp critical (MagickCore_SpreadImage)
3542#endif
3543 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3544 if (proceed == MagickFalse)
3545 status=MagickFalse;
3546 }
3547 }
3548 spread_view=DestroyCacheView(spread_view);
3549 image_view=DestroyCacheView(image_view);
3550 random_info=DestroyRandomInfoThreadSet(random_info);
3551 if (status == MagickFalse)
3552 spread_image=DestroyImage(spread_image);
3553 return(spread_image);
3554}
3555
cristy3ed852e2009-09-05 21:47:34 +00003556MagickExport Image *SpreadImage(const Image *image,const double radius,
Cristycc332e32015-08-09 12:15:50 -04003557 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003558{
3559#define SpreadImageTag "Spread/Image"
3560
cristyfa112112010-01-04 17:48:07 +00003561 CacheView
cristy9f7e7cb2011-03-26 00:49:57 +00003562 *image_view,
3563 *spread_view;
cristyfa112112010-01-04 17:48:07 +00003564
cristy3ed852e2009-09-05 21:47:34 +00003565 Image
3566 *spread_image;
3567
cristy3ed852e2009-09-05 21:47:34 +00003568 MagickBooleanType
3569 status;
3570
cristybb503372010-05-27 20:51:26 +00003571 MagickOffsetType
3572 progress;
3573
cristy3ed852e2009-09-05 21:47:34 +00003574 RandomInfo
Cristycc332e32015-08-09 12:15:50 -04003575 *restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00003576
cristybb503372010-05-27 20:51:26 +00003577 size_t
cristy3ed852e2009-09-05 21:47:34 +00003578 width;
3579
cristybb503372010-05-27 20:51:26 +00003580 ssize_t
3581 y;
3582
cristy3ed852e2009-09-05 21:47:34 +00003583 /*
3584 Initialize spread image attributes.
3585 */
3586 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003587 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00003588 if (image->debug != MagickFalse)
3589 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3590 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003591 assert(exception->signature == MagickCoreSignature);
Cristy43b536b2015-08-20 20:04:04 -04003592 if (image->interpolate != NearestInterpolatePixel)
Cristy1c03a242015-08-11 06:22:22 -04003593 return(InterpolateSpreadImage(image,image->interpolate,radius,exception));
Cristycc332e32015-08-09 12:15:50 -04003594 spread_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00003595 if (spread_image == (Image *) NULL)
3596 return((Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003597 /*
3598 Spread image.
3599 */
3600 status=MagickTrue;
3601 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00003602 width=GetOptimalKernelWidth1D(radius,0.5);
Cristycc332e32015-08-09 12:15:50 -04003603 random_info=AcquireRandomInfo();
3604 image_view=AcquireAuthenticCacheView(spread_image,exception);
cristy46ff2672012-12-14 15:32:26 +00003605 spread_view=AcquireAuthenticCacheView(spread_image,exception);
cristybe82ad52011-09-06 01:17:20 +00003606 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003607 {
cristy117ff172010-08-15 21:35:32 +00003608 register ssize_t
3609 x;
3610
cristy3ed852e2009-09-05 21:47:34 +00003611 if (status == MagickFalse)
3612 continue;
cristybe82ad52011-09-06 01:17:20 +00003613 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003614 {
Cristycc332e32015-08-09 12:15:50 -04003615 register Quantum
3616 *restrict p,
3617 *restrict q;
cristybe82ad52011-09-06 01:17:20 +00003618
Cristycc332e32015-08-09 12:15:50 -04003619 register ssize_t
3620 i;
3621
3622 ssize_t
3623 x_offset,
3624 y_offset;
3625
3626 for ( ; ; )
3627 {
Cristy4d8aeb12015-08-09 19:18:56 -04003628 x_offset=(ssize_t) floor((double) x+width*
Cristyb4e15f22015-08-10 06:29:20 -04003629 (GetPseudoRandomValue(random_info)-0.5));
Cristy4d8aeb12015-08-09 19:18:56 -04003630 y_offset=(ssize_t) floor((double) y+width*
Cristyb4e15f22015-08-10 06:29:20 -04003631 (GetPseudoRandomValue(random_info)-0.5));
Cristya049d382015-08-11 06:24:26 -04003632 if ((x_offset >= 0) && (x_offset < (ssize_t) image->columns) &&
3633 (y_offset >= 0) && (y_offset < (ssize_t) image->rows))
Cristycc332e32015-08-09 12:15:50 -04003634 break;
3635 }
3636 p=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,1,1,exception);
3637 q=GetCacheViewAuthenticPixels(spread_view,x,y,1,1,exception);
3638 if ((p == (Quantum *) NULL) || (q == (Quantum *) NULL))
3639 {
3640 status=MagickFalse;
3641 continue;
3642 }
3643 for (i=0; i < (ssize_t) GetPixelChannels(spread_image); i++)
3644 SwapPixelComponent(p+i,q+i);
3645 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3646 status=MagickFalse;
3647 if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3648 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003649 }
cristy3ed852e2009-09-05 21:47:34 +00003650 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3651 {
3652 MagickBooleanType
3653 proceed;
3654
cristy3ed852e2009-09-05 21:47:34 +00003655 proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3656 if (proceed == MagickFalse)
3657 status=MagickFalse;
3658 }
3659 }
cristy9f7e7cb2011-03-26 00:49:57 +00003660 spread_view=DestroyCacheView(spread_view);
cristy3ed852e2009-09-05 21:47:34 +00003661 image_view=DestroyCacheView(image_view);
Cristycc332e32015-08-09 12:15:50 -04003662 random_info=DestroyRandomInfo(random_info);
cristy14aa8b92012-12-13 20:37:52 +00003663 if (status == MagickFalse)
3664 spread_image=DestroyImage(spread_image);
cristy3ed852e2009-09-05 21:47:34 +00003665 return(spread_image);
3666}
3667
3668/*
3669%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3670% %
3671% %
3672% %
3673% U n s h a r p M a s k I m a g e %
3674% %
3675% %
3676% %
3677%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3678%
3679% UnsharpMaskImage() sharpens one or more image channels. We convolve the
3680% image with a Gaussian operator of the given radius and standard deviation
3681% (sigma). For reasonable results, radius should be larger than sigma. Use a
3682% radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
3683%
3684% The format of the UnsharpMaskImage method is:
3685%
3686% Image *UnsharpMaskImage(const Image *image,const double radius,
cristy3afd4012013-03-25 11:30:44 +00003687% const double sigma,const double amount,const double threshold,
3688% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003689%
3690% A description of each parameter follows:
3691%
3692% o image: the image.
3693%
cristy3ed852e2009-09-05 21:47:34 +00003694% o radius: the radius of the Gaussian, in pixels, not counting the center
3695% pixel.
3696%
3697% o sigma: the standard deviation of the Gaussian, in pixels.
3698%
cristy2fd9be62013-03-23 20:44:18 +00003699% o gain: the percentage of the difference between the original and the
cristy3ed852e2009-09-05 21:47:34 +00003700% blur image that is added back into the original.
3701%
cristy3afd4012013-03-25 11:30:44 +00003702% o threshold: the threshold in pixels needed to apply the diffence gain.
3703%
cristy3ed852e2009-09-05 21:47:34 +00003704% o exception: return any errors or warnings in this structure.
3705%
3706*/
cristy31bb6272011-11-20 23:57:25 +00003707MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
cristy3afd4012013-03-25 11:30:44 +00003708 const double sigma,const double gain,const double threshold,
3709 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003710{
cristy3afd4012013-03-25 11:30:44 +00003711#define SharpenImageTag "Sharpen/Image"
cristy3ed852e2009-09-05 21:47:34 +00003712
cristy3afd4012013-03-25 11:30:44 +00003713 CacheView
3714 *image_view,
3715 *unsharp_view;
cristyc4c8d132010-01-07 01:58:38 +00003716
cristy3ed852e2009-09-05 21:47:34 +00003717 Image
3718 *unsharp_image;
3719
cristy3afd4012013-03-25 11:30:44 +00003720 MagickBooleanType
3721 status;
3722
3723 MagickOffsetType
3724 progress;
3725
3726 double
3727 quantum_threshold;
3728
3729 ssize_t
3730 y;
3731
cristy3ed852e2009-09-05 21:47:34 +00003732 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003733 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00003734 if (image->debug != MagickFalse)
3735 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3736 assert(exception != (ExceptionInfo *) NULL);
cristy3afd4012013-03-25 11:30:44 +00003737 unsharp_image=BlurImage(image,radius,sigma,exception);
3738 if (unsharp_image == (Image *) NULL)
3739 return((Image *) NULL);
3740 quantum_threshold=(double) QuantumRange*threshold;
3741 /*
3742 Unsharp-mask image.
3743 */
3744 status=MagickTrue;
3745 progress=0;
3746 image_view=AcquireVirtualCacheView(image,exception);
3747 unsharp_view=AcquireAuthenticCacheView(unsharp_image,exception);
3748#if defined(MAGICKCORE_OPENMP_SUPPORT)
3749 #pragma omp parallel for schedule(static,4) shared(progress,status) \
3750 magick_threads(image,unsharp_image,image->rows,1)
3751#endif
3752 for (y=0; y < (ssize_t) image->rows; y++)
3753 {
3754 register const Quantum
3755 *restrict p;
3756
3757 register Quantum
3758 *restrict q;
3759
3760 register ssize_t
3761 x;
3762
3763 if (status == MagickFalse)
3764 continue;
3765 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3766 q=QueueCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
3767 exception);
3768 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3769 {
3770 status=MagickFalse;
3771 continue;
3772 }
3773 for (x=0; x < (ssize_t) image->columns; x++)
3774 {
3775 register ssize_t
3776 i;
3777
3778 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3779 {
3780 double
3781 pixel;
3782
3783 PixelChannel
3784 channel;
3785
3786 PixelTrait
3787 traits,
3788 unsharp_traits;
3789
3790 channel=GetPixelChannelChannel(image,i);
3791 traits=GetPixelChannelTraits(image,channel);
3792 unsharp_traits=GetPixelChannelTraits(unsharp_image,channel);
3793 if ((traits == UndefinedPixelTrait) ||
3794 (unsharp_traits == UndefinedPixelTrait))
3795 continue;
3796 if (((unsharp_traits & CopyPixelTrait) != 0) ||
dirk05d33c92014-09-04 21:03:28 +00003797 (GetPixelReadMask(image,p) == 0))
cristy3afd4012013-03-25 11:30:44 +00003798 {
3799 SetPixelChannel(unsharp_image,channel,p[i],q);
3800 continue;
3801 }
3802 pixel=p[i]-(double) GetPixelChannel(unsharp_image,channel,q);
3803 if (fabs(2.0*pixel) < quantum_threshold)
3804 pixel=(double) p[i];
3805 else
3806 pixel=(double) p[i]+gain*pixel;
3807 SetPixelChannel(unsharp_image,channel,ClampToQuantum(pixel),q);
3808 }
3809 p+=GetPixelChannels(image);
3810 q+=GetPixelChannels(unsharp_image);
3811 }
3812 if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
3813 status=MagickFalse;
3814 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3815 {
3816 MagickBooleanType
3817 proceed;
3818
3819#if defined(MAGICKCORE_OPENMP_SUPPORT)
3820 #pragma omp critical (MagickCore_UnsharpMaskImage)
3821#endif
3822 proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
3823 if (proceed == MagickFalse)
3824 status=MagickFalse;
3825 }
3826 }
3827 unsharp_image->type=image->type;
3828 unsharp_view=DestroyCacheView(unsharp_view);
3829 image_view=DestroyCacheView(image_view);
3830 if (status == MagickFalse)
3831 unsharp_image=DestroyImage(unsharp_image);
cristy3ed852e2009-09-05 21:47:34 +00003832 return(unsharp_image);
3833}