blob: 402957e35f3caddfd0ac8776664289fd42525888 [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/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)
207 #pragma omp parallel for schedule(dynamic,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
cristy30301712011-07-18 15:06:51 +0000263 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
cristy30301712011-07-18 15:06:51 +0000264 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
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;
269 if ((threshold_traits & CopyPixelTrait) != 0)
270 {
cristy0beccfa2011-09-25 20:47:53 +0000271 SetPixelChannel(threshold_image,channel,p[center+i],q);
cristyde5cc632011-07-18 14:47:00 +0000272 continue;
273 }
274 pixels=p;
275 pixel=0.0;
276 for (v=0; v < (ssize_t) height; v++)
cristy3ed852e2009-09-05 21:47:34 +0000277 {
cristyde5cc632011-07-18 14:47:00 +0000278 for (u=0; u < (ssize_t) width; u++)
279 {
280 pixel+=pixels[i];
cristya0312c92011-07-23 21:04:30 +0000281 pixels+=GetPixelChannels(image);
cristyde5cc632011-07-18 14:47:00 +0000282 }
cristya0312c92011-07-23 21:04:30 +0000283 pixels+=image->columns*GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000284 }
cristy5f9f2462011-09-28 23:37:58 +0000285 mean=(MagickRealType) (pixel/number_pixels+bias);
286 SetPixelChannel(threshold_image,channel,(Quantum) ((MagickRealType)
287 p[center+i] <= mean ? 0 : QuantumRange),q);
cristy3ed852e2009-09-05 21:47:34 +0000288 }
cristya0312c92011-07-23 21:04:30 +0000289 p+=GetPixelChannels(image);
290 q+=GetPixelChannels(threshold_image);
cristy3ed852e2009-09-05 21:47:34 +0000291 }
cristyde5cc632011-07-18 14:47:00 +0000292 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000293 status=MagickFalse;
294 if (image->progress_monitor != (MagickProgressMonitor) NULL)
295 {
296 MagickBooleanType
297 proceed;
298
cristyb5d5f722009-11-04 03:03:49 +0000299#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000300 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
301#endif
cristyde5cc632011-07-18 14:47:00 +0000302 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
cristy3ed852e2009-09-05 21:47:34 +0000303 image->rows);
304 if (proceed == MagickFalse)
305 status=MagickFalse;
306 }
307 }
cristyde5cc632011-07-18 14:47:00 +0000308 threshold_image->type=image->type;
cristy3ed852e2009-09-05 21:47:34 +0000309 threshold_view=DestroyCacheView(threshold_view);
310 image_view=DestroyCacheView(image_view);
311 if (status == MagickFalse)
312 threshold_image=DestroyImage(threshold_image);
313 return(threshold_image);
314}
315
316/*
317%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
318% %
319% %
320% %
321% B i l e v e l I m a g e %
322% %
323% %
324% %
325%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326%
327% BilevelImage() changes the value of individual pixels based on the
328% intensity of each pixel channel. The result is a high-contrast image.
329%
330% More precisely each channel value of the image is 'thresholded' so that if
331% it is equal to or less than the given value it is set to zero, while any
332% value greater than that give is set to it maximum or QuantumRange.
333%
334% This function is what is used to implement the "-threshold" operator for
335% the command line API.
336%
337% If the default channel setting is given the image is thresholded using just
338% the gray 'intensity' of the image, rather than the individual channels.
339%
cristyf4ad9df2011-07-08 16:49:03 +0000340% The format of the BilevelImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000341%
342% MagickBooleanType BilevelImage(Image *image,const double threshold)
cristy3ed852e2009-09-05 21:47:34 +0000343%
344% A description of each parameter follows:
345%
346% o image: the image.
347%
cristy3ed852e2009-09-05 21:47:34 +0000348% o threshold: define the threshold values.
349%
cristyf89cb1d2011-07-07 01:24:37 +0000350% Aside: You can get the same results as operator using LevelImages()
cristy3ed852e2009-09-05 21:47:34 +0000351% with the 'threshold' value for both the black_point and the white_point.
352%
353*/
cristye23ec9d2011-08-16 18:15:40 +0000354MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
cristy3ed852e2009-09-05 21:47:34 +0000355{
356#define ThresholdImageTag "Threshold/Image"
357
cristyc4c8d132010-01-07 01:58:38 +0000358 CacheView
359 *image_view;
360
cristy3ed852e2009-09-05 21:47:34 +0000361 ExceptionInfo
362 *exception;
363
cristy3ed852e2009-09-05 21:47:34 +0000364 MagickBooleanType
365 status;
366
cristy5f959472010-05-27 22:19:46 +0000367 MagickOffsetType
368 progress;
369
370 ssize_t
371 y;
372
cristy3ed852e2009-09-05 21:47:34 +0000373 assert(image != (Image *) NULL);
374 assert(image->signature == MagickSignature);
375 if (image->debug != MagickFalse)
376 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy48598922011-08-05 17:45:56 +0000377 exception=(&image->exception);
cristy574cc262011-08-05 01:23:58 +0000378 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000379 return(MagickFalse);
380 /*
381 Bilevel threshold image.
382 */
383 status=MagickTrue;
384 progress=0;
cristy3ed852e2009-09-05 21:47:34 +0000385 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000386#if defined(MAGICKCORE_OPENMP_SUPPORT)
387 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000388#endif
cristybb503372010-05-27 20:51:26 +0000389 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000390 {
cristybb503372010-05-27 20:51:26 +0000391 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000392 x;
393
cristy4c08aed2011-07-01 19:47:50 +0000394 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000395 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000396
397 if (status == MagickFalse)
398 continue;
399 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000400 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000401 {
402 status=MagickFalse;
403 continue;
404 }
cristy805b6a02011-08-09 00:59:35 +0000405 for (x=0; x < (ssize_t) image->columns; x++)
406 {
cristy95111202011-08-09 19:41:42 +0000407 register ssize_t
408 i;
409
410 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
411 {
412 PixelTrait
413 traits;
414
415 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
416 if ((traits & UpdatePixelTrait) != 0)
cristy5f9f2462011-09-28 23:37:58 +0000417 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
418 QuantumRange);
cristy95111202011-08-09 19:41:42 +0000419 }
cristy805b6a02011-08-09 00:59:35 +0000420 q+=GetPixelChannels(image);
421 }
cristy3ed852e2009-09-05 21:47:34 +0000422 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
423 status=MagickFalse;
424 if (image->progress_monitor != (MagickProgressMonitor) NULL)
425 {
426 MagickBooleanType
427 proceed;
428
cristyb5d5f722009-11-04 03:03:49 +0000429#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000430 #pragma omp critical (MagickCore_BilevelImage)
cristy3ed852e2009-09-05 21:47:34 +0000431#endif
432 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
433 image->rows);
434 if (proceed == MagickFalse)
435 status=MagickFalse;
436 }
437 }
438 image_view=DestroyCacheView(image_view);
439 return(status);
440}
441
442/*
443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
444% %
445% %
446% %
447% B l a c k T h r e s h o l d I m a g e %
448% %
449% %
450% %
451%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
452%
453% BlackThresholdImage() is like ThresholdImage() but forces all pixels below
cristy4e101302009-09-17 12:49:12 +0000454% the threshold into black while leaving all pixels at or above the threshold
cristy3ed852e2009-09-05 21:47:34 +0000455% unchanged.
456%
457% The format of the BlackThresholdImage method is:
458%
cristyf4ad9df2011-07-08 16:49:03 +0000459% MagickBooleanType BlackThresholdImage(Image *image,
460% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000461%
462% A description of each parameter follows:
463%
464% o image: the image.
465%
cristy5f9f2462011-09-28 23:37:58 +0000466% o threshold: define the threshold value.
cristy3ed852e2009-09-05 21:47:34 +0000467%
468% o exception: return any errors or warnings in this structure.
469%
470*/
471MagickExport MagickBooleanType BlackThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +0000472 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000473{
474#define ThresholdImageTag "Threshold/Image"
475
cristyc4c8d132010-01-07 01:58:38 +0000476 CacheView
477 *image_view;
478
cristy3ed852e2009-09-05 21:47:34 +0000479 GeometryInfo
480 geometry_info;
481
cristy3ed852e2009-09-05 21:47:34 +0000482 MagickBooleanType
483 status;
484
cristy5f959472010-05-27 22:19:46 +0000485 MagickOffsetType
486 progress;
487
cristy5f9f2462011-09-28 23:37:58 +0000488 MagickRealType
489 threshold[5];
cristy3ed852e2009-09-05 21:47:34 +0000490
491 MagickStatusType
492 flags;
493
cristy5f9f2462011-09-28 23:37:58 +0000494 register ssize_t
495 i;
496
cristy5f959472010-05-27 22:19:46 +0000497 ssize_t
498 y;
499
cristy3ed852e2009-09-05 21:47:34 +0000500 assert(image != (Image *) NULL);
501 assert(image->signature == MagickSignature);
502 if (image->debug != MagickFalse)
503 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
504 if (thresholds == (const char *) NULL)
505 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +0000506 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000507 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000508 flags=ParseGeometry(thresholds,&geometry_info);
cristy5f9f2462011-09-28 23:37:58 +0000509 for (i=0; i < 5; i++)
510 threshold[i]=geometry_info.rho;
511 if ((flags & SigmaValue) != 0)
512 threshold[1]=geometry_info.sigma;
513 if ((flags & XiValue) != 0)
514 threshold[2]=geometry_info.xi;
515 if ((flags & PsiValue) != 0)
516 threshold[3]=geometry_info.psi;
517 if ((flags & ChiValue) != 0)
518 threshold[4]=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +0000519 if ((flags & PercentValue) != 0)
cristy5f9f2462011-09-28 23:37:58 +0000520 for (i=0; i < 5; i++)
521 threshold[i]*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +0000522 /*
cristy5f9f2462011-09-28 23:37:58 +0000523 White threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000524 */
525 status=MagickTrue;
526 progress=0;
527 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000528#if defined(MAGICKCORE_OPENMP_SUPPORT)
529 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000530#endif
cristybb503372010-05-27 20:51:26 +0000531 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000532 {
cristybb503372010-05-27 20:51:26 +0000533 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000534 x;
535
cristy4c08aed2011-07-01 19:47:50 +0000536 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000537 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000538
539 if (status == MagickFalse)
540 continue;
541 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000542 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000543 {
544 status=MagickFalse;
545 continue;
546 }
cristybb503372010-05-27 20:51:26 +0000547 for (x=0; x < (ssize_t) image->columns; x++)
cristyb0ea1af2009-11-28 20:44:46 +0000548 {
cristy5f9f2462011-09-28 23:37:58 +0000549 register ssize_t
550 i;
551
552 ssize_t
553 n;
554
555 n=0;
556 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
557 {
558 PixelTrait
559 traits;
560
561 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
562 if ((traits & UpdatePixelTrait) == 0)
563 continue;
564 if ((MagickRealType) q[i] < threshold[n++ % 5])
565 q[i]=QuantumRange;
566 }
cristyed231572011-07-14 02:18:59 +0000567 q+=GetPixelChannels(image);
cristyb0ea1af2009-11-28 20:44:46 +0000568 }
cristy3ed852e2009-09-05 21:47:34 +0000569 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
570 status=MagickFalse;
571 if (image->progress_monitor != (MagickProgressMonitor) NULL)
572 {
573 MagickBooleanType
574 proceed;
575
cristyb5d5f722009-11-04 03:03:49 +0000576#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy5f9f2462011-09-28 23:37:58 +0000577 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +0000578#endif
579 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
580 image->rows);
581 if (proceed == MagickFalse)
582 status=MagickFalse;
583 }
584 }
585 image_view=DestroyCacheView(image_view);
586 return(status);
587}
588
589/*
590%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
591% %
592% %
593% %
cristy1eb45dd2009-09-25 16:38:06 +0000594% C l a m p I m a g e %
595% %
596% %
597% %
598%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
599%
cristyecb0c6d2009-09-25 16:50:09 +0000600% ClampImage() restricts the color range from 0 to the quantum depth.
cristy1eb45dd2009-09-25 16:38:06 +0000601%
cristyf4ad9df2011-07-08 16:49:03 +0000602% The format of the ClampImage method is:
cristy1eb45dd2009-09-25 16:38:06 +0000603%
604% MagickBooleanType ClampImage(Image *image)
cristy1eb45dd2009-09-25 16:38:06 +0000605%
606% A description of each parameter follows:
607%
608% o image: the image.
609%
cristy1eb45dd2009-09-25 16:38:06 +0000610*/
611
cristy75ffdb72010-01-07 17:40:12 +0000612static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
cristy1eb45dd2009-09-25 16:38:06 +0000613{
614#if defined(MAGICKCORE_HDRI_SUPPORT)
615 if (quantum <= 0)
616 return(0);
617 if (quantum >= QuantumRange)
618 return(QuantumRange);
619 return(quantum);
620#else
621 return(quantum);
622#endif
623}
624
625MagickExport MagickBooleanType ClampImage(Image *image)
626{
cristy1eb45dd2009-09-25 16:38:06 +0000627#define ClampImageTag "Clamp/Image"
628
cristyc4c8d132010-01-07 01:58:38 +0000629 CacheView
630 *image_view;
631
cristy1eb45dd2009-09-25 16:38:06 +0000632 ExceptionInfo
633 *exception;
634
cristy1eb45dd2009-09-25 16:38:06 +0000635 MagickBooleanType
636 status;
637
cristy5f959472010-05-27 22:19:46 +0000638 MagickOffsetType
639 progress;
640
641 ssize_t
642 y;
643
cristy1eb45dd2009-09-25 16:38:06 +0000644 assert(image != (Image *) NULL);
645 assert(image->signature == MagickSignature);
646 if (image->debug != MagickFalse)
647 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
648 if (image->storage_class == PseudoClass)
649 {
cristybb503372010-05-27 20:51:26 +0000650 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000651 i;
652
653 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000654 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000655
656 q=image->colormap;
cristybb503372010-05-27 20:51:26 +0000657 for (i=0; i < (ssize_t) image->colors; i++)
cristy1eb45dd2009-09-25 16:38:06 +0000658 {
cristy4c08aed2011-07-01 19:47:50 +0000659 q->red=ClampToUnsignedQuantum(q->red);
660 q->green=ClampToUnsignedQuantum(q->green);
661 q->blue=ClampToUnsignedQuantum(q->blue);
662 q->alpha=ClampToUnsignedQuantum(q->alpha);
cristy1eb45dd2009-09-25 16:38:06 +0000663 q++;
664 }
665 return(SyncImage(image));
666 }
667 /*
cristy611721d2009-09-25 16:42:17 +0000668 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000669 */
670 status=MagickTrue;
671 progress=0;
672 exception=(&image->exception);
673 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000674#if defined(MAGICKCORE_OPENMP_SUPPORT)
675 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy1eb45dd2009-09-25 16:38:06 +0000676#endif
cristybb503372010-05-27 20:51:26 +0000677 for (y=0; y < (ssize_t) image->rows; y++)
cristy1eb45dd2009-09-25 16:38:06 +0000678 {
cristybb503372010-05-27 20:51:26 +0000679 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000680 x;
681
cristy4c08aed2011-07-01 19:47:50 +0000682 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000683 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000684
685 if (status == MagickFalse)
686 continue;
687 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000688 if (q == (Quantum *) NULL)
cristy1eb45dd2009-09-25 16:38:06 +0000689 {
690 status=MagickFalse;
691 continue;
692 }
cristybb503372010-05-27 20:51:26 +0000693 for (x=0; x < (ssize_t) image->columns; x++)
cristy1eb45dd2009-09-25 16:38:06 +0000694 {
cristy5f9f2462011-09-28 23:37:58 +0000695 register ssize_t
696 i;
697
698 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
699 {
700 PixelTrait
701 traits;
702
703 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
704 if (traits == UndefinedPixelTrait)
705 continue;
706 q[i]=ClampToUnsignedQuantum(q[i]);
707 }
cristyed231572011-07-14 02:18:59 +0000708 q+=GetPixelChannels(image);
cristy1eb45dd2009-09-25 16:38:06 +0000709 }
710 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
711 status=MagickFalse;
712 if (image->progress_monitor != (MagickProgressMonitor) NULL)
713 {
714 MagickBooleanType
715 proceed;
716
cristyb5d5f722009-11-04 03:03:49 +0000717#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000718 #pragma omp critical (MagickCore_ClampImage)
cristy1eb45dd2009-09-25 16:38:06 +0000719#endif
720 proceed=SetImageProgress(image,ClampImageTag,progress++,
721 image->rows);
722 if (proceed == MagickFalse)
723 status=MagickFalse;
724 }
725 }
726 image_view=DestroyCacheView(image_view);
727 return(status);
728}
729
730/*
731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
732% %
733% %
734% %
cristy3ed852e2009-09-05 21:47:34 +0000735% D e s t r o y T h r e s h o l d M a p %
736% %
737% %
738% %
739%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
740%
741% DestroyThresholdMap() de-allocate the given ThresholdMap
742%
743% The format of the ListThresholdMaps method is:
744%
745% ThresholdMap *DestroyThresholdMap(Threshold *map)
746%
747% A description of each parameter follows.
748%
749% o map: Pointer to the Threshold map to destroy
750%
751*/
752MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
753{
754 assert(map != (ThresholdMap *) NULL);
755 if (map->map_id != (char *) NULL)
756 map->map_id=DestroyString(map->map_id);
757 if (map->description != (char *) NULL)
758 map->description=DestroyString(map->description);
cristybb503372010-05-27 20:51:26 +0000759 if (map->levels != (ssize_t *) NULL)
760 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
cristy3ed852e2009-09-05 21:47:34 +0000761 map=(ThresholdMap *) RelinquishMagickMemory(map);
762 return(map);
763}
764
765/*
766%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
767% %
768% %
769% %
cristyb9eb87b2011-09-29 01:15:19 +0000770% G e t T h r e s h o l d M a p %
771% %
772% %
773% %
774%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
775%
776% GetThresholdMap() loads and searches one or more threshold map files for the
777% map matching the given name or alias.
778%
779% The format of the GetThresholdMap method is:
780%
781% ThresholdMap *GetThresholdMap(const char *map_id,
782% ExceptionInfo *exception)
783%
784% A description of each parameter follows.
785%
786% o map_id: ID of the map to look for.
787%
788% o exception: return any errors or warnings in this structure.
789%
790*/
791MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
792 ExceptionInfo *exception)
793{
794 const StringInfo
795 *option;
796
797 LinkedListInfo
798 *options;
799
800 ThresholdMap
801 *map;
802
803 map=(ThresholdMap *)NULL;
804 options=GetConfigureOptions(ThresholdsFilename,exception);
805 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
806 (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
807 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
808 GetStringInfoPath(option),map_id,exception);
809 options=DestroyConfigureOptions(options);
810 return(map);
811}
812
813/*
814%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
815% %
816% %
817% %
cristy3ed852e2009-09-05 21:47:34 +0000818+ G e t T h r e s h o l d M a p F i l e %
819% %
820% %
821% %
822%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
823%
824% GetThresholdMapFile() look for a given threshold map name or alias in the
825% given XML file data, and return the allocated the map when found.
826%
827% The format of the ListThresholdMaps method is:
828%
829% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
830% const char *map_id,ExceptionInfo *exception)
831%
832% A description of each parameter follows.
833%
834% o xml: The threshold map list in XML format.
835%
836% o filename: The threshold map XML filename.
837%
838% o map_id: ID of the map to look for in XML list.
839%
840% o exception: return any errors or warnings in this structure.
841%
842*/
cristybd0ebf02011-09-29 01:19:42 +0000843static ThresholdMap *GetThresholdMapFile(const char *xml,
cristy3ed852e2009-09-05 21:47:34 +0000844 const char *filename,const char *map_id,ExceptionInfo *exception)
845{
cristyb9eb87b2011-09-29 01:15:19 +0000846 char
847 *p;
848
cristy3ed852e2009-09-05 21:47:34 +0000849 const char
cristyb9eb87b2011-09-29 01:15:19 +0000850 *attribute,
cristy3ed852e2009-09-05 21:47:34 +0000851 *content;
852
853 double
854 value;
855
cristyb9eb87b2011-09-29 01:15:19 +0000856 register ssize_t
857 i;
cristy3ed852e2009-09-05 21:47:34 +0000858
859 ThresholdMap
860 *map;
861
cristyb9eb87b2011-09-29 01:15:19 +0000862 XMLTreeInfo
863 *description,
864 *levels,
865 *threshold,
866 *thresholds;
867
868 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
869 "Loading threshold map file \"%s\" ...",filename);
870 map=(ThresholdMap *) NULL;
871 thresholds=NewXMLTree(xml,exception);
872 if (thresholds == (XMLTreeInfo *) NULL)
873 return(map);
874 for (threshold=GetXMLTreeChild(thresholds,"threshold");
875 threshold != (XMLTreeInfo *) NULL;
876 threshold=GetNextXMLTreeTag(threshold))
877 {
878 attribute=GetXMLTreeAttribute(threshold,"map");
879 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
880 break;
881 attribute=GetXMLTreeAttribute(threshold,"alias");
882 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
883 break;
884 }
885 if (threshold == (XMLTreeInfo *) NULL)
886 return(map);
887 description=GetXMLTreeChild(threshold,"description");
888 if (description == (XMLTreeInfo *) NULL)
889 {
890 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
891 "XmlMissingElement", "<description>, map \"%s\"",map_id);
892 thresholds=DestroyXMLTree(thresholds);
893 return(map);
894 }
895 levels=GetXMLTreeChild(threshold,"levels");
896 if (levels == (XMLTreeInfo *) NULL)
897 {
898 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
899 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
900 thresholds=DestroyXMLTree(thresholds);
901 return(map);
902 }
903 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
904 if (map == (ThresholdMap *) NULL)
905 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
906 map->map_id=(char *) NULL;
907 map->description=(char *) NULL;
908 map->levels=(ssize_t *) NULL;
909 attribute=GetXMLTreeAttribute(threshold,"map");
910 if (attribute != (char *) NULL)
911 map->map_id=ConstantString(attribute);
912 content=GetXMLTreeContent(description);
913 if (content != (char *) NULL)
914 map->description=ConstantString(content);
915 attribute=GetXMLTreeAttribute(levels,"width");
916 if (attribute == (char *) NULL)
917 {
918 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
919 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
920 thresholds=DestroyXMLTree(thresholds);
921 map=DestroyThresholdMap(map);
922 return(map);
923 }
924 map->width=StringToUnsignedLong(attribute);
925 if (map->width == 0)
926 {
927 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
928 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
929 thresholds=DestroyXMLTree(thresholds);
930 map=DestroyThresholdMap(map);
931 return(map);
932 }
933 attribute=GetXMLTreeAttribute(levels,"height");
934 if (attribute == (char *) NULL)
935 {
936 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
937 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
938 thresholds=DestroyXMLTree(thresholds);
939 map=DestroyThresholdMap(map);
940 return(map);
941 }
942 map->height=StringToUnsignedLong(attribute);
943 if (map->height == 0)
944 {
945 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
946 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
947 thresholds=DestroyXMLTree(thresholds);
948 map=DestroyThresholdMap(map);
949 return(map);
950 }
951 attribute=GetXMLTreeAttribute(levels,"divisor");
952 if (attribute == (char *) NULL)
953 {
954 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
955 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
956 thresholds=DestroyXMLTree(thresholds);
957 map=DestroyThresholdMap(map);
958 return(map);
959 }
960 map->divisor=(ssize_t) StringToLong(attribute);
961 if (map->divisor < 2)
962 {
963 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
964 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
965 thresholds=DestroyXMLTree(thresholds);
966 map=DestroyThresholdMap(map);
967 return(map);
968 }
969 content=GetXMLTreeContent(levels);
970 if (content == (char *) NULL)
971 {
972 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
973 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
974 thresholds=DestroyXMLTree(thresholds);
975 map=DestroyThresholdMap(map);
976 return(map);
977 }
978 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
979 sizeof(*map->levels));
980 if (map->levels == (ssize_t *) NULL)
981 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
982 for (i=0; i < (ssize_t) (map->width*map->height); i++)
983 {
984 map->levels[i]=(ssize_t) strtol(content,&p,10);
985 if (p == content)
986 {
987 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
988 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
989 thresholds=DestroyXMLTree(thresholds);
990 map=DestroyThresholdMap(map);
991 return(map);
992 }
993 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
994 {
995 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
996 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
997 (double) map->levels[i],map_id);
998 thresholds=DestroyXMLTree(thresholds);
999 map=DestroyThresholdMap(map);
1000 return(map);
1001 }
1002 content=p;
1003 }
1004 value=(double) strtol(content,&p,10);
1005 (void) value;
1006 if (p != content)
1007 {
1008 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1009 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1010 thresholds=DestroyXMLTree(thresholds);
1011 map=DestroyThresholdMap(map);
1012 return(map);
1013 }
1014 thresholds=DestroyXMLTree(thresholds);
cristy3ed852e2009-09-05 21:47:34 +00001015 return(map);
1016}
1017
1018/*
1019%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1020% %
1021% %
1022% %
1023+ L i s t T h r e s h o l d M a p F i l e %
1024% %
1025% %
1026% %
1027%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1028%
1029% ListThresholdMapFile() lists the threshold maps and their descriptions
1030% in the given XML file data.
1031%
1032% The format of the ListThresholdMaps method is:
1033%
1034% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1035% const char *filename,ExceptionInfo *exception)
1036%
1037% A description of each parameter follows.
1038%
1039% o file: An pointer to the output FILE.
1040%
1041% o xml: The threshold map list in XML format.
1042%
1043% o filename: The threshold map XML filename.
1044%
1045% o exception: return any errors or warnings in this structure.
1046%
1047*/
1048MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1049 const char *filename,ExceptionInfo *exception)
1050{
cristy5f9f2462011-09-28 23:37:58 +00001051 const char
1052 *alias,
1053 *content,
1054 *map;
1055
1056 XMLTreeInfo
1057 *description,
1058 *threshold,
1059 *thresholds;
cristy3ed852e2009-09-05 21:47:34 +00001060
1061 assert( xml != (char *)NULL );
1062 assert( file != (FILE *)NULL );
cristy3ed852e2009-09-05 21:47:34 +00001063 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1064 "Loading threshold map file \"%s\" ...",filename);
1065 thresholds=NewXMLTree(xml,exception);
1066 if ( thresholds == (XMLTreeInfo *)NULL )
1067 return(MagickFalse);
cristy1e604812011-05-19 18:07:50 +00001068 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1069 (void) FormatLocaleFile(file,
1070 "----------------------------------------------------\n");
cristy5f9f2462011-09-28 23:37:58 +00001071 threshold=GetXMLTreeChild(thresholds,"threshold");
cristyb9eb87b2011-09-29 01:15:19 +00001072 for ( ; threshold != (XMLTreeInfo *) NULL;
1073 threshold=GetNextXMLTreeTag(threshold))
cristy3ed852e2009-09-05 21:47:34 +00001074 {
cristy5f9f2462011-09-28 23:37:58 +00001075 map=GetXMLTreeAttribute(threshold,"map");
1076 if (map == (char *) NULL)
1077 {
1078 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1079 "XmlMissingAttribute", "<map>");
1080 thresholds=DestroyXMLTree(thresholds);
1081 return(MagickFalse);
1082 }
1083 alias=GetXMLTreeAttribute(threshold,"alias");
cristy3ed852e2009-09-05 21:47:34 +00001084 description=GetXMLTreeChild(threshold,"description");
cristy5f9f2462011-09-28 23:37:58 +00001085 if (description == (XMLTreeInfo *) NULL)
1086 {
1087 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyb9eb87b2011-09-29 01:15:19 +00001088 "XmlMissingElement", "<description>, map \"%s\"",map);
cristy5f9f2462011-09-28 23:37:58 +00001089 thresholds=DestroyXMLTree(thresholds);
1090 return(MagickFalse);
1091 }
cristy3ed852e2009-09-05 21:47:34 +00001092 content=GetXMLTreeContent(description);
cristy5f9f2462011-09-28 23:37:58 +00001093 if (content == (char *) NULL)
1094 {
1095 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1096 "XmlMissingContent", "<description>, map \"%s\"", map);
1097 thresholds=DestroyXMLTree(thresholds);
1098 return(MagickFalse);
1099 }
cristy1e604812011-05-19 18:07:50 +00001100 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1101 content);
cristy3ed852e2009-09-05 21:47:34 +00001102 }
1103 thresholds=DestroyXMLTree(thresholds);
1104 return(MagickTrue);
1105}
1106
1107/*
1108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1109% %
1110% %
1111% %
1112% L i s t T h r e s h o l d M a p s %
1113% %
1114% %
1115% %
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117%
1118% ListThresholdMaps() lists the threshold maps and their descriptions
1119% as defined by "threshold.xml" to a file.
1120%
1121% The format of the ListThresholdMaps method is:
1122%
1123% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1124%
1125% A description of each parameter follows.
1126%
1127% o file: An pointer to the output FILE.
1128%
1129% o exception: return any errors or warnings in this structure.
1130%
1131*/
1132MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1133 ExceptionInfo *exception)
1134{
1135 const StringInfo
1136 *option;
1137
1138 LinkedListInfo
1139 *options;
1140
1141 MagickStatusType
1142 status;
1143
1144 status=MagickFalse;
cristy5f9f2462011-09-28 23:37:58 +00001145 if (file == (FILE *) NULL)
1146 file=stdout;
cristy3ed852e2009-09-05 21:47:34 +00001147 options=GetConfigureOptions(ThresholdsFilename,exception);
cristy1e604812011-05-19 18:07:50 +00001148 (void) FormatLocaleFile(file,
1149 "\n Threshold Maps for Ordered Dither Operations\n");
cristy5f9f2462011-09-28 23:37:58 +00001150 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1151 (const StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001152 {
cristyb51dff52011-05-19 16:55:47 +00001153 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
cristy3ed852e2009-09-05 21:47:34 +00001154 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1155 GetStringInfoPath(option),exception);
1156 }
1157 options=DestroyConfigureOptions(options);
1158 return(status != 0 ? MagickTrue : MagickFalse);
1159}
1160
1161/*
1162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1163% %
1164% %
1165% %
cristy3ed852e2009-09-05 21:47:34 +00001166% O r d e r e d P o s t e r i z e I m a g e %
1167% %
1168% %
1169% %
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171%
1172% OrderedPosterizeImage() will perform a ordered dither based on a number
1173% of pre-defined dithering threshold maps, but over multiple intensity
1174% levels, which can be different for different channels, according to the
1175% input argument.
1176%
1177% The format of the OrderedPosterizeImage method is:
1178%
1179% MagickBooleanType OrderedPosterizeImage(Image *image,
1180% const char *threshold_map,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001181%
1182% A description of each parameter follows:
1183%
1184% o image: the image.
1185%
cristy3ed852e2009-09-05 21:47:34 +00001186% o threshold_map: A string containing the name of the threshold dither
1187% map to use, followed by zero or more numbers representing the number
1188% of color levels tho dither between.
1189%
cristyf998fb32011-04-27 23:00:47 +00001190% Any level number less than 2 will be equivalent to 2, and means only
cristy3ed852e2009-09-05 21:47:34 +00001191% binary dithering will be applied to each color channel.
1192%
1193% No numbers also means a 2 level (bitmap) dither will be applied to all
1194% channels, while a single number is the number of levels applied to each
1195% channel in sequence. More numbers will be applied in turn to each of
1196% the color channels.
1197%
1198% For example: "o3x3,6" will generate a 6 level posterization of the
1199% image with a ordered 3x3 diffused pixel dither being applied between
1200% each level. While checker,8,8,4 will produce a 332 colormaped image
1201% with only a single checkerboard hash pattern (50% grey) between each
1202% color level, to basically double the number of color levels with
1203% a bare minimim of dithering.
1204%
1205% o exception: return any errors or warnings in this structure.
1206%
1207*/
1208MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1209 const char *threshold_map,ExceptionInfo *exception)
1210{
cristy3ed852e2009-09-05 21:47:34 +00001211#define DitherImageTag "Dither/Image"
1212
cristyc4c8d132010-01-07 01:58:38 +00001213 CacheView
1214 *image_view;
1215
cristy4e0b82a2011-09-29 12:47:44 +00001216 char
1217 token[MaxTextExtent];
1218
1219 const char
1220 *p;
cristy3ed852e2009-09-05 21:47:34 +00001221
1222 MagickBooleanType
1223 status;
1224
cristy5f959472010-05-27 22:19:46 +00001225 MagickOffsetType
1226 progress;
1227
cristy4e0b82a2011-09-29 12:47:44 +00001228 MagickRealType
1229 levels[MaxPixelChannels];
1230
1231 register ssize_t
1232 i;
1233
cristy5f959472010-05-27 22:19:46 +00001234 ssize_t
1235 y;
1236
cristy3ed852e2009-09-05 21:47:34 +00001237 ThresholdMap
1238 *map;
1239
cristy3ed852e2009-09-05 21:47:34 +00001240 assert(image != (Image *) NULL);
1241 assert(image->signature == MagickSignature);
1242 if (image->debug != MagickFalse)
1243 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1244 assert(exception != (ExceptionInfo *) NULL);
1245 assert(exception->signature == MagickSignature);
1246 if (threshold_map == (const char *) NULL)
1247 return(MagickTrue);
cristy4e0b82a2011-09-29 12:47:44 +00001248 p=(char *) threshold_map;
1249 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1250 (*p != '\0'))
1251 p++;
1252 threshold_map=p;
1253 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1254 (*p != '\0'))
cristy3ed852e2009-09-05 21:47:34 +00001255 {
cristy4e0b82a2011-09-29 12:47:44 +00001256 if ((p-threshold_map) >= (MaxTextExtent-1))
1257 break;
1258 token[p-threshold_map]=(*p);
1259 p++;
cristy3ed852e2009-09-05 21:47:34 +00001260 }
cristy4e0b82a2011-09-29 12:47:44 +00001261 token[p-threshold_map]='\0';
1262 map=GetThresholdMap(token,exception);
1263 if (map == (ThresholdMap *) NULL)
1264 {
1265 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1266 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
cristy574cc262011-08-05 01:23:58 +00001267 return(MagickFalse);
cristy4e0b82a2011-09-29 12:47:44 +00001268 }
1269 for (i=0; i < MaxPixelChannels; i++)
1270 levels[i]=2.0;
1271 p=strchr((char *) threshold_map,',');
1272 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1273 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1274 {
1275 GetMagickToken(p,&p,token);
1276 if (*token == ',')
1277 GetMagickToken(p,&p,token);
1278 levels[i]=InterpretLocaleValue(token,(char **) NULL);
1279 }
1280 for (i=0; i < MaxPixelChannels; i++)
1281 if (fabs(levels[i]) >= 1)
1282 levels[i]-=1.0;
1283 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1284 return(MagickFalse);
1285 status=MagickTrue;
1286 progress=0;
1287 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001288#if defined(MAGICKCORE_OPENMP_SUPPORT)
1289 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001290#endif
cristy4e0b82a2011-09-29 12:47:44 +00001291 for (y=0; y < (ssize_t) image->rows; y++)
1292 {
1293 register ssize_t
1294 x;
1295
1296 register Quantum
1297 *restrict q;
1298
1299 if (status == MagickFalse)
1300 continue;
1301 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1302 if (q == (Quantum *) NULL)
1303 {
1304 status=MagickFalse;
1305 continue;
1306 }
1307 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001308 {
cristybb503372010-05-27 20:51:26 +00001309 register ssize_t
cristy4e0b82a2011-09-29 12:47:44 +00001310 i;
cristy3ed852e2009-09-05 21:47:34 +00001311
cristy4e0b82a2011-09-29 12:47:44 +00001312 ssize_t
1313 n;
cristy3ed852e2009-09-05 21:47:34 +00001314
cristy4e0b82a2011-09-29 12:47:44 +00001315 n=0;
1316 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +00001317 {
cristy4e0b82a2011-09-29 12:47:44 +00001318 PixelTrait
1319 traits;
1320
1321 ssize_t
cristy3f5d8152011-09-29 13:00:19 +00001322 level,
1323 threshold;
cristy3ed852e2009-09-05 21:47:34 +00001324
cristy4e0b82a2011-09-29 12:47:44 +00001325 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1326 if ((traits & UpdatePixelTrait) == 0)
1327 continue;
cristy3f5d8152011-09-29 13:00:19 +00001328 if (fabs(levels[n++]) < MagickEpsilon)
1329 continue;
1330 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1331 level=threshold/(map->divisor-1);
1332 threshold-=level*(map->divisor-1);
1333 q[i]=RoundToQuantum((level+(threshold >= map->levels[(x % map->width)+
1334 map->width*(y % map->height)]))*QuantumRange/levels[n]);
cristy4e0b82a2011-09-29 12:47:44 +00001335 n++;
cristy3ed852e2009-09-05 21:47:34 +00001336 }
cristy4e0b82a2011-09-29 12:47:44 +00001337 q+=GetPixelChannels(image);
1338 }
1339 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1340 status=MagickFalse;
1341 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1342 {
1343 MagickBooleanType
1344 proceed;
cristy3ed852e2009-09-05 21:47:34 +00001345
cristyb5d5f722009-11-04 03:03:49 +00001346#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy4e0b82a2011-09-29 12:47:44 +00001347#pragma omp critical (MagickCore_OrderedPosterizeImage)
cristy3ed852e2009-09-05 21:47:34 +00001348#endif
cristy4e0b82a2011-09-29 12:47:44 +00001349 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1350 if (proceed == MagickFalse)
1351 status=MagickFalse;
1352 }
cristy3ed852e2009-09-05 21:47:34 +00001353 }
cristy4e0b82a2011-09-29 12:47:44 +00001354 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00001355 map=DestroyThresholdMap(map);
1356 return(MagickTrue);
1357}
1358
1359/*
1360%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1361% %
1362% %
1363% %
1364% R a n d o m T h r e s h o l d I m a g e %
1365% %
1366% %
1367% %
1368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1369%
1370% RandomThresholdImage() changes the value of individual pixels based on the
1371% intensity of each pixel compared to a random threshold. The result is a
1372% low-contrast, two color image.
1373%
1374% The format of the RandomThresholdImage method is:
1375%
cristyf4ad9df2011-07-08 16:49:03 +00001376% MagickBooleanType RandomThresholdImage(Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001377% const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001378%
1379% A description of each parameter follows:
1380%
1381% o image: the image.
1382%
cristy3ed852e2009-09-05 21:47:34 +00001383% o thresholds: a geometry string containing low,high thresholds. If the
1384% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1385% is performed instead.
1386%
1387% o exception: return any errors or warnings in this structure.
1388%
1389*/
cristy3ed852e2009-09-05 21:47:34 +00001390MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1391 const char *thresholds,ExceptionInfo *exception)
1392{
cristy3ed852e2009-09-05 21:47:34 +00001393#define ThresholdImageTag "Threshold/Image"
1394
cristyfa112112010-01-04 17:48:07 +00001395 CacheView
1396 *image_view;
1397
cristy3ed852e2009-09-05 21:47:34 +00001398 GeometryInfo
1399 geometry_info;
1400
1401 MagickStatusType
1402 flags;
1403
cristy3ed852e2009-09-05 21:47:34 +00001404 MagickBooleanType
1405 status;
1406
cristy5f959472010-05-27 22:19:46 +00001407 MagickOffsetType
1408 progress;
1409
cristy4c08aed2011-07-01 19:47:50 +00001410 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001411 threshold;
1412
1413 MagickRealType
1414 min_threshold,
1415 max_threshold;
1416
1417 RandomInfo
cristyfa112112010-01-04 17:48:07 +00001418 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00001419
cristy5f959472010-05-27 22:19:46 +00001420 ssize_t
1421 y;
1422
cristy3ed852e2009-09-05 21:47:34 +00001423 assert(image != (Image *) NULL);
1424 assert(image->signature == MagickSignature);
1425 if (image->debug != MagickFalse)
1426 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1427 assert(exception != (ExceptionInfo *) NULL);
1428 assert(exception->signature == MagickSignature);
1429 if (thresholds == (const char *) NULL)
1430 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00001431 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001432 min_threshold=0.0;
1433 max_threshold=(MagickRealType) QuantumRange;
1434 flags=ParseGeometry(thresholds,&geometry_info);
1435 min_threshold=geometry_info.rho;
1436 max_threshold=geometry_info.sigma;
1437 if ((flags & SigmaValue) == 0)
1438 max_threshold=min_threshold;
1439 if (strchr(thresholds,'%') != (char *) NULL)
1440 {
1441 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1442 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1443 }
cristy3ed852e2009-09-05 21:47:34 +00001444 /*
1445 Random threshold image.
1446 */
1447 status=MagickTrue;
1448 progress=0;
cristy574cc262011-08-05 01:23:58 +00001449 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1450 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001451 random_info=AcquireRandomInfoThreadSet();
1452 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001453#if defined(MAGICKCORE_OPENMP_SUPPORT)
1454 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001455#endif
cristybb503372010-05-27 20:51:26 +00001456 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001457 {
cristy5c9e6f22010-09-17 17:31:01 +00001458 const int
1459 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001460
cristy4c08aed2011-07-01 19:47:50 +00001461 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001462 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001463
cristy5c9e6f22010-09-17 17:31:01 +00001464 register ssize_t
1465 x;
1466
cristy3ed852e2009-09-05 21:47:34 +00001467 if (status == MagickFalse)
1468 continue;
1469 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001470 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001471 {
1472 status=MagickFalse;
1473 continue;
1474 }
cristybb503372010-05-27 20:51:26 +00001475 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001476 {
cristy5f9f2462011-09-28 23:37:58 +00001477 register ssize_t
1478 i;
1479
1480 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1481 {
1482 MagickRealType
1483 threshold;
1484
1485 PixelTrait
1486 traits;
1487
cristy5f9f2462011-09-28 23:37:58 +00001488 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1489 if ((traits & UpdatePixelTrait) == 0)
1490 continue;
1491 if ((MagickRealType) q[i] < min_threshold)
1492 threshold=min_threshold;
1493 else
1494 if ((MagickRealType) q[i] > max_threshold)
1495 threshold=max_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001496 else
cristy5f9f2462011-09-28 23:37:58 +00001497 threshold=(MagickRealType) (QuantumRange*
1498 GetPseudoRandomValue(random_info[id]));
1499 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
1500 QuantumRange);
1501 }
cristyed231572011-07-14 02:18:59 +00001502 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001503 }
1504 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1505 status=MagickFalse;
1506 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1507 {
1508 MagickBooleanType
1509 proceed;
1510
cristyb5d5f722009-11-04 03:03:49 +00001511#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001512 #pragma omp critical (MagickCore_RandomThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001513#endif
1514 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1515 image->rows);
1516 if (proceed == MagickFalse)
1517 status=MagickFalse;
1518 }
1519 }
1520 image_view=DestroyCacheView(image_view);
1521 random_info=DestroyRandomInfoThreadSet(random_info);
1522 return(status);
1523}
1524
1525/*
1526%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1527% %
1528% %
1529% %
1530% W h i t e T h r e s h o l d I m a g e %
1531% %
1532% %
1533% %
1534%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1535%
1536% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001537% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001538% unchanged.
1539%
1540% The format of the WhiteThresholdImage method is:
1541%
cristyf4ad9df2011-07-08 16:49:03 +00001542% MagickBooleanType WhiteThresholdImage(Image *image,
1543% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001544%
1545% A description of each parameter follows:
1546%
1547% o image: the image.
1548%
cristy3ed852e2009-09-05 21:47:34 +00001549% o threshold: Define the threshold value.
1550%
1551% o exception: return any errors or warnings in this structure.
1552%
1553*/
1554MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +00001555 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001556{
1557#define ThresholdImageTag "Threshold/Image"
1558
cristy5f959472010-05-27 22:19:46 +00001559 CacheView
1560 *image_view;
1561
cristy3ed852e2009-09-05 21:47:34 +00001562 GeometryInfo
1563 geometry_info;
1564
cristy3ed852e2009-09-05 21:47:34 +00001565 MagickBooleanType
1566 status;
1567
cristy5f959472010-05-27 22:19:46 +00001568 MagickOffsetType
1569 progress;
1570
cristy5f9f2462011-09-28 23:37:58 +00001571 MagickRealType
1572 threshold[5];
1573
cristy3ed852e2009-09-05 21:47:34 +00001574 MagickStatusType
1575 flags;
1576
cristy5f9f2462011-09-28 23:37:58 +00001577 register ssize_t
1578 i;
1579
cristy5f959472010-05-27 22:19:46 +00001580 ssize_t
1581 y;
cristy3ed852e2009-09-05 21:47:34 +00001582
1583 assert(image != (Image *) NULL);
1584 assert(image->signature == MagickSignature);
1585 if (image->debug != MagickFalse)
1586 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1587 if (thresholds == (const char *) NULL)
1588 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +00001589 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001590 return(MagickFalse);
1591 flags=ParseGeometry(thresholds,&geometry_info);
cristy5f9f2462011-09-28 23:37:58 +00001592 for (i=0; i < 5; i++)
1593 threshold[i]=geometry_info.rho;
1594 if ((flags & SigmaValue) != 0)
1595 threshold[1]=geometry_info.sigma;
1596 if ((flags & XiValue) != 0)
1597 threshold[2]=geometry_info.xi;
1598 if ((flags & PsiValue) != 0)
1599 threshold[3]=geometry_info.psi;
1600 if ((flags & ChiValue) != 0)
1601 threshold[4]=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +00001602 if ((flags & PercentValue) != 0)
cristy5f9f2462011-09-28 23:37:58 +00001603 for (i=0; i < 5; i++)
1604 threshold[i]*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001605 /*
1606 White threshold image.
1607 */
1608 status=MagickTrue;
1609 progress=0;
1610 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001611#if defined(MAGICKCORE_OPENMP_SUPPORT)
1612 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001613#endif
cristybb503372010-05-27 20:51:26 +00001614 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001615 {
cristybb503372010-05-27 20:51:26 +00001616 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001617 x;
1618
cristy4c08aed2011-07-01 19:47:50 +00001619 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001620 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001621
1622 if (status == MagickFalse)
1623 continue;
1624 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001625 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001626 {
1627 status=MagickFalse;
1628 continue;
1629 }
cristybb503372010-05-27 20:51:26 +00001630 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001631 {
cristy5f9f2462011-09-28 23:37:58 +00001632 register ssize_t
1633 i;
1634
1635 ssize_t
1636 n;
1637
1638 n=0;
1639 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1640 {
1641 PixelTrait
1642 traits;
1643
1644 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1645 if ((traits & UpdatePixelTrait) == 0)
1646 continue;
1647 if ((MagickRealType) q[i] > threshold[n++ % 5])
1648 q[i]=QuantumRange;
1649 }
cristyed231572011-07-14 02:18:59 +00001650 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001651 }
1652 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1653 status=MagickFalse;
1654 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1655 {
1656 MagickBooleanType
1657 proceed;
1658
cristyb5d5f722009-11-04 03:03:49 +00001659#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001660 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001661#endif
1662 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1663 image->rows);
1664 if (proceed == MagickFalse)
1665 status=MagickFalse;
1666 }
1667 }
1668 image_view=DestroyCacheView(image_view);
1669 return(status);
1670}