1 pixBackgroundNormSimple

函数功能:自适应背影标准化

位置:adampmap.c

/*------------------------------------------------------------------*
* Adaptive background normalization *
*------------------------------------------------------------------*/
/*!
* pixBackgroundNormSimple()
*
* Input: pixs (8 bpp grayscale or 32 bpp rgb)
* pixim (<optional> 1 bpp 'image' mask; can be null)
* pixg (<optional> 8 bpp grayscale version; can be null)
* Return: pixd (8 bpp or 32 bpp rgb), or null on error
*
* Notes:
* (1) This is a simplified interface to pixBackgroundNorm(),
* where seven parameters are defaulted.
* (2) The input image is either grayscale or rgb.
* (3) See pixBackgroundNorm() for usage and function.
*/
PIX *
pixBackgroundNormSimple(PIX *pixs,
PIX *pixim,
PIX *pixg)
{
return pixBackgroundNorm(pixs, pixim, pixg,
DEFAULT_TILE_WIDTH, DEFAULT_TILE_HEIGHT,
DEFAULT_FG_THRESHOLD, DEFAULT_MIN_COUNT,
DEFAULT_BG_VAL, DEFAULT_X_SMOOTH_SIZE,
DEFAULT_Y_SMOOTH_SIZE);
}

由代码可知,此函数是pixBackgroundNorm的简化接口,参数已经由默认设置。默认参数为:

    /* Default input parameters for pixBackgroundNormSimple()
* Note:
* (1) mincount must never exceed the tile area (width * height)
* (2) bgval must be sufficiently below 255 to avoid accidental
* saturation; otherwise it should be large to avoid
* shrinking the dynamic range
* (3) results should otherwise not be sensitive to these values
*/
static const l_int32 DEFAULT_TILE_WIDTH = ;
static const l_int32 DEFAULT_TILE_HEIGHT = ;
static const l_int32 DEFAULT_FG_THRESHOLD = ;
static const l_int32 DEFAULT_MIN_COUNT = ;
static const l_int32 DEFAULT_BG_VAL = ;
static const l_int32 DEFAULT_X_SMOOTH_SIZE = ;
static const l_int32 DEFAULT_Y_SMOOTH_SIZE = ;

2 pixBackgroundNorm

函数位置:adaptmap.c

/*!
* pixBackgroundNorm()
*
* Input: pixs (8 bpp grayscale or 32 bpp rgb)
* pixim (<optional> 1 bpp 'image' mask; can be null)
* pixg (<optional> 8 bpp grayscale version; can be null)
* sx, sy (tile size in pixels)
* thresh (threshold for determining foreground)
* mincount (min threshold on counts in a tile)
* bgval (target bg val; typ. > 128)
* smoothx (half-width of block convolution kernel width)
* smoothy (half-width of block convolution kernel height)
* Return: pixd (8 bpp or 32 bpp rgb), or null on error
*
* Notes:
* (1) This is a top-level interface for normalizing the image intensity
* by mapping the image so that the background is near the input
* value 'bgval'.
* (2) The input image is either grayscale or rgb.
* (3) For each component in the input image, the background value
* in each tile is estimated using the values in the tile that
* are not part of the foreground, where the foreground is
* determined by the input 'thresh' argument.
* (4) An optional binary mask can be specified, with the foreground
* pixels typically over image regions. The resulting background
* map values will be determined by surrounding pixels that are
* not under the mask foreground. The origin (0,0) of this mask
* is assumed to be aligned with the origin of the input image.
* This binary mask must not fully cover pixs, because then there
* will be no pixels in the input image available to compute
* the background.
* (5) An optional grayscale version of the input pixs can be supplied.
* The only reason to do this is if the input is RGB and this
* grayscale version can be used elsewhere. If the input is RGB
* and this is not supplied, it is made internally using only
* the green component, and destroyed after use.
* (6) The dimensions of the pixel tile (sx, sy) give the amount by
* by which the map is reduced in size from the input image.
* (7) The threshold is used to binarize the input image, in order to
* locate the foreground components. If this is set too low,
* some actual foreground may be used to determine the maps;
* if set too high, there may not be enough background
* to determine the map values accurately. Typically, it's
* better to err by setting the threshold too high.
* (8) A 'mincount' threshold is a minimum count of pixels in a
* tile for which a background reading is made, in order for that
* pixel in the map to be valid. This number should perhaps be
* at least 1/3 the size of the tile.
* (9) A 'bgval' target background value for the normalized image. This
* should be at least 128. If set too close to 255, some
* clipping will occur in the result.
* (10) Two factors, 'smoothx' and 'smoothy', are input for smoothing
* the map. Each low-pass filter kernel dimension is
* is 2 * (smoothing factor) + 1, so a
* value of 0 means no smoothing. A value of 1 or 2 is recommended.
*/
PIX *
pixBackgroundNorm(PIX *pixs,
PIX *pixim,
PIX *pixg,
l_int32 sx,
l_int32 sy,
l_int32 thresh,
l_int32 mincount,
l_int32 bgval,
l_int32 smoothx,
l_int32 smoothy)
{
l_int32 d, allfg;
PIX *pixm, *pixmi, *pixd;
PIX *pixmr, *pixmg, *pixmb, *pixmri, *pixmgi, *pixmbi; PROCNAME("pixBackgroundNorm"); if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
d = pixGetDepth(pixs);
if (d != && d != )
return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", procName, NULL);
if (sx < || sy < )
return (PIX *)ERROR_PTR("sx and sy must be >= 4", procName, NULL);
if (mincount > sx * sy) {
L_WARNING("mincount too large for tile size\n", procName);
mincount = (sx * sy) / ;
} /* If pixim exists, verify that it is not all foreground. */
if (pixim) {
pixInvert(pixim, pixim);
pixZero(pixim, &allfg);
pixInvert(pixim, pixim);
if (allfg)
return (PIX *)ERROR_PTR("pixim all foreground", procName, NULL);
} pixd = NULL;
if (d == ) {
pixm = NULL;
pixGetBackgroundGrayMap(pixs, pixim, sx, sy, thresh, mincount, &pixm);
if (!pixm) {
L_WARNING("map not made; return a copy of the source\n", procName);
return pixCopy(NULL, pixs);
} pixmi = pixGetInvBackgroundMap(pixm, bgval, smoothx, smoothy);
if (!pixmi)
ERROR_PTR("pixmi not made", procName, NULL);
else
pixd = pixApplyInvBackgroundGrayMap(pixs, pixmi, sx, sy); pixDestroy(&pixm);
pixDestroy(&pixmi);
}
else {
pixmr = pixmg = pixmb = NULL;
pixGetBackgroundRGBMap(pixs, pixim, pixg, sx, sy, thresh,
mincount, &pixmr, &pixmg, &pixmb);
if (!pixmr || !pixmg || !pixmb) {
pixDestroy(&pixmr);
pixDestroy(&pixmg);
pixDestroy(&pixmb);
L_WARNING("map not made; return a copy of the source\n", procName);
return pixCopy(NULL, pixs);
} pixmri = pixGetInvBackgroundMap(pixmr, bgval, smoothx, smoothy);
pixmgi = pixGetInvBackgroundMap(pixmg, bgval, smoothx, smoothy);
pixmbi = pixGetInvBackgroundMap(pixmb, bgval, smoothx, smoothy);
if (!pixmri || !pixmgi || !pixmbi)
ERROR_PTR("not all pixm*i are made", procName, NULL);
else
pixd = pixApplyInvBackgroundRGBMap(pixs, pixmri, pixmgi, pixmbi,
sx, sy); pixDestroy(&pixmr);
pixDestroy(&pixmg);
pixDestroy(&pixmb);
pixDestroy(&pixmri);
pixDestroy(&pixmgi);
pixDestroy(&pixmbi);
} if (!pixd)
ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
return pixd;
}

3 pixGetBackgroundGrayMap

函数功能:局部背景测量

函数位置:adaptmap.c

/*------------------------------------------------------------------*
* Measurement of local background *
*------------------------------------------------------------------*/
/*!
* pixGetBackgroundGrayMap()
*
* Input: pixs (8 bpp grayscale; not cmapped)
* pixim (<optional> 1 bpp 'image' mask; can be null; it
* should not have all foreground pixels)
* sx, sy (tile size in pixels)
* thresh (threshold for determining foreground)
* mincount (min threshold on counts in a tile)
* &pixd (<return> 8 bpp grayscale map)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) The background is measured in regions that don't have
* images. It is then propagated into the image regions,
* and finally smoothed in each image region.
*/
l_int32
pixGetBackgroundGrayMap(PIX *pixs,
PIX *pixim,
l_int32 sx,
l_int32 sy,
l_int32 thresh,
l_int32 mincount,
PIX **ppixd)
{
l_int32 w, h, wd, hd, wim, him, wpls, wplim, wpld, wplf;
l_int32 xim, yim, delx, nx, ny, i, j, k, m;
l_int32 count, sum, val8;
l_int32 empty, fgpixels;
l_uint32 *datas, *dataim, *datad, *dataf, *lines, *lineim, *lined, *linef;
l_float32 scalex, scaley;
PIX *pixd, *piximi, *pixb, *pixf, *pixims; PROCNAME("pixGetBackgroundGrayMap"); if (!ppixd)
return ERROR_INT("&pixd not defined", procName, );
*ppixd = NULL;
if (!pixs || pixGetDepth(pixs) != )
return ERROR_INT("pixs not defined or not 8 bpp", procName, );
if (pixGetColormap(pixs))
return ERROR_INT("pixs is colormapped", procName, );
if (pixim && pixGetDepth(pixim) != )
return ERROR_INT("pixim not 1 bpp", procName, );
if (sx < || sy < )
return ERROR_INT("sx and sy must be >= 4", procName, );
if (mincount > sx * sy) {
L_WARNING("mincount too large for tile size\n", procName);
mincount = (sx * sy) / ;
} /* Evaluate the 'image' mask, pixim, and make sure
* it is not all fg. */
fgpixels = ; /* boolean for existence of fg pixels in the image mask. */
if (pixim) {
piximi = pixInvert(NULL, pixim); /* set non-'image' pixels to 1 */
pixZero(piximi, &empty);
pixDestroy(&piximi);
if (empty)
return ERROR_INT("pixim all fg; no background", procName, );
pixZero(pixim, &empty);
if (!empty) /* there are fg pixels in pixim */
fgpixels = ;
} /* Generate the foreground mask, pixf, which is at
* full resolution. These pixels will be ignored when
* computing the background values. */
pixb = pixThresholdToBinary(pixs, thresh);
pixf = pixMorphSequence(pixb, "d7.1 + d1.7", );
pixDestroy(&pixb); /* ------------- Set up the output map pixd --------------- */
/* Generate pixd, which is reduced by the factors (sx, sy). */
w = pixGetWidth(pixs);
h = pixGetHeight(pixs);
wd = (w + sx - ) / sx;
hd = (h + sy - ) / sy;
pixd = pixCreate(wd, hd, ); /* Note: we only compute map values in tiles that are complete.
* In general, tiles at right and bottom edges will not be
* complete, and we must fill them in later. */
nx = w / sx;
ny = h / sy;
wpls = pixGetWpl(pixs);
datas = pixGetData(pixs);
wpld = pixGetWpl(pixd);
datad = pixGetData(pixd);
wplf = pixGetWpl(pixf);
dataf = pixGetData(pixf);
for (i = ; i < ny; i++) {
lines = datas + sy * i * wpls;
linef = dataf + sy * i * wplf;
lined = datad + i * wpld;
for (j = ; j < nx; j++) {
delx = j * sx;
sum = ;
count = ;
for (k = ; k < sy; k++) {
for (m = ; m < sx; m++) {
if (GET_DATA_BIT(linef + k * wplf, delx + m) == ) {
sum += GET_DATA_BYTE(lines + k * wpls, delx + m);
count++;
}
}
}
if (count >= mincount) {
val8 = sum / count;
SET_DATA_BYTE(lined, j, val8);
}
}
}
pixDestroy(&pixf); /* If there is an optional mask with fg pixels, erase the previous
* calculation for the corresponding map pixels, setting the
* map values to 0. Then, when all the map holes are filled,
* these erased pixels will be set by the surrounding map values.
*
* The calculation here is relatively efficient: for each pixel
* in pixd (which corresponds to a tile of mask pixels in pixim)
* we look only at the pixel in pixim that is at the center
* of the tile. If the mask pixel is ON, we reset the map
* pixel in pixd to 0, so that it can later be filled in. */
pixims = NULL;
if (pixim && fgpixels) {
wim = pixGetWidth(pixim);
him = pixGetHeight(pixim);
dataim = pixGetData(pixim);
wplim = pixGetWpl(pixim);
for (i = ; i < ny; i++) {
yim = i * sy + sy / ;
if (yim >= him)
break;
lineim = dataim + yim * wplim;
for (j = ; j < nx; j++) {
xim = j * sx + sx / ;
if (xim >= wim)
break;
if (GET_DATA_BIT(lineim, xim))
pixSetPixel(pixd, j, i, );
}
}
} /* Fill all the holes in the map. */
if (pixFillMapHoles(pixd, nx, ny, L_FILL_BLACK)) {
pixDestroy(&pixd);
L_WARNING("can't make the map\n", procName);
return ;
} /* Finally, for each connected region corresponding to the
* 'image' mask, reset all pixels to their average value.
* Each of these components represents an image (or part of one)
* in the input, and this smooths the background values
* in each of these regions. */
if (pixim && fgpixels) {
scalex = . / (l_float32)sx;
scaley = . / (l_float32)sy;
pixims = pixScaleBySampling(pixim, scalex, scaley);
pixSmoothConnectedRegions(pixd, pixims, );
pixDestroy(&pixims);
} *ppixd = pixd;
pixCopyResolution(*ppixd, pixs);
return ;
}

4 pixThresholdToBinary

函数功能:

位置:grayquant.c

/*------------------------------------------------------------------*
* Simple (pixelwise) binarization with fixed threshold *
*------------------------------------------------------------------*/
/*!
* pixThresholdToBinary()
*
* Input: pixs (4 or 8 bpp)
* threshold value
* Return: pixd (1 bpp), or null on error
*
* Notes:
* (1) If the source pixel is less than the threshold value,
* the dest will be 1; otherwise, it will be 0
*/
PIX *
pixThresholdToBinary(PIX *pixs,
l_int32 thresh)
{
l_int32 d, w, h, wplt, wpld;
l_uint32 *datat, *datad;
PIX *pixt, *pixd; PROCNAME("pixThresholdToBinary"); if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
pixGetDimensions(pixs, &w, &h, &d);
if (d != && d != )
return (PIX *)ERROR_PTR("pixs must be 4 or 8 bpp", procName, NULL);
if (thresh < )
return (PIX *)ERROR_PTR("thresh must be non-negative", procName, NULL);
if (d == && thresh > )
return (PIX *)ERROR_PTR("4 bpp thresh not in {0-16}", procName, NULL);
if (d == && thresh > )
return (PIX *)ERROR_PTR("8 bpp thresh not in {0-256}", procName, NULL); if ((pixd = pixCreate(w, h, )) == NULL)
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
pixCopyResolution(pixd, pixs);
datad = pixGetData(pixd);
wpld = pixGetWpl(pixd); /* Remove colormap if it exists. If there is a colormap,
* pixt will be 8 bpp regardless of the depth of pixs. */
pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
datat = pixGetData(pixt);
wplt = pixGetWpl(pixt);
if (pixGetColormap(pixs) && d == ) { /* promoted to 8 bpp */
d = ;
thresh *= ;
} thresholdToBinaryLow(datad, w, h, wpld, datat, d, wplt, thresh);
pixDestroy(&pixt);
return pixd;
}

5 pixMorphSequence

函数功能:

位置:morphseq.c

/*-------------------------------------------------------------------------*
* Run a sequence of binary rasterop morphological operations *
*-------------------------------------------------------------------------*/
/*!
* pixMorphSequence()
*
* Input: pixs
* sequence (string specifying sequence)
* dispsep (controls debug display of each result in the sequence:
* 0: no output
* > 0: gives horizontal separation in pixels between
* successive displays
* < 0: pdf output; abs(dispsep) is used for naming)
* Return: pixd, or null on error
*
* Notes:
* (1) This does rasterop morphology on binary images.
* (2) This runs a pipeline of operations; no branching is allowed.
* (3) This only uses brick Sels, which are created on the fly.
* In the future this will be generalized to extract Sels from
* a Sela by name.
* (4) A new image is always produced; the input image is not changed.
* (5) This contains an interpreter, allowing sequences to be
* generated and run.
* (6) The format of the sequence string is defined below.
* (7) In addition to morphological operations, rank order reduction
* and replicated expansion allow operations to take place
* downscaled by a power of 2.
* (8) Intermediate results can optionally be displayed.
* (9) Thanks to Dar-Shyang Lee, who had the idea for this and
* built the first implementation.
* (10) The sequence string is formatted as follows:
* - An arbitrary number of operations, each separated
* by a '+' character. White space is ignored.
* - Each operation begins with a case-independent character
* specifying the operation:
* d or D (dilation)
* e or E (erosion)
* o or O (opening)
* c or C (closing)
* r or R (rank binary reduction)
* x or X (replicative binary expansion)
* b or B (add a border of 0 pixels of this size)
* - The args to the morphological operations are bricks of hits,
* and are formatted as a.b, where a and b are horizontal and
* vertical dimensions, rsp.
* - The args to the reduction are a sequence of up to 4 integers,
* each from 1 to 4.
* - The arg to the expansion is a power of two, in the set
* {2, 4, 8, 16}.
* (11) An example valid sequence is:
* "b32 + o1.3 + C3.1 + r23 + e2.2 + D3.2 + X4"
* In this example, the following operation sequence is carried out:
* * b32: Add a 32 pixel border around the input image
* * o1.3: Opening with vert sel of length 3 (e.g., 1 x 3)
* * C3.1: Closing with horiz sel of length 3 (e.g., 3 x 1)
* * r23: Two successive 2x2 reductions with rank 2 in the first
* and rank 3 in the second. The result is a 4x reduced pix.
* * e2.2: Erosion with a 2x2 sel (origin will be at x,y: 0,0)
* * d3.2: Dilation with a 3x2 sel (origin will be at x,y: 1,0)
* * X4: 4x replicative expansion, back to original resolution
* (12) The safe closing is used. However, if you implement a
* closing as separable dilations followed by separable erosions,
* it will not be safe. For that situation, you need to add
* a sufficiently large border as the first operation in
* the sequence. This will be removed automatically at the
* end. There are two cautions:
* - When computing what is sufficient, remember that if
* reductions are carried out, the border is also reduced.
* - The border is removed at the end, so if a border is
* added at the beginning, the result must be at the
* same resolution as the input!
*/
PIX *
pixMorphSequence(PIX *pixs,
const char *sequence,
l_int32 dispsep)
{
char *rawop, *op, *fname;
char buf[];
l_int32 nops, i, j, nred, fact, w, h, x, y, border, pdfout;
l_int32 level[];
PIX *pixt1, *pixt2;
PIXA *pixa;
SARRAY *sa; PROCNAME("pixMorphSequence"); if (!pixs)
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
if (!sequence)
return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); /* Split sequence into individual operations */
sa = sarrayCreate();
sarraySplitString(sa, sequence, "+");
nops = sarrayGetCount(sa);
pdfout = (dispsep < ) ? : ; if (!morphSequenceVerify(sa)) {
sarrayDestroy(&sa);
return (PIX *)ERROR_PTR("sequence not valid", procName, NULL);
} /* Parse and operate */
pixa = NULL;
if (pdfout) {
pixa = pixaCreate();
pixaAddPix(pixa, pixs, L_CLONE);
snprintf(buf, sizeof(buf), "/tmp/seq_output_%d.pdf", L_ABS(dispsep));
fname = genPathname(buf, NULL);
}
border = ;
pixt1 = pixCopy(NULL, pixs);
pixt2 = NULL;
x = y = ;
for (i = ; i < nops; i++) {
rawop = sarrayGetString(sa, i, );
op = stringRemoveChars(rawop, " \n\t");
switch (op[])
{
case 'd':
case 'D':
sscanf(&op[], "%d.%d", &w, &h);
pixt2 = pixDilateBrick(NULL, pixt1, w, h);
pixSwapAndDestroy(&pixt1, &pixt2);
break;
case 'e':
case 'E':
sscanf(&op[], "%d.%d", &w, &h);
pixt2 = pixErodeBrick(NULL, pixt1, w, h);
pixSwapAndDestroy(&pixt1, &pixt2);
break;
case 'o':
case 'O':
sscanf(&op[], "%d.%d", &w, &h);
pixOpenBrick(pixt1, pixt1, w, h);
break;
case 'c':
case 'C':
sscanf(&op[], "%d.%d", &w, &h);
pixCloseSafeBrick(pixt1, pixt1, w, h);
break;
case 'r':
case 'R':
nred = strlen(op) - ;
for (j = ; j < nred; j++)
level[j] = op[j + ] - '';
for (j = nred; j < ; j++)
level[j] = ;
pixt2 = pixReduceRankBinaryCascade(pixt1, level[], level[],
level[], level[]);
pixSwapAndDestroy(&pixt1, &pixt2);
break;
case 'x':
case 'X':
sscanf(&op[], "%d", &fact);
pixt2 = pixExpandReplicate(pixt1, fact);
pixSwapAndDestroy(&pixt1, &pixt2);
break;
case 'b':
case 'B':
sscanf(&op[], "%d", &border);
pixt2 = pixAddBorder(pixt1, border, );
pixSwapAndDestroy(&pixt1, &pixt2);
break;
default:
/* All invalid ops are caught in the first pass */
break;
}
FREE(op); /* Debug output */
if (dispsep > ) {
pixDisplay(pixt1, x, y);
x += dispsep;
}
if (pdfout)
pixaAddPix(pixa, pixt1, L_COPY);
}
if (border > ) {
pixt2 = pixRemoveBorder(pixt1, border);
pixSwapAndDestroy(&pixt1, &pixt2);
} if (pdfout) {
pixaConvertToPdf(pixa, , 1.0, L_FLATE_ENCODE, , fname, fname);
FREE(fname);
pixaDestroy(&pixa);
} sarrayDestroy(&sa);
return pixt1;
}
05-06 07:11