Leptonica 1.68
C Image Processing Library

morphapp.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 /*
00018  *  morphapp.c
00019  *
00020  *      These are some useful and/or interesting composite
00021  *      image processing operations, of the type that are often
00022  *      useful in applications.  Most are morphological in
00023  *      nature.
00024  *
00025  *      Extraction of boundary pixels
00026  *            PIX       *pixExtractBoundary()
00027  *
00028  *      Selective morph sequence operation under mask
00029  *            PIX       *pixMorphSequenceMasked()
00030  *
00031  *      Selective morph sequence operation on each component
00032  *            PIX       *pixMorphSequenceByComponent()
00033  *            PIXA      *pixaMorphSequenceByComponent()
00034  *
00035  *      Selective morph sequence operation on each region
00036  *            PIX       *pixMorphSequenceByRegion()
00037  *            PIXA      *pixaMorphSequenceByRegion()
00038  *
00039  *      Union and intersection of parallel composite operations
00040  *            PIX       *pixUnionOfMorphOps()
00041  *            PIX       *pixIntersectionOfMorphOps()
00042  *
00043  *      Selective connected component filling
00044  *            PIX       *pixSelectiveConnCompFill()
00045  *
00046  *      Removal of matched patterns
00047  *            PIX       *pixRemoveMatchedPattern()
00048  *
00049  *      Display of matched patterns
00050  *            PIX       *pixDisplayMatchedPattern()
00051  *
00052  *      Iterative morphological seed filling (don't use for real work)
00053  *            PIX       *pixSeedfillMorph()
00054  *      
00055  *      Granulometry on binary images
00056  *            NUMA      *pixRunHistogramMorph()
00057  *
00058  *      Composite operations on grayscale images
00059  *            PIX       *pixTophat()
00060  *            PIX       *pixHDome()
00061  *            PIX       *pixFastTophat()
00062  *            PIX       *pixMorphGradient()
00063  *
00064  *      Centroid of component
00065  *            PTA       *pixaCentroids()
00066  *            l_int32    pixCentroid()
00067  */
00068 
00069 #include "allheaders.h"
00070 
00071 #define   SWAP(x, y)   {temp = (x); (x) = (y); (y) = temp;}
00072 
00073 
00074 /*-----------------------------------------------------------------*
00075  *                   Extraction of boundary pixels                 *
00076  *-----------------------------------------------------------------*/
00077 /*!
00078  *  pixExtractBoundary()
00079  *
00080  *      Input:  pixs (1 bpp)
00081  *              type (0 for background pixels; 1 for foreground pixels)
00082  *      Return: pixd, or null on error
00083  *
00084  *  Notes:
00085  *      (1) Extracts the fg or bg boundary pixels for each component.
00086  *          Components are assumed to end at the boundary of pixs.
00087  */
00088 PIX *
00089 pixExtractBoundary(PIX     *pixs,
00090                    l_int32  type)
00091 {
00092 PIX  *pixd;
00093 
00094     PROCNAME("pixExtractBoundary");
00095 
00096     if (!pixs)
00097         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00098 
00099     if (type == 0)
00100         pixd = pixDilateBrick(NULL, pixs, 3, 3);
00101     else
00102         pixd = pixErodeBrick(NULL, pixs, 3, 3);
00103     pixXor(pixd, pixd, pixs);
00104     return pixd;
00105 }
00106 
00107 
00108 /*-----------------------------------------------------------------*
00109  *           Selective morph sequence operation under mask         *
00110  *-----------------------------------------------------------------*/
00111 /*!
00112  *  pixMorphSequenceMasked()
00113  *
00114  *      Input:  pixs (1 bpp)
00115  *              pixm (<optional> 1 bpp mask)
00116  *              sequence (string specifying sequence of operations)
00117  *              dispsep (horizontal separation in pixels between
00118  *                       successive displays; use zero to suppress display)
00119  *      Return: pixd, or null on error
00120  *
00121  *  Notes:
00122  *      (1) This applies the morph sequence to the image, but only allows
00123  *          changes in pixs for pixels under the background of pixm.
00124  *      (5) If pixm is NULL, this is just pixMorphSequence().
00125  */
00126 PIX *
00127 pixMorphSequenceMasked(PIX         *pixs,
00128                        PIX         *pixm,
00129                        const char  *sequence,
00130                        l_int32      dispsep)
00131 {
00132 PIX  *pixd;
00133 
00134     PROCNAME("pixMorphSequenceMasked");
00135 
00136     if (!pixs)
00137         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00138     if (!sequence)
00139         return (PIX *)ERROR_PTR("sequence not defined", procName, NULL);
00140 
00141     pixd = pixMorphSequence(pixs, sequence, dispsep);
00142     pixCombineMasked(pixd, pixs, pixm);  /* restore src pixels under mask fg */
00143     return pixd;
00144 }
00145 
00146 
00147 /*-----------------------------------------------------------------*
00148  *             Morph sequence operation on each component          *
00149  *-----------------------------------------------------------------*/
00150 /*!
00151  *  pixMorphSequenceByComponent()
00152  *
00153  *      Input:  pixs (1 bpp)
00154  *              sequence (string specifying sequence)
00155  *              connectivity (4 or 8)
00156  *              minw  (minimum width to consider; use 0 or 1 for any width)
00157  *              minh  (minimum height to consider; use 0 or 1 for any height)
00158  *              &boxa (<optional> return boxa of c.c. in pixs)
00159  *      Return: pixd, or null on error
00160  *
00161  *  Notes:
00162  *      (1) See pixMorphSequence() for composing operation sequences.
00163  *      (2) This operates separately on each c.c. in the input pix.
00164  *      (3) The dilation does NOT increase the c.c. size; it is clipped
00165  *          to the size of the original c.c.   This is necessary to
00166  *          keep the c.c. independent after the operation.
00167  *      (4) You can specify that the width and/or height must equal
00168  *          or exceed a minimum size for the operation to take place.
00169  *      (5) Use NULL for boxa to avoid returning the boxa.
00170  */
00171 PIX *
00172 pixMorphSequenceByComponent(PIX         *pixs,
00173                             const char  *sequence,
00174                             l_int32      connectivity,
00175                             l_int32      minw,
00176                             l_int32      minh,
00177                             BOXA       **pboxa)
00178 {
00179 l_int32  n, i, x, y, w, h;
00180 BOXA    *boxa;
00181 PIX     *pix, *pixd;
00182 PIXA    *pixas, *pixad;
00183 
00184     PROCNAME("pixMorphSequenceByComponent");
00185 
00186     if (!pixs)
00187         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00188     if (!sequence)
00189         return (PIX *)ERROR_PTR("sequence not defined", procName, NULL);
00190 
00191     if (minw <= 0) minw = 1;
00192     if (minh <= 0) minh = 1;
00193 
00194         /* Get the c.c. */
00195     if ((boxa = pixConnComp(pixs, &pixas, connectivity)) == NULL)
00196         return (PIX *)ERROR_PTR("boxa not made", procName, NULL);
00197 
00198         /* Operate on each c.c. independently */
00199     pixad = pixaMorphSequenceByComponent(pixas, sequence, minw, minh);
00200     pixaDestroy(&pixas);
00201     boxaDestroy(&boxa);
00202     if (!pixad)
00203         return (PIX *)ERROR_PTR("pixad not made", procName, NULL);
00204 
00205         /* Display the result out into pixd */
00206     pixd = pixCreateTemplate(pixs);
00207     n = pixaGetCount(pixad);
00208     for (i = 0; i < n; i++) {
00209         pixaGetBoxGeometry(pixad, i, &x, &y, &w, &h);
00210         pix = pixaGetPix(pixad, i, L_CLONE);
00211         pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix, 0, 0);
00212         pixDestroy(&pix);
00213     }
00214 
00215     if (pboxa)
00216         *pboxa = pixaGetBoxa(pixad, L_CLONE);
00217     pixaDestroy(&pixad);
00218     return pixd;
00219 }
00220 
00221 
00222 /*!
00223  *  pixaMorphSequenceByComponent()
00224  *
00225  *      Input:  pixas (of 1 bpp pix)
00226  *              sequence (string specifying sequence)
00227  *              minw  (minimum width to consider; use 0 or 1 for any width)
00228  *              minh  (minimum height to consider; use 0 or 1 for any height)
00229  *      Return: pixad, or null on error
00230  *
00231  *  Notes:
00232  *      (1) See pixMorphSequence() for composing operation sequences.
00233  *      (2) This operates separately on each c.c. in the input pixa.
00234  *      (3) You can specify that the width and/or height must equal
00235  *          or exceed a minimum size for the operation to take place.
00236  *      (4) The input pixa should have a boxa giving the locations
00237  *          of the pix components.
00238  */
00239 PIXA *
00240 pixaMorphSequenceByComponent(PIXA        *pixas,
00241                              const char  *sequence,
00242                              l_int32      minw,
00243                              l_int32      minh)
00244 {
00245 l_int32  n, i, w, h, d;
00246 BOX     *box;
00247 PIX     *pixt1, *pixt2;
00248 PIXA    *pixad;
00249 
00250     PROCNAME("pixaMorphSequenceByComponent");
00251 
00252     if (!pixas)
00253         return (PIXA *)ERROR_PTR("pixas not defined", procName, NULL);
00254     if ((n = pixaGetCount(pixas)) == 0)
00255         return (PIXA *)ERROR_PTR("no pix in pixas", procName, NULL);
00256     if (n != pixaGetBoxaCount(pixas))
00257         L_WARNING("boxa size != n", procName);
00258     pixaGetPixDimensions(pixas, 0, NULL, NULL, &d);
00259     if (d != 1)
00260         return (PIXA *)ERROR_PTR("depth not 1 bpp", procName, NULL);
00261 
00262     if (!sequence)
00263         return (PIXA *)ERROR_PTR("sequence not defined", procName, NULL);
00264     if (minw <= 0) minw = 1;
00265     if (minh <= 0) minh = 1;
00266 
00267     if ((pixad = pixaCreate(n)) == NULL)
00268         return (PIXA *)ERROR_PTR("pixad not made", procName, NULL);
00269     for (i = 0; i < n; i++) {
00270         pixaGetPixDimensions(pixas, i, &w, &h, NULL);
00271         if (w >= minw && h >= minh) {
00272             if ((pixt1 = pixaGetPix(pixas, i, L_CLONE)) == NULL)
00273                 return (PIXA *)ERROR_PTR("pixt1 not found", procName, NULL);
00274             if ((pixt2 = pixMorphCompSequence(pixt1, sequence, 0)) == NULL)
00275                 return (PIXA *)ERROR_PTR("pixt2 not made", procName, NULL);
00276             pixaAddPix(pixad, pixt2, L_INSERT);
00277             box = pixaGetBox(pixas, i, L_COPY);
00278             pixaAddBox(pixad, box, L_INSERT);
00279             pixDestroy(&pixt1);
00280         }
00281     }
00282 
00283     return pixad;
00284 }
00285 
00286 
00287 /*-----------------------------------------------------------------*
00288  *              Morph sequence operation on each region            *
00289  *-----------------------------------------------------------------*/
00290 /*!
00291  *  pixMorphSequenceByRegion()
00292  *
00293  *      Input:  pixs (1 bpp)
00294  *              pixm (mask specifying regions)
00295  *              sequence (string specifying sequence)
00296  *              connectivity (4 or 8, used on mask)
00297  *              minw  (minimum width to consider; use 0 or 1 for any width)
00298  *              minh  (minimum height to consider; use 0 or 1 for any height)
00299  *              &boxa (<optional> return boxa of c.c. in pixm)
00300  *      Return: pixd, or null on error
00301  *
00302  *  Notes:
00303  *      (1) See pixMorphCompSequence() for composing operation sequences.
00304  *      (2) This operates separately on the region in pixs corresponding
00305  *          to each c.c. in the mask pixm.  It differs from
00306  *          pixMorphSequenceByComponent() in that the latter does not have
00307  *          a pixm (mask), but instead operates independently on each
00308  *          component in pixs.
00309  *      (3) Dilation will NOT increase the region size; the result
00310  *          is clipped to the size of the mask region.  This is necessary
00311  *          to make regions independent after the operation.
00312  *      (4) You can specify that the width and/or height of a region must
00313  *          equal or exceed a minimum size for the operation to take place.
00314  *      (5) Use NULL for @pboxa to avoid returning the boxa.
00315  */
00316 PIX *
00317 pixMorphSequenceByRegion(PIX         *pixs,
00318                          PIX         *pixm,
00319                          const char  *sequence,
00320                          l_int32      connectivity,
00321                          l_int32      minw,
00322                          l_int32      minh,
00323                          BOXA       **pboxa)
00324 {
00325 l_int32  n, i, x, y, w, h;
00326 BOXA    *boxa;
00327 PIX     *pix, *pixd;
00328 PIXA    *pixam, *pixad;
00329 
00330     PROCNAME("pixMorphSequenceByRegion");
00331 
00332     if (!pixs)
00333         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00334     if (!pixm)
00335         return (PIX *)ERROR_PTR("pixm not defined", procName, NULL);
00336     if (pixGetDepth(pixs) != 1 || pixGetDepth(pixm) != 1)
00337         return (PIX *)ERROR_PTR("pixs and pixm not both 1 bpp", procName, NULL);
00338     if (!sequence)
00339         return (PIX *)ERROR_PTR("sequence not defined", procName, NULL);
00340 
00341     if (minw <= 0) minw = 1;
00342     if (minh <= 0) minh = 1;
00343 
00344         /* Get the c.c. of the mask */
00345     if ((boxa = pixConnComp(pixm, &pixam, connectivity)) == NULL)
00346         return (PIX *)ERROR_PTR("boxa not made", procName, NULL);
00347 
00348         /* Operate on each region in pixs independently */
00349     pixad = pixaMorphSequenceByRegion(pixs, pixam, sequence, minw, minh);
00350     pixaDestroy(&pixam);
00351     boxaDestroy(&boxa);
00352     if (!pixad)
00353         return (PIX *)ERROR_PTR("pixad not made", procName, NULL);
00354 
00355         /* Display the result out into pixd */
00356     pixd = pixCreateTemplate(pixs);
00357     n = pixaGetCount(pixad);
00358     for (i = 0; i < n; i++) {
00359         pixaGetBoxGeometry(pixad, i, &x, &y, &w, &h);
00360         pix = pixaGetPix(pixad, i, L_CLONE);
00361         pixRasterop(pixd, x, y, w, h, PIX_PAINT, pix, 0, 0);
00362         pixDestroy(&pix);
00363     }
00364 
00365     if (pboxa)
00366         *pboxa = pixaGetBoxa(pixad, L_CLONE);
00367     pixaDestroy(&pixad);
00368     return pixd;
00369 }
00370 
00371 
00372 /*!
00373  *  pixaMorphSequenceByRegion()
00374  *
00375  *      Input:  pixs (1 bpp)
00376  *              pixam (of 1 bpp mask elements)
00377  *              sequence (string specifying sequence)
00378  *              minw  (minimum width to consider; use 0 or 1 for any width)
00379  *              minh  (minimum height to consider; use 0 or 1 for any height)
00380  *      Return: pixad, or null on error
00381  *
00382  *  Notes:
00383  *      (1) See pixMorphSequence() for composing operation sequences.
00384  *      (2) This operates separately on each region in the input pixs
00385  *          defined by the components in pixam.
00386  *      (3) You can specify that the width and/or height of a mask
00387  *          component must equal or exceed a minimum size for the
00388  *          operation to take place.
00389  *      (4) The input pixam should have a boxa giving the locations
00390  *          of the regions in pixs.
00391  */
00392 PIXA *
00393 pixaMorphSequenceByRegion(PIX         *pixs,
00394                           PIXA        *pixam,
00395                           const char  *sequence,
00396                           l_int32      minw,
00397                           l_int32      minh)
00398 {
00399 l_int32  n, i, w, h, d;
00400 BOX     *box;
00401 PIX     *pixt1, *pixt2, *pixt3;
00402 PIXA    *pixad;
00403 
00404     PROCNAME("pixaMorphSequenceByRegion");
00405 
00406     if (!pixs)
00407         return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL);
00408     if (pixGetDepth(pixs) != 1)
00409         return (PIXA *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00410     if (!pixam)
00411         return (PIXA *)ERROR_PTR("pixam not defined", procName, NULL);
00412     pixaGetPixDimensions(pixam, 0, NULL, NULL, &d);
00413     if (d != 1)
00414         return (PIXA *)ERROR_PTR("mask depth not 1 bpp", procName, NULL);
00415     if ((n = pixaGetCount(pixam)) == 0)
00416         return (PIXA *)ERROR_PTR("no regions specified", procName, NULL);
00417     if (n != pixaGetBoxaCount(pixam))
00418         L_WARNING("boxa size != n", procName);
00419     if (!sequence)
00420         return (PIXA *)ERROR_PTR("sequence not defined", procName, NULL);
00421 
00422     if (minw <= 0) minw = 1;
00423     if (minh <= 0) minh = 1;
00424 
00425     if ((pixad = pixaCreate(n)) == NULL)
00426         return (PIXA *)ERROR_PTR("pixad not made", procName, NULL);
00427 
00428         /* Use the rectangle to remove the appropriate part of pixs;
00429          * then AND with the mask component to get the actual fg
00430          * of pixs that is under the mask component. */
00431     for (i = 0; i < n; i++) {
00432         pixaGetPixDimensions(pixam, i, &w, &h, NULL);
00433         if (w >= minw && h >= minh) {
00434             if ((pixt1 = pixaGetPix(pixam, i, L_CLONE)) == NULL)
00435                 return (PIXA *)ERROR_PTR("pixt1 not found", procName, NULL);
00436             box = pixaGetBox(pixam, i, L_COPY);
00437             pixt2 = pixClipRectangle(pixs, box, NULL);
00438             pixAnd(pixt2, pixt2, pixt1);
00439             if ((pixt3 = pixMorphCompSequence(pixt2, sequence, 0)) == NULL)
00440                 return (PIXA *)ERROR_PTR("pixt3 not made", procName, NULL);
00441             pixaAddPix(pixad, pixt3, L_INSERT);
00442             pixaAddBox(pixad, box, L_INSERT);
00443             pixDestroy(&pixt1);
00444             pixDestroy(&pixt2);
00445         }
00446     }
00447 
00448     return pixad;
00449 }
00450 
00451 
00452 /*-----------------------------------------------------------------*
00453  *      Union and intersection of parallel composite operations    *
00454  *-----------------------------------------------------------------*/
00455 /*!
00456  *  pixUnionOfMorphOps()
00457  *
00458  *      Input:  pixs (binary)
00459  *              sela
00460  *              type (L_MORPH_DILATE, etc.)
00461  *      Return: pixd (union of the specified morphological operation
00462  *                    on pixs for each Sel in the Sela), or null on error
00463  */
00464 PIX *
00465 pixUnionOfMorphOps(PIX     *pixs,
00466                    SELA    *sela,
00467                    l_int32  type)
00468 {
00469 l_int32  n, i;
00470 PIX     *pixt, *pixd;
00471 SEL     *sel;
00472 
00473     PROCNAME("pixUnionOfMorphOps");
00474 
00475     if (!pixs || pixGetDepth(pixs) != 1)
00476         return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
00477     if (!sela)
00478         return (PIX *)ERROR_PTR("sela not defined", procName, NULL);
00479     n = selaGetCount(sela);
00480     if (n == 0)
00481         return (PIX *)ERROR_PTR("no sels in sela", procName, NULL);
00482     if (type != L_MORPH_DILATE && type != L_MORPH_ERODE &&
00483         type != L_MORPH_OPEN && type != L_MORPH_CLOSE &&
00484         type != L_MORPH_HMT)
00485         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
00486 
00487     pixd = pixCreateTemplate(pixs);
00488     for (i = 0; i < n; i++) {
00489         sel = selaGetSel(sela, i);
00490         if (type == L_MORPH_DILATE)
00491             pixt = pixDilate(NULL, pixs, sel);
00492         else if (type == L_MORPH_ERODE)
00493             pixt = pixErode(NULL, pixs, sel);
00494         else if (type == L_MORPH_OPEN)
00495             pixt = pixOpen(NULL, pixs, sel);
00496         else if (type == L_MORPH_CLOSE)
00497             pixt = pixClose(NULL, pixs, sel);
00498         else  /* type == L_MORPH_HMT */
00499             pixt = pixHMT(NULL, pixs, sel);
00500         pixOr(pixd, pixd, pixt);
00501         pixDestroy(&pixt);
00502     }
00503 
00504     return pixd;
00505 }
00506 
00507 
00508 /*!
00509  *  pixIntersectionOfMorphOps()
00510  *
00511  *      Input:  pixs (binary)
00512  *              sela 
00513  *              type (L_MORPH_DILATE, etc.)
00514  *      Return: pixd (intersection of the specified morphological operation
00515  *                    on pixs for each Sel in the Sela), or null on error
00516  */
00517 PIX *
00518 pixIntersectionOfMorphOps(PIX     *pixs,
00519                           SELA    *sela,
00520                           l_int32  type)
00521 {
00522 l_int32  n, i;
00523 PIX     *pixt, *pixd;
00524 SEL     *sel;
00525 
00526     PROCNAME("pixIntersectionOfMorphOps");
00527 
00528     if (!pixs || pixGetDepth(pixs) != 1)
00529         return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
00530     if (!sela)
00531         return (PIX *)ERROR_PTR("sela not defined", procName, NULL);
00532     n = selaGetCount(sela);
00533     if (n == 0)
00534         return (PIX *)ERROR_PTR("no sels in sela", procName, NULL);
00535     if (type != L_MORPH_DILATE && type != L_MORPH_ERODE &&
00536         type != L_MORPH_OPEN && type != L_MORPH_CLOSE &&
00537         type != L_MORPH_HMT)
00538         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
00539 
00540     pixd = pixCreateTemplate(pixs);
00541     pixSetAll(pixd);
00542     for (i = 0; i < n; i++) {
00543         sel = selaGetSel(sela, i);
00544         if (type == L_MORPH_DILATE)
00545             pixt = pixDilate(NULL, pixs, sel);
00546         else if (type == L_MORPH_ERODE)
00547             pixt = pixErode(NULL, pixs, sel);
00548         else if (type == L_MORPH_OPEN)
00549             pixt = pixOpen(NULL, pixs, sel);
00550         else if (type == L_MORPH_CLOSE)
00551             pixt = pixClose(NULL, pixs, sel);
00552         else  /* type == L_MORPH_HMT */
00553             pixt = pixHMT(NULL, pixs, sel);
00554         pixAnd(pixd, pixd, pixt);
00555         pixDestroy(&pixt);
00556     }
00557 
00558     return pixd;
00559 }
00560 
00561 
00562 
00563 /*-----------------------------------------------------------------*
00564  *             Selective connected component filling               *
00565  *-----------------------------------------------------------------*/
00566 /*!
00567  *  pixSelectiveConnCompFill()
00568  *
00569  *      Input:  pixs (binary)
00570  *              connectivity (4 or 8)
00571  *              minw  (minimum width to consider; use 0 or 1 for any width)
00572  *              minh  (minimum height to consider; use 0 or 1 for any height)
00573  *      Return: pix (with holes filled in selected c.c.), or null on error
00574  */
00575 PIX *
00576 pixSelectiveConnCompFill(PIX     *pixs,
00577                          l_int32  connectivity,
00578                          l_int32  minw,
00579                          l_int32  minh)
00580 {
00581 l_int32  n, i, x, y, w, h;
00582 BOXA    *boxa;
00583 PIX     *pixt1, *pixt2, *pixd;
00584 PIXA    *pixa;
00585 
00586     PROCNAME("pixSelectiveConnCompFill");
00587 
00588     if (!pixs)
00589         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00590     if (pixGetDepth(pixs) != 1)
00591         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00592 
00593     if (minw <= 0) minw = 1;
00594     if (minh <= 0) minh = 1;
00595 
00596     if ((pixd = pixCopy(NULL, pixs)) == NULL)
00597         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
00598 
00599     if ((boxa = pixConnComp(pixs, &pixa, connectivity)) == NULL)
00600         return (PIX *)ERROR_PTR("boxa not made", procName, NULL);
00601     n = boxaGetCount(boxa);
00602     for (i = 0; i < n; i++) {
00603         boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
00604         if (w >= minw && h >= minh) {
00605             if ((pixt1 = pixaGetPix(pixa, i, L_CLONE)) == NULL)
00606                 return (PIX *)ERROR_PTR("pixt1 not found", procName, NULL);
00607             if ((pixt2 = pixHolesByFilling(pixt1, 12 - connectivity)) == NULL)
00608                 return (PIX *)ERROR_PTR("pixt2 not made", procName, NULL);
00609             pixRasterop(pixd, x, y, w, h, PIX_PAINT, pixt2, 0, 0);
00610             pixDestroy(&pixt1);
00611             pixDestroy(&pixt2);
00612         }
00613     }
00614     pixaDestroy(&pixa);
00615     boxaDestroy(&boxa);
00616 
00617     return pixd;
00618 }
00619 
00620 
00621 /*-----------------------------------------------------------------*
00622  *                    Removal of matched patterns                  *
00623  *-----------------------------------------------------------------*/
00624 /*!
00625  *  pixRemoveMatchedPattern()
00626  *
00627  *      Input:  pixs (input image, 1 bpp)
00628  *              pixp (pattern to be removed from image, 1 bpp)
00629  *              pixe (image after erosion by Sel that approximates pixp, 1 bpp)
00630  *              x0, y0 (center of Sel)
00631  *              dsize (number of pixels on each side by which pixp is
00632  *                     dilated before being subtracted from pixs;
00633  *                     valid values are {0, 1, 2, 3, 4})
00634  *      Return: 0 if OK, 1 on error
00635  *
00636  *  Notes:
00637  *    (1) This is in-place.
00638  *    (2) You can use various functions in selgen to create a Sel
00639  *        that is used to generate pixe from pixs.
00640  *    (3) This function is applied after pixe has been computed.
00641  *        It finds the centroid of each c.c., and subtracts
00642  *        (the appropriately dilated version of) pixp, with the center
00643  *        of the Sel used to align pixp with pixs.
00644  */
00645 l_int32
00646 pixRemoveMatchedPattern(PIX     *pixs,
00647                         PIX     *pixp,
00648                         PIX     *pixe,
00649                         l_int32  x0,
00650                         l_int32  y0,
00651                         l_int32  dsize)
00652 {
00653 l_int32  i, nc, x, y, w, h, xb, yb;
00654 BOXA    *boxa;
00655 PIX     *pixt1, *pixt2;
00656 PIXA    *pixa;
00657 PTA     *pta;
00658 SEL     *sel;
00659 
00660     PROCNAME("pixRemoveMatchedPattern");
00661 
00662     if (!pixs)
00663         return ERROR_INT("pixs not defined", procName, 1);
00664     if (!pixp)
00665         return ERROR_INT("pixp not defined", procName, 1);
00666     if (!pixe)
00667         return ERROR_INT("pixe not defined", procName, 1);
00668     if (pixGetDepth(pixs) != 1 || pixGetDepth(pixp) != 1 ||
00669         pixGetDepth(pixe) != 1)
00670         return ERROR_INT("all input pix not 1 bpp", procName, 1);
00671     if (dsize < 0 || dsize > 4)
00672         return ERROR_INT("dsize not in {0,1,2,3,4}", procName, 1);
00673 
00674         /* Find the connected components and their centroids */
00675     boxa = pixConnComp(pixe, &pixa, 8);
00676     if ((nc = boxaGetCount(boxa)) == 0) {
00677         L_WARNING("no matched patterns", procName);
00678         boxaDestroy(&boxa);
00679         pixaDestroy(&pixa);
00680         return 0;
00681     }
00682     pta = pixaCentroids(pixa);
00683 
00684         /* Optionally dilate the pattern, first adding a border that
00685          * is large enough to accommodate the dilated pixels */
00686     sel = NULL;
00687     if (dsize > 0) {
00688         sel = selCreateBrick(2 * dsize + 1, 2 * dsize + 1, dsize, dsize,
00689                              SEL_HIT);
00690         pixt1 = pixAddBorder(pixp, dsize, 0);
00691         pixt2 = pixDilate(NULL, pixt1, sel);
00692         selDestroy(&sel);
00693         pixDestroy(&pixt1);
00694     }
00695     else
00696         pixt2 = pixClone(pixp);
00697 
00698         /* Subtract out each dilated pattern.  The centroid of each
00699          * component is located at:
00700          *       (box->x + x, box->y + y)
00701          * and the 'center' of the pattern used in making pixe is located at
00702          *       (x0 + dsize, (y0 + dsize)
00703          * relative to the UL corner of the pattern.  The center of the
00704          * pattern is placed at the center of the component. */
00705     w = pixGetWidth(pixt2);
00706     h = pixGetHeight(pixt2);
00707     for (i = 0; i < nc; i++) {
00708         ptaGetIPt(pta, i, &x, &y);
00709         boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL);
00710         pixRasterop(pixs, xb + x - x0 - dsize, yb + y - y0 - dsize,
00711                     w, h, PIX_DST & PIX_NOT(PIX_SRC), pixt2, 0, 0);
00712     }
00713 
00714     boxaDestroy(&boxa);
00715     pixaDestroy(&pixa);
00716     ptaDestroy(&pta);
00717     pixDestroy(&pixt2);
00718     return 0;
00719 }
00720 
00721 
00722 /*-----------------------------------------------------------------*
00723  *                    Display of matched patterns                  *
00724  *-----------------------------------------------------------------*/
00725 /*!
00726  *  pixDisplayMatchedPattern()
00727  *
00728  *      Input:  pixs (input image, 1 bpp)
00729  *              pixp (pattern to be removed from image, 1 bpp)
00730  *              pixe (image after erosion by Sel that approximates pixp, 1 bpp)
00731  *              x0, y0 (center of Sel)
00732  *              color (to paint the matched patterns; 0xrrggbb00)
00733  *              scale (reduction factor for output pixd)
00734  *              nlevels (if scale < 1.0, threshold to this number of levels)
00735  *      Return: pixd (8 bpp, colormapped), or null on error
00736  *
00737  *  Notes:
00738  *    (1) A 4 bpp colormapped image is generated.
00739  *    (2) If scale <= 1.0, do scale to gray for the output, and threshold
00740  *        to nlevels of gray.
00741  *    (3) You can use various functions in selgen to create a Sel
00742  *        that will generate pixe from pixs.
00743  *    (4) This function is applied after pixe has been computed.
00744  *        It finds the centroid of each c.c., and colors the output
00745  *        pixels using pixp (appropriately aligned) as a stencil.
00746  *        Alignment is done using the origin of the Sel and the
00747  *        centroid of the eroded image to place the stencil pixp.
00748  */
00749 PIX *
00750 pixDisplayMatchedPattern(PIX       *pixs,
00751                          PIX       *pixp,
00752                          PIX       *pixe,
00753                          l_int32    x0,
00754                          l_int32    y0,
00755                          l_uint32   color,
00756                          l_float32  scale,
00757                          l_int32    nlevels)
00758 {
00759 l_int32   i, nc, xb, yb, x, y, xi, yi, rval, gval, bval;
00760 BOXA     *boxa;
00761 PIX      *pixd, *pixt, *pixps;
00762 PIXA     *pixa;
00763 PTA      *pta;
00764 PIXCMAP  *cmap;
00765 
00766     PROCNAME("pixDisplayMatchedPattern");
00767 
00768     if (!pixs)
00769         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00770     if (!pixp)
00771         return (PIX *)ERROR_PTR("pixp not defined", procName, NULL);
00772     if (!pixe)
00773         return (PIX *)ERROR_PTR("pixe not defined", procName, NULL);
00774     if (pixGetDepth(pixs) != 1 || pixGetDepth(pixp) != 1 ||
00775         pixGetDepth(pixe) != 1)
00776         return (PIX *)ERROR_PTR("all input pix not 1 bpp", procName, NULL);
00777     if (scale > 1.0 || scale <= 0.0) {
00778         L_WARNING("scale > 1.0 or < 0.0; setting to 1.0", procName);
00779         scale = 1.0;
00780     }
00781 
00782         /* Find the connected components and their centroids */
00783     boxa = pixConnComp(pixe, &pixa, 8);
00784     if ((nc = boxaGetCount(boxa)) == 0) {
00785         L_WARNING("no matched patterns", procName);
00786         boxaDestroy(&boxa);
00787         pixaDestroy(&pixa);
00788         return 0;
00789     }
00790     pta = pixaCentroids(pixa);
00791 
00792     rval = GET_DATA_BYTE(&color, COLOR_RED);
00793     gval = GET_DATA_BYTE(&color, COLOR_GREEN);
00794     bval = GET_DATA_BYTE(&color, COLOR_BLUE);
00795     if (scale == 1.0) {  /* output 4 bpp at full resolution */
00796         pixd = pixConvert1To4(NULL, pixs, 0, 1);
00797         cmap = pixcmapCreate(4);
00798         pixcmapAddColor(cmap, 255, 255, 255);
00799         pixcmapAddColor(cmap, 0, 0, 0);
00800         pixSetColormap(pixd, cmap);
00801 
00802         /* Paint through pixp for each match location.  The centroid of each
00803          * component in pixe is located at:
00804          *       (box->x + x, box->y + y)
00805          * and the 'center' of the pattern used in making pixe is located at
00806          *       (x0, y0)
00807          * relative to the UL corner of the pattern.  The center of the
00808          * pattern is placed at the center of the component. */
00809         for (i = 0; i < nc; i++) {
00810             ptaGetIPt(pta, i, &x, &y);
00811             boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL);
00812             pixSetMaskedCmap(pixd, pixp, xb + x - x0, yb + y - y0,
00813                              rval, gval, bval);
00814         }
00815     }
00816     else {  /* output 4 bpp downscaled */
00817         pixt = pixScaleToGray(pixs, scale);
00818         pixd = pixThresholdTo4bpp(pixt, nlevels, 1);
00819         pixps = pixScaleBySampling(pixp, scale, scale);
00820 
00821         for (i = 0; i < nc; i++) {
00822             ptaGetIPt(pta, i, &x, &y);
00823             boxaGetBoxGeometry(boxa, i, &xb, &yb, NULL, NULL);
00824             xi = (l_int32)(scale * (xb + x - x0));
00825             yi = (l_int32)(scale * (yb + y - y0));
00826             pixSetMaskedCmap(pixd, pixps, xi, yi, rval, gval, bval);
00827         }
00828         pixDestroy(&pixt);
00829         pixDestroy(&pixps);
00830     }
00831 
00832     boxaDestroy(&boxa);
00833     pixaDestroy(&pixa);
00834     ptaDestroy(&pta);
00835     return pixd;
00836 }
00837 
00838 
00839 /*-----------------------------------------------------------------*
00840  *             Iterative morphological seed filling                *
00841  *-----------------------------------------------------------------*/
00842 /*!
00843  *  pixSeedfillMorph()
00844  *
00845  *      Input:  pixs (seed)
00846  *              pixm (mask)
00847  *              connectivity (4 or 8)
00848  *      Return: pix where seed has been grown to completion
00849  *              into the mask, or null on error
00850  *
00851  *  Notes:
00852  *    (1) This is in general a very inefficient method for filling
00853  *        from a seed into a mask.  I've included it here for
00854  *        pedagogical reasons, but it should NEVER be used if
00855  *        efficiency is any consideration -- use pixSeedfillBinary()!
00856  *    (2) We use a 3x3 brick SEL for 8-cc filling and a 3x3 plus SEL for 4-cc.
00857  */
00858 PIX *
00859 pixSeedfillMorph(PIX     *pixs,
00860                  PIX     *pixm,
00861                  l_int32  connectivity)
00862 {
00863 l_int32  same, iter;
00864 PIX     *pixt1, *pixd, *temp;
00865 SEL     *sel_3;
00866 
00867     PROCNAME("pixSeedfillMorph");
00868 
00869     if (!pixs)
00870         return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL);
00871     if (!pixm)
00872         return (PIX *)ERROR_PTR("mask pix not defined", procName, NULL);
00873     if (connectivity != 4 && connectivity != 8)
00874         return (PIX *)ERROR_PTR("connectivity not in {4,8}", procName, NULL);
00875 
00876     if (pixSizesEqual(pixs, pixm) == 0)
00877         return (PIX *)ERROR_PTR("pix sizes unequal", procName, NULL);
00878     if (pixGetDepth(pixs) != 1)
00879         return (PIX *)ERROR_PTR("pix not binary", procName, NULL);
00880 
00881     if ((sel_3 = selCreateBrick(3, 3, 1, 1, 1)) == NULL)
00882         return (PIX *)ERROR_PTR("sel_3 not made", procName, NULL);
00883     if (connectivity == 4) {  /* remove corner hits to make a '+' */
00884         selSetElement(sel_3, 0, 0, SEL_DONT_CARE);
00885         selSetElement(sel_3, 2, 2, SEL_DONT_CARE);
00886         selSetElement(sel_3, 2, 0, SEL_DONT_CARE);
00887         selSetElement(sel_3, 0, 2, SEL_DONT_CARE);
00888     }
00889 
00890     if ((pixt1 = pixCopy(NULL, pixs)) == NULL)
00891         return (PIX *)ERROR_PTR("pixt1 not made", procName, NULL);
00892     if ((pixd = pixCreateTemplate(pixs)) == NULL)
00893         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
00894 
00895     iter = 0;
00896     while (1) {
00897         iter++;
00898         pixDilate(pixd, pixt1, sel_3);
00899         pixAnd(pixd, pixd, pixm);
00900         pixEqual(pixd, pixt1, &same);
00901         if (same)
00902             break;
00903         else
00904             SWAP(pixt1, pixd);
00905     }
00906     fprintf(stderr, " Num iters in binary reconstruction = %d\n", iter);
00907 
00908     pixDestroy(&pixt1);
00909     selDestroy(&sel_3);
00910 
00911     return pixd;
00912 }
00913 
00914 
00915 
00916 /*-----------------------------------------------------------------*
00917  *                   Granulometry on binary images                 *
00918  *-----------------------------------------------------------------*/
00919 /*!
00920  *  pixRunHistogramMorph()
00921  *
00922  *      Input:  pixs
00923  *              runtype (L_RUN_OFF, L_RUN_ON)
00924  *              direction (L_HORIZ, L_VERT)
00925  *              maxsize  (size of largest runlength counted)
00926  *      Return: numa of run-lengths
00927  */
00928 NUMA *
00929 pixRunHistogramMorph(PIX     *pixs,
00930                      l_int32  runtype,
00931                      l_int32  direction,
00932                      l_int32  maxsize)
00933 {
00934 l_int32    count, i;
00935 l_float32  val;
00936 NUMA      *na, *nah;
00937 PIX       *pixt1, *pixt2, *pixt3;
00938 SEL       *sel_2a;
00939 
00940     PROCNAME("pixRunHistogramMorph");
00941 
00942     if (!pixs)
00943         return (NUMA *)ERROR_PTR("seed pix not defined", procName, NULL);
00944     if (runtype != L_RUN_OFF && runtype != L_RUN_ON)
00945         return (NUMA *)ERROR_PTR("invalid run type", procName, NULL);
00946     if (direction != L_HORIZ && direction != L_VERT)
00947         return (NUMA *)ERROR_PTR("direction not in {L_HORIZ, L_VERT}",
00948                                  procName, NULL);
00949 
00950     if (pixGetDepth(pixs) != 1)
00951         return (NUMA *)ERROR_PTR("pixs must be binary", procName, NULL);
00952 
00953     if ((na = numaCreate(0)) == NULL)
00954         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
00955 
00956     if (direction == L_HORIZ)
00957         sel_2a = selCreateBrick(1, 2, 0, 0, 1);
00958     else   /* direction == L_VERT */
00959         sel_2a = selCreateBrick(2, 1, 0, 0, 1);
00960     if (!sel_2a)
00961         return (NUMA *)ERROR_PTR("sel_2a not made", procName, NULL);
00962 
00963     if (runtype == L_RUN_OFF) {
00964         if ((pixt1 = pixCopy(NULL, pixs)) == NULL)
00965             return (NUMA *)ERROR_PTR("pix1 not made", procName, NULL);
00966         pixInvert(pixt1, pixt1);
00967     }
00968     else  /* runtype == L_RUN_ON */
00969         pixt1 = pixClone(pixs);
00970 
00971     if ((pixt2 = pixCreateTemplate(pixs)) == NULL)
00972         return (NUMA *)ERROR_PTR("pix2 not made", procName, NULL);
00973     if ((pixt3 = pixCreateTemplate(pixs)) == NULL)
00974         return (NUMA *)ERROR_PTR("pix3 not made", procName, NULL);
00975 
00976         /* Get pixel counts at different stages of erosion */
00977     pixCountPixels(pixt1, &count, NULL);
00978     numaAddNumber(na, count);
00979     pixErode(pixt2, pixt1, sel_2a);
00980     pixCountPixels(pixt2, &count, NULL);
00981     numaAddNumber(na, count);
00982     for (i = 0; i < maxsize / 2; i++) {
00983         pixErode(pixt3, pixt2, sel_2a);
00984         pixCountPixels(pixt3, &count, NULL);
00985         numaAddNumber(na, count);
00986         pixErode(pixt2, pixt3, sel_2a);
00987         pixCountPixels(pixt2, &count, NULL);
00988         numaAddNumber(na, count);
00989     }
00990 
00991         /* Compute length histogram */
00992     if ((nah = numaCreate(na->n)) == NULL)
00993         return (NUMA *)ERROR_PTR("nah not made", procName, NULL);
00994     numaAddNumber(nah, 0); /* number at length 0 */
00995     for (i = 1; i < na->n - 1; i++) {
00996         val = na->array[i+1] - 2 * na->array[i] + na->array[i-1];
00997         numaAddNumber(nah, val);
00998     }
00999 
01000     pixDestroy(&pixt1);
01001     pixDestroy(&pixt2);
01002     pixDestroy(&pixt3);
01003     selDestroy(&sel_2a);
01004     numaDestroy(&na);
01005 
01006     return nah;
01007 }
01008 
01009 
01010 /*-----------------------------------------------------------------*
01011  *            Composite operations on grayscale images             *
01012  *-----------------------------------------------------------------*/
01013 /*!
01014  *  pixTophat()
01015  *
01016  *      Input:  pixs
01017  *              hsize (of Sel; must be odd; origin implicitly in center)
01018  *              vsize (ditto)
01019  *              type   (L_TOPHAT_WHITE: image - opening
01020  *                      L_TOPHAT_BLACK: closing - image)
01021  *      Return: pixd, or null on error
01022  *
01023  *  Notes:
01024  *      (1) Sel is a brick with all elements being hits
01025  *      (2) If hsize = vsize = 1, returns an image with all 0 data.
01026  *      (3) The L_TOPHAT_WHITE flag emphasizes small bright regions,
01027  *          whereas the L_TOPHAT_BLACK flag emphasizes small dark regions.
01028  *          The L_TOPHAT_WHITE tophat can be accomplished by doing a
01029  *          L_TOPHAT_BLACK tophat on the inverse, or v.v.
01030  */
01031 PIX *
01032 pixTophat(PIX     *pixs,
01033           l_int32  hsize,
01034           l_int32  vsize,
01035           l_int32  type)
01036 {
01037 PIX  *pixt, *pixd;
01038 
01039     PROCNAME("pixTophat");
01040 
01041     if (!pixs)
01042         return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL);
01043     if (pixGetDepth(pixs) != 8)
01044         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
01045     if (hsize < 1 || vsize < 1)
01046         return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL);
01047     if ((hsize & 1) == 0 ) {
01048         L_WARNING("horiz sel size must be odd; increasing by 1", procName);
01049         hsize++;
01050     }
01051     if ((vsize & 1) == 0 ) {
01052         L_WARNING("vert sel size must be odd; increasing by 1", procName);
01053         vsize++;
01054     }
01055     if (type != L_TOPHAT_WHITE && type != L_TOPHAT_BLACK)
01056         return (PIX *)ERROR_PTR("type must be L_TOPHAT_BLACK or L_TOPHAT_WHITE",
01057                                 procName, NULL);
01058 
01059     if (hsize == 1 && vsize == 1)
01060         return pixCreateTemplate(pixs);
01061 
01062     switch (type)
01063     {
01064     case L_TOPHAT_WHITE:
01065         if ((pixt = pixOpenGray(pixs, hsize, vsize)) == NULL)
01066             return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
01067         pixd = pixSubtractGray(NULL, pixs, pixt);
01068         pixDestroy(&pixt);
01069         break;
01070     case L_TOPHAT_BLACK:
01071         if ((pixd = pixCloseGray(pixs, hsize, vsize)) == NULL)
01072             return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
01073         pixSubtractGray(pixd, pixd, pixs);
01074         break;
01075     default:
01076         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
01077     }
01078 
01079     return pixd;
01080 }
01081         
01082 
01083 /*!
01084  *  pixHDome()
01085  *
01086  *      Input:  pixs (8 bpp, filling mask)
01087  *              height (of seed below the filling maskhdome; must be >= 0)
01088  *              connectivity (4 or 8)
01089  *      Return: pixd (8 bpp), or null on error
01090  *
01091  *  Notes:
01092  *      (1) It is more efficient to use a connectivity of 4 for the fill.
01093  *      (2) This fills bumps to some level, and extracts the unfilled
01094  *          part of the bump.  To extract the troughs of basins, first
01095  *          invert pixs and then apply pixHDome().
01096  *      (3) It is useful to compare the HDome operation with the TopHat.
01097  *          The latter extracts peaks or valleys that have a width
01098  *          not exceeding the size of the structuring element used
01099  *          in the opening or closing, rsp.  The height of the peak is
01100  *          irrelevant.  By contrast, for the HDome, the gray seedfill
01101  *          is used to extract all peaks that have a height not exceeding
01102  *          a given value, regardless of their width!
01103  *      (4) Slightly more precisely, suppose you set 'height' = 40.
01104  *          Then all bumps in pixs with a height greater than or equal
01105  *          to 40 become, in pixd, bumps with a max value of exactly 40.
01106  *          All shorter bumps have a max value in pixd equal to the height
01107  *          of the bump.
01108  *      (5) The method: the filling mask, pixs, is the image whose peaks
01109  *          are to be extracted.  The height of a peak is the distance
01110  *          between the top of the peak and the highest "leak" to the
01111  *          outside -- think of a sombrero, where the leak occurs
01112  *          at the highest point on the rim.
01113  *            (a) Generate a seed, pixd, by subtracting some value, p, from
01114  *                each pixel in the filling mask, pixs.  The value p is
01115  *                the 'height' input to this function.
01116  *            (b) Fill in pixd starting with this seed, clipping by pixs,
01117  *                in the way described in seedfillGrayLow().  The filling
01118  *                stops before the peaks in pixs are filled.
01119  *                For peaks that have a height > p, pixd is filled to
01120  *                the level equal to the (top-of-the-peak - p).
01121  *                For peaks of height < p, the peak is left unfilled
01122  *                from its highest saddle point (the leak to the outside).
01123  *            (c) Subtract the filled seed (pixd) from the filling mask (pixs).
01124  *          Note that in this procedure, everything is done starting
01125  *          with the filling mask, pixs.
01126  *      (6) For segmentation, the resulting image, pixd, can be thresholded
01127  *          and used as a seed for another filling operation.
01128  */
01129 PIX *
01130 pixHDome(PIX     *pixs,
01131          l_int32  height,
01132          l_int32  connectivity)
01133 {
01134 PIX  *pixsd, *pixd;
01135 
01136     PROCNAME("pixHDome");
01137 
01138     if (!pixs)
01139         return (PIX *)ERROR_PTR("src pix not defined", procName, NULL);
01140     if (pixGetDepth(pixs) != 8)
01141         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
01142     if (height < 0)
01143         return (PIX *)ERROR_PTR("height not >= 0", procName, NULL);
01144     if (height == 0)
01145         return pixCreateTemplate(pixs);
01146 
01147     if ((pixsd = pixCopy(NULL, pixs)) == NULL)
01148         return (PIX *)ERROR_PTR("pixsd not made", procName, NULL);
01149     pixAddConstantGray(pixsd, -height);
01150     pixSeedfillGray(pixsd, pixs, connectivity);
01151     pixd = pixSubtractGray(NULL, pixs, pixsd);
01152 
01153     pixDestroy(&pixsd);
01154     return pixd;
01155 }
01156 
01157 
01158 /*!
01159  *  pixFastTophat()
01160  *
01161  *      Input:  pixs
01162  *              xsize (width of max/min op, smoothing; any integer >= 1)
01163  *              ysize (height of max/min op, smoothing; any integer >= 1)
01164  *              type   (L_TOPHAT_WHITE: image - min
01165  *                      L_TOPHAT_BLACK: max - image)
01166  *      Return: pixd, or null on error
01167  *
01168  *  Notes:
01169  *      (1) Don't be fooled. This is NOT a tophat.  It is a tophat-like
01170  *          operation, where the result is similar to what you'd get
01171  *          if you used an erosion instead of an opening, or a dilation
01172  *          instead of a closing.
01173  *      (2) Instead of opening or closing at full resolution, it does
01174  *          a fast downscale/minmax operation, then a quick small smoothing
01175  *          at low res, a replicative expansion of the "background"
01176  *          to full res, and finally a removal of the background level
01177  *          from the input image.  The smoothing step may not be important.
01178  *      (3) It does not remove noise as well as a tophat, but it is
01179  *          5 to 10 times faster.
01180  *          If you need the preciseness of the tophat, don't use this.
01181  *      (4) The L_TOPHAT_WHITE flag emphasizes small bright regions,
01182  *          whereas the L_TOPHAT_BLACK flag emphasizes small dark regions.
01183  */
01184 PIX *
01185 pixFastTophat(PIX     *pixs,
01186               l_int32  xsize,
01187               l_int32  ysize,
01188               l_int32  type)
01189 {
01190 PIX  *pixt1, *pixt2, *pixt3, *pixd;
01191 
01192     PROCNAME("pixFastTophat");
01193 
01194     if (!pixs)
01195         return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL);
01196     if (pixGetDepth(pixs) != 8)
01197         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
01198     if (xsize < 1 || ysize < 1)
01199         return (PIX *)ERROR_PTR("size < 1", procName, NULL);
01200     if (type != L_TOPHAT_WHITE && type != L_TOPHAT_BLACK)
01201         return (PIX *)ERROR_PTR("type must be L_TOPHAT_BLACK or L_TOPHAT_WHITE",
01202                                 procName, NULL);
01203 
01204     if (xsize == 1 && ysize == 1)
01205         return pixCreateTemplate(pixs);
01206 
01207     switch (type)
01208     {
01209     case L_TOPHAT_WHITE:
01210         if ((pixt1 = pixScaleGrayMinMax(pixs, xsize, ysize, L_CHOOSE_MIN))
01211                == NULL)
01212             return (PIX *)ERROR_PTR("pixt1 not made", procName, NULL);
01213         pixt2 = pixBlockconv(pixt1, 1, 1);  /* small smoothing */
01214         pixt3 = pixScaleBySampling(pixt2, xsize, ysize);
01215         pixd = pixSubtractGray(NULL, pixs, pixt3);
01216         pixDestroy(&pixt3);
01217         break;
01218     case L_TOPHAT_BLACK:
01219         if ((pixt1 = pixScaleGrayMinMax(pixs, xsize, ysize, L_CHOOSE_MAX))
01220                == NULL)
01221             return (PIX *)ERROR_PTR("pixt1 not made", procName, NULL);
01222         pixt2 = pixBlockconv(pixt1, 1, 1);  /* small smoothing */
01223         pixd = pixScaleBySampling(pixt2, xsize, ysize);
01224         pixSubtractGray(pixd, pixd, pixs);
01225         break;
01226     default:
01227         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
01228     }
01229 
01230     pixDestroy(&pixt1);
01231     pixDestroy(&pixt2);
01232     return pixd;
01233 }
01234         
01235 
01236 /*!
01237  *  pixMorphGradient()
01238  *
01239  *      Input:  pixs
01240  *              hsize (of Sel; must be odd; origin implicitly in center)
01241  *              vsize (ditto)
01242  *              smoothing  (half-width of convolution smoothing filter.
01243  *                          The width is (2 * smoothing + 1), so 0 is no-op.
01244  *      Return: pixd, or null on error
01245  */
01246 PIX *
01247 pixMorphGradient(PIX     *pixs,
01248                  l_int32  hsize,
01249                  l_int32  vsize,
01250                  l_int32  smoothing)
01251 {
01252 PIX  *pixg, *pixd;
01253 
01254     PROCNAME("pixMorphGradient");
01255 
01256     if (!pixs)
01257         return (PIX *)ERROR_PTR("seed pix not defined", procName, NULL);
01258     if (pixGetDepth(pixs) != 8)
01259         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
01260     if (hsize < 1 || vsize < 1)
01261         return (PIX *)ERROR_PTR("hsize or vsize < 1", procName, NULL);
01262     if ((hsize & 1) == 0 ) {
01263         L_WARNING("horiz sel size must be odd; increasing by 1", procName);
01264         hsize++;
01265     }
01266     if ((vsize & 1) == 0 ) {
01267         L_WARNING("vert sel size must be odd; increasing by 1", procName);
01268         vsize++;
01269     }
01270 
01271         /* Optionally smooth first to remove noise.
01272          * If smoothing is 0, just get a copy */
01273     pixg = pixBlockconvGray(pixs, NULL, smoothing, smoothing);
01274 
01275         /* This gives approximately the gradient of a transition */
01276     pixd = pixDilateGray(pixg, hsize, vsize);
01277     pixSubtractGray(pixd, pixd, pixg);
01278     pixDestroy(&pixg);
01279     return pixd;
01280 }
01281 
01282 
01283 /*-----------------------------------------------------------------*
01284  *                       Centroid of component                     *
01285  *-----------------------------------------------------------------*/
01286 /*!
01287  *  pixaCentroids()
01288  *
01289  *      Input:  pixa of components (1 or 8 bpp)
01290  *      Return: pta of centroids relative to the UL corner of
01291  *              each pix, or null on error
01292  *
01293  *  Notes:
01294  *      (1) An error message is returned if any pix has something other
01295  *          than 1 bpp or 8 bpp depth, and the centroid from that pix
01296  *          is saved as (0, 0).
01297  */
01298 PTA *
01299 pixaCentroids(PIXA  *pixa)
01300 {
01301 l_int32    i, n;
01302 l_int32   *centtab = NULL;
01303 l_int32   *sumtab = NULL;
01304 l_float32  x, y;
01305 PIX       *pix;
01306 PTA       *pta;
01307 
01308     PROCNAME("pixaCentroids");
01309 
01310     if (!pixa)
01311         return (PTA *)ERROR_PTR("pixa not defined", procName, NULL);
01312     if ((n = pixaGetCount(pixa)) == 0)
01313         return (PTA *)ERROR_PTR("no pix in pixa", procName, NULL);
01314 
01315     if ((pta = ptaCreate(n)) == NULL)
01316         return (PTA *)ERROR_PTR("pta not defined", procName, NULL);
01317     centtab = makePixelCentroidTab8();
01318     sumtab = makePixelSumTab8();
01319 
01320     for (i = 0; i < n; i++) {
01321         pix = pixaGetPix(pixa, i, L_CLONE);
01322         if (pixCentroid(pix, centtab, sumtab, &x, &y) == 1)
01323             L_ERROR_INT("centroid failure for pix %d", procName, i);
01324         pixDestroy(&pix);
01325         ptaAddPt(pta, x, y);
01326     }
01327 
01328     FREE(centtab);
01329     FREE(sumtab);
01330     return pta;
01331 }
01332 
01333 
01334 /*!
01335  *  pixCentroid()
01336  *
01337  *      Input:  pix (1 or 8 bpp)
01338  *              centtab (<optional> table for finding centroids; can be null)
01339  *              sumtab (<optional> table for finding pixel sums; can be null)
01340  *              &xave, &yave (<return> coordinates of centroid, relative to
01341  *                            the UL corner of the pix)
01342  *      Return: 0 if OK, 1 on error
01343  *
01344  *  Notes:
01345  *      (1) Any table not passed in will be made internally and destroyed
01346  *          after use.
01347  */
01348 l_int32
01349 pixCentroid(PIX        *pix,
01350             l_int32    *centtab,
01351             l_int32    *sumtab,
01352             l_float32  *pxave,
01353             l_float32  *pyave)
01354 {
01355 l_int32    w, h, d, i, j, wpl, pixsum, rowsum, val;
01356 l_float32  xsum, ysum;
01357 l_uint32  *data, *line;
01358 l_uint32   word;
01359 l_uint8    byte;
01360 l_int32   *ctab, *stab;
01361 
01362     PROCNAME("pixCentroid");
01363 
01364     if (!pxave || !pyave)
01365         return ERROR_INT("&pxave and &pyave not defined", procName, 1);
01366     *pxave = *pyave = 0.0;
01367     if (!pix)
01368         return ERROR_INT("pix not defined", procName, 1);
01369     pixGetDimensions(pix, &w, &h, &d);
01370     if (d != 1 && d != 8)
01371         return ERROR_INT("pix not 1 or 8 bpp", procName, 1);
01372 
01373     if (!centtab)
01374         ctab = makePixelCentroidTab8();
01375     else
01376         ctab = centtab;
01377     if (!sumtab)
01378         stab = makePixelSumTab8();
01379     else
01380         stab = sumtab;
01381 
01382     data = pixGetData(pix);
01383     wpl = pixGetWpl(pix);
01384     xsum = ysum = 0.0;
01385     pixsum = 0;
01386     if (d == 1) {
01387         for (i = 0; i < h; i++) {
01388                 /* The body of this loop computes the sum of the set
01389                  * (1) bits on this row, weighted by their distance
01390                  * from the left edge of pix, and accumulates that into
01391                  * xsum; it accumulates their distance from the top
01392                  * edge of pix into ysum, and their total count into
01393                  * pixsum.  It's equivalent to
01394                  * for (j = 0; j < w; j++) {
01395                  *     if (GET_DATA_BIT(line, j)) {
01396                  *         xsum += j;
01397                  *         ysum += i;
01398                  *         pixsum++;
01399                  *     }
01400                  * }
01401                  */
01402             line = data + wpl * i;
01403             rowsum = 0;
01404             for (j = 0; j < wpl; j++) {
01405                 word = line[j];
01406                 if (word) {
01407                     byte = word & 0xff;
01408                     rowsum += stab[byte];
01409                     xsum += ctab[byte] + (j * 32 + 24) * stab[byte];
01410                     byte = (word >> 8) & 0xff;
01411                     rowsum += stab[byte];
01412                     xsum += ctab[byte] + (j * 32 + 16) * stab[byte];
01413                     byte = (word >> 16) & 0xff;
01414                     rowsum += stab[byte];
01415                     xsum += ctab[byte] + (j * 32 + 8) * stab[byte];
01416                     byte = (word >> 24) & 0xff;
01417                     rowsum += stab[byte];
01418                     xsum += ctab[byte] + j * 32 * stab[byte];
01419                 }
01420             }
01421             pixsum += rowsum;
01422             ysum += rowsum * i;
01423         }
01424         if (pixsum == 0)
01425             L_WARNING("no ON pixels in pix", procName);
01426         else {
01427             *pxave = xsum / (l_float32)pixsum;
01428             *pyave = ysum / (l_float32)pixsum;
01429         }
01430     }
01431     else {  /* d == 8 */
01432         for (i = 0; i < h; i++) {
01433             line = data + wpl * i;
01434             for (j = 0; j < w; j++) {
01435                 val = GET_DATA_BYTE(line, j);
01436                 xsum += val * j;
01437                 ysum += val * i;
01438                 pixsum += val;
01439             }
01440         }
01441         if (pixsum == 0)
01442             L_WARNING("all pixels are 0", procName);
01443         else {
01444             *pxave = xsum / (l_float32)pixsum;
01445             *pyave = ysum / (l_float32)pixsum;
01446         }
01447     }
01448 
01449     if (!centtab) FREE(ctab);
01450     if (!sumtab) FREE(stab);
01451     return 0;
01452 }
01453 
01454 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines