threshold.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %       TTTTT  H   H  RRRR   EEEEE  SSSSS  H   H   OOO   L      DDDD          %
00007 %         T    H   H  R   R  E      SS     H   H  O   O  L      D   D         %
00008 %         T    HHHHH  RRRR   EEE     SSS   HHHHH  O   O  L      D   D         %
00009 %         T    H   H  R R    E         SS  H   H  O   O  L      D   D         %
00010 %         T    H   H  R  R   EEEEE  SSSSS  H   H   OOO   LLLLL  DDDD          %
00011 %                                                                             %
00012 %                                                                             %
00013 %                      MagickCore Image Threshold Methods                     %
00014 %                                                                             %
00015 %                               Software Design                               %
00016 %                                 John Cristy                                 %
00017 %                                 October 1996                                %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2008 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 %
00037 %
00038 */
00039 
00040 /*
00041   Include declarations.
00042 */
00043 #include "magick/studio.h"
00044 #include "magick/property.h"
00045 #include "magick/blob.h"
00046 #include "magick/cache-view.h"
00047 #include "magick/color.h"
00048 #include "magick/color-private.h"
00049 #include "magick/colorspace.h"
00050 #include "magick/configure.h"
00051 #include "magick/constitute.h"
00052 #include "magick/decorate.h"
00053 #include "magick/draw.h"
00054 #include "magick/enhance.h"
00055 #include "magick/exception.h"
00056 #include "magick/exception-private.h"
00057 #include "magick/effect.h"
00058 #include "magick/fx.h"
00059 #include "magick/gem.h"
00060 #include "magick/geometry.h"
00061 #include "magick/image-private.h"
00062 #include "magick/list.h"
00063 #include "magick/log.h"
00064 #include "magick/memory_.h"
00065 #include "magick/monitor.h"
00066 #include "magick/monitor-private.h"
00067 #include "magick/montage.h"
00068 #include "magick/pixel-private.h"
00069 #include "magick/quantize.h"
00070 #include "magick/quantum.h"
00071 #include "magick/random_.h"
00072 #include "magick/resize.h"
00073 #include "magick/resource_.h"
00074 #include "magick/segment.h"
00075 #include "magick/shear.h"
00076 #include "magick/signature-private.h"
00077 #include "magick/string_.h"
00078 #include "magick/transform.h"
00079 #include "magick/threshold.h"
00080 #include "magick/option.h"
00081 #include "magick/xml-tree.h"
00082 
00083 /*
00084   Define declarations.
00085 */
00086 #define ThresholdsFilename  "thresholds.xml"
00087 
00088 /*
00089   Typedef declarations.
00090 */
00091 struct _ThresholdMap
00092 {
00093   char
00094     *map_id,
00095     *description;
00096 
00097   unsigned long
00098     width,
00099     height;
00100 
00101   long
00102     divisor,
00103     *levels;
00104 };
00105 
00106 /*
00107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00108 %                                                                             %
00109 %                                                                             %
00110 %                                                                             %
00111 %     A d a p t i v e T h r e s h o l d I m a g e                             %
00112 %                                                                             %
00113 %                                                                             %
00114 %                                                                             %
00115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00116 %
00117 %  AdaptiveThresholdImage() selects an individual threshold for each pixel
00118 %  based on the range of intensity values in its local neighborhood.  This
00119 %  allows for thresholding of an image whose global intensity histogram
00120 %  doesn't contain distinctive peaks.
00121 %
00122 %  The format of the AdaptiveThresholdImage method is:
00123 %
00124 %      Image *AdaptiveThresholdImage(const Image *image,
00125 %        const unsigned long width,const unsigned long height,
00126 %        const long offset,ExceptionInfo *exception)
00127 %
00128 %  A description of each parameter follows:
00129 %
00130 %    o image: the image.
00131 %
00132 %    o width: the width of the local neighborhood.
00133 %
00134 %    o height: the height of the local neighborhood.
00135 %
00136 %    o offset: the mean offset.
00137 %
00138 %    o exception: return any errors or warnings in this structure.
00139 %
00140 */
00141 MagickExport Image *AdaptiveThresholdImage(const Image *image,
00142   const unsigned long width,const unsigned long height,const long offset,
00143   ExceptionInfo *exception)
00144 {
00145 #define ThresholdImageTag  "Threshold/Image"
00146 
00147   Image
00148     *threshold_image;
00149 
00150   long
00151     progress,
00152     y;
00153 
00154   MagickBooleanType
00155     status;
00156 
00157   MagickPixelPacket
00158     zero;
00159 
00160   MagickRealType
00161     number_pixels;
00162 
00163   ViewInfo
00164     *image_view,
00165     *threshold_view;
00166 
00167   assert(image != (const Image *) NULL);
00168   assert(image->signature == MagickSignature);
00169   if (image->debug != MagickFalse)
00170     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00171   assert(exception != (ExceptionInfo *) NULL);
00172   assert(exception->signature == MagickSignature);
00173   if ((image->columns < width) || (image->rows < height))
00174     ThrowImageException(OptionError,"ImageSmallerThanRadius");
00175   threshold_image=CloneImage(image,0,0,MagickTrue,exception);
00176   if (threshold_image == (Image *) NULL)
00177     return((Image *) NULL);
00178   if (SetImageStorageClass(threshold_image,DirectClass) == MagickFalse)
00179     {
00180       InheritException(exception,&threshold_image->exception);
00181       threshold_image=DestroyImage(threshold_image);
00182       return((Image *) NULL);
00183     }
00184   /*
00185     Local adaptive threshold.
00186   */
00187   status=MagickTrue;
00188   progress=0;
00189   GetMagickPixelPacket(image,&zero);
00190   number_pixels=(MagickRealType) width*height;
00191   image_view=AcquireCacheView(image);
00192   threshold_view=AcquireCacheView(threshold_image);
00193 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00194   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00195 #endif
00196   for (y=0; y < (long) image->rows; y++)
00197   {
00198     MagickBooleanType
00199       sync;
00200 
00201     register const IndexPacket
00202       *indexes;
00203 
00204     register const PixelPacket
00205       *p;
00206 
00207     register IndexPacket
00208       *threshold_indexes;
00209 
00210     register long
00211       x;
00212 
00213     register PixelPacket
00214       *q;
00215 
00216     if (status == MagickFalse)
00217       continue;
00218     p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-height/2L,
00219       image->columns+width,height,exception);
00220     q=GetCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,1,
00221       exception);
00222     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00223       {
00224         status=MagickFalse;
00225         continue;
00226       }
00227     indexes=GetCacheViewVirtualIndexQueue(image_view);
00228     threshold_indexes=GetCacheViewAuthenticIndexQueue(threshold_view);
00229     for (x=0; x < (long) image->columns; x++)
00230     {
00231       long
00232         v;
00233 
00234       MagickPixelPacket
00235         mean,
00236         pixel;
00237 
00238       register const PixelPacket
00239         *r;
00240 
00241       register long
00242         u;
00243 
00244       pixel=zero;
00245       mean=zero;
00246       r=p;
00247       for (v=0; v < (long) height; v++)
00248       {
00249         for (u=0; u < (long) width; u++)
00250         {
00251           pixel.red+=r[u].red;
00252           pixel.green+=r[u].green;
00253           pixel.blue+=r[u].blue;
00254           pixel.opacity+=r[u].opacity;
00255           if (image->colorspace == CMYKColorspace)
00256             pixel.index=(MagickRealType) indexes[x+(r-p)+u];
00257         }
00258         r+=image->columns+width;
00259       }
00260       mean.red=(MagickRealType) (pixel.red/number_pixels+offset);
00261       mean.green=(MagickRealType) (pixel.green/number_pixels+offset);
00262       mean.blue=(MagickRealType) (pixel.blue/number_pixels+offset);
00263       mean.opacity=(MagickRealType) (pixel.opacity/number_pixels+offset);
00264       if (image->colorspace == CMYKColorspace)
00265         mean.index=(MagickRealType) (pixel.index/number_pixels+offset);
00266       q->red=(Quantum) (((MagickRealType) q->red <= mean.red) ?
00267         0 : QuantumRange);
00268       q->green=(Quantum) (((MagickRealType) q->green <= mean.green) ?
00269         0 : QuantumRange);
00270       q->blue=(Quantum) (((MagickRealType) q->blue <= mean.blue) ?
00271         0 : QuantumRange);
00272       q->opacity=(Quantum) (((MagickRealType) q->opacity <= mean.opacity) ?
00273         0 : QuantumRange);
00274       if (image->colorspace == CMYKColorspace)
00275         threshold_indexes[x]=(IndexPacket) (((MagickRealType)
00276           threshold_indexes[x] <= mean.index) ? 0 : QuantumRange);
00277       p++;
00278       q++;
00279     }
00280     sync=SyncCacheViewAuthenticPixels(threshold_view,exception);
00281     if (sync == MagickFalse)
00282       status=MagickFalse;
00283     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00284       {
00285         MagickBooleanType
00286           proceed;
00287 
00288 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00289   #pragma omp critical
00290 #endif
00291         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
00292           image->rows);
00293         if (proceed == MagickFalse)
00294           status=MagickFalse;
00295       }
00296   }
00297   threshold_view=DestroyCacheView(threshold_view);
00298   image_view=DestroyCacheView(image_view);
00299   if (status == MagickFalse)
00300     threshold_image=DestroyImage(threshold_image);
00301   return(threshold_image);
00302 }
00303 
00304 /*
00305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00306 %                                                                             %
00307 %                                                                             %
00308 %                                                                             %
00309 %     B i l e v e l I m a g e                                                 %
00310 %                                                                             %
00311 %                                                                             %
00312 %                                                                             %
00313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00314 %
00315 %  BilevelImage() changes the value of individual pixels based on the
00316 %  intensity of each pixel channel.  The result is a high-contrast image.
00317 %
00318 %  More precisely each channel value of the image is 'thresholded' so that if
00319 %  it is equal to or less than the given value it is set to zero, while any
00320 %  value greater than that give is set to it maximum or QuantumRange.
00321 %
00322 %  This function is what is used to implement the "-threshold" operator for
00323 %  the command line API.
00324 %
00325 %  If the default channel setting is given the image is thresholded using just
00326 %  the gray 'intensity' of the image, rather than the individual channels.
00327 %
00328 %  The format of the BilevelImageChannel method is:
00329 %
00330 %      MagickBooleanType BilevelImage(Image *image,const double threshold)
00331 %      MagickBooleanType BilevelImageChannel(Image *image,
00332 %        const ChannelType channel,const double threshold)
00333 %
00334 %  A description of each parameter follows:
00335 %
00336 %    o image: the image.
00337 %
00338 %    o channel: the channel type.
00339 %
00340 %    o threshold: define the threshold values.
00341 %
00342 %  Aside: You can get the same results as operator using LevelImageChannels()
00343 %  with the 'threshold' value for both the black_point and the white_point.
00344 %
00345 */
00346 
00347 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
00348 {
00349   MagickBooleanType
00350     status;
00351 
00352   status=BilevelImageChannel(image,DefaultChannels,threshold);
00353   return(status);
00354 }
00355 
00356 MagickExport MagickBooleanType BilevelImageChannel(Image *image,
00357   const ChannelType channel,const double threshold)
00358 {
00359 #define ThresholdImageTag  "Threshold/Image"
00360 
00361   ExceptionInfo
00362     *exception;
00363 
00364   long
00365     progress,
00366     y;
00367 
00368   MagickBooleanType
00369     status;
00370 
00371   ViewInfo
00372     *image_view;
00373 
00374   assert(image != (Image *) NULL);
00375   assert(image->signature == MagickSignature);
00376   if (image->debug != MagickFalse)
00377     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00378   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00379     return(MagickFalse);
00380   /*
00381     Bilevel threshold image.
00382   */
00383   status=MagickTrue;
00384   progress=0;
00385   exception=(&image->exception);
00386   image_view=AcquireCacheView(image);
00387 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00388   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00389 #endif
00390   for (y=0; y < (long) image->rows; y++)
00391   {
00392     register IndexPacket
00393       *indexes;
00394 
00395     register long
00396       x;
00397 
00398     register PixelPacket
00399       *q;
00400 
00401     if (status == MagickFalse)
00402       continue;
00403     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00404     if (q == (PixelPacket *) NULL)
00405       {
00406         status=MagickFalse;
00407         continue;
00408       }
00409     indexes=GetCacheViewAuthenticIndexQueue(image_view);
00410     if (channel == DefaultChannels)
00411       {
00412         for (x=0; x < (long) image->columns; x++)
00413         {
00414           q->red=(Quantum) ((MagickRealType) PixelIntensityToQuantum(q) <=
00415             threshold ? 0 : QuantumRange);
00416           q->green=q->red;
00417           q->blue=q->red;
00418           q++;
00419         }
00420       }
00421     else
00422       for (x=0; x < (long) image->columns; x++)
00423       {
00424         if ((channel & RedChannel) != 0)
00425           q->red=(Quantum) ((MagickRealType) q->red <= threshold ? 0 :
00426             QuantumRange);
00427         if ((channel & GreenChannel) != 0)
00428           q->green=(Quantum) ((MagickRealType) q->green <= threshold ? 0 :
00429             QuantumRange);
00430         if ((channel & BlueChannel) != 0)
00431           q->blue=(Quantum) ((MagickRealType) q->blue <= threshold ? 0 :
00432             QuantumRange);
00433         if ((channel & OpacityChannel) != 0)
00434           {
00435             if (image->matte == MagickFalse)
00436               q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
00437                 0 : QuantumRange);
00438             else
00439               q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
00440                 OpaqueOpacity : TransparentOpacity);
00441           }
00442         if (((channel & IndexChannel) != 0) &&
00443             (image->colorspace == CMYKColorspace))
00444           indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <= threshold ?
00445             0 : QuantumRange);
00446         q++;
00447       }
00448     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00449       status=MagickFalse;
00450     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00451       {
00452         MagickBooleanType
00453           proceed;
00454 
00455 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00456   #pragma omp critical
00457 #endif
00458         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
00459           image->rows);
00460         if (proceed == MagickFalse)
00461           status=MagickFalse;
00462       }
00463   }
00464   image_view=DestroyCacheView(image_view);
00465   return(status);
00466 }
00467 
00468 /*
00469 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00470 %                                                                             %
00471 %                                                                             %
00472 %                                                                             %
00473 %     B l a c k T h r e s h o l d I m a g e                                   %
00474 %                                                                             %
00475 %                                                                             %
00476 %                                                                             %
00477 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00478 %
00479 %  BlackThresholdImage() is like ThresholdImage() but forces all pixels below
00480 %  the threshold into black while leaving all pixels above the threshold
00481 %  unchanged.
00482 %
00483 %  The format of the BlackThresholdImage method is:
00484 %
00485 %      MagickBooleanType BlackThresholdImage(Image *image,const char *threshold)
00486 %      MagickBooleanType BlackThresholdImageChannel(Image *image,
00487 %        const ChannelType channel,const char *threshold,
00488 %        ExceptionInfo *exception)
00489 %
00490 %  A description of each parameter follows:
00491 %
00492 %    o image: the image.
00493 %
00494 %    o channel: the channel or channels to be thresholded.
00495 %
00496 %    o threshold: Define the threshold value.
00497 %
00498 %    o exception: return any errors or warnings in this structure.
00499 %
00500 */
00501 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
00502   const char *threshold)
00503 {
00504   MagickBooleanType
00505     status;
00506 
00507   status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
00508     &image->exception);
00509   return(status);
00510 }
00511 
00512 static inline MagickBooleanType IsLongGrayPixel(const LongPixelPacket *pixel)
00513 {
00514 #if !defined(MAGICKCORE_HDRI_SUPPORT)
00515   if ((pixel->red == pixel->green) && (pixel->green == pixel->blue))
00516     return(MagickTrue);
00517 #else
00518   if ((fabs(pixel->red-pixel->green) <= MagickEpsilon) &&
00519       (fabs(pixel->green-pixel->blue) <= MagickEpsilon))
00520     return(MagickTrue);
00521 #endif
00522   return(MagickFalse);
00523 }
00524 
00525 MagickExport MagickBooleanType BlackThresholdImageChannel(Image *image,
00526   const ChannelType channel,const char *threshold,ExceptionInfo *exception)
00527 {
00528 #define ThresholdImageTag  "Threshold/Image"
00529 
00530   GeometryInfo
00531     geometry_info;
00532 
00533   long
00534     progress,
00535     y;
00536 
00537   LongPixelPacket
00538     pixel;
00539 
00540   MagickBooleanType
00541     status;
00542 
00543   MagickStatusType
00544     flags;
00545 
00546   ViewInfo
00547     *image_view;
00548 
00549   assert(image != (Image *) NULL);
00550   assert(image->signature == MagickSignature);
00551   if (image->debug != MagickFalse)
00552     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00553   if (threshold == (const char *) NULL)
00554     return(MagickTrue);
00555   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00556     return(MagickFalse);
00557   flags=ParseGeometry(threshold,&geometry_info);
00558   pixel.red=(long) (geometry_info.rho+0.5);
00559   pixel.green=(long) (geometry_info.sigma+0.5);
00560   if ((flags & SigmaValue) == 0)
00561     pixel.green=pixel.red;
00562   pixel.blue=(long) (geometry_info.xi+0.5);
00563   if ((flags & XiValue) == 0)
00564     pixel.blue=pixel.red;
00565   pixel.opacity=(long) (geometry_info.psi+0.5);
00566   if ((flags & XiValue) == 0)
00567     pixel.opacity=pixel.red;
00568   pixel.index=(long) (geometry_info.chi+0.5);
00569   if ((flags & ChiValue) == 0)
00570     pixel.index=pixel.red;
00571   if ((flags & PercentValue) != 0)
00572     {
00573       pixel.red*=(QuantumRange/100.0);
00574       pixel.green*=(QuantumRange/100.0);
00575       pixel.blue*=(QuantumRange/100.0);
00576       pixel.opacity*=(QuantumRange/100.0);
00577       pixel.index*=(QuantumRange/100.0);
00578     }
00579   /*
00580     Black threshold image.
00581   */
00582   status=MagickTrue;
00583   progress=0;
00584   image_view=AcquireCacheView(image);
00585 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00586   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00587 #endif
00588   for (y=0; y < (long) image->rows; y++)
00589   {
00590     register IndexPacket
00591       *indexes;
00592 
00593     register long
00594       x;
00595 
00596     register PixelPacket
00597       *q;
00598 
00599     if (status == MagickFalse)
00600       continue;
00601     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00602     if (q == (PixelPacket *) NULL)
00603       {
00604         status=MagickFalse;
00605         continue;
00606       }
00607     indexes=GetCacheViewAuthenticIndexQueue(image_view);
00608     if (IsLongGrayPixel(&pixel) != MagickFalse)
00609       for (x=0; x < (long) image->columns; x++)
00610       {
00611         if ((MagickRealType) PixelIntensityToQuantum(q) < pixel.red)
00612           {
00613             if ((channel & RedChannel) != 0)
00614               q->red=(Quantum) 0;
00615             if ((channel & GreenChannel) != 0)
00616               q->green=(Quantum) 0;
00617             if ((channel & BlueChannel) != 0)
00618               q->blue=(Quantum) 0;
00619           }
00620         q++;
00621       }
00622     else
00623       for (x=0; x < (long) image->columns; x++)
00624       {
00625         if (((channel & RedChannel) != 0) &&
00626             (q->red < pixel.red))
00627           q->red=(Quantum) 0;
00628         if (((channel & GreenChannel) != 0) &&
00629             (q->green < pixel.green))
00630           q->green=(Quantum) 0;
00631         if (((channel & BlueChannel) != 0) &&
00632             (q->blue < pixel.blue))
00633           q->blue=(Quantum) 0;
00634         if (((channel & OpacityChannel) != 0) &&
00635             (q->opacity < pixel.opacity))
00636           q->opacity=(Quantum) 0;
00637         if (((channel & IndexChannel) != 0) &&
00638             (image->colorspace == CMYKColorspace) &&
00639             (indexes[x] < pixel.index))
00640           indexes[x]=(Quantum) 0;
00641         q++;
00642       }
00643     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00644       status=MagickFalse;
00645     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00646       {
00647         MagickBooleanType
00648           proceed;
00649 
00650 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00651   #pragma omp critical
00652 #endif
00653         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
00654           image->rows);
00655         if (proceed == MagickFalse)
00656           status=MagickFalse;
00657       }
00658   }
00659   image_view=DestroyCacheView(image_view);
00660   return(status);
00661 }
00662 
00663 /*
00664 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00665 %                                                                             %
00666 %                                                                             %
00667 %                                                                             %
00668 %  D e s t r o y T h r e s h o l d M a p                                      %
00669 %                                                                             %
00670 %                                                                             %
00671 %                                                                             %
00672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00673 %
00674 %  DestroyThresholdMap() de-allocate the given ThresholdMap
00675 %
00676 %  The format of the ListThresholdMaps method is:
00677 %
00678 %      ThresholdMap *DestroyThresholdMap(Threshold *map)
00679 %
00680 %  A description of each parameter follows.
00681 %
00682 %    o map:    Pointer to the Threshold map to destroy
00683 %
00684 */
00685 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
00686 {
00687   assert(map != (ThresholdMap *) NULL);
00688   if (map->map_id != (char *) NULL)
00689     map->map_id=DestroyString(map->map_id);
00690   if (map->description != (char *) NULL)
00691     map->description=DestroyString(map->description);
00692   if (map->levels != (long *) NULL)
00693     map->levels=(long *) RelinquishMagickMemory(map->levels);
00694   map=(ThresholdMap *) RelinquishMagickMemory(map);
00695   return(map);
00696 }
00697 
00698 /*
00699 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00700 %                                                                             %
00701 %                                                                             %
00702 %                                                                             %
00703 +  G e t T h r e s h o l d M a p F i l e                                      %
00704 %                                                                             %
00705 %                                                                             %
00706 %                                                                             %
00707 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00708 %
00709 %  GetThresholdMapFile() look for a given threshold map name or alias in the
00710 %  given XML file data, and return the allocated the map when found.
00711 %
00712 %  The format of the ListThresholdMaps method is:
00713 %
00714 %      ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
00715 %         const char *map_id,ExceptionInfo *exception)
00716 %
00717 %  A description of each parameter follows.
00718 %
00719 %    o xml:  The threshold map list in XML format.
00720 %
00721 %    o filename:  The threshold map XML filename.
00722 %
00723 %    o map_id:  ID of the map to look for in XML list.
00724 %
00725 %    o exception: return any errors or warnings in this structure.
00726 %
00727 */
00728 MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
00729   const char *filename,const char *map_id,ExceptionInfo *exception)
00730 {
00731   const char *attr, *content;
00732   XMLTreeInfo *thresholds,*threshold,*description,*levels;
00733   ThresholdMap *map;
00734 
00735   map = (ThresholdMap *)NULL;
00736   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
00737     "Loading threshold map file \"%s\" ...",filename);
00738   thresholds=NewXMLTree(xml,exception);
00739   if ( thresholds == (XMLTreeInfo *)NULL )
00740     return(map);
00741 
00742   for( threshold = GetXMLTreeChild(thresholds,"threshold");
00743        threshold != (XMLTreeInfo *)NULL;
00744        threshold = GetNextXMLTreeTag(threshold) ) {
00745     attr = GetXMLTreeAttribute(threshold, "map");
00746     if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
00747       break;
00748     attr = GetXMLTreeAttribute(threshold, "alias");
00749     if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
00750       break;
00751   }
00752   if ( threshold == (XMLTreeInfo *)NULL ) {
00753     return(map);
00754   }
00755   description = GetXMLTreeChild(threshold,"description");
00756   if ( description == (XMLTreeInfo *)NULL ) {
00757     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00758       "XmlMissingElement", "<description>, map \"%s\"", map_id);
00759     thresholds = DestroyXMLTree(thresholds);
00760     return(map);
00761   }
00762   levels = GetXMLTreeChild(threshold,"levels");
00763   if ( levels == (XMLTreeInfo *)NULL ) {
00764     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00765       "XmlMissingElement", "<levels>, map \"%s\"", map_id);
00766     thresholds = DestroyXMLTree(thresholds);
00767     return(map);
00768   }
00769 
00770   /* The map has been found -- Allocate a Threshold Map to return */
00771   map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
00772   if ( map == (ThresholdMap *)NULL )
00773     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
00774   map->map_id = (char *)NULL;
00775   map->description = (char *)NULL;
00776   map->levels = (long *) NULL;
00777 
00778   /* Assign Basic Attributes */
00779   attr = GetXMLTreeAttribute(threshold, "map");
00780   if ( attr != (char *)NULL )
00781     map->map_id = ConstantString(attr);
00782 
00783   content = GetXMLTreeContent(description);
00784   if ( content != (char *)NULL )
00785     map->description = ConstantString(content);
00786 
00787   attr = GetXMLTreeAttribute(levels, "width");
00788   if ( attr == (char *)NULL ) {
00789     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00790       "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
00791     thresholds = DestroyXMLTree(thresholds);
00792     map = DestroyThresholdMap(map);
00793     return(map);
00794   }
00795   map->width = (unsigned long) atoi(attr);
00796   if ( map->width == 0 ) {
00797     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00798      "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
00799     thresholds = DestroyXMLTree(thresholds);
00800     map = DestroyThresholdMap(map);
00801     return(map);
00802   }
00803 
00804   attr = GetXMLTreeAttribute(levels, "height");
00805   if ( attr == (char *)NULL ) {
00806     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00807       "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
00808     thresholds = DestroyXMLTree(thresholds);
00809     map = DestroyThresholdMap(map);
00810     return(map);
00811   }
00812   map->height = (unsigned long) atoi(attr);
00813   if ( map->height == 0 ) {
00814     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00815       "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
00816     thresholds = DestroyXMLTree(thresholds);
00817     map = DestroyThresholdMap(map);
00818     return(map);
00819   }
00820 
00821   attr = GetXMLTreeAttribute(levels, "divisor");
00822   if ( attr == (char *)NULL ) {
00823     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00824       "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
00825     thresholds = DestroyXMLTree(thresholds);
00826     map = DestroyThresholdMap(map);
00827     return(map);
00828   }
00829   map->divisor = atoi(attr);
00830   if ( map->divisor < 2 ) {
00831     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00832       "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
00833     thresholds = DestroyXMLTree(thresholds);
00834     map = DestroyThresholdMap(map);
00835     return(map);
00836   }
00837 
00838   /* Allocate theshold levels array */
00839   content = GetXMLTreeContent(levels);
00840   if ( content == (char *)NULL ) {
00841     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00842       "XmlMissingContent", "<levels>, map \"%s\"", map_id);
00843     thresholds = DestroyXMLTree(thresholds);
00844     map = DestroyThresholdMap(map);
00845     return(map);
00846   }
00847   map->levels=(long *) AcquireQuantumMemory((size_t) map->width,map->height*
00848     sizeof(*map->levels));
00849   if ( map->levels == (long *)NULL )
00850     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
00851   { /* parse levels into integer array */
00852     int i;
00853     char *p;
00854     for( i=0; i< (long) (map->width*map->height); i++) {
00855       map->levels[i] = (int)strtol(content, &p, 10);
00856       if ( p == content ) {
00857         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00858           "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
00859         thresholds = DestroyXMLTree(thresholds);
00860         map = DestroyThresholdMap(map);
00861         return(map);
00862       }
00863       if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
00864         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00865           "XmlInvalidContent", "<level> %ld out of range, map \"%s\"",
00866           map->levels[i], map_id);
00867         thresholds = DestroyXMLTree(thresholds);
00868         map = DestroyThresholdMap(map);
00869         return(map);
00870       }
00871       content = p;
00872     }
00873     (void) strtol(content, &p, 10);
00874     if ( p != content ) {
00875        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00876          "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
00877       thresholds = DestroyXMLTree(thresholds);
00878       map = DestroyThresholdMap(map);
00879       return(map);
00880     }
00881   }
00882 
00883   thresholds = DestroyXMLTree(thresholds);
00884   return(map);
00885 }
00886 
00887 /*
00888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00889 %                                                                             %
00890 %                                                                             %
00891 %                                                                             %
00892 %  G e t T h r e s h o l d M a p                                              %
00893 %                                                                             %
00894 %                                                                             %
00895 %                                                                             %
00896 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00897 %
00898 %  GetThresholdMap() load and search one or more threshold map files for the
00899 %  a map matching the given name or aliase.
00900 %
00901 %  The format of the GetThresholdMap method is:
00902 %
00903 %      ThresholdMap *GetThresholdMap(const char *map_id,
00904 %         ExceptionInfo *exception)
00905 %
00906 %  A description of each parameter follows.
00907 %
00908 %    o map_id:  ID of the map to look for.
00909 %
00910 %    o exception: return any errors or warnings in this structure.
00911 %
00912 */
00913 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
00914   ExceptionInfo *exception)
00915 {
00916   const StringInfo
00917     *option;
00918 
00919   LinkedListInfo
00920     *options;
00921 
00922   ThresholdMap
00923     *map;
00924 
00925   map=(ThresholdMap *)NULL;
00926   options=GetConfigureOptions(ThresholdsFilename,exception);
00927   while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
00928           != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
00929     map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
00930       GetStringInfoPath(option),map_id,exception);
00931   options=DestroyConfigureOptions(options);
00932   return(map);
00933 }
00934 
00935 /*
00936 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00937 %                                                                             %
00938 %                                                                             %
00939 %                                                                             %
00940 +  L i s t T h r e s h o l d M a p F i l e                                    %
00941 %                                                                             %
00942 %                                                                             %
00943 %                                                                             %
00944 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00945 %
00946 %  ListThresholdMapFile() lists the threshold maps and their descriptions
00947 %  in the given XML file data.
00948 %
00949 %  The format of the ListThresholdMaps method is:
00950 %
00951 %      MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
00952 %         const char *filename,ExceptionInfo *exception)
00953 %
00954 %  A description of each parameter follows.
00955 %
00956 %    o file:  An pointer to the output FILE.
00957 %
00958 %    o xml:  The threshold map list in XML format.
00959 %
00960 %    o filename:  The threshold map XML filename.
00961 %
00962 %    o exception: return any errors or warnings in this structure.
00963 %
00964 */
00965 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
00966   const char *filename,ExceptionInfo *exception)
00967 {
00968   XMLTreeInfo *thresholds,*threshold,*description;
00969   const char *map,*alias,*content;
00970 
00971   assert( xml != (char *)NULL );
00972   assert( file != (FILE *)NULL );
00973 
00974   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
00975     "Loading threshold map file \"%s\" ...",filename);
00976   thresholds=NewXMLTree(xml,exception);
00977   if ( thresholds == (XMLTreeInfo *)NULL )
00978     return(MagickFalse);
00979 
00980   (void) fprintf(file,