Leptonica 1.68
C Image Processing Library

rank.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  *  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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines