• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*====================================================================*
2  -  Copyright (C) 2001 Leptonica.  All rights reserved.
3  -  This software is distributed in the hope that it will be
4  -  useful, but with NO WARRANTY OF ANY KIND.
5  -  No author or distributor accepts responsibility to anyone for the
6  -  consequences of using this software, or for whether it serves any
7  -  particular purpose or works at all, unless he or she says so in
8  -  writing.  Everyone is granted permission to copy, modify and
9  -  redistribute this source code, for commercial or non-commercial
10  -  purposes, with the following restrictions: (1) the origin of this
11  -  source code must not be misrepresented; (2) modified versions must
12  -  be plainly marked as such; and (3) this notice may not be removed
13  -  or altered from any source or modified source distribution.
14  *====================================================================*/
15 
16 /*
17  *  edge.c
18  *
19  *      Sobel edge detecting filter
20  *          PIX      *pixSobelEdgeFilter()
21  *
22  *      Two-sided edge gradient filter
23  *          PIX      *pixTwoSidedEdgeFilter()
24  *
25  *  The Sobel edge detector uses these two simple gradient filters.
26  *
27  *       1    2    1             1    0   -1
28  *       0    0    0             2    0   -2
29  *      -1   -2   -1             1    0   -1
30  *
31  *      (horizontal)             (vertical)
32  *
33  *  To use both the vertical and horizontal filters, set the orientation
34  *  flag to L_ALL_EDGES; this sums the abs. value of their outputs,
35  *  clipped to 255.
36  *
37  *  See comments below for displaying the resulting image with
38  *  the edges dark, both for 8 bpp and 1 bpp.
39  */
40 
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include "allheaders.h"
44 
45 
46 /*----------------------------------------------------------------------*
47  *                    Sobel edge detecting filter                       *
48  *----------------------------------------------------------------------*/
49 /*!
50  *  pixSobelEdgeFilter()
51  *
52  *      Input:  pixs (8 bpp; no colormap)
53  *              orientflag (L_HORIZONTAL_EDGES, L_VERTICAL_EDGES, L_ALL_EDGES)
54  *      Return: pixd (8 bpp, edges are brighter), or null on error
55  *
56  *  Notes:
57  *      (1) Invert pixd to see larger gradients as darker (grayscale).
58  *      (2) To generate a binary image of the edges, threshold
59  *          the result using pixThresholdToBinary().  If the high
60  *          edge values are to be fg (1), invert after running
61  *          pixThresholdToBinary().
62  *      (3) Label the pixels as follows:
63  *              1    4    7
64  *              2    5    8
65  *              3    6    9
66  *          Read the data incrementally across the image and unroll
67  *          the loop.
68  *      (4) This runs at about 45 Mpix/sec on a 3 GHz processor.
69  */
70 PIX *
pixSobelEdgeFilter(PIX * pixs,l_int32 orientflag)71 pixSobelEdgeFilter(PIX     *pixs,
72                    l_int32  orientflag)
73 {
74 l_int32    w, h, d, i, j, wplt, wpld, gx, gy, vald;
75 l_int32    val1, val2, val3, val4, val5, val6, val7, val8, val9;
76 l_uint32  *datat, *linet, *datad, *lined;
77 PIX       *pixt, *pixd;
78 
79     PROCNAME("pixSobelEdgeFilter");
80 
81     if (!pixs)
82         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
83     pixGetDimensions(pixs, &w, &h, &d);
84     if (d != 8)
85         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
86     if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES &&
87         orientflag != L_ALL_EDGES)
88         return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL);
89 
90         /* Add 1 pixel (mirrored) to each side of the image. */
91     if ((pixt = pixAddMirroredBorder(pixs, 1, 1, 1, 1)) == NULL)
92         return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
93 
94         /* Compute filter output at each location. */
95     pixd = pixCreateTemplate(pixs);
96     datat = pixGetData(pixt);
97     wplt = pixGetWpl(pixt);
98     datad = pixGetData(pixd);
99     wpld = pixGetWpl(pixd);
100     for (i = 0; i < h; i++) {
101         linet = datat + i * wplt;
102         lined = datad + i * wpld;
103         for (j = 0; j < w; j++) {
104             if (j == 0) {  /* start a new row */
105                 val1 = GET_DATA_BYTE(linet, j);
106                 val2 = GET_DATA_BYTE(linet + wplt, j);
107                 val3 = GET_DATA_BYTE(linet + 2 * wplt, j);
108                 val4 = GET_DATA_BYTE(linet, j + 1);
109                 val5 = GET_DATA_BYTE(linet + wplt, j + 1);
110                 val6 = GET_DATA_BYTE(linet + 2 * wplt, j + 1);
111                 val7 = GET_DATA_BYTE(linet, j + 2);
112                 val8 = GET_DATA_BYTE(linet + wplt, j + 2);
113                 val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2);
114             } else {  /* shift right by 1 pixel; update incrementally */
115                 val1 = val4;
116                 val2 = val5;
117                 val3 = val6;
118                 val4 = val7;
119                 val5 = val8;
120                 val6 = val9;
121                 val7 = GET_DATA_BYTE(linet, j + 2);
122                 val8 = GET_DATA_BYTE(linet + wplt, j + 2);
123                 val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2);
124             }
125             if (orientflag == L_HORIZONTAL_EDGES)
126                 vald = L_ABS(val1 + 2 * val4 + val7
127                              - val3 - 2 * val6 - val9) >> 3;
128             else if (orientflag == L_VERTICAL_EDGES)
129                 vald = L_ABS(val1 + 2 * val2 + val3 - val7
130                              - 2 * val8 - val9) >> 3;
131             else {  /* L_ALL_EDGES */
132                 gx = L_ABS(val1 + 2 * val2 + val3 - val7
133                            - 2 * val8 - val9) >> 3;
134                 gy = L_ABS(val1 + 2 * val4 + val7
135                              - val3 - 2 * val6 - val9) >> 3;
136                 vald = L_MIN(255, gx + gy);
137             }
138             SET_DATA_BYTE(lined, j, vald);
139         }
140     }
141 
142     pixDestroy(&pixt);
143     return pixd;
144 }
145 
146 
147 /*----------------------------------------------------------------------*
148  *                   Two-sided edge gradient filter                     *
149  *----------------------------------------------------------------------*/
150 /*!
151  *  pixTwoSidedEdgeFilter()
152  *
153  *      Input:  pixs (8 bpp; no colormap)
154  *              orientflag (L_HORIZONTAL_EDGES, L_VERTICAL_EDGES)
155  *      Return: pixd (8 bpp, edges are brighter), or null on error
156  *
157  *  Notes:
158  *      (1) For detecting vertical edges, this considers the
159  *          difference of the central pixel from those on the left
160  *          and right.  For situations where the gradient is the same
161  *          sign on both sides, this computes and stores the minimum
162  *          (absolute value of the) difference.  The reason for
163  *          checking the sign is that we are looking for pixels within
164  *          a transition.  By contrast, for single pixel noise, the pixel
165  *          value is either larger than or smaller than its neighbors,
166  *          so the gradient would change direction on each side.  Horizontal
167  *          edges are handled similarly, looking for vertical gradients.
168  *      (2) To generate a binary image of the edges, threshold
169  *          the result using pixThresholdToBinary().  If the high
170  *          edge values are to be fg (1), invert after running
171  *          pixThresholdToBinary().
172  *      (3) This runs at about 60 Mpix/sec on a 3 GHz processor.
173  *          It is about 30% faster than Sobel, and the results are
174  *          similar.
175  */
176 PIX *
pixTwoSidedEdgeFilter(PIX * pixs,l_int32 orientflag)177 pixTwoSidedEdgeFilter(PIX     *pixs,
178                       l_int32  orientflag)
179 {
180 l_int32    w, h, d, i, j, wpls, wpld;
181 l_int32    cval, rval, bval, val, lgrad, rgrad, tgrad, bgrad;
182 l_uint32  *datas, *lines, *datad, *lined;
183 PIX       *pixd;
184 
185     PROCNAME("pixTwoSidedEdgeFilter");
186 
187     if (!pixs)
188         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
189     pixGetDimensions(pixs, &w, &h, &d);
190     if (d != 8)
191         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
192     if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES)
193         return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL);
194 
195     pixd = pixCreateTemplate(pixs);
196     datas = pixGetData(pixs);
197     wpls = pixGetWpl(pixs);
198     datad = pixGetData(pixd);
199     wpld = pixGetWpl(pixd);
200     if (orientflag == L_VERTICAL_EDGES) {
201         for (i = 0; i < h; i++) {
202             lines = datas + i * wpls;
203             lined = datad + i * wpld;
204             cval = GET_DATA_BYTE(lines, 1);
205             lgrad = cval - GET_DATA_BYTE(lines, 0);
206             for (j = 1; j < w - 1; j++) {
207                 rval = GET_DATA_BYTE(lines, j + 1);
208                 rgrad = rval - cval;
209                 if (lgrad * rgrad > 0) {
210                     if (lgrad < 0)
211                         val = -L_MAX(lgrad, rgrad);
212                     else
213                         val = L_MIN(lgrad, rgrad);
214                     SET_DATA_BYTE(lined, j, val);
215                 }
216                 lgrad = rgrad;
217                 cval = rval;
218             }
219         }
220     }
221     else {  /* L_HORIZONTAL_EDGES) */
222         for (j = 0; j < w; j++) {
223             lines = datas + wpls;
224             cval = GET_DATA_BYTE(lines, j);  /* for line 1 */
225             tgrad = cval - GET_DATA_BYTE(datas, j);
226             for (i = 1; i < h - 1; i++) {
227                 lines += wpls;  /* for line i + 1 */
228                 lined = datad + i * wpld;
229                 bval = GET_DATA_BYTE(lines, j);
230                 bgrad = bval - cval;
231                 if (tgrad * bgrad > 0) {
232                     if (tgrad < 0)
233                         val = -L_MAX(tgrad, bgrad);
234                     else
235                         val = L_MIN(tgrad, bgrad);
236                     SET_DATA_BYTE(lined, j, val);
237                 }
238                 tgrad = bgrad;
239                 cval = bval;
240             }
241         }
242     }
243 
244     return pixd;
245 }
246 
247 
248