blob: 429ea04c8f197da302ad8010bdfbbac2029bb0be [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% TTTTT H H RRRR EEEEE SSSSS H H OOO L DDDD %
7% T H H R R E SS H H O O L D D %
8% T HHHHH RRRR EEE SSS HHHHH O O L D D %
9% T H H R R E SS H H O O L D D %
10% T H H R R EEEEE SSSSS H H OOO LLLLL DDDD %
11% %
12% %
13% MagickCore Image Threshold Methods %
14% %
15% Software Design %
16% John Cristy %
17% October 1996 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/property.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colormap.h"
50#include "MagickCore/colorspace.h"
cristy23e55c02012-04-10 01:21:56 +000051#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/configure.h"
53#include "MagickCore/constitute.h"
54#include "MagickCore/decorate.h"
55#include "MagickCore/draw.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/effect.h"
60#include "MagickCore/fx.h"
61#include "MagickCore/gem.h"
62#include "MagickCore/geometry.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/monitor.h"
68#include "MagickCore/monitor-private.h"
69#include "MagickCore/montage.h"
70#include "MagickCore/option.h"
71#include "MagickCore/pixel-accessor.h"
72#include "MagickCore/quantize.h"
73#include "MagickCore/quantum.h"
74#include "MagickCore/random_.h"
75#include "MagickCore/random-private.h"
76#include "MagickCore/resize.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/segment.h"
79#include "MagickCore/shear.h"
80#include "MagickCore/signature-private.h"
81#include "MagickCore/string_.h"
82#include "MagickCore/string-private.h"
83#include "MagickCore/thread-private.h"
84#include "MagickCore/threshold.h"
cristy4e0b82a2011-09-29 12:47:44 +000085#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000086#include "MagickCore/transform.h"
87#include "MagickCore/xml-tree.h"
cristy433d1182011-09-04 13:38:52 +000088#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000089
90/*
91 Define declarations.
92*/
93#define ThresholdsFilename "thresholds.xml"
94
95/*
96 Typedef declarations.
97*/
98struct _ThresholdMap
99{
100 char
101 *map_id,
102 *description;
103
cristybb503372010-05-27 20:51:26 +0000104 size_t
cristy3ed852e2009-09-05 21:47:34 +0000105 width,
106 height;
107
cristybb503372010-05-27 20:51:26 +0000108 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000109 divisor,
110 *levels;
111};
112
113/*
cristybd0ebf02011-09-29 01:19:42 +0000114 Forward declarations.
115*/
116static ThresholdMap
117 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
118
119/*
cristy3ed852e2009-09-05 21:47:34 +0000120%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121% %
122% %
123% %
124% A d a p t i v e T h r e s h o l d I m a g e %
125% %
126% %
127% %
128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129%
130% AdaptiveThresholdImage() selects an individual threshold for each pixel
131% based on the range of intensity values in its local neighborhood. This
132% allows for thresholding of an image whose global intensity histogram
133% doesn't contain distinctive peaks.
134%
135% The format of the AdaptiveThresholdImage method is:
136%
cristyde5cc632011-07-18 14:47:00 +0000137% Image *AdaptiveThresholdImage(const Image *image,const size_t width,
138% const size_t height,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000139%
140% A description of each parameter follows:
141%
142% o image: the image.
143%
144% o width: the width of the local neighborhood.
145%
146% o height: the height of the local neighborhood.
147%
cristyde5cc632011-07-18 14:47:00 +0000148% o bias: the mean bias.
cristy3ed852e2009-09-05 21:47:34 +0000149%
150% o exception: return any errors or warnings in this structure.
151%
152*/
153MagickExport Image *AdaptiveThresholdImage(const Image *image,
cristyde5cc632011-07-18 14:47:00 +0000154 const size_t width,const size_t height,const double bias,
cristy3ed852e2009-09-05 21:47:34 +0000155 ExceptionInfo *exception)
156{
cristyde5cc632011-07-18 14:47:00 +0000157#define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
cristy3ed852e2009-09-05 21:47:34 +0000158
cristyc4c8d132010-01-07 01:58:38 +0000159 CacheView
160 *image_view,
161 *threshold_view;
162
cristy3ed852e2009-09-05 21:47:34 +0000163 Image
164 *threshold_image;
165
cristy3ed852e2009-09-05 21:47:34 +0000166 MagickBooleanType
167 status;
168
cristy5f959472010-05-27 22:19:46 +0000169 MagickOffsetType
170 progress;
171
cristyde5cc632011-07-18 14:47:00 +0000172 MagickSizeType
cristy3ed852e2009-09-05 21:47:34 +0000173 number_pixels;
174
cristy5f959472010-05-27 22:19:46 +0000175 ssize_t
176 y;
177
cristyde5cc632011-07-18 14:47:00 +0000178 /*
179 Initialize threshold image attributes.
180 */
181 assert(image != (Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000182 assert(image->signature == MagickSignature);
183 if (image->debug != MagickFalse)
184 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
185 assert(exception != (ExceptionInfo *) NULL);
186 assert(exception->signature == MagickSignature);
cristyde5cc632011-07-18 14:47:00 +0000187 if ((width % 2) == 0)
188 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
189 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
190 exception);
cristy3ed852e2009-09-05 21:47:34 +0000191 if (threshold_image == (Image *) NULL)
192 return((Image *) NULL);
cristyb9eb87b2011-09-29 01:15:19 +0000193 status=SetImageStorageClass(threshold_image,DirectClass,exception);
194 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000195 {
cristy3ed852e2009-09-05 21:47:34 +0000196 threshold_image=DestroyImage(threshold_image);
197 return((Image *) NULL);
198 }
199 /*
cristyde5cc632011-07-18 14:47:00 +0000200 Threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000201 */
202 status=MagickTrue;
203 progress=0;
cristyde5cc632011-07-18 14:47:00 +0000204 number_pixels=(MagickSizeType) width*height;
cristydb070952012-04-20 14:33:00 +0000205 image_view=AcquireVirtualCacheView(image,exception);
206 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000207#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000208 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000209 dynamic_number_threads(image,image->columns,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000210#endif
cristybb503372010-05-27 20:51:26 +0000211 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000212 {
cristy4c08aed2011-07-01 19:47:50 +0000213 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000214 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000215
cristy4c08aed2011-07-01 19:47:50 +0000216 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000217 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000218
cristyde5cc632011-07-18 14:47:00 +0000219 register ssize_t
220 x;
221
cristyde5cc632011-07-18 14:47:00 +0000222 ssize_t
223 center;
224
cristy3ed852e2009-09-05 21:47:34 +0000225 if (status == MagickFalse)
226 continue;
cristyd99b0962010-05-29 23:14:26 +0000227 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
cristyde5cc632011-07-18 14:47:00 +0000228 (height/2L),image->columns+width,height,exception);
229 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
230 1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000231 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000232 {
233 status=MagickFalse;
234 continue;
235 }
cristy5f9f2462011-09-28 23:37:58 +0000236 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
cristya0312c92011-07-23 21:04:30 +0000237 GetPixelChannels(image)*(width/2);
cristybb503372010-05-27 20:51:26 +0000238 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000239 {
cristybb503372010-05-27 20:51:26 +0000240 register ssize_t
cristyde5cc632011-07-18 14:47:00 +0000241 i;
cristy3ed852e2009-09-05 21:47:34 +0000242
cristy177e41c2012-04-15 15:08:25 +0000243 if (GetPixelMask(image,p) != 0)
244 {
245 p+=GetPixelChannels(image);
246 q+=GetPixelChannels(threshold_image);
247 continue;
248 }
cristya0312c92011-07-23 21:04:30 +0000249 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000250 {
cristya19f1d72012-08-07 18:24:38 +0000251 double
cristyde5cc632011-07-18 14:47:00 +0000252 mean,
253 pixel;
254
255 PixelChannel
256 channel;
257
258 PixelTrait
259 threshold_traits,
260 traits;
261
262 register const Quantum
263 *restrict pixels;
264
265 register ssize_t
266 u;
267
268 ssize_t
269 v;
270
cristye2a912b2011-12-05 20:02:07 +0000271 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000272 traits=GetPixelChannelMapTraits(image,channel);
cristyde5cc632011-07-18 14:47:00 +0000273 threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
cristy010d7d12011-08-31 01:02:48 +0000274 if ((traits == UndefinedPixelTrait) ||
275 (threshold_traits == UndefinedPixelTrait))
cristyde5cc632011-07-18 14:47:00 +0000276 continue;
cristy177e41c2012-04-15 15:08:25 +0000277 if ((threshold_traits & CopyPixelTrait) != 0)
cristyde5cc632011-07-18 14:47:00 +0000278 {
cristy0beccfa2011-09-25 20:47:53 +0000279 SetPixelChannel(threshold_image,channel,p[center+i],q);
cristyde5cc632011-07-18 14:47:00 +0000280 continue;
281 }
282 pixels=p;
283 pixel=0.0;
284 for (v=0; v < (ssize_t) height; v++)
cristy3ed852e2009-09-05 21:47:34 +0000285 {
cristyde5cc632011-07-18 14:47:00 +0000286 for (u=0; u < (ssize_t) width; u++)
287 {
288 pixel+=pixels[i];
cristya0312c92011-07-23 21:04:30 +0000289 pixels+=GetPixelChannels(image);
cristyde5cc632011-07-18 14:47:00 +0000290 }
cristya0312c92011-07-23 21:04:30 +0000291 pixels+=image->columns*GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000292 }
cristya19f1d72012-08-07 18:24:38 +0000293 mean=(double) (pixel/number_pixels+bias);
294 SetPixelChannel(threshold_image,channel,(Quantum) ((double)
cristy5f9f2462011-09-28 23:37:58 +0000295 p[center+i] <= mean ? 0 : QuantumRange),q);
cristy3ed852e2009-09-05 21:47:34 +0000296 }
cristya0312c92011-07-23 21:04:30 +0000297 p+=GetPixelChannels(image);
298 q+=GetPixelChannels(threshold_image);
cristy3ed852e2009-09-05 21:47:34 +0000299 }
cristyde5cc632011-07-18 14:47:00 +0000300 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000301 status=MagickFalse;
302 if (image->progress_monitor != (MagickProgressMonitor) NULL)
303 {
304 MagickBooleanType
305 proceed;
306
cristyb5d5f722009-11-04 03:03:49 +0000307#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000308 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +0000309#endif
cristyde5cc632011-07-18 14:47:00 +0000310 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
cristy3ed852e2009-09-05 21:47:34 +0000311 image->rows);
312 if (proceed == MagickFalse)
313 status=MagickFalse;
314 }
315 }
cristyde5cc632011-07-18 14:47:00 +0000316 threshold_image->type=image->type;
cristy3ed852e2009-09-05 21:47:34 +0000317 threshold_view=DestroyCacheView(threshold_view);
318 image_view=DestroyCacheView(image_view);
319 if (status == MagickFalse)
320 threshold_image=DestroyImage(threshold_image);
321 return(threshold_image);
322}
323
324/*
325%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326% %
327% %
328% %
329% B i l e v e l I m a g e %
330% %
331% %
332% %
333%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
334%
335% BilevelImage() changes the value of individual pixels based on the
336% intensity of each pixel channel. The result is a high-contrast image.
337%
338% More precisely each channel value of the image is 'thresholded' so that if
339% it is equal to or less than the given value it is set to zero, while any
340% value greater than that give is set to it maximum or QuantumRange.
341%
342% This function is what is used to implement the "-threshold" operator for
343% the command line API.
344%
345% If the default channel setting is given the image is thresholded using just
346% the gray 'intensity' of the image, rather than the individual channels.
347%
cristyf4ad9df2011-07-08 16:49:03 +0000348% The format of the BilevelImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000349%
cristye941a752011-10-15 01:52:48 +0000350% MagickBooleanType BilevelImage(Image *image,const double threshold,
351% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000352%
353% A description of each parameter follows:
354%
355% o image: the image.
356%
cristy3ed852e2009-09-05 21:47:34 +0000357% o threshold: define the threshold values.
358%
cristye941a752011-10-15 01:52:48 +0000359% o exception: return any errors or warnings in this structure.
360%
cristyf89cb1d2011-07-07 01:24:37 +0000361% Aside: You can get the same results as operator using LevelImages()
cristy3ed852e2009-09-05 21:47:34 +0000362% with the 'threshold' value for both the black_point and the white_point.
363%
364*/
cristye941a752011-10-15 01:52:48 +0000365MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
366 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000367{
368#define ThresholdImageTag "Threshold/Image"
369
cristyc4c8d132010-01-07 01:58:38 +0000370 CacheView
371 *image_view;
372
cristy3ed852e2009-09-05 21:47:34 +0000373 MagickBooleanType
374 status;
375
cristy5f959472010-05-27 22:19:46 +0000376 MagickOffsetType
377 progress;
378
379 ssize_t
380 y;
381
cristy3ed852e2009-09-05 21:47:34 +0000382 assert(image != (Image *) NULL);
383 assert(image->signature == MagickSignature);
384 if (image->debug != MagickFalse)
385 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy574cc262011-08-05 01:23:58 +0000386 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000387 return(MagickFalse);
388 /*
389 Bilevel threshold image.
390 */
391 status=MagickTrue;
392 progress=0;
cristydb070952012-04-20 14:33:00 +0000393 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000394#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000395 #pragma omp parallel for schedule(static,8) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000396 dynamic_number_threads(image,image->columns,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000397#endif
cristybb503372010-05-27 20:51:26 +0000398 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000399 {
cristybb503372010-05-27 20:51:26 +0000400 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000401 x;
402
cristy4c08aed2011-07-01 19:47:50 +0000403 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000404 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000405
406 if (status == MagickFalse)
407 continue;
408 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000409 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000410 {
411 status=MagickFalse;
412 continue;
413 }
cristy805b6a02011-08-09 00:59:35 +0000414 for (x=0; x < (ssize_t) image->columns; x++)
415 {
cristy171e2352012-07-10 17:43:12 +0000416 double
417 pixel;
418
cristy95111202011-08-09 19:41:42 +0000419 register ssize_t
420 i;
421
cristy10a6c612012-01-29 21:41:05 +0000422 if (GetPixelMask(image,q) != 0)
423 {
424 q+=GetPixelChannels(image);
425 continue;
426 }
cristy171e2352012-07-10 17:43:12 +0000427 pixel=(double) GetPixelIntensity(image,q);
cristy95111202011-08-09 19:41:42 +0000428 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
429 {
cristyabace412011-12-11 15:56:53 +0000430 PixelChannel
431 channel;
432
cristy95111202011-08-09 19:41:42 +0000433 PixelTrait
434 traits;
435
cristyabace412011-12-11 15:56:53 +0000436 channel=GetPixelChannelMapChannel(image,i);
437 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +0000438 if ((traits & UpdatePixelTrait) == 0)
439 continue;
cristya64f4b92012-07-11 23:59:00 +0000440 if (image->channel_mask != DefaultChannels)
cristy171e2352012-07-10 17:43:12 +0000441 pixel=q[i];
442 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
cristy95111202011-08-09 19:41:42 +0000443 }
cristy805b6a02011-08-09 00:59:35 +0000444 q+=GetPixelChannels(image);
445 }
cristy3ed852e2009-09-05 21:47:34 +0000446 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
447 status=MagickFalse;
448 if (image->progress_monitor != (MagickProgressMonitor) NULL)
449 {
450 MagickBooleanType
451 proceed;
452
cristyb5d5f722009-11-04 03:03:49 +0000453#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000454 #pragma omp critical (MagickCore_BilevelImage)
cristy3ed852e2009-09-05 21:47:34 +0000455#endif
456 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
457 image->rows);
458 if (proceed == MagickFalse)
459 status=MagickFalse;
460 }
461 }
462 image_view=DestroyCacheView(image_view);
463 return(status);
464}
465
466/*
467%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
468% %
469% %
470% %
471% B l a c k T h r e s h o l d I m a g e %
472% %
473% %
474% %
475%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
476%
477% BlackThresholdImage() is like ThresholdImage() but forces all pixels below
cristy4e101302009-09-17 12:49:12 +0000478% the threshold into black while leaving all pixels at or above the threshold
cristy3ed852e2009-09-05 21:47:34 +0000479% unchanged.
480%
481% The format of the BlackThresholdImage method is:
482%
cristyf4ad9df2011-07-08 16:49:03 +0000483% MagickBooleanType BlackThresholdImage(Image *image,
484% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000485%
486% A description of each parameter follows:
487%
488% o image: the image.
489%
cristy5f9f2462011-09-28 23:37:58 +0000490% o threshold: define the threshold value.
cristy3ed852e2009-09-05 21:47:34 +0000491%
492% o exception: return any errors or warnings in this structure.
493%
494*/
495MagickExport MagickBooleanType BlackThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +0000496 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000497{
498#define ThresholdImageTag "Threshold/Image"
499
cristyc4c8d132010-01-07 01:58:38 +0000500 CacheView
501 *image_view;
502
cristy3ed852e2009-09-05 21:47:34 +0000503 GeometryInfo
504 geometry_info;
505
cristy3ed852e2009-09-05 21:47:34 +0000506 MagickBooleanType
507 status;
508
cristy5f959472010-05-27 22:19:46 +0000509 MagickOffsetType
510 progress;
511
cristyd6803382012-04-10 01:41:25 +0000512 PixelInfo
cristya12d8ba2012-04-29 16:33:41 +0000513 threshold;
cristy3ed852e2009-09-05 21:47:34 +0000514
515 MagickStatusType
516 flags;
517
cristy5f959472010-05-27 22:19:46 +0000518 ssize_t
519 y;
520
cristy3ed852e2009-09-05 21:47:34 +0000521 assert(image != (Image *) NULL);
522 assert(image->signature == MagickSignature);
523 if (image->debug != MagickFalse)
524 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
525 if (thresholds == (const char *) NULL)
526 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +0000527 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000528 return(MagickFalse);
cristy23e55c02012-04-10 01:21:56 +0000529 if (IsGrayColorspace(image->colorspace) != MagickFalse)
cristyb09db112012-07-11 12:04:31 +0000530 (void) TransformImageColorspace(image,RGBColorspace,exception);
cristya12d8ba2012-04-29 16:33:41 +0000531 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +0000532 flags=ParseGeometry(thresholds,&geometry_info);
cristya12d8ba2012-04-29 16:33:41 +0000533 threshold.red=geometry_info.rho;
534 threshold.green=geometry_info.rho;
535 threshold.blue=geometry_info.rho;
536 threshold.black=geometry_info.rho;
537 threshold.alpha=100.0;
cristy5f9f2462011-09-28 23:37:58 +0000538 if ((flags & SigmaValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000539 threshold.green=geometry_info.sigma;
cristy5f9f2462011-09-28 23:37:58 +0000540 if ((flags & XiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000541 threshold.blue=geometry_info.xi;
cristy5f9f2462011-09-28 23:37:58 +0000542 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000543 threshold.alpha=geometry_info.psi;
544 if (threshold.colorspace == CMYKColorspace)
cristyd6803382012-04-10 01:41:25 +0000545 {
546 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000547 threshold.black=geometry_info.psi;
cristyd6803382012-04-10 01:41:25 +0000548 if ((flags & ChiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000549 threshold.alpha=geometry_info.chi;
550 }
551 if ((flags & PercentValue) != 0)
552 {
553 threshold.red*=(QuantumRange/100.0);
554 threshold.green*=(QuantumRange/100.0);
555 threshold.blue*=(QuantumRange/100.0);
556 threshold.black*=(QuantumRange/100.0);
557 threshold.alpha*=(QuantumRange/100.0);
cristyd6803382012-04-10 01:41:25 +0000558 }
cristy3ed852e2009-09-05 21:47:34 +0000559 /*
cristy5f9f2462011-09-28 23:37:58 +0000560 White threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000561 */
562 status=MagickTrue;
563 progress=0;
cristydb070952012-04-20 14:33:00 +0000564 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000565#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000566 #pragma omp parallel for schedule(static,8) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000567 dynamic_number_threads(image,image->columns,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000568#endif
cristybb503372010-05-27 20:51:26 +0000569 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000570 {
cristybb503372010-05-27 20:51:26 +0000571 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000572 x;
573
cristy4c08aed2011-07-01 19:47:50 +0000574 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000575 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000576
577 if (status == MagickFalse)
578 continue;
579 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000580 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000581 {
582 status=MagickFalse;
583 continue;
584 }
cristybb503372010-05-27 20:51:26 +0000585 for (x=0; x < (ssize_t) image->columns; x++)
cristyb0ea1af2009-11-28 20:44:46 +0000586 {
cristy81629aa2012-07-12 20:08:52 +0000587 double
588 pixel;
589
cristyc4567182012-06-24 20:55:08 +0000590 register ssize_t
591 i;
592
cristy10a6c612012-01-29 21:41:05 +0000593 if (GetPixelMask(image,q) != 0)
594 {
595 q+=GetPixelChannels(image);
596 continue;
597 }
cristy81629aa2012-07-12 20:08:52 +0000598 pixel=(double) GetPixelIntensity(image,q);
cristy188f29a2012-06-24 19:09:53 +0000599 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
600 {
601 PixelChannel
602 channel;
603
604 PixelTrait
605 traits;
606
607 channel=GetPixelChannelMapChannel(image,i);
608 traits=GetPixelChannelMapTraits(image,channel);
609 if ((traits & UpdatePixelTrait) == 0)
610 continue;
cristy81629aa2012-07-12 20:08:52 +0000611 if (image->channel_mask != DefaultChannels)
612 pixel=q[i];
613 if (pixel <= GetPixelInfoChannel(&threshold,channel))
cristy188f29a2012-06-24 19:09:53 +0000614 q[i]=0;
615 }
cristyed231572011-07-14 02:18:59 +0000616 q+=GetPixelChannels(image);
cristyb0ea1af2009-11-28 20:44:46 +0000617 }
cristy3ed852e2009-09-05 21:47:34 +0000618 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
619 status=MagickFalse;
620 if (image->progress_monitor != (MagickProgressMonitor) NULL)
621 {
622 MagickBooleanType
623 proceed;
624
cristyb5d5f722009-11-04 03:03:49 +0000625#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya12d8ba2012-04-29 16:33:41 +0000626 #pragma omp critical (MagickCore_BlackThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +0000627#endif
628 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
629 image->rows);
630 if (proceed == MagickFalse)
631 status=MagickFalse;
632 }
633 }
634 image_view=DestroyCacheView(image_view);
635 return(status);
636}
637
638/*
639%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
640% %
641% %
642% %
cristy1eb45dd2009-09-25 16:38:06 +0000643% C l a m p I m a g e %
644% %
645% %
646% %
647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648%
cristyecb0c6d2009-09-25 16:50:09 +0000649% ClampImage() restricts the color range from 0 to the quantum depth.
cristy1eb45dd2009-09-25 16:38:06 +0000650%
cristyf4ad9df2011-07-08 16:49:03 +0000651% The format of the ClampImage method is:
cristy1eb45dd2009-09-25 16:38:06 +0000652%
cristy092d71c2011-10-14 18:01:29 +0000653% MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000654%
655% A description of each parameter follows:
656%
657% o image: the image.
658%
cristy092d71c2011-10-14 18:01:29 +0000659% o exception: return any errors or warnings in this structure.
660%
cristy1eb45dd2009-09-25 16:38:06 +0000661*/
662
cristy75ffdb72010-01-07 17:40:12 +0000663static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
cristy1eb45dd2009-09-25 16:38:06 +0000664{
cristyd75ce452012-06-04 23:56:20 +0000665#if defined(MAGICKCORE_HDRI_SUPPORT)
cristy1eb45dd2009-09-25 16:38:06 +0000666 if (quantum <= 0)
667 return(0);
668 if (quantum >= QuantumRange)
669 return(QuantumRange);
670 return(quantum);
cristyd75ce452012-06-04 23:56:20 +0000671#else
672 return(quantum);
673#endif
cristy1eb45dd2009-09-25 16:38:06 +0000674}
675
cristy092d71c2011-10-14 18:01:29 +0000676MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000677{
cristy1eb45dd2009-09-25 16:38:06 +0000678#define ClampImageTag "Clamp/Image"
679
cristyc4c8d132010-01-07 01:58:38 +0000680 CacheView
681 *image_view;
682
cristy1eb45dd2009-09-25 16:38:06 +0000683 MagickBooleanType
684 status;
685
cristy5f959472010-05-27 22:19:46 +0000686 MagickOffsetType
687 progress;
688
689 ssize_t
690 y;
691
cristy1eb45dd2009-09-25 16:38:06 +0000692 assert(image != (Image *) NULL);
693 assert(image->signature == MagickSignature);
694 if (image->debug != MagickFalse)
695 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
696 if (image->storage_class == PseudoClass)
697 {
cristybb503372010-05-27 20:51:26 +0000698 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000699 i;
700
cristy101ab702011-10-13 13:06:32 +0000701 register PixelInfo
cristyc47d1f82009-11-26 01:44:43 +0000702 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000703
704 q=image->colormap;
cristybb503372010-05-27 20:51:26 +0000705 for (i=0; i < (ssize_t) image->colors; i++)
cristy1eb45dd2009-09-25 16:38:06 +0000706 {
cristye42f6582012-02-11 17:59:50 +0000707 q->red=(double) ClampToUnsignedQuantum(ClampToQuantum(q->red));
708 q->green=(double) ClampToUnsignedQuantum(ClampToQuantum(q->green));
709 q->blue=(double) ClampToUnsignedQuantum(ClampToQuantum(q->blue));
710 q->alpha=(double) ClampToUnsignedQuantum(ClampToQuantum(q->alpha));
cristy1eb45dd2009-09-25 16:38:06 +0000711 q++;
712 }
cristyea1a8aa2011-10-20 13:24:06 +0000713 return(SyncImage(image,exception));
cristy1eb45dd2009-09-25 16:38:06 +0000714 }
715 /*
cristy611721d2009-09-25 16:42:17 +0000716 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000717 */
718 status=MagickTrue;
719 progress=0;
cristydb070952012-04-20 14:33:00 +0000720 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000721#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000722 #pragma omp parallel for schedule(static,8) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000723 dynamic_number_threads(image,image->columns,image->rows,1)
cristy1eb45dd2009-09-25 16:38:06 +0000724#endif
cristybb503372010-05-27 20:51:26 +0000725 for (y=0; y < (ssize_t) image->rows; y++)
cristy1eb45dd2009-09-25 16:38:06 +0000726 {
cristybb503372010-05-27 20:51:26 +0000727 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000728 x;
729
cristy4c08aed2011-07-01 19:47:50 +0000730 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000731 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000732
733 if (status == MagickFalse)
734 continue;
735 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000736 if (q == (Quantum *) NULL)
cristy1eb45dd2009-09-25 16:38:06 +0000737 {
738 status=MagickFalse;
739 continue;
740 }
cristybb503372010-05-27 20:51:26 +0000741 for (x=0; x < (ssize_t) image->columns; x++)
cristy1eb45dd2009-09-25 16:38:06 +0000742 {
cristy5f9f2462011-09-28 23:37:58 +0000743 register ssize_t
744 i;
745
cristy10a6c612012-01-29 21:41:05 +0000746 if (GetPixelMask(image,q) != 0)
747 {
748 q+=GetPixelChannels(image);
749 continue;
750 }
cristy5f9f2462011-09-28 23:37:58 +0000751 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
752 {
cristyabace412011-12-11 15:56:53 +0000753 PixelChannel
754 channel;
755
cristy5f9f2462011-09-28 23:37:58 +0000756 PixelTrait
757 traits;
758
cristyabace412011-12-11 15:56:53 +0000759 channel=GetPixelChannelMapChannel(image,i);
760 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +0000761 if (traits == UndefinedPixelTrait)
762 continue;
763 q[i]=ClampToUnsignedQuantum(q[i]);
764 }
cristyed231572011-07-14 02:18:59 +0000765 q+=GetPixelChannels(image);
cristy1eb45dd2009-09-25 16:38:06 +0000766 }
767 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
768 status=MagickFalse;
769 if (image->progress_monitor != (MagickProgressMonitor) NULL)
770 {
771 MagickBooleanType
772 proceed;
773
cristyb5d5f722009-11-04 03:03:49 +0000774#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000775 #pragma omp critical (MagickCore_ClampImage)
cristy1eb45dd2009-09-25 16:38:06 +0000776#endif
cristyaa17cde2012-06-04 23:43:42 +0000777 proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
cristy1eb45dd2009-09-25 16:38:06 +0000778 if (proceed == MagickFalse)
779 status=MagickFalse;
780 }
781 }
782 image_view=DestroyCacheView(image_view);
783 return(status);
784}
785
786/*
787%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788% %
789% %
790% %
cristy3ed852e2009-09-05 21:47:34 +0000791% D e s t r o y T h r e s h o l d M a p %
792% %
793% %
794% %
795%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
796%
797% DestroyThresholdMap() de-allocate the given ThresholdMap
798%
799% The format of the ListThresholdMaps method is:
800%
801% ThresholdMap *DestroyThresholdMap(Threshold *map)
802%
803% A description of each parameter follows.
804%
805% o map: Pointer to the Threshold map to destroy
806%
807*/
808MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
809{
810 assert(map != (ThresholdMap *) NULL);
811 if (map->map_id != (char *) NULL)
812 map->map_id=DestroyString(map->map_id);
813 if (map->description != (char *) NULL)
814 map->description=DestroyString(map->description);
cristybb503372010-05-27 20:51:26 +0000815 if (map->levels != (ssize_t *) NULL)
816 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
cristy3ed852e2009-09-05 21:47:34 +0000817 map=(ThresholdMap *) RelinquishMagickMemory(map);
818 return(map);
819}
820
821/*
822%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
823% %
824% %
825% %
cristyb9eb87b2011-09-29 01:15:19 +0000826% G e t T h r e s h o l d M a p %
827% %
828% %
829% %
830%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
831%
832% GetThresholdMap() loads and searches one or more threshold map files for the
833% map matching the given name or alias.
834%
835% The format of the GetThresholdMap method is:
836%
837% ThresholdMap *GetThresholdMap(const char *map_id,
838% ExceptionInfo *exception)
839%
840% A description of each parameter follows.
841%
842% o map_id: ID of the map to look for.
843%
844% o exception: return any errors or warnings in this structure.
845%
846*/
847MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
848 ExceptionInfo *exception)
849{
850 const StringInfo
851 *option;
852
853 LinkedListInfo
854 *options;
855
856 ThresholdMap
857 *map;
858
859 map=(ThresholdMap *)NULL;
860 options=GetConfigureOptions(ThresholdsFilename,exception);
861 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
862 (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
863 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
864 GetStringInfoPath(option),map_id,exception);
865 options=DestroyConfigureOptions(options);
866 return(map);
867}
868
869/*
870%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
871% %
872% %
873% %
cristy3ed852e2009-09-05 21:47:34 +0000874+ G e t T h r e s h o l d M a p F i l e %
875% %
876% %
877% %
878%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
879%
880% GetThresholdMapFile() look for a given threshold map name or alias in the
881% given XML file data, and return the allocated the map when found.
882%
883% The format of the ListThresholdMaps method is:
884%
885% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
886% const char *map_id,ExceptionInfo *exception)
887%
888% A description of each parameter follows.
889%
890% o xml: The threshold map list in XML format.
891%
892% o filename: The threshold map XML filename.
893%
894% o map_id: ID of the map to look for in XML list.
895%
896% o exception: return any errors or warnings in this structure.
897%
898*/
cristybd0ebf02011-09-29 01:19:42 +0000899static ThresholdMap *GetThresholdMapFile(const char *xml,
cristy3ed852e2009-09-05 21:47:34 +0000900 const char *filename,const char *map_id,ExceptionInfo *exception)
901{
cristyb9eb87b2011-09-29 01:15:19 +0000902 char
903 *p;
904
cristy3ed852e2009-09-05 21:47:34 +0000905 const char
cristyb9eb87b2011-09-29 01:15:19 +0000906 *attribute,
cristy3ed852e2009-09-05 21:47:34 +0000907 *content;
908
909 double
910 value;
911
cristyb9eb87b2011-09-29 01:15:19 +0000912 register ssize_t
913 i;
cristy3ed852e2009-09-05 21:47:34 +0000914
915 ThresholdMap
916 *map;
917
cristyb9eb87b2011-09-29 01:15:19 +0000918 XMLTreeInfo
919 *description,
920 *levels,
921 *threshold,
922 *thresholds;
923
924 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
925 "Loading threshold map file \"%s\" ...",filename);
926 map=(ThresholdMap *) NULL;
927 thresholds=NewXMLTree(xml,exception);
928 if (thresholds == (XMLTreeInfo *) NULL)
929 return(map);
930 for (threshold=GetXMLTreeChild(thresholds,"threshold");
931 threshold != (XMLTreeInfo *) NULL;
932 threshold=GetNextXMLTreeTag(threshold))
933 {
934 attribute=GetXMLTreeAttribute(threshold,"map");
935 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
936 break;
937 attribute=GetXMLTreeAttribute(threshold,"alias");
938 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
939 break;
940 }
941 if (threshold == (XMLTreeInfo *) NULL)
942 return(map);
943 description=GetXMLTreeChild(threshold,"description");
944 if (description == (XMLTreeInfo *) NULL)
945 {
946 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
947 "XmlMissingElement", "<description>, map \"%s\"",map_id);
948 thresholds=DestroyXMLTree(thresholds);
949 return(map);
950 }
951 levels=GetXMLTreeChild(threshold,"levels");
952 if (levels == (XMLTreeInfo *) NULL)
953 {
954 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
955 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
956 thresholds=DestroyXMLTree(thresholds);
957 return(map);
958 }
959 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
960 if (map == (ThresholdMap *) NULL)
961 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
962 map->map_id=(char *) NULL;
963 map->description=(char *) NULL;
964 map->levels=(ssize_t *) NULL;
965 attribute=GetXMLTreeAttribute(threshold,"map");
966 if (attribute != (char *) NULL)
967 map->map_id=ConstantString(attribute);
968 content=GetXMLTreeContent(description);
969 if (content != (char *) NULL)
970 map->description=ConstantString(content);
971 attribute=GetXMLTreeAttribute(levels,"width");
972 if (attribute == (char *) NULL)
973 {
974 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
975 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
976 thresholds=DestroyXMLTree(thresholds);
977 map=DestroyThresholdMap(map);
978 return(map);
979 }
980 map->width=StringToUnsignedLong(attribute);
981 if (map->width == 0)
982 {
983 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
984 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
985 thresholds=DestroyXMLTree(thresholds);
986 map=DestroyThresholdMap(map);
987 return(map);
988 }
989 attribute=GetXMLTreeAttribute(levels,"height");
990 if (attribute == (char *) NULL)
991 {
992 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
993 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
994 thresholds=DestroyXMLTree(thresholds);
995 map=DestroyThresholdMap(map);
996 return(map);
997 }
998 map->height=StringToUnsignedLong(attribute);
999 if (map->height == 0)
1000 {
1001 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1002 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1003 thresholds=DestroyXMLTree(thresholds);
1004 map=DestroyThresholdMap(map);
1005 return(map);
1006 }
1007 attribute=GetXMLTreeAttribute(levels,"divisor");
1008 if (attribute == (char *) NULL)
1009 {
1010 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1011 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1012 thresholds=DestroyXMLTree(thresholds);
1013 map=DestroyThresholdMap(map);
1014 return(map);
1015 }
1016 map->divisor=(ssize_t) StringToLong(attribute);
1017 if (map->divisor < 2)
1018 {
1019 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1020 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1021 thresholds=DestroyXMLTree(thresholds);
1022 map=DestroyThresholdMap(map);
1023 return(map);
1024 }
1025 content=GetXMLTreeContent(levels);
1026 if (content == (char *) NULL)
1027 {
1028 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1029 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1030 thresholds=DestroyXMLTree(thresholds);
1031 map=DestroyThresholdMap(map);
1032 return(map);
1033 }
1034 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1035 sizeof(*map->levels));
1036 if (map->levels == (ssize_t *) NULL)
1037 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1038 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1039 {
1040 map->levels[i]=(ssize_t) strtol(content,&p,10);
1041 if (p == content)
1042 {
1043 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1044 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1045 thresholds=DestroyXMLTree(thresholds);
1046 map=DestroyThresholdMap(map);
1047 return(map);
1048 }
1049 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1050 {
1051 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1052 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1053 (double) map->levels[i],map_id);
1054 thresholds=DestroyXMLTree(thresholds);
1055 map=DestroyThresholdMap(map);
1056 return(map);
1057 }
1058 content=p;
1059 }
1060 value=(double) strtol(content,&p,10);
1061 (void) value;
1062 if (p != content)
1063 {
1064 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1065 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1066 thresholds=DestroyXMLTree(thresholds);
1067 map=DestroyThresholdMap(map);
1068 return(map);
1069 }
1070 thresholds=DestroyXMLTree(thresholds);
cristy3ed852e2009-09-05 21:47:34 +00001071 return(map);
1072}
1073
1074/*
1075%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1076% %
1077% %
1078% %
1079+ L i s t T h r e s h o l d M a p F i l e %
1080% %
1081% %
1082% %
1083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1084%
1085% ListThresholdMapFile() lists the threshold maps and their descriptions
1086% in the given XML file data.
1087%
1088% The format of the ListThresholdMaps method is:
1089%
1090% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1091% const char *filename,ExceptionInfo *exception)
1092%
1093% A description of each parameter follows.
1094%
1095% o file: An pointer to the output FILE.
1096%
1097% o xml: The threshold map list in XML format.
1098%
1099% o filename: The threshold map XML filename.
1100%
1101% o exception: return any errors or warnings in this structure.
1102%
1103*/
1104MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1105 const char *filename,ExceptionInfo *exception)
1106{
cristy5f9f2462011-09-28 23:37:58 +00001107 const char
1108 *alias,
1109 *content,
1110 *map;
1111
1112 XMLTreeInfo
1113 *description,
1114 *threshold,
1115 *thresholds;
cristy3ed852e2009-09-05 21:47:34 +00001116
1117 assert( xml != (char *)NULL );
1118 assert( file != (FILE *)NULL );
cristy3ed852e2009-09-05 21:47:34 +00001119 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1120 "Loading threshold map file \"%s\" ...",filename);
1121 thresholds=NewXMLTree(xml,exception);
1122 if ( thresholds == (XMLTreeInfo *)NULL )
1123 return(MagickFalse);
cristy1e604812011-05-19 18:07:50 +00001124 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1125 (void) FormatLocaleFile(file,
1126 "----------------------------------------------------\n");
cristy5f9f2462011-09-28 23:37:58 +00001127 threshold=GetXMLTreeChild(thresholds,"threshold");
cristyb9eb87b2011-09-29 01:15:19 +00001128 for ( ; threshold != (XMLTreeInfo *) NULL;
1129 threshold=GetNextXMLTreeTag(threshold))
cristy3ed852e2009-09-05 21:47:34 +00001130 {
cristy5f9f2462011-09-28 23:37:58 +00001131 map=GetXMLTreeAttribute(threshold,"map");
1132 if (map == (char *) NULL)
1133 {
1134 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1135 "XmlMissingAttribute", "<map>");
1136 thresholds=DestroyXMLTree(thresholds);
1137 return(MagickFalse);
1138 }
1139 alias=GetXMLTreeAttribute(threshold,"alias");
cristy3ed852e2009-09-05 21:47:34 +00001140 description=GetXMLTreeChild(threshold,"description");
cristy5f9f2462011-09-28 23:37:58 +00001141 if (description == (XMLTreeInfo *) NULL)
1142 {
1143 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyb9eb87b2011-09-29 01:15:19 +00001144 "XmlMissingElement", "<description>, map \"%s\"",map);
cristy5f9f2462011-09-28 23:37:58 +00001145 thresholds=DestroyXMLTree(thresholds);
1146 return(MagickFalse);
1147 }
cristy3ed852e2009-09-05 21:47:34 +00001148 content=GetXMLTreeContent(description);
cristy5f9f2462011-09-28 23:37:58 +00001149 if (content == (char *) NULL)
1150 {
1151 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1152 "XmlMissingContent", "<description>, map \"%s\"", map);
1153 thresholds=DestroyXMLTree(thresholds);
1154 return(MagickFalse);
1155 }
cristy1e604812011-05-19 18:07:50 +00001156 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1157 content);
cristy3ed852e2009-09-05 21:47:34 +00001158 }
1159 thresholds=DestroyXMLTree(thresholds);
1160 return(MagickTrue);
1161}
1162
1163/*
1164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1165% %
1166% %
1167% %
1168% L i s t T h r e s h o l d M a p s %
1169% %
1170% %
1171% %
1172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1173%
1174% ListThresholdMaps() lists the threshold maps and their descriptions
1175% as defined by "threshold.xml" to a file.
1176%
1177% The format of the ListThresholdMaps method is:
1178%
1179% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1180%
1181% A description of each parameter follows.
1182%
1183% o file: An pointer to the output FILE.
1184%
1185% o exception: return any errors or warnings in this structure.
1186%
1187*/
1188MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1189 ExceptionInfo *exception)
1190{
1191 const StringInfo
1192 *option;
1193
1194 LinkedListInfo
1195 *options;
1196
1197 MagickStatusType
1198 status;
1199
1200 status=MagickFalse;
cristy5f9f2462011-09-28 23:37:58 +00001201 if (file == (FILE *) NULL)
1202 file=stdout;
cristy3ed852e2009-09-05 21:47:34 +00001203 options=GetConfigureOptions(ThresholdsFilename,exception);
cristy1e604812011-05-19 18:07:50 +00001204 (void) FormatLocaleFile(file,
1205 "\n Threshold Maps for Ordered Dither Operations\n");
cristy5f9f2462011-09-28 23:37:58 +00001206 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1207 (const StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001208 {
cristyb51dff52011-05-19 16:55:47 +00001209 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
cristy3ed852e2009-09-05 21:47:34 +00001210 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1211 GetStringInfoPath(option),exception);
1212 }
1213 options=DestroyConfigureOptions(options);
1214 return(status != 0 ? MagickTrue : MagickFalse);
1215}
1216
1217/*
1218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1219% %
1220% %
1221% %
cristy3ed852e2009-09-05 21:47:34 +00001222% O r d e r e d P o s t e r i z e I m a g e %
1223% %
1224% %
1225% %
1226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1227%
1228% OrderedPosterizeImage() will perform a ordered dither based on a number
1229% of pre-defined dithering threshold maps, but over multiple intensity
1230% levels, which can be different for different channels, according to the
1231% input argument.
1232%
1233% The format of the OrderedPosterizeImage method is:
1234%
1235% MagickBooleanType OrderedPosterizeImage(Image *image,
1236% const char *threshold_map,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001237%
1238% A description of each parameter follows:
1239%
1240% o image: the image.
1241%
cristy3ed852e2009-09-05 21:47:34 +00001242% o threshold_map: A string containing the name of the threshold dither
1243% map to use, followed by zero or more numbers representing the number
1244% of color levels tho dither between.
1245%
cristyf998fb32011-04-27 23:00:47 +00001246% Any level number less than 2 will be equivalent to 2, and means only
cristy3ed852e2009-09-05 21:47:34 +00001247% binary dithering will be applied to each color channel.
1248%
1249% No numbers also means a 2 level (bitmap) dither will be applied to all
1250% channels, while a single number is the number of levels applied to each
1251% channel in sequence. More numbers will be applied in turn to each of
1252% the color channels.
1253%
1254% For example: "o3x3,6" will generate a 6 level posterization of the
1255% image with a ordered 3x3 diffused pixel dither being applied between
1256% each level. While checker,8,8,4 will produce a 332 colormaped image
1257% with only a single checkerboard hash pattern (50% grey) between each
1258% color level, to basically double the number of color levels with
1259% a bare minimim of dithering.
1260%
1261% o exception: return any errors or warnings in this structure.
1262%
1263*/
1264MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1265 const char *threshold_map,ExceptionInfo *exception)
1266{
cristy3ed852e2009-09-05 21:47:34 +00001267#define DitherImageTag "Dither/Image"
1268
cristyc4c8d132010-01-07 01:58:38 +00001269 CacheView
1270 *image_view;
1271
cristy4e0b82a2011-09-29 12:47:44 +00001272 char
1273 token[MaxTextExtent];
1274
1275 const char
1276 *p;
cristy3ed852e2009-09-05 21:47:34 +00001277
1278 MagickBooleanType
1279 status;
1280
cristy5f959472010-05-27 22:19:46 +00001281 MagickOffsetType
1282 progress;
1283
cristya19f1d72012-08-07 18:24:38 +00001284 double
cristy5f95f4f2011-10-23 01:01:01 +00001285 levels[CompositePixelChannel];
cristy4e0b82a2011-09-29 12:47:44 +00001286
1287 register ssize_t
1288 i;
1289
cristy5f959472010-05-27 22:19:46 +00001290 ssize_t
1291 y;
1292
cristy3ed852e2009-09-05 21:47:34 +00001293 ThresholdMap
1294 *map;
1295
cristy3ed852e2009-09-05 21:47:34 +00001296 assert(image != (Image *) NULL);
1297 assert(image->signature == MagickSignature);
1298 if (image->debug != MagickFalse)
1299 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1300 assert(exception != (ExceptionInfo *) NULL);
1301 assert(exception->signature == MagickSignature);
1302 if (threshold_map == (const char *) NULL)
1303 return(MagickTrue);
cristy4e0b82a2011-09-29 12:47:44 +00001304 p=(char *) threshold_map;
1305 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1306 (*p != '\0'))
1307 p++;
1308 threshold_map=p;
1309 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1310 (*p != '\0'))
cristy3ed852e2009-09-05 21:47:34 +00001311 {
cristy4e0b82a2011-09-29 12:47:44 +00001312 if ((p-threshold_map) >= (MaxTextExtent-1))
1313 break;
1314 token[p-threshold_map]=(*p);
1315 p++;
cristy3ed852e2009-09-05 21:47:34 +00001316 }
cristy4e0b82a2011-09-29 12:47:44 +00001317 token[p-threshold_map]='\0';
1318 map=GetThresholdMap(token,exception);
1319 if (map == (ThresholdMap *) NULL)
1320 {
1321 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1322 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
cristy574cc262011-08-05 01:23:58 +00001323 return(MagickFalse);
cristy4e0b82a2011-09-29 12:47:44 +00001324 }
1325 for (i=0; i < MaxPixelChannels; i++)
1326 levels[i]=2.0;
1327 p=strchr((char *) threshold_map,',');
1328 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1329 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1330 {
1331 GetMagickToken(p,&p,token);
1332 if (*token == ',')
1333 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001334 levels[i]=StringToDouble(token,(char **) NULL);
cristy4e0b82a2011-09-29 12:47:44 +00001335 }
1336 for (i=0; i < MaxPixelChannels; i++)
1337 if (fabs(levels[i]) >= 1)
1338 levels[i]-=1.0;
1339 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1340 return(MagickFalse);
1341 status=MagickTrue;
1342 progress=0;
cristydb070952012-04-20 14:33:00 +00001343 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001344#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001345 #pragma omp parallel for schedule(static,8) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +00001346 dynamic_number_threads(image,image->columns,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00001347#endif
cristy4e0b82a2011-09-29 12:47:44 +00001348 for (y=0; y < (ssize_t) image->rows; y++)
1349 {
1350 register ssize_t
1351 x;
1352
1353 register Quantum
1354 *restrict q;
1355
1356 if (status == MagickFalse)
1357 continue;
1358 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1359 if (q == (Quantum *) NULL)
1360 {
1361 status=MagickFalse;
1362 continue;
1363 }
1364 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001365 {
cristybb503372010-05-27 20:51:26 +00001366 register ssize_t
cristy4e0b82a2011-09-29 12:47:44 +00001367 i;
cristy3ed852e2009-09-05 21:47:34 +00001368
cristy4e0b82a2011-09-29 12:47:44 +00001369 ssize_t
1370 n;
cristy3ed852e2009-09-05 21:47:34 +00001371
cristy4e0b82a2011-09-29 12:47:44 +00001372 n=0;
cristy10a6c612012-01-29 21:41:05 +00001373 if (GetPixelMask(image,q) != 0)
1374 {
1375 q+=GetPixelChannels(image);
1376 continue;
1377 }
cristy4e0b82a2011-09-29 12:47:44 +00001378 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +00001379 {
cristyabace412011-12-11 15:56:53 +00001380 PixelChannel
1381 channel;
1382
cristy4e0b82a2011-09-29 12:47:44 +00001383 PixelTrait
1384 traits;
1385
1386 ssize_t
cristy3f5d8152011-09-29 13:00:19 +00001387 level,
1388 threshold;
cristy3ed852e2009-09-05 21:47:34 +00001389
cristyabace412011-12-11 15:56:53 +00001390 channel=GetPixelChannelMapChannel(image,i);
1391 traits=GetPixelChannelMapTraits(image,channel);
cristy4e0b82a2011-09-29 12:47:44 +00001392 if ((traits & UpdatePixelTrait) == 0)
1393 continue;
cristy3f5d8152011-09-29 13:00:19 +00001394 if (fabs(levels[n++]) < MagickEpsilon)
1395 continue;
1396 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1397 level=threshold/(map->divisor-1);
1398 threshold-=level*(map->divisor-1);
cristyada285b2012-07-07 19:00:46 +00001399 q[i]=ClampToQuantum((double) (level+(threshold >=
cristye42f6582012-02-11 17:59:50 +00001400 map->levels[(x % map->width)+map->width*(y % map->height)]))*
1401 QuantumRange/levels[n]);
cristy4e0b82a2011-09-29 12:47:44 +00001402 n++;
cristy3ed852e2009-09-05 21:47:34 +00001403 }
cristy4e0b82a2011-09-29 12:47:44 +00001404 q+=GetPixelChannels(image);
1405 }
1406 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1407 status=MagickFalse;
1408 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1409 {
1410 MagickBooleanType
1411 proceed;
cristy3ed852e2009-09-05 21:47:34 +00001412
cristyb5d5f722009-11-04 03:03:49 +00001413#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001414 #pragma omp critical (MagickCore_OrderedPosterizeImage)
cristy3ed852e2009-09-05 21:47:34 +00001415#endif
cristy4e0b82a2011-09-29 12:47:44 +00001416 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1417 if (proceed == MagickFalse)
1418 status=MagickFalse;
1419 }
cristy3ed852e2009-09-05 21:47:34 +00001420 }
cristy4e0b82a2011-09-29 12:47:44 +00001421 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00001422 map=DestroyThresholdMap(map);
1423 return(MagickTrue);
1424}
1425
1426/*
1427%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1428% %
1429% %
1430% %
1431% R a n d o m T h r e s h o l d I m a g e %
1432% %
1433% %
1434% %
1435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1436%
1437% RandomThresholdImage() changes the value of individual pixels based on the
1438% intensity of each pixel compared to a random threshold. The result is a
1439% low-contrast, two color image.
1440%
1441% The format of the RandomThresholdImage method is:
1442%
cristyf4ad9df2011-07-08 16:49:03 +00001443% MagickBooleanType RandomThresholdImage(Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001444% const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001445%
1446% A description of each parameter follows:
1447%
1448% o image: the image.
1449%
cristy3ed852e2009-09-05 21:47:34 +00001450% o thresholds: a geometry string containing low,high thresholds. If the
1451% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1452% is performed instead.
1453%
1454% o exception: return any errors or warnings in this structure.
1455%
1456*/
cristy3ed852e2009-09-05 21:47:34 +00001457MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1458 const char *thresholds,ExceptionInfo *exception)
1459{
cristy3ed852e2009-09-05 21:47:34 +00001460#define ThresholdImageTag "Threshold/Image"
1461
cristyfa112112010-01-04 17:48:07 +00001462 CacheView
1463 *image_view;
1464
cristy3ed852e2009-09-05 21:47:34 +00001465 GeometryInfo
1466 geometry_info;
1467
1468 MagickStatusType
1469 flags;
1470
cristy3ed852e2009-09-05 21:47:34 +00001471 MagickBooleanType
1472 status;
1473
cristy5f959472010-05-27 22:19:46 +00001474 MagickOffsetType
1475 progress;
1476
cristy4c08aed2011-07-01 19:47:50 +00001477 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001478 threshold;
1479
cristya19f1d72012-08-07 18:24:38 +00001480 double
cristy3ed852e2009-09-05 21:47:34 +00001481 min_threshold,
1482 max_threshold;
1483
1484 RandomInfo
cristyfa112112010-01-04 17:48:07 +00001485 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00001486
cristy5f959472010-05-27 22:19:46 +00001487 ssize_t
1488 y;
1489
cristy57340e02012-05-05 00:53:23 +00001490 unsigned long
1491 key;
1492
cristy3ed852e2009-09-05 21:47:34 +00001493 assert(image != (Image *) NULL);
1494 assert(image->signature == MagickSignature);
1495 if (image->debug != MagickFalse)
1496 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1497 assert(exception != (ExceptionInfo *) NULL);
1498 assert(exception->signature == MagickSignature);
1499 if (thresholds == (const char *) NULL)
1500 return(MagickTrue);
cristye7452652012-04-14 01:34:21 +00001501 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1502 return(MagickFalse);
cristy4c08aed2011-07-01 19:47:50 +00001503 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001504 min_threshold=0.0;
cristya19f1d72012-08-07 18:24:38 +00001505 max_threshold=(double) QuantumRange;
cristy3ed852e2009-09-05 21:47:34 +00001506 flags=ParseGeometry(thresholds,&geometry_info);
1507 min_threshold=geometry_info.rho;
1508 max_threshold=geometry_info.sigma;
1509 if ((flags & SigmaValue) == 0)
1510 max_threshold=min_threshold;
1511 if (strchr(thresholds,'%') != (char *) NULL)
1512 {
cristya19f1d72012-08-07 18:24:38 +00001513 max_threshold*=(double) (0.01*QuantumRange);
1514 min_threshold*=(double) (0.01*QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +00001515 }
cristy3ed852e2009-09-05 21:47:34 +00001516 /*
1517 Random threshold image.
1518 */
1519 status=MagickTrue;
1520 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00001521 random_info=AcquireRandomInfoThreadSet();
cristy57340e02012-05-05 00:53:23 +00001522 key=GetRandomSecretKey(random_info[0]);
cristydb070952012-04-20 14:33:00 +00001523 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001524#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy57340e02012-05-05 00:53:23 +00001525 #pragma omp parallel for schedule(static,8) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +00001526 dynamic_number_threads(image,image->columns,image->rows,key == ~0UL)
cristy3ed852e2009-09-05 21:47:34 +00001527#endif
cristybb503372010-05-27 20:51:26 +00001528 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001529 {
cristy5c9e6f22010-09-17 17:31:01 +00001530 const int
1531 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001532
cristy4c08aed2011-07-01 19:47:50 +00001533 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001534 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001535
cristy5c9e6f22010-09-17 17:31:01 +00001536 register ssize_t
1537 x;
1538
cristy3ed852e2009-09-05 21:47:34 +00001539 if (status == MagickFalse)
1540 continue;
1541 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001542 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001543 {
1544 status=MagickFalse;
1545 continue;
1546 }
cristybb503372010-05-27 20:51:26 +00001547 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001548 {
cristy5f9f2462011-09-28 23:37:58 +00001549 register ssize_t
1550 i;
1551
cristy10a6c612012-01-29 21:41:05 +00001552 if (GetPixelMask(image,q) != 0)
1553 {
1554 q+=GetPixelChannels(image);
1555 continue;
1556 }
cristy5f9f2462011-09-28 23:37:58 +00001557 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1558 {
cristya19f1d72012-08-07 18:24:38 +00001559 double
cristy5f9f2462011-09-28 23:37:58 +00001560 threshold;
1561
cristyabace412011-12-11 15:56:53 +00001562 PixelChannel
1563 channel;
1564
cristy5f9f2462011-09-28 23:37:58 +00001565 PixelTrait
1566 traits;
1567
cristyabace412011-12-11 15:56:53 +00001568 channel=GetPixelChannelMapChannel(image,i);
1569 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +00001570 if ((traits & UpdatePixelTrait) == 0)
1571 continue;
cristya19f1d72012-08-07 18:24:38 +00001572 if ((double) q[i] < min_threshold)
cristy5f9f2462011-09-28 23:37:58 +00001573 threshold=min_threshold;
1574 else
cristya19f1d72012-08-07 18:24:38 +00001575 if ((double) q[i] > max_threshold)
cristy5f9f2462011-09-28 23:37:58 +00001576 threshold=max_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001577 else
cristya19f1d72012-08-07 18:24:38 +00001578 threshold=(double) (QuantumRange*
cristy5f9f2462011-09-28 23:37:58 +00001579 GetPseudoRandomValue(random_info[id]));
cristya19f1d72012-08-07 18:24:38 +00001580 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
cristy5f9f2462011-09-28 23:37:58 +00001581 }
cristyed231572011-07-14 02:18:59 +00001582 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001583 }
1584 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1585 status=MagickFalse;
1586 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1587 {
1588 MagickBooleanType
1589 proceed;
1590
cristyb5d5f722009-11-04 03:03:49 +00001591#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001592 #pragma omp critical (MagickCore_RandomThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001593#endif
1594 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1595 image->rows);
1596 if (proceed == MagickFalse)
1597 status=MagickFalse;
1598 }
1599 }
1600 image_view=DestroyCacheView(image_view);
1601 random_info=DestroyRandomInfoThreadSet(random_info);
1602 return(status);
1603}
1604
1605/*
1606%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1607% %
1608% %
1609% %
1610% W h i t e T h r e s h o l d I m a g e %
1611% %
1612% %
1613% %
1614%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1615%
1616% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001617% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001618% unchanged.
1619%
1620% The format of the WhiteThresholdImage method is:
1621%
cristyf4ad9df2011-07-08 16:49:03 +00001622% MagickBooleanType WhiteThresholdImage(Image *image,
1623% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001624%
1625% A description of each parameter follows:
1626%
1627% o image: the image.
1628%
cristy3ed852e2009-09-05 21:47:34 +00001629% o threshold: Define the threshold value.
1630%
1631% o exception: return any errors or warnings in this structure.
1632%
1633*/
1634MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +00001635 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001636{
1637#define ThresholdImageTag "Threshold/Image"
1638
cristy5f959472010-05-27 22:19:46 +00001639 CacheView
1640 *image_view;
1641
cristy3ed852e2009-09-05 21:47:34 +00001642 GeometryInfo
1643 geometry_info;
1644
cristy3ed852e2009-09-05 21:47:34 +00001645 MagickBooleanType
1646 status;
1647
cristy5f959472010-05-27 22:19:46 +00001648 MagickOffsetType
1649 progress;
1650
cristyd6803382012-04-10 01:41:25 +00001651 PixelInfo
cristya12d8ba2012-04-29 16:33:41 +00001652 threshold;
cristy5f9f2462011-09-28 23:37:58 +00001653
cristy3ed852e2009-09-05 21:47:34 +00001654 MagickStatusType
1655 flags;
1656
cristy5f959472010-05-27 22:19:46 +00001657 ssize_t
1658 y;
cristy3ed852e2009-09-05 21:47:34 +00001659
1660 assert(image != (Image *) NULL);
1661 assert(image->signature == MagickSignature);
1662 if (image->debug != MagickFalse)
1663 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1664 if (thresholds == (const char *) NULL)
1665 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +00001666 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001667 return(MagickFalse);
cristy23e55c02012-04-10 01:21:56 +00001668 if (IsGrayColorspace(image->colorspace) != MagickFalse)
cristyb09db112012-07-11 12:04:31 +00001669 (void) TransformImageColorspace(image,RGBColorspace,exception);
cristya12d8ba2012-04-29 16:33:41 +00001670 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001671 flags=ParseGeometry(thresholds,&geometry_info);
cristya12d8ba2012-04-29 16:33:41 +00001672 threshold.red=geometry_info.rho;
1673 threshold.green=geometry_info.rho;
1674 threshold.blue=geometry_info.rho;
1675 threshold.black=geometry_info.rho;
1676 threshold.alpha=100.0;
cristy5f9f2462011-09-28 23:37:58 +00001677 if ((flags & SigmaValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001678 threshold.green=geometry_info.sigma;
cristy5f9f2462011-09-28 23:37:58 +00001679 if ((flags & XiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001680 threshold.blue=geometry_info.xi;
cristy5f9f2462011-09-28 23:37:58 +00001681 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001682 threshold.alpha=geometry_info.psi;
1683 if (threshold.colorspace == CMYKColorspace)
cristyd6803382012-04-10 01:41:25 +00001684 {
1685 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001686 threshold.black=geometry_info.psi;
cristyd6803382012-04-10 01:41:25 +00001687 if ((flags & ChiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001688 threshold.alpha=geometry_info.chi;
1689 }
1690 if ((flags & PercentValue) != 0)
1691 {
1692 threshold.red*=(QuantumRange/100.0);
1693 threshold.green*=(QuantumRange/100.0);
1694 threshold.blue*=(QuantumRange/100.0);
1695 threshold.black*=(QuantumRange/100.0);
1696 threshold.alpha*=(QuantumRange/100.0);
cristyd6803382012-04-10 01:41:25 +00001697 }
cristy3ed852e2009-09-05 21:47:34 +00001698 /*
1699 White threshold image.
1700 */
1701 status=MagickTrue;
1702 progress=0;
cristydb070952012-04-20 14:33:00 +00001703 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001704#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001705 #pragma omp parallel for schedule(static,8) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +00001706 dynamic_number_threads(image,image->columns,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00001707#endif
cristybb503372010-05-27 20:51:26 +00001708 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001709 {
cristybb503372010-05-27 20:51:26 +00001710 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001711 x;
1712
cristy4c08aed2011-07-01 19:47:50 +00001713 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001714 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001715
1716 if (status == MagickFalse)
1717 continue;
1718 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001719 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001720 {
1721 status=MagickFalse;
1722 continue;
1723 }
cristybb503372010-05-27 20:51:26 +00001724 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001725 {
cristy81629aa2012-07-12 20:08:52 +00001726 double
1727 pixel;
1728
cristy188f29a2012-06-24 19:09:53 +00001729 register ssize_t
1730 i;
1731
cristy10a6c612012-01-29 21:41:05 +00001732 if (GetPixelMask(image,q) != 0)
1733 {
1734 q+=GetPixelChannels(image);
1735 continue;
1736 }
cristy81629aa2012-07-12 20:08:52 +00001737 pixel=(double) GetPixelIntensity(image,q);
cristy188f29a2012-06-24 19:09:53 +00001738 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1739 {
1740 PixelChannel
1741 channel;
1742
1743 PixelTrait
1744 traits;
1745
1746 channel=GetPixelChannelMapChannel(image,i);
1747 traits=GetPixelChannelMapTraits(image,channel);
1748 if ((traits & UpdatePixelTrait) == 0)
1749 continue;
cristy81629aa2012-07-12 20:08:52 +00001750 if (image->channel_mask != DefaultChannels)
1751 pixel=q[i];
1752 if (pixel > GetPixelInfoChannel(&threshold,channel))
cristy188f29a2012-06-24 19:09:53 +00001753 q[i]=QuantumRange;
1754 }
cristyed231572011-07-14 02:18:59 +00001755 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001756 }
1757 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1758 status=MagickFalse;
1759 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1760 {
1761 MagickBooleanType
1762 proceed;
1763
cristyb5d5f722009-11-04 03:03:49 +00001764#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya12d8ba2012-04-29 16:33:41 +00001765 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001766#endif
1767 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1768 image->rows);
1769 if (proceed == MagickFalse)
1770 status=MagickFalse;
1771 }
1772 }
1773 image_view=DestroyCacheView(image_view);
1774 return(status);
1775}