Leptonica 1.68
C Image Processing Library
|
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 * graymorphlow.c 00019 * 00020 * Low-level grayscale morphological operations 00021 * 00022 * void dilateGrayLow() 00023 * void erodeGrayLow() 00024 * 00025 * 00026 * We use the van Herk/Gil-Werman (vHGW) algorithm, [van Herk, 00027 * Patt. Recog. Let. 13, pp. 517-521, 1992; Gil and Werman, 00028 * IEEE Trans PAMI 15(5), pp. 504-507, 1993.] 00029 * This was the first grayscale morphology 00030 * algorithm to compute dilation and erosion with 00031 * complexity independent of the size of the structuring 00032 * element. It is simple and elegant, and surprising that 00033 * it was discovered as recently as 1992. It works for 00034 * SEs composed of horizontal and/or vertical lines. The 00035 * general case requires finding the Min or Max over an 00036 * arbitrary set of pixels, and this requires a number of 00037 * pixel comparisons equal to the SE "size" at each pixel 00038 * in the image. The vHGW algorithm requires not 00039 * more than 3 comparisons at each point. The algorithm has been 00040 * recently refined by Gil and Kimmel ("Efficient Dilation 00041 * Erosion, Opening and Closing Algorithms", in "Mathematical 00042 * Morphology and its Applications to Image and Signal Processing", 00043 * the proceedings of the International Symposium on Mathematical 00044 * Morphology, Palo Alto, CA, June 2000, Kluwer Academic 00045 * Publishers, pp. 301-310). They bring this number down below 00046 * 1.5 comparisons per output pixel but at a cost of significantly 00047 * increased complexity, so I don't bother with that here. 00048 * 00049 * In brief, the method is as follows. We evaluate the dilation 00050 * in groups of "size" pixels, equal to the size of the SE. 00051 * For horizontal, we start at x = "size"/2 and go 00052 * (w - 2 * ("size"/2))/"size" steps. This means that 00053 * we don't evaluate the first 0.5 * "size" pixels and, worst 00054 * case, the last 1.5 * "size" pixels. Thus we embed the 00055 * image in a larger image with these augmented dimensions, where 00056 * the new border pixels are appropriately initialized (0 for 00057 * dilation; 255 for erosion), and remove the boundary at the end. 00058 * (For vertical, use h instead of w.) Then for each group 00059 * of "size" pixels, we form an array of length 2 * "size" + 1, 00060 * consisting of backward and forward partial maxima (for 00061 * dilation) or minima (for erosion). This represents a 00062 * jumping window computed from the source image, over which 00063 * the SE will slide. The center of the array gets the source 00064 * pixel at the center of the SE. Call this the center pixel 00065 * of the window. Array values to left of center get 00066 * the maxima(minima) of the pixels from the center 00067 * one and going to the left an equal distance. Array 00068 * values to the right of center get the maxima(minima) to 00069 * the pixels from the center one and going to the right 00070 * an equal distance. These are computed sequentially starting 00071 * from the center one. The SE (of length "size") can slide over this 00072 * window (of length 2 * "size + 1) at "size" different places. 00073 * At each place, the maxima(minima) of the values in the window 00074 * that correspond to the end points of the SE give the extremal 00075 * values over that interval, and these are stored at the dest 00076 * pixel corresponding to the SE center. A picture is worth 00077 * at least this many words, so if this isn't clear, see the 00078 * leptonica documentation on grayscale morphology. 00079 * 00080 */ 00081 00082 #include <stdio.h> 00083 00084 #include "allheaders.h" 00085 00086 00087 00088 /*-----------------------------------------------------------------* 00089 * Low-level gray morphological operations * 00090 *-----------------------------------------------------------------*/ 00091 /*! 00092 * dilateGrayLow() 00093 * 00094 * Input: datad, w, h, wpld (8 bpp image) 00095 * datas, wpls (8 bpp image, of same dimensions) 00096 * size (full length of SEL; restricted to odd numbers) 00097 * direction (L_HORIZ or L_VERT) 00098 * buffer (holds full line or column of src image pixels) 00099 * maxarray (array of dimension 2*size+1) 00100 * Return: void 00101 * 00102 * Note: To eliminate border effects on the actual image, these images 00103 * are prepared with an additional border of dimensions: 00104 * leftpix = 0.5 * size 00105 * rightpix = 1.5 * size 00106 * toppix = 0.5 * size 00107 * bottompix = 1.5 * size 00108 * and we initialize the src border pixels to 0. 00109 * This allows full processing over the actual image; at 00110 * the end the border is removed. 00111 * 00112 * Method: Algorithm by van Herk and Gil and Werman 00113 */ 00114 void 00115 dilateGrayLow(l_uint32 *datad, 00116 l_int32 w, 00117 l_int32 h, 00118 l_int32 wpld, 00119 l_uint32 *datas, 00120 l_int32 wpls, 00121 l_int32 size, 00122 l_int32 direction, 00123 l_uint8 *buffer, 00124 l_uint8 *maxarray) 00125 { 00126 l_int32 i, j, k; 00127 l_int32 hsize, nsteps, startmax, startx, starty; 00128 l_uint8 maxval; 00129 l_uint32 *lines, *lined; 00130 00131 if (direction == L_HORIZ) { 00132 hsize = size / 2; 00133 nsteps = (w - 2 * hsize) / size; 00134 for (i = 0; i < h; i++) { 00135 lines = datas + i * wpls; 00136 lined = datad + i * wpld; 00137 00138 /* fill buffer with pixels in byte order */ 00139 for (j = 0; j < w; j++) 00140 buffer[j] = GET_DATA_BYTE(lines, j); 00141 00142 for (j = 0; j < nsteps; j++) 00143 { 00144 /* refill the minarray */ 00145 startmax = (j + 1) * size - 1; 00146 maxarray[size - 1] = buffer[startmax]; 00147 for (k = 1; k < size; k++) { 00148 maxarray[size - 1 - k] = 00149 L_MAX(maxarray[size - k], buffer[startmax - k]); 00150 maxarray[size - 1 + k] = 00151 L_MAX(maxarray[size + k - 2], buffer[startmax + k]); 00152 } 00153 00154 /* compute dilation values */ 00155 startx = hsize + j * size; 00156 SET_DATA_BYTE(lined, startx, maxarray[0]); 00157 SET_DATA_BYTE(lined, startx + size - 1, maxarray[2 * size - 2]); 00158 for (k = 1; k < size - 1; k++) { 00159 maxval = L_MAX(maxarray[k], maxarray[k + size - 1]); 00160 SET_DATA_BYTE(lined, startx + k, maxval); 00161 } 00162 } 00163 } 00164 } 00165 else { /* direction == L_VERT */ 00166 hsize = size / 2; 00167 nsteps = (h - 2 * hsize) / size; 00168 for (j = 0; j < w; j++) { 00169 00170 /* fill buffer with pixels in byte order */ 00171 for (i = 0; i < h; i++) { 00172 lines = datas + i * wpls; 00173 buffer[i] = GET_DATA_BYTE(lines, j); 00174 } 00175 00176 for (i = 0; i < nsteps; i++) 00177 { 00178 /* refill the minarray */ 00179 startmax = (i + 1) * size - 1; 00180 maxarray[size - 1] = buffer[startmax]; 00181 for (k = 1; k < size; k++) { 00182 maxarray[size - 1 - k] = 00183 L_MAX(maxarray[size - k], buffer[startmax - k]); 00184 maxarray[size - 1 + k] = 00185 L_MAX(maxarray[size + k - 2], buffer[startmax + k]); 00186 } 00187 00188 /* compute dilation values */ 00189 starty = hsize + i * size; 00190 lined = datad + starty * wpld; 00191 SET_DATA_BYTE(lined, j, maxarray[0]); 00192 SET_DATA_BYTE(lined + (size - 1) * wpld, j, 00193 maxarray[2 * size - 2]); 00194 for (k = 1; k < size - 1; k++) { 00195 maxval = L_MAX(maxarray[k], maxarray[k + size - 1]); 00196 SET_DATA_BYTE(lined + wpld * k, j, maxval); 00197 } 00198 } 00199 } 00200 } 00201 00202 return; 00203 } 00204 00205 00206 /*! 00207 * erodeGrayLow() 00208 * 00209 * Input: datad, w, h, wpld (8 bpp image) 00210 * datas, wpls (8 bpp image, of same dimensions) 00211 * size (full length of SEL; restricted to odd numbers) 00212 * direction (L_HORIZ or L_VERT) 00213 * buffer (holds full line or column of src image pixels) 00214 * minarray (array of dimension 2*size+1) 00215 * Return: void 00216 * 00217 * Note: To eliminate border effects on the actual image, these images 00218 * are prepared with an additional border of dimensions: 00219 * leftpix = 0.5 * size 00220 * rightpix = 1.5 * size 00221 * toppix = 0.5 * size 00222 * bottompix = 1.5 * size 00223 * and we initialize the src border pixels to 255. 00224 * This allows full processing over the actual image; at 00225 * the end the border is removed. 00226 * 00227 * Method: Algorithm by van Herk and Gil and Werman 00228 * 00229 */ 00230 void 00231 erodeGrayLow(l_uint32 *datad, 00232 l_int32 w, 00233 l_int32 h, 00234 l_int32 wpld, 00235 l_uint32 *datas, 00236 l_int32 wpls, 00237 l_int32 size, 00238 l_int32 direction, 00239 l_uint8 *buffer, 00240 l_uint8 *minarray) 00241 { 00242 l_int32 i, j, k; 00243 l_int32 hsize, nsteps, startmin, startx, starty; 00244 l_uint8 minval; 00245 l_uint32 *lines, *lined; 00246 00247 if (direction == L_HORIZ) { 00248 hsize = size / 2; 00249 nsteps = (w - 2 * hsize) / size; 00250 for (i = 0; i < h; i++) { 00251 lines = datas + i * wpls; 00252 lined = datad + i * wpld; 00253 00254 /* fill buffer with pixels in byte order */ 00255 for (j = 0; j < w; j++) 00256 buffer[j] = GET_DATA_BYTE(lines, j); 00257 00258 for (j = 0; j < nsteps; j++) 00259 { 00260 /* refill the minarray */ 00261 startmin = (j + 1) * size - 1; 00262 minarray[size - 1] = buffer[startmin]; 00263 for (k = 1; k < size; k++) { 00264 minarray[size - 1 - k] = 00265 L_MIN(minarray[size - k], buffer[startmin - k]); 00266 minarray[size - 1 + k] = 00267 L_MIN(minarray[size + k - 2], buffer[startmin + k]); 00268 } 00269 00270 /* compute erosion values */ 00271 startx = hsize + j * size; 00272 SET_DATA_BYTE(lined, startx, minarray[0]); 00273 SET_DATA_BYTE(lined, startx + size - 1, minarray[2 * size - 2]); 00274 for (k = 1; k < size - 1; k++) { 00275 minval = L_MIN(minarray[k], minarray[k + size - 1]); 00276 SET_DATA_BYTE(lined, startx + k, minval); 00277 } 00278 } 00279 } 00280 } 00281 else { /* direction == L_VERT */ 00282 hsize = size / 2; 00283 nsteps = (h - 2 * hsize) / size; 00284 for (j = 0; j < w; j++) { 00285 00286 /* fill buffer with pixels in byte order */ 00287 for (i = 0; i < h; i++) { 00288 lines = datas + i * wpls; 00289 buffer[i] = GET_DATA_BYTE(lines, j); 00290 } 00291 00292 for (i = 0; i < nsteps; i++) 00293 { 00294 /* refill the minarray */ 00295 startmin = (i + 1) * size - 1; 00296 minarray[size - 1] = buffer[startmin]; 00297 for (k = 1; k < size; k++) { 00298 minarray[size - 1 - k] = 00299 L_MIN(minarray[size - k], buffer[startmin - k]); 00300 minarray[size - 1 + k] = 00301 L_MIN(minarray[size + k - 2], buffer[startmin + k]); 00302 } 00303 00304 /* compute erosion values */ 00305 starty = hsize + i * size; 00306 lined = datad + starty * wpld; 00307 SET_DATA_BYTE(lined, j, minarray[0]); 00308 SET_DATA_BYTE(lined + (size - 1) * wpld, j, 00309 minarray[2 * size - 2]); 00310 for (k = 1; k < size - 1; k++) { 00311 minval = L_MIN(minarray[k], minarray[k + size - 1]); 00312 SET_DATA_BYTE(lined + wpld * k, j, minval); 00313 } 00314 } 00315 } 00316 } 00317 00318 return; 00319 } 00320 00321