blob: 57e38dab08a4e8db668680284b8959e5b89546fb [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% TTTTT H H RRRR EEEEE SSSSS H H OOO L DDDD %
7% T H H R R E SS H H O O L D D %
8% T HHHHH RRRR EEE SSS HHHHH O O L D D %
9% T H H R R E SS H H O O L D D %
10% T H H R R EEEEE SSSSS H H OOO LLLLL DDDD %
11% %
12% %
13% MagickCore Image Threshold Methods %
14% %
15% Software Design %
16% John Cristy %
17% October 1996 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 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/property.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/colormap.h"
50#include "MagickCore/colorspace.h"
51#include "MagickCore/configure.h"
52#include "MagickCore/constitute.h"
53#include "MagickCore/decorate.h"
54#include "MagickCore/draw.h"
55#include "MagickCore/enhance.h"
56#include "MagickCore/exception.h"
57#include "MagickCore/exception-private.h"
58#include "MagickCore/effect.h"
59#include "MagickCore/fx.h"
60#include "MagickCore/gem.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/monitor.h"
67#include "MagickCore/monitor-private.h"
68#include "MagickCore/montage.h"
69#include "MagickCore/option.h"
70#include "MagickCore/pixel-accessor.h"
71#include "MagickCore/quantize.h"
72#include "MagickCore/quantum.h"
73#include "MagickCore/random_.h"
74#include "MagickCore/random-private.h"
75#include "MagickCore/resize.h"
76#include "MagickCore/resource_.h"
77#include "MagickCore/segment.h"
78#include "MagickCore/shear.h"
79#include "MagickCore/signature-private.h"
80#include "MagickCore/string_.h"
81#include "MagickCore/string-private.h"
82#include "MagickCore/thread-private.h"
83#include "MagickCore/threshold.h"
cristy4e0b82a2011-09-29 12:47:44 +000084#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000085#include "MagickCore/transform.h"
86#include "MagickCore/xml-tree.h"
cristy433d1182011-09-04 13:38:52 +000087#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000088
89/*
90 Define declarations.
91*/
92#define ThresholdsFilename "thresholds.xml"
93
94/*
95 Typedef declarations.
96*/
97struct _ThresholdMap
98{
99 char
100 *map_id,
101 *description;
102
cristybb503372010-05-27 20:51:26 +0000103 size_t
cristy3ed852e2009-09-05 21:47:34 +0000104 width,
105 height;
106
cristybb503372010-05-27 20:51:26 +0000107 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000108 divisor,
109 *levels;
110};
111
112/*
cristybd0ebf02011-09-29 01:19:42 +0000113 Forward declarations.
114*/
115static ThresholdMap
116 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
117
118/*
cristy3ed852e2009-09-05 21:47:34 +0000119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120% %
121% %
122% %
123% A d a p t i v e T h r e s h o l d I m a g e %
124% %
125% %
126% %
127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
128%
129% AdaptiveThresholdImage() selects an individual threshold for each pixel
130% based on the range of intensity values in its local neighborhood. This
131% allows for thresholding of an image whose global intensity histogram
132% doesn't contain distinctive peaks.
133%
134% The format of the AdaptiveThresholdImage method is:
135%
cristyde5cc632011-07-18 14:47:00 +0000136% Image *AdaptiveThresholdImage(const Image *image,const size_t width,
137% const size_t height,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000138%
139% A description of each parameter follows:
140%
141% o image: the image.
142%
143% o width: the width of the local neighborhood.
144%
145% o height: the height of the local neighborhood.
146%
cristyde5cc632011-07-18 14:47:00 +0000147% o bias: the mean bias.
cristy3ed852e2009-09-05 21:47:34 +0000148%
149% o exception: return any errors or warnings in this structure.
150%
151*/
152MagickExport Image *AdaptiveThresholdImage(const Image *image,
cristyde5cc632011-07-18 14:47:00 +0000153 const size_t width,const size_t height,const double bias,
cristy3ed852e2009-09-05 21:47:34 +0000154 ExceptionInfo *exception)
155{
cristyde5cc632011-07-18 14:47:00 +0000156#define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
cristy3ed852e2009-09-05 21:47:34 +0000157
cristyc4c8d132010-01-07 01:58:38 +0000158 CacheView
159 *image_view,
160 *threshold_view;
161
cristy3ed852e2009-09-05 21:47:34 +0000162 Image
163 *threshold_image;
164
cristy3ed852e2009-09-05 21:47:34 +0000165 MagickBooleanType
166 status;
167
cristy5f959472010-05-27 22:19:46 +0000168 MagickOffsetType
169 progress;
170
cristyde5cc632011-07-18 14:47:00 +0000171 MagickSizeType
cristy3ed852e2009-09-05 21:47:34 +0000172 number_pixels;
173
cristy5f959472010-05-27 22:19:46 +0000174 ssize_t
175 y;
176
cristyde5cc632011-07-18 14:47:00 +0000177 /*
178 Initialize threshold image attributes.
179 */
180 assert(image != (Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000181 assert(image->signature == MagickSignature);
182 if (image->debug != MagickFalse)
183 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
184 assert(exception != (ExceptionInfo *) NULL);
185 assert(exception->signature == MagickSignature);
cristyde5cc632011-07-18 14:47:00 +0000186 if ((width % 2) == 0)
187 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
188 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
189 exception);
cristy3ed852e2009-09-05 21:47:34 +0000190 if (threshold_image == (Image *) NULL)
191 return((Image *) NULL);
cristyb9eb87b2011-09-29 01:15:19 +0000192 status=SetImageStorageClass(threshold_image,DirectClass,exception);
193 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000194 {
cristy3ed852e2009-09-05 21:47:34 +0000195 threshold_image=DestroyImage(threshold_image);
196 return((Image *) NULL);
197 }
198 /*
cristyde5cc632011-07-18 14:47:00 +0000199 Threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000200 */
201 status=MagickTrue;
202 progress=0;
cristyde5cc632011-07-18 14:47:00 +0000203 number_pixels=(MagickSizeType) width*height;
cristy3ed852e2009-09-05 21:47:34 +0000204 image_view=AcquireCacheView(image);
205 threshold_view=AcquireCacheView(threshold_image);
cristyb5d5f722009-11-04 03:03:49 +0000206#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd36a25e2012-01-18 14:30:53 +0000207 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000208#endif
cristybb503372010-05-27 20:51:26 +0000209 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000210 {
cristy4c08aed2011-07-01 19:47:50 +0000211 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000212 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000213
cristy4c08aed2011-07-01 19:47:50 +0000214 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000215 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000216
cristyde5cc632011-07-18 14:47:00 +0000217 register ssize_t
218 x;
219
cristyde5cc632011-07-18 14:47:00 +0000220 ssize_t
221 center;
222
cristy3ed852e2009-09-05 21:47:34 +0000223 if (status == MagickFalse)
224 continue;
cristyd99b0962010-05-29 23:14:26 +0000225 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
cristyde5cc632011-07-18 14:47:00 +0000226 (height/2L),image->columns+width,height,exception);
227 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
228 1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000229 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000230 {
231 status=MagickFalse;
232 continue;
233 }
cristy5f9f2462011-09-28 23:37:58 +0000234 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
cristya0312c92011-07-23 21:04:30 +0000235 GetPixelChannels(image)*(width/2);
cristybb503372010-05-27 20:51:26 +0000236 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000237 {
cristybb503372010-05-27 20:51:26 +0000238 register ssize_t
cristyde5cc632011-07-18 14:47:00 +0000239 i;
cristy3ed852e2009-09-05 21:47:34 +0000240
cristya0312c92011-07-23 21:04:30 +0000241 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000242 {
cristyde5cc632011-07-18 14:47:00 +0000243 MagickRealType
244 mean,
245 pixel;
246
247 PixelChannel
248 channel;
249
250 PixelTrait
251 threshold_traits,
252 traits;
253
254 register const Quantum
255 *restrict pixels;
256
257 register ssize_t
258 u;
259
260 ssize_t
261 v;
262
cristye2a912b2011-12-05 20:02:07 +0000263 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000264 traits=GetPixelChannelMapTraits(image,channel);
cristyde5cc632011-07-18 14:47:00 +0000265 threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
cristy010d7d12011-08-31 01:02:48 +0000266 if ((traits == UndefinedPixelTrait) ||
267 (threshold_traits == UndefinedPixelTrait))
cristyde5cc632011-07-18 14:47:00 +0000268 continue;
cristyd09f8802012-02-04 16:44:10 +0000269 if (((threshold_traits & CopyPixelTrait) != 0) ||
270 (GetPixelMask(image,p) != 0))
cristyde5cc632011-07-18 14:47:00 +0000271 {
cristy0beccfa2011-09-25 20:47:53 +0000272 SetPixelChannel(threshold_image,channel,p[center+i],q);
cristyde5cc632011-07-18 14:47:00 +0000273 continue;
274 }
275 pixels=p;
276 pixel=0.0;
277 for (v=0; v < (ssize_t) height; v++)
cristy3ed852e2009-09-05 21:47:34 +0000278 {
cristyde5cc632011-07-18 14:47:00 +0000279 for (u=0; u < (ssize_t) width; u++)
280 {
281 pixel+=pixels[i];
cristya0312c92011-07-23 21:04:30 +0000282 pixels+=GetPixelChannels(image);
cristyde5cc632011-07-18 14:47:00 +0000283 }
cristya0312c92011-07-23 21:04:30 +0000284 pixels+=image->columns*GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000285 }
cristy5f9f2462011-09-28 23:37:58 +0000286 mean=(MagickRealType) (pixel/number_pixels+bias);
287 SetPixelChannel(threshold_image,channel,(Quantum) ((MagickRealType)
288 p[center+i] <= mean ? 0 : QuantumRange),q);
cristy3ed852e2009-09-05 21:47:34 +0000289 }
cristya0312c92011-07-23 21:04:30 +0000290 p+=GetPixelChannels(image);
291 q+=GetPixelChannels(threshold_image);
cristy3ed852e2009-09-05 21:47:34 +0000292 }
cristyde5cc632011-07-18 14:47:00 +0000293 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000294 status=MagickFalse;
295 if (image->progress_monitor != (MagickProgressMonitor) NULL)
296 {
297 MagickBooleanType
298 proceed;
299
cristyb5d5f722009-11-04 03:03:49 +0000300#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000301 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
302#endif
cristyde5cc632011-07-18 14:47:00 +0000303 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
cristy3ed852e2009-09-05 21:47:34 +0000304 image->rows);
305 if (proceed == MagickFalse)
306 status=MagickFalse;
307 }
308 }
cristyde5cc632011-07-18 14:47:00 +0000309 threshold_image->type=image->type;
cristy3ed852e2009-09-05 21:47:34 +0000310 threshold_view=DestroyCacheView(threshold_view);
311 image_view=DestroyCacheView(image_view);
312 if (status == MagickFalse)
313 threshold_image=DestroyImage(threshold_image);
314 return(threshold_image);
315}
316
317/*
318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319% %
320% %
321% %
322% B i l e v e l I m a g e %
323% %
324% %
325% %
326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327%
328% BilevelImage() changes the value of individual pixels based on the
329% intensity of each pixel channel. The result is a high-contrast image.
330%
331% More precisely each channel value of the image is 'thresholded' so that if
332% it is equal to or less than the given value it is set to zero, while any
333% value greater than that give is set to it maximum or QuantumRange.
334%
335% This function is what is used to implement the "-threshold" operator for
336% the command line API.
337%
338% If the default channel setting is given the image is thresholded using just
339% the gray 'intensity' of the image, rather than the individual channels.
340%
cristyf4ad9df2011-07-08 16:49:03 +0000341% The format of the BilevelImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000342%
cristye941a752011-10-15 01:52:48 +0000343% MagickBooleanType BilevelImage(Image *image,const double threshold,
344% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000345%
346% A description of each parameter follows:
347%
348% o image: the image.
349%
cristy3ed852e2009-09-05 21:47:34 +0000350% o threshold: define the threshold values.
351%
cristye941a752011-10-15 01:52:48 +0000352% o exception: return any errors or warnings in this structure.
353%
cristyf89cb1d2011-07-07 01:24:37 +0000354% Aside: You can get the same results as operator using LevelImages()
cristy3ed852e2009-09-05 21:47:34 +0000355% with the 'threshold' value for both the black_point and the white_point.
356%
357*/
cristye941a752011-10-15 01:52:48 +0000358MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
359 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000360{
361#define ThresholdImageTag "Threshold/Image"
362
cristyc4c8d132010-01-07 01:58:38 +0000363 CacheView
364 *image_view;
365
cristy3ed852e2009-09-05 21:47:34 +0000366 MagickBooleanType
367 status;
368
cristy5f959472010-05-27 22:19:46 +0000369 MagickOffsetType
370 progress;
371
372 ssize_t
373 y;
374
cristy3ed852e2009-09-05 21:47:34 +0000375 assert(image != (Image *) NULL);
376 assert(image->signature == MagickSignature);
377 if (image->debug != MagickFalse)
378 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy574cc262011-08-05 01:23:58 +0000379 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000380 return(MagickFalse);
381 /*
382 Bilevel threshold image.
383 */
384 status=MagickTrue;
385 progress=0;
cristy3ed852e2009-09-05 21:47:34 +0000386 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000387#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +0000388 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000389#endif
cristybb503372010-05-27 20:51:26 +0000390 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000391 {
cristybb503372010-05-27 20:51:26 +0000392 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000393 x;
394
cristy4c08aed2011-07-01 19:47:50 +0000395 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000396 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000397
398 if (status == MagickFalse)
399 continue;
400 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000401 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000402 {
403 status=MagickFalse;
404 continue;
405 }
cristy805b6a02011-08-09 00:59:35 +0000406 for (x=0; x < (ssize_t) image->columns; x++)
407 {
cristy95111202011-08-09 19:41:42 +0000408 register ssize_t
409 i;
410
cristy10a6c612012-01-29 21:41:05 +0000411 if (GetPixelMask(image,q) != 0)
412 {
413 q+=GetPixelChannels(image);
414 continue;
415 }
cristy95111202011-08-09 19:41:42 +0000416 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
417 {
cristyabace412011-12-11 15:56:53 +0000418 PixelChannel
419 channel;
420
cristy95111202011-08-09 19:41:42 +0000421 PixelTrait
422 traits;
423
cristyabace412011-12-11 15:56:53 +0000424 channel=GetPixelChannelMapChannel(image,i);
425 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +0000426 if ((traits & UpdatePixelTrait) == 0)
427 continue;
428 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 : QuantumRange);
cristy95111202011-08-09 19:41:42 +0000429 }
cristy805b6a02011-08-09 00:59:35 +0000430 q+=GetPixelChannels(image);
431 }
cristy3ed852e2009-09-05 21:47:34 +0000432 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
433 status=MagickFalse;
434 if (image->progress_monitor != (MagickProgressMonitor) NULL)
435 {
436 MagickBooleanType
437 proceed;
438
cristyb5d5f722009-11-04 03:03:49 +0000439#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000440 #pragma omp critical (MagickCore_BilevelImage)
cristy3ed852e2009-09-05 21:47:34 +0000441#endif
442 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
443 image->rows);
444 if (proceed == MagickFalse)
445 status=MagickFalse;
446 }
447 }
448 image_view=DestroyCacheView(image_view);
449 return(status);
450}
451
452/*
453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454% %
455% %
456% %
457% B l a c k T h r e s h o l d I m a g e %
458% %
459% %
460% %
461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
462%
463% BlackThresholdImage() is like ThresholdImage() but forces all pixels below
cristy4e101302009-09-17 12:49:12 +0000464% the threshold into black while leaving all pixels at or above the threshold
cristy3ed852e2009-09-05 21:47:34 +0000465% unchanged.
466%
467% The format of the BlackThresholdImage method is:
468%
cristyf4ad9df2011-07-08 16:49:03 +0000469% MagickBooleanType BlackThresholdImage(Image *image,
470% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000471%
472% A description of each parameter follows:
473%
474% o image: the image.
475%
cristy5f9f2462011-09-28 23:37:58 +0000476% o threshold: define the threshold value.
cristy3ed852e2009-09-05 21:47:34 +0000477%
478% o exception: return any errors or warnings in this structure.
479%
480*/
481MagickExport MagickBooleanType BlackThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +0000482 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000483{
484#define ThresholdImageTag "Threshold/Image"
485
cristyc4c8d132010-01-07 01:58:38 +0000486 CacheView
487 *image_view;
488
cristy3ed852e2009-09-05 21:47:34 +0000489 GeometryInfo
490 geometry_info;
491
cristy3ed852e2009-09-05 21:47:34 +0000492 MagickBooleanType
493 status;
494
cristy5f959472010-05-27 22:19:46 +0000495 MagickOffsetType
496 progress;
497
cristy5f9f2462011-09-28 23:37:58 +0000498 MagickRealType
499 threshold[5];
cristy3ed852e2009-09-05 21:47:34 +0000500
501 MagickStatusType
502 flags;
503
cristy5f9f2462011-09-28 23:37:58 +0000504 register ssize_t
505 i;
506
cristy5f959472010-05-27 22:19:46 +0000507 ssize_t
508 y;
509
cristy3ed852e2009-09-05 21:47:34 +0000510 assert(image != (Image *) NULL);
511 assert(image->signature == MagickSignature);
512 if (image->debug != MagickFalse)
513 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
514 if (thresholds == (const char *) NULL)
515 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +0000516 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000517 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000518 flags=ParseGeometry(thresholds,&geometry_info);
cristy5f9f2462011-09-28 23:37:58 +0000519 for (i=0; i < 5; i++)
520 threshold[i]=geometry_info.rho;
521 if ((flags & SigmaValue) != 0)
522 threshold[1]=geometry_info.sigma;
523 if ((flags & XiValue) != 0)
524 threshold[2]=geometry_info.xi;
525 if ((flags & PsiValue) != 0)
526 threshold[3]=geometry_info.psi;
527 if ((flags & ChiValue) != 0)
528 threshold[4]=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +0000529 if ((flags & PercentValue) != 0)
cristy5f9f2462011-09-28 23:37:58 +0000530 for (i=0; i < 5; i++)
531 threshold[i]*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +0000532 /*
cristy5f9f2462011-09-28 23:37:58 +0000533 White threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000534 */
535 status=MagickTrue;
536 progress=0;
537 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000538#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +0000539 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000540#endif
cristybb503372010-05-27 20:51:26 +0000541 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000542 {
cristybb503372010-05-27 20:51:26 +0000543 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000544 x;
545
cristy4c08aed2011-07-01 19:47:50 +0000546 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000547 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000548
549 if (status == MagickFalse)
550 continue;
551 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000552 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000553 {
554 status=MagickFalse;
555 continue;
556 }
cristybb503372010-05-27 20:51:26 +0000557 for (x=0; x < (ssize_t) image->columns; x++)
cristyb0ea1af2009-11-28 20:44:46 +0000558 {
cristy5f9f2462011-09-28 23:37:58 +0000559 register ssize_t
560 i;
561
562 ssize_t
563 n;
564
cristy10a6c612012-01-29 21:41:05 +0000565 if (GetPixelMask(image,q) != 0)
566 {
567 q+=GetPixelChannels(image);
568 continue;
569 }
cristyc94ba6f2012-01-29 23:19:58 +0000570 n=0;
cristy5f9f2462011-09-28 23:37:58 +0000571 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
572 {
cristyabace412011-12-11 15:56:53 +0000573 PixelChannel
574 channel;
575
cristy5f9f2462011-09-28 23:37:58 +0000576 PixelTrait
577 traits;
578
cristyabace412011-12-11 15:56:53 +0000579 channel=GetPixelChannelMapChannel(image,i);
580 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +0000581 if ((traits & UpdatePixelTrait) == 0)
582 continue;
583 if ((MagickRealType) q[i] < threshold[n++ % 5])
584 q[i]=QuantumRange;
585 }
cristyed231572011-07-14 02:18:59 +0000586 q+=GetPixelChannels(image);
cristyb0ea1af2009-11-28 20:44:46 +0000587 }
cristy3ed852e2009-09-05 21:47:34 +0000588 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
589 status=MagickFalse;
590 if (image->progress_monitor != (MagickProgressMonitor) NULL)
591 {
592 MagickBooleanType
593 proceed;
594
cristyb5d5f722009-11-04 03:03:49 +0000595#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy5f9f2462011-09-28 23:37:58 +0000596 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +0000597#endif
598 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
599 image->rows);
600 if (proceed == MagickFalse)
601 status=MagickFalse;
602 }
603 }
604 image_view=DestroyCacheView(image_view);
605 return(status);
606}
607
608/*
609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
610% %
611% %
612% %
cristy1eb45dd2009-09-25 16:38:06 +0000613% C l a m p I m a g e %
614% %
615% %
616% %
617%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
618%
cristyecb0c6d2009-09-25 16:50:09 +0000619% ClampImage() restricts the color range from 0 to the quantum depth.
cristy1eb45dd2009-09-25 16:38:06 +0000620%
cristyf4ad9df2011-07-08 16:49:03 +0000621% The format of the ClampImage method is:
cristy1eb45dd2009-09-25 16:38:06 +0000622%
cristy092d71c2011-10-14 18:01:29 +0000623% MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000624%
625% A description of each parameter follows:
626%
627% o image: the image.
628%
cristy092d71c2011-10-14 18:01:29 +0000629% o exception: return any errors or warnings in this structure.
630%
cristy1eb45dd2009-09-25 16:38:06 +0000631*/
632
cristy75ffdb72010-01-07 17:40:12 +0000633static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
cristy1eb45dd2009-09-25 16:38:06 +0000634{
635#if defined(MAGICKCORE_HDRI_SUPPORT)
636 if (quantum <= 0)
637 return(0);
638 if (quantum >= QuantumRange)
639 return(QuantumRange);
640 return(quantum);
641#else
642 return(quantum);
643#endif
644}
645
cristy092d71c2011-10-14 18:01:29 +0000646MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000647{
cristy1eb45dd2009-09-25 16:38:06 +0000648#define ClampImageTag "Clamp/Image"
649
cristyc4c8d132010-01-07 01:58:38 +0000650 CacheView
651 *image_view;
652
cristy1eb45dd2009-09-25 16:38:06 +0000653 MagickBooleanType
654 status;
655
cristy5f959472010-05-27 22:19:46 +0000656 MagickOffsetType
657 progress;
658
659 ssize_t
660 y;
661
cristy1eb45dd2009-09-25 16:38:06 +0000662 assert(image != (Image *) NULL);
663 assert(image->signature == MagickSignature);
664 if (image->debug != MagickFalse)
665 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
666 if (image->storage_class == PseudoClass)
667 {
cristybb503372010-05-27 20:51:26 +0000668 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000669 i;
670
cristy101ab702011-10-13 13:06:32 +0000671 register PixelInfo
cristyc47d1f82009-11-26 01:44:43 +0000672 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000673
674 q=image->colormap;
cristybb503372010-05-27 20:51:26 +0000675 for (i=0; i < (ssize_t) image->colors; i++)
cristy1eb45dd2009-09-25 16:38:06 +0000676 {
cristy4c08aed2011-07-01 19:47:50 +0000677 q->red=ClampToUnsignedQuantum(q->red);
678 q->green=ClampToUnsignedQuantum(q->green);
679 q->blue=ClampToUnsignedQuantum(q->blue);
680 q->alpha=ClampToUnsignedQuantum(q->alpha);
cristy1eb45dd2009-09-25 16:38:06 +0000681 q++;
682 }
cristyea1a8aa2011-10-20 13:24:06 +0000683 return(SyncImage(image,exception));
cristy1eb45dd2009-09-25 16:38:06 +0000684 }
685 /*
cristy611721d2009-09-25 16:42:17 +0000686 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000687 */
688 status=MagickTrue;
689 progress=0;
cristy1eb45dd2009-09-25 16:38:06 +0000690 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000691#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +0000692 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy1eb45dd2009-09-25 16:38:06 +0000693#endif
cristybb503372010-05-27 20:51:26 +0000694 for (y=0; y < (ssize_t) image->rows; y++)
cristy1eb45dd2009-09-25 16:38:06 +0000695 {
cristybb503372010-05-27 20:51:26 +0000696 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000697 x;
698
cristy4c08aed2011-07-01 19:47:50 +0000699 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000700 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000701
702 if (status == MagickFalse)
703 continue;
704 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000705 if (q == (Quantum *) NULL)
cristy1eb45dd2009-09-25 16:38:06 +0000706 {
707 status=MagickFalse;
708 continue;
709 }
cristybb503372010-05-27 20:51:26 +0000710 for (x=0; x < (ssize_t) image->columns; x++)
cristy1eb45dd2009-09-25 16:38:06 +0000711 {
cristy5f9f2462011-09-28 23:37:58 +0000712 register ssize_t
713 i;
714
cristy10a6c612012-01-29 21:41:05 +0000715 if (GetPixelMask(image,q) != 0)
716 {
717 q+=GetPixelChannels(image);
718 continue;
719 }
cristy5f9f2462011-09-28 23:37:58 +0000720 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
721 {
cristyabace412011-12-11 15:56:53 +0000722 PixelChannel
723 channel;
724
cristy5f9f2462011-09-28 23:37:58 +0000725 PixelTrait
726 traits;
727
cristyabace412011-12-11 15:56:53 +0000728 channel=GetPixelChannelMapChannel(image,i);
729 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +0000730 if (traits == UndefinedPixelTrait)
731 continue;
732 q[i]=ClampToUnsignedQuantum(q[i]);
733 }
cristyed231572011-07-14 02:18:59 +0000734 q+=GetPixelChannels(image);
cristy1eb45dd2009-09-25 16:38:06 +0000735 }
736 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
737 status=MagickFalse;
738 if (image->progress_monitor != (MagickProgressMonitor) NULL)
739 {
740 MagickBooleanType
741 proceed;
742
cristyb5d5f722009-11-04 03:03:49 +0000743#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000744 #pragma omp critical (MagickCore_ClampImage)
cristy1eb45dd2009-09-25 16:38:06 +0000745#endif
746 proceed=SetImageProgress(image,ClampImageTag,progress++,
747 image->rows);
748 if (proceed == MagickFalse)
749 status=MagickFalse;
750 }
751 }
752 image_view=DestroyCacheView(image_view);
753 return(status);
754}
755
756/*
757%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
758% %
759% %
760% %
cristy3ed852e2009-09-05 21:47:34 +0000761% D e s t r o y T h r e s h o l d M a p %
762% %
763% %
764% %
765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
766%
767% DestroyThresholdMap() de-allocate the given ThresholdMap
768%
769% The format of the ListThresholdMaps method is:
770%
771% ThresholdMap *DestroyThresholdMap(Threshold *map)
772%
773% A description of each parameter follows.
774%
775% o map: Pointer to the Threshold map to destroy
776%
777*/
778MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
779{
780 assert(map != (ThresholdMap *) NULL);
781 if (map->map_id != (char *) NULL)
782 map->map_id=DestroyString(map->map_id);
783 if (map->description != (char *) NULL)
784 map->description=DestroyString(map->description);
cristybb503372010-05-27 20:51:26 +0000785 if (map->levels != (ssize_t *) NULL)
786 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
cristy3ed852e2009-09-05 21:47:34 +0000787 map=(ThresholdMap *) RelinquishMagickMemory(map);
788 return(map);
789}
790
791/*
792%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
793% %
794% %
795% %
cristyb9eb87b2011-09-29 01:15:19 +0000796% G e t T h r e s h o l d M a p %
797% %
798% %
799% %
800%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
801%
802% GetThresholdMap() loads and searches one or more threshold map files for the
803% map matching the given name or alias.
804%
805% The format of the GetThresholdMap method is:
806%
807% ThresholdMap *GetThresholdMap(const char *map_id,
808% ExceptionInfo *exception)
809%
810% A description of each parameter follows.
811%
812% o map_id: ID of the map to look for.
813%
814% o exception: return any errors or warnings in this structure.
815%
816*/
817MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
818 ExceptionInfo *exception)
819{
820 const StringInfo
821 *option;
822
823 LinkedListInfo
824 *options;
825
826 ThresholdMap
827 *map;
828
829 map=(ThresholdMap *)NULL;
830 options=GetConfigureOptions(ThresholdsFilename,exception);
831 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
832 (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
833 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
834 GetStringInfoPath(option),map_id,exception);
835 options=DestroyConfigureOptions(options);
836 return(map);
837}
838
839/*
840%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
841% %
842% %
843% %
cristy3ed852e2009-09-05 21:47:34 +0000844+ G e t T h r e s h o l d M a p F i l e %
845% %
846% %
847% %
848%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
849%
850% GetThresholdMapFile() look for a given threshold map name or alias in the
851% given XML file data, and return the allocated the map when found.
852%
853% The format of the ListThresholdMaps method is:
854%
855% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
856% const char *map_id,ExceptionInfo *exception)
857%
858% A description of each parameter follows.
859%
860% o xml: The threshold map list in XML format.
861%
862% o filename: The threshold map XML filename.
863%
864% o map_id: ID of the map to look for in XML list.
865%
866% o exception: return any errors or warnings in this structure.
867%
868*/
cristybd0ebf02011-09-29 01:19:42 +0000869static ThresholdMap *GetThresholdMapFile(const char *xml,
cristy3ed852e2009-09-05 21:47:34 +0000870 const char *filename,const char *map_id,ExceptionInfo *exception)
871{
cristyb9eb87b2011-09-29 01:15:19 +0000872 char
873 *p;
874
cristy3ed852e2009-09-05 21:47:34 +0000875 const char
cristyb9eb87b2011-09-29 01:15:19 +0000876 *attribute,
cristy3ed852e2009-09-05 21:47:34 +0000877 *content;
878
879 double
880 value;
881
cristyb9eb87b2011-09-29 01:15:19 +0000882 register ssize_t
883 i;
cristy3ed852e2009-09-05 21:47:34 +0000884
885 ThresholdMap
886 *map;
887
cristyb9eb87b2011-09-29 01:15:19 +0000888 XMLTreeInfo
889 *description,
890 *levels,
891 *threshold,
892 *thresholds;
893
894 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
895 "Loading threshold map file \"%s\" ...",filename);
896 map=(ThresholdMap *) NULL;
897 thresholds=NewXMLTree(xml,exception);
898 if (thresholds == (XMLTreeInfo *) NULL)
899 return(map);
900 for (threshold=GetXMLTreeChild(thresholds,"threshold");
901 threshold != (XMLTreeInfo *) NULL;
902 threshold=GetNextXMLTreeTag(threshold))
903 {
904 attribute=GetXMLTreeAttribute(threshold,"map");
905 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
906 break;
907 attribute=GetXMLTreeAttribute(threshold,"alias");
908 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
909 break;
910 }
911 if (threshold == (XMLTreeInfo *) NULL)
912 return(map);
913 description=GetXMLTreeChild(threshold,"description");
914 if (description == (XMLTreeInfo *) NULL)
915 {
916 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
917 "XmlMissingElement", "<description>, map \"%s\"",map_id);
918 thresholds=DestroyXMLTree(thresholds);
919 return(map);
920 }
921 levels=GetXMLTreeChild(threshold,"levels");
922 if (levels == (XMLTreeInfo *) NULL)
923 {
924 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
925 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
926 thresholds=DestroyXMLTree(thresholds);
927 return(map);
928 }
929 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
930 if (map == (ThresholdMap *) NULL)
931 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
932 map->map_id=(char *) NULL;
933 map->description=(char *) NULL;
934 map->levels=(ssize_t *) NULL;
935 attribute=GetXMLTreeAttribute(threshold,"map");
936 if (attribute != (char *) NULL)
937 map->map_id=ConstantString(attribute);
938 content=GetXMLTreeContent(description);
939 if (content != (char *) NULL)
940 map->description=ConstantString(content);
941 attribute=GetXMLTreeAttribute(levels,"width");
942 if (attribute == (char *) NULL)
943 {
944 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
945 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
946 thresholds=DestroyXMLTree(thresholds);
947 map=DestroyThresholdMap(map);
948 return(map);
949 }
950 map->width=StringToUnsignedLong(attribute);
951 if (map->width == 0)
952 {
953 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
954 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
955 thresholds=DestroyXMLTree(thresholds);
956 map=DestroyThresholdMap(map);
957 return(map);
958 }
959 attribute=GetXMLTreeAttribute(levels,"height");
960 if (attribute == (char *) NULL)
961 {
962 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
963 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
964 thresholds=DestroyXMLTree(thresholds);
965 map=DestroyThresholdMap(map);
966 return(map);
967 }
968 map->height=StringToUnsignedLong(attribute);
969 if (map->height == 0)
970 {
971 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
972 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
973 thresholds=DestroyXMLTree(thresholds);
974 map=DestroyThresholdMap(map);
975 return(map);
976 }
977 attribute=GetXMLTreeAttribute(levels,"divisor");
978 if (attribute == (char *) NULL)
979 {
980 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
981 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
982 thresholds=DestroyXMLTree(thresholds);
983 map=DestroyThresholdMap(map);
984 return(map);
985 }
986 map->divisor=(ssize_t) StringToLong(attribute);
987 if (map->divisor < 2)
988 {
989 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
990 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
991 thresholds=DestroyXMLTree(thresholds);
992 map=DestroyThresholdMap(map);
993 return(map);
994 }
995 content=GetXMLTreeContent(levels);
996 if (content == (char *) NULL)
997 {
998 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
999 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1000 thresholds=DestroyXMLTree(thresholds);
1001 map=DestroyThresholdMap(map);
1002 return(map);
1003 }
1004 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1005 sizeof(*map->levels));
1006 if (map->levels == (ssize_t *) NULL)
1007 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1008 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1009 {
1010 map->levels[i]=(ssize_t) strtol(content,&p,10);
1011 if (p == content)
1012 {
1013 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1014 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1015 thresholds=DestroyXMLTree(thresholds);
1016 map=DestroyThresholdMap(map);
1017 return(map);
1018 }
1019 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1020 {
1021 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1022 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1023 (double) map->levels[i],map_id);
1024 thresholds=DestroyXMLTree(thresholds);
1025 map=DestroyThresholdMap(map);
1026 return(map);
1027 }
1028 content=p;
1029 }
1030 value=(double) strtol(content,&p,10);
1031 (void) value;
1032 if (p != content)
1033 {
1034 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1035 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1036 thresholds=DestroyXMLTree(thresholds);
1037 map=DestroyThresholdMap(map);
1038 return(map);
1039 }
1040 thresholds=DestroyXMLTree(thresholds);
cristy3ed852e2009-09-05 21:47:34 +00001041 return(map);
1042}
1043
1044/*
1045%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1046% %
1047% %
1048% %
1049+ L i s t T h r e s h o l d M a p F i l e %
1050% %
1051% %
1052% %
1053%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1054%
1055% ListThresholdMapFile() lists the threshold maps and their descriptions
1056% in the given XML file data.
1057%
1058% The format of the ListThresholdMaps method is:
1059%
1060% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1061% const char *filename,ExceptionInfo *exception)
1062%
1063% A description of each parameter follows.
1064%
1065% o file: An pointer to the output FILE.
1066%
1067% o xml: The threshold map list in XML format.
1068%
1069% o filename: The threshold map XML filename.
1070%
1071% o exception: return any errors or warnings in this structure.
1072%
1073*/
1074MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1075 const char *filename,ExceptionInfo *exception)
1076{
cristy5f9f2462011-09-28 23:37:58 +00001077 const char
1078 *alias,
1079 *content,
1080 *map;
1081
1082 XMLTreeInfo
1083 *description,
1084 *threshold,
1085 *thresholds;
cristy3ed852e2009-09-05 21:47:34 +00001086
1087 assert( xml != (char *)NULL );
1088 assert( file != (FILE *)NULL );
cristy3ed852e2009-09-05 21:47:34 +00001089 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1090 "Loading threshold map file \"%s\" ...",filename);
1091 thresholds=NewXMLTree(xml,exception);
1092 if ( thresholds == (XMLTreeInfo *)NULL )
1093 return(MagickFalse);
cristy1e604812011-05-19 18:07:50 +00001094 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1095 (void) FormatLocaleFile(file,
1096 "----------------------------------------------------\n");
cristy5f9f2462011-09-28 23:37:58 +00001097 threshold=GetXMLTreeChild(thresholds,"threshold");
cristyb9eb87b2011-09-29 01:15:19 +00001098 for ( ; threshold != (XMLTreeInfo *) NULL;
1099 threshold=GetNextXMLTreeTag(threshold))
cristy3ed852e2009-09-05 21:47:34 +00001100 {
cristy5f9f2462011-09-28 23:37:58 +00001101 map=GetXMLTreeAttribute(threshold,"map");
1102 if (map == (char *) NULL)
1103 {
1104 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1105 "XmlMissingAttribute", "<map>");
1106 thresholds=DestroyXMLTree(thresholds);
1107 return(MagickFalse);
1108 }
1109 alias=GetXMLTreeAttribute(threshold,"alias");
cristy3ed852e2009-09-05 21:47:34 +00001110 description=GetXMLTreeChild(threshold,"description");
cristy5f9f2462011-09-28 23:37:58 +00001111 if (description == (XMLTreeInfo *) NULL)
1112 {
1113 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyb9eb87b2011-09-29 01:15:19 +00001114 "XmlMissingElement", "<description>, map \"%s\"",map);
cristy5f9f2462011-09-28 23:37:58 +00001115 thresholds=DestroyXMLTree(thresholds);
1116 return(MagickFalse);
1117 }
cristy3ed852e2009-09-05 21:47:34 +00001118 content=GetXMLTreeContent(description);
cristy5f9f2462011-09-28 23:37:58 +00001119 if (content == (char *) NULL)
1120 {
1121 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1122 "XmlMissingContent", "<description>, map \"%s\"", map);
1123 thresholds=DestroyXMLTree(thresholds);
1124 return(MagickFalse);
1125 }
cristy1e604812011-05-19 18:07:50 +00001126 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1127 content);
cristy3ed852e2009-09-05 21:47:34 +00001128 }
1129 thresholds=DestroyXMLTree(thresholds);
1130 return(MagickTrue);
1131}
1132
1133/*
1134%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1135% %
1136% %
1137% %
1138% L i s t T h r e s h o l d M a p s %
1139% %
1140% %
1141% %
1142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1143%
1144% ListThresholdMaps() lists the threshold maps and their descriptions
1145% as defined by "threshold.xml" to a file.
1146%
1147% The format of the ListThresholdMaps method is:
1148%
1149% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1150%
1151% A description of each parameter follows.
1152%
1153% o file: An pointer to the output FILE.
1154%
1155% o exception: return any errors or warnings in this structure.
1156%
1157*/
1158MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1159 ExceptionInfo *exception)
1160{
1161 const StringInfo
1162 *option;
1163
1164 LinkedListInfo
1165 *options;
1166
1167 MagickStatusType
1168 status;
1169
1170 status=MagickFalse;
cristy5f9f2462011-09-28 23:37:58 +00001171 if (file == (FILE *) NULL)
1172 file=stdout;
cristy3ed852e2009-09-05 21:47:34 +00001173 options=GetConfigureOptions(ThresholdsFilename,exception);
cristy1e604812011-05-19 18:07:50 +00001174 (void) FormatLocaleFile(file,
1175 "\n Threshold Maps for Ordered Dither Operations\n");
cristy5f9f2462011-09-28 23:37:58 +00001176 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1177 (const StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001178 {
cristyb51dff52011-05-19 16:55:47 +00001179 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
cristy3ed852e2009-09-05 21:47:34 +00001180 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1181 GetStringInfoPath(option),exception);
1182 }
1183 options=DestroyConfigureOptions(options);
1184 return(status != 0 ? MagickTrue : MagickFalse);
1185}
1186
1187/*
1188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1189% %
1190% %
1191% %
cristy3ed852e2009-09-05 21:47:34 +00001192% O r d e r e d P o s t e r i z e I m a g e %
1193% %
1194% %
1195% %
1196%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1197%
1198% OrderedPosterizeImage() will perform a ordered dither based on a number
1199% of pre-defined dithering threshold maps, but over multiple intensity
1200% levels, which can be different for different channels, according to the
1201% input argument.
1202%
1203% The format of the OrderedPosterizeImage method is:
1204%
1205% MagickBooleanType OrderedPosterizeImage(Image *image,
1206% const char *threshold_map,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001207%
1208% A description of each parameter follows:
1209%
1210% o image: the image.
1211%
cristy3ed852e2009-09-05 21:47:34 +00001212% o threshold_map: A string containing the name of the threshold dither
1213% map to use, followed by zero or more numbers representing the number
1214% of color levels tho dither between.
1215%
cristyf998fb32011-04-27 23:00:47 +00001216% Any level number less than 2 will be equivalent to 2, and means only
cristy3ed852e2009-09-05 21:47:34 +00001217% binary dithering will be applied to each color channel.
1218%
1219% No numbers also means a 2 level (bitmap) dither will be applied to all
1220% channels, while a single number is the number of levels applied to each
1221% channel in sequence. More numbers will be applied in turn to each of
1222% the color channels.
1223%
1224% For example: "o3x3,6" will generate a 6 level posterization of the
1225% image with a ordered 3x3 diffused pixel dither being applied between
1226% each level. While checker,8,8,4 will produce a 332 colormaped image
1227% with only a single checkerboard hash pattern (50% grey) between each
1228% color level, to basically double the number of color levels with
1229% a bare minimim of dithering.
1230%
1231% o exception: return any errors or warnings in this structure.
1232%
1233*/
1234MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1235 const char *threshold_map,ExceptionInfo *exception)
1236{
cristy3ed852e2009-09-05 21:47:34 +00001237#define DitherImageTag "Dither/Image"
1238
cristyc4c8d132010-01-07 01:58:38 +00001239 CacheView
1240 *image_view;
1241
cristy4e0b82a2011-09-29 12:47:44 +00001242 char
1243 token[MaxTextExtent];
1244
1245 const char
1246 *p;
cristy3ed852e2009-09-05 21:47:34 +00001247
1248 MagickBooleanType
1249 status;
1250
cristy5f959472010-05-27 22:19:46 +00001251 MagickOffsetType
1252 progress;
1253
cristy4e0b82a2011-09-29 12:47:44 +00001254 MagickRealType
cristy5f95f4f2011-10-23 01:01:01 +00001255 levels[CompositePixelChannel];
cristy4e0b82a2011-09-29 12:47:44 +00001256
1257 register ssize_t
1258 i;
1259
cristy5f959472010-05-27 22:19:46 +00001260 ssize_t
1261 y;
1262
cristy3ed852e2009-09-05 21:47:34 +00001263 ThresholdMap
1264 *map;
1265
cristy3ed852e2009-09-05 21:47:34 +00001266 assert(image != (Image *) NULL);
1267 assert(image->signature == MagickSignature);
1268 if (image->debug != MagickFalse)
1269 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1270 assert(exception != (ExceptionInfo *) NULL);
1271 assert(exception->signature == MagickSignature);
1272 if (threshold_map == (const char *) NULL)
1273 return(MagickTrue);
cristy4e0b82a2011-09-29 12:47:44 +00001274 p=(char *) threshold_map;
1275 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1276 (*p != '\0'))
1277 p++;
1278 threshold_map=p;
1279 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1280 (*p != '\0'))
cristy3ed852e2009-09-05 21:47:34 +00001281 {
cristy4e0b82a2011-09-29 12:47:44 +00001282 if ((p-threshold_map) >= (MaxTextExtent-1))
1283 break;
1284 token[p-threshold_map]=(*p);
1285 p++;
cristy3ed852e2009-09-05 21:47:34 +00001286 }
cristy4e0b82a2011-09-29 12:47:44 +00001287 token[p-threshold_map]='\0';
1288 map=GetThresholdMap(token,exception);
1289 if (map == (ThresholdMap *) NULL)
1290 {
1291 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1292 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
cristy574cc262011-08-05 01:23:58 +00001293 return(MagickFalse);
cristy4e0b82a2011-09-29 12:47:44 +00001294 }
1295 for (i=0; i < MaxPixelChannels; i++)
1296 levels[i]=2.0;
1297 p=strchr((char *) threshold_map,',');
1298 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1299 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1300 {
1301 GetMagickToken(p,&p,token);
1302 if (*token == ',')
1303 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001304 levels[i]=StringToDouble(token,(char **) NULL);
cristy4e0b82a2011-09-29 12:47:44 +00001305 }
1306 for (i=0; i < MaxPixelChannels; i++)
1307 if (fabs(levels[i]) >= 1)
1308 levels[i]-=1.0;
1309 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1310 return(MagickFalse);
1311 status=MagickTrue;
1312 progress=0;
1313 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001314#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00001315 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001316#endif
cristy4e0b82a2011-09-29 12:47:44 +00001317 for (y=0; y < (ssize_t) image->rows; y++)
1318 {
1319 register ssize_t
1320 x;
1321
1322 register Quantum
1323 *restrict q;
1324
1325 if (status == MagickFalse)
1326 continue;
1327 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1328 if (q == (Quantum *) NULL)
1329 {
1330 status=MagickFalse;
1331 continue;
1332 }
1333 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001334 {
cristybb503372010-05-27 20:51:26 +00001335 register ssize_t
cristy4e0b82a2011-09-29 12:47:44 +00001336 i;
cristy3ed852e2009-09-05 21:47:34 +00001337
cristy4e0b82a2011-09-29 12:47:44 +00001338 ssize_t
1339 n;
cristy3ed852e2009-09-05 21:47:34 +00001340
cristy4e0b82a2011-09-29 12:47:44 +00001341 n=0;
cristy10a6c612012-01-29 21:41:05 +00001342 if (GetPixelMask(image,q) != 0)
1343 {
1344 q+=GetPixelChannels(image);
1345 continue;
1346 }
cristy4e0b82a2011-09-29 12:47:44 +00001347 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +00001348 {
cristyabace412011-12-11 15:56:53 +00001349 PixelChannel
1350 channel;
1351
cristy4e0b82a2011-09-29 12:47:44 +00001352 PixelTrait
1353 traits;
1354
1355 ssize_t
cristy3f5d8152011-09-29 13:00:19 +00001356 level,
1357 threshold;
cristy3ed852e2009-09-05 21:47:34 +00001358
cristyabace412011-12-11 15:56:53 +00001359 channel=GetPixelChannelMapChannel(image,i);
1360 traits=GetPixelChannelMapTraits(image,channel);
cristy4e0b82a2011-09-29 12:47:44 +00001361 if ((traits & UpdatePixelTrait) == 0)
1362 continue;
cristy3f5d8152011-09-29 13:00:19 +00001363 if (fabs(levels[n++]) < MagickEpsilon)
1364 continue;
1365 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1366 level=threshold/(map->divisor-1);
1367 threshold-=level*(map->divisor-1);
1368 q[i]=RoundToQuantum((level+(threshold >= map->levels[(x % map->width)+
1369 map->width*(y % map->height)]))*QuantumRange/levels[n]);
cristy4e0b82a2011-09-29 12:47:44 +00001370 n++;
cristy3ed852e2009-09-05 21:47:34 +00001371 }
cristy4e0b82a2011-09-29 12:47:44 +00001372 q+=GetPixelChannels(image);
1373 }
1374 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1375 status=MagickFalse;
1376 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1377 {
1378 MagickBooleanType
1379 proceed;
cristy3ed852e2009-09-05 21:47:34 +00001380
cristyb5d5f722009-11-04 03:03:49 +00001381#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy4e0b82a2011-09-29 12:47:44 +00001382#pragma omp critical (MagickCore_OrderedPosterizeImage)
cristy3ed852e2009-09-05 21:47:34 +00001383#endif
cristy4e0b82a2011-09-29 12:47:44 +00001384 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1385 if (proceed == MagickFalse)
1386 status=MagickFalse;
1387 }
cristy3ed852e2009-09-05 21:47:34 +00001388 }
cristy4e0b82a2011-09-29 12:47:44 +00001389 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00001390 map=DestroyThresholdMap(map);
1391 return(MagickTrue);
1392}
1393
1394/*
1395%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1396% %
1397% %
1398% %
1399% R a n d o m T h r e s h o l d I m a g e %
1400% %
1401% %
1402% %
1403%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1404%
1405% RandomThresholdImage() changes the value of individual pixels based on the
1406% intensity of each pixel compared to a random threshold. The result is a
1407% low-contrast, two color image.
1408%
1409% The format of the RandomThresholdImage method is:
1410%
cristyf4ad9df2011-07-08 16:49:03 +00001411% MagickBooleanType RandomThresholdImage(Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001412% const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001413%
1414% A description of each parameter follows:
1415%
1416% o image: the image.
1417%
cristy3ed852e2009-09-05 21:47:34 +00001418% o thresholds: a geometry string containing low,high thresholds. If the
1419% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1420% is performed instead.
1421%
1422% o exception: return any errors or warnings in this structure.
1423%
1424*/
cristy3ed852e2009-09-05 21:47:34 +00001425MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1426 const char *thresholds,ExceptionInfo *exception)
1427{
cristy3ed852e2009-09-05 21:47:34 +00001428#define ThresholdImageTag "Threshold/Image"
1429
cristyfa112112010-01-04 17:48:07 +00001430 CacheView
1431 *image_view;
1432
cristy3ed852e2009-09-05 21:47:34 +00001433 GeometryInfo
1434 geometry_info;
1435
1436 MagickStatusType
1437 flags;
1438
cristy3ed852e2009-09-05 21:47:34 +00001439 MagickBooleanType
1440 status;
1441
cristy5f959472010-05-27 22:19:46 +00001442 MagickOffsetType
1443 progress;
1444
cristy4c08aed2011-07-01 19:47:50 +00001445 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001446 threshold;
1447
1448 MagickRealType
1449 min_threshold,
1450 max_threshold;
1451
1452 RandomInfo
cristyfa112112010-01-04 17:48:07 +00001453 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00001454
cristy5f959472010-05-27 22:19:46 +00001455 ssize_t
1456 y;
1457
cristy3ed852e2009-09-05 21:47:34 +00001458 assert(image != (Image *) NULL);
1459 assert(image->signature == MagickSignature);
1460 if (image->debug != MagickFalse)
1461 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1462 assert(exception != (ExceptionInfo *) NULL);
1463 assert(exception->signature == MagickSignature);
1464 if (thresholds == (const char *) NULL)
1465 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00001466 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001467 min_threshold=0.0;
1468 max_threshold=(MagickRealType) QuantumRange;
1469 flags=ParseGeometry(thresholds,&geometry_info);
1470 min_threshold=geometry_info.rho;
1471 max_threshold=geometry_info.sigma;
1472 if ((flags & SigmaValue) == 0)
1473 max_threshold=min_threshold;
1474 if (strchr(thresholds,'%') != (char *) NULL)
1475 {
1476 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1477 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1478 }
cristy3ed852e2009-09-05 21:47:34 +00001479 /*
1480 Random threshold image.
1481 */
1482 status=MagickTrue;
1483 progress=0;
cristy574cc262011-08-05 01:23:58 +00001484 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1485 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001486 random_info=AcquireRandomInfoThreadSet();
1487 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001488#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00001489 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001490#endif
cristybb503372010-05-27 20:51:26 +00001491 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001492 {
cristy5c9e6f22010-09-17 17:31:01 +00001493 const int
1494 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001495
cristy4c08aed2011-07-01 19:47:50 +00001496 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001497 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001498
cristy5c9e6f22010-09-17 17:31:01 +00001499 register ssize_t
1500 x;
1501
cristy3ed852e2009-09-05 21:47:34 +00001502 if (status == MagickFalse)
1503 continue;
1504 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001505 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001506 {
1507 status=MagickFalse;
1508 continue;
1509 }
cristybb503372010-05-27 20:51:26 +00001510 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001511 {
cristy5f9f2462011-09-28 23:37:58 +00001512 register ssize_t
1513 i;
1514
cristy10a6c612012-01-29 21:41:05 +00001515 if (GetPixelMask(image,q) != 0)
1516 {
1517 q+=GetPixelChannels(image);
1518 continue;
1519 }
cristy5f9f2462011-09-28 23:37:58 +00001520 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1521 {
1522 MagickRealType
1523 threshold;
1524
cristyabace412011-12-11 15:56:53 +00001525 PixelChannel
1526 channel;
1527
cristy5f9f2462011-09-28 23:37:58 +00001528 PixelTrait
1529 traits;
1530
cristyabace412011-12-11 15:56:53 +00001531 channel=GetPixelChannelMapChannel(image,i);
1532 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +00001533 if ((traits & UpdatePixelTrait) == 0)
1534 continue;
1535 if ((MagickRealType) q[i] < min_threshold)
1536 threshold=min_threshold;
1537 else
1538 if ((MagickRealType) q[i] > max_threshold)
1539 threshold=max_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001540 else
cristy5f9f2462011-09-28 23:37:58 +00001541 threshold=(MagickRealType) (QuantumRange*
1542 GetPseudoRandomValue(random_info[id]));
1543 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
1544 QuantumRange);
1545 }
cristyed231572011-07-14 02:18:59 +00001546 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001547 }
1548 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1549 status=MagickFalse;
1550 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1551 {
1552 MagickBooleanType
1553 proceed;
1554
cristyb5d5f722009-11-04 03:03:49 +00001555#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001556 #pragma omp critical (MagickCore_RandomThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001557#endif
1558 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1559 image->rows);
1560 if (proceed == MagickFalse)
1561 status=MagickFalse;
1562 }
1563 }
1564 image_view=DestroyCacheView(image_view);
1565 random_info=DestroyRandomInfoThreadSet(random_info);
1566 return(status);
1567}
1568
1569/*
1570%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1571% %
1572% %
1573% %
1574% W h i t e T h r e s h o l d I m a g e %
1575% %
1576% %
1577% %
1578%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1579%
1580% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001581% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001582% unchanged.
1583%
1584% The format of the WhiteThresholdImage method is:
1585%
cristyf4ad9df2011-07-08 16:49:03 +00001586% MagickBooleanType WhiteThresholdImage(Image *image,
1587% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001588%
1589% A description of each parameter follows:
1590%
1591% o image: the image.
1592%
cristy3ed852e2009-09-05 21:47:34 +00001593% o threshold: Define the threshold value.
1594%
1595% o exception: return any errors or warnings in this structure.
1596%
1597*/
1598MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +00001599 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001600{
1601#define ThresholdImageTag "Threshold/Image"
1602
cristy5f959472010-05-27 22:19:46 +00001603 CacheView
1604 *image_view;
1605
cristy3ed852e2009-09-05 21:47:34 +00001606 GeometryInfo
1607 geometry_info;
1608
cristy3ed852e2009-09-05 21:47:34 +00001609 MagickBooleanType
1610 status;
1611
cristy5f959472010-05-27 22:19:46 +00001612 MagickOffsetType
1613 progress;
1614
cristy5f9f2462011-09-28 23:37:58 +00001615 MagickRealType
1616 threshold[5];
1617
cristy3ed852e2009-09-05 21:47:34 +00001618 MagickStatusType
1619 flags;
1620
cristy5f9f2462011-09-28 23:37:58 +00001621 register ssize_t
1622 i;
1623
cristy5f959472010-05-27 22:19:46 +00001624 ssize_t
1625 y;
cristy3ed852e2009-09-05 21:47:34 +00001626
1627 assert(image != (Image *) NULL);
1628 assert(image->signature == MagickSignature);
1629 if (image->debug != MagickFalse)
1630 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1631 if (thresholds == (const char *) NULL)
1632 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +00001633 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001634 return(MagickFalse);
1635 flags=ParseGeometry(thresholds,&geometry_info);
cristy5f9f2462011-09-28 23:37:58 +00001636 for (i=0; i < 5; i++)
1637 threshold[i]=geometry_info.rho;
1638 if ((flags & SigmaValue) != 0)
1639 threshold[1]=geometry_info.sigma;
1640 if ((flags & XiValue) != 0)
1641 threshold[2]=geometry_info.xi;
1642 if ((flags & PsiValue) != 0)
1643 threshold[3]=geometry_info.psi;
1644 if ((flags & ChiValue) != 0)
1645 threshold[4]=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +00001646 if ((flags & PercentValue) != 0)
cristy5f9f2462011-09-28 23:37:58 +00001647 for (i=0; i < 5; i++)
1648 threshold[i]*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001649 /*
1650 White threshold image.
1651 */
1652 status=MagickTrue;
1653 progress=0;
1654 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001655#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00001656 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001657#endif
cristybb503372010-05-27 20:51:26 +00001658 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001659 {
cristybb503372010-05-27 20:51:26 +00001660 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001661 x;
1662
cristy4c08aed2011-07-01 19:47:50 +00001663 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001664 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001665
1666 if (status == MagickFalse)
1667 continue;
1668 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001669 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001670 {
1671 status=MagickFalse;
1672 continue;
1673 }
cristybb503372010-05-27 20:51:26 +00001674 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001675 {
cristy5f9f2462011-09-28 23:37:58 +00001676 register ssize_t
1677 i;
1678
1679 ssize_t
1680 n;
1681
cristy10a6c612012-01-29 21:41:05 +00001682 if (GetPixelMask(image,q) != 0)
1683 {
1684 q+=GetPixelChannels(image);
1685 continue;
1686 }
cristyd09f8802012-02-04 16:44:10 +00001687 n=0;
cristy5f9f2462011-09-28 23:37:58 +00001688 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1689 {
cristyabace412011-12-11 15:56:53 +00001690 PixelChannel
1691 channel;
1692
cristy5f9f2462011-09-28 23:37:58 +00001693 PixelTrait
1694 traits;
1695
cristyabace412011-12-11 15:56:53 +00001696 channel=GetPixelChannelMapChannel(image,i);
1697 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +00001698 if ((traits & UpdatePixelTrait) == 0)
1699 continue;
1700 if ((MagickRealType) q[i] > threshold[n++ % 5])
1701 q[i]=QuantumRange;
1702 }
cristyed231572011-07-14 02:18:59 +00001703 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001704 }
1705 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1706 status=MagickFalse;
1707 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1708 {
1709 MagickBooleanType
1710 proceed;
1711
cristyb5d5f722009-11-04 03:03:49 +00001712#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001713 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001714#endif
1715 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1716 image->rows);
1717 if (proceed == MagickFalse)
1718 status=MagickFalse;
1719 }
1720 }
1721 image_view=DestroyCacheView(image_view);
1722 return(status);
1723}