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