Leptonica 1.68
C Image Processing Library

morph.c

Go to the documentation of this file.
00001 /*====================================================================*
00002  -  Copyright (C) 2001 Leptonica.  All rights reserved.
00003  -  This software is distributed in the hope that it will be
00004  -  useful, but with NO WARRANTY OF ANY KIND.
00005  -  No author or distributor accepts responsibility to anyone for the
00006  -  consequences of using this software, or for whether it serves any
00007  -  particular purpose or works at all, unless he or she says so in
00008  -  writing.  Everyone is granted permission to copy, modify and
00009  -  redistribute this source code, for commercial or non-commercial
00010  -  purposes, with the following restrictions: (1) the origin of this
00011  -  source code must not be misrepresented; (2) modified versions must
00012  -  be plainly marked as such; and (3) this notice may not be removed
00013  -  or altered from any source or modified source distribution.
00014  *====================================================================*/
00015 
00016 /*
00017  *  morph.c
00018  *
00019  *     Generic binary morphological ops implemented with rasterop
00020  *         PIX     *pixDilate()
00021  *         PIX     *pixErode()
00022  *         PIX     *pixHMT()
00023  *         PIX     *pixOpen()
00024  *         PIX     *pixClose()
00025  *         PIX     *pixCloseSafe()
00026  *         PIX     *pixOpenGeneralized()
00027  *         PIX     *pixCloseGeneralized()
00028  *
00029  *     Binary morphological (raster) ops with brick Sels
00030  *         PIX     *pixDilateBrick()
00031  *         PIX     *pixErodeBrick()
00032  *         PIX     *pixOpenBrick()
00033  *         PIX     *pixCloseBrick()
00034  *         PIX     *pixCloseSafeBrick()
00035  *
00036  *     Binary composed morphological (raster) ops with brick Sels
00037  *         l_int32  selectComposableSels()
00038  *         l_int32  selectComposableSizes()
00039  *         PIX     *pixDilateCompBrick()
00040  *         PIX     *pixErodeCompBrick()
00041  *         PIX     *pixOpenCompBrick()
00042  *         PIX     *pixCloseCompBrick()
00043  *         PIX     *pixCloseSafeCompBrick()
00044  *
00045  *     Functions associated with boundary conditions
00046  *         void     resetMorphBoundaryCondition()
00047  *         l_int32  getMorphBorderPixelColor()
00048  *      
00049  *     Static helpers for arg processing
00050  *         static PIX     *processMorphArgs1()
00051  *         static PIX     *processMorphArgs2()
00052  *
00053  *  You are provided with many simple ways to do binary morphology.
00054  *  In particular, if you are using brick Sels, there are six
00055  *  convenient methods, all specially tailored for separable operations
00056  *  on brick Sels.  A "brick" Sel is a Sel that is a rectangle
00057  *  of solid SEL_HITs with the origin at or near the center.
00058  *  Note that a brick Sel can have one dimension of size 1.
00059  *  This is very common.  All the brick Sel operations are
00060  *  separable, meaning the operation is done first in the horizontal
00061  *  direction and then in the vertical direction.  If one of the
00062  *  dimensions is 1, this is a special case where the operation is
00063  *  only performed in the other direction.
00064  *
00065  *  These six brick Sel methods are enumerated as follows:
00066  *
00067  *  (1) Brick Sels: pix*Brick(), where * = {Dilate, Erode, Open, Close}.
00068  *      These are separable rasterop implementations.  The Sels are
00069  *      automatically generated, used, and destroyed at the end.
00070  *      You can get the result as a new Pix, in-place back into the src Pix,
00071  *      or written to another existing Pix.
00072  *
00073  *  (2) Brick Sels: pix*CompBrick(), where * = {Dilate, Erode, Open, Close}.
00074  *      These are separable, 2-way composite, rasterop implementations.
00075  *      The Sels are automatically generated, used, and destroyed at the end.
00076  *      You can get the result as a new Pix, in-place back into the src Pix,
00077  *      or written to another existing Pix.  For large Sels, these are
00078  *      considerably faster than the corresponding pix*Brick() functions.
00079  *      N.B.:  The size of the Sels that are actually used are typically
00080  *      close to, but not exactly equal to, the size input to the function.
00081  *
00082  *  (3) Brick Sels: pix*BrickDwa(), where * = {Dilate, Erode, Open, Close}.
00083  *      These are separable dwa (destination word accumulation)
00084  *      implementations.  They use auto-gen'd dwa code.  You can get
00085  *      the result as a new Pix, in-place back into the src Pix,
00086  *      or written to another existing Pix.  This is typically
00087  *      about 3x faster than the analogous rasterop pix*Brick()
00088  *      function, but it has the limitation that the Sel size must
00089  *      be less than 63.  This is pre-set to work on a number
00090  *      of pre-generated Sels.  If you want to use other Sels, the
00091  *      code can be auto-gen'd for them; see the instructions in morphdwa.c.
00092  *
00093  *  (4) Same as (1), but you run it through pixMorphSequence(), with
00094  *      the sequence string either compiled in or generated using sprintf.
00095  *      All intermediate images and Sels are created, used and destroyed.
00096  *      You always get the result as a new Pix.  For example, you can
00097  *      specify a separable 11 x 17 brick opening as "o11.17",
00098  *      or you can specify the horizontal and vertical operations
00099  *      explicitly as "o11.1 + o1.11".  See morphseq.c for details.
00100  *
00101  *  (5) Same as (2), but you run it through pixMorphCompSequence(), with
00102  *      the sequence string either compiled in or generated using sprintf.
00103  *      All intermediate images and Sels are created, used and destroyed.
00104  *      You always get the result as a new Pix.  See morphseq.c for details.
00105  *
00106  *  (6) Same as (3), but you run it through pixMorphSequenceDwa(), with
00107  *      the sequence string either compiled in or generated using sprintf.
00108  *      All intermediate images and Sels are created, used and destroyed.
00109  *      You always get the result as a new Pix.  See morphseq.c for details.
00110  *
00111  *  If you are using Sels that are not bricks, you have two choices:
00112  *      (a) simplest: use the basic rasterop implementations (pixDilate(), ...)
00113  *      (b) fastest: generate the destination word accumumlation (dwa)
00114  *          code for your Sels and compile it with the library.
00115  *
00116  *      For an example, see flipdetect.c, which gives implementations
00117  *      using hit-miss Sels with both the rasterop and dwa versions.
00118  *      For the latter, the dwa code resides in fliphmtgen.c, and it
00119  *      was generated by prog/flipselgen.c.  Both the rasterop and dwa
00120  *      implementations are tested by prog/fliptest.c.
00121  *
00122  *  A global constant MORPH_BC is used to set the boundary conditions
00123  *  for rasterop-based binary morphology.  MORPH_BC, in morph.c,
00124  *  is set by default to ASYMMETRIC_MORPH_BC for a non-symmetric
00125  *  convention for boundary pixels in dilation and erosion:
00126  *      All pixels outside the image are assumed to be OFF
00127  *      for both dilation and erosion.
00128  *  To use a symmetric definition, see comments in pixErode()
00129  *  and reset MORPH_BC to SYMMETRIC_MORPH_BC, using 
00130  *  resetMorphBoundaryCondition().
00131  *
00132  *  Boundary artifacts are possible in closing when the non-symmetric
00133  *  boundary conditions are used, because foreground pixels very close
00134  *  to the edge can be removed.  This can be avoided by using either
00135  *  the symmetric boundary conditions or the function pixCloseSafe(),
00136  *  which adds a border before the operation and removes it afterwards.
00137  *
00138  *  The hit-miss transform (HMT) is the bit-and of 2 erosions:
00139  *     (erosion of the src by the hits)  &  (erosion of the bit-inverted
00140  *                                           src by the misses)
00141  *
00142  *  The 'generalized opening' is an HMT followed by a dilation that uses
00143  *  only the hits of the hit-miss Sel.
00144  *  The 'generalized closing' is a dilation (again, with the hits
00145  *  of a hit-miss Sel), followed by the HMT.
00146  *  Both of these 'generalized' functions are idempotent.
00147  *
00148  *  These functions are extensively tested in prog/binmorph1_reg.c,
00149  *  prog/binmorph2_reg.c, and prog/binmorph3_reg.c.
00150  */
00151 
00152 #include <stdio.h>
00153 #include <math.h>
00154 #include "allheaders.h"
00155 
00156     /* Global constant; initialized here; must be declared extern
00157      * in other files to access it directly.  However, in most
00158      * cases that is not necessary, because it can be reset
00159      * using resetMorphBoundaryCondition().  */
00160 LEPT_DLL l_int32  MORPH_BC = ASYMMETRIC_MORPH_BC;
00161 
00162     /* We accept this cost in extra rasterops for decomposing exactly. */
00163 static const l_int32  ACCEPTABLE_COST = 5;
00164 
00165     /* Static helpers for arg processing */
00166 static PIX * processMorphArgs1(PIX *pixd, PIX *pixs, SEL *sel, PIX **ppixt);
00167 static PIX * processMorphArgs2(PIX *pixd, PIX *pixs, SEL *sel);
00168 
00169 
00170 /*-----------------------------------------------------------------*
00171  *    Generic binary morphological ops implemented with rasterop   *
00172  *-----------------------------------------------------------------*/
00173 /*!
00174  *  pixDilate()
00175  *
00176  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
00177  *                     or different from pixs)
00178  *              pixs (1 bpp)
00179  *              sel
00180  *      Return: pixd
00181  *
00182  *  Notes:
00183  *      (1) This dilates src using hits in Sel.
00184  *      (2) There are three cases:
00185  *          (a) pixd == null   (result into new pixd)
00186  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00187  *          (c) pixd != pixs   (puts result into existing pixd)
00188  *      (3) For clarity, if the case is known, use these patterns:
00189  *          (a) pixd = pixDilate(NULL, pixs, ...);
00190  *          (b) pixDilate(pixs, pixs, ...);
00191  *          (c) pixDilate(pixd, pixs, ...);
00192  *      (4) The size of the result is determined by pixs.
00193  */
00194 PIX *
00195 pixDilate(PIX  *pixd,
00196           PIX  *pixs,
00197           SEL  *sel)
00198 {
00199 l_int32  i, j, w, h, sx, sy, cx, cy, seldata;
00200 PIX     *pixt;
00201 
00202     PROCNAME("pixDilate");
00203 
00204     if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
00205         return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd);
00206 
00207     pixGetDimensions(pixs, &w, &h, NULL);
00208     selGetParameters(sel, &sy, &sx, &cy, &cx);
00209     pixClearAll(pixd);
00210     for (i = 0; i < sy; i++) {
00211         for (j = 0; j < sx; j++) {
00212             seldata = sel->data[i][j];
00213             if (seldata == 1) {   /* src | dst */
00214                 pixRasterop(pixd, j - cx, i - cy, w, h, PIX_SRC | PIX_DST,
00215                             pixt, 0, 0);
00216             }
00217         }
00218     }
00219 
00220     pixDestroy(&pixt);
00221     return pixd;
00222 }
00223 
00224 
00225 /*!
00226  *  pixErode()
00227  *
00228  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
00229  *                     or different from pixs)
00230  *              pixs (1 bpp)
00231  *              sel
00232  *      Return: pixd
00233  *
00234  *  Notes:
00235  *      (1) This erodes src using hits in Sel.
00236  *      (2) There are three cases:
00237  *          (a) pixd == null   (result into new pixd)
00238  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00239  *          (c) pixd != pixs   (puts result into existing pixd)
00240  *      (3) For clarity, if the case is known, use these patterns:
00241  *          (a) pixd = pixErode(NULL, pixs, ...);
00242  *          (b) pixErode(pixs, pixs, ...);
00243  *          (c) pixErode(pixd, pixs, ...);
00244  *      (4) The size of the result is determined by pixs.
00245  */
00246 PIX *
00247 pixErode(PIX  *pixd,
00248          PIX  *pixs,
00249          SEL  *sel)
00250 {
00251 l_int32  i, j, w, h, sx, sy, cx, cy, seldata;
00252 l_int32  xp, yp, xn, yn;
00253 PIX     *pixt;
00254 
00255     PROCNAME("pixErode");
00256 
00257     if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
00258         return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd);
00259 
00260     pixGetDimensions(pixs, &w, &h, NULL);
00261     selGetParameters(sel, &sy, &sx, &cy, &cx);
00262     pixSetAll(pixd);
00263     for (i = 0; i < sy; i++) {
00264         for (j = 0; j < sx; j++) {
00265             seldata = sel->data[i][j];
00266             if (seldata == 1) {   /* src & dst */
00267                     pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST,
00268                                 pixt, 0, 0);
00269             }
00270         }
00271     }
00272 
00273         /* Clear near edges.  We do this for the asymmetric boundary
00274          * condition convention that implements erosion assuming all
00275          * pixels surrounding the image are OFF.  If you use a
00276          * use a symmetric b.c. convention, where the erosion is
00277          * implemented assuming pixels surrounding the image
00278          * are ON, these operations are omitted.  */
00279     if (MORPH_BC == ASYMMETRIC_MORPH_BC) {
00280         selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
00281         if (xp > 0)
00282             pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0);
00283         if (xn > 0)
00284             pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0);
00285         if (yp > 0)
00286             pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0);
00287         if (yn > 0)
00288             pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0);
00289     }
00290 
00291     pixDestroy(&pixt);
00292     return pixd;
00293 }
00294 
00295 
00296 /*!
00297  *  pixHMT()
00298  *
00299  *      Input:  pixd (<optional>; this can be null, equal to pixs,
00300  *                    or different from pixs)
00301  *              pixs (1 bpp)
00302  *              sel
00303  *      Return: pixd
00304  *
00305  *  Notes:
00306  *      (1) The hit-miss transform erodes the src, using both hits
00307  *          and misses in the Sel.  It ANDs the shifted src for hits
00308  *          and ANDs the inverted shifted src for misses.
00309  *      (2) There are three cases:
00310  *          (a) pixd == null   (result into new pixd)
00311  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00312  *          (c) pixd != pixs   (puts result into existing pixd)
00313  *      (3) For clarity, if the case is known, use these patterns:
00314  *          (a) pixd = pixHMT(NULL, pixs, ...);
00315  *          (b) pixHMT(pixs, pixs, ...);
00316  *          (c) pixHMT(pixd, pixs, ...);
00317  *      (4) The size of the result is determined by pixs.
00318  */
00319 PIX *
00320 pixHMT(PIX  *pixd,
00321        PIX  *pixs,
00322        SEL  *sel)
00323 {
00324 l_int32  i, j, w, h, sx, sy, cx, cy, firstrasterop, seldata;
00325 l_int32  xp, yp, xn, yn;
00326 PIX     *pixt;
00327 
00328     PROCNAME("pixHMT");
00329 
00330     if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
00331         return (PIX *)ERROR_PTR("processMorphArgs1 failed", procName, pixd);
00332 
00333     pixGetDimensions(pixs, &w, &h, NULL);
00334     selGetParameters(sel, &sy, &sx, &cy, &cx);
00335     firstrasterop = TRUE;
00336     for (i = 0; i < sy; i++) {
00337         for (j = 0; j < sx; j++) {
00338             seldata = sel->data[i][j];
00339             if (seldata == 1) {  /* hit */
00340                 if (firstrasterop == TRUE) {  /* src only */
00341                     pixClearAll(pixd);
00342                     pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC,
00343                                 pixt, 0, 0);
00344                     firstrasterop = FALSE;
00345                 }
00346                 else {   /* src & dst */
00347                     pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST,
00348                                 pixt, 0, 0);
00349                 }
00350             }
00351             else if (seldata == 2) {  /* miss */
00352                 if (firstrasterop == TRUE) {  /* ~src only */
00353                     pixSetAll(pixd);
00354                     pixRasterop(pixd, cx - j, cy - i, w, h, PIX_NOT(PIX_SRC),
00355                              pixt, 0, 0);
00356                     firstrasterop = FALSE;
00357                 }
00358                 else  {  /* ~src & dst */
00359                     pixRasterop(pixd, cx - j, cy - i, w, h,
00360                                 PIX_NOT(PIX_SRC) & PIX_DST,
00361                                 pixt, 0, 0);
00362                 }
00363             }
00364         }
00365     }
00366 
00367         /* Clear near edges */
00368     selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
00369     if (xp > 0)
00370         pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0);
00371     if (xn > 0)
00372         pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0);
00373     if (yp > 0)
00374         pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0);
00375     if (yn > 0)
00376         pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0);
00377 
00378     pixDestroy(&pixt);
00379     return pixd;
00380 }
00381 
00382 
00383 /*!
00384  *  pixOpen()
00385  *
00386  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
00387  *                     or different from pixs)
00388  *              pixs (1 bpp)
00389  *              sel
00390  *      Return: pixd
00391  *
00392  *  Notes:
00393  *      (1) Generic morphological opening, using hits in the Sel.
00394  *      (2) There are three cases:
00395  *          (a) pixd == null   (result into new pixd)
00396  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00397  *          (c) pixd != pixs   (puts result into existing pixd)
00398  *      (3) For clarity, if the case is known, use these patterns:
00399  *          (a) pixd = pixOpen(NULL, pixs, ...);
00400  *          (b) pixOpen(pixs, pixs, ...);
00401  *          (c) pixOpen(pixd, pixs, ...);
00402  *      (4) The size of the result is determined by pixs.
00403  */
00404 PIX *
00405 pixOpen(PIX  *pixd,
00406         PIX  *pixs,
00407         SEL  *sel)
00408 {
00409 PIX  *pixt;
00410 
00411     PROCNAME("pixOpen");
00412 
00413     if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
00414         return (PIX *)ERROR_PTR("pixd not returned", procName, pixd);
00415 
00416     if ((pixt = pixErode(NULL, pixs, sel)) == NULL)
00417         return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
00418     pixDilate(pixd, pixt, sel);
00419     pixDestroy(&pixt);
00420 
00421     return pixd;
00422 }
00423     
00424     
00425 /*!
00426  *  pixClose()
00427  *
00428  *      Input:  pixd (<optional>; this can be null, equal to pixs,
00429  *                    or different from pixs)
00430  *              pixs (1 bpp)
00431  *              sel
00432  *      Return: pixd
00433  *
00434  *  Notes:
00435  *      (1) Generic morphological closing, using hits in the Sel.
00436  *      (2) This implementation is a strict dual of the opening if
00437  *          symmetric boundary conditions are used (see notes at top
00438  *          of this file).
00439  *      (3) There are three cases:
00440  *          (a) pixd == null   (result into new pixd)
00441  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00442  *          (c) pixd != pixs   (puts result into existing pixd)
00443  *      (4) For clarity, if the case is known, use these patterns:
00444  *          (a) pixd = pixClose(NULL, pixs, ...);
00445  *          (b) pixClose(pixs, pixs, ...);
00446  *          (c) pixClose(pixd, pixs, ...);
00447  *      (5) The size of the result is determined by pixs.
00448  */
00449 PIX *
00450 pixClose(PIX  *pixd,
00451          PIX  *pixs,
00452          SEL  *sel)
00453 {
00454 PIX  *pixt;
00455 
00456     PROCNAME("pixClose");
00457 
00458     if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
00459         return (PIX *)ERROR_PTR("pixd not returned", procName, pixd);
00460 
00461     if ((pixt = pixDilate(NULL, pixs, sel)) == NULL)
00462         return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
00463     pixErode(pixd, pixt, sel);
00464     pixDestroy(&pixt);
00465 
00466     return pixd;
00467 }
00468     
00469     
00470 /*!
00471  *  pixCloseSafe()
00472  *
00473  *      Input:  pixd (<optional>; this can be null, equal to pixs,
00474  *                    or different from pixs)
00475  *              pixs (1 bpp)
00476  *              sel
00477  *      Return: pixd
00478  *
00479  *  Notes:
00480  *      (1) Generic morphological closing, using hits in the Sel.
00481  *      (2) If non-symmetric boundary conditions are used, this
00482  *          function adds a border of OFF pixels that is of
00483  *          sufficient size to avoid losing pixels from the dilation,
00484  *          and it removes the border after the operation is finished.
00485  *          It thus enforces a correct extensive result for closing.
00486  *      (3) If symmetric b.c. are used, it is not necessary to add
00487  *          and remove this border.
00488  *      (4) There are three cases:
00489  *          (a) pixd == null   (result into new pixd)
00490  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00491  *          (c) pixd != pixs   (puts result into existing pixd)
00492  *      (5) For clarity, if the case is known, use these patterns:
00493  *          (a) pixd = pixCloseSafe(NULL, pixs, ...);
00494  *          (b) pixCloseSafe(pixs, pixs, ...);
00495  *          (c) pixCloseSafe(pixd, pixs, ...);
00496  *      (6) The size of the result is determined by pixs.
00497  */
00498 PIX *
00499 pixCloseSafe(PIX  *pixd,
00500              PIX  *pixs,
00501              SEL  *sel)
00502 {
00503 l_int32  xp, yp, xn, yn, xmax, xbord;
00504 PIX     *pixt1, *pixt2;
00505 
00506     PROCNAME("pixCloseSafe");
00507 
00508     if (!pixs)
00509         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
00510     if (!sel)
00511         return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
00512     if (pixGetDepth(pixs) != 1)
00513         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
00514 
00515         /* Symmetric b.c. handles correctly without added pixels */
00516     if (MORPH_BC == SYMMETRIC_MORPH_BC)
00517         return pixClose(pixd, pixs, sel);
00518 
00519     selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
00520     xmax = L_MAX(xp, xn);
00521     xbord = 32 * ((xmax + 31) / 32);  /* full 32 bit words */
00522 
00523     if ((pixt1 = pixAddBorderGeneral(pixs, xbord, xbord, yp, yn, 0)) == NULL)
00524         return (PIX *)ERROR_PTR("pixt1 not made", procName, pixd);
00525     pixClose(pixt1, pixt1, sel);
00526     if ((pixt2 = pixRemoveBorderGeneral(pixt1, xbord, xbord, yp, yn)) == NULL)
00527         return (PIX *)ERROR_PTR("pixt2 not made", procName, pixd);
00528     pixDestroy(&pixt1);
00529 
00530     if (!pixd)
00531         return pixt2;
00532 
00533     pixCopy(pixd, pixt2);
00534     pixDestroy(&pixt2);
00535     return pixd;
00536 }
00537     
00538     
00539 /*!
00540  *  pixOpenGeneralized()
00541  *
00542  *      Input:  pixd (<optional>; this can be null, equal to pixs,
00543  *                    or different from pixs)
00544  *              pixs (1 bpp)
00545  *              sel
00546  *      Return: pixd
00547  *
00548  *  Notes:
00549  *      (1) Generalized morphological opening, using both hits and
00550  *          misses in the Sel.
00551  *      (2) This does a hit-miss transform, followed by a dilation
00552  *          using the hits.
00553  *      (3) There are three cases:
00554  *          (a) pixd == null   (result into new pixd)
00555  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00556  *          (c) pixd != pixs   (puts result into existing pixd)
00557  *      (4) For clarity, if the case is known, use these patterns:
00558  *          (a) pixd = pixOpenGeneralized(NULL, pixs, ...);
00559  *          (b) pixOpenGeneralized(pixs, pixs, ...);
00560  *          (c) pixOpenGeneralized(pixd, pixs, ...);
00561  *      (5) The size of the result is determined by pixs.
00562  */
00563 PIX *
00564 pixOpenGeneralized(PIX  *pixd,
00565                    PIX  *pixs,
00566                    SEL  *sel)
00567 {
00568 PIX  *pixt;
00569 
00570     PROCNAME("pixOpenGeneralized");
00571 
00572     if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
00573         return (PIX *)ERROR_PTR("pixd not returned", procName, pixd);
00574 
00575     if ((pixt = pixHMT(NULL, pixs, sel)) == NULL)
00576         return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
00577     pixDilate(pixd, pixt, sel);
00578     pixDestroy(&pixt);
00579     return pixd;
00580 }
00581     
00582     
00583 /*!
00584  *  pixCloseGeneralized()
00585  *
00586  *      Input:  pixd (<optional>; this can be null, equal to pixs,
00587  *                    or different from pixs)
00588  *              pixs (1 bpp)
00589  *              sel
00590  *      Return: pixd
00591  *
00592  *  Notes:
00593  *      (1) Generalized morphological closing, using both hits and
00594  *          misses in the Sel.
00595  *      (2) This does a dilation using the hits, followed by a
00596  *          hit-miss transform.
00597  *      (3) This operation is a dual of the generalized opening.
00598  *      (4) There are three cases:
00599  *          (a) pixd == null   (result into new pixd)
00600  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00601  *          (c) pixd != pixs   (puts result into existing pixd)
00602  *      (5) For clarity, if the case is known, use these patterns:
00603  *          (a) pixd = pixCloseGeneralized(NULL, pixs, ...);
00604  *          (b) pixCloseGeneralized(pixs, pixs, ...);
00605  *          (c) pixCloseGeneralized(pixd, pixs, ...);
00606  *      (6) The size of the result is determined by pixs.
00607  */
00608 PIX *
00609 pixCloseGeneralized(PIX  *pixd,
00610                     PIX  *pixs,
00611                     SEL  *sel)
00612 {
00613 PIX  *pixt;
00614 
00615     PROCNAME("pixCloseGeneralized");
00616 
00617     if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
00618         return (PIX *)ERROR_PTR("pixd not returned", procName, pixd);
00619 
00620     if ((pixt = pixDilate(NULL, pixs, sel)) == NULL)
00621         return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
00622     pixHMT(pixd, pixt, sel);
00623     pixDestroy(&pixt);
00624 
00625     return pixd;
00626 }
00627 
00628 
00629 /*-----------------------------------------------------------------*
00630  *          Binary morphological (raster) ops with brick Sels      *
00631  *-----------------------------------------------------------------*/
00632 /*!
00633  *  pixDilateBrick()
00634  *
00635  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
00636  *                     or different from pixs)
00637  *              pixs (1 bpp)
00638  *              hsize (width of brick Sel)
00639  *              vsize (height of brick Sel)
00640  *      Return: pixd
00641  *
00642  *  Notes:
00643  *      (1) Sel is a brick with all elements being hits
00644  *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
00645  *      (3) Do separably if both hsize and vsize are > 1.
00646  *      (4) There are three cases:
00647  *          (a) pixd == null   (result into new pixd)
00648  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00649  *          (c) pixd != pixs   (puts result into existing pixd)
00650  *      (5) For clarity, if the case is known, use these patterns:
00651  *          (a) pixd = pixDilateBrick(NULL, pixs, ...);
00652  *          (b) pixDilateBrick(pixs, pixs, ...);
00653  *          (c) pixDilateBrick(pixd, pixs, ...);
00654  *      (6) The size of the result is determined by pixs.
00655  */
00656 PIX *
00657 pixDilateBrick(PIX     *pixd,
00658                PIX     *pixs,
00659                l_int32  hsize,
00660                l_int32  vsize)
00661 {
00662 PIX  *pixt;
00663 SEL  *sel, *selh, *selv;
00664 
00665     PROCNAME("pixDilateBrick");
00666 
00667     if (!pixs)
00668         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
00669     if (pixGetDepth(pixs) != 1)
00670         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
00671     if (hsize < 1 || vsize < 1)
00672         return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
00673 
00674     if (hsize == 1 && vsize == 1)
00675         return pixCopy(pixd, pixs);
00676     if (hsize == 1 || vsize == 1) {  /* no intermediate result */
00677         sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
00678         pixd = pixDilate(pixd, pixs, sel);
00679         selDestroy(&sel);
00680     }
00681     else {
00682         selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
00683         selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
00684         pixt = pixDilate(NULL, pixs, selh);
00685         pixd = pixDilate(pixd, pixt, selv);
00686         pixDestroy(&pixt);
00687         selDestroy(&selh);
00688         selDestroy(&selv);
00689     }
00690 
00691     return pixd;
00692 }
00693 
00694 
00695 /*!
00696  *  pixErodeBrick()
00697  *
00698  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
00699  *                     or different from pixs)
00700  *              pixs (1 bpp)
00701  *              hsize (width of brick Sel)
00702  *              vsize (height of brick Sel)
00703  *      Return: pixd
00704  *
00705  *  Notes:
00706  *      (1) Sel is a brick with all elements being hits
00707  *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
00708  *      (3) Do separably if both hsize and vsize are > 1.
00709  *      (4) There are three cases:
00710  *          (a) pixd == null   (result into new pixd)
00711  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00712  *          (c) pixd != pixs   (puts result into existing pixd)
00713  *      (5) For clarity, if the case is known, use these patterns:
00714  *          (a) pixd = pixErodeBrick(NULL, pixs, ...);
00715  *          (b) pixErodeBrick(pixs, pixs, ...);
00716  *          (c) pixErodeBrick(pixd, pixs, ...);
00717  *      (6) The size of the result is determined by pixs.
00718  */
00719 PIX *
00720 pixErodeBrick(PIX     *pixd,
00721               PIX     *pixs,
00722               l_int32  hsize,
00723               l_int32  vsize)
00724 {
00725 PIX  *pixt;
00726 SEL  *sel, *selh, *selv;
00727 
00728     PROCNAME("pixErodeBrick");
00729 
00730     if (!pixs)
00731         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
00732     if (pixGetDepth(pixs) != 1)
00733         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
00734     if (hsize < 1 || vsize < 1)
00735         return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
00736 
00737     if (hsize == 1 && vsize == 1)
00738         return pixCopy(pixd, pixs);
00739     if (hsize == 1 || vsize == 1) {  /* no intermediate result */
00740         sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
00741         pixd = pixErode(pixd, pixs, sel);
00742         selDestroy(&sel);
00743     }
00744     else {
00745         selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
00746         selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
00747         pixt = pixErode(NULL, pixs, selh);
00748         pixd = pixErode(pixd, pixt, selv);
00749         pixDestroy(&pixt);
00750         selDestroy(&selh);
00751         selDestroy(&selv);
00752     }
00753 
00754     return pixd;
00755 }
00756 
00757 
00758 /*!
00759  *  pixOpenBrick()
00760  *
00761  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
00762  *                     or different from pixs)
00763  *              pixs (1 bpp)
00764  *              hsize (width of brick Sel)
00765  *              vsize (height of brick Sel)
00766  *      Return: pixd, or null on error
00767  *
00768  *  Notes:
00769  *      (1) Sel is a brick with all elements being hits
00770  *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
00771  *      (3) Do separably if both hsize and vsize are > 1.
00772  *      (4) There are three cases:
00773  *          (a) pixd == null   (result into new pixd)
00774  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00775  *          (c) pixd != pixs   (puts result into existing pixd)
00776  *      (5) For clarity, if the case is known, use these patterns:
00777  *          (a) pixd = pixOpenBrick(NULL, pixs, ...);
00778  *          (b) pixOpenBrick(pixs, pixs, ...);
00779  *          (c) pixOpenBrick(pixd, pixs, ...);
00780  *      (6) The size of the result is determined by pixs.
00781  */
00782 PIX *
00783 pixOpenBrick(PIX     *pixd,
00784              PIX     *pixs,
00785              l_int32  hsize,
00786              l_int32  vsize)
00787 {
00788 PIX  *pixt;
00789 SEL  *sel, *selh, *selv;
00790 
00791     PROCNAME("pixOpenBrick");
00792 
00793     if (!pixs)
00794         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
00795     if (pixGetDepth(pixs) != 1)
00796         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
00797     if (hsize < 1 || vsize < 1)
00798         return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
00799 
00800     if (hsize == 1 && vsize == 1)
00801         return pixCopy(pixd, pixs);
00802     if (hsize == 1 || vsize == 1) {  /* no intermediate result */
00803         sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
00804         pixd = pixOpen(pixd, pixs, sel);
00805         selDestroy(&sel);
00806     }
00807     else {  /* do separably */
00808         selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
00809         selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
00810         pixt = pixErode(NULL, pixs, selh);
00811         pixd = pixErode(pixd, pixt, selv);
00812         pixDilate(pixt, pixd, selh);
00813         pixDilate(pixd, pixt, selv);
00814         pixDestroy(&pixt);
00815         selDestroy(&selh);
00816         selDestroy(&selv);
00817     }
00818 
00819     return pixd;
00820 }
00821 
00822 
00823 /*!
00824  *  pixCloseBrick()
00825  *
00826  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
00827  *                     or different from pixs)
00828  *              pixs (1 bpp)
00829  *              hsize (width of brick Sel)
00830  *              vsize (height of brick Sel)
00831  *      Return: pixd, or null on error
00832  *
00833  *  Notes:
00834  *      (1) Sel is a brick with all elements being hits
00835  *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
00836  *      (3) Do separably if both hsize and vsize are > 1.
00837  *      (4) There are three cases:
00838  *          (a) pixd == null   (result into new pixd)
00839  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00840  *          (c) pixd != pixs   (puts result into existing pixd)
00841  *      (5) For clarity, if the case is known, use these patterns:
00842  *          (a) pixd = pixCloseBrick(NULL, pixs, ...);
00843  *          (b) pixCloseBrick(pixs, pixs, ...);
00844  *          (c) pixCloseBrick(pixd, pixs, ...);
00845  *      (6) The size of the result is determined by pixs.
00846  */
00847 PIX *
00848 pixCloseBrick(PIX     *pixd,
00849               PIX     *pixs,
00850               l_int32  hsize,
00851               l_int32  vsize)
00852 {
00853 PIX  *pixt;
00854 SEL  *sel, *selh, *selv;
00855 
00856     PROCNAME("pixCloseBrick");
00857 
00858     if (!pixs)
00859         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
00860     if (pixGetDepth(pixs) != 1)
00861         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
00862     if (hsize < 1 || vsize < 1)
00863         return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
00864 
00865     if (hsize == 1 && vsize == 1)
00866         return pixCopy(pixd, pixs);
00867     if (hsize == 1 || vsize == 1) {  /* no intermediate result */
00868         sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
00869         pixd = pixClose(pixd, pixs, sel);
00870         selDestroy(&sel);
00871     }
00872     else {  /* do separably */
00873         selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
00874         selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
00875         pixt = pixDilate(NULL, pixs, selh);
00876         pixd = pixDilate(pixd, pixt, selv);
00877         pixErode(pixt, pixd, selh);
00878         pixErode(pixd, pixt, selv);
00879         pixDestroy(&pixt);
00880         selDestroy(&selh);
00881         selDestroy(&selv);
00882     }
00883 
00884     return pixd;
00885 }
00886 
00887 
00888 /*!
00889  *  pixCloseSafeBrick()
00890  *
00891  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
00892  *                     or different from pixs)
00893  *              pixs (1 bpp)
00894  *              hsize (width of brick Sel)
00895  *              vsize (height of brick Sel)
00896  *      Return: pixd, or null on error
00897  *
00898  *  Notes:
00899  *      (1) Sel is a brick with all elements being hits
00900  *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
00901  *      (3) Do separably if both hsize and vsize are > 1.
00902  *      (4) Safe closing adds a border of 0 pixels, of sufficient size so
00903  *          that all pixels in input image are processed within
00904  *          32-bit words in the expanded image.  As a result, there is
00905  *          no special processing for pixels near the boundary, and there
00906  *          are no boundary effects.  The border is removed at the end.
00907  *      (5) There are three cases:
00908  *          (a) pixd == null   (result into new pixd)
00909  *          (b) pixd == pixs   (in-place; writes result back to pixs)
00910  *          (c) pixd != pixs   (puts result into existing pixd)
00911  *      (6) For clarity, if the case is known, use these patterns:
00912  *          (a) pixd = pixCloseBrick(NULL, pixs, ...);
00913  *          (b) pixCloseBrick(pixs, pixs, ...);
00914  *          (c) pixCloseBrick(pixd, pixs, ...);
00915  *      (7) The size of the result is determined by pixs.
00916  */
00917 PIX *
00918 pixCloseSafeBrick(PIX     *pixd,
00919                   PIX     *pixs,
00920                   l_int32  hsize,
00921                   l_int32  vsize)
00922 {
00923 l_int32  maxtrans, bordsize;
00924 PIX     *pixsb, *pixt, *pixdb;
00925 SEL     *sel, *selh, *selv;
00926 
00927     PROCNAME("pixCloseSafeBrick");
00928 
00929     if (!pixs)
00930         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
00931     if (pixGetDepth(pixs) != 1)
00932         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
00933     if (hsize < 1 || vsize < 1)
00934         return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
00935 
00936     if (hsize == 1 && vsize == 1)
00937         return pixCopy(pixd, pixs);
00938 
00939         /* Symmetric b.c. handles correctly without added pixels */
00940     if (MORPH_BC == SYMMETRIC_MORPH_BC)
00941         return pixCloseBrick(pixd, pixs, hsize, vsize);
00942 
00943     maxtrans = L_MAX(hsize / 2, vsize / 2);
00944     bordsize = 32 * ((maxtrans + 31) / 32);  /* full 32 bit words */
00945     pixsb = pixAddBorder(pixs, bordsize, 0);
00946 
00947     if (hsize == 1 || vsize == 1) {  /* no intermediate result */
00948         sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
00949         pixdb = pixClose(NULL, pixsb, sel);
00950         selDestroy(&sel);
00951     }
00952     else {  /* do separably */
00953         selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
00954         selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
00955         pixt = pixDilate(NULL, pixsb, selh);
00956         pixdb = pixDilate(NULL, pixt, selv);
00957         pixErode(pixt, pixdb, selh);
00958         pixErode(pixdb, pixt, selv);
00959         pixDestroy(&pixt);
00960         selDestroy(&selh);
00961         selDestroy(&selv);
00962     }
00963 
00964     pixt = pixRemoveBorder(pixdb, bordsize);
00965     pixDestroy(&pixsb);
00966     pixDestroy(&pixdb);
00967 
00968     if (!pixd)
00969         pixd = pixt;
00970     else {
00971         pixCopy(pixd, pixt);
00972         pixDestroy(&pixt);
00973     }
00974 
00975     return pixd;
00976 }
00977 
00978 
00979 /*-----------------------------------------------------------------*
00980  *     Binary composed morphological (raster) ops with brick Sels  *
00981  *-----------------------------------------------------------------*/
00982 /*  selectComposableSels()
00983  *
00984  *      Input:  size (of composed sel)
00985  *              direction (L_HORIZ, L_VERT)
00986  *              &sel1 (<optional return> contiguous sel; can be null)
00987  *              &sel2 (<optional return> comb sel; can be null)
00988  *      Return: 0 if OK, 1 on error
00989  *
00990  *  Notes:
00991  *      (1) When using composable Sels, where the original Sel is
00992  *          decomposed into two, the best you can do in terms
00993  *          of reducing the computation is by a factor:
00994  *
00995  *               2 * sqrt(size) / size
00996  *
00997  *          In practice, you get quite close to this.  E.g.,
00998  *
00999  *             Sel size     |   Optimum reduction factor
01000  *             --------         ------------------------
01001  *                36        |          1/3
01002  *                64        |          1/4
01003  *               144        |          1/6
01004  *               256        |          1/8
01005  */
01006 l_int32
01007 selectComposableSels(l_int32  size,
01008                      l_int32  direction,
01009                      SEL    **psel1,
01010                      SEL    **psel2)
01011 {
01012 l_int32  factor1, factor2;
01013 
01014     PROCNAME("selectComposableSels");
01015 
01016     if (!psel1 && !psel2)
01017         return ERROR_INT("neither &sel1 nor &sel2 are defined", procName, 1);
01018     if (psel1) *psel1 = NULL;
01019     if (psel2) *psel2 = NULL;
01020     if (size < 1 || size > 250 * 250)
01021         return ERROR_INT("size < 1", procName, 1);
01022     if (direction != L_HORIZ && direction != L_VERT)
01023         return ERROR_INT("invalid direction", procName, 1);
01024 
01025     if (selectComposableSizes(size, &factor1, &factor2))
01026         return ERROR_INT("factors not found", procName, 1);
01027 
01028     if (psel1) {
01029         if (direction == L_HORIZ)
01030             *psel1 = selCreateBrick(1, factor1, 0, factor1 / 2, SEL_HIT);
01031         else
01032             *psel1 = selCreateBrick(factor1, 1, factor1 / 2 , 0, SEL_HIT);
01033     }
01034     if (psel2)
01035         *psel2 = selCreateComb(factor1, factor2, direction);
01036     return 0;
01037 }
01038 
01039 
01040 /*!
01041  *  selectComposableSizes()
01042  *
01043  *      Input:  size (of sel to be decomposed)
01044  *              &factor1 (<return> larger factor)
01045  *              &factor2 (<return> smaller factor)
01046  *      Return: 0 if OK, 1 on error
01047  *
01048  *  Notes:
01049  *      (1) This works for Sel sizes up to 62500, which seems sufficient.
01050  *      (2) The composable sel size is typically within +- 1 of
01051  *          the requested size.  Up to size = 300, the maximum difference
01052  *          is +- 2.
01053  *      (3) We choose an overall cost function where the penalty for
01054  *          the size difference between input and actual is 4 times
01055  *          the penalty for additional rasterops.
01056  *      (4) Returned values: factor1 >= factor2
01057  *          If size > 1, then factor1 > 1.
01058  */
01059 l_int32
01060 selectComposableSizes(l_int32   size,
01061                       l_int32  *pfactor1,
01062                       l_int32  *pfactor2)
01063 {
01064 l_int32  i, midval, val1, val2m, val2p;
01065 l_int32  index, prodm, prodp;
01066 l_int32  mincost, totcost, rastcostm, rastcostp, diffm, diffp;
01067 l_int32  lowval[256];
01068 l_int32  hival[256];
01069 l_int32  rastcost[256];  /* excess in sum of sizes (extra rasterops) */
01070 l_int32  diff[256];  /* diff between product (sel size) and input size */
01071 
01072     PROCNAME("selectComposableSizes");
01073 
01074     if (size < 1 || size > 250 * 250)
01075         return ERROR_INT("size < 1", procName, 1);
01076     if (!pfactor1 || !pfactor2)
01077         return ERROR_INT("&factor1 or &factor2 not defined", procName, 1);
01078     
01079     midval = (l_int32)(sqrt((l_float64)size) + 0.001);
01080     if (midval * midval == size) {
01081         *pfactor1 = *pfactor2 = midval;
01082         return 0;
01083     }
01084 
01085         /* Set up arrays.  For each val1, optimize for lowest diff,
01086          * and save the rastcost, the diff, and the two factors. */
01087     for (val1 = midval + 1, i = 0; val1 > 0; val1--, i++) {
01088         val2m = size / val1;
01089         val2p = val2m + 1;
01090         prodm = val1 * val2m;
01091         prodp = val1 * val2p;
01092         rastcostm = val1 + val2m - 2 * midval;
01093         rastcostp = val1 + val2p - 2 * midval;
01094         diffm = L_ABS(size - prodm);
01095         diffp = L_ABS(size - prodp);
01096         if (diffm <= diffp) {
01097             lowval[i] = L_MIN(val1, val2m);
01098             hival[i] = L_MAX(val1, val2m);
01099             rastcost[i] = rastcostm;
01100             diff[i] = diffm;
01101         }
01102         else {
01103             lowval[i] = L_MIN(val1, val2p);
01104             hival[i] = L_MAX(val1, val2p);
01105             rastcost[i] = rastcostp;
01106             diff[i] = diffp;
01107         }
01108     }
01109 
01110         /* Choose the optimum factors; use cost ratio 4 on diff */
01111     mincost = 10000;
01112     for (i = 0; i < midval + 1; i++) {
01113         if (diff[i] == 0 && rastcost[i] < ACCEPTABLE_COST) {
01114             *pfactor1 = hival[i];
01115             *pfactor2 = lowval[i];
01116             return 0;
01117         }
01118         totcost = 4 * diff[i] + rastcost[i];
01119         if (totcost < mincost) {
01120             mincost = totcost;
01121             index = i;
01122         }
01123     }
01124     *pfactor1 = hival[index];
01125     *pfactor2 = lowval[index];
01126 
01127     return 0;
01128 }
01129 
01130 
01131 /*!
01132  *  pixDilateCompBrick()
01133  *
01134  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
01135  *                     or different from pixs)
01136  *              pixs (1 bpp)
01137  *              hsize (width of brick Sel)
01138  *              vsize (height of brick Sel)
01139  *      Return: pixd, or null on error
01140  *
01141  *  Notes:
01142  *      (1) Sel is a brick with all elements being hits
01143  *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
01144  *      (3) Do compositely for each dimension > 1.
01145  *      (4) Do separably if both hsize and vsize are > 1.
01146  *      (5) There are three cases:
01147  *          (a) pixd == null   (result into new pixd)
01148  *          (b) pixd == pixs   (in-place; writes result back to pixs)
01149  *          (c) pixd != pixs   (puts result into existing pixd)
01150  *      (6) For clarity, if the case is known, use these patterns:
01151  *          (a) pixd = pixDilateCompBrick(NULL, pixs, ...);
01152  *          (b) pixDilateCompBrick(pixs, pixs, ...);
01153  *          (c) pixDilateCompBrick(pixd, pixs, ...);
01154  *      (7) The dimensions of the resulting image are determined by pixs.
01155  *      (8) CAUTION: both hsize and vsize are being decomposed.
01156  *          The decomposer chooses a product of sizes (call them
01157  *          'terms') for each that is close to the input size,
01158  *          but not necessarily equal to it.  It attempts to optimize:
01159  *             (a) for consistency with the input values: the product
01160  *                 of terms is close to the input size
01161  *             (b) for efficiency of the operation: the sum of the
01162  *                 terms is small; ideally about twice the square
01163  *                 root of the input size.
01164  *          So, for example, if the input hsize = 37, which is
01165  *          a prime number, the decomposer will break this into two
01166  *          terms, 6 and 6, so that the net result is a dilation
01167  *          with hsize = 36.
01168  */
01169 PIX *
01170 pixDilateCompBrick(PIX     *pixd,
01171                    PIX     *pixs,
01172                    l_int32  hsize,
01173                    l_int32  vsize)
01174 {
01175 PIX  *pixt1, *pixt2, *pixt3;
01176 SEL  *selh1, *selh2, *selv1, *selv2;
01177 
01178     PROCNAME("pixDilateCompBrick");
01179 
01180     if (!pixs)
01181         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
01182     if (pixGetDepth(pixs) != 1)
01183         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
01184     if (hsize < 1 || vsize < 1)
01185         return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
01186 
01187     pixt1 = pixAddBorder(pixs, 32, 0);
01188 
01189     if (hsize == 1 && vsize == 1)
01190         return pixCopy(pixd, pixs);
01191     if (hsize > 1)
01192         selectComposableSels(hsize, L_HORIZ, &selh1, &selh2);
01193     if (vsize > 1)
01194         selectComposableSels(vsize, L_VERT, &selv1, &selv2);
01195     if (vsize == 1) {
01196         pixt2 = pixDilate(NULL, pixt1, selh1);
01197         pixt3 = pixDilate(NULL, pixt2, selh2);
01198     } 
01199     else if (hsize == 1) {
01200         pixt2 = pixDilate(NULL, pixt1, selv1);
01201         pixt3 = pixDilate(NULL, pixt2, selv2);
01202     } 
01203     else {
01204         pixt2 = pixDilate(NULL, pixt1, selh1);
01205         pixt3 = pixDilate(NULL, pixt2, selh2);
01206         pixDilate(pixt2, pixt3, selv1);
01207         pixDilate(pixt3, pixt2, selv2);
01208     }
01209     pixDestroy(&pixt1);
01210     pixDestroy(&pixt2);
01211 
01212     if (hsize > 1) {
01213         selDestroy(&selh1);
01214         selDestroy(&selh2);
01215     }
01216     if (vsize > 1) {
01217         selDestroy(&selv1);
01218         selDestroy(&selv2);
01219     }
01220 
01221     pixt1 = pixRemoveBorder(pixt3, 32);
01222     pixDestroy(&pixt3);
01223     if (!pixd)
01224         return pixt1;
01225     pixCopy(pixd, pixt1);
01226     pixDestroy(&pixt1);
01227     return pixd;
01228 }
01229 
01230 
01231 /*!
01232  *  pixErodeCompBrick()
01233  *
01234  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
01235  *                     or different from pixs)
01236  *              pixs (1 bpp)
01237  *              hsize (width of brick Sel)
01238  *              vsize (height of brick Sel)
01239  *      Return: pixd, or null on error
01240  *
01241  *  Notes:
01242  *      (1) Sel is a brick with all elements being hits
01243  *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
01244  *      (3) Do compositely for each dimension > 1.
01245  *      (4) Do separably if both hsize and vsize are > 1.
01246  *      (5) There are three cases:
01247  *          (a) pixd == null   (result into new pixd)
01248  *          (b) pixd == pixs   (in-place; writes result back to pixs)
01249  *          (c) pixd != pixs   (puts result into existing pixd)
01250  *      (6) For clarity, if the case is known, use these patterns:
01251  *          (a) pixd = pixErodeCompBrick(NULL, pixs, ...);
01252  *          (b) pixErodeCompBrick(pixs, pixs, ...);
01253  *          (c) pixErodeCompBrick(pixd, pixs, ...);
01254  *      (7) The dimensions of the resulting image are determined by pixs.
01255  *      (8) CAUTION: both hsize and vsize are being decomposed.
01256  *          The decomposer chooses a product of sizes (call them
01257  *          'terms') for each that is close to the input size,
01258  *          but not necessarily equal to it.  It attempts to optimize:
01259  *             (a) for consistency with the input values: the product
01260  *                 of terms is close to the input size
01261  *             (b) for efficiency of the operation: the sum of the
01262  *                 terms is small; ideally about twice the square
01263  *                 root of the input size.
01264  *          So, for example, if the input hsize = 37, which is
01265  *          a prime number, the decomposer will break this into two
01266  *          terms, 6 and 6, so that the net result is a dilation
01267  *          with hsize = 36.
01268  */
01269 PIX *
01270 pixErodeCompBrick(PIX     *pixd,
01271                   PIX     *pixs,
01272                   l_int32  hsize,
01273                   l_int32  vsize)
01274 {
01275 PIX  *pixt;
01276 SEL  *selh1, *selh2, *selv1, *selv2;
01277 
01278     PROCNAME("pixErodeCompBrick");
01279 
01280     if (!pixs)
01281         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
01282     if (pixGetDepth(pixs) != 1)
01283         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
01284     if (hsize < 1 || vsize < 1)
01285         return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
01286 
01287     if (hsize == 1 && vsize == 1)
01288         return pixCopy(pixd, pixs);
01289     if (hsize > 1)
01290         selectComposableSels(hsize, L_HORIZ, &selh1, &selh2);
01291     if (vsize > 1)
01292         selectComposableSels(vsize, L_VERT, &selv1, &selv2);
01293     if (vsize == 1) {
01294         pixt = pixErode(NULL, pixs, selh1);
01295         pixd = pixErode(pixd, pixt, selh2);
01296     } 
01297     else if (hsize == 1) {
01298         pixt = pixErode(NULL, pixs, selv1);
01299         pixd = pixErode(pixd, pixt, selv2);
01300     } 
01301     else {
01302         pixt = pixErode(NULL, pixs, selh1);
01303         pixd = pixErode(pixd, pixt, selh2);
01304         pixErode(pixt, pixd, selv1);
01305         pixErode(pixd, pixt, selv2);
01306     }
01307     pixDestroy(&pixt);
01308 
01309     if (hsize > 1) {
01310         selDestroy(&selh1);
01311         selDestroy(&selh2);
01312     }
01313     if (vsize > 1) {
01314         selDestroy(&selv1);
01315         selDestroy(&selv2);
01316     }
01317 
01318     return pixd;
01319 }
01320 
01321 
01322 /*!
01323  *  pixOpenCompBrick()
01324  *
01325  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
01326  *                     or different from pixs)
01327  *              pixs (1 bpp)
01328  *              hsize (width of brick Sel)
01329  *              vsize (height of brick Sel)
01330  *      Return: pixd, or null on error
01331  *
01332  *  Notes:
01333  *      (1) Sel is a brick with all elements being hits
01334  *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
01335  *      (3) Do compositely for each dimension > 1.
01336  *      (4) Do separably if both hsize and vsize are > 1.
01337  *      (5) There are three cases:
01338  *          (a) pixd == null   (result into new pixd)
01339  *          (b) pixd == pixs   (in-place; writes result back to pixs)
01340  *          (c) pixd != pixs   (puts result into existing pixd)
01341  *      (6) For clarity, if the case is known, use these patterns:
01342  *          (a) pixd = pixOpenCompBrick(NULL, pixs, ...);
01343  *          (b) pixOpenCompBrick(pixs, pixs, ...);
01344  *          (c) pixOpenCompBrick(pixd, pixs, ...);
01345  *      (7) The dimensions of the resulting image are determined by pixs.
01346  *      (8) CAUTION: both hsize and vsize are being decomposed.
01347  *          The decomposer chooses a product of sizes (call them
01348  *          'terms') for each that is close to the input size,
01349  *          but not necessarily equal to it.  It attempts to optimize:
01350  *             (a) for consistency with the input values: the product
01351  *                 of terms is close to the input size
01352  *             (b) for efficiency of the operation: the sum of the
01353  *                 terms is small; ideally about twice the square
01354  *                 root of the input size.
01355  *          So, for example, if the input hsize = 37, which is
01356  *          a prime number, the decomposer will break this into two
01357  *          terms, 6 and 6, so that the net result is a dilation
01358  *          with hsize = 36.
01359  */
01360 PIX *
01361 pixOpenCompBrick(PIX     *pixd,
01362                  PIX     *pixs,
01363                  l_int32  hsize,
01364                  l_int32  vsize)
01365 {
01366 PIX  *pixt;
01367 SEL  *selh1, *selh2, *selv1, *selv2;
01368 
01369     PROCNAME("pixOpenCompBrick");
01370 
01371     if (!pixs)
01372         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
01373     if (pixGetDepth(pixs) != 1)
01374         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
01375     if (hsize < 1 || vsize < 1)
01376         return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
01377 
01378     if (hsize == 1 && vsize == 1)
01379         return pixCopy(pixd, pixs);
01380     if (hsize > 1)
01381         selectComposableSels(hsize, L_HORIZ, &selh1, &selh2);
01382     if (vsize > 1)
01383         selectComposableSels(vsize, L_VERT, &selv1, &selv2);
01384     if (vsize == 1) {
01385         pixt = pixErode(NULL, pixs, selh1);
01386         pixd = pixErode(pixd, pixt, selh2);
01387         pixDilate(pixt, pixd, selh1);
01388         pixDilate(pixd, pixt, selh2);
01389     } 
01390     else if (hsize == 1) {
01391         pixt = pixErode(NULL, pixs, selv1);
01392         pixd = pixErode(pixd, pixt, selv2);
01393         pixDilate(pixt, pixd, selv1);
01394         pixDilate(pixd, pixt, selv2);
01395     } 
01396     else {  /* do separably */
01397         pixt = pixErode(NULL, pixs, selh1);
01398         pixd = pixErode(pixd, pixt, selh2);
01399         pixErode(pixt, pixd, selv1);
01400         pixErode(pixd, pixt, selv2);
01401         pixDilate(pixt, pixd, selh1);
01402         pixDilate(pixd, pixt, selh2);
01403         pixDilate(pixt, pixd, selv1);
01404         pixDilate(pixd, pixt, selv2);
01405     }
01406     pixDestroy(&pixt);
01407 
01408     if (hsize > 1) {
01409         selDestroy(&selh1);
01410         selDestroy(&selh2);
01411     }
01412     if (vsize > 1) {
01413         selDestroy(&selv1);
01414         selDestroy(&selv2);
01415     }
01416 
01417     return pixd;
01418 }
01419 
01420 
01421 /*!
01422  *  pixCloseCompBrick()
01423  *
01424  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
01425  *                     or different from pixs)
01426  *              pixs (1 bpp)
01427  *              hsize (width of brick Sel)
01428  *              vsize (height of brick Sel)
01429  *      Return: pixd, or null on error
01430  *
01431  *  Notes:
01432  *      (1) Sel is a brick with all elements being hits
01433  *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
01434  *      (3) Do compositely for each dimension > 1.
01435  *      (4) Do separably if both hsize and vsize are > 1.
01436  *      (5) There are three cases:
01437  *          (a) pixd == null   (result into new pixd)
01438  *          (b) pixd == pixs   (in-place; writes result back to pixs)
01439  *          (c) pixd != pixs   (puts result into existing pixd)
01440  *      (6) For clarity, if the case is known, use these patterns:
01441  *          (a) pixd = pixCloseCompBrick(NULL, pixs, ...);
01442  *          (b) pixCloseCompBrick(pixs, pixs, ...);
01443  *          (c) pixCloseCompBrick(pixd, pixs, ...);
01444  *      (7) The dimensions of the resulting image are determined by pixs.
01445  *      (8) CAUTION: both hsize and vsize are being decomposed.
01446  *          The decomposer chooses a product of sizes (call them
01447  *          'terms') for each that is close to the input size,
01448  *          but not necessarily equal to it.  It attempts to optimize:
01449  *             (a) for consistency with the input values: the product
01450  *                 of terms is close to the input size
01451  *             (b) for efficiency of the operation: the sum of the
01452  *                 terms is small; ideally about twice the square
01453  *                 root of the input size.
01454  *          So, for example, if the input hsize = 37, which is
01455  *          a prime number, the decomposer will break this into two
01456  *          terms, 6 and 6, so that the net result is a dilation
01457  *          with hsize = 36.
01458  */
01459 PIX *
01460 pixCloseCompBrick(PIX     *pixd,
01461                   PIX     *pixs,
01462                   l_int32  hsize,
01463                   l_int32  vsize)
01464 {
01465 PIX  *pixt;
01466 SEL  *selh1, *selh2, *selv1, *selv2;
01467 
01468     PROCNAME("pixCloseCompBrick");
01469 
01470     if (!pixs)
01471         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
01472     if (pixGetDepth(pixs) != 1)
01473         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
01474     if (hsize < 1 || vsize < 1)
01475         return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
01476 
01477     if (hsize == 1 && vsize == 1)
01478         return pixCopy(pixd, pixs);
01479     if (hsize > 1)
01480         selectComposableSels(hsize, L_HORIZ, &selh1, &selh2);
01481     if (vsize > 1)
01482         selectComposableSels(vsize, L_VERT, &selv1, &selv2);
01483     if (vsize == 1) {
01484         pixt = pixDilate(NULL, pixs, selh1);
01485         pixd = pixDilate(pixd, pixt, selh2);
01486         pixErode(pixt, pixd, selh1);
01487         pixErode(pixd, pixt, selh2);
01488     } 
01489     else if (hsize == 1) {
01490         pixt = pixDilate(NULL, pixs, selv1);
01491         pixd = pixDilate(pixd, pixt, selv2);
01492         pixErode(pixt, pixd, selv1);
01493         pixErode(pixd, pixt, selv2);
01494     } 
01495     else {  /* do separably */ 
01496         pixt = pixDilate(NULL, pixs, selh1);
01497         pixd = pixDilate(pixd, pixt, selh2);
01498         pixDilate(pixt, pixd, selv1);
01499         pixDilate(pixd, pixt, selv2);
01500         pixErode(pixt, pixd, selh1);
01501         pixErode(pixd, pixt, selh2);
01502         pixErode(pixt, pixd, selv1);
01503         pixErode(pixd, pixt, selv2);
01504     }
01505     pixDestroy(&pixt);
01506 
01507     if (hsize > 1) {
01508         selDestroy(&selh1);
01509         selDestroy(&selh2);
01510     }
01511     if (vsize > 1) {
01512         selDestroy(&selv1);
01513         selDestroy(&selv2);
01514     }
01515 
01516     return pixd;
01517 }
01518 
01519 
01520 /*!
01521  *  pixCloseSafeCompBrick()
01522  *
01523  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
01524  *                     or different from pixs)
01525  *              pixs (1 bpp)
01526  *              hsize (width of brick Sel)
01527  *              vsize (height of brick Sel)
01528  *      Return: pixd, or null on error
01529  *
01530  *  Notes:
01531  *      (1) Sel is a brick with all elements being hits
01532  *      (2) The origin is at (x, y) = (hsize/2, vsize/2)
01533  *      (3) Do compositely for each dimension > 1.
01534  *      (4) Do separably if both hsize and vsize are > 1.
01535  *      (5) Safe closing adds a border of 0 pixels, of sufficient size so
01536  *          that all pixels in input image are processed within
01537  *          32-bit words in the expanded image.  As a result, there is
01538  *          no special processing for pixels near the boundary, and there
01539  *          are no boundary effects.  The border is removed at the end.
01540  *      (6) There are three cases:
01541  *          (a) pixd == null   (result into new pixd)
01542  *          (b) pixd == pixs   (in-place; writes result back to pixs)
01543  *          (c) pixd != pixs   (puts result into existing pixd)
01544  *      (7) For clarity, if the case is known, use these patterns:
01545  *          (a) pixd = pixCloseSafeCompBrick(NULL, pixs, ...);
01546  *          (b) pixCloseSafeCompBrick(pixs, pixs, ...);
01547  *          (c) pixCloseSafeCompBrick(pixd, pixs, ...);
01548  *      (8) The dimensions of the resulting image are determined by pixs.
01549  *      (9) CAUTION: both hsize and vsize are being decomposed.
01550  *          The decomposer chooses a product of sizes (call them
01551  *          'terms') for each that is close to the input size,
01552  *          but not necessarily equal to it.  It attempts to optimize:
01553  *             (a) for consistency with the input values: the product
01554  *                 of terms is close to the input size
01555  *             (b) for efficiency of the operation: the sum of the
01556  *                 terms is small; ideally about twice the square
01557  *                 root of the input size.
01558  *          So, for example, if the input hsize = 37, which is
01559  *          a prime number, the decomposer will break this into two
01560  *          terms, 6 and 6, so that the net result is a dilation
01561  *          with hsize = 36.
01562  */
01563 PIX *
01564 pixCloseSafeCompBrick(PIX     *pixd,
01565                       PIX     *pixs,
01566                       l_int32  hsize,
01567                       l_int32  vsize)
01568 {
01569 l_int32  maxtrans, bordsize;
01570 PIX     *pixsb, *pixt, *pixdb;
01571 SEL     *selh1, *selh2, *selv1, *selv2;
01572 
01573     PROCNAME("pixCloseSafeCompBrick");
01574 
01575     if (!pixs)
01576         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
01577     if (pixGetDepth(pixs) != 1)
01578         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
01579     if (hsize < 1 || vsize < 1)
01580         return (PIX *)ERROR_PTR("hsize and vsize not >= 1", procName, pixd);
01581 
01582     if (hsize == 1 && vsize == 1)
01583         return pixCopy(pixd, pixs);
01584 
01585         /* Symmetric b.c. handles correctly without added pixels */
01586     if (MORPH_BC == SYMMETRIC_MORPH_BC)
01587         return pixCloseCompBrick(pixd, pixs, hsize, vsize);
01588 
01589     maxtrans = L_MAX(hsize / 2, vsize / 2);
01590     bordsize = 32 * ((maxtrans + 31) / 32);  /* full 32 bit words */
01591     pixsb = pixAddBorder(pixs, bordsize, 0);
01592 
01593     if (hsize > 1)
01594         selectComposableSels(hsize, L_HORIZ, &selh1, &selh2);
01595     if (vsize > 1)
01596         selectComposableSels(vsize, L_VERT, &selv1, &selv2);
01597     if (vsize == 1) {
01598         pixt = pixDilate(NULL, pixsb, selh1);
01599         pixdb = pixDilate(NULL, pixt, selh2);
01600         pixErode(pixt, pixdb, selh1);
01601         pixErode(pixdb, pixt, selh2);
01602     } 
01603     else if (hsize == 1) {
01604         pixt = pixDilate(NULL, pixsb, selv1);
01605         pixdb = pixDilate(NULL, pixt, selv2);
01606         pixErode(pixt, pixdb, selv1);
01607         pixErode(pixdb, pixt, selv2);
01608     } 
01609     else {  /* do separably */ 
01610         pixt = pixDilate(NULL, pixsb, selh1);
01611         pixdb = pixDilate(NULL, pixt, selh2);
01612         pixDilate(pixt, pixdb, selv1);
01613         pixDilate(pixdb, pixt, selv2);
01614         pixErode(pixt, pixdb, selh1);
01615         pixErode(pixdb, pixt, selh2);
01616         pixErode(pixt, pixdb, selv1);
01617         pixErode(pixdb, pixt, selv2);
01618     }
01619     pixDestroy(&pixt);
01620 
01621     pixt = pixRemoveBorder(pixdb, bordsize);
01622     pixDestroy(&pixsb);
01623     pixDestroy(&pixdb);
01624 
01625     if (!pixd)
01626         pixd = pixt;
01627     else {
01628         pixCopy(pixd, pixt);
01629         pixDestroy(&pixt);
01630     }
01631 
01632     if (hsize > 1) {
01633         selDestroy(&selh1);
01634         selDestroy(&selh2);
01635     }
01636     if (vsize > 1) {
01637         selDestroy(&selv1);
01638         selDestroy(&selv2);
01639     }
01640 
01641     return pixd;
01642 }
01643 
01644 
01645 /*-----------------------------------------------------------------*
01646  *           Functions associated with boundary conditions         *
01647  *-----------------------------------------------------------------*/
01648 /*!
01649  *  resetMorphBoundaryCondition()
01650  *
01651  *      Input:  bc (SYMMETRIC_MORPH_BC, ASYMMETRIC_MORPH_BC)
01652  *      Return: void
01653  */
01654 void
01655 resetMorphBoundaryCondition(l_int32  bc)
01656 {
01657     PROCNAME("resetMorphBoundaryCondition");
01658 
01659     if (bc != SYMMETRIC_MORPH_BC && bc != ASYMMETRIC_MORPH_BC) {
01660         L_WARNING("invalid bc; using asymmetric", procName);
01661         bc = ASYMMETRIC_MORPH_BC;
01662     }
01663     MORPH_BC = bc;
01664     return;
01665 }
01666 
01667 
01668 /*!
01669  *  getMorphBorderPixelColor()
01670  *
01671  *      Input:  type (L_MORPH_DILATE, L_MORPH_ERODE) 
01672  *              depth (of pix)
01673  *      Return: color of border pixels for this operation
01674  */
01675 l_uint32
01676 getMorphBorderPixelColor(l_int32  type,
01677                          l_int32  depth)
01678 {
01679     PROCNAME("getMorphBorderPixelColor");
01680 
01681     if (type != L_MORPH_DILATE && type != L_MORPH_ERODE)
01682         return ERROR_INT("invalid type", procName, 0);
01683     if (depth != 1 && depth != 2 && depth != 4 && depth != 8 &&
01684         depth != 16 && depth != 32)
01685         return ERROR_INT("invalid depth", procName, 0);
01686 
01687     if (MORPH_BC == ASYMMETRIC_MORPH_BC || type == L_MORPH_DILATE)
01688         return 0;
01689 
01690         /* Symmetric & erosion */
01691     if (depth < 32)
01692         return ((1 << depth) - 1);
01693     else  /* depth == 32 */
01694         return 0xffffff00;
01695 }
01696 
01697 
01698 /*-----------------------------------------------------------------*
01699  *               Static helpers for arg processing                 *
01700  *-----------------------------------------------------------------*/
01701 /*!
01702  *  processMorphArgs1()
01703  *
01704  *      Input:  pixd (<optional>; this can be null, equal to pixs,
01705  *                    or different from pixs)
01706  *              pixs (1 bpp)
01707  *              sel
01708  *              &pixt (<returned>)
01709  *      Return: pixd, or null on error.
01710  *
01711  *  Notes:
01712  *      (1) This is used for generic erosion, dilation and HMT.
01713  */
01714 static PIX *
01715 processMorphArgs1(PIX   *pixd,
01716                   PIX   *pixs,
01717                   SEL   *sel,
01718                   PIX  **ppixt)
01719 {
01720 l_int32  sx, sy;
01721 
01722     PROCNAME("processMorphArgs1");
01723 
01724     if (!ppixt)
01725         return (PIX *)ERROR_PTR("&pixt not defined", procName, pixd);
01726     *ppixt = NULL;
01727     if (!pixs)
01728         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
01729     if (!sel)
01730         return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
01731     if (pixGetDepth(pixs) != 1)
01732         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
01733 
01734     selGetParameters(sel, &sx, &sy, NULL, NULL);
01735     if (sx == 0 || sy == 0)
01736         return (PIX *)ERROR_PTR("sel of size 0", procName, pixd);
01737 
01738         /* We require pixd to exist and to be the same size as pixs.
01739          * Further, pixt must be a copy (or clone) of pixs.  */
01740     if (!pixd) {
01741         if ((pixd = pixCreateTemplate(pixs)) == NULL)
01742             return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
01743         *ppixt = pixClone(pixs);
01744     }
01745     else {
01746         pixResizeImageData(pixd, pixs);
01747         if (pixd == pixs) {  /* in-place; must make a copy of pixs */
01748             if ((*ppixt = pixCopy(NULL, pixs)) == NULL)
01749                 return (PIX *)ERROR_PTR("pixt not made", procName, pixd);
01750         }
01751         else
01752             *ppixt = pixClone(pixs);
01753     }
01754     return pixd;
01755 }
01756 
01757 
01758 /*!
01759  *  processMorphArgs2()
01760  *
01761  *  This is used for generic openings and closings.
01762  */
01763 static PIX *
01764 processMorphArgs2(PIX   *pixd,
01765                   PIX   *pixs,
01766                   SEL   *sel)
01767 {
01768 l_int32  sx, sy;
01769 
01770     PROCNAME("processMorphArgs2");
01771 
01772     if (!pixs)
01773         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
01774     if (!sel)
01775         return (PIX *)ERROR_PTR("sel not defined", procName, pixd);
01776     if (pixGetDepth(pixs) != 1)
01777         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
01778 
01779     selGetParameters(sel, &sx, &sy, NULL, NULL);
01780     if (sx == 0 || sy == 0)
01781         return (PIX *)ERROR_PTR("sel of size 0", procName, pixd);
01782 
01783     if (!pixd)
01784         return pixCreateTemplate(pixs);
01785     pixResizeImageData(pixd, pixs);
01786     return pixd;
01787 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines