blob: af85140139b1b3e50420c68091aa1508e1566021 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% TTTTT H H RRRR EEEEE SSSSS H H OOO L DDDD %
7% T H H R R E SS H H O O L D D %
8% T HHHHH RRRR EEE SSS HHHHH O O L D D %
9% T H H R R E SS H H O O L D D %
10% T H H R R EEEEE SSSSS H H OOO LLLLL DDDD %
11% %
12% %
13% MagickCore Image Threshold Methods %
14% %
15% Software Design %
16% John Cristy %
17% October 1996 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/property.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colormap.h"
50#include "MagickCore/colorspace.h"
51#include "MagickCore/configure.h"
52#include "MagickCore/constitute.h"
53#include "MagickCore/decorate.h"
54#include "MagickCore/draw.h"
55#include "MagickCore/enhance.h"
56#include "MagickCore/exception.h"
57#include "MagickCore/exception-private.h"
58#include "MagickCore/effect.h"
59#include "MagickCore/fx.h"
60#include "MagickCore/gem.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/monitor.h"
67#include "MagickCore/monitor-private.h"
68#include "MagickCore/montage.h"
69#include "MagickCore/option.h"
70#include "MagickCore/pixel-accessor.h"
71#include "MagickCore/quantize.h"
72#include "MagickCore/quantum.h"
73#include "MagickCore/random_.h"
74#include "MagickCore/random-private.h"
75#include "MagickCore/resize.h"
76#include "MagickCore/resource_.h"
77#include "MagickCore/segment.h"
78#include "MagickCore/shear.h"
79#include "MagickCore/signature-private.h"
80#include "MagickCore/string_.h"
81#include "MagickCore/string-private.h"
82#include "MagickCore/thread-private.h"
83#include "MagickCore/threshold.h"
cristy4e0b82a2011-09-29 12:47:44 +000084#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000085#include "MagickCore/transform.h"
86#include "MagickCore/xml-tree.h"
cristy433d1182011-09-04 13:38:52 +000087#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000088
89/*
90 Define declarations.
91*/
92#define ThresholdsFilename "thresholds.xml"
93
94/*
95 Typedef declarations.
96*/
97struct _ThresholdMap
98{
99 char
100 *map_id,
101 *description;
102
cristybb503372010-05-27 20:51:26 +0000103 size_t
cristy3ed852e2009-09-05 21:47:34 +0000104 width,
105 height;
106
cristybb503372010-05-27 20:51:26 +0000107 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000108 divisor,
109 *levels;
110};
111
112/*
cristybd0ebf02011-09-29 01:19:42 +0000113 Forward declarations.
114*/
115static ThresholdMap
116 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
117
118/*
cristy3ed852e2009-09-05 21:47:34 +0000119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120% %
121% %
122% %
123% A d a p t i v e T h r e s h o l d I m a g e %
124% %
125% %
126% %
127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
128%
129% AdaptiveThresholdImage() selects an individual threshold for each pixel
130% based on the range of intensity values in its local neighborhood. This
131% allows for thresholding of an image whose global intensity histogram
132% doesn't contain distinctive peaks.
133%
134% The format of the AdaptiveThresholdImage method is:
135%
cristyde5cc632011-07-18 14:47:00 +0000136% Image *AdaptiveThresholdImage(const Image *image,const size_t width,
137% const size_t height,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000138%
139% A description of each parameter follows:
140%
141% o image: the image.
142%
143% o width: the width of the local neighborhood.
144%
145% o height: the height of the local neighborhood.
146%
cristyde5cc632011-07-18 14:47:00 +0000147% o bias: the mean bias.
cristy3ed852e2009-09-05 21:47:34 +0000148%
149% o exception: return any errors or warnings in this structure.
150%
151*/
152MagickExport Image *AdaptiveThresholdImage(const Image *image,
cristyde5cc632011-07-18 14:47:00 +0000153 const size_t width,const size_t height,const double bias,
cristy3ed852e2009-09-05 21:47:34 +0000154 ExceptionInfo *exception)
155{
cristyde5cc632011-07-18 14:47:00 +0000156#define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
cristy3ed852e2009-09-05 21:47:34 +0000157
cristyc4c8d132010-01-07 01:58:38 +0000158 CacheView
159 *image_view,
160 *threshold_view;
161
cristy3ed852e2009-09-05 21:47:34 +0000162 Image
163 *threshold_image;
164
cristy3ed852e2009-09-05 21:47:34 +0000165 MagickBooleanType
166 status;
167
cristy5f959472010-05-27 22:19:46 +0000168 MagickOffsetType
169 progress;
170
cristyde5cc632011-07-18 14:47:00 +0000171 MagickSizeType
cristy3ed852e2009-09-05 21:47:34 +0000172 number_pixels;
173
cristy5f959472010-05-27 22:19:46 +0000174 ssize_t
175 y;
176
cristyde5cc632011-07-18 14:47:00 +0000177 /*
178 Initialize threshold image attributes.
179 */
180 assert(image != (Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000181 assert(image->signature == MagickSignature);
182 if (image->debug != MagickFalse)
183 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
184 assert(exception != (ExceptionInfo *) NULL);
185 assert(exception->signature == MagickSignature);
cristyde5cc632011-07-18 14:47:00 +0000186 if ((width % 2) == 0)
187 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
188 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
189 exception);
cristy3ed852e2009-09-05 21:47:34 +0000190 if (threshold_image == (Image *) NULL)
191 return((Image *) NULL);
cristyb9eb87b2011-09-29 01:15:19 +0000192 status=SetImageStorageClass(threshold_image,DirectClass,exception);
193 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000194 {
cristy3ed852e2009-09-05 21:47:34 +0000195 threshold_image=DestroyImage(threshold_image);
196 return((Image *) NULL);
197 }
198 /*
cristyde5cc632011-07-18 14:47:00 +0000199 Threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000200 */
201 status=MagickTrue;
202 progress=0;
cristyde5cc632011-07-18 14:47:00 +0000203 number_pixels=(MagickSizeType) width*height;
cristy3ed852e2009-09-05 21:47:34 +0000204 image_view=AcquireCacheView(image);
205 threshold_view=AcquireCacheView(threshold_image);
cristyb5d5f722009-11-04 03:03:49 +0000206#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd36a25e2012-01-18 14:30:53 +0000207 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000208#endif
cristybb503372010-05-27 20:51:26 +0000209 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000210 {
cristy4c08aed2011-07-01 19:47:50 +0000211 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000212 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000213
cristy4c08aed2011-07-01 19:47:50 +0000214 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000215 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000216
cristyde5cc632011-07-18 14:47:00 +0000217 register ssize_t
218 x;
219
cristyde5cc632011-07-18 14:47:00 +0000220 ssize_t
221 center;
222
cristy3ed852e2009-09-05 21:47:34 +0000223 if (status == MagickFalse)
224 continue;
cristyd99b0962010-05-29 23:14:26 +0000225 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
cristyde5cc632011-07-18 14:47:00 +0000226 (height/2L),image->columns+width,height,exception);
227 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
228 1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000229 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000230 {
231 status=MagickFalse;
232 continue;
233 }
cristy5f9f2462011-09-28 23:37:58 +0000234 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
cristya0312c92011-07-23 21:04:30 +0000235 GetPixelChannels(image)*(width/2);
cristybb503372010-05-27 20:51:26 +0000236 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000237 {
cristybb503372010-05-27 20:51:26 +0000238 register ssize_t
cristyde5cc632011-07-18 14:47:00 +0000239 i;
cristy3ed852e2009-09-05 21:47:34 +0000240
cristya0312c92011-07-23 21:04:30 +0000241 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000242 {
cristyde5cc632011-07-18 14:47:00 +0000243 MagickRealType
244 mean,
245 pixel;
246
247 PixelChannel
248 channel;
249
250 PixelTrait
251 threshold_traits,
252 traits;
253
254 register const Quantum
255 *restrict pixels;
256
257 register ssize_t
258 u;
259
260 ssize_t
261 v;
262
cristye2a912b2011-12-05 20:02:07 +0000263 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000264 traits=GetPixelChannelMapTraits(image,channel);
cristyde5cc632011-07-18 14:47:00 +0000265 threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
cristy010d7d12011-08-31 01:02:48 +0000266 if ((traits == UndefinedPixelTrait) ||
267 (threshold_traits == UndefinedPixelTrait))
cristyde5cc632011-07-18 14:47:00 +0000268 continue;
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)
cristy78778cb2012-01-17 14:48:47 +0000387 #pragma omp parallel for schedule(static,8) 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 {
cristyabace412011-12-11 15:56:53 +0000412 PixelChannel
413 channel;
414
cristy95111202011-08-09 19:41:42 +0000415 PixelTrait
416 traits;
417
cristyabace412011-12-11 15:56:53 +0000418 channel=GetPixelChannelMapChannel(image,i);
419 traits=GetPixelChannelMapTraits(image,channel);
cristy95111202011-08-09 19:41:42 +0000420 if ((traits & UpdatePixelTrait) != 0)
cristy5f9f2462011-09-28 23:37:58 +0000421 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
422 QuantumRange);
cristy95111202011-08-09 19:41:42 +0000423 }
cristy805b6a02011-08-09 00:59:35 +0000424 q+=GetPixelChannels(image);
425 }
cristy3ed852e2009-09-05 21:47:34 +0000426 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
427 status=MagickFalse;
428 if (image->progress_monitor != (MagickProgressMonitor) NULL)
429 {
430 MagickBooleanType
431 proceed;
432
cristyb5d5f722009-11-04 03:03:49 +0000433#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000434 #pragma omp critical (MagickCore_BilevelImage)
cristy3ed852e2009-09-05 21:47:34 +0000435#endif
436 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
437 image->rows);
438 if (proceed == MagickFalse)
439 status=MagickFalse;
440 }
441 }
442 image_view=DestroyCacheView(image_view);
443 return(status);
444}
445
446/*
447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
448% %
449% %
450% %
451% B l a c k T h r e s h o l d I m a g e %
452% %
453% %
454% %
455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
456%
457% BlackThresholdImage() is like ThresholdImage() but forces all pixels below
cristy4e101302009-09-17 12:49:12 +0000458% the threshold into black while leaving all pixels at or above the threshold
cristy3ed852e2009-09-05 21:47:34 +0000459% unchanged.
460%
461% The format of the BlackThresholdImage method is:
462%
cristyf4ad9df2011-07-08 16:49:03 +0000463% MagickBooleanType BlackThresholdImage(Image *image,
464% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000465%
466% A description of each parameter follows:
467%
468% o image: the image.
469%
cristy5f9f2462011-09-28 23:37:58 +0000470% o threshold: define the threshold value.
cristy3ed852e2009-09-05 21:47:34 +0000471%
472% o exception: return any errors or warnings in this structure.
473%
474*/
475MagickExport MagickBooleanType BlackThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +0000476 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000477{
478#define ThresholdImageTag "Threshold/Image"
479
cristyc4c8d132010-01-07 01:58:38 +0000480 CacheView
481 *image_view;
482
cristy3ed852e2009-09-05 21:47:34 +0000483 GeometryInfo
484 geometry_info;
485
cristy3ed852e2009-09-05 21:47:34 +0000486 MagickBooleanType
487 status;
488
cristy5f959472010-05-27 22:19:46 +0000489 MagickOffsetType
490 progress;
491
cristy5f9f2462011-09-28 23:37:58 +0000492 MagickRealType
493 threshold[5];
cristy3ed852e2009-09-05 21:47:34 +0000494
495 MagickStatusType
496 flags;
497
cristy5f9f2462011-09-28 23:37:58 +0000498 register ssize_t
499 i;
500
cristy5f959472010-05-27 22:19:46 +0000501 ssize_t
502 y;
503
cristy3ed852e2009-09-05 21:47:34 +0000504 assert(image != (Image *) NULL);
505 assert(image->signature == MagickSignature);
506 if (image->debug != MagickFalse)
507 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
508 if (thresholds == (const char *) NULL)
509 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +0000510 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000511 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000512 flags=ParseGeometry(thresholds,&geometry_info);
cristy5f9f2462011-09-28 23:37:58 +0000513 for (i=0; i < 5; i++)
514 threshold[i]=geometry_info.rho;
515 if ((flags & SigmaValue) != 0)
516 threshold[1]=geometry_info.sigma;
517 if ((flags & XiValue) != 0)
518 threshold[2]=geometry_info.xi;
519 if ((flags & PsiValue) != 0)
520 threshold[3]=geometry_info.psi;
521 if ((flags & ChiValue) != 0)
522 threshold[4]=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +0000523 if ((flags & PercentValue) != 0)
cristy5f9f2462011-09-28 23:37:58 +0000524 for (i=0; i < 5; i++)
525 threshold[i]*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +0000526 /*
cristy5f9f2462011-09-28 23:37:58 +0000527 White threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000528 */
529 status=MagickTrue;
530 progress=0;
531 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000532#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +0000533 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000534#endif
cristybb503372010-05-27 20:51:26 +0000535 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000536 {
cristybb503372010-05-27 20:51:26 +0000537 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000538 x;
539
cristy4c08aed2011-07-01 19:47:50 +0000540 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000541 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000542
543 if (status == MagickFalse)
544 continue;
545 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000546 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000547 {
548 status=MagickFalse;
549 continue;
550 }
cristybb503372010-05-27 20:51:26 +0000551 for (x=0; x < (ssize_t) image->columns; x++)
cristyb0ea1af2009-11-28 20:44:46 +0000552 {
cristy5f9f2462011-09-28 23:37:58 +0000553 register ssize_t
554 i;
555
556 ssize_t
557 n;
558
559 n=0;
560 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
561 {
cristyabace412011-12-11 15:56:53 +0000562 PixelChannel
563 channel;
564
cristy5f9f2462011-09-28 23:37:58 +0000565 PixelTrait
566 traits;
567
cristyabace412011-12-11 15:56:53 +0000568 channel=GetPixelChannelMapChannel(image,i);
569 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +0000570 if ((traits & UpdatePixelTrait) == 0)
571 continue;
572 if ((MagickRealType) q[i] < threshold[n++ % 5])
573 q[i]=QuantumRange;
574 }
cristyed231572011-07-14 02:18:59 +0000575 q+=GetPixelChannels(image);
cristyb0ea1af2009-11-28 20:44:46 +0000576 }
cristy3ed852e2009-09-05 21:47:34 +0000577 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
578 status=MagickFalse;
579 if (image->progress_monitor != (MagickProgressMonitor) NULL)
580 {
581 MagickBooleanType
582 proceed;
583
cristyb5d5f722009-11-04 03:03:49 +0000584#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy5f9f2462011-09-28 23:37:58 +0000585 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +0000586#endif
587 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
588 image->rows);
589 if (proceed == MagickFalse)
590 status=MagickFalse;
591 }
592 }
593 image_view=DestroyCacheView(image_view);
594 return(status);
595}
596
597/*
598%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
599% %
600% %
601% %
cristy1eb45dd2009-09-25 16:38:06 +0000602% C l a m p I m a g e %
603% %
604% %
605% %
606%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
607%
cristyecb0c6d2009-09-25 16:50:09 +0000608% ClampImage() restricts the color range from 0 to the quantum depth.
cristy1eb45dd2009-09-25 16:38:06 +0000609%
cristyf4ad9df2011-07-08 16:49:03 +0000610% The format of the ClampImage method is:
cristy1eb45dd2009-09-25 16:38:06 +0000611%
cristy092d71c2011-10-14 18:01:29 +0000612% MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000613%
614% A description of each parameter follows:
615%
616% o image: the image.
617%
cristy092d71c2011-10-14 18:01:29 +0000618% o exception: return any errors or warnings in this structure.
619%
cristy1eb45dd2009-09-25 16:38:06 +0000620*/
621
cristy75ffdb72010-01-07 17:40:12 +0000622static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
cristy1eb45dd2009-09-25 16:38:06 +0000623{
624#if defined(MAGICKCORE_HDRI_SUPPORT)
625 if (quantum <= 0)
626 return(0);
627 if (quantum >= QuantumRange)
628 return(QuantumRange);
629 return(quantum);
630#else
631 return(quantum);
632#endif
633}
634
cristy092d71c2011-10-14 18:01:29 +0000635MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000636{
cristy1eb45dd2009-09-25 16:38:06 +0000637#define ClampImageTag "Clamp/Image"
638
cristyc4c8d132010-01-07 01:58:38 +0000639 CacheView
640 *image_view;
641
cristy1eb45dd2009-09-25 16:38:06 +0000642 MagickBooleanType
643 status;
644
cristy5f959472010-05-27 22:19:46 +0000645 MagickOffsetType
646 progress;
647
648 ssize_t
649 y;
650
cristy1eb45dd2009-09-25 16:38:06 +0000651 assert(image != (Image *) NULL);
652 assert(image->signature == MagickSignature);
653 if (image->debug != MagickFalse)
654 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
655 if (image->storage_class == PseudoClass)
656 {
cristybb503372010-05-27 20:51:26 +0000657 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000658 i;
659
cristy101ab702011-10-13 13:06:32 +0000660 register PixelInfo
cristyc47d1f82009-11-26 01:44:43 +0000661 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000662
663 q=image->colormap;
cristybb503372010-05-27 20:51:26 +0000664 for (i=0; i < (ssize_t) image->colors; i++)
cristy1eb45dd2009-09-25 16:38:06 +0000665 {
cristy4c08aed2011-07-01 19:47:50 +0000666 q->red=ClampToUnsignedQuantum(q->red);
667 q->green=ClampToUnsignedQuantum(q->green);
668 q->blue=ClampToUnsignedQuantum(q->blue);
669 q->alpha=ClampToUnsignedQuantum(q->alpha);
cristy1eb45dd2009-09-25 16:38:06 +0000670 q++;
671 }
cristyea1a8aa2011-10-20 13:24:06 +0000672 return(SyncImage(image,exception));
cristy1eb45dd2009-09-25 16:38:06 +0000673 }
674 /*
cristy611721d2009-09-25 16:42:17 +0000675 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000676 */
677 status=MagickTrue;
678 progress=0;
cristy1eb45dd2009-09-25 16:38:06 +0000679 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000680#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +0000681 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy1eb45dd2009-09-25 16:38:06 +0000682#endif
cristybb503372010-05-27 20:51:26 +0000683 for (y=0; y < (ssize_t) image->rows; y++)
cristy1eb45dd2009-09-25 16:38:06 +0000684 {
cristybb503372010-05-27 20:51:26 +0000685 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000686 x;
687
cristy4c08aed2011-07-01 19:47:50 +0000688 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000689 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000690
691 if (status == MagickFalse)
692 continue;
693 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000694 if (q == (Quantum *) NULL)
cristy1eb45dd2009-09-25 16:38:06 +0000695 {
696 status=MagickFalse;
697 continue;
698 }
cristybb503372010-05-27 20:51:26 +0000699 for (x=0; x < (ssize_t) image->columns; x++)
cristy1eb45dd2009-09-25 16:38:06 +0000700 {
cristy5f9f2462011-09-28 23:37:58 +0000701 register ssize_t
702 i;
703
704 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
705 {
cristyabace412011-12-11 15:56:53 +0000706 PixelChannel
707 channel;
708
cristy5f9f2462011-09-28 23:37:58 +0000709 PixelTrait
710 traits;
711
cristyabace412011-12-11 15:56:53 +0000712 channel=GetPixelChannelMapChannel(image,i);
713 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +0000714 if (traits == UndefinedPixelTrait)
715 continue;
716 q[i]=ClampToUnsignedQuantum(q[i]);
717 }
cristyed231572011-07-14 02:18:59 +0000718 q+=GetPixelChannels(image);
cristy1eb45dd2009-09-25 16:38:06 +0000719 }
720 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
721 status=MagickFalse;
722 if (image->progress_monitor != (MagickProgressMonitor) NULL)
723 {
724 MagickBooleanType
725 proceed;
726
cristyb5d5f722009-11-04 03:03:49 +0000727#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000728 #pragma omp critical (MagickCore_ClampImage)
cristy1eb45dd2009-09-25 16:38:06 +0000729#endif
730 proceed=SetImageProgress(image,ClampImageTag,progress++,
731 image->rows);
732 if (proceed == MagickFalse)
733 status=MagickFalse;
734 }
735 }
736 image_view=DestroyCacheView(image_view);
737 return(status);
738}
739
740/*
741%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
742% %
743% %
744% %
cristy3ed852e2009-09-05 21:47:34 +0000745% D e s t r o y T h r e s h o l d M a p %
746% %
747% %
748% %
749%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
750%
751% DestroyThresholdMap() de-allocate the given ThresholdMap
752%
753% The format of the ListThresholdMaps method is:
754%
755% ThresholdMap *DestroyThresholdMap(Threshold *map)
756%
757% A description of each parameter follows.
758%
759% o map: Pointer to the Threshold map to destroy
760%
761*/
762MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
763{
764 assert(map != (ThresholdMap *) NULL);
765 if (map->map_id != (char *) NULL)
766 map->map_id=DestroyString(map->map_id);
767 if (map->description != (char *) NULL)
768 map->description=DestroyString(map->description);
cristybb503372010-05-27 20:51:26 +0000769 if (map->levels != (ssize_t *) NULL)
770 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
cristy3ed852e2009-09-05 21:47:34 +0000771 map=(ThresholdMap *) RelinquishMagickMemory(map);
772 return(map);
773}
774
775/*
776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
777% %
778% %
779% %
cristyb9eb87b2011-09-29 01:15:19 +0000780% G e t T h r e s h o l d M a p %
781% %
782% %
783% %
784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
785%
786% GetThresholdMap() loads and searches one or more threshold map files for the
787% map matching the given name or alias.
788%
789% The format of the GetThresholdMap method is:
790%
791% ThresholdMap *GetThresholdMap(const char *map_id,
792% ExceptionInfo *exception)
793%
794% A description of each parameter follows.
795%
796% o map_id: ID of the map to look for.
797%
798% o exception: return any errors or warnings in this structure.
799%
800*/
801MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
802 ExceptionInfo *exception)
803{
804 const StringInfo
805 *option;
806
807 LinkedListInfo
808 *options;
809
810 ThresholdMap
811 *map;
812
813 map=(ThresholdMap *)NULL;
814 options=GetConfigureOptions(ThresholdsFilename,exception);
815 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
816 (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
817 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
818 GetStringInfoPath(option),map_id,exception);
819 options=DestroyConfigureOptions(options);
820 return(map);
821}
822
823/*
824%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
825% %
826% %
827% %
cristy3ed852e2009-09-05 21:47:34 +0000828+ G e t T h r e s h o l d M a p F i l e %
829% %
830% %
831% %
832%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
833%
834% GetThresholdMapFile() look for a given threshold map name or alias in the
835% given XML file data, and return the allocated the map when found.
836%
837% The format of the ListThresholdMaps method is:
838%
839% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
840% const char *map_id,ExceptionInfo *exception)
841%
842% A description of each parameter follows.
843%
844% o xml: The threshold map list in XML format.
845%
846% o filename: The threshold map XML filename.
847%
848% o map_id: ID of the map to look for in XML list.
849%
850% o exception: return any errors or warnings in this structure.
851%
852*/
cristybd0ebf02011-09-29 01:19:42 +0000853static ThresholdMap *GetThresholdMapFile(const char *xml,
cristy3ed852e2009-09-05 21:47:34 +0000854 const char *filename,const char *map_id,ExceptionInfo *exception)
855{
cristyb9eb87b2011-09-29 01:15:19 +0000856 char
857 *p;
858
cristy3ed852e2009-09-05 21:47:34 +0000859 const char
cristyb9eb87b2011-09-29 01:15:19 +0000860 *attribute,
cristy3ed852e2009-09-05 21:47:34 +0000861 *content;
862
863 double
864 value;
865
cristyb9eb87b2011-09-29 01:15:19 +0000866 register ssize_t
867 i;
cristy3ed852e2009-09-05 21:47:34 +0000868
869 ThresholdMap
870 *map;
871
cristyb9eb87b2011-09-29 01:15:19 +0000872 XMLTreeInfo
873 *description,
874 *levels,
875 *threshold,
876 *thresholds;
877
878 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
879 "Loading threshold map file \"%s\" ...",filename);
880 map=(ThresholdMap *) NULL;
881 thresholds=NewXMLTree(xml,exception);
882 if (thresholds == (XMLTreeInfo *) NULL)
883 return(map);
884 for (threshold=GetXMLTreeChild(thresholds,"threshold");
885 threshold != (XMLTreeInfo *) NULL;
886 threshold=GetNextXMLTreeTag(threshold))
887 {
888 attribute=GetXMLTreeAttribute(threshold,"map");
889 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
890 break;
891 attribute=GetXMLTreeAttribute(threshold,"alias");
892 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
893 break;
894 }
895 if (threshold == (XMLTreeInfo *) NULL)
896 return(map);
897 description=GetXMLTreeChild(threshold,"description");
898 if (description == (XMLTreeInfo *) NULL)
899 {
900 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
901 "XmlMissingElement", "<description>, map \"%s\"",map_id);
902 thresholds=DestroyXMLTree(thresholds);
903 return(map);
904 }
905 levels=GetXMLTreeChild(threshold,"levels");
906 if (levels == (XMLTreeInfo *) NULL)
907 {
908 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
909 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
910 thresholds=DestroyXMLTree(thresholds);
911 return(map);
912 }
913 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
914 if (map == (ThresholdMap *) NULL)
915 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
916 map->map_id=(char *) NULL;
917 map->description=(char *) NULL;
918 map->levels=(ssize_t *) NULL;
919 attribute=GetXMLTreeAttribute(threshold,"map");
920 if (attribute != (char *) NULL)
921 map->map_id=ConstantString(attribute);
922 content=GetXMLTreeContent(description);
923 if (content != (char *) NULL)
924 map->description=ConstantString(content);
925 attribute=GetXMLTreeAttribute(levels,"width");
926 if (attribute == (char *) NULL)
927 {
928 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
929 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
930 thresholds=DestroyXMLTree(thresholds);
931 map=DestroyThresholdMap(map);
932 return(map);
933 }
934 map->width=StringToUnsignedLong(attribute);
935 if (map->width == 0)
936 {
937 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
938 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
939 thresholds=DestroyXMLTree(thresholds);
940 map=DestroyThresholdMap(map);
941 return(map);
942 }
943 attribute=GetXMLTreeAttribute(levels,"height");
944 if (attribute == (char *) NULL)
945 {
946 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
947 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
948 thresholds=DestroyXMLTree(thresholds);
949 map=DestroyThresholdMap(map);
950 return(map);
951 }
952 map->height=StringToUnsignedLong(attribute);
953 if (map->height == 0)
954 {
955 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
956 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
957 thresholds=DestroyXMLTree(thresholds);
958 map=DestroyThresholdMap(map);
959 return(map);
960 }
961 attribute=GetXMLTreeAttribute(levels,"divisor");
962 if (attribute == (char *) NULL)
963 {
964 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
965 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
966 thresholds=DestroyXMLTree(thresholds);
967 map=DestroyThresholdMap(map);
968 return(map);
969 }
970 map->divisor=(ssize_t) StringToLong(attribute);
971 if (map->divisor < 2)
972 {
973 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
974 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
975 thresholds=DestroyXMLTree(thresholds);
976 map=DestroyThresholdMap(map);
977 return(map);
978 }
979 content=GetXMLTreeContent(levels);
980 if (content == (char *) NULL)
981 {
982 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
983 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
984 thresholds=DestroyXMLTree(thresholds);
985 map=DestroyThresholdMap(map);
986 return(map);
987 }
988 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
989 sizeof(*map->levels));
990 if (map->levels == (ssize_t *) NULL)
991 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
992 for (i=0; i < (ssize_t) (map->width*map->height); i++)
993 {
994 map->levels[i]=(ssize_t) strtol(content,&p,10);
995 if (p == content)
996 {
997 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
998 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
999 thresholds=DestroyXMLTree(thresholds);
1000 map=DestroyThresholdMap(map);
1001 return(map);
1002 }
1003 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1004 {
1005 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1006 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1007 (double) map->levels[i],map_id);
1008 thresholds=DestroyXMLTree(thresholds);
1009 map=DestroyThresholdMap(map);
1010 return(map);
1011 }
1012 content=p;
1013 }
1014 value=(double) strtol(content,&p,10);
1015 (void) value;
1016 if (p != content)
1017 {
1018 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1019 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1020 thresholds=DestroyXMLTree(thresholds);
1021 map=DestroyThresholdMap(map);
1022 return(map);
1023 }
1024 thresholds=DestroyXMLTree(thresholds);
cristy3ed852e2009-09-05 21:47:34 +00001025 return(map);
1026}
1027
1028/*
1029%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1030% %
1031% %
1032% %
1033+ L i s t T h r e s h o l d M a p F i l e %
1034% %
1035% %
1036% %
1037%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1038%
1039% ListThresholdMapFile() lists the threshold maps and their descriptions
1040% in the given XML file data.
1041%
1042% The format of the ListThresholdMaps method is:
1043%
1044% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1045% const char *filename,ExceptionInfo *exception)
1046%
1047% A description of each parameter follows.
1048%
1049% o file: An pointer to the output FILE.
1050%
1051% o xml: The threshold map list in XML format.
1052%
1053% o filename: The threshold map XML filename.
1054%
1055% o exception: return any errors or warnings in this structure.
1056%
1057*/
1058MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1059 const char *filename,ExceptionInfo *exception)
1060{
cristy5f9f2462011-09-28 23:37:58 +00001061 const char
1062 *alias,
1063 *content,
1064 *map;
1065
1066 XMLTreeInfo
1067 *description,
1068 *threshold,
1069 *thresholds;
cristy3ed852e2009-09-05 21:47:34 +00001070
1071 assert( xml != (char *)NULL );
1072 assert( file != (FILE *)NULL );
cristy3ed852e2009-09-05 21:47:34 +00001073 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1074 "Loading threshold map file \"%s\" ...",filename);
1075 thresholds=NewXMLTree(xml,exception);
1076 if ( thresholds == (XMLTreeInfo *)NULL )
1077 return(MagickFalse);
cristy1e604812011-05-19 18:07:50 +00001078 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1079 (void) FormatLocaleFile(file,
1080 "----------------------------------------------------\n");
cristy5f9f2462011-09-28 23:37:58 +00001081 threshold=GetXMLTreeChild(thresholds,"threshold");
cristyb9eb87b2011-09-29 01:15:19 +00001082 for ( ; threshold != (XMLTreeInfo *) NULL;
1083 threshold=GetNextXMLTreeTag(threshold))
cristy3ed852e2009-09-05 21:47:34 +00001084 {
cristy5f9f2462011-09-28 23:37:58 +00001085 map=GetXMLTreeAttribute(threshold,"map");
1086 if (map == (char *) NULL)
1087 {
1088 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1089 "XmlMissingAttribute", "<map>");
1090 thresholds=DestroyXMLTree(thresholds);
1091 return(MagickFalse);
1092 }
1093 alias=GetXMLTreeAttribute(threshold,"alias");
cristy3ed852e2009-09-05 21:47:34 +00001094 description=GetXMLTreeChild(threshold,"description");
cristy5f9f2462011-09-28 23:37:58 +00001095 if (description == (XMLTreeInfo *) NULL)
1096 {
1097 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyb9eb87b2011-09-29 01:15:19 +00001098 "XmlMissingElement", "<description>, map \"%s\"",map);
cristy5f9f2462011-09-28 23:37:58 +00001099 thresholds=DestroyXMLTree(thresholds);
1100 return(MagickFalse);
1101 }
cristy3ed852e2009-09-05 21:47:34 +00001102 content=GetXMLTreeContent(description);
cristy5f9f2462011-09-28 23:37:58 +00001103 if (content == (char *) NULL)
1104 {
1105 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1106 "XmlMissingContent", "<description>, map \"%s\"", map);
1107 thresholds=DestroyXMLTree(thresholds);
1108 return(MagickFalse);
1109 }
cristy1e604812011-05-19 18:07:50 +00001110 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1111 content);
cristy3ed852e2009-09-05 21:47:34 +00001112 }
1113 thresholds=DestroyXMLTree(thresholds);
1114 return(MagickTrue);
1115}
1116
1117/*
1118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1119% %
1120% %
1121% %
1122% L i s t T h r e s h o l d M a p s %
1123% %
1124% %
1125% %
1126%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1127%
1128% ListThresholdMaps() lists the threshold maps and their descriptions
1129% as defined by "threshold.xml" to a file.
1130%
1131% The format of the ListThresholdMaps method is:
1132%
1133% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1134%
1135% A description of each parameter follows.
1136%
1137% o file: An pointer to the output FILE.
1138%
1139% o exception: return any errors or warnings in this structure.
1140%
1141*/
1142MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1143 ExceptionInfo *exception)
1144{
1145 const StringInfo
1146 *option;
1147
1148 LinkedListInfo
1149 *options;
1150
1151 MagickStatusType
1152 status;
1153
1154 status=MagickFalse;
cristy5f9f2462011-09-28 23:37:58 +00001155 if (file == (FILE *) NULL)
1156 file=stdout;
cristy3ed852e2009-09-05 21:47:34 +00001157 options=GetConfigureOptions(ThresholdsFilename,exception);
cristy1e604812011-05-19 18:07:50 +00001158 (void) FormatLocaleFile(file,
1159 "\n Threshold Maps for Ordered Dither Operations\n");
cristy5f9f2462011-09-28 23:37:58 +00001160 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1161 (const StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001162 {
cristyb51dff52011-05-19 16:55:47 +00001163 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
cristy3ed852e2009-09-05 21:47:34 +00001164 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1165 GetStringInfoPath(option),exception);
1166 }
1167 options=DestroyConfigureOptions(options);
1168 return(status != 0 ? MagickTrue : MagickFalse);
1169}
1170
1171/*
1172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1173% %
1174% %
1175% %
cristy3ed852e2009-09-05 21:47:34 +00001176% O r d e r e d P o s t e r i z e I m a g e %
1177% %
1178% %
1179% %
1180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1181%
1182% OrderedPosterizeImage() will perform a ordered dither based on a number
1183% of pre-defined dithering threshold maps, but over multiple intensity
1184% levels, which can be different for different channels, according to the
1185% input argument.
1186%
1187% The format of the OrderedPosterizeImage method is:
1188%
1189% MagickBooleanType OrderedPosterizeImage(Image *image,
1190% const char *threshold_map,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001191%
1192% A description of each parameter follows:
1193%
1194% o image: the image.
1195%
cristy3ed852e2009-09-05 21:47:34 +00001196% o threshold_map: A string containing the name of the threshold dither
1197% map to use, followed by zero or more numbers representing the number
1198% of color levels tho dither between.
1199%
cristyf998fb32011-04-27 23:00:47 +00001200% Any level number less than 2 will be equivalent to 2, and means only
cristy3ed852e2009-09-05 21:47:34 +00001201% binary dithering will be applied to each color channel.
1202%
1203% No numbers also means a 2 level (bitmap) dither will be applied to all
1204% channels, while a single number is the number of levels applied to each
1205% channel in sequence. More numbers will be applied in turn to each of
1206% the color channels.
1207%
1208% For example: "o3x3,6" will generate a 6 level posterization of the
1209% image with a ordered 3x3 diffused pixel dither being applied between
1210% each level. While checker,8,8,4 will produce a 332 colormaped image
1211% with only a single checkerboard hash pattern (50% grey) between each
1212% color level, to basically double the number of color levels with
1213% a bare minimim of dithering.
1214%
1215% o exception: return any errors or warnings in this structure.
1216%
1217*/
1218MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1219 const char *threshold_map,ExceptionInfo *exception)
1220{
cristy3ed852e2009-09-05 21:47:34 +00001221#define DitherImageTag "Dither/Image"
1222
cristyc4c8d132010-01-07 01:58:38 +00001223 CacheView
1224 *image_view;
1225
cristy4e0b82a2011-09-29 12:47:44 +00001226 char
1227 token[MaxTextExtent];
1228
1229 const char
1230 *p;
cristy3ed852e2009-09-05 21:47:34 +00001231
1232 MagickBooleanType
1233 status;
1234
cristy5f959472010-05-27 22:19:46 +00001235 MagickOffsetType
1236 progress;
1237
cristy4e0b82a2011-09-29 12:47:44 +00001238 MagickRealType
cristy5f95f4f2011-10-23 01:01:01 +00001239 levels[CompositePixelChannel];
cristy4e0b82a2011-09-29 12:47:44 +00001240
1241 register ssize_t
1242 i;
1243
cristy5f959472010-05-27 22:19:46 +00001244 ssize_t
1245 y;
1246
cristy3ed852e2009-09-05 21:47:34 +00001247 ThresholdMap
1248 *map;
1249
cristy3ed852e2009-09-05 21:47:34 +00001250 assert(image != (Image *) NULL);
1251 assert(image->signature == MagickSignature);
1252 if (image->debug != MagickFalse)
1253 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1254 assert(exception != (ExceptionInfo *) NULL);
1255 assert(exception->signature == MagickSignature);
1256 if (threshold_map == (const char *) NULL)
1257 return(MagickTrue);
cristy4e0b82a2011-09-29 12:47:44 +00001258 p=(char *) threshold_map;
1259 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1260 (*p != '\0'))
1261 p++;
1262 threshold_map=p;
1263 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1264 (*p != '\0'))
cristy3ed852e2009-09-05 21:47:34 +00001265 {
cristy4e0b82a2011-09-29 12:47:44 +00001266 if ((p-threshold_map) >= (MaxTextExtent-1))
1267 break;
1268 token[p-threshold_map]=(*p);
1269 p++;
cristy3ed852e2009-09-05 21:47:34 +00001270 }
cristy4e0b82a2011-09-29 12:47:44 +00001271 token[p-threshold_map]='\0';
1272 map=GetThresholdMap(token,exception);
1273 if (map == (ThresholdMap *) NULL)
1274 {
1275 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1276 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
cristy574cc262011-08-05 01:23:58 +00001277 return(MagickFalse);
cristy4e0b82a2011-09-29 12:47:44 +00001278 }
1279 for (i=0; i < MaxPixelChannels; i++)
1280 levels[i]=2.0;
1281 p=strchr((char *) threshold_map,',');
1282 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1283 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1284 {
1285 GetMagickToken(p,&p,token);
1286 if (*token == ',')
1287 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001288 levels[i]=StringToDouble(token,(char **) NULL);
cristy4e0b82a2011-09-29 12:47:44 +00001289 }
1290 for (i=0; i < MaxPixelChannels; i++)
1291 if (fabs(levels[i]) >= 1)
1292 levels[i]-=1.0;
1293 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1294 return(MagickFalse);
1295 status=MagickTrue;
1296 progress=0;
1297 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001298#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00001299 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001300#endif
cristy4e0b82a2011-09-29 12:47:44 +00001301 for (y=0; y < (ssize_t) image->rows; y++)
1302 {
1303 register ssize_t
1304 x;
1305
1306 register Quantum
1307 *restrict q;
1308
1309 if (status == MagickFalse)
1310 continue;
1311 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1312 if (q == (Quantum *) NULL)
1313 {
1314 status=MagickFalse;
1315 continue;
1316 }
1317 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001318 {
cristybb503372010-05-27 20:51:26 +00001319 register ssize_t
cristy4e0b82a2011-09-29 12:47:44 +00001320 i;
cristy3ed852e2009-09-05 21:47:34 +00001321
cristy4e0b82a2011-09-29 12:47:44 +00001322 ssize_t
1323 n;
cristy3ed852e2009-09-05 21:47:34 +00001324
cristy4e0b82a2011-09-29 12:47:44 +00001325 n=0;
1326 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +00001327 {
cristyabace412011-12-11 15:56:53 +00001328 PixelChannel
1329 channel;
1330
cristy4e0b82a2011-09-29 12:47:44 +00001331 PixelTrait
1332 traits;
1333
1334 ssize_t
cristy3f5d8152011-09-29 13:00:19 +00001335 level,
1336 threshold;
cristy3ed852e2009-09-05 21:47:34 +00001337
cristyabace412011-12-11 15:56:53 +00001338 channel=GetPixelChannelMapChannel(image,i);
1339 traits=GetPixelChannelMapTraits(image,channel);
cristy4e0b82a2011-09-29 12:47:44 +00001340 if ((traits & UpdatePixelTrait) == 0)
1341 continue;
cristy3f5d8152011-09-29 13:00:19 +00001342 if (fabs(levels[n++]) < MagickEpsilon)
1343 continue;
1344 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1345 level=threshold/(map->divisor-1);
1346 threshold-=level*(map->divisor-1);
1347 q[i]=RoundToQuantum((level+(threshold >= map->levels[(x % map->width)+
1348 map->width*(y % map->height)]))*QuantumRange/levels[n]);
cristy4e0b82a2011-09-29 12:47:44 +00001349 n++;
cristy3ed852e2009-09-05 21:47:34 +00001350 }
cristy4e0b82a2011-09-29 12:47:44 +00001351 q+=GetPixelChannels(image);
1352 }
1353 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1354 status=MagickFalse;
1355 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1356 {
1357 MagickBooleanType
1358 proceed;
cristy3ed852e2009-09-05 21:47:34 +00001359
cristyb5d5f722009-11-04 03:03:49 +00001360#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy4e0b82a2011-09-29 12:47:44 +00001361#pragma omp critical (MagickCore_OrderedPosterizeImage)
cristy3ed852e2009-09-05 21:47:34 +00001362#endif
cristy4e0b82a2011-09-29 12:47:44 +00001363 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1364 if (proceed == MagickFalse)
1365 status=MagickFalse;
1366 }
cristy3ed852e2009-09-05 21:47:34 +00001367 }
cristy4e0b82a2011-09-29 12:47:44 +00001368 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00001369 map=DestroyThresholdMap(map);
1370 return(MagickTrue);
1371}
1372
1373/*
1374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1375% %
1376% %
1377% %
1378% R a n d o m T h r e s h o l d I m a g e %
1379% %
1380% %
1381% %
1382%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1383%
1384% RandomThresholdImage() changes the value of individual pixels based on the
1385% intensity of each pixel compared to a random threshold. The result is a
1386% low-contrast, two color image.
1387%
1388% The format of the RandomThresholdImage method is:
1389%
cristyf4ad9df2011-07-08 16:49:03 +00001390% MagickBooleanType RandomThresholdImage(Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001391% const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001392%
1393% A description of each parameter follows:
1394%
1395% o image: the image.
1396%
cristy3ed852e2009-09-05 21:47:34 +00001397% o thresholds: a geometry string containing low,high thresholds. If the
1398% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1399% is performed instead.
1400%
1401% o exception: return any errors or warnings in this structure.
1402%
1403*/
cristy3ed852e2009-09-05 21:47:34 +00001404MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1405 const char *thresholds,ExceptionInfo *exception)
1406{
cristy3ed852e2009-09-05 21:47:34 +00001407#define ThresholdImageTag "Threshold/Image"
1408
cristyfa112112010-01-04 17:48:07 +00001409 CacheView
1410 *image_view;
1411
cristy3ed852e2009-09-05 21:47:34 +00001412 GeometryInfo
1413 geometry_info;
1414
1415 MagickStatusType
1416 flags;
1417
cristy3ed852e2009-09-05 21:47:34 +00001418 MagickBooleanType
1419 status;
1420
cristy5f959472010-05-27 22:19:46 +00001421 MagickOffsetType
1422 progress;
1423
cristy4c08aed2011-07-01 19:47:50 +00001424 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001425 threshold;
1426
1427 MagickRealType
1428 min_threshold,
1429 max_threshold;
1430
1431 RandomInfo
cristyfa112112010-01-04 17:48:07 +00001432 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00001433
cristy5f959472010-05-27 22:19:46 +00001434 ssize_t
1435 y;
1436
cristy3ed852e2009-09-05 21:47:34 +00001437 assert(image != (Image *) NULL);
1438 assert(image->signature == MagickSignature);
1439 if (image->debug != MagickFalse)
1440 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1441 assert(exception != (ExceptionInfo *) NULL);
1442 assert(exception->signature == MagickSignature);
1443 if (thresholds == (const char *) NULL)
1444 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00001445 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001446 min_threshold=0.0;
1447 max_threshold=(MagickRealType) QuantumRange;
1448 flags=ParseGeometry(thresholds,&geometry_info);
1449 min_threshold=geometry_info.rho;
1450 max_threshold=geometry_info.sigma;
1451 if ((flags & SigmaValue) == 0)
1452 max_threshold=min_threshold;
1453 if (strchr(thresholds,'%') != (char *) NULL)
1454 {
1455 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1456 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1457 }
cristy3ed852e2009-09-05 21:47:34 +00001458 /*
1459 Random threshold image.
1460 */
1461 status=MagickTrue;
1462 progress=0;
cristy574cc262011-08-05 01:23:58 +00001463 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1464 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001465 random_info=AcquireRandomInfoThreadSet();
1466 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001467#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00001468 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001469#endif
cristybb503372010-05-27 20:51:26 +00001470 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001471 {
cristy5c9e6f22010-09-17 17:31:01 +00001472 const int
1473 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001474
cristy4c08aed2011-07-01 19:47:50 +00001475 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001476 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001477
cristy5c9e6f22010-09-17 17:31:01 +00001478 register ssize_t
1479 x;
1480
cristy3ed852e2009-09-05 21:47:34 +00001481 if (status == MagickFalse)
1482 continue;
1483 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001484 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001485 {
1486 status=MagickFalse;
1487 continue;
1488 }
cristybb503372010-05-27 20:51:26 +00001489 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001490 {
cristy5f9f2462011-09-28 23:37:58 +00001491 register ssize_t
1492 i;
1493
1494 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1495 {
1496 MagickRealType
1497 threshold;
1498
cristyabace412011-12-11 15:56:53 +00001499 PixelChannel
1500 channel;
1501
cristy5f9f2462011-09-28 23:37:58 +00001502 PixelTrait
1503 traits;
1504
cristyabace412011-12-11 15:56:53 +00001505 channel=GetPixelChannelMapChannel(image,i);
1506 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +00001507 if ((traits & UpdatePixelTrait) == 0)
1508 continue;
1509 if ((MagickRealType) q[i] < min_threshold)
1510 threshold=min_threshold;
1511 else
1512 if ((MagickRealType) q[i] > max_threshold)
1513 threshold=max_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001514 else
cristy5f9f2462011-09-28 23:37:58 +00001515 threshold=(MagickRealType) (QuantumRange*
1516 GetPseudoRandomValue(random_info[id]));
1517 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
1518 QuantumRange);
1519 }
cristyed231572011-07-14 02:18:59 +00001520 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001521 }
1522 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1523 status=MagickFalse;
1524 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1525 {
1526 MagickBooleanType
1527 proceed;
1528
cristyb5d5f722009-11-04 03:03:49 +00001529#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001530 #pragma omp critical (MagickCore_RandomThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001531#endif
1532 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1533 image->rows);
1534 if (proceed == MagickFalse)
1535 status=MagickFalse;
1536 }
1537 }
1538 image_view=DestroyCacheView(image_view);
1539 random_info=DestroyRandomInfoThreadSet(random_info);
1540 return(status);
1541}
1542
1543/*
1544%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1545% %
1546% %
1547% %
1548% W h i t e T h r e s h o l d I m a g e %
1549% %
1550% %
1551% %
1552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1553%
1554% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001555% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001556% unchanged.
1557%
1558% The format of the WhiteThresholdImage method is:
1559%
cristyf4ad9df2011-07-08 16:49:03 +00001560% MagickBooleanType WhiteThresholdImage(Image *image,
1561% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001562%
1563% A description of each parameter follows:
1564%
1565% o image: the image.
1566%
cristy3ed852e2009-09-05 21:47:34 +00001567% o threshold: Define the threshold value.
1568%
1569% o exception: return any errors or warnings in this structure.
1570%
1571*/
1572MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +00001573 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001574{
1575#define ThresholdImageTag "Threshold/Image"
1576
cristy5f959472010-05-27 22:19:46 +00001577 CacheView
1578 *image_view;
1579
cristy3ed852e2009-09-05 21:47:34 +00001580 GeometryInfo
1581 geometry_info;
1582
cristy3ed852e2009-09-05 21:47:34 +00001583 MagickBooleanType
1584 status;
1585
cristy5f959472010-05-27 22:19:46 +00001586 MagickOffsetType
1587 progress;
1588
cristy5f9f2462011-09-28 23:37:58 +00001589 MagickRealType
1590 threshold[5];
1591
cristy3ed852e2009-09-05 21:47:34 +00001592 MagickStatusType
1593 flags;
1594
cristy5f9f2462011-09-28 23:37:58 +00001595 register ssize_t
1596 i;
1597
cristy5f959472010-05-27 22:19:46 +00001598 ssize_t
1599 y;
cristy3ed852e2009-09-05 21:47:34 +00001600
1601 assert(image != (Image *) NULL);
1602 assert(image->signature == MagickSignature);
1603 if (image->debug != MagickFalse)
1604 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1605 if (thresholds == (const char *) NULL)
1606 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +00001607 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001608 return(MagickFalse);
1609 flags=ParseGeometry(thresholds,&geometry_info);
cristy5f9f2462011-09-28 23:37:58 +00001610 for (i=0; i < 5; i++)
1611 threshold[i]=geometry_info.rho;
1612 if ((flags & SigmaValue) != 0)
1613 threshold[1]=geometry_info.sigma;
1614 if ((flags & XiValue) != 0)
1615 threshold[2]=geometry_info.xi;
1616 if ((flags & PsiValue) != 0)
1617 threshold[3]=geometry_info.psi;
1618 if ((flags & ChiValue) != 0)
1619 threshold[4]=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +00001620 if ((flags & PercentValue) != 0)
cristy5f9f2462011-09-28 23:37:58 +00001621 for (i=0; i < 5; i++)
1622 threshold[i]*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001623 /*
1624 White threshold image.
1625 */
1626 status=MagickTrue;
1627 progress=0;
1628 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001629#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00001630 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001631#endif
cristybb503372010-05-27 20:51:26 +00001632 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001633 {
cristybb503372010-05-27 20:51:26 +00001634 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001635 x;
1636
cristy4c08aed2011-07-01 19:47:50 +00001637 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001638 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001639
1640 if (status == MagickFalse)
1641 continue;
1642 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001643 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001644 {
1645 status=MagickFalse;
1646 continue;
1647 }
cristybb503372010-05-27 20:51:26 +00001648 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001649 {
cristy5f9f2462011-09-28 23:37:58 +00001650 register ssize_t
1651 i;
1652
1653 ssize_t
1654 n;
1655
1656 n=0;
1657 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1658 {
cristyabace412011-12-11 15:56:53 +00001659 PixelChannel
1660 channel;
1661
cristy5f9f2462011-09-28 23:37:58 +00001662 PixelTrait
1663 traits;
1664
cristyabace412011-12-11 15:56:53 +00001665 channel=GetPixelChannelMapChannel(image,i);
1666 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +00001667 if ((traits & UpdatePixelTrait) == 0)
1668 continue;
1669 if ((MagickRealType) q[i] > threshold[n++ % 5])
1670 q[i]=QuantumRange;
1671 }
cristyed231572011-07-14 02:18:59 +00001672 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001673 }
1674 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1675 status=MagickFalse;
1676 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1677 {
1678 MagickBooleanType
1679 proceed;
1680
cristyb5d5f722009-11-04 03:03:49 +00001681#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001682 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001683#endif
1684 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1685 image->rows);
1686 if (proceed == MagickFalse)
1687 status=MagickFalse;
1688 }
1689 }
1690 image_view=DestroyCacheView(image_view);
1691 return(status);
1692}