Leptonica 1.68
C Image Processing Library

edge.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  *  edge.c
00018  *
00019  *      Sobel edge detecting filter
00020  *          PIX      *pixSobelEdgeFilter()
00021  *
00022  *      Two-sided edge gradient filter
00023  *          PIX      *pixTwoSidedEdgeFilter()
00024  *
00025  *      Measurement of edge smoothness
00026  *          l_int32   pixMeasureEdgeSmoothness()
00027  *          NUMA     *pixGetEdgeProfile()
00028  *          l_int32   pixGetLastOffPixelInRun()
00029  *          l_int32   pixGetLastOnPixelInRun()
00030  *
00031  *
00032  *  The Sobel edge detector uses these two simple gradient filters.
00033  *
00034  *       1    2    1             1    0   -1 
00035  *       0    0    0             2    0   -2
00036  *      -1   -2   -1             1    0   -1
00037  *
00038  *      (horizontal)             (vertical)
00039  *
00040  *  To use both the vertical and horizontal filters, set the orientation
00041  *  flag to L_ALL_EDGES; this sums the abs. value of their outputs,
00042  *  clipped to 255.
00043  *
00044  *  See comments below for displaying the resulting image with
00045  *  the edges dark, both for 8 bpp and 1 bpp.
00046  */
00047 
00048 #include <stdio.h>
00049 #include <stdlib.h>
00050 #include "allheaders.h"
00051 
00052 
00053 /*----------------------------------------------------------------------*
00054  *                    Sobel edge detecting filter                       *
00055  *----------------------------------------------------------------------*/
00056 /*!
00057  *  pixSobelEdgeFilter()
00058  *
00059  *      Input:  pixs (8 bpp; no colormap)
00060  *              orientflag (L_HORIZONTAL_EDGES, L_VERTICAL_EDGES, L_ALL_EDGES)
00061  *      Return: pixd (8 bpp, edges are brighter), or null on error
00062  *
00063  *  Notes:
00064  *      (1) Invert pixd to see larger gradients as darker (grayscale).
00065  *      (2) To generate a binary image of the edges, threshold
00066  *          the result using pixThresholdToBinary().  If the high
00067  *          edge values are to be fg (1), invert after running
00068  *          pixThresholdToBinary().
00069  *      (3) Label the pixels as follows:
00070  *              1    4    7
00071  *              2    5    8
00072  *              3    6    9
00073  *          Read the data incrementally across the image and unroll
00074  *          the loop.
00075  *      (4) This runs at about 45 Mpix/sec on a 3 GHz processor.
00076  */
00077 PIX *
00078 pixSobelEdgeFilter(PIX     *pixs,
00079                    l_int32  orientflag)
00080 {
00081 l_int32    w, h, d, i, j, wplt, wpld, gx, gy, vald;
00082 l_int32    val1, val2, val3, val4, val5, val6, val7, val8, val9;
00083 l_uint32  *datat, *linet, *datad, *lined;
00084 PIX       *pixt, *pixd;
00085 
00086     PROCNAME("pixSobelEdgeFilter");
00087 
00088     if (!pixs)
00089         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00090     pixGetDimensions(pixs, &w, &h, &d);
00091     if (d != 8)
00092         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
00093     if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES &&
00094         orientflag != L_ALL_EDGES)
00095         return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL);
00096 
00097         /* Add 1 pixel (mirrored) to each side of the image. */
00098     if ((pixt = pixAddMirroredBorder(pixs, 1, 1, 1, 1)) == NULL)
00099         return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
00100 
00101         /* Compute filter output at each location. */
00102     pixd = pixCreateTemplate(pixs);
00103     datat = pixGetData(pixt);
00104     wplt = pixGetWpl(pixt);
00105     datad = pixGetData(pixd);
00106     wpld = pixGetWpl(pixd);
00107     for (i = 0; i < h; i++) {
00108         linet = datat + i * wplt;
00109         lined = datad + i * wpld;
00110         for (j = 0; j < w; j++) {
00111             if (j == 0) {  /* start a new row */
00112                 val1 = GET_DATA_BYTE(linet, j);
00113                 val2 = GET_DATA_BYTE(linet + wplt, j);
00114                 val3 = GET_DATA_BYTE(linet + 2 * wplt, j);
00115                 val4 = GET_DATA_BYTE(linet, j + 1);
00116                 val5 = GET_DATA_BYTE(linet + wplt, j + 1);
00117                 val6 = GET_DATA_BYTE(linet + 2 * wplt, j + 1);
00118                 val7 = GET_DATA_BYTE(linet, j + 2);
00119                 val8 = GET_DATA_BYTE(linet + wplt, j + 2);
00120                 val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2);
00121             } else {  /* shift right by 1 pixel; update incrementally */
00122                 val1 = val4;
00123                 val2 = val5;
00124                 val3 = val6;
00125                 val4 = val7;
00126                 val5 = val8;
00127                 val6 = val9;
00128                 val7 = GET_DATA_BYTE(linet, j + 2);
00129                 val8 = GET_DATA_BYTE(linet + wplt, j + 2);
00130                 val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2);
00131             }
00132             if (orientflag == L_HORIZONTAL_EDGES)
00133                 vald = L_ABS(val1 + 2 * val4 + val7
00134                              - val3 - 2 * val6 - val9) >> 3;
00135             else if (orientflag == L_VERTICAL_EDGES)
00136                 vald = L_ABS(val1 + 2 * val2 + val3 - val7
00137                              - 2 * val8 - val9) >> 3;
00138             else {  /* L_ALL_EDGES */
00139                 gx = L_ABS(val1 + 2 * val2 + val3 - val7
00140                            - 2 * val8 - val9) >> 3;
00141                 gy = L_ABS(val1 + 2 * val4 + val7
00142                              - val3 - 2 * val6 - val9) >> 3;
00143                 vald = L_MIN(255, gx + gy);
00144             }
00145             SET_DATA_BYTE(lined, j, vald);
00146         }
00147     }
00148 
00149     pixDestroy(&pixt);
00150     return pixd;
00151 }
00152 
00153 
00154 /*----------------------------------------------------------------------*
00155  *                   Two-sided edge gradient filter                     *
00156  *----------------------------------------------------------------------*/
00157 /*!
00158  *  pixTwoSidedEdgeFilter()
00159  *
00160  *      Input:  pixs (8 bpp; no colormap)
00161  *              orientflag (L_HORIZONTAL_EDGES, L_VERTICAL_EDGES)
00162  *      Return: pixd (8 bpp, edges are brighter), or null on error
00163  *
00164  *  Notes:
00165  *      (1) For detecting vertical edges, this considers the 
00166  *          difference of the central pixel from those on the left
00167  *          and right.  For situations where the gradient is the same
00168  *          sign on both sides, this computes and stores the minimum
00169  *          (absolute value of the) difference.  The reason for
00170  *          checking the sign is that we are looking for pixels within
00171  *          a transition.  By contrast, for single pixel noise, the pixel
00172  *          value is either larger than or smaller than its neighbors,
00173  *          so the gradient would change direction on each side.  Horizontal
00174  *          edges are handled similarly, looking for vertical gradients.
00175  *      (2) To generate a binary image of the edges, threshold
00176  *          the result using pixThresholdToBinary().  If the high
00177  *          edge values are to be fg (1), invert after running
00178  *          pixThresholdToBinary().
00179  *      (3) This runs at about 60 Mpix/sec on a 3 GHz processor.
00180  *          It is about 30% faster than Sobel, and the results are
00181  *          similar.
00182  */
00183 PIX *
00184 pixTwoSidedEdgeFilter(PIX     *pixs,
00185                       l_int32  orientflag)
00186 {
00187 l_int32    w, h, d, i, j, wpls, wpld;
00188 l_int32    cval, rval, bval, val, lgrad, rgrad, tgrad, bgrad;
00189 l_uint32  *datas, *lines, *datad, *lined;
00190 PIX       *pixd;
00191 
00192     PROCNAME("pixTwoSidedEdgeFilter");
00193 
00194     if (!pixs)
00195         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
00196     pixGetDimensions(pixs, &w, &h, &d);
00197     if (d != 8)
00198         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
00199     if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES)
00200         return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL);
00201 
00202     pixd = pixCreateTemplate(pixs);
00203     datas = pixGetData(pixs);
00204     wpls = pixGetWpl(pixs);
00205     datad = pixGetData(pixd);
00206     wpld = pixGetWpl(pixd);
00207     if (orientflag == L_VERTICAL_EDGES) {
00208         for (i = 0; i < h; i++) {
00209             lines = datas + i * wpls;
00210             lined = datad + i * wpld;
00211             cval = GET_DATA_BYTE(lines, 1);
00212             lgrad = cval - GET_DATA_BYTE(lines, 0);
00213             for (j = 1; j < w - 1; j++) {
00214                 rval = GET_DATA_BYTE(lines, j + 1);
00215                 rgrad = rval - cval;
00216                 if (lgrad * rgrad > 0) {
00217                     if (lgrad < 0)
00218                         val = -L_MAX(lgrad, rgrad);
00219                     else
00220                         val = L_MIN(lgrad, rgrad);
00221                     SET_DATA_BYTE(lined, j, val);
00222                 }
00223                 lgrad = rgrad;
00224                 cval = rval;
00225             }
00226         }
00227     }
00228     else {  /* L_HORIZONTAL_EDGES) */
00229         for (j = 0; j < w; j++) {
00230             lines = datas + wpls;
00231             cval = GET_DATA_BYTE(lines, j);  /* for line 1 */
00232             tgrad = cval - GET_DATA_BYTE(datas, j);
00233             for (i = 1; i < h - 1; i++) {
00234                 lines += wpls;  /* for line i + 1 */
00235                 lined = datad + i * wpld;
00236                 bval = GET_DATA_BYTE(lines, j);
00237                 bgrad = bval - cval;
00238                 if (tgrad * bgrad > 0) {
00239                     if (tgrad < 0)
00240                         val = -L_MAX(tgrad, bgrad);
00241                     else
00242                         val = L_MIN(tgrad, bgrad);
00243                     SET_DATA_BYTE(lined, j, val);
00244                 }
00245                 tgrad = bgrad;
00246                 cval = bval;
00247             }
00248         }
00249     }
00250                 
00251     return pixd;
00252 }
00253 
00254 
00255 /*----------------------------------------------------------------------*
00256  *                   Measurement of edge smoothness                     *
00257  *----------------------------------------------------------------------*/
00258 /*!
00259  *  pixMeasureEdgeSmoothness()
00260  *
00261  *      Input:  pixs (1 bpp)
00262  *              side (L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOTTOM)
00263  *              minjump (minimum jump to be counted; >= 1)
00264  *              minreversal (minimum reversal size for new peak or valley)
00265  *              &jpl (<optional return> jumps/length: number of jumps,
00266  *                    normalized to length of component side)
00267  *              &jspl (<optional return> jumpsum/length: sum of all
00268  *                     sufficiently large jumps, normalized to length
00269  *                     of component side)
00270  *              &rpl (<optional return> reversals/length: number of
00271  *                    peak-to-valley or valley-to-peak reversals,
00272  *                    normalized to length of component side)
00273  *              debugfile (<optional> displays constructed edge; use NULL
00274  *                         for no output)
00275  *      Return: 0 if OK, 1 on error
00276  *
00277  *  Notes:
00278  *      (1) This computes three measures of smoothness of the edge of a
00279  *          connected component:
00280  *            * jumps/length: (jpl) the number of jumps of size >= @minjump,
00281  *              normalized to the length of the side
00282  *            * jump sum/length: (jspl) the sum of all jump lengths of
00283  *              size >= @minjump, normalized to the length of the side
00284  *            * reversals/length: (rpl) the number of peak <--> valley
00285  *              reversals, using @minreverse as a minimum deviation of
00286  *              the peak or valley from its preceeding extremum,
00287  *              normalized to the length of the side
00288  *      (2) The input pix should be a single connected component, but
00289  *          this is not required.
00290  */
00291 l_int32
00292 pixMeasureEdgeSmoothness(PIX         *pixs,
00293                          l_int32      side,
00294                          l_int32      minjump,
00295                          l_int32      minreversal,
00296                          l_float32   *pjpl,
00297                          l_float32   *pjspl,
00298                          l_float32   *prpl,
00299                          const char  *debugfile)
00300 {
00301 l_int32  i, n, val, nval, diff, njumps, jumpsum, nreversal;
00302 NUMA    *na, *nae;
00303 
00304     PROCNAME("pixMeasureEdgeSmoothness");
00305 
00306     if (pjpl) *pjpl = 0.0;
00307     if (pjspl) *pjspl = 0.0;
00308     if (prpl) *prpl = 0.0;
00309     if (!pjpl && !pjspl && !prpl && !debugfile)
00310         return ERROR_INT("no output requested", procName, 1);
00311     if (!pixs || pixGetDepth(pixs) != 1)
00312         return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
00313     if (side != L_FROM_LEFT && side != L_FROM_RIGHT &&
00314         side != L_FROM_TOP && side != L_FROM_BOTTOM)
00315         return ERROR_INT("invalid side", procName, 1);
00316     if (minjump < 1)
00317         return ERROR_INT("invalid minjump; must be >= 1", procName, 1);
00318     if (minreversal < 1)
00319         return ERROR_INT("invalid minreversal; must be >= 1", procName, 1);
00320 
00321     if ((na = pixGetEdgeProfile(pixs, side, debugfile)) == NULL)
00322         return ERROR_INT("edge profile not made", procName, 1);
00323     if ((n = numaGetCount(na)) < 2) {
00324         numaDestroy(&na);
00325         return 0;
00326     }
00327 
00328     if (pjpl || pjspl) {
00329         jumpsum = 0;
00330         njumps = 0;
00331         numaGetIValue(na, 0, &val);
00332         for (i = 1; i < n; i++) {
00333             numaGetIValue(na, i, &nval);
00334             diff = L_ABS(nval - val);
00335             if (diff >= minjump) {
00336                 njumps++;
00337                 jumpsum += diff;
00338             }
00339             val = nval;
00340         }
00341         if (pjpl)
00342             *pjpl = (l_float32)njumps / (l_float32)(n - 1);
00343         if (pjspl)
00344             *pjspl = (l_float32)jumpsum / (l_float32)(n - 1);
00345     }
00346 
00347     if (prpl) {
00348         nae = numaFindExtrema(na, minreversal);
00349         nreversal = numaGetCount(nae) - 1;
00350         *prpl = (l_float32)nreversal / (l_float32)(n - 1);
00351         numaDestroy(&nae);
00352     }
00353 
00354     numaDestroy(&na);
00355     return 0;
00356 }
00357 
00358 
00359 /*!
00360  *  pixGetEdgeProfile()
00361  *
00362  *      Input:  pixs (1 bpp)
00363  *              side (L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOTTOM)
00364  *              debugfile (<optional> displays constructed edge; use NULL
00365  *                         for no output)
00366  *      Return: na (of fg edge pixel locations), or null on error
00367  */
00368 NUMA *
00369 pixGetEdgeProfile(PIX         *pixs,
00370                   l_int32      side,
00371                   const char  *debugfile)
00372 {
00373 l_int32   x, y, w, h, loc, n, index, ival;
00374 l_uint32  val;
00375 NUMA     *na;
00376 PIX      *pixt;
00377 PIXCMAP  *cmap;
00378 
00379     PROCNAME("pixGetEdgeProfile");
00380 
00381     if (!pixs || pixGetDepth(pixs) != 1)
00382         return (NUMA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
00383     if (side != L_FROM_LEFT && side != L_FROM_RIGHT &&
00384         side != L_FROM_TOP && side != L_FROM_BOTTOM)
00385         return (NUMA *)ERROR_PTR("invalid side", procName, NULL);
00386 
00387     pixGetDimensions(pixs, &w, &h, NULL);
00388     if (side == L_FROM_LEFT || side == L_FROM_RIGHT)
00389         na = numaCreate(h);
00390     else
00391         na = numaCreate(w);
00392     if (side == L_FROM_LEFT) {
00393         pixGetLastOffPixelInRun(pixs, 0, 0, L_FROM_LEFT, &loc);
00394         loc = (loc == w - 1) ? 0 : loc + 1;  /* back to the left edge */
00395         numaAddNumber(na, loc);
00396         for (y = 1; y < h; y++) {
00397             pixGetPixel(pixs, loc, y, &val);
00398             if (val == 1)
00399                 pixGetLastOnPixelInRun(pixs, loc, y, L_FROM_RIGHT, &loc);
00400             else {
00401                 pixGetLastOffPixelInRun(pixs, loc, y, L_FROM_LEFT, &loc);
00402                 loc = (loc == w - 1) ? 0 : loc + 1;
00403             }
00404             numaAddNumber(na, loc);
00405         }
00406     }
00407     else if (side == L_FROM_RIGHT) {
00408         pixGetLastOffPixelInRun(pixs, w - 1, 0, L_FROM_RIGHT, &loc);
00409         loc = (loc == 0) ? w - 1 : loc - 1;  /* back to the right edge */
00410         numaAddNumber(na, loc);
00411         for (y = 1; y < h; y++) {
00412             pixGetPixel(pixs, loc, y, &val);
00413             if (val == 1)
00414                 pixGetLastOnPixelInRun(pixs, loc, y, L_FROM_LEFT, &loc);
00415             else {
00416                 pixGetLastOffPixelInRun(pixs, loc, y, L_FROM_RIGHT, &loc);
00417                 loc = (loc == 0) ? w - 1 : loc - 1;
00418             }
00419             numaAddNumber(na, loc);
00420         }
00421     }
00422     else if (side == L_FROM_TOP) {
00423         pixGetLastOffPixelInRun(pixs, 0, 0, L_FROM_TOP, &loc);
00424         loc = (loc == h - 1) ? 0 : loc + 1;  /* back to the top edge */
00425         numaAddNumber(na, loc);
00426         for (x = 1; x < w; x++) {
00427             pixGetPixel(pixs, x, loc, &val);
00428             if (val == 1)
00429                 pixGetLastOnPixelInRun(pixs, x, loc, L_FROM_BOTTOM, &loc);
00430             else {
00431                 pixGetLastOffPixelInRun(pixs, x, loc, L_FROM_TOP, &loc);
00432                 loc = (loc == h - 1) ? 0 : loc + 1;
00433             }
00434             numaAddNumber(na, loc);
00435         }
00436     }
00437     else {  /* side == L_FROM_BOTTOM */
00438         pixGetLastOffPixelInRun(pixs, 0, h - 1, L_FROM_BOTTOM, &loc);
00439         loc = (loc == 0) ? h - 1 : loc - 1;  /* back to the bottom edge */
00440         numaAddNumber(na, loc);
00441         for (x = 1; x < w; x++) {
00442             pixGetPixel(pixs, x, loc, &val);
00443             if (val == 1)
00444                 pixGetLastOnPixelInRun(pixs, x, loc, L_FROM_TOP, &loc);
00445             else {
00446                 pixGetLastOffPixelInRun(pixs, x, loc, L_FROM_BOTTOM, &loc);
00447                 loc = (loc == 0) ? h - 1 : loc - 1;
00448             }
00449             numaAddNumber(na, loc);
00450         }
00451     }
00452 
00453     if (debugfile) {
00454         pixt = pixConvertTo8(pixs, TRUE);
00455         cmap = pixGetColormap(pixt);
00456         pixcmapAddColor(cmap, 255, 0, 0);
00457         index = pixcmapGetCount(cmap) - 1;
00458         n = numaGetCount(na);
00459         if (side == L_FROM_LEFT || side == L_FROM_RIGHT) {
00460             for (y = 0; y < h; y++) {
00461                 numaGetIValue(na, y, &ival);
00462                 pixSetPixel(pixt, ival, y, index);
00463             }
00464         } else {  /* L_FROM_TOP or L_FROM_BOTTOM */
00465             for (x = 0; x < w; x++) {
00466                 numaGetIValue(na, x, &ival);
00467                 pixSetPixel(pixt, x, ival, index);
00468             }
00469         }
00470         pixWrite(debugfile, pixt, IFF_PNG);
00471         pixDestroy(&pixt);
00472     }
00473 
00474     return na;
00475 }
00476 
00477 
00478 /*
00479  *  pixGetLastOffPixelInRun()
00480  *
00481  *      Input:  pixs (1 bpp)
00482  *              x, y (starting location)
00483  *              direction (L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOTTOM)
00484  *              &loc (<return> location in scan direction coordinate
00485  *                    of last OFF pixel found)
00486  *      Return: na (of fg edge pixel locations), or null on error
00487  *
00488  *  Notes:
00489  *      (1) Search starts from the pixel at (x, y), which is OFF.
00490  *      (2) It returns the location in the scan direction of the last
00491  *          pixel in the current run that is OFF.
00492  *      (3) The interface for these pixel run functions is cleaner when
00493  *          you ask for the last pixel in the current run, rather than the
00494  *          first pixel of opposite polarity that is found, because the
00495  *          current run may go to the edge of the image, in which case
00496  *          no pixel of opposite polarity is found.
00497  */
00498 l_int32
00499 pixGetLastOffPixelInRun(PIX      *pixs,
00500                         l_int32   x,
00501                         l_int32   y,
00502                         l_int32   direction,
00503                         l_int32  *ploc)
00504 {
00505 l_int32   loc, w, h;
00506 l_uint32  val;
00507 
00508     PROCNAME("pixGetLastOffPixelInRun");
00509 
00510     if (!ploc)
00511         return ERROR_INT("&loc not defined", procName, 1);
00512     *ploc = 0;
00513     if (!pixs || pixGetDepth(pixs) != 1)
00514         return ERROR_INT("pixs undefined or not 1 bpp", procName, 1);
00515     if (direction != L_FROM_LEFT && direction != L_FROM_RIGHT &&
00516         direction != L_FROM_TOP && direction != L_FROM_BOTTOM)
00517         return ERROR_INT("invalid side", procName, 1);
00518 
00519     pixGetDimensions(pixs, &w, &h, NULL);
00520     if (direction == L_FROM_LEFT) {
00521         for (loc = x; loc < w; loc++) {
00522             pixGetPixel(pixs, loc, y, &val);
00523             if (val == 1)
00524                 break;
00525         }
00526         *ploc = loc - 1;
00527     } else if (direction == L_FROM_RIGHT) {
00528         for (loc = x; loc >= 0; loc--) {
00529             pixGetPixel(pixs, loc, y, &val);
00530             if (val == 1)
00531                 break;
00532         }
00533         *ploc = loc + 1;
00534     }
00535     else if (direction == L_FROM_TOP) {
00536         for (loc = y; loc < h; loc++) {
00537             pixGetPixel(pixs, x, loc, &val);
00538             if (val == 1)
00539                 break;
00540         }
00541         *ploc = loc - 1;
00542     }
00543     else if (direction == L_FROM_BOTTOM) {
00544         for (loc = y; loc >= 0; loc--) {
00545             pixGetPixel(pixs, x, loc, &val);
00546             if (val == 1)
00547                 break;
00548         }
00549         *ploc = loc + 1;
00550     }
00551     return 0;
00552 }
00553 
00554 
00555 /*
00556  *  pixGetLastOnPixelInRun()
00557  *
00558  *      Input:  pixs (1 bpp)
00559  *              x, y (starting location)
00560  *              direction (L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOTTOM)
00561  *              &loc (<return> location in scan direction coordinate
00562  *                    of first ON pixel found)
00563  *      Return: na (of fg edge pixel locations), or null on error
00564  *
00565  *  Notes:
00566  *      (1) Search starts from the pixel at (x, y), which is ON.
00567  *      (2) It returns the location in the scan direction of the last
00568  *          pixel in the current run that is ON.
00569  */
00570 l_int32
00571 pixGetLastOnPixelInRun(PIX      *pixs,
00572                        l_int32   x,
00573                        l_int32   y,
00574                        l_int32   direction,
00575                        l_int32  *ploc)
00576 {
00577 l_int32   loc, w, h;
00578 l_uint32  val;
00579 
00580     PROCNAME("pixLastOnPixelInRun");
00581 
00582     if (!ploc)
00583         return ERROR_INT("&loc not defined", procName, 1);
00584     *ploc = 0;
00585     if (!pixs || pixGetDepth(pixs) != 1)
00586         return ERROR_INT("pixs undefined or not 1 bpp", procName, 1);
00587     if (direction != L_FROM_LEFT && direction != L_FROM_RIGHT &&
00588         direction != L_FROM_TOP && direction != L_FROM_BOTTOM)
00589         return ERROR_INT("invalid side", procName, 1);
00590 
00591     pixGetDimensions(pixs, &w, &h, NULL);
00592     if (direction == L_FROM_LEFT) {
00593         for (loc = x; loc < w; loc++) {
00594             pixGetPixel(pixs, loc, y, &val);
00595             if (val == 0)
00596                 break;
00597         }
00598         *ploc = loc - 1;
00599     } else if (direction == L_FROM_RIGHT) {
00600         for (loc = x; loc >= 0; loc--) {
00601             pixGetPixel(pixs, loc, y, &val);
00602             if (val == 0)
00603                 break;
00604         }
00605         *ploc = loc + 1;
00606     }
00607     else if (direction == L_FROM_TOP) {
00608         for (loc = y; loc < h; loc++) {
00609             pixGetPixel(pixs, x, loc, &val);
00610             if (val == 0)
00611                 break;
00612         }
00613         *ploc = loc - 1;
00614     }
00615     else if (direction == L_FROM_BOTTOM) {
00616         for (loc = y; loc >= 0; loc--) {
00617             pixGetPixel(pixs, x, loc, &val);
00618             if (val == 0)
00619                 break;
00620         }
00621         *ploc = loc + 1;
00622     }
00623     return 0;
00624 }
00625 
00626 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines