blob: e42e3abada6a4f5b6848df75b32c2a00f9d63d57 [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%
cristye941a752011-10-15 01:52:48 +0000342% MagickBooleanType BilevelImage(Image *image,const double threshold,
343% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000344%
345% A description of each parameter follows:
346%
347% o image: the image.
348%
cristy3ed852e2009-09-05 21:47:34 +0000349% o threshold: define the threshold values.
350%
cristye941a752011-10-15 01:52:48 +0000351% o exception: return any errors or warnings in this structure.
352%
cristyf89cb1d2011-07-07 01:24:37 +0000353% Aside: You can get the same results as operator using LevelImages()
cristy3ed852e2009-09-05 21:47:34 +0000354% with the 'threshold' value for both the black_point and the white_point.
355%
356*/
cristye941a752011-10-15 01:52:48 +0000357MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
358 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000359{
360#define ThresholdImageTag "Threshold/Image"
361
cristyc4c8d132010-01-07 01:58:38 +0000362 CacheView
363 *image_view;
364
cristy3ed852e2009-09-05 21:47:34 +0000365 MagickBooleanType
366 status;
367
cristy5f959472010-05-27 22:19:46 +0000368 MagickOffsetType
369 progress;
370
371 ssize_t
372 y;
373
cristy3ed852e2009-09-05 21:47:34 +0000374 assert(image != (Image *) NULL);
375 assert(image->signature == MagickSignature);
376 if (image->debug != MagickFalse)
377 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
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%
cristy092d71c2011-10-14 18:01:29 +0000604% MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000605%
606% A description of each parameter follows:
607%
608% o image: the image.
609%
cristy092d71c2011-10-14 18:01:29 +0000610% o exception: return any errors or warnings in this structure.
611%
cristy1eb45dd2009-09-25 16:38:06 +0000612*/
613
cristy75ffdb72010-01-07 17:40:12 +0000614static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
cristy1eb45dd2009-09-25 16:38:06 +0000615{
616#if defined(MAGICKCORE_HDRI_SUPPORT)
617 if (quantum <= 0)
618 return(0);
619 if (quantum >= QuantumRange)
620 return(QuantumRange);
621 return(quantum);
622#else
623 return(quantum);
624#endif
625}
626
cristy092d71c2011-10-14 18:01:29 +0000627MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000628{
cristy1eb45dd2009-09-25 16:38:06 +0000629#define ClampImageTag "Clamp/Image"
630
cristyc4c8d132010-01-07 01:58:38 +0000631 CacheView
632 *image_view;
633
cristy1eb45dd2009-09-25 16:38:06 +0000634 MagickBooleanType
635 status;
636
cristy5f959472010-05-27 22:19:46 +0000637 MagickOffsetType
638 progress;
639
640 ssize_t
641 y;
642
cristy1eb45dd2009-09-25 16:38:06 +0000643 assert(image != (Image *) NULL);
644 assert(image->signature == MagickSignature);
645 if (image->debug != MagickFalse)
646 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
647 if (image->storage_class == PseudoClass)
648 {
cristybb503372010-05-27 20:51:26 +0000649 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000650 i;
651
cristy101ab702011-10-13 13:06:32 +0000652 register PixelInfo
cristyc47d1f82009-11-26 01:44:43 +0000653 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000654
655 q=image->colormap;
cristybb503372010-05-27 20:51:26 +0000656 for (i=0; i < (ssize_t) image->colors; i++)
cristy1eb45dd2009-09-25 16:38:06 +0000657 {
cristy4c08aed2011-07-01 19:47:50 +0000658 q->red=ClampToUnsignedQuantum(q->red);
659 q->green=ClampToUnsignedQuantum(q->green);
660 q->blue=ClampToUnsignedQuantum(q->blue);
661 q->alpha=ClampToUnsignedQuantum(q->alpha);
cristy1eb45dd2009-09-25 16:38:06 +0000662 q++;
663 }
cristyea1a8aa2011-10-20 13:24:06 +0000664 return(SyncImage(image,exception));
cristy1eb45dd2009-09-25 16:38:06 +0000665 }
666 /*
cristy611721d2009-09-25 16:42:17 +0000667 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000668 */
669 status=MagickTrue;
670 progress=0;
cristy1eb45dd2009-09-25 16:38:06 +0000671 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000672#if defined(MAGICKCORE_OPENMP_SUPPORT)
673 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy1eb45dd2009-09-25 16:38:06 +0000674#endif
cristybb503372010-05-27 20:51:26 +0000675 for (y=0; y < (ssize_t) image->rows; y++)
cristy1eb45dd2009-09-25 16:38:06 +0000676 {
cristybb503372010-05-27 20:51:26 +0000677 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000678 x;
679
cristy4c08aed2011-07-01 19:47:50 +0000680 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000681 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000682
683 if (status == MagickFalse)
684 continue;
685 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000686 if (q == (Quantum *) NULL)
cristy1eb45dd2009-09-25 16:38:06 +0000687 {
688 status=MagickFalse;
689 continue;
690 }
cristybb503372010-05-27 20:51:26 +0000691 for (x=0; x < (ssize_t) image->columns; x++)
cristy1eb45dd2009-09-25 16:38:06 +0000692 {
cristy5f9f2462011-09-28 23:37:58 +0000693 register ssize_t
694 i;
695
696 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
697 {
698 PixelTrait
699 traits;
700
701 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
702 if (traits == UndefinedPixelTrait)
703 continue;
704 q[i]=ClampToUnsignedQuantum(q[i]);
705 }
cristyed231572011-07-14 02:18:59 +0000706 q+=GetPixelChannels(image);
cristy1eb45dd2009-09-25 16:38:06 +0000707 }
708 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
709 status=MagickFalse;
710 if (image->progress_monitor != (MagickProgressMonitor) NULL)
711 {
712 MagickBooleanType
713 proceed;
714
cristyb5d5f722009-11-04 03:03:49 +0000715#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000716 #pragma omp critical (MagickCore_ClampImage)
cristy1eb45dd2009-09-25 16:38:06 +0000717#endif
718 proceed=SetImageProgress(image,ClampImageTag,progress++,
719 image->rows);
720 if (proceed == MagickFalse)
721 status=MagickFalse;
722 }
723 }
724 image_view=DestroyCacheView(image_view);
725 return(status);
726}
727
728/*
729%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
730% %
731% %
732% %
cristy3ed852e2009-09-05 21:47:34 +0000733% D e s t r o y T h r e s h o l d M a p %
734% %
735% %
736% %
737%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
738%
739% DestroyThresholdMap() de-allocate the given ThresholdMap
740%
741% The format of the ListThresholdMaps method is:
742%
743% ThresholdMap *DestroyThresholdMap(Threshold *map)
744%
745% A description of each parameter follows.
746%
747% o map: Pointer to the Threshold map to destroy
748%
749*/
750MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
751{
752 assert(map != (ThresholdMap *) NULL);
753 if (map->map_id != (char *) NULL)
754 map->map_id=DestroyString(map->map_id);
755 if (map->description != (char *) NULL)
756 map->description=DestroyString(map->description);
cristybb503372010-05-27 20:51:26 +0000757 if (map->levels != (ssize_t *) NULL)
758 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
cristy3ed852e2009-09-05 21:47:34 +0000759 map=(ThresholdMap *) RelinquishMagickMemory(map);
760 return(map);
761}
762
763/*
764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
765% %
766% %
767% %
cristyb9eb87b2011-09-29 01:15:19 +0000768% G e t T h r e s h o l d M a p %
769% %
770% %
771% %
772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
773%
774% GetThresholdMap() loads and searches one or more threshold map files for the
775% map matching the given name or alias.
776%
777% The format of the GetThresholdMap method is:
778%
779% ThresholdMap *GetThresholdMap(const char *map_id,
780% ExceptionInfo *exception)
781%
782% A description of each parameter follows.
783%
784% o map_id: ID of the map to look for.
785%
786% o exception: return any errors or warnings in this structure.
787%
788*/
789MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
790 ExceptionInfo *exception)
791{
792 const StringInfo
793 *option;
794
795 LinkedListInfo
796 *options;
797
798 ThresholdMap
799 *map;
800
801 map=(ThresholdMap *)NULL;
802 options=GetConfigureOptions(ThresholdsFilename,exception);
803 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
804 (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
805 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
806 GetStringInfoPath(option),map_id,exception);
807 options=DestroyConfigureOptions(options);
808 return(map);
809}
810
811/*
812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813% %
814% %
815% %
cristy3ed852e2009-09-05 21:47:34 +0000816+ G e t T h r e s h o l d M a p F i l e %
817% %
818% %
819% %
820%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821%
822% GetThresholdMapFile() look for a given threshold map name or alias in the
823% given XML file data, and return the allocated the map when found.
824%
825% The format of the ListThresholdMaps method is:
826%
827% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
828% const char *map_id,ExceptionInfo *exception)
829%
830% A description of each parameter follows.
831%
832% o xml: The threshold map list in XML format.
833%
834% o filename: The threshold map XML filename.
835%
836% o map_id: ID of the map to look for in XML list.
837%
838% o exception: return any errors or warnings in this structure.
839%
840*/
cristybd0ebf02011-09-29 01:19:42 +0000841static ThresholdMap *GetThresholdMapFile(const char *xml,
cristy3ed852e2009-09-05 21:47:34 +0000842 const char *filename,const char *map_id,ExceptionInfo *exception)
843{
cristyb9eb87b2011-09-29 01:15:19 +0000844 char
845 *p;
846
cristy3ed852e2009-09-05 21:47:34 +0000847 const char
cristyb9eb87b2011-09-29 01:15:19 +0000848 *attribute,
cristy3ed852e2009-09-05 21:47:34 +0000849 *content;
850
851 double
852 value;
853
cristyb9eb87b2011-09-29 01:15:19 +0000854 register ssize_t
855 i;
cristy3ed852e2009-09-05 21:47:34 +0000856
857 ThresholdMap
858 *map;
859
cristyb9eb87b2011-09-29 01:15:19 +0000860 XMLTreeInfo
861 *description,
862 *levels,
863 *threshold,
864 *thresholds;
865
866 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
867 "Loading threshold map file \"%s\" ...",filename);
868 map=(ThresholdMap *) NULL;
869 thresholds=NewXMLTree(xml,exception);
870 if (thresholds == (XMLTreeInfo *) NULL)
871 return(map);
872 for (threshold=GetXMLTreeChild(thresholds,"threshold");
873 threshold != (XMLTreeInfo *) NULL;
874 threshold=GetNextXMLTreeTag(threshold))
875 {
876 attribute=GetXMLTreeAttribute(threshold,"map");
877 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
878 break;
879 attribute=GetXMLTreeAttribute(threshold,"alias");
880 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
881 break;
882 }
883 if (threshold == (XMLTreeInfo *) NULL)
884 return(map);
885 description=GetXMLTreeChild(threshold,"description");
886 if (description == (XMLTreeInfo *) NULL)
887 {
888 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
889 "XmlMissingElement", "<description>, map \"%s\"",map_id);
890 thresholds=DestroyXMLTree(thresholds);
891 return(map);
892 }
893 levels=GetXMLTreeChild(threshold,"levels");
894 if (levels == (XMLTreeInfo *) NULL)
895 {
896 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
897 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
898 thresholds=DestroyXMLTree(thresholds);
899 return(map);
900 }
901 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
902 if (map == (ThresholdMap *) NULL)
903 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
904 map->map_id=(char *) NULL;
905 map->description=(char *) NULL;
906 map->levels=(ssize_t *) NULL;
907 attribute=GetXMLTreeAttribute(threshold,"map");
908 if (attribute != (char *) NULL)
909 map->map_id=ConstantString(attribute);
910 content=GetXMLTreeContent(description);
911 if (content != (char *) NULL)
912 map->description=ConstantString(content);
913 attribute=GetXMLTreeAttribute(levels,"width");
914 if (attribute == (char *) NULL)
915 {
916 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
917 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
918 thresholds=DestroyXMLTree(thresholds);
919 map=DestroyThresholdMap(map);
920 return(map);
921 }
922 map->width=StringToUnsignedLong(attribute);
923 if (map->width == 0)
924 {
925 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
926 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
927 thresholds=DestroyXMLTree(thresholds);
928 map=DestroyThresholdMap(map);
929 return(map);
930 }
931 attribute=GetXMLTreeAttribute(levels,"height");
932 if (attribute == (char *) NULL)
933 {
934 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
935 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
936 thresholds=DestroyXMLTree(thresholds);
937 map=DestroyThresholdMap(map);
938 return(map);
939 }
940 map->height=StringToUnsignedLong(attribute);
941 if (map->height == 0)
942 {
943 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
944 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
945 thresholds=DestroyXMLTree(thresholds);
946 map=DestroyThresholdMap(map);
947 return(map);
948 }
949 attribute=GetXMLTreeAttribute(levels,"divisor");
950 if (attribute == (char *) NULL)
951 {
952 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
953 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
954 thresholds=DestroyXMLTree(thresholds);
955 map=DestroyThresholdMap(map);
956 return(map);
957 }
958 map->divisor=(ssize_t) StringToLong(attribute);
959 if (map->divisor < 2)
960 {
961 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
962 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
963 thresholds=DestroyXMLTree(thresholds);
964 map=DestroyThresholdMap(map);
965 return(map);
966 }
967 content=GetXMLTreeContent(levels);
968 if (content == (char *) NULL)
969 {
970 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
971 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
972 thresholds=DestroyXMLTree(thresholds);
973 map=DestroyThresholdMap(map);
974 return(map);
975 }
976 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
977 sizeof(*map->levels));
978 if (map->levels == (ssize_t *) NULL)
979 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
980 for (i=0; i < (ssize_t) (map->width*map->height); i++)
981 {
982 map->levels[i]=(ssize_t) strtol(content,&p,10);
983 if (p == content)
984 {
985 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
986 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
987 thresholds=DestroyXMLTree(thresholds);
988 map=DestroyThresholdMap(map);
989 return(map);
990 }
991 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
992 {
993 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
994 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
995 (double) map->levels[i],map_id);
996 thresholds=DestroyXMLTree(thresholds);
997 map=DestroyThresholdMap(map);
998 return(map);
999 }
1000 content=p;
1001 }
1002 value=(double) strtol(content,&p,10);
1003 (void) value;
1004 if (p != content)
1005 {
1006 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1007 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1008 thresholds=DestroyXMLTree(thresholds);
1009 map=DestroyThresholdMap(map);
1010 return(map);
1011 }
1012 thresholds=DestroyXMLTree(thresholds);
cristy3ed852e2009-09-05 21:47:34 +00001013 return(map);
1014}
1015
1016/*
1017%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1018% %
1019% %
1020% %
1021+ L i s t T h r e s h o l d M a p F i l e %
1022% %
1023% %
1024% %
1025%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1026%
1027% ListThresholdMapFile() lists the threshold maps and their descriptions
1028% in the given XML file data.
1029%
1030% The format of the ListThresholdMaps method is:
1031%
1032% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1033% const char *filename,ExceptionInfo *exception)
1034%
1035% A description of each parameter follows.
1036%
1037% o file: An pointer to the output FILE.
1038%
1039% o xml: The threshold map list in XML format.
1040%
1041% o filename: The threshold map XML filename.
1042%
1043% o exception: return any errors or warnings in this structure.
1044%
1045*/
1046MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1047 const char *filename,ExceptionInfo *exception)
1048{
cristy5f9f2462011-09-28 23:37:58 +00001049 const char
1050 *alias,
1051 *content,
1052 *map;
1053
1054 XMLTreeInfo
1055 *description,
1056 *threshold,
1057 *thresholds;
cristy3ed852e2009-09-05 21:47:34 +00001058
1059 assert( xml != (char *)NULL );
1060 assert( file != (FILE *)NULL );
cristy3ed852e2009-09-05 21:47:34 +00001061 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1062 "Loading threshold map file \"%s\" ...",filename);
1063 thresholds=NewXMLTree(xml,exception);
1064 if ( thresholds == (XMLTreeInfo *)NULL )
1065 return(MagickFalse);
cristy1e604812011-05-19 18:07:50 +00001066 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1067 (void) FormatLocaleFile(file,
1068 "----------------------------------------------------\n");
cristy5f9f2462011-09-28 23:37:58 +00001069 threshold=GetXMLTreeChild(thresholds,"threshold");
cristyb9eb87b2011-09-29 01:15:19 +00001070 for ( ; threshold != (XMLTreeInfo *) NULL;
1071 threshold=GetNextXMLTreeTag(threshold))
cristy3ed852e2009-09-05 21:47:34 +00001072 {
cristy5f9f2462011-09-28 23:37:58 +00001073 map=GetXMLTreeAttribute(threshold,"map");
1074 if (map == (char *) NULL)
1075 {
1076 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1077 "XmlMissingAttribute", "<map>");
1078 thresholds=DestroyXMLTree(thresholds);
1079 return(MagickFalse);
1080 }
1081 alias=GetXMLTreeAttribute(threshold,"alias");
cristy3ed852e2009-09-05 21:47:34 +00001082 description=GetXMLTreeChild(threshold,"description");
cristy5f9f2462011-09-28 23:37:58 +00001083 if (description == (XMLTreeInfo *) NULL)
1084 {
1085 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyb9eb87b2011-09-29 01:15:19 +00001086 "XmlMissingElement", "<description>, map \"%s\"",map);
cristy5f9f2462011-09-28 23:37:58 +00001087 thresholds=DestroyXMLTree(thresholds);
1088 return(MagickFalse);
1089 }
cristy3ed852e2009-09-05 21:47:34 +00001090 content=GetXMLTreeContent(description);
cristy5f9f2462011-09-28 23:37:58 +00001091 if (content == (char *) NULL)
1092 {
1093 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1094 "XmlMissingContent", "<description>, map \"%s\"", map);
1095 thresholds=DestroyXMLTree(thresholds);
1096 return(MagickFalse);
1097 }
cristy1e604812011-05-19 18:07:50 +00001098 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1099 content);
cristy3ed852e2009-09-05 21:47:34 +00001100 }
1101 thresholds=DestroyXMLTree(thresholds);
1102 return(MagickTrue);
1103}
1104
1105/*
1106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1107% %
1108% %
1109% %
1110% L i s t T h r e s h o l d M a p s %
1111% %
1112% %
1113% %
1114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1115%
1116% ListThresholdMaps() lists the threshold maps and their descriptions
1117% as defined by "threshold.xml" to a file.
1118%
1119% The format of the ListThresholdMaps method is:
1120%
1121% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1122%
1123% A description of each parameter follows.
1124%
1125% o file: An pointer to the output FILE.
1126%
1127% o exception: return any errors or warnings in this structure.
1128%
1129*/
1130MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1131 ExceptionInfo *exception)
1132{
1133 const StringInfo
1134 *option;
1135
1136 LinkedListInfo
1137 *options;
1138
1139 MagickStatusType
1140 status;
1141
1142 status=MagickFalse;
cristy5f9f2462011-09-28 23:37:58 +00001143 if (file == (FILE *) NULL)
1144 file=stdout;
cristy3ed852e2009-09-05 21:47:34 +00001145 options=GetConfigureOptions(ThresholdsFilename,exception);
cristy1e604812011-05-19 18:07:50 +00001146 (void) FormatLocaleFile(file,
1147 "\n Threshold Maps for Ordered Dither Operations\n");
cristy5f9f2462011-09-28 23:37:58 +00001148 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1149 (const StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001150 {
cristyb51dff52011-05-19 16:55:47 +00001151 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
cristy3ed852e2009-09-05 21:47:34 +00001152 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1153 GetStringInfoPath(option),exception);
1154 }
1155 options=DestroyConfigureOptions(options);
1156 return(status != 0 ? MagickTrue : MagickFalse);
1157}
1158
1159/*
1160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1161% %
1162% %
1163% %
cristy3ed852e2009-09-05 21:47:34 +00001164% O r d e r e d P o s t e r i z e I m a g e %
1165% %
1166% %
1167% %
1168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1169%
1170% OrderedPosterizeImage() will perform a ordered dither based on a number
1171% of pre-defined dithering threshold maps, but over multiple intensity
1172% levels, which can be different for different channels, according to the
1173% input argument.
1174%
1175% The format of the OrderedPosterizeImage method is:
1176%
1177% MagickBooleanType OrderedPosterizeImage(Image *image,
1178% const char *threshold_map,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001179%
1180% A description of each parameter follows:
1181%
1182% o image: the image.
1183%
cristy3ed852e2009-09-05 21:47:34 +00001184% o threshold_map: A string containing the name of the threshold dither
1185% map to use, followed by zero or more numbers representing the number
1186% of color levels tho dither between.
1187%
cristyf998fb32011-04-27 23:00:47 +00001188% Any level number less than 2 will be equivalent to 2, and means only
cristy3ed852e2009-09-05 21:47:34 +00001189% binary dithering will be applied to each color channel.
1190%
1191% No numbers also means a 2 level (bitmap) dither will be applied to all
1192% channels, while a single number is the number of levels applied to each
1193% channel in sequence. More numbers will be applied in turn to each of
1194% the color channels.
1195%
1196% For example: "o3x3,6" will generate a 6 level posterization of the
1197% image with a ordered 3x3 diffused pixel dither being applied between
1198% each level. While checker,8,8,4 will produce a 332 colormaped image
1199% with only a single checkerboard hash pattern (50% grey) between each
1200% color level, to basically double the number of color levels with
1201% a bare minimim of dithering.
1202%
1203% o exception: return any errors or warnings in this structure.
1204%
1205*/
1206MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1207 const char *threshold_map,ExceptionInfo *exception)
1208{
cristy3ed852e2009-09-05 21:47:34 +00001209#define DitherImageTag "Dither/Image"
1210
cristyc4c8d132010-01-07 01:58:38 +00001211 CacheView
1212 *image_view;
1213
cristy4e0b82a2011-09-29 12:47:44 +00001214 char
1215 token[MaxTextExtent];
1216
1217 const char
1218 *p;
cristy3ed852e2009-09-05 21:47:34 +00001219
1220 MagickBooleanType
1221 status;
1222
cristy5f959472010-05-27 22:19:46 +00001223 MagickOffsetType
1224 progress;
1225
cristy4e0b82a2011-09-29 12:47:44 +00001226 MagickRealType
cristy5f95f4f2011-10-23 01:01:01 +00001227 levels[CompositePixelChannel];
cristy4e0b82a2011-09-29 12:47:44 +00001228
1229 register ssize_t
1230 i;
1231
cristy5f959472010-05-27 22:19:46 +00001232 ssize_t
1233 y;
1234
cristy3ed852e2009-09-05 21:47:34 +00001235 ThresholdMap
1236 *map;
1237
cristy3ed852e2009-09-05 21:47:34 +00001238 assert(image != (Image *) NULL);
1239 assert(image->signature == MagickSignature);
1240 if (image->debug != MagickFalse)
1241 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1242 assert(exception != (ExceptionInfo *) NULL);
1243 assert(exception->signature == MagickSignature);
1244 if (threshold_map == (const char *) NULL)
1245 return(MagickTrue);
cristy4e0b82a2011-09-29 12:47:44 +00001246 p=(char *) threshold_map;
1247 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1248 (*p != '\0'))
1249 p++;
1250 threshold_map=p;
1251 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1252 (*p != '\0'))
cristy3ed852e2009-09-05 21:47:34 +00001253 {
cristy4e0b82a2011-09-29 12:47:44 +00001254 if ((p-threshold_map) >= (MaxTextExtent-1))
1255 break;
1256 token[p-threshold_map]=(*p);
1257 p++;
cristy3ed852e2009-09-05 21:47:34 +00001258 }
cristy4e0b82a2011-09-29 12:47:44 +00001259 token[p-threshold_map]='\0';
1260 map=GetThresholdMap(token,exception);
1261 if (map == (ThresholdMap *) NULL)
1262 {
1263 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1264 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
cristy574cc262011-08-05 01:23:58 +00001265 return(MagickFalse);
cristy4e0b82a2011-09-29 12:47:44 +00001266 }
1267 for (i=0; i < MaxPixelChannels; i++)
1268 levels[i]=2.0;
1269 p=strchr((char *) threshold_map,',');
1270 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1271 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1272 {
1273 GetMagickToken(p,&p,token);
1274 if (*token == ',')
1275 GetMagickToken(p,&p,token);
1276 levels[i]=InterpretLocaleValue(token,(char **) NULL);
1277 }
1278 for (i=0; i < MaxPixelChannels; i++)
1279 if (fabs(levels[i]) >= 1)
1280 levels[i]-=1.0;
1281 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1282 return(MagickFalse);
1283 status=MagickTrue;
1284 progress=0;
1285 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001286#if defined(MAGICKCORE_OPENMP_SUPPORT)
1287 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001288#endif
cristy4e0b82a2011-09-29 12:47:44 +00001289 for (y=0; y < (ssize_t) image->rows; y++)
1290 {
1291 register ssize_t
1292 x;
1293
1294 register Quantum
1295 *restrict q;
1296
1297 if (status == MagickFalse)
1298 continue;
1299 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1300 if (q == (Quantum *) NULL)
1301 {
1302 status=MagickFalse;
1303 continue;
1304 }
1305 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001306 {
cristybb503372010-05-27 20:51:26 +00001307 register ssize_t
cristy4e0b82a2011-09-29 12:47:44 +00001308 i;
cristy3ed852e2009-09-05 21:47:34 +00001309
cristy4e0b82a2011-09-29 12:47:44 +00001310 ssize_t
1311 n;
cristy3ed852e2009-09-05 21:47:34 +00001312
cristy4e0b82a2011-09-29 12:47:44 +00001313 n=0;
1314 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +00001315 {
cristy4e0b82a2011-09-29 12:47:44 +00001316 PixelTrait
1317 traits;
1318
1319 ssize_t
cristy3f5d8152011-09-29 13:00:19 +00001320 level,
1321 threshold;
cristy3ed852e2009-09-05 21:47:34 +00001322
cristy4e0b82a2011-09-29 12:47:44 +00001323 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1324 if ((traits & UpdatePixelTrait) == 0)
1325 continue;
cristy3f5d8152011-09-29 13:00:19 +00001326 if (fabs(levels[n++]) < MagickEpsilon)
1327 continue;
1328 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1329 level=threshold/(map->divisor-1);
1330 threshold-=level*(map->divisor-1);
1331 q[i]=RoundToQuantum((level+(threshold >= map->levels[(x % map->width)+
1332 map->width*(y % map->height)]))*QuantumRange/levels[n]);
cristy4e0b82a2011-09-29 12:47:44 +00001333 n++;
cristy3ed852e2009-09-05 21:47:34 +00001334 }
cristy4e0b82a2011-09-29 12:47:44 +00001335 q+=GetPixelChannels(image);
1336 }
1337 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1338 status=MagickFalse;
1339 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1340 {
1341 MagickBooleanType
1342 proceed;
cristy3ed852e2009-09-05 21:47:34 +00001343
cristyb5d5f722009-11-04 03:03:49 +00001344#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy4e0b82a2011-09-29 12:47:44 +00001345#pragma omp critical (MagickCore_OrderedPosterizeImage)
cristy3ed852e2009-09-05 21:47:34 +00001346#endif
cristy4e0b82a2011-09-29 12:47:44 +00001347 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1348 if (proceed == MagickFalse)
1349 status=MagickFalse;
1350 }
cristy3ed852e2009-09-05 21:47:34 +00001351 }
cristy4e0b82a2011-09-29 12:47:44 +00001352 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00001353 map=DestroyThresholdMap(map);
1354 return(MagickTrue);
1355}
1356
1357/*
1358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1359% %
1360% %
1361% %
1362% R a n d o m T h r e s h o l d I m a g e %
1363% %
1364% %
1365% %
1366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1367%
1368% RandomThresholdImage() changes the value of individual pixels based on the
1369% intensity of each pixel compared to a random threshold. The result is a
1370% low-contrast, two color image.
1371%
1372% The format of the RandomThresholdImage method is:
1373%
cristyf4ad9df2011-07-08 16:49:03 +00001374% MagickBooleanType RandomThresholdImage(Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001375% const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001376%
1377% A description of each parameter follows:
1378%
1379% o image: the image.
1380%
cristy3ed852e2009-09-05 21:47:34 +00001381% o thresholds: a geometry string containing low,high thresholds. If the
1382% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1383% is performed instead.
1384%
1385% o exception: return any errors or warnings in this structure.
1386%
1387*/
cristy3ed852e2009-09-05 21:47:34 +00001388MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1389 const char *thresholds,ExceptionInfo *exception)
1390{
cristy3ed852e2009-09-05 21:47:34 +00001391#define ThresholdImageTag "Threshold/Image"
1392
cristyfa112112010-01-04 17:48:07 +00001393 CacheView
1394 *image_view;
1395
cristy3ed852e2009-09-05 21:47:34 +00001396 GeometryInfo
1397 geometry_info;
1398
1399 MagickStatusType
1400 flags;
1401
cristy3ed852e2009-09-05 21:47:34 +00001402 MagickBooleanType
1403 status;
1404
cristy5f959472010-05-27 22:19:46 +00001405 MagickOffsetType
1406 progress;
1407
cristy4c08aed2011-07-01 19:47:50 +00001408 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001409 threshold;
1410
1411 MagickRealType
1412 min_threshold,
1413 max_threshold;
1414
1415 RandomInfo
cristyfa112112010-01-04 17:48:07 +00001416 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00001417
cristy5f959472010-05-27 22:19:46 +00001418 ssize_t
1419 y;
1420
cristy3ed852e2009-09-05 21:47:34 +00001421 assert(image != (Image *) NULL);
1422 assert(image->signature == MagickSignature);
1423 if (image->debug != MagickFalse)
1424 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1425 assert(exception != (ExceptionInfo *) NULL);
1426 assert(exception->signature == MagickSignature);
1427 if (thresholds == (const char *) NULL)
1428 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00001429 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001430 min_threshold=0.0;
1431 max_threshold=(MagickRealType) QuantumRange;
1432 flags=ParseGeometry(thresholds,&geometry_info);
1433 min_threshold=geometry_info.rho;
1434 max_threshold=geometry_info.sigma;
1435 if ((flags & SigmaValue) == 0)
1436 max_threshold=min_threshold;
1437 if (strchr(thresholds,'%') != (char *) NULL)
1438 {
1439 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1440 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1441 }
cristy3ed852e2009-09-05 21:47:34 +00001442 /*
1443 Random threshold image.
1444 */
1445 status=MagickTrue;
1446 progress=0;
cristy574cc262011-08-05 01:23:58 +00001447 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1448 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001449 random_info=AcquireRandomInfoThreadSet();
1450 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001451#if defined(MAGICKCORE_OPENMP_SUPPORT)
1452 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001453#endif
cristybb503372010-05-27 20:51:26 +00001454 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001455 {
cristy5c9e6f22010-09-17 17:31:01 +00001456 const int
1457 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001458
cristy4c08aed2011-07-01 19:47:50 +00001459 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001460 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001461
cristy5c9e6f22010-09-17 17:31:01 +00001462 register ssize_t
1463 x;
1464
cristy3ed852e2009-09-05 21:47:34 +00001465 if (status == MagickFalse)
1466 continue;
1467 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001468 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001469 {
1470 status=MagickFalse;
1471 continue;
1472 }
cristybb503372010-05-27 20:51:26 +00001473 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001474 {
cristy5f9f2462011-09-28 23:37:58 +00001475 register ssize_t
1476 i;
1477
1478 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1479 {
1480 MagickRealType
1481 threshold;
1482
1483 PixelTrait
1484 traits;
1485
cristy5f9f2462011-09-28 23:37:58 +00001486 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1487 if ((traits & UpdatePixelTrait) == 0)
1488 continue;
1489 if ((MagickRealType) q[i] < min_threshold)
1490 threshold=min_threshold;
1491 else
1492 if ((MagickRealType) q[i] > max_threshold)
1493 threshold=max_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001494 else
cristy5f9f2462011-09-28 23:37:58 +00001495 threshold=(MagickRealType) (QuantumRange*
1496 GetPseudoRandomValue(random_info[id]));
1497 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
1498 QuantumRange);
1499 }
cristyed231572011-07-14 02:18:59 +00001500 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001501 }
1502 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1503 status=MagickFalse;
1504 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1505 {
1506 MagickBooleanType
1507 proceed;
1508
cristyb5d5f722009-11-04 03:03:49 +00001509#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001510 #pragma omp critical (MagickCore_RandomThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001511#endif
1512 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1513 image->rows);
1514 if (proceed == MagickFalse)
1515 status=MagickFalse;
1516 }
1517 }
1518 image_view=DestroyCacheView(image_view);
1519 random_info=DestroyRandomInfoThreadSet(random_info);
1520 return(status);
1521}
1522
1523/*
1524%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1525% %
1526% %
1527% %
1528% W h i t e T h r e s h o l d I m a g e %
1529% %
1530% %
1531% %
1532%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1533%
1534% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001535% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001536% unchanged.
1537%
1538% The format of the WhiteThresholdImage method is:
1539%
cristyf4ad9df2011-07-08 16:49:03 +00001540% MagickBooleanType WhiteThresholdImage(Image *image,
1541% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001542%
1543% A description of each parameter follows:
1544%
1545% o image: the image.
1546%
cristy3ed852e2009-09-05 21:47:34 +00001547% o threshold: Define the threshold value.
1548%
1549% o exception: return any errors or warnings in this structure.
1550%
1551*/
1552MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +00001553 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001554{
1555#define ThresholdImageTag "Threshold/Image"
1556
cristy5f959472010-05-27 22:19:46 +00001557 CacheView
1558 *image_view;
1559
cristy3ed852e2009-09-05 21:47:34 +00001560 GeometryInfo
1561 geometry_info;
1562
cristy3ed852e2009-09-05 21:47:34 +00001563 MagickBooleanType
1564 status;
1565
cristy5f959472010-05-27 22:19:46 +00001566 MagickOffsetType
1567 progress;
1568
cristy5f9f2462011-09-28 23:37:58 +00001569 MagickRealType
1570 threshold[5];
1571
cristy3ed852e2009-09-05 21:47:34 +00001572 MagickStatusType
1573 flags;
1574
cristy5f9f2462011-09-28 23:37:58 +00001575 register ssize_t
1576 i;
1577
cristy5f959472010-05-27 22:19:46 +00001578 ssize_t
1579 y;
cristy3ed852e2009-09-05 21:47:34 +00001580
1581 assert(image != (Image *) NULL);
1582 assert(image->signature == MagickSignature);
1583 if (image->debug != MagickFalse)
1584 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1585 if (thresholds == (const char *) NULL)
1586 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +00001587 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001588 return(MagickFalse);
1589 flags=ParseGeometry(thresholds,&geometry_info);
cristy5f9f2462011-09-28 23:37:58 +00001590 for (i=0; i < 5; i++)
1591 threshold[i]=geometry_info.rho;
1592 if ((flags & SigmaValue) != 0)
1593 threshold[1]=geometry_info.sigma;
1594 if ((flags & XiValue) != 0)
1595 threshold[2]=geometry_info.xi;
1596 if ((flags & PsiValue) != 0)
1597 threshold[3]=geometry_info.psi;
1598 if ((flags & ChiValue) != 0)
1599 threshold[4]=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +00001600 if ((flags & PercentValue) != 0)
cristy5f9f2462011-09-28 23:37:58 +00001601 for (i=0; i < 5; i++)
1602 threshold[i]*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001603 /*
1604 White threshold image.
1605 */
1606 status=MagickTrue;
1607 progress=0;
1608 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001609#if defined(MAGICKCORE_OPENMP_SUPPORT)
1610 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001611#endif
cristybb503372010-05-27 20:51:26 +00001612 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001613 {
cristybb503372010-05-27 20:51:26 +00001614 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001615 x;
1616
cristy4c08aed2011-07-01 19:47:50 +00001617 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001618 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001619
1620 if (status == MagickFalse)
1621 continue;
1622 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001623 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001624 {
1625 status=MagickFalse;
1626 continue;
1627 }
cristybb503372010-05-27 20:51:26 +00001628 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001629 {
cristy5f9f2462011-09-28 23:37:58 +00001630 register ssize_t
1631 i;
1632
1633 ssize_t
1634 n;
1635
1636 n=0;
1637 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1638 {
1639 PixelTrait
1640 traits;
1641
1642 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1643 if ((traits & UpdatePixelTrait) == 0)
1644 continue;
1645 if ((MagickRealType) q[i] > threshold[n++ % 5])
1646 q[i]=QuantumRange;
1647 }
cristyed231572011-07-14 02:18:59 +00001648 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001649 }
1650 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1651 status=MagickFalse;
1652 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1653 {
1654 MagickBooleanType
1655 proceed;
1656
cristyb5d5f722009-11-04 03:03:49 +00001657#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001658 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001659#endif
1660 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1661 image->rows);
1662 if (proceed == MagickFalse)
1663 status=MagickFalse;
1664 }
1665 }
1666 image_view=DestroyCacheView(image_view);
1667 return(status);
1668}