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 * rank.c 00018 * 00019 * Rank filter (gray and rgb) 00020 * PIX *pixRankFilter() 00021 * PIX *pixRankFilterRGB() 00022 * PIX *pixRankFilterGray() 00023 * 00024 * Median filter 00025 * PIX *pixMedianFilter() 00026 * 00027 * What is a brick rank filter? 00028 * 00029 * A brick rank order filter evaluates, for every pixel in the image, 00030 * a rectangular set of n = wf x hf pixels in its neighborhood (where the 00031 * pixel in question is at the "center" of the rectangle and is 00032 * included in the evaluation). It determines the value of the 00033 * neighboring pixel that is the r-th smallest in the set, 00034 * where r is some integer between 1 and n. The input rank parameter 00035 * is a fraction between 0.0 and 1.0, where 0.0 represents the 00036 * smallest value (r = 1) and 1.0 represents the largest value (r = n). 00037 * A median filter is a rank filter where rank = 0.5. 00038 * 00039 * It is important to note that grayscale erosion is equivalent 00040 * to rank = 0.0, and grayscale dilation is equivalent to rank = 1.0. 00041 * These are much easier to calculate than the general rank value, 00042 * thanks to the van Herk/Gil-Werman algorithm: 00043 * http://www.leptonica.com/grayscale-morphology.html 00044 * so you should use pixErodeGray() and pixDilateGray() for 00045 * rank 0.0 and 1.0, rsp. See notes below in the function header. 00046 * 00047 * How is a rank filter implemented efficiently on an image? 00048 * 00049 * Sorting will not work. 00050 * 00051 * * The best sort algorithms are O(n*logn), where n is the number 00052 * of values to be sorted (the area of the filter). For large 00053 * filters this is an impractically large number. 00054 * 00055 * * Selection of the rank value is O(n). (To understand why it's not 00056 * O(n*logn), see Numerical Recipes in C, 2nd edition, 1992, p. 355ff). 00057 * This also still far too much computation for large filters. 00058 * 00059 * * Suppose we get clever. We really only need to do an incremental 00060 * selection or sorting, because, for example, moving the filter 00061 * down by one pixel causes one filter width of pixels to be added 00062 * and another to be removed. Can we do this incrementally in 00063 * an efficient way? Unfortunately, no. The sorted values will be 00064 * in an array. Even if the filter width is 1, we can expect to 00065 * have to move O(n) pixels, because insertion and deletion can happen 00066 * anywhere in the array. By comparison, heapsort is excellent for 00067 * incremental sorting, where the cost for insertion or deletion 00068 * is O(logn), because the array itself doesn't need to 00069 * be sorted into strictly increasing order. However, heapsort 00070 * only gives the max (or min) value, not the general rank value. 00071 * 00072 * This leaves histograms. 00073 * 00074 * * Represented as an array. The problem with an array of 256 00075 * bins is that, in general, a significant fraction of the 00076 * entire histogram must be summed to find the rank value bin. 00077 * Suppose the filter size is 5x5. You spend most of your time 00078 * adding zeroes. Ouch! 00079 * 00080 * * Represented as a linked list. This would overcome the 00081 * summing-over-empty-bin problem, but you lose random access 00082 * for insertions and deletions. No way. 00083 * 00084 * * Two histogram solution. Maintain two histograms with 00085 * bin sizes of 1 and 16. Proceed from coarse to fine. 00086 * First locate the coarse bin for the given rank, of which 00087 * there are only 16. Then, in the 256 entry (fine) histogram, 00088 * you need look at a maximum of 16 bins. For each output 00089 * pixel, the average number of bins summed over, both in the 00090 * coarse and fine histograms, is thus 16. 00091 * 00092 * If someone has a better method, please let me know! 00093 */ 00094 00095 #include <stdio.h> 00096 #include <stdlib.h> 00097 #include "allheaders.h" 00098 00099 00100 /*----------------------------------------------------------------------* 00101 * Rank order filter * 00102 *----------------------------------------------------------------------*/ 00103 /*! 00104 * pixRankFilter() 00105 * 00106 * Input: pixs (8 or 32 bpp; no colormap) 00107 * wf, hf (width and height of filter; each is >= 1) 00108 * rank (in [0.0 ... 1.0]) 00109 * Return: pixd (of rank values), or null on error 00110 * 00111 * Notes: 00112 * (1) This defines, for each pixel in pixs, a neighborhood of 00113 * pixels given by a rectangle "centered" on the pixel. 00114 * This set of wf*hf pixels has a distribution of values. 00115 * For each component, if the values are sorted in increasing 00116 * order, we choose the component such that rank*(wf*hf-1) 00117 * pixels have a lower or equal value and 00118 * (1-rank)*(wf*hf-1) pixels have an equal or greater value. 00119 * (2) See notes in pixRankFilterGray() for further details. 00120 */ 00121 PIX * 00122 pixRankFilter(PIX *pixs, 00123 l_int32 wf, 00124 l_int32 hf, 00125 l_float32 rank) 00126 { 00127 l_int32 d; 00128 00129 PROCNAME("pixRankFilter"); 00130 00131 if (!pixs) 00132 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00133 if (pixGetColormap(pixs) != NULL) 00134 return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); 00135 d = pixGetDepth(pixs); 00136 if (d != 8 && d != 32) 00137 return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL); 00138 if (wf < 1 || hf < 1) 00139 return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); 00140 if (rank < 0.0 || rank > 1.0) 00141 return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); 00142 if (wf == 1 && hf == 1) /* no-op */ 00143 return pixCopy(NULL, pixs); 00144 00145 if (d == 8) 00146 return pixRankFilterGray(pixs, wf, hf, rank); 00147 else /* d == 32 */ 00148 return pixRankFilterRGB(pixs, wf, hf, rank); 00149 } 00150 00151 00152 /*! 00153 * pixRankFilterRGB() 00154 * 00155 * Input: pixs (32 bpp) 00156 * wf, hf (width and height of filter; each is >= 1) 00157 * rank (in [0.0 ... 1.0]) 00158 * Return: pixd (of rank values), or null on error 00159 * 00160 * Notes: 00161 * (1) This defines, for each pixel in pixs, a neighborhood of 00162 * pixels given by a rectangle "centered" on the pixel. 00163 * This set of wf*hf pixels has a distribution of values. 00164 * For each component, if the values are sorted in increasing 00165 * order, we choose the component such that rank*(wf*hf-1) 00166 * pixels have a lower or equal value and 00167 * (1-rank)*(wf*hf-1) pixels have an equal or greater value. 00168 * (2) Apply gray rank filtering to each component independently. 00169 * (3) See notes in pixRankFilterGray() for further details. 00170 */ 00171 PIX * 00172 pixRankFilterRGB(PIX *pixs, 00173 l_int32 wf, 00174 l_int32 hf, 00175 l_float32 rank) 00176 { 00177 PIX *pixr, *pixg, *pixb, *pixrf, *pixgf, *pixbf, *pixd; 00178 00179 PROCNAME("pixRankFilterRGB"); 00180 00181 if (!pixs) 00182 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00183 if (pixGetDepth(pixs) != 32) 00184 return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); 00185 if (wf < 1 || hf < 1) 00186 return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); 00187 if (rank < 0.0 || rank > 1.0) 00188 return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); 00189 if (wf == 1 && hf == 1) /* no-op */ 00190 return pixCopy(NULL, pixs); 00191 00192 pixr = pixGetRGBComponent(pixs, COLOR_RED); 00193 pixg = pixGetRGBComponent(pixs, COLOR_GREEN); 00194 pixb = pixGetRGBComponent(pixs, COLOR_BLUE); 00195 00196 pixrf = pixRankFilterGray(pixr, wf, hf, rank); 00197 pixgf = pixRankFilterGray(pixg, wf, hf, rank); 00198 pixbf = pixRankFilterGray(pixb, wf, hf, rank); 00199 00200 pixd = pixCreateRGBImage(pixrf, pixgf, pixbf); 00201 pixDestroy(&pixr); 00202 pixDestroy(&pixg); 00203 pixDestroy(&pixb); 00204 pixDestroy(&pixrf); 00205 pixDestroy(&pixgf); 00206 pixDestroy(&pixbf); 00207 return pixd; 00208 } 00209 00210 00211 /*! 00212 * pixRankFilterGray() 00213 * 00214 * Input: pixs (8 bpp; no colormap) 00215 * wf, hf (width and height of filter; each is >= 1) 00216 * rank (in [0.0 ... 1.0]) 00217 * Return: pixd (of rank values), or null on error 00218 * 00219 * Notes: 00220 * (1) This defines, for each pixel in pixs, a neighborhood of 00221 * pixels given by a rectangle "centered" on the pixel. 00222 * This set of wf*hf pixels has a distribution of values, 00223 * and if they are sorted in increasing order, we choose 00224 * the pixel such that rank*(wf*hf-1) pixels have a lower 00225 * or equal value and (1-rank)*(wf*hf-1) pixels have an equal 00226 * or greater value. 00227 * (2) By this definition, the rank = 0.0 pixel has the lowest 00228 * value, and the rank = 1.0 pixel has the highest value. 00229 * (3) We add mirrored boundary pixels to avoid boundary effects, 00230 * and put the filter center at (0, 0). 00231 * (4) This dispatches to grayscale erosion or dilation if the 00232 * filter dimensions are odd and the rank is 0.0 or 1.0, rsp. 00233 * (5) Returns a copy if both wf and hf are 1. 00234 * (6) Uses row-major or column-major incremental updates to the 00235 * histograms depending on whether hf > wf or hv <= wf, rsp. 00236 */ 00237 PIX * 00238 pixRankFilterGray(PIX *pixs, 00239 l_int32 wf, 00240 l_int32 hf, 00241 l_float32 rank) 00242 { 00243 l_int32 w, h, d, i, j, k, m, n, rankloc, wplt, wpld, val, sum; 00244 l_int32 *histo, *histo16; 00245 l_uint32 *datat, *linet, *datad, *lined; 00246 PIX *pixt, *pixd; 00247 00248 PROCNAME("pixRankFilterGray"); 00249 00250 if (!pixs) 00251 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00252 if (pixGetColormap(pixs) != NULL) 00253 return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL); 00254 pixGetDimensions(pixs, &w, &h, &d); 00255 if (d != 8) 00256 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); 00257 if (wf < 1 || hf < 1) 00258 return (PIX *)ERROR_PTR("wf < 1 || hf < 1", procName, NULL); 00259 if (rank < 0.0 || rank > 1.0) 00260 return (PIX *)ERROR_PTR("rank must be in [0.0, 1.0]", procName, NULL); 00261 if (wf == 1 && hf == 1) /* no-op */ 00262 return pixCopy(NULL, pixs); 00263 00264 /* For rank = 0.0, this is a grayscale erosion, and for rank = 1.0, 00265 * a dilation. Grayscale morphology operations are implemented 00266 * for filters of odd dimension, so we dispatch to grayscale 00267 * morphology if both wf and hf are odd. Otherwise, we 00268 * slightly adjust the rank (to get the correct behavior) and 00269 * use the slower rank filter here. */ 00270 if (wf % 2 && hf % 2) { 00271 if (rank == 0.0) 00272 return pixErodeGray(pixs, wf, hf); 00273 else if (rank == 1.0) 00274 return pixDilateGray(pixs, wf, hf); 00275 } 00276 if (rank == 0.0) rank = 0.0001; 00277 if (rank == 1.0) rank = 0.9999; 00278 00279 /* Add wf/2 to each side, and hf/2 to top and bottom of the 00280 * image, mirroring for accuracy and to avoid special-casing 00281 * the boundary. */ 00282 if ((pixt = pixAddMirroredBorder(pixs, wf / 2, wf / 2, hf / 2, hf / 2)) 00283 == NULL) 00284 return (PIX *)ERROR_PTR("pixt not made", procName, NULL); 00285 00286 /* Set up the two histogram arrays. */ 00287 histo = (l_int32 *)CALLOC(256, sizeof(l_int32)); 00288 histo16 = (l_int32 *)CALLOC(16, sizeof(l_int32)); 00289 rankloc = (l_int32)(rank * wf * hf); 00290 00291 /* Place the filter center at (0, 0). This is just a 00292 * convenient location, because it allows us to perform 00293 * the rank filter over x:(0 ... w - 1) and y:(0 ... h - 1). */ 00294 pixd = pixCreateTemplate(pixs); 00295 datat = pixGetData(pixt); 00296 wplt = pixGetWpl(pixt); 00297 datad = pixGetData(pixd); 00298 wpld = pixGetWpl(pixd); 00299 00300 /* If hf > wf, it's more efficient to use row-major scanning. 00301 * Otherwise, traverse the image in use column-major order. */ 00302 if (hf > wf) { 00303 for (j = 0; j < w; j++) { /* row-major */ 00304 /* Start each column with clean histogram arrays. */ 00305 for (n = 0; n < 256; n++) 00306 histo[n] = 0; 00307 for (n = 0; n < 16; n++) 00308 histo16[n] = 0; 00309 00310 for (i = 0; i < h; i++) { /* fast scan on columns */ 00311 /* Update the histos for the new location */ 00312 lined = datad + i * wpld; 00313 if (i == 0) { /* do full histo */ 00314 for (k = 0; k < hf; k++) { 00315 linet = datat + (i + k) * wplt; 00316 for (m = 0; m < wf; m++) { 00317 val = GET_DATA_BYTE(linet, j + m); 00318 histo[val]++; 00319 histo16[val >> 4]++; 00320 } 00321 } 00322 } else { /* incremental update */ 00323 linet = datat + (i - 1) * wplt; 00324 for (m = 0; m < wf; m++) { /* remove top line */ 00325 val = GET_DATA_BYTE(linet, j + m); 00326 histo[val]--; 00327 histo16[val >> 4]--; 00328 } 00329 linet = datat + (i + hf - 1) * wplt; 00330 for (m = 0; m < wf; m++) { /* add bottom line */ 00331 val = GET_DATA_BYTE(linet, j + m); 00332 histo[val]++; 00333 histo16[val >> 4]++; 00334 } 00335 } 00336 00337 /* Find the rank value */ 00338 sum = 0; 00339 for (n = 0; n < 16; n++) { /* search over coarse histo */ 00340 sum += histo16[n]; 00341 if (sum > rankloc) { 00342 sum -= histo16[n]; 00343 break; 00344 } 00345 } 00346 k = 16 * n; /* starting value in fine histo */ 00347 for (m = 0; m < 16; m++) { 00348 sum += histo[k]; 00349 if (sum > rankloc) { 00350 SET_DATA_BYTE(lined, j, k); 00351 break; 00352 } 00353 k++; 00354 } 00355 } 00356 } 00357 } else { /* wf >= hf */ 00358 for (i = 0; i < h; i++) { /* column-major */ 00359 /* Start each row with clean histogram arrays. */ 00360 for (n = 0; n < 256; n++) 00361 histo[n] = 0; 00362 for (n = 0; n < 16; n++) 00363 histo16[n] = 0; 00364 lined = datad + i * wpld; 00365 for (j = 0; j < w; j++) { /* fast scan on rows */ 00366 /* Update the histos for the new location */ 00367 if (j == 0) { /* do full histo */ 00368 for (k = 0; k < hf; k++) { 00369 linet = datat + (i + k) * wplt; 00370 for (m = 0; m < wf; m++) { 00371 val = GET_DATA_BYTE(linet, j + m); 00372 histo[val]++; 00373 histo16[val >> 4]++; 00374 } 00375 } 00376 } else { /* incremental update at left and right sides */ 00377 for (k = 0; k < hf; k++) { 00378 linet = datat + (i + k) * wplt; 00379 val = GET_DATA_BYTE(linet, j - 1); 00380 histo[val]--; 00381 histo16[val >> 4]--; 00382 val = GET_DATA_BYTE(linet, j + wf - 1); 00383 histo[val]++; 00384 histo16[val >> 4]++; 00385 } 00386 } 00387 00388 /* Find the rank value */ 00389 sum = 0; 00390 for (n = 0; n < 16; n++) { /* search over coarse histo */ 00391 sum += histo16[n]; 00392 if (sum > rankloc) { 00393 sum -= histo16[n]; 00394 break; 00395 } 00396 } 00397 k = 16 * n; /* starting value in fine histo */ 00398 for (m = 0; m < 16; m++) { 00399 sum += histo[k]; 00400 if (sum > rankloc) { 00401 SET_DATA_BYTE(lined, j, k); 00402 break; 00403 } 00404 k++; 00405 } 00406 } 00407 } 00408 } 00409 00410 pixDestroy(&pixt); 00411 FREE(histo); 00412 FREE(histo16); 00413 return pixd; 00414 } 00415 00416 00417 /*----------------------------------------------------------------------* 00418 * Median filter * 00419 *----------------------------------------------------------------------*/ 00420 /*! 00421 * pixMedianFilter() 00422 * 00423 * Input: pixs (8 or 32 bpp; no colormap) 00424 * wf, hf (width and height of filter; each is >= 1) 00425 * Return: pixd (of median values), or null on error 00426 */ 00427 PIX * 00428 pixMedianFilter(PIX *pixs, 00429 l_int32 wf, 00430 l_int32 hf) 00431 { 00432 PROCNAME("pixMedianFilter"); 00433 00434 if (!pixs) 00435 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00436 return pixRankFilter(pixs, wf, hf, 0.5); 00437 } 00438 00439