Leptonica 1.68
C Image Processing Library

selgen.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  *  selgen.c
00018  *
00019  *      This file contains functions that generate hit-miss Sels
00020  *      for doing a loose match to a small bitmap.  The hit-miss
00021  *      Sel is made from a given bitmap.  Several "knobs"
00022  *      are available to control the looseness of the match.
00023  *      In general, a tight match will have fewer false positives
00024  *      (bad matches) but more false negatives (missed patterns).
00025  *      The values to be used depend on the quality and variation
00026  *      of the image in which the pattern is to be searched,
00027  *      and the relative penalties of false positives and
00028  *      false negatives.  Default values for the three knobs --
00029  *      minimum distance to boundary pixels, number of extra pixels
00030  *      added to selected sides, and minimum acceptable runlength
00031  *      in eroded version -- are provided.
00032  *
00033  *      The generated hit-miss Sels can always be used in the
00034  *      rasterop implementation of binary morphology (in morph.h).
00035  *      If they are small enough (not more than 31 pixels extending
00036  *      in any direction from the Sel origin), they can also be used
00037  *      to auto-generate dwa code (fmorphauto.c).
00038  *
00039  *
00040  *      Generate a subsampled structuring element
00041  *            SEL     *pixGenerateSelWithRuns()
00042  *            SEL     *pixGenerateSelRandom()
00043  *            SEL     *pixGenerateSelBoundary()
00044  *      
00045  *      Accumulate data on runs along lines
00046  *            NUMA    *pixGetRunCentersOnLine()
00047  *            NUMA    *pixGetRunsOnLine()
00048  *
00049  *      Subsample boundary pixels in relatively ordered way
00050  *            PTA     *pixSubsampleBoundaryPixels()
00051  *            PTA     *adjacentOnPixelInRaster()
00052  *
00053  *      Display generated sel with originating image
00054  *            PIX     *pixDisplayHitMissSel()
00055  */
00056 
00057 #include <stdio.h>
00058 #include <stdlib.h>
00059 #include "allheaders.h"
00060 
00061 
00062     /* default minimum distance of a hit-miss pixel element to
00063      * a boundary pixel of its color. */
00064 static const l_int32  DEFAULT_DISTANCE_TO_BOUNDARY = 1;
00065 static const l_int32  MAX_DISTANCE_TO_BOUNDARY = 4;
00066 
00067     /* default min runlength to accept a hit or miss element located
00068      * at its center */
00069 static const l_int32  DEFAULT_MIN_RUNLENGTH = 3;
00070 
00071 
00072     /* default scalefactor for displaying image and hit-miss sel
00073      * that is derived from it */
00074 static const l_int32  DEFAULT_SEL_SCALEFACTOR = 7;
00075 static const l_int32  MAX_SEL_SCALEFACTOR = 31;  /* should be big enough */
00076 
00077 #ifndef  NO_CONSOLE_IO
00078 #define  DEBUG_DISPLAY_HM_SEL   0
00079 #endif  /* ~NO_CONSOLE_IO */
00080 
00081 
00082 /*-----------------------------------------------------------------*
00083  *           Generate a subsampled structuring element             *
00084  *-----------------------------------------------------------------*/
00085 /*!
00086  *  pixGenerateSelWithRuns()
00087  *
00088  *      Input:  pix (1 bpp, typically small, to be used as a pattern)
00089  *              nhlines (number of hor lines along which elements are found)
00090  *              nvlines (number of vert lines along which elements are found)
00091  *              distance (min distance from boundary pixel; use 0 for default)
00092  *              minlength (min runlength to set hit or miss; use 0 for default)
00093  *              toppix (number of extra pixels of bg added above)
00094  *              botpix (number of extra pixels of bg added below)
00095  *              leftpix (number of extra pixels of bg added to left)
00096  *              rightpix (number of extra pixels of bg added to right)
00097  *              &pixe (<optional return> input pix expanded by extra pixels)
00098  *      Return: sel (hit-miss for input pattern), or null on error
00099  *
00100  *  Notes:
00101  *    (1) The horizontal and vertical lines along which elements are
00102  *        selected are roughly equally spaced.  The actual locations of
00103  *        the hits and misses are the centers of respective run-lengths.
00104  *    (2) No elements are selected that are less than 'distance' pixels away
00105  *        from a boundary pixel of the same color.  This makes the
00106  *        match much more robust to edge noise.  Valid inputs of
00107  *        'distance' are 0, 1, 2, 3 and 4.  If distance is either 0 or
00108  *        greater than 4, we reset it to the default value.
00109  *    (3) The 4 numbers for adding rectangles of pixels outside the fg
00110  *        can be use if the pattern is expected to be surrounded by bg
00111  *        (white) pixels.  On the other hand, if the pattern may be near
00112  *        other fg (black) components on some sides, use 0 for those sides.
00113  *    (4) The pixels added to a side allow you to have miss elements there.
00114  *        There is a constraint between distance, minlength, and
00115  *        the added pixels for this to work.  We illustrate using the
00116  *        default values.  If you add 5 pixels to the top, and use a 
00117  *        distance of 1, then you end up with a vertical run of at least
00118  *        4 bg pixels along the top edge of the image.  If you use a
00119  *        minimum runlength of 3, each vertical line will always find
00120  *        a miss near the center of its run.  However, if you use a
00121  *        minimum runlength of 5, you will not get a miss on every vertical
00122  *        line.  As another example, if you have 7 added pixels and a
00123  *        distance of 2, you can use a runlength up to 5 to guarantee
00124  *        that the miss element is recorded.  We give a warning if the
00125  *        contraint does not guarantee a miss element outside the 
00126  *        image proper.
00127  *    (5) The input pix, as extended by the extra pixels on selected sides,
00128  *        can optionally be returned.  For debugging, call
00129  *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
00130  *        on the generating bitmap.
00131  */
00132 SEL *
00133 pixGenerateSelWithRuns(PIX     *pixs,
00134                        l_int32  nhlines,
00135                        l_int32  nvlines,
00136                        l_int32  distance,
00137                        l_int32  minlength,
00138                        l_int32  toppix,
00139                        l_int32  botpix,
00140                        l_int32  leftpix,
00141                        l_int32  rightpix,
00142                        PIX    **ppixe)
00143 {
00144 l_int32    ws, hs, w, h, x, y, xval, yval, i, j, nh, nm;
00145 l_float32  delh, delw;
00146 NUMA      *nah, *nam;
00147 PIX       *pixt1, *pixt2, *pixfg, *pixbg;
00148 PTA       *ptah, *ptam;
00149 SEL       *seld, *sel;
00150 
00151     PROCNAME("pixGenerateSelWithRuns");
00152 
00153     if (ppixe) *ppixe = NULL;
00154     if (!pixs)
00155         return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
00156     if (pixGetDepth(pixs) != 1)
00157         return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00158     if (nhlines < 1 && nvlines < 1)
00159         return (SEL *)ERROR_PTR("nvlines and nhlines both < 1", procName, NULL);
00160 
00161     if (distance <= 0)
00162         distance = DEFAULT_DISTANCE_TO_BOUNDARY;
00163     if (minlength <= 0)
00164         minlength = DEFAULT_MIN_RUNLENGTH;
00165     if (distance > MAX_DISTANCE_TO_BOUNDARY) {
00166         L_WARNING("distance too large; setting to max value", procName);
00167         distance = MAX_DISTANCE_TO_BOUNDARY;
00168     }
00169 
00170         /* Locate the foreground */
00171     pixClipToForeground(pixs, &pixt1, NULL);
00172     if (!pixt1)
00173         return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
00174     ws = pixGetWidth(pixt1);
00175     hs = pixGetHeight(pixt1);
00176     w = ws;
00177     h = hs;
00178 
00179         /* Crop out a region including the foreground, and add pixels
00180          * on sides depending on the side flags */
00181     if (toppix || botpix || leftpix || rightpix) {
00182         x = y = 0;
00183         if (toppix) {
00184             h += toppix;
00185             y = toppix;
00186             if (toppix < distance + minlength)
00187                 L_WARNING("no miss elements in added top pixels", procName);
00188         }
00189         if (botpix) {
00190             h += botpix;
00191             if (botpix < distance + minlength)
00192                 L_WARNING("no miss elements in added bot pixels", procName);
00193         }
00194         if (leftpix) {
00195             w += leftpix;
00196             x = leftpix;
00197             if (leftpix < distance + minlength)
00198                 L_WARNING("no miss elements in added left pixels", procName);
00199         }
00200         if (rightpix) {
00201             w += rightpix;
00202             if (rightpix < distance + minlength)
00203                 L_WARNING("no miss elements in added right pixels", procName);
00204         }
00205         pixt2 = pixCreate(w, h, 1);
00206         pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
00207     }
00208     else
00209         pixt2 = pixClone(pixt1);
00210     if (ppixe)
00211         *ppixe = pixClone(pixt2);
00212     pixDestroy(&pixt1);
00213 
00214         /* Identify fg and bg pixels that are at least 'distance' pixels
00215          * away from the boundary pixels in their set */
00216     seld = selCreateBrick(2 * distance + 1, 2 * distance + 1,
00217                           distance, distance, SEL_HIT);
00218     pixfg = pixErode(NULL, pixt2, seld);
00219     pixbg = pixDilate(NULL, pixt2, seld);
00220     pixInvert(pixbg, pixbg);
00221     selDestroy(&seld);
00222     pixDestroy(&pixt2);
00223 
00224         /* Accumulate hit and miss points */
00225     ptah = ptaCreate(0);
00226     ptam = ptaCreate(0);
00227     if (nhlines >= 1) {
00228         delh = (l_float32)h / (l_float32)(nhlines + 1);
00229         for (i = 0, y = 0; i < nhlines; i++) {
00230             y += (l_int32)(delh + 0.5); 
00231             nah = pixGetRunCentersOnLine(pixfg, -1, y, minlength);
00232             nam = pixGetRunCentersOnLine(pixbg, -1, y, minlength);
00233             nh = numaGetCount(nah);
00234             nm = numaGetCount(nam);
00235             for (j = 0; j < nh; j++) {
00236                 numaGetIValue(nah, j, &xval);
00237                 ptaAddPt(ptah, xval, y);
00238             }
00239             for (j = 0; j < nm; j++) {
00240                 numaGetIValue(nam, j, &xval);
00241                 ptaAddPt(ptam, xval, y);
00242             }
00243             numaDestroy(&nah);
00244             numaDestroy(&nam);
00245         }
00246     }
00247     if (nvlines >= 1) {
00248         delw = (l_float32)w / (l_float32)(nvlines + 1);
00249         for (i = 0, x = 0; i < nvlines; i++) {
00250             x += (l_int32)(delw + 0.5); 
00251             nah = pixGetRunCentersOnLine(pixfg, x, -1, minlength);
00252             nam = pixGetRunCentersOnLine(pixbg, x, -1, minlength);
00253             nh = numaGetCount(nah);
00254             nm = numaGetCount(nam);
00255             for (j = 0; j < nh; j++) {
00256                 numaGetIValue(nah, j, &yval);
00257                 ptaAddPt(ptah, x, yval);
00258             }
00259             for (j = 0; j < nm; j++) {
00260                 numaGetIValue(nam, j, &yval);
00261                 ptaAddPt(ptam, x, yval);
00262             }
00263             numaDestroy(&nah);
00264             numaDestroy(&nam);
00265         }
00266     }
00267 
00268         /* Make the Sel with those points */
00269     sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
00270     nh = ptaGetCount(ptah);
00271     for (i = 0; i < nh; i++) {
00272         ptaGetIPt(ptah, i, &x, &y);
00273         selSetElement(sel, y, x, SEL_HIT);
00274     }
00275     nm = ptaGetCount(ptam);
00276     for (i = 0; i < nm; i++) {
00277         ptaGetIPt(ptam, i, &x, &y);
00278         selSetElement(sel, y, x, SEL_MISS);
00279     }
00280 
00281     pixDestroy(&pixfg);
00282     pixDestroy(&pixbg);
00283     ptaDestroy(&ptah);
00284     ptaDestroy(&ptam);
00285     return sel;
00286 }
00287 
00288 
00289 /*!
00290  *  pixGenerateSelRandom()
00291  *
00292  *      Input:  pix (1 bpp, typically small, to be used as a pattern)
00293  *              hitfract (fraction of allowable fg pixels that are hits)
00294  *              missfract (fraction of allowable bg pixels that are misses)
00295  *              distance (min distance from boundary pixel; use 0 for default)
00296  *              toppix (number of extra pixels of bg added above)
00297  *              botpix (number of extra pixels of bg added below)
00298  *              leftpix (number of extra pixels of bg added to left)
00299  *              rightpix (number of extra pixels of bg added to right)
00300  *              &pixe (<optional return> input pix expanded by extra pixels)
00301  *      Return: sel (hit-miss for input pattern), or null on error
00302  *
00303  *  Notes:
00304  *    (1) Either of hitfract and missfract can be zero.  If both are zero,
00305  *        the sel would be empty, and NULL is returned.
00306  *    (2) No elements are selected that are less than 'distance' pixels away
00307  *        from a boundary pixel of the same color.  This makes the
00308  *        match much more robust to edge noise.  Valid inputs of
00309  *        'distance' are 0, 1, 2, 3 and 4.  If distance is either 0 or
00310  *        greater than 4, we reset it to the default value.
00311  *    (3) The 4 numbers for adding rectangles of pixels outside the fg
00312  *        can be use if the pattern is expected to be surrounded by bg
00313  *        (white) pixels.  On the other hand, if the pattern may be near
00314  *        other fg (black) components on some sides, use 0 for those sides.
00315  *    (4) The input pix, as extended by the extra pixels on selected sides,
00316  *        can optionally be returned.  For debugging, call
00317  *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
00318  *        on the generating bitmap.
00319  */
00320 SEL *
00321 pixGenerateSelRandom(PIX       *pixs,
00322                      l_float32  hitfract,
00323                      l_float32  missfract,
00324                      l_int32    distance,
00325                      l_int32    toppix,
00326                      l_int32    botpix,
00327                      l_int32    leftpix,
00328                      l_int32    rightpix,
00329                      PIX      **ppixe)
00330 {
00331 l_int32    ws, hs, w, h, x, y, i, j, thresh;
00332 l_uint32   val;
00333 PIX       *pixt1, *pixt2, *pixfg, *pixbg;
00334 SEL       *seld, *sel;
00335 
00336     PROCNAME("pixGenerateSelRandom");
00337 
00338     if (ppixe) *ppixe = NULL;
00339     if (!pixs)
00340         return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
00341     if (pixGetDepth(pixs) != 1)
00342         return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00343     if (hitfract <= 0.0 && missfract <= 0.0)
00344         return (SEL *)ERROR_PTR("no hits or misses", procName, NULL);
00345     if (hitfract > 1.0 || missfract > 1.0) 
00346         return (SEL *)ERROR_PTR("fraction can't be > 1.0", procName, NULL);
00347 
00348     if (distance <= 0)
00349         distance = DEFAULT_DISTANCE_TO_BOUNDARY;
00350     if (distance > MAX_DISTANCE_TO_BOUNDARY) {
00351         L_WARNING("distance too large; setting to max value", procName);
00352         distance = MAX_DISTANCE_TO_BOUNDARY;
00353     }
00354 
00355         /* Locate the foreground */
00356     pixClipToForeground(pixs, &pixt1, NULL);
00357     if (!pixt1)
00358         return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
00359     ws = pixGetWidth(pixt1);
00360     hs = pixGetHeight(pixt1);
00361     w = ws;
00362     h = hs;
00363 
00364         /* Crop out a region including the foreground, and add pixels
00365          * on sides depending on the side flags */
00366     if (toppix || botpix || leftpix || rightpix) {
00367         x = y = 0;
00368         if (toppix) {
00369             h += toppix;
00370             y = toppix;
00371         }
00372         if (botpix)
00373             h += botpix;
00374         if (leftpix) {
00375             w += leftpix;
00376             x = leftpix;
00377         }
00378         if (rightpix)
00379             w += rightpix;
00380         pixt2 = pixCreate(w, h, 1);
00381         pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
00382     }
00383     else
00384         pixt2 = pixClone(pixt1);
00385     if (ppixe)
00386         *ppixe = pixClone(pixt2);
00387     pixDestroy(&pixt1);
00388 
00389         /* Identify fg and bg pixels that are at least 'distance' pixels
00390          * away from the boundary pixels in their set */
00391     seld = selCreateBrick(2 * distance + 1, 2 * distance + 1,
00392                           distance, distance, SEL_HIT);
00393     pixfg = pixErode(NULL, pixt2, seld);
00394     pixbg = pixDilate(NULL, pixt2, seld);
00395     pixInvert(pixbg, pixbg);
00396     selDestroy(&seld);
00397     pixDestroy(&pixt2);
00398 
00399         /* Generate the sel from a random selection of these points */
00400     sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
00401     if (hitfract > 0.0) {
00402         thresh = (l_int32)(hitfract * (l_float64)RAND_MAX);
00403         for (i = 0; i < h; i++) {
00404             for (j = 0; j < w; j++) {
00405                 pixGetPixel(pixfg, j, i, &val);
00406                 if (val) {
00407                     if (rand() < thresh)
00408                         selSetElement(sel, i, j, SEL_HIT);
00409                 }
00410             }
00411         }
00412     }
00413     if (missfract > 0.0) {
00414         thresh = (l_int32)(missfract * (l_float64)RAND_MAX);
00415         for (i = 0; i < h; i++) {
00416             for (j = 0; j < w; j++) {
00417                 pixGetPixel(pixbg, j, i, &val);
00418                 if (val) {
00419                     if (rand() < thresh)
00420                         selSetElement(sel, i, j, SEL_MISS);
00421                 }
00422             }
00423         }
00424     }
00425 
00426     pixDestroy(&pixfg);
00427     pixDestroy(&pixbg);
00428     return sel;
00429 }
00430 
00431 
00432 /*!
00433  *  pixGenerateSelBoundary()
00434  *
00435  *      Input:  pix (1 bpp, typically small, to be used as a pattern)
00436  *              hitdist (min distance from fg boundary pixel)
00437  *              missdist (min distance from bg boundary pixel)
00438  *              hitskip (number of boundary pixels skipped between hits)
00439  *              missskip (number of boundary pixels skipped between misses)
00440  *              topflag (flag for extra pixels of bg added above)
00441  *              botflag (flag for extra pixels of bg added below)
00442  *              leftflag (flag for extra pixels of bg added to left)
00443  *              rightflag (flag for extra pixels of bg added to right)
00444  *              &pixe (<optional return> input pix expanded by extra pixels)
00445  *      Return: sel (hit-miss for input pattern), or null on error
00446  *
00447  *  Notes:
00448  *    (1) All fg elements selected are exactly hitdist pixels away from
00449  *        the nearest fg boundary pixel, and ditto for bg elements.
00450  *        Valid inputs of hitdist and missdist are 0, 1, 2, 3 and 4.
00451  *        For example, a hitdist of 0 puts the hits at the fg boundary.
00452  *        Usually, the distances should be > 0 avoid the effect of
00453  *        noise at the boundary.
00454  *    (2) Set hitskip < 0 if no hits are to be used.  Ditto for missskip.
00455  *        If both hitskip and missskip are < 0, the sel would be empty,
00456  *        and NULL is returned.
00457  *    (3) The 4 flags determine whether the sel is increased on that side
00458  *        to allow bg misses to be placed all along that boundary.
00459  *        The increase in sel size on that side is the minimum necessary
00460  *        to allow the misses to be placed at mindist.  For text characters,
00461  *        the topflag and botflag are typically set to 1, and the leftflag
00462  *        and rightflag to 0.
00463  *    (4) The input pix, as extended by the extra pixels on selected sides,
00464  *        can optionally be returned.  For debugging, call
00465  *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
00466  *        on the generating bitmap.
00467  *    (5) This is probably the best of the three sel generators, in the
00468  *        sense that you have the most flexibility with the smallest number
00469  *        of hits and misses.
00470  */
00471 SEL *
00472 pixGenerateSelBoundary(PIX     *pixs,
00473                        l_int32  hitdist,
00474                        l_int32  missdist,
00475                        l_int32  hitskip,
00476                        l_int32  missskip,
00477                        l_int32  topflag,
00478                        l_int32  botflag,
00479                        l_int32  leftflag,
00480                        l_int32  rightflag,
00481                        PIX      **ppixe)
00482 {
00483 l_int32  ws, hs, w, h, x, y, ix, iy, i, npt;
00484 PIX     *pixt1, *pixt2, *pixt3, *pixfg, *pixbg;
00485 SEL     *selh, *selm, *sel_3, *sel;
00486 PTA     *ptah, *ptam; 
00487 
00488     PROCNAME("pixGenerateSelBoundary");
00489 
00490     if (ppixe) *ppixe = NULL;
00491     if (!pixs)
00492         return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
00493     if (pixGetDepth(pixs) != 1)
00494         return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00495     if (hitdist < 0 || hitdist > 4 || missdist < 0 || missdist > 4) 
00496         return (SEL *)ERROR_PTR("dist not in {0 .. 4}", procName, NULL);
00497     if (hitskip < 0 && missskip < 0)
00498         return (SEL *)ERROR_PTR("no hits or misses", procName, NULL);
00499 
00500         /* Locate the foreground */
00501     pixClipToForeground(pixs, &pixt1, NULL);
00502     if (!pixt1)
00503         return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
00504     ws = pixGetWidth(pixt1);
00505     hs = pixGetHeight(pixt1);
00506     w = ws;
00507     h = hs;
00508 
00509         /* Crop out a region including the foreground, and add pixels
00510          * on sides depending on the side flags */
00511     if (topflag || botflag || leftflag || rightflag) {
00512         x = y = 0;
00513         if (topflag) {
00514             h += missdist + 1;
00515             y = missdist + 1;
00516         }
00517         if (botflag)
00518             h += missdist + 1;
00519         if (leftflag) {
00520             w += missdist + 1;
00521             x = missdist + 1;
00522         }
00523         if (rightflag)
00524             w += missdist + 1;
00525         pixt2 = pixCreate(w, h, 1);
00526         pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
00527     }
00528     else {
00529         pixt2 = pixClone(pixt1);
00530     }
00531     if (ppixe)
00532         *ppixe = pixClone(pixt2);
00533     pixDestroy(&pixt1);
00534 
00535         /* Identify fg and bg pixels that are exactly hitdist and
00536          * missdist (rsp) away from the boundary pixels in their set.
00537          * Then get a subsampled set of these points. */
00538     sel_3 = selCreateBrick(3, 3, 1, 1, SEL_HIT);
00539     if (hitskip >= 0) {
00540         selh = selCreateBrick(2 * hitdist + 1, 2 * hitdist + 1,
00541                               hitdist, hitdist, SEL_HIT);
00542         pixt3 = pixErode(NULL, pixt2, selh);
00543         pixfg = pixErode(NULL, pixt3, sel_3);
00544         pixXor(pixfg, pixfg, pixt3);
00545         ptah = pixSubsampleBoundaryPixels(pixfg, hitskip);
00546         pixDestroy(&pixt3);
00547         pixDestroy(&pixfg);
00548         selDestroy(&selh);
00549     }
00550     if (missskip >= 0) {
00551         selm = selCreateBrick(2 * missdist + 1, 2 * missdist + 1,
00552                               missdist, missdist, SEL_HIT);
00553         pixt3 = pixDilate(NULL, pixt2, selm);
00554         pixbg = pixDilate(NULL, pixt3, sel_3);
00555         pixXor(pixbg, pixbg, pixt3);
00556         ptam = pixSubsampleBoundaryPixels(pixbg, missskip);
00557         pixDestroy(&pixt3);
00558         pixDestroy(&pixbg);
00559         selDestroy(&selm);
00560     }
00561     selDestroy(&sel_3);
00562     pixDestroy(&pixt2);
00563 
00564         /* Generate the hit-miss sel from these point */
00565     sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
00566     if (hitskip >= 0) {
00567         npt = ptaGetCount(ptah);
00568         for (i = 0; i < npt; i++) {
00569             ptaGetIPt(ptah, i, &ix, &iy);
00570             selSetElement(sel, iy, ix, SEL_HIT);
00571         }
00572     }
00573     if (missskip >= 0) {
00574         npt = ptaGetCount(ptam);
00575         for (i = 0; i < npt; i++) {
00576             ptaGetIPt(ptam, i, &ix, &iy);
00577             selSetElement(sel, iy, ix, SEL_MISS);
00578         }
00579     }
00580 
00581     ptaDestroy(&ptah);
00582     ptaDestroy(&ptam);
00583     return sel;
00584 }
00585 
00586 
00587 /*-----------------------------------------------------------------*
00588  *              Accumulate data on runs along lines                *
00589  *-----------------------------------------------------------------*/
00590 /*!
00591  *  pixGetRunCentersOnLine()
00592  *
00593  *      Input:  pixs (1 bpp)
00594  *              x, y (set one of these to -1; see notes)
00595  *              minlength (minimum length of acceptable run)
00596  *      Return: numa of fg runs, or null on error
00597  *
00598  *  Notes:
00599  *      (1) Action: this function computes the fg (black) and bg (white)
00600  *          pixel runlengths along the specified horizontal or vertical line,
00601  *          and returns a Numa of the "center" pixels of each fg run
00602  *          whose length equals or exceeds the minimum length.
00603  *      (2) This only works on horizontal and vertical lines.
00604  *      (3) For horizontal runs, set x = -1 and y to the value
00605  *          for all points along the raster line.  For vertical runs,
00606  *          set y = -1 and x to the value for all points along the
00607  *          pixel column.
00608  *      (4) For horizontal runs, the points in the Numa are the x
00609  *          values in the center of fg runs that are of length at
00610  *          least 'minlength'.  For vertical runs, the points in the
00611  *          Numa are the y values in the center of fg runs, again
00612  *          of length 'minlength' or greater.
00613  *      (5) If there are no fg runs along the line that satisfy the
00614  *          minlength constraint, the returned Numa is empty.  This
00615  *          is not an error.
00616  */
00617 NUMA *
00618 pixGetRunCentersOnLine(PIX     *pixs,
00619                        l_int32  x,
00620                        l_int32  y,
00621                        l_int32  minlength)
00622 {
00623 l_int32   w, h, i, r, nruns, len;
00624 NUMA     *naruns, *nad;
00625 
00626     PROCNAME("pixGetRunCentersOnLine");
00627 
00628     if (!pixs)
00629         return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL);
00630     if (pixGetDepth(pixs) != 1)
00631         return (NUMA *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00632     if (x != -1 && y != -1)
00633         return (NUMA *)ERROR_PTR("x or y must be -1", procName, NULL);
00634     if (x == -1 && y == -1)
00635         return (NUMA *)ERROR_PTR("x or y cannot both be -1", procName, NULL);
00636 
00637     if ((nad = numaCreate(0)) == NULL)
00638         return (NUMA *)ERROR_PTR("nad not made", procName, NULL);
00639     w = pixGetWidth(pixs);
00640     h = pixGetHeight(pixs);
00641     if (x == -1) {  /* horizontal run */
00642         if (y < 0 || y >= h)
00643             return nad;
00644         naruns = pixGetRunsOnLine(pixs, 0, y, w - 1, y);
00645     }
00646     else {  /* vertical run */
00647         if (x < 0 || x >= w)
00648             return nad;
00649         naruns = pixGetRunsOnLine(pixs, x, 0, x, h - 1);
00650     }
00651     nruns = numaGetCount(naruns);
00652 
00653         /* extract run center values; the first run is always bg */
00654     r = 0;  /* cumulative distance along line */
00655     for (i = 0; i < nruns; i++) {
00656         if (i % 2 == 0) {  /* bg run */
00657             numaGetIValue(naruns, i, &len);
00658             r += len;
00659             continue;
00660         }
00661         else {
00662             numaGetIValue(naruns, i, &len);
00663             if (len >= minlength)
00664                 numaAddNumber(nad, r + len / 2);
00665             r += len;
00666         }
00667     }
00668 
00669     numaDestroy(&naruns);
00670     return nad;
00671 }
00672 
00673 
00674 /*!
00675  *  pixGetRunsOnLine()
00676  *
00677  *      Input:  pixs (1 bpp)
00678  *              x1, y1, x2, y2 
00679  *      Return: numa, or null on error
00680  *
00681  *  Notes:
00682  *      (1) Action: this function uses the bresenham algorithm to compute
00683  *          the pixels along the specified line.  It returns a Numa of the
00684  *          runlengths of the fg (black) and bg (white) runs, always
00685  *          starting with a white run.
00686  *      (2) If the first pixel on the line is black, the length of the
00687  *          first returned run (which is white) is 0.
00688  */
00689 NUMA *
00690 pixGetRunsOnLine(PIX     *pixs,
00691                  l_int32  x1,
00692                  l_int32  y1,
00693                  l_int32  x2,
00694                  l_int32  y2)
00695 {
00696 l_int32   w, h, x, y, npts;
00697 l_int32   i, runlen, preval;
00698 l_uint32  val;
00699 NUMA     *numa;
00700 PTA      *pta;
00701 
00702     PROCNAME("pixGetRunsOnLine");
00703 
00704     if (!pixs)
00705         return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL);
00706     if (pixGetDepth(pixs) != 1)
00707         return (NUMA *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00708 
00709     w = pixGetWidth(pixs);
00710     h = pixGetHeight(pixs);
00711     if (x1 < 0 || x1 >= w)
00712         return (NUMA *)ERROR_PTR("x1 not valid", procName, NULL);
00713     if (x2 < 0 || x2 >= w)
00714         return (NUMA *)ERROR_PTR("x2 not valid", procName, NULL);
00715     if (y1 < 0 || y1 >= h)
00716         return (NUMA *)ERROR_PTR("y1 not valid", procName, NULL);
00717     if (y2 < 0 || y2 >= h)
00718         return (NUMA *)ERROR_PTR("y2 not valid", procName, NULL);
00719 
00720     if ((pta = generatePtaLine(x1, y1, x2, y2)) == NULL)
00721         return (NUMA *)ERROR_PTR("pta not made", procName, NULL);
00722     if ((npts = ptaGetCount(pta)) == 0)
00723         return (NUMA *)ERROR_PTR("pta has no pts", procName, NULL);
00724 
00725     if ((numa = numaCreate(0)) == NULL)
00726         return (NUMA *)ERROR_PTR("numa not made", procName, NULL);
00727 
00728     for (i = 0; i < npts; i++) {
00729         ptaGetIPt(pta, i, &x, &y);
00730         pixGetPixel(pixs, x, y, &val);
00731         if (i == 0) {
00732             if (val == 1) {  /* black pixel; append white run of size 0 */
00733                 numaAddNumber(numa, 0);
00734             }
00735             preval = val;
00736             runlen = 1;
00737             continue;
00738         }
00739         if (val == preval) {  /* extend current run */
00740             preval = val;
00741             runlen++;
00742         }
00743         else {  /* end previous run */
00744             numaAddNumber(numa, runlen);
00745             preval = val;
00746             runlen = 1;
00747         }
00748     }
00749     numaAddNumber(numa, runlen);  /* append last run */
00750 
00751     ptaDestroy(&pta);
00752     return numa;
00753 }
00754 
00755 
00756 /*-----------------------------------------------------------------*
00757  *        Subsample boundary pixels in relatively ordered way      *
00758  *-----------------------------------------------------------------*/
00759 /*!
00760  *  pixSubsampleBoundaryPixels()
00761  *
00762  *      Input:  pixs (1 bpp, with only boundary pixels in fg)
00763  *              skip (number to skip between samples as you traverse boundary)
00764  *      Return: pta, or null on error
00765  *
00766  *  Notes:
00767  *      (1) If skip = 0, we take all the fg pixels.
00768  *      (2) We try to traverse the boundaries in a regular way. 
00769  *          Some pixels may be missed, and these are then subsampled
00770  *          randomly with a fraction determined by 'skip'.
00771  *      (3) The most natural approach is to use a depth first (stack-based)
00772  *          method to find the fg pixels.  However, the pixel runs are
00773  *          4-connected and there are relatively few branches.  So 
00774  *          instead of doing a proper depth-first search, we get nearly
00775  *          the same result using two nested while loops: the outer
00776  *          one continues a raster-based search for the next fg pixel,
00777  *          and the inner one does a reasonable job running along
00778  *          each 4-connected coutour.
00779  */
00780 PTA *
00781 pixSubsampleBoundaryPixels(PIX     *pixs,
00782                            l_int32  skip)
00783 {
00784 l_int32  x, y, xn, yn, xs, ys, xa, ya, count;
00785 PIX     *pixt;
00786 PTA     *pta;
00787 
00788     PROCNAME("pixSubsampleBoundaryPixels");
00789 
00790     if (!pixs)
00791         return (PTA *)ERROR_PTR("pixs not defined", procName, NULL);
00792     if (pixGetDepth(pixs) != 1)
00793         return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00794     if (skip < 0)
00795         return (PTA *)ERROR_PTR("skip < 0", procName, NULL);
00796         
00797     if (skip == 0)
00798         return ptaGetPixelsFromPix(pixs, NULL);
00799 
00800     pta = ptaCreate(0);    
00801     pixt = pixCopy(NULL, pixs);
00802     xs = ys = 0;
00803     while (nextOnPixelInRaster(pixt, xs, ys, &xn, &yn)) {  /* new series */
00804         xs = xn;
00805         ys = yn;
00806 
00807             /* Add first point in this series */
00808         ptaAddPt(pta, xs, ys);
00809 
00810             /* Trace out boundary, erasing all and saving every (skip + 1)th */
00811         x = xs;
00812         y = ys;
00813         pixSetPixel(pixt, x, y, 0);
00814         count = 0;
00815         while (adjacentOnPixelInRaster(pixt, x, y, &xa, &ya)) {
00816             x = xa;
00817             y = ya;
00818             pixSetPixel(pixt, x, y, 0);
00819             if (count == skip) {
00820                 ptaAddPt(pta, x, y);
00821                 count = 0;
00822             }
00823             else {
00824                 count++;
00825             }
00826         }
00827     }
00828 
00829     pixDestroy(&pixt);
00830     return pta;
00831 }
00832 
00833 
00834 /*!
00835  *  adjacentOnPixelInRaster()
00836  *
00837  *      Input:  pixs (1 bpp)
00838  *              x, y (current pixel)
00839  *              xa, ya (adjacent ON pixel, found by simple CCW search)
00840  *      Return: 1 if a pixel is found; 0 otherwise or on error
00841  *
00842  *  Notes:
00843  *      (1) Search is in 4-connected directions first; then on diagonals.
00844  *          This allows traversal along a 4-connected boundary.
00845  */
00846 l_int32
00847 adjacentOnPixelInRaster(PIX      *pixs,
00848                         l_int32   x,
00849                         l_int32   y,
00850                         l_int32  *pxa,
00851                         l_int32  *pya)
00852 {
00853 l_int32   w, h, i, xa, ya, found;
00854 l_int32   xdel[] = {-1, 0, 1, 0, -1, 1, 1, -1};
00855 l_int32   ydel[] = {0, 1, 0, -1, 1, 1, -1, -1};
00856 l_uint32  val;
00857 
00858     PROCNAME("adjacentOnPixelInRaster");
00859 
00860     if (!pixs)
00861         return ERROR_INT("pixs not defined", procName, 0);
00862     if (pixGetDepth(pixs) != 1)
00863         return ERROR_INT("pixs not 1 bpp", procName, 0);
00864     w = pixGetWidth(pixs);
00865     h = pixGetHeight(pixs);
00866     found = 0;
00867     for (i = 0; i < 8; i++) {
00868         xa = x + xdel[i];
00869         ya = y + ydel[i];
00870         if (xa < 0 || xa >= w || ya < 0 || ya >= h)
00871             continue;
00872         pixGetPixel(pixs, xa, ya, &val);
00873         if (val == 1) {
00874             found = 1;
00875             *pxa = xa;
00876             *pya = ya;
00877             break;
00878         }
00879     }
00880     return found;
00881 }
00882 
00883 
00884 
00885 /*-----------------------------------------------------------------*
00886  *          Display generated sel with originating image           *
00887  *-----------------------------------------------------------------*/
00888 /*!
00889  *  pixDisplayHitMissSel()
00890  *
00891  *      Input:  pixs (1 bpp)
00892  *              sel (hit-miss in general)
00893  *              scalefactor (an integer >= 1; use 0 for default)
00894  *              hitcolor (RGB0 color for center of hit pixels)
00895  *              misscolor (RGB0 color for center of miss pixels)
00896  *      Return: pixd (RGB showing both pixs and sel), or null on error
00897  *  Notes:
00898  *    (1) We don't allow scalefactor to be larger than MAX_SEL_SCALEFACTOR
00899  *    (2) The colors are conveniently given as 4 bytes in hex format,
00900  *        such as 0xff008800.  The least significant byte is ignored.
00901  */
00902 PIX *
00903 pixDisplayHitMissSel(PIX      *pixs,
00904                      SEL      *sel,
00905                      l_int32   scalefactor,
00906                      l_uint32  hitcolor,
00907                      l_uint32  misscolor)
00908 {
00909 l_int32    i, j, type;
00910 l_float32  fscale;
00911 PIX       *pixt, *pixd;
00912 PIXCMAP   *cmap;
00913 
00914     PROCNAME("pixDisplayHitMissSel");
00915 
00916     if (!pixs)
00917         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00918     if (pixGetDepth(pixs) != 1)
00919         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
00920     if (!sel)
00921         return (PIX *)ERROR_PTR("sel not defined", procName, NULL);
00922 
00923     if (scalefactor <= 0)
00924         scalefactor = DEFAULT_SEL_SCALEFACTOR;
00925     if (scalefactor > MAX_SEL_SCALEFACTOR) {
00926         L_WARNING("scalefactor too large; using max value", procName); 
00927         scalefactor = MAX_SEL_SCALEFACTOR;
00928     }
00929 
00930         /* Generate a version of pixs with a colormap */
00931     pixt = pixConvert1To8(NULL, pixs, 0, 1);
00932     cmap = pixcmapCreate(8);
00933     pixcmapAddColor(cmap, 255, 255, 255);
00934     pixcmapAddColor(cmap, 0, 0, 0);
00935     pixcmapAddColor(cmap, hitcolor >> 24, (hitcolor >> 16) & 0xff,
00936                     (hitcolor >> 8) & 0xff);
00937     pixcmapAddColor(cmap, misscolor >> 24, (misscolor >> 16) & 0xff,
00938                     (misscolor >> 8) & 0xff);
00939     pixSetColormap(pixt, cmap);
00940 
00941         /* Color the hits and misses */
00942     for (i = 0; i < sel->sy; i++) {
00943         for (j = 0; j < sel->sx; j++) {
00944             selGetElement(sel, i, j, &type);
00945             if (type == SEL_DONT_CARE)
00946                 continue;
00947             if (type == SEL_HIT)
00948                 pixSetPixel(pixt, j, i, 2);
00949             else  /* type == SEL_MISS */
00950                 pixSetPixel(pixt, j, i, 3);
00951         }
00952     }
00953 
00954         /* Scale it up */
00955     fscale = (l_float32)scalefactor; 
00956     pixd = pixScaleBySampling(pixt, fscale, fscale);
00957 
00958     pixDestroy(&pixt);
00959     return pixd;
00960 }
00961 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines