blob: a1420e9d825947740e3f73a477964a393654c440 [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
cristy10a6c612012-01-29 21:41:05 +0000241 if (GetPixelMask(image,p) != 0)
242 {
243 p+=GetPixelChannels(image);
244 q+=GetPixelChannels(threshold_image);
245 continue;
246 }
cristya0312c92011-07-23 21:04:30 +0000247 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000248 {
cristyde5cc632011-07-18 14:47:00 +0000249 MagickRealType
250 mean,
251 pixel;
252
253 PixelChannel
254 channel;
255
256 PixelTrait
257 threshold_traits,
258 traits;
259
260 register const Quantum
261 *restrict pixels;
262
263 register ssize_t
264 u;
265
266 ssize_t
267 v;
268
cristye2a912b2011-12-05 20:02:07 +0000269 channel=GetPixelChannelMapChannel(image,i);
cristyabace412011-12-11 15:56:53 +0000270 traits=GetPixelChannelMapTraits(image,channel);
cristyde5cc632011-07-18 14:47:00 +0000271 threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
cristy010d7d12011-08-31 01:02:48 +0000272 if ((traits == UndefinedPixelTrait) ||
273 (threshold_traits == UndefinedPixelTrait))
cristyde5cc632011-07-18 14:47:00 +0000274 continue;
275 if ((threshold_traits & CopyPixelTrait) != 0)
276 {
cristy0beccfa2011-09-25 20:47:53 +0000277 SetPixelChannel(threshold_image,channel,p[center+i],q);
cristyde5cc632011-07-18 14:47:00 +0000278 continue;
279 }
280 pixels=p;
281 pixel=0.0;
282 for (v=0; v < (ssize_t) height; v++)
cristy3ed852e2009-09-05 21:47:34 +0000283 {
cristyde5cc632011-07-18 14:47:00 +0000284 for (u=0; u < (ssize_t) width; u++)
285 {
286 pixel+=pixels[i];
cristya0312c92011-07-23 21:04:30 +0000287 pixels+=GetPixelChannels(image);
cristyde5cc632011-07-18 14:47:00 +0000288 }
cristya0312c92011-07-23 21:04:30 +0000289 pixels+=image->columns*GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000290 }
cristy5f9f2462011-09-28 23:37:58 +0000291 mean=(MagickRealType) (pixel/number_pixels+bias);
292 SetPixelChannel(threshold_image,channel,(Quantum) ((MagickRealType)
293 p[center+i] <= mean ? 0 : QuantumRange),q);
cristy3ed852e2009-09-05 21:47:34 +0000294 }
cristya0312c92011-07-23 21:04:30 +0000295 p+=GetPixelChannels(image);
296 q+=GetPixelChannels(threshold_image);
cristy3ed852e2009-09-05 21:47:34 +0000297 }
cristyde5cc632011-07-18 14:47:00 +0000298 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000299 status=MagickFalse;
300 if (image->progress_monitor != (MagickProgressMonitor) NULL)
301 {
302 MagickBooleanType
303 proceed;
304
cristyb5d5f722009-11-04 03:03:49 +0000305#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000306 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
307#endif
cristyde5cc632011-07-18 14:47:00 +0000308 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
cristy3ed852e2009-09-05 21:47:34 +0000309 image->rows);
310 if (proceed == MagickFalse)
311 status=MagickFalse;
312 }
313 }
cristyde5cc632011-07-18 14:47:00 +0000314 threshold_image->type=image->type;
cristy3ed852e2009-09-05 21:47:34 +0000315 threshold_view=DestroyCacheView(threshold_view);
316 image_view=DestroyCacheView(image_view);
317 if (status == MagickFalse)
318 threshold_image=DestroyImage(threshold_image);
319 return(threshold_image);
320}
321
322/*
323%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
324% %
325% %
326% %
327% B i l e v e l I m a g e %
328% %
329% %
330% %
331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
332%
333% BilevelImage() changes the value of individual pixels based on the
334% intensity of each pixel channel. The result is a high-contrast image.
335%
336% More precisely each channel value of the image is 'thresholded' so that if
337% it is equal to or less than the given value it is set to zero, while any
338% value greater than that give is set to it maximum or QuantumRange.
339%
340% This function is what is used to implement the "-threshold" operator for
341% the command line API.
342%
343% If the default channel setting is given the image is thresholded using just
344% the gray 'intensity' of the image, rather than the individual channels.
345%
cristyf4ad9df2011-07-08 16:49:03 +0000346% The format of the BilevelImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000347%
cristye941a752011-10-15 01:52:48 +0000348% MagickBooleanType BilevelImage(Image *image,const double threshold,
349% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000350%
351% A description of each parameter follows:
352%
353% o image: the image.
354%
cristy3ed852e2009-09-05 21:47:34 +0000355% o threshold: define the threshold values.
356%
cristye941a752011-10-15 01:52:48 +0000357% o exception: return any errors or warnings in this structure.
358%
cristyf89cb1d2011-07-07 01:24:37 +0000359% Aside: You can get the same results as operator using LevelImages()
cristy3ed852e2009-09-05 21:47:34 +0000360% with the 'threshold' value for both the black_point and the white_point.
361%
362*/
cristye941a752011-10-15 01:52:48 +0000363MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
364 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000365{
366#define ThresholdImageTag "Threshold/Image"
367
cristyc4c8d132010-01-07 01:58:38 +0000368 CacheView
369 *image_view;
370
cristy3ed852e2009-09-05 21:47:34 +0000371 MagickBooleanType
372 status;
373
cristy5f959472010-05-27 22:19:46 +0000374 MagickOffsetType
375 progress;
376
377 ssize_t
378 y;
379
cristy3ed852e2009-09-05 21:47:34 +0000380 assert(image != (Image *) NULL);
381 assert(image->signature == MagickSignature);
382 if (image->debug != MagickFalse)
383 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy574cc262011-08-05 01:23:58 +0000384 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000385 return(MagickFalse);
386 /*
387 Bilevel threshold image.
388 */
389 status=MagickTrue;
390 progress=0;
cristy3ed852e2009-09-05 21:47:34 +0000391 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000392#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +0000393 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000394#endif
cristybb503372010-05-27 20:51:26 +0000395 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000396 {
cristybb503372010-05-27 20:51:26 +0000397 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000398 x;
399
cristy4c08aed2011-07-01 19:47:50 +0000400 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000401 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000402
403 if (status == MagickFalse)
404 continue;
405 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000406 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000407 {
408 status=MagickFalse;
409 continue;
410 }
cristy805b6a02011-08-09 00:59:35 +0000411 for (x=0; x < (ssize_t) image->columns; x++)
412 {
cristy95111202011-08-09 19:41:42 +0000413 register ssize_t
414 i;
415
cristy10a6c612012-01-29 21:41:05 +0000416 if (GetPixelMask(image,q) != 0)
417 {
418 q+=GetPixelChannels(image);
419 continue;
420 }
cristy95111202011-08-09 19:41:42 +0000421 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
422 {
cristyabace412011-12-11 15:56:53 +0000423 PixelChannel
424 channel;
425
cristy95111202011-08-09 19:41:42 +0000426 PixelTrait
427 traits;
428
cristyabace412011-12-11 15:56:53 +0000429 channel=GetPixelChannelMapChannel(image,i);
430 traits=GetPixelChannelMapTraits(image,channel);
cristy95111202011-08-09 19:41:42 +0000431 if ((traits & UpdatePixelTrait) != 0)
cristy5f9f2462011-09-28 23:37:58 +0000432 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
433 QuantumRange);
cristy95111202011-08-09 19:41:42 +0000434 }
cristy805b6a02011-08-09 00:59:35 +0000435 q+=GetPixelChannels(image);
436 }
cristy3ed852e2009-09-05 21:47:34 +0000437 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
438 status=MagickFalse;
439 if (image->progress_monitor != (MagickProgressMonitor) NULL)
440 {
441 MagickBooleanType
442 proceed;
443
cristyb5d5f722009-11-04 03:03:49 +0000444#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000445 #pragma omp critical (MagickCore_BilevelImage)
cristy3ed852e2009-09-05 21:47:34 +0000446#endif
447 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
448 image->rows);
449 if (proceed == MagickFalse)
450 status=MagickFalse;
451 }
452 }
453 image_view=DestroyCacheView(image_view);
454 return(status);
455}
456
457/*
458%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459% %
460% %
461% %
462% B l a c k T h r e s h o l d I m a g e %
463% %
464% %
465% %
466%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
467%
468% BlackThresholdImage() is like ThresholdImage() but forces all pixels below
cristy4e101302009-09-17 12:49:12 +0000469% the threshold into black while leaving all pixels at or above the threshold
cristy3ed852e2009-09-05 21:47:34 +0000470% unchanged.
471%
472% The format of the BlackThresholdImage method is:
473%
cristyf4ad9df2011-07-08 16:49:03 +0000474% MagickBooleanType BlackThresholdImage(Image *image,
475% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000476%
477% A description of each parameter follows:
478%
479% o image: the image.
480%
cristy5f9f2462011-09-28 23:37:58 +0000481% o threshold: define the threshold value.
cristy3ed852e2009-09-05 21:47:34 +0000482%
483% o exception: return any errors or warnings in this structure.
484%
485*/
486MagickExport MagickBooleanType BlackThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +0000487 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000488{
489#define ThresholdImageTag "Threshold/Image"
490
cristyc4c8d132010-01-07 01:58:38 +0000491 CacheView
492 *image_view;
493
cristy3ed852e2009-09-05 21:47:34 +0000494 GeometryInfo
495 geometry_info;
496
cristy3ed852e2009-09-05 21:47:34 +0000497 MagickBooleanType
498 status;
499
cristy5f959472010-05-27 22:19:46 +0000500 MagickOffsetType
501 progress;
502
cristy5f9f2462011-09-28 23:37:58 +0000503 MagickRealType
504 threshold[5];
cristy3ed852e2009-09-05 21:47:34 +0000505
506 MagickStatusType
507 flags;
508
cristy5f9f2462011-09-28 23:37:58 +0000509 register ssize_t
510 i;
511
cristy5f959472010-05-27 22:19:46 +0000512 ssize_t
513 y;
514
cristy3ed852e2009-09-05 21:47:34 +0000515 assert(image != (Image *) NULL);
516 assert(image->signature == MagickSignature);
517 if (image->debug != MagickFalse)
518 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
519 if (thresholds == (const char *) NULL)
520 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +0000521 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000522 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000523 flags=ParseGeometry(thresholds,&geometry_info);
cristy5f9f2462011-09-28 23:37:58 +0000524 for (i=0; i < 5; i++)
525 threshold[i]=geometry_info.rho;
526 if ((flags & SigmaValue) != 0)
527 threshold[1]=geometry_info.sigma;
528 if ((flags & XiValue) != 0)
529 threshold[2]=geometry_info.xi;
530 if ((flags & PsiValue) != 0)
531 threshold[3]=geometry_info.psi;
532 if ((flags & ChiValue) != 0)
533 threshold[4]=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +0000534 if ((flags & PercentValue) != 0)
cristy5f9f2462011-09-28 23:37:58 +0000535 for (i=0; i < 5; i++)
536 threshold[i]*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +0000537 /*
cristy5f9f2462011-09-28 23:37:58 +0000538 White threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000539 */
540 status=MagickTrue;
541 progress=0;
542 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000543#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +0000544 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000545#endif
cristybb503372010-05-27 20:51:26 +0000546 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000547 {
cristybb503372010-05-27 20:51:26 +0000548 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000549 x;
550
cristy4c08aed2011-07-01 19:47:50 +0000551 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000552 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000553
554 if (status == MagickFalse)
555 continue;
556 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000557 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000558 {
559 status=MagickFalse;
560 continue;
561 }
cristybb503372010-05-27 20:51:26 +0000562 for (x=0; x < (ssize_t) image->columns; x++)
cristyb0ea1af2009-11-28 20:44:46 +0000563 {
cristy5f9f2462011-09-28 23:37:58 +0000564 register ssize_t
565 i;
566
567 ssize_t
568 n;
569
cristy10a6c612012-01-29 21:41:05 +0000570 if (GetPixelMask(image,q) != 0)
571 {
572 q+=GetPixelChannels(image);
573 continue;
574 }
cristyc94ba6f2012-01-29 23:19:58 +0000575 n=0;
cristy5f9f2462011-09-28 23:37:58 +0000576 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
577 {
cristyabace412011-12-11 15:56:53 +0000578 PixelChannel
579 channel;
580
cristy5f9f2462011-09-28 23:37:58 +0000581 PixelTrait
582 traits;
583
cristyabace412011-12-11 15:56:53 +0000584 channel=GetPixelChannelMapChannel(image,i);
585 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +0000586 if ((traits & UpdatePixelTrait) == 0)
587 continue;
588 if ((MagickRealType) q[i] < threshold[n++ % 5])
589 q[i]=QuantumRange;
590 }
cristyed231572011-07-14 02:18:59 +0000591 q+=GetPixelChannels(image);
cristyb0ea1af2009-11-28 20:44:46 +0000592 }
cristy3ed852e2009-09-05 21:47:34 +0000593 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
594 status=MagickFalse;
595 if (image->progress_monitor != (MagickProgressMonitor) NULL)
596 {
597 MagickBooleanType
598 proceed;
599
cristyb5d5f722009-11-04 03:03:49 +0000600#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy5f9f2462011-09-28 23:37:58 +0000601 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +0000602#endif
603 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
604 image->rows);
605 if (proceed == MagickFalse)
606 status=MagickFalse;
607 }
608 }
609 image_view=DestroyCacheView(image_view);
610 return(status);
611}
612
613/*
614%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
615% %
616% %
617% %
cristy1eb45dd2009-09-25 16:38:06 +0000618% C l a m p I m a g e %
619% %
620% %
621% %
622%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
623%
cristyecb0c6d2009-09-25 16:50:09 +0000624% ClampImage() restricts the color range from 0 to the quantum depth.
cristy1eb45dd2009-09-25 16:38:06 +0000625%
cristyf4ad9df2011-07-08 16:49:03 +0000626% The format of the ClampImage method is:
cristy1eb45dd2009-09-25 16:38:06 +0000627%
cristy092d71c2011-10-14 18:01:29 +0000628% MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000629%
630% A description of each parameter follows:
631%
632% o image: the image.
633%
cristy092d71c2011-10-14 18:01:29 +0000634% o exception: return any errors or warnings in this structure.
635%
cristy1eb45dd2009-09-25 16:38:06 +0000636*/
637
cristy75ffdb72010-01-07 17:40:12 +0000638static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
cristy1eb45dd2009-09-25 16:38:06 +0000639{
640#if defined(MAGICKCORE_HDRI_SUPPORT)
641 if (quantum <= 0)
642 return(0);
643 if (quantum >= QuantumRange)
644 return(QuantumRange);
645 return(quantum);
646#else
647 return(quantum);
648#endif
649}
650
cristy092d71c2011-10-14 18:01:29 +0000651MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000652{
cristy1eb45dd2009-09-25 16:38:06 +0000653#define ClampImageTag "Clamp/Image"
654
cristyc4c8d132010-01-07 01:58:38 +0000655 CacheView
656 *image_view;
657
cristy1eb45dd2009-09-25 16:38:06 +0000658 MagickBooleanType
659 status;
660
cristy5f959472010-05-27 22:19:46 +0000661 MagickOffsetType
662 progress;
663
664 ssize_t
665 y;
666
cristy1eb45dd2009-09-25 16:38:06 +0000667 assert(image != (Image *) NULL);
668 assert(image->signature == MagickSignature);
669 if (image->debug != MagickFalse)
670 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
671 if (image->storage_class == PseudoClass)
672 {
cristybb503372010-05-27 20:51:26 +0000673 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000674 i;
675
cristy101ab702011-10-13 13:06:32 +0000676 register PixelInfo
cristyc47d1f82009-11-26 01:44:43 +0000677 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000678
679 q=image->colormap;
cristybb503372010-05-27 20:51:26 +0000680 for (i=0; i < (ssize_t) image->colors; i++)
cristy1eb45dd2009-09-25 16:38:06 +0000681 {
cristy4c08aed2011-07-01 19:47:50 +0000682 q->red=ClampToUnsignedQuantum(q->red);
683 q->green=ClampToUnsignedQuantum(q->green);
684 q->blue=ClampToUnsignedQuantum(q->blue);
685 q->alpha=ClampToUnsignedQuantum(q->alpha);
cristy1eb45dd2009-09-25 16:38:06 +0000686 q++;
687 }
cristyea1a8aa2011-10-20 13:24:06 +0000688 return(SyncImage(image,exception));
cristy1eb45dd2009-09-25 16:38:06 +0000689 }
690 /*
cristy611721d2009-09-25 16:42:17 +0000691 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000692 */
693 status=MagickTrue;
694 progress=0;
cristy1eb45dd2009-09-25 16:38:06 +0000695 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000696#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +0000697 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy1eb45dd2009-09-25 16:38:06 +0000698#endif
cristybb503372010-05-27 20:51:26 +0000699 for (y=0; y < (ssize_t) image->rows; y++)
cristy1eb45dd2009-09-25 16:38:06 +0000700 {
cristybb503372010-05-27 20:51:26 +0000701 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000702 x;
703
cristy4c08aed2011-07-01 19:47:50 +0000704 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000705 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000706
707 if (status == MagickFalse)
708 continue;
709 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000710 if (q == (Quantum *) NULL)
cristy1eb45dd2009-09-25 16:38:06 +0000711 {
712 status=MagickFalse;
713 continue;
714 }
cristybb503372010-05-27 20:51:26 +0000715 for (x=0; x < (ssize_t) image->columns; x++)
cristy1eb45dd2009-09-25 16:38:06 +0000716 {
cristy5f9f2462011-09-28 23:37:58 +0000717 register ssize_t
718 i;
719
cristy10a6c612012-01-29 21:41:05 +0000720 if (GetPixelMask(image,q) != 0)
721 {
722 q+=GetPixelChannels(image);
723 continue;
724 }
cristy5f9f2462011-09-28 23:37:58 +0000725 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
726 {
cristyabace412011-12-11 15:56:53 +0000727 PixelChannel
728 channel;
729
cristy5f9f2462011-09-28 23:37:58 +0000730 PixelTrait
731 traits;
732
cristyabace412011-12-11 15:56:53 +0000733 channel=GetPixelChannelMapChannel(image,i);
734 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +0000735 if (traits == UndefinedPixelTrait)
736 continue;
737 q[i]=ClampToUnsignedQuantum(q[i]);
738 }
cristyed231572011-07-14 02:18:59 +0000739 q+=GetPixelChannels(image);
cristy1eb45dd2009-09-25 16:38:06 +0000740 }
741 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
742 status=MagickFalse;
743 if (image->progress_monitor != (MagickProgressMonitor) NULL)
744 {
745 MagickBooleanType
746 proceed;
747
cristyb5d5f722009-11-04 03:03:49 +0000748#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000749 #pragma omp critical (MagickCore_ClampImage)
cristy1eb45dd2009-09-25 16:38:06 +0000750#endif
751 proceed=SetImageProgress(image,ClampImageTag,progress++,
752 image->rows);
753 if (proceed == MagickFalse)
754 status=MagickFalse;
755 }
756 }
757 image_view=DestroyCacheView(image_view);
758 return(status);
759}
760
761/*
762%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
763% %
764% %
765% %
cristy3ed852e2009-09-05 21:47:34 +0000766% D e s t r o y T h r e s h o l d M a p %
767% %
768% %
769% %
770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771%
772% DestroyThresholdMap() de-allocate the given ThresholdMap
773%
774% The format of the ListThresholdMaps method is:
775%
776% ThresholdMap *DestroyThresholdMap(Threshold *map)
777%
778% A description of each parameter follows.
779%
780% o map: Pointer to the Threshold map to destroy
781%
782*/
783MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
784{
785 assert(map != (ThresholdMap *) NULL);
786 if (map->map_id != (char *) NULL)
787 map->map_id=DestroyString(map->map_id);
788 if (map->description != (char *) NULL)
789 map->description=DestroyString(map->description);
cristybb503372010-05-27 20:51:26 +0000790 if (map->levels != (ssize_t *) NULL)
791 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
cristy3ed852e2009-09-05 21:47:34 +0000792 map=(ThresholdMap *) RelinquishMagickMemory(map);
793 return(map);
794}
795
796/*
797%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798% %
799% %
800% %
cristyb9eb87b2011-09-29 01:15:19 +0000801% G e t T h r e s h o l d M a p %
802% %
803% %
804% %
805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806%
807% GetThresholdMap() loads and searches one or more threshold map files for the
808% map matching the given name or alias.
809%
810% The format of the GetThresholdMap method is:
811%
812% ThresholdMap *GetThresholdMap(const char *map_id,
813% ExceptionInfo *exception)
814%
815% A description of each parameter follows.
816%
817% o map_id: ID of the map to look for.
818%
819% o exception: return any errors or warnings in this structure.
820%
821*/
822MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
823 ExceptionInfo *exception)
824{
825 const StringInfo
826 *option;
827
828 LinkedListInfo
829 *options;
830
831 ThresholdMap
832 *map;
833
834 map=(ThresholdMap *)NULL;
835 options=GetConfigureOptions(ThresholdsFilename,exception);
836 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
837 (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
838 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
839 GetStringInfoPath(option),map_id,exception);
840 options=DestroyConfigureOptions(options);
841 return(map);
842}
843
844/*
845%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
846% %
847% %
848% %
cristy3ed852e2009-09-05 21:47:34 +0000849+ G e t T h r e s h o l d M a p F i l e %
850% %
851% %
852% %
853%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
854%
855% GetThresholdMapFile() look for a given threshold map name or alias in the
856% given XML file data, and return the allocated the map when found.
857%
858% The format of the ListThresholdMaps method is:
859%
860% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
861% const char *map_id,ExceptionInfo *exception)
862%
863% A description of each parameter follows.
864%
865% o xml: The threshold map list in XML format.
866%
867% o filename: The threshold map XML filename.
868%
869% o map_id: ID of the map to look for in XML list.
870%
871% o exception: return any errors or warnings in this structure.
872%
873*/
cristybd0ebf02011-09-29 01:19:42 +0000874static ThresholdMap *GetThresholdMapFile(const char *xml,
cristy3ed852e2009-09-05 21:47:34 +0000875 const char *filename,const char *map_id,ExceptionInfo *exception)
876{
cristyb9eb87b2011-09-29 01:15:19 +0000877 char
878 *p;
879
cristy3ed852e2009-09-05 21:47:34 +0000880 const char
cristyb9eb87b2011-09-29 01:15:19 +0000881 *attribute,
cristy3ed852e2009-09-05 21:47:34 +0000882 *content;
883
884 double
885 value;
886
cristyb9eb87b2011-09-29 01:15:19 +0000887 register ssize_t
888 i;
cristy3ed852e2009-09-05 21:47:34 +0000889
890 ThresholdMap
891 *map;
892
cristyb9eb87b2011-09-29 01:15:19 +0000893 XMLTreeInfo
894 *description,
895 *levels,
896 *threshold,
897 *thresholds;
898
899 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
900 "Loading threshold map file \"%s\" ...",filename);
901 map=(ThresholdMap *) NULL;
902 thresholds=NewXMLTree(xml,exception);
903 if (thresholds == (XMLTreeInfo *) NULL)
904 return(map);
905 for (threshold=GetXMLTreeChild(thresholds,"threshold");
906 threshold != (XMLTreeInfo *) NULL;
907 threshold=GetNextXMLTreeTag(threshold))
908 {
909 attribute=GetXMLTreeAttribute(threshold,"map");
910 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
911 break;
912 attribute=GetXMLTreeAttribute(threshold,"alias");
913 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
914 break;
915 }
916 if (threshold == (XMLTreeInfo *) NULL)
917 return(map);
918 description=GetXMLTreeChild(threshold,"description");
919 if (description == (XMLTreeInfo *) NULL)
920 {
921 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
922 "XmlMissingElement", "<description>, map \"%s\"",map_id);
923 thresholds=DestroyXMLTree(thresholds);
924 return(map);
925 }
926 levels=GetXMLTreeChild(threshold,"levels");
927 if (levels == (XMLTreeInfo *) NULL)
928 {
929 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
930 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
931 thresholds=DestroyXMLTree(thresholds);
932 return(map);
933 }
934 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
935 if (map == (ThresholdMap *) NULL)
936 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
937 map->map_id=(char *) NULL;
938 map->description=(char *) NULL;
939 map->levels=(ssize_t *) NULL;
940 attribute=GetXMLTreeAttribute(threshold,"map");
941 if (attribute != (char *) NULL)
942 map->map_id=ConstantString(attribute);
943 content=GetXMLTreeContent(description);
944 if (content != (char *) NULL)
945 map->description=ConstantString(content);
946 attribute=GetXMLTreeAttribute(levels,"width");
947 if (attribute == (char *) NULL)
948 {
949 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
950 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
951 thresholds=DestroyXMLTree(thresholds);
952 map=DestroyThresholdMap(map);
953 return(map);
954 }
955 map->width=StringToUnsignedLong(attribute);
956 if (map->width == 0)
957 {
958 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
959 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
960 thresholds=DestroyXMLTree(thresholds);
961 map=DestroyThresholdMap(map);
962 return(map);
963 }
964 attribute=GetXMLTreeAttribute(levels,"height");
965 if (attribute == (char *) NULL)
966 {
967 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
968 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
969 thresholds=DestroyXMLTree(thresholds);
970 map=DestroyThresholdMap(map);
971 return(map);
972 }
973 map->height=StringToUnsignedLong(attribute);
974 if (map->height == 0)
975 {
976 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
977 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
978 thresholds=DestroyXMLTree(thresholds);
979 map=DestroyThresholdMap(map);
980 return(map);
981 }
982 attribute=GetXMLTreeAttribute(levels,"divisor");
983 if (attribute == (char *) NULL)
984 {
985 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
986 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
987 thresholds=DestroyXMLTree(thresholds);
988 map=DestroyThresholdMap(map);
989 return(map);
990 }
991 map->divisor=(ssize_t) StringToLong(attribute);
992 if (map->divisor < 2)
993 {
994 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
995 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
996 thresholds=DestroyXMLTree(thresholds);
997 map=DestroyThresholdMap(map);
998 return(map);
999 }
1000 content=GetXMLTreeContent(levels);
1001 if (content == (char *) NULL)
1002 {
1003 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1004 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1005 thresholds=DestroyXMLTree(thresholds);
1006 map=DestroyThresholdMap(map);
1007 return(map);
1008 }
1009 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1010 sizeof(*map->levels));
1011 if (map->levels == (ssize_t *) NULL)
1012 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1013 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1014 {
1015 map->levels[i]=(ssize_t) strtol(content,&p,10);
1016 if (p == content)
1017 {
1018 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1019 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1020 thresholds=DestroyXMLTree(thresholds);
1021 map=DestroyThresholdMap(map);
1022 return(map);
1023 }
1024 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1025 {
1026 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1027 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1028 (double) map->levels[i],map_id);
1029 thresholds=DestroyXMLTree(thresholds);
1030 map=DestroyThresholdMap(map);
1031 return(map);
1032 }
1033 content=p;
1034 }
1035 value=(double) strtol(content,&p,10);
1036 (void) value;
1037 if (p != content)
1038 {
1039 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1040 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1041 thresholds=DestroyXMLTree(thresholds);
1042 map=DestroyThresholdMap(map);
1043 return(map);
1044 }
1045 thresholds=DestroyXMLTree(thresholds);
cristy3ed852e2009-09-05 21:47:34 +00001046 return(map);
1047}
1048
1049/*
1050%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1051% %
1052% %
1053% %
1054+ L i s t T h r e s h o l d M a p F i l e %
1055% %
1056% %
1057% %
1058%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1059%
1060% ListThresholdMapFile() lists the threshold maps and their descriptions
1061% in the given XML file data.
1062%
1063% The format of the ListThresholdMaps method is:
1064%
1065% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1066% const char *filename,ExceptionInfo *exception)
1067%
1068% A description of each parameter follows.
1069%
1070% o file: An pointer to the output FILE.
1071%
1072% o xml: The threshold map list in XML format.
1073%
1074% o filename: The threshold map XML filename.
1075%
1076% o exception: return any errors or warnings in this structure.
1077%
1078*/
1079MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1080 const char *filename,ExceptionInfo *exception)
1081{
cristy5f9f2462011-09-28 23:37:58 +00001082 const char
1083 *alias,
1084 *content,
1085 *map;
1086
1087 XMLTreeInfo
1088 *description,
1089 *threshold,
1090 *thresholds;
cristy3ed852e2009-09-05 21:47:34 +00001091
1092 assert( xml != (char *)NULL );
1093 assert( file != (FILE *)NULL );
cristy3ed852e2009-09-05 21:47:34 +00001094 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1095 "Loading threshold map file \"%s\" ...",filename);
1096 thresholds=NewXMLTree(xml,exception);
1097 if ( thresholds == (XMLTreeInfo *)NULL )
1098 return(MagickFalse);
cristy1e604812011-05-19 18:07:50 +00001099 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1100 (void) FormatLocaleFile(file,
1101 "----------------------------------------------------\n");
cristy5f9f2462011-09-28 23:37:58 +00001102 threshold=GetXMLTreeChild(thresholds,"threshold");
cristyb9eb87b2011-09-29 01:15:19 +00001103 for ( ; threshold != (XMLTreeInfo *) NULL;
1104 threshold=GetNextXMLTreeTag(threshold))
cristy3ed852e2009-09-05 21:47:34 +00001105 {
cristy5f9f2462011-09-28 23:37:58 +00001106 map=GetXMLTreeAttribute(threshold,"map");
1107 if (map == (char *) NULL)
1108 {
1109 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1110 "XmlMissingAttribute", "<map>");
1111 thresholds=DestroyXMLTree(thresholds);
1112 return(MagickFalse);
1113 }
1114 alias=GetXMLTreeAttribute(threshold,"alias");
cristy3ed852e2009-09-05 21:47:34 +00001115 description=GetXMLTreeChild(threshold,"description");
cristy5f9f2462011-09-28 23:37:58 +00001116 if (description == (XMLTreeInfo *) NULL)
1117 {
1118 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyb9eb87b2011-09-29 01:15:19 +00001119 "XmlMissingElement", "<description>, map \"%s\"",map);
cristy5f9f2462011-09-28 23:37:58 +00001120 thresholds=DestroyXMLTree(thresholds);
1121 return(MagickFalse);
1122 }
cristy3ed852e2009-09-05 21:47:34 +00001123 content=GetXMLTreeContent(description);
cristy5f9f2462011-09-28 23:37:58 +00001124 if (content == (char *) NULL)
1125 {
1126 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1127 "XmlMissingContent", "<description>, map \"%s\"", map);
1128 thresholds=DestroyXMLTree(thresholds);
1129 return(MagickFalse);
1130 }
cristy1e604812011-05-19 18:07:50 +00001131 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1132 content);
cristy3ed852e2009-09-05 21:47:34 +00001133 }
1134 thresholds=DestroyXMLTree(thresholds);
1135 return(MagickTrue);
1136}
1137
1138/*
1139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1140% %
1141% %
1142% %
1143% L i s t T h r e s h o l d M a p s %
1144% %
1145% %
1146% %
1147%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1148%
1149% ListThresholdMaps() lists the threshold maps and their descriptions
1150% as defined by "threshold.xml" to a file.
1151%
1152% The format of the ListThresholdMaps method is:
1153%
1154% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1155%
1156% A description of each parameter follows.
1157%
1158% o file: An pointer to the output FILE.
1159%
1160% o exception: return any errors or warnings in this structure.
1161%
1162*/
1163MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1164 ExceptionInfo *exception)
1165{
1166 const StringInfo
1167 *option;
1168
1169 LinkedListInfo
1170 *options;
1171
1172 MagickStatusType
1173 status;
1174
1175 status=MagickFalse;
cristy5f9f2462011-09-28 23:37:58 +00001176 if (file == (FILE *) NULL)
1177 file=stdout;
cristy3ed852e2009-09-05 21:47:34 +00001178 options=GetConfigureOptions(ThresholdsFilename,exception);
cristy1e604812011-05-19 18:07:50 +00001179 (void) FormatLocaleFile(file,
1180 "\n Threshold Maps for Ordered Dither Operations\n");
cristy5f9f2462011-09-28 23:37:58 +00001181 while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1182 (const StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001183 {
cristyb51dff52011-05-19 16:55:47 +00001184 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
cristy3ed852e2009-09-05 21:47:34 +00001185 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1186 GetStringInfoPath(option),exception);
1187 }
1188 options=DestroyConfigureOptions(options);
1189 return(status != 0 ? MagickTrue : MagickFalse);
1190}
1191
1192/*
1193%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1194% %
1195% %
1196% %
cristy3ed852e2009-09-05 21:47:34 +00001197% O r d e r e d P o s t e r i z e I m a g e %
1198% %
1199% %
1200% %
1201%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1202%
1203% OrderedPosterizeImage() will perform a ordered dither based on a number
1204% of pre-defined dithering threshold maps, but over multiple intensity
1205% levels, which can be different for different channels, according to the
1206% input argument.
1207%
1208% The format of the OrderedPosterizeImage method is:
1209%
1210% MagickBooleanType OrderedPosterizeImage(Image *image,
1211% const char *threshold_map,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001212%
1213% A description of each parameter follows:
1214%
1215% o image: the image.
1216%
cristy3ed852e2009-09-05 21:47:34 +00001217% o threshold_map: A string containing the name of the threshold dither
1218% map to use, followed by zero or more numbers representing the number
1219% of color levels tho dither between.
1220%
cristyf998fb32011-04-27 23:00:47 +00001221% Any level number less than 2 will be equivalent to 2, and means only
cristy3ed852e2009-09-05 21:47:34 +00001222% binary dithering will be applied to each color channel.
1223%
1224% No numbers also means a 2 level (bitmap) dither will be applied to all
1225% channels, while a single number is the number of levels applied to each
1226% channel in sequence. More numbers will be applied in turn to each of
1227% the color channels.
1228%
1229% For example: "o3x3,6" will generate a 6 level posterization of the
1230% image with a ordered 3x3 diffused pixel dither being applied between
1231% each level. While checker,8,8,4 will produce a 332 colormaped image
1232% with only a single checkerboard hash pattern (50% grey) between each
1233% color level, to basically double the number of color levels with
1234% a bare minimim of dithering.
1235%
1236% o exception: return any errors or warnings in this structure.
1237%
1238*/
1239MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1240 const char *threshold_map,ExceptionInfo *exception)
1241{
cristy3ed852e2009-09-05 21:47:34 +00001242#define DitherImageTag "Dither/Image"
1243
cristyc4c8d132010-01-07 01:58:38 +00001244 CacheView
1245 *image_view;
1246
cristy4e0b82a2011-09-29 12:47:44 +00001247 char
1248 token[MaxTextExtent];
1249
1250 const char
1251 *p;
cristy3ed852e2009-09-05 21:47:34 +00001252
1253 MagickBooleanType
1254 status;
1255
cristy5f959472010-05-27 22:19:46 +00001256 MagickOffsetType
1257 progress;
1258
cristy4e0b82a2011-09-29 12:47:44 +00001259 MagickRealType
cristy5f95f4f2011-10-23 01:01:01 +00001260 levels[CompositePixelChannel];
cristy4e0b82a2011-09-29 12:47:44 +00001261
1262 register ssize_t
1263 i;
1264
cristy5f959472010-05-27 22:19:46 +00001265 ssize_t
1266 y;
1267
cristy3ed852e2009-09-05 21:47:34 +00001268 ThresholdMap
1269 *map;
1270
cristy3ed852e2009-09-05 21:47:34 +00001271 assert(image != (Image *) NULL);
1272 assert(image->signature == MagickSignature);
1273 if (image->debug != MagickFalse)
1274 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1275 assert(exception != (ExceptionInfo *) NULL);
1276 assert(exception->signature == MagickSignature);
1277 if (threshold_map == (const char *) NULL)
1278 return(MagickTrue);
cristy4e0b82a2011-09-29 12:47:44 +00001279 p=(char *) threshold_map;
1280 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1281 (*p != '\0'))
1282 p++;
1283 threshold_map=p;
1284 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1285 (*p != '\0'))
cristy3ed852e2009-09-05 21:47:34 +00001286 {
cristy4e0b82a2011-09-29 12:47:44 +00001287 if ((p-threshold_map) >= (MaxTextExtent-1))
1288 break;
1289 token[p-threshold_map]=(*p);
1290 p++;
cristy3ed852e2009-09-05 21:47:34 +00001291 }
cristy4e0b82a2011-09-29 12:47:44 +00001292 token[p-threshold_map]='\0';
1293 map=GetThresholdMap(token,exception);
1294 if (map == (ThresholdMap *) NULL)
1295 {
1296 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1297 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
cristy574cc262011-08-05 01:23:58 +00001298 return(MagickFalse);
cristy4e0b82a2011-09-29 12:47:44 +00001299 }
1300 for (i=0; i < MaxPixelChannels; i++)
1301 levels[i]=2.0;
1302 p=strchr((char *) threshold_map,',');
1303 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1304 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1305 {
1306 GetMagickToken(p,&p,token);
1307 if (*token == ',')
1308 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001309 levels[i]=StringToDouble(token,(char **) NULL);
cristy4e0b82a2011-09-29 12:47:44 +00001310 }
1311 for (i=0; i < MaxPixelChannels; i++)
1312 if (fabs(levels[i]) >= 1)
1313 levels[i]-=1.0;
1314 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1315 return(MagickFalse);
1316 status=MagickTrue;
1317 progress=0;
1318 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001319#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00001320 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001321#endif
cristy4e0b82a2011-09-29 12:47:44 +00001322 for (y=0; y < (ssize_t) image->rows; y++)
1323 {
1324 register ssize_t
1325 x;
1326
1327 register Quantum
1328 *restrict q;
1329
1330 if (status == MagickFalse)
1331 continue;
1332 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1333 if (q == (Quantum *) NULL)
1334 {
1335 status=MagickFalse;
1336 continue;
1337 }
1338 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001339 {
cristybb503372010-05-27 20:51:26 +00001340 register ssize_t
cristy4e0b82a2011-09-29 12:47:44 +00001341 i;
cristy3ed852e2009-09-05 21:47:34 +00001342
cristy4e0b82a2011-09-29 12:47:44 +00001343 ssize_t
1344 n;
cristy3ed852e2009-09-05 21:47:34 +00001345
cristy4e0b82a2011-09-29 12:47:44 +00001346 n=0;
cristy10a6c612012-01-29 21:41:05 +00001347 if (GetPixelMask(image,q) != 0)
1348 {
1349 q+=GetPixelChannels(image);
1350 continue;
1351 }
cristy4e0b82a2011-09-29 12:47:44 +00001352 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +00001353 {
cristyabace412011-12-11 15:56:53 +00001354 PixelChannel
1355 channel;
1356
cristy4e0b82a2011-09-29 12:47:44 +00001357 PixelTrait
1358 traits;
1359
1360 ssize_t
cristy3f5d8152011-09-29 13:00:19 +00001361 level,
1362 threshold;
cristy3ed852e2009-09-05 21:47:34 +00001363
cristyabace412011-12-11 15:56:53 +00001364 channel=GetPixelChannelMapChannel(image,i);
1365 traits=GetPixelChannelMapTraits(image,channel);
cristy4e0b82a2011-09-29 12:47:44 +00001366 if ((traits & UpdatePixelTrait) == 0)
1367 continue;
cristy3f5d8152011-09-29 13:00:19 +00001368 if (fabs(levels[n++]) < MagickEpsilon)
1369 continue;
1370 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1371 level=threshold/(map->divisor-1);
1372 threshold-=level*(map->divisor-1);
1373 q[i]=RoundToQuantum((level+(threshold >= map->levels[(x % map->width)+
1374 map->width*(y % map->height)]))*QuantumRange/levels[n]);
cristy4e0b82a2011-09-29 12:47:44 +00001375 n++;
cristy3ed852e2009-09-05 21:47:34 +00001376 }
cristy4e0b82a2011-09-29 12:47:44 +00001377 q+=GetPixelChannels(image);
1378 }
1379 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1380 status=MagickFalse;
1381 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1382 {
1383 MagickBooleanType
1384 proceed;
cristy3ed852e2009-09-05 21:47:34 +00001385
cristyb5d5f722009-11-04 03:03:49 +00001386#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy4e0b82a2011-09-29 12:47:44 +00001387#pragma omp critical (MagickCore_OrderedPosterizeImage)
cristy3ed852e2009-09-05 21:47:34 +00001388#endif
cristy4e0b82a2011-09-29 12:47:44 +00001389 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1390 if (proceed == MagickFalse)
1391 status=MagickFalse;
1392 }
cristy3ed852e2009-09-05 21:47:34 +00001393 }
cristy4e0b82a2011-09-29 12:47:44 +00001394 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00001395 map=DestroyThresholdMap(map);
1396 return(MagickTrue);
1397}
1398
1399/*
1400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1401% %
1402% %
1403% %
1404% R a n d o m T h r e s h o l d I m a g e %
1405% %
1406% %
1407% %
1408%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1409%
1410% RandomThresholdImage() changes the value of individual pixels based on the
1411% intensity of each pixel compared to a random threshold. The result is a
1412% low-contrast, two color image.
1413%
1414% The format of the RandomThresholdImage method is:
1415%
cristyf4ad9df2011-07-08 16:49:03 +00001416% MagickBooleanType RandomThresholdImage(Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001417% const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001418%
1419% A description of each parameter follows:
1420%
1421% o image: the image.
1422%
cristy3ed852e2009-09-05 21:47:34 +00001423% o thresholds: a geometry string containing low,high thresholds. If the
1424% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1425% is performed instead.
1426%
1427% o exception: return any errors or warnings in this structure.
1428%
1429*/
cristy3ed852e2009-09-05 21:47:34 +00001430MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1431 const char *thresholds,ExceptionInfo *exception)
1432{
cristy3ed852e2009-09-05 21:47:34 +00001433#define ThresholdImageTag "Threshold/Image"
1434
cristyfa112112010-01-04 17:48:07 +00001435 CacheView
1436 *image_view;
1437
cristy3ed852e2009-09-05 21:47:34 +00001438 GeometryInfo
1439 geometry_info;
1440
1441 MagickStatusType
1442 flags;
1443
cristy3ed852e2009-09-05 21:47:34 +00001444 MagickBooleanType
1445 status;
1446
cristy5f959472010-05-27 22:19:46 +00001447 MagickOffsetType
1448 progress;
1449
cristy4c08aed2011-07-01 19:47:50 +00001450 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001451 threshold;
1452
1453 MagickRealType
1454 min_threshold,
1455 max_threshold;
1456
1457 RandomInfo
cristyfa112112010-01-04 17:48:07 +00001458 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00001459
cristy5f959472010-05-27 22:19:46 +00001460 ssize_t
1461 y;
1462
cristy3ed852e2009-09-05 21:47:34 +00001463 assert(image != (Image *) NULL);
1464 assert(image->signature == MagickSignature);
1465 if (image->debug != MagickFalse)
1466 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1467 assert(exception != (ExceptionInfo *) NULL);
1468 assert(exception->signature == MagickSignature);
1469 if (thresholds == (const char *) NULL)
1470 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00001471 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001472 min_threshold=0.0;
1473 max_threshold=(MagickRealType) QuantumRange;
1474 flags=ParseGeometry(thresholds,&geometry_info);
1475 min_threshold=geometry_info.rho;
1476 max_threshold=geometry_info.sigma;
1477 if ((flags & SigmaValue) == 0)
1478 max_threshold=min_threshold;
1479 if (strchr(thresholds,'%') != (char *) NULL)
1480 {
1481 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1482 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1483 }
cristy3ed852e2009-09-05 21:47:34 +00001484 /*
1485 Random threshold image.
1486 */
1487 status=MagickTrue;
1488 progress=0;
cristy574cc262011-08-05 01:23:58 +00001489 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1490 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001491 random_info=AcquireRandomInfoThreadSet();
1492 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001493#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00001494 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001495#endif
cristybb503372010-05-27 20:51:26 +00001496 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001497 {
cristy5c9e6f22010-09-17 17:31:01 +00001498 const int
1499 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001500
cristy4c08aed2011-07-01 19:47:50 +00001501 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001502 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001503
cristy5c9e6f22010-09-17 17:31:01 +00001504 register ssize_t
1505 x;
1506
cristy3ed852e2009-09-05 21:47:34 +00001507 if (status == MagickFalse)
1508 continue;
1509 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001510 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001511 {
1512 status=MagickFalse;
1513 continue;
1514 }
cristybb503372010-05-27 20:51:26 +00001515 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001516 {
cristy5f9f2462011-09-28 23:37:58 +00001517 register ssize_t
1518 i;
1519
cristy10a6c612012-01-29 21:41:05 +00001520 if (GetPixelMask(image,q) != 0)
1521 {
1522 q+=GetPixelChannels(image);
1523 continue;
1524 }
cristy5f9f2462011-09-28 23:37:58 +00001525 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1526 {
1527 MagickRealType
1528 threshold;
1529
cristyabace412011-12-11 15:56:53 +00001530 PixelChannel
1531 channel;
1532
cristy5f9f2462011-09-28 23:37:58 +00001533 PixelTrait
1534 traits;
1535
cristyabace412011-12-11 15:56:53 +00001536 channel=GetPixelChannelMapChannel(image,i);
1537 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +00001538 if ((traits & UpdatePixelTrait) == 0)
1539 continue;
1540 if ((MagickRealType) q[i] < min_threshold)
1541 threshold=min_threshold;
1542 else
1543 if ((MagickRealType) q[i] > max_threshold)
1544 threshold=max_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001545 else
cristy5f9f2462011-09-28 23:37:58 +00001546 threshold=(MagickRealType) (QuantumRange*
1547 GetPseudoRandomValue(random_info[id]));
1548 q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
1549 QuantumRange);
1550 }
cristyed231572011-07-14 02:18:59 +00001551 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001552 }
1553 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1554 status=MagickFalse;
1555 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1556 {
1557 MagickBooleanType
1558 proceed;
1559
cristyb5d5f722009-11-04 03:03:49 +00001560#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001561 #pragma omp critical (MagickCore_RandomThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001562#endif
1563 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1564 image->rows);
1565 if (proceed == MagickFalse)
1566 status=MagickFalse;
1567 }
1568 }
1569 image_view=DestroyCacheView(image_view);
1570 random_info=DestroyRandomInfoThreadSet(random_info);
1571 return(status);
1572}
1573
1574/*
1575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1576% %
1577% %
1578% %
1579% W h i t e T h r e s h o l d I m a g e %
1580% %
1581% %
1582% %
1583%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1584%
1585% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001586% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001587% unchanged.
1588%
1589% The format of the WhiteThresholdImage method is:
1590%
cristyf4ad9df2011-07-08 16:49:03 +00001591% MagickBooleanType WhiteThresholdImage(Image *image,
1592% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001593%
1594% A description of each parameter follows:
1595%
1596% o image: the image.
1597%
cristy3ed852e2009-09-05 21:47:34 +00001598% o threshold: Define the threshold value.
1599%
1600% o exception: return any errors or warnings in this structure.
1601%
1602*/
1603MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +00001604 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001605{
1606#define ThresholdImageTag "Threshold/Image"
1607
cristy5f959472010-05-27 22:19:46 +00001608 CacheView
1609 *image_view;
1610
cristy3ed852e2009-09-05 21:47:34 +00001611 GeometryInfo
1612 geometry_info;
1613
cristy3ed852e2009-09-05 21:47:34 +00001614 MagickBooleanType
1615 status;
1616
cristy5f959472010-05-27 22:19:46 +00001617 MagickOffsetType
1618 progress;
1619
cristy5f9f2462011-09-28 23:37:58 +00001620 MagickRealType
1621 threshold[5];
1622
cristy3ed852e2009-09-05 21:47:34 +00001623 MagickStatusType
1624 flags;
1625
cristy5f9f2462011-09-28 23:37:58 +00001626 register ssize_t
1627 i;
1628
cristy5f959472010-05-27 22:19:46 +00001629 ssize_t
1630 y;
cristy3ed852e2009-09-05 21:47:34 +00001631
1632 assert(image != (Image *) NULL);
1633 assert(image->signature == MagickSignature);
1634 if (image->debug != MagickFalse)
1635 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1636 if (thresholds == (const char *) NULL)
1637 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +00001638 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001639 return(MagickFalse);
1640 flags=ParseGeometry(thresholds,&geometry_info);
cristy5f9f2462011-09-28 23:37:58 +00001641 for (i=0; i < 5; i++)
1642 threshold[i]=geometry_info.rho;
1643 if ((flags & SigmaValue) != 0)
1644 threshold[1]=geometry_info.sigma;
1645 if ((flags & XiValue) != 0)
1646 threshold[2]=geometry_info.xi;
1647 if ((flags & PsiValue) != 0)
1648 threshold[3]=geometry_info.psi;
1649 if ((flags & ChiValue) != 0)
1650 threshold[4]=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +00001651 if ((flags & PercentValue) != 0)
cristy5f9f2462011-09-28 23:37:58 +00001652 for (i=0; i < 5; i++)
1653 threshold[i]*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001654 /*
1655 White threshold image.
1656 */
1657 status=MagickTrue;
1658 progress=0;
1659 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001660#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy78778cb2012-01-17 14:48:47 +00001661 #pragma omp parallel for schedule(static,8) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001662#endif
cristybb503372010-05-27 20:51:26 +00001663 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001664 {
cristybb503372010-05-27 20:51:26 +00001665 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001666 x;
1667
cristy4c08aed2011-07-01 19:47:50 +00001668 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001669 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001670
1671 if (status == MagickFalse)
1672 continue;
1673 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001674 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001675 {
1676 status=MagickFalse;
1677 continue;
1678 }
cristybb503372010-05-27 20:51:26 +00001679 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001680 {
cristy5f9f2462011-09-28 23:37:58 +00001681 register ssize_t
1682 i;
1683
1684 ssize_t
1685 n;
1686
1687 n=0;
cristy10a6c612012-01-29 21:41:05 +00001688 if (GetPixelMask(image,q) != 0)
1689 {
1690 q+=GetPixelChannels(image);
1691 continue;
1692 }
cristy5f9f2462011-09-28 23:37:58 +00001693 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1694 {
cristyabace412011-12-11 15:56:53 +00001695 PixelChannel
1696 channel;
1697
cristy5f9f2462011-09-28 23:37:58 +00001698 PixelTrait
1699 traits;
1700
cristyabace412011-12-11 15:56:53 +00001701 channel=GetPixelChannelMapChannel(image,i);
1702 traits=GetPixelChannelMapTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +00001703 if ((traits & UpdatePixelTrait) == 0)
1704 continue;
1705 if ((MagickRealType) q[i] > threshold[n++ % 5])
1706 q[i]=QuantumRange;
1707 }
cristyed231572011-07-14 02:18:59 +00001708 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001709 }
1710 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1711 status=MagickFalse;
1712 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1713 {
1714 MagickBooleanType
1715 proceed;
1716
cristyb5d5f722009-11-04 03:03:49 +00001717#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001718 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001719#endif
1720 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1721 image->rows);
1722 if (proceed == MagickFalse)
1723 status=MagickFalse;
1724 }
1725 }
1726 image_view=DestroyCacheView(image_view);
1727 return(status);
1728}