mirror of http://192.168.1.51:8099/lmh188/twain3.0
812 lines
26 KiB
C
812 lines
26 KiB
C
/*====================================================================*
|
|
- Copyright (C) 2001 Leptonica. All rights reserved.
|
|
-
|
|
- Redistribution and use in source and binary forms, with or without
|
|
- modification, are permitted provided that the following conditions
|
|
- are met:
|
|
- 1. Redistributions of source code must retain the above copyright
|
|
- notice, this list of conditions and the following disclaimer.
|
|
- 2. Redistributions in binary form must reproduce the above
|
|
- copyright notice, this list of conditions and the following
|
|
- disclaimer in the documentation and/or other materials
|
|
- provided with the distribution.
|
|
-
|
|
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
|
|
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*====================================================================*/
|
|
|
|
/*!
|
|
* \file runlength.c
|
|
* <pre>
|
|
*
|
|
* Label pixels by membership in runs
|
|
* PIX *pixStrokeWidthTransform()
|
|
* static PIX *pixFindMinRunsOrthogonal()
|
|
* PIX *pixRunlengthTransform()
|
|
*
|
|
* Find runs along horizontal and vertical lines
|
|
* l_int32 pixFindHorizontalRuns()
|
|
* l_int32 pixFindVerticalRuns()
|
|
*
|
|
* Find max runs along horizontal and vertical lines
|
|
* l_int32 pixFindMaxRuns()
|
|
* l_int32 pixFindMaxHorizontalRunOnLine()
|
|
* l_int32 pixFindMaxVerticalRunOnLine()
|
|
*
|
|
* Compute runlength-to-membership transform on a line
|
|
* l_int32 runlengthMembershipOnLine()
|
|
*
|
|
* Make byte position LUT
|
|
* l_int32 makeMSBitLocTab()
|
|
*
|
|
* Here we're handling runs of either black or white pixels on 1 bpp
|
|
* images. The directions of the runs in the stroke width transform
|
|
* are selectable from given sets of angles. Most of the other runs
|
|
* are oriented either horizontally along the raster lines or
|
|
* vertically along pixel columns.
|
|
* </pre>
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "allheaders.h"
|
|
|
|
static PIX *pixFindMinRunsOrthogonal(PIX *pixs, l_float32 angle, l_int32 depth);
|
|
|
|
|
|
/*-----------------------------------------------------------------------*
|
|
* Label pixels by membership in runs *
|
|
*-----------------------------------------------------------------------*/
|
|
/*!
|
|
* \brief pixStrokeWidthTransform()
|
|
*
|
|
* \param[in] pixs 1 bpp
|
|
* \param[in] color 0 for white runs, 1 for black runs
|
|
* \param[in] depth of pixd: 8 or 16 bpp
|
|
* \param[in] nangles 2, 4, 6 or 8
|
|
* \return pixd 8 or 16 bpp, or NULL on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) The dest Pix is 8 or 16 bpp, with the pixel values
|
|
* equal to the stroke width in which it is a member.
|
|
* The values are clipped to the max pixel value if necessary.
|
|
* (2) %color determines if we're labelling white or black strokes.
|
|
* (3) A pixel that is not a member of the chosen color gets
|
|
* value 0; it belongs to a width of length 0 of the
|
|
* chosen color.
|
|
* (4) This chooses, for each dest pixel, the minimum of sets
|
|
* of runlengths through each pixel. Here are the sets:
|
|
* nangles increment set
|
|
* ------- --------- --------------------------------
|
|
* 2 90 {0, 90}
|
|
* 4 45 {0, 45, 90, 135}
|
|
* 6 30 {0, 30, 60, 90, 120, 150}
|
|
* 8 22.5 {0, 22.5, 45, 67.5, 90, 112.5, 135, 157.5}
|
|
* (5) Runtime scales linearly with (%nangles - 2).
|
|
* </pre>
|
|
*/
|
|
PIX *
|
|
pixStrokeWidthTransform(PIX *pixs,
|
|
l_int32 color,
|
|
l_int32 depth,
|
|
l_int32 nangles)
|
|
{
|
|
l_float32 angle, pi;
|
|
PIX *pixh, *pixv, *pixt, *pixg1, *pixg2, *pixg3, *pixg4;
|
|
|
|
PROCNAME("pixStrokeWidthTransform");
|
|
|
|
if (!pixs || pixGetDepth(pixs) != 1)
|
|
return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
|
|
if (depth != 8 && depth != 16)
|
|
return (PIX *)ERROR_PTR("depth must be 8 or 16 bpp", procName, NULL);
|
|
if (nangles != 2 && nangles != 4 && nangles != 6 && nangles != 8)
|
|
return (PIX *)ERROR_PTR("nangles not in {2,4,6,8}", procName, NULL);
|
|
|
|
/* Use fg runs for evaluation */
|
|
if (color == 0)
|
|
pixt = pixInvert(NULL, pixs);
|
|
else
|
|
pixt = pixClone(pixs);
|
|
|
|
/* Find min length at 0 and 90 degrees */
|
|
pixh = pixRunlengthTransform(pixt, 1, L_HORIZONTAL_RUNS, depth);
|
|
pixv = pixRunlengthTransform(pixt, 1, L_VERTICAL_RUNS, depth);
|
|
pixg1 = pixMinOrMax(NULL, pixh, pixv, L_CHOOSE_MIN);
|
|
pixDestroy(&pixh);
|
|
pixDestroy(&pixv);
|
|
|
|
pixg2 = pixg3 = pixg4 = NULL;
|
|
pi = 3.1415926535;
|
|
if (nangles == 4 || nangles == 8) {
|
|
/* Find min length at +45 and -45 degrees */
|
|
angle = pi / 4.0;
|
|
pixg2 = pixFindMinRunsOrthogonal(pixt, angle, depth);
|
|
}
|
|
|
|
if (nangles == 6) {
|
|
/* Find min length at +30 and -60 degrees */
|
|
angle = pi / 6.0;
|
|
pixg2 = pixFindMinRunsOrthogonal(pixt, angle, depth);
|
|
|
|
/* Find min length at +60 and -30 degrees */
|
|
angle = pi / 3.0;
|
|
pixg3 = pixFindMinRunsOrthogonal(pixt, angle, depth);
|
|
}
|
|
|
|
if (nangles == 8) {
|
|
/* Find min length at +22.5 and -67.5 degrees */
|
|
angle = pi / 8.0;
|
|
pixg3 = pixFindMinRunsOrthogonal(pixt, angle, depth);
|
|
|
|
/* Find min length at +67.5 and -22.5 degrees */
|
|
angle = 3.0 * pi / 8.0;
|
|
pixg4 = pixFindMinRunsOrthogonal(pixt, angle, depth);
|
|
}
|
|
pixDestroy(&pixt);
|
|
|
|
if (nangles > 2)
|
|
pixMinOrMax(pixg1, pixg1, pixg2, L_CHOOSE_MIN);
|
|
if (nangles > 4)
|
|
pixMinOrMax(pixg1, pixg1, pixg3, L_CHOOSE_MIN);
|
|
if (nangles > 6)
|
|
pixMinOrMax(pixg1, pixg1, pixg4, L_CHOOSE_MIN);
|
|
pixDestroy(&pixg2);
|
|
pixDestroy(&pixg3);
|
|
pixDestroy(&pixg4);
|
|
return pixg1;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief pixFindMinRunsOrthogonal()
|
|
*
|
|
* \param[in] pixs 1 bpp
|
|
* \param[in] angle in radians
|
|
* \param[in] depth of pixd: 8 or 16 bpp
|
|
* \return pixd 8 or 16 bpp, or NULL on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This computes, for each fg pixel in pixs, the minimum of
|
|
* the runlengths going through that pixel in two orthogonal
|
|
* directions: at %angle and at (90 + %angle).
|
|
* (2) We use rotation by shear because the forward and backward
|
|
* rotations by the same angle are exact inverse operations.
|
|
* As a result, the nonzero pixels in pixd correspond exactly
|
|
* to the fg pixels in pixs. This is not the case with
|
|
* sampled rotation, due to spatial quantization. Nevertheless,
|
|
* the result suffers from lack of exact correspondence
|
|
* between original and rotated pixels, also due to spatial
|
|
* quantization, causing some boundary pixels to be
|
|
* shifted from bg to fg or v.v.
|
|
* </pre>
|
|
*/
|
|
static PIX *
|
|
pixFindMinRunsOrthogonal(PIX *pixs,
|
|
l_float32 angle,
|
|
l_int32 depth)
|
|
{
|
|
l_int32 w, h, diag, xoff, yoff;
|
|
PIX *pixb, *pixr, *pixh, *pixv, *pixg1, *pixg2, *pixd;
|
|
BOX *box;
|
|
|
|
PROCNAME("pixFindMinRunsOrthogonal");
|
|
|
|
if (!pixs || pixGetDepth(pixs) != 1)
|
|
return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
|
|
|
|
/* Rasterop into the center of a sufficiently large image
|
|
* so we don't lose pixels for any rotation angle. */
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
diag = (l_int32)(sqrt((l_float64)(w * w + h * h)) + 2.5);
|
|
xoff = (diag - w) / 2;
|
|
yoff = (diag - h) / 2;
|
|
pixb = pixCreate(diag, diag, 1);
|
|
pixRasterop(pixb, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0);
|
|
|
|
/* Rotate about the 'center', get the min of orthogonal transforms,
|
|
* rotate back, and crop the part corresponding to pixs. */
|
|
pixr = pixRotateShear(pixb, diag / 2, diag / 2, angle, L_BRING_IN_WHITE);
|
|
pixh = pixRunlengthTransform(pixr, 1, L_HORIZONTAL_RUNS, depth);
|
|
pixv = pixRunlengthTransform(pixr, 1, L_VERTICAL_RUNS, depth);
|
|
pixg1 = pixMinOrMax(NULL, pixh, pixv, L_CHOOSE_MIN);
|
|
pixg2 = pixRotateShear(pixg1, diag / 2, diag / 2, -angle, L_BRING_IN_WHITE);
|
|
box = boxCreate(xoff, yoff, w, h);
|
|
pixd = pixClipRectangle(pixg2, box, NULL);
|
|
|
|
pixDestroy(&pixb);
|
|
pixDestroy(&pixr);
|
|
pixDestroy(&pixh);
|
|
pixDestroy(&pixv);
|
|
pixDestroy(&pixg1);
|
|
pixDestroy(&pixg2);
|
|
boxDestroy(&box);
|
|
return pixd;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief pixRunlengthTransform()
|
|
*
|
|
* \param[in] pixs 1 bpp
|
|
* \param[in] color 0 for white runs, 1 for black runs
|
|
* \param[in] direction L_HORIZONTAL_RUNS, L_VERTICAL_RUNS
|
|
* \param[in] depth 8 or 16 bpp
|
|
* \return pixd 8 or 16 bpp, or NULL on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) The dest Pix is 8 or 16 bpp, with the pixel values
|
|
* equal to the runlength in which it is a member.
|
|
* The length is clipped to the max pixel value if necessary.
|
|
* (2) %color determines if we're labelling white or black runs.
|
|
* (3) A pixel that is not a member of the chosen color gets
|
|
* value 0; it belongs to a run of length 0 of the
|
|
* chosen color.
|
|
* (4) To convert for maximum dynamic range, either linear or
|
|
* log, use pixMaxDynamicRange().
|
|
* </pre>
|
|
*/
|
|
PIX *
|
|
pixRunlengthTransform(PIX *pixs,
|
|
l_int32 color,
|
|
l_int32 direction,
|
|
l_int32 depth)
|
|
{
|
|
l_int32 i, j, w, h, wpld, bufsize, maxsize, n;
|
|
l_int32 *start, *end, *buffer;
|
|
l_uint32 *datad, *lined;
|
|
PIX *pixt, *pixd;
|
|
|
|
PROCNAME("pixRunlengthTransform");
|
|
|
|
if (!pixs)
|
|
return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
|
|
if (pixGetDepth(pixs) != 1)
|
|
return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
|
|
if (depth != 8 && depth != 16)
|
|
return (PIX *)ERROR_PTR("depth must be 8 or 16 bpp", procName, NULL);
|
|
|
|
pixGetDimensions(pixs, &w, &h, NULL);
|
|
if (direction == L_HORIZONTAL_RUNS)
|
|
maxsize = 1 + w / 2;
|
|
else if (direction == L_VERTICAL_RUNS)
|
|
maxsize = 1 + h / 2;
|
|
else
|
|
return (PIX *)ERROR_PTR("invalid direction", procName, NULL);
|
|
bufsize = L_MAX(w, h);
|
|
if (bufsize > 1000000) {
|
|
L_ERROR("largest image dimension = %d; too big\n", procName, bufsize);
|
|
return NULL;
|
|
}
|
|
|
|
if ((pixd = pixCreate(w, h, depth)) == NULL)
|
|
return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
|
|
datad = pixGetData(pixd);
|
|
wpld = pixGetWpl(pixd);
|
|
|
|
start = (l_int32 *)LEPT_CALLOC(maxsize, sizeof(l_int32));
|
|
end = (l_int32 *)LEPT_CALLOC(maxsize, sizeof(l_int32));
|
|
buffer = (l_int32 *)LEPT_CALLOC(bufsize, sizeof(l_int32));
|
|
|
|
/* Use fg runs for evaluation */
|
|
if (color == 0)
|
|
pixt = pixInvert(NULL, pixs);
|
|
else
|
|
pixt = pixClone(pixs);
|
|
|
|
if (direction == L_HORIZONTAL_RUNS) {
|
|
for (i = 0; i < h; i++) {
|
|
pixFindHorizontalRuns(pixt, i, start, end, &n);
|
|
runlengthMembershipOnLine(buffer, w, depth, start, end, n);
|
|
lined = datad + i * wpld;
|
|
if (depth == 8) {
|
|
for (j = 0; j < w; j++)
|
|
SET_DATA_BYTE(lined, j, buffer[j]);
|
|
} else { /* depth == 16 */
|
|
for (j = 0; j < w; j++)
|
|
SET_DATA_TWO_BYTES(lined, j, buffer[j]);
|
|
}
|
|
}
|
|
} else { /* L_VERTICAL_RUNS */
|
|
for (j = 0; j < w; j++) {
|
|
pixFindVerticalRuns(pixt, j, start, end, &n);
|
|
runlengthMembershipOnLine(buffer, h, depth, start, end, n);
|
|
if (depth == 8) {
|
|
for (i = 0; i < h; i++) {
|
|
lined = datad + i * wpld;
|
|
SET_DATA_BYTE(lined, j, buffer[i]);
|
|
}
|
|
} else { /* depth == 16 */
|
|
for (i = 0; i < h; i++) {
|
|
lined = datad + i * wpld;
|
|
SET_DATA_TWO_BYTES(lined, j, buffer[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pixDestroy(&pixt);
|
|
LEPT_FREE(start);
|
|
LEPT_FREE(end);
|
|
LEPT_FREE(buffer);
|
|
return pixd;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*
|
|
* Find runs along horizontal and vertical lines *
|
|
*-----------------------------------------------------------------------*/
|
|
/*!
|
|
* \brief pixFindHorizontalRuns()
|
|
*
|
|
* \param[in] pix 1 bpp
|
|
* \param[in] y line to traverse
|
|
* \param[in] xstart returns array of start positions for fg runs
|
|
* \param[in] xend returns array of end positions for fg runs
|
|
* \param[out] pn the number of runs found
|
|
* \return 0 if OK; 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This finds foreground horizontal runs on a single scanline.
|
|
* (2) To find background runs, use pixInvert() before applying
|
|
* this function.
|
|
* (3) %xstart and %xend arrays are input. They should be
|
|
* of size w/2 + 1 to insure that they can hold
|
|
* the maximum number of runs in the raster line.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
pixFindHorizontalRuns(PIX *pix,
|
|
l_int32 y,
|
|
l_int32 *xstart,
|
|
l_int32 *xend,
|
|
l_int32 *pn)
|
|
{
|
|
l_int32 inrun; /* boolean */
|
|
l_int32 index, w, h, d, j, wpl, val;
|
|
l_uint32 *line;
|
|
|
|
PROCNAME("pixFindHorizontalRuns");
|
|
|
|
if (!pn)
|
|
return ERROR_INT("&n not defined", procName, 1);
|
|
*pn = 0;
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (d != 1)
|
|
return ERROR_INT("pix not 1 bpp", procName, 1);
|
|
if (y < 0 || y >= h)
|
|
return ERROR_INT("y not in [0 ... h - 1]", procName, 1);
|
|
if (!xstart)
|
|
return ERROR_INT("xstart not defined", procName, 1);
|
|
if (!xend)
|
|
return ERROR_INT("xend not defined", procName, 1);
|
|
|
|
wpl = pixGetWpl(pix);
|
|
line = pixGetData(pix) + y * wpl;
|
|
|
|
inrun = FALSE;
|
|
index = 0;
|
|
for (j = 0; j < w; j++) {
|
|
val = GET_DATA_BIT(line, j);
|
|
if (!inrun) {
|
|
if (val) {
|
|
xstart[index] = j;
|
|
inrun = TRUE;
|
|
}
|
|
} else {
|
|
if (!val) {
|
|
xend[index++] = j - 1;
|
|
inrun = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Finish last run if necessary */
|
|
if (inrun)
|
|
xend[index++] = w - 1;
|
|
|
|
*pn = index;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief pixFindVerticalRuns()
|
|
*
|
|
* \param[in] pix 1 bpp
|
|
* \param[in] x line to traverse
|
|
* \param[in] ystart returns array of start positions for fg runs
|
|
* \param[in] yend returns array of end positions for fg runs
|
|
* \param[out] pn the number of runs found
|
|
* \return 0 if OK; 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This finds foreground vertical runs on a single scanline.
|
|
* (2) To find background runs, use pixInvert() before applying
|
|
* this function.
|
|
* (3) %ystart and %yend arrays are input. They should be
|
|
* of size h/2 + 1 to insure that they can hold
|
|
* the maximum number of runs in the raster line.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
pixFindVerticalRuns(PIX *pix,
|
|
l_int32 x,
|
|
l_int32 *ystart,
|
|
l_int32 *yend,
|
|
l_int32 *pn)
|
|
{
|
|
l_int32 inrun; /* boolean */
|
|
l_int32 index, w, h, d, i, wpl, val;
|
|
l_uint32 *data, *line;
|
|
|
|
PROCNAME("pixFindVerticalRuns");
|
|
|
|
if (!pn)
|
|
return ERROR_INT("&n not defined", procName, 1);
|
|
*pn = 0;
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
pixGetDimensions(pix, &w, &h, &d);
|
|
if (d != 1)
|
|
return ERROR_INT("pix not 1 bpp", procName, 1);
|
|
if (x < 0 || x >= w)
|
|
return ERROR_INT("x not in [0 ... w - 1]", procName, 1);
|
|
if (!ystart)
|
|
return ERROR_INT("ystart not defined", procName, 1);
|
|
if (!yend)
|
|
return ERROR_INT("yend not defined", procName, 1);
|
|
|
|
wpl = pixGetWpl(pix);
|
|
data = pixGetData(pix);
|
|
|
|
inrun = FALSE;
|
|
index = 0;
|
|
for (i = 0; i < h; i++) {
|
|
line = data + i * wpl;
|
|
val = GET_DATA_BIT(line, x);
|
|
if (!inrun) {
|
|
if (val) {
|
|
ystart[index] = i;
|
|
inrun = TRUE;
|
|
}
|
|
} else {
|
|
if (!val) {
|
|
yend[index++] = i - 1;
|
|
inrun = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Finish last run if necessary */
|
|
if (inrun)
|
|
yend[index++] = h - 1;
|
|
|
|
*pn = index;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*
|
|
* Find max runs along horizontal and vertical lines *
|
|
*-----------------------------------------------------------------------*/
|
|
/*!
|
|
* \brief pixFindMaxRuns()
|
|
*
|
|
* \param[in] pix 1 bpp
|
|
* \param[in] direction L_HORIZONTAL_RUNS or L_VERTICAL_RUNS
|
|
* \param[out] pnastart [optional] start locations of longest runs
|
|
* \return na of lengths of runs, or NULL on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This finds the longest foreground runs by row or column
|
|
* (2) To find background runs, use pixInvert() before applying
|
|
* this function.
|
|
* </pre>
|
|
*/
|
|
NUMA *
|
|
pixFindMaxRuns(PIX *pix,
|
|
l_int32 direction,
|
|
NUMA **pnastart)
|
|
{
|
|
l_int32 w, h, i, start, size;
|
|
NUMA *nasize;
|
|
|
|
PROCNAME("pixFindMaxRuns");
|
|
|
|
if (pnastart) *pnastart = NULL;
|
|
if (direction != L_HORIZONTAL_RUNS && direction != L_VERTICAL_RUNS)
|
|
return (NUMA *)ERROR_PTR("direction invalid", procName, NULL);
|
|
if (!pix || pixGetDepth(pix) != 1)
|
|
return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL);
|
|
|
|
pixGetDimensions(pix, &w, &h, NULL);
|
|
nasize = numaCreate(w);
|
|
if (pnastart) *pnastart = numaCreate(w);
|
|
if (direction == L_HORIZONTAL_RUNS) {
|
|
for (i = 0; i < h; i++) {
|
|
pixFindMaxHorizontalRunOnLine(pix, i, &start, &size);
|
|
numaAddNumber(nasize, size);
|
|
if (pnastart) numaAddNumber(*pnastart, start);
|
|
}
|
|
} else { /* vertical scans */
|
|
for (i = 0; i < w; i++) {
|
|
pixFindMaxVerticalRunOnLine(pix, i, &start, &size);
|
|
numaAddNumber(nasize, size);
|
|
if (pnastart) numaAddNumber(*pnastart, start);
|
|
}
|
|
}
|
|
|
|
return nasize;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief pixFindMaxHorizontalRunOnLine()
|
|
*
|
|
* \param[in] pix 1 bpp
|
|
* \param[in] y line to traverse
|
|
* \param[out] pxstart [optional] start position
|
|
* \param[out] psize the size of the run
|
|
* \return 0 if OK; 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This finds the longest foreground horizontal run on a scanline.
|
|
* (2) To find background runs, use pixInvert() before applying
|
|
* this function.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
pixFindMaxHorizontalRunOnLine(PIX *pix,
|
|
l_int32 y,
|
|
l_int32 *pxstart,
|
|
l_int32 *psize)
|
|
{
|
|
l_int32 inrun; /* boolean */
|
|
l_int32 w, h, j, wpl, val, maxstart, maxsize, length, start;
|
|
l_uint32 *line;
|
|
|
|
PROCNAME("pixFindMaxHorizontalRunOnLine");
|
|
|
|
if (pxstart) *pxstart = 0;
|
|
if (!psize)
|
|
return ERROR_INT("&size not defined", procName, 1);
|
|
*psize = 0;
|
|
if (!pix || pixGetDepth(pix) != 1)
|
|
return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
|
|
pixGetDimensions(pix, &w, &h, NULL);
|
|
if (y < 0 || y >= h)
|
|
return ERROR_INT("y not in [0 ... h - 1]", procName, 1);
|
|
|
|
wpl = pixGetWpl(pix);
|
|
line = pixGetData(pix) + y * wpl;
|
|
inrun = FALSE;
|
|
start = 0;
|
|
maxstart = 0;
|
|
maxsize = 0;
|
|
for (j = 0; j < w; j++) {
|
|
val = GET_DATA_BIT(line, j);
|
|
if (!inrun) {
|
|
if (val) {
|
|
start = j;
|
|
inrun = TRUE;
|
|
}
|
|
} else if (!val) { /* run just ended */
|
|
length = j - start;
|
|
if (length > maxsize) {
|
|
maxsize = length;
|
|
maxstart = start;
|
|
}
|
|
inrun = FALSE;
|
|
}
|
|
}
|
|
|
|
if (inrun) { /* a run has continued to the end of the row */
|
|
length = j - start;
|
|
if (length > maxsize) {
|
|
maxsize = length;
|
|
maxstart = start;
|
|
}
|
|
}
|
|
if (pxstart) *pxstart = maxstart;
|
|
*psize = maxsize;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief pixFindMaxVerticalRunOnLine()
|
|
*
|
|
* \param[in] pix 1 bpp
|
|
* \param[in] x column to traverse
|
|
* \param[out] pystart [optional] start position
|
|
* \param[out] psize the size of the run
|
|
* \return 0 if OK; 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) This finds the longest foreground vertical run on a scanline.
|
|
* (2) To find background runs, use pixInvert() before applying
|
|
* this function.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
pixFindMaxVerticalRunOnLine(PIX *pix,
|
|
l_int32 x,
|
|
l_int32 *pystart,
|
|
l_int32 *psize)
|
|
{
|
|
l_int32 inrun; /* boolean */
|
|
l_int32 w, h, i, wpl, val, maxstart, maxsize, length, start;
|
|
l_uint32 *data, *line;
|
|
|
|
PROCNAME("pixFindMaxVerticalRunOnLine");
|
|
|
|
if (pystart) *pystart = 0;
|
|
if (!psize)
|
|
return ERROR_INT("&size not defined", procName, 1);
|
|
*psize = 0;
|
|
if (!pix || pixGetDepth(pix) != 1)
|
|
return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
|
|
pixGetDimensions(pix, &w, &h, NULL);
|
|
if (x < 0 || x >= w)
|
|
return ERROR_INT("x not in [0 ... w - 1]", procName, 1);
|
|
|
|
wpl = pixGetWpl(pix);
|
|
data = pixGetData(pix);
|
|
inrun = FALSE;
|
|
start = 0;
|
|
maxstart = 0;
|
|
maxsize = 0;
|
|
for (i = 0; i < h; i++) {
|
|
line = data + i * wpl;
|
|
val = GET_DATA_BIT(line, x);
|
|
if (!inrun) {
|
|
if (val) {
|
|
start = i;
|
|
inrun = TRUE;
|
|
}
|
|
} else if (!val) { /* run just ended */
|
|
length = i - start;
|
|
if (length > maxsize) {
|
|
maxsize = length;
|
|
maxstart = start;
|
|
}
|
|
inrun = FALSE;
|
|
}
|
|
}
|
|
|
|
if (inrun) { /* a run has continued to the end of the column */
|
|
length = i - start;
|
|
if (length > maxsize) {
|
|
maxsize = length;
|
|
maxstart = start;
|
|
}
|
|
}
|
|
if (pystart) *pystart = maxstart;
|
|
*psize = maxsize;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*
|
|
* Compute runlength-to-membership transform on a line *
|
|
*-----------------------------------------------------------------------*/
|
|
/*!
|
|
* \brief runlengthMembershipOnLine()
|
|
*
|
|
* \param[in] buffer into which full line of data is placed
|
|
* \param[in] size full size of line; w or h
|
|
* \param[in] depth 8 or 16 bpp
|
|
* \param[in] start array of start positions for fg runs
|
|
* \param[in] end array of end positions for fg runs
|
|
* \param[in] n the number of runs
|
|
* \return 0 if OK; 1 on error
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) Converts a set of runlengths into a buffer of
|
|
* runlength membership values.
|
|
* (2) Initialization of the array gives pixels that are
|
|
* not within a run the value 0.
|
|
* </pre>
|
|
*/
|
|
l_ok
|
|
runlengthMembershipOnLine(l_int32 *buffer,
|
|
l_int32 size,
|
|
l_int32 depth,
|
|
l_int32 *start,
|
|
l_int32 *end,
|
|
l_int32 n)
|
|
{
|
|
l_int32 i, j, first, last, diff, max;
|
|
|
|
PROCNAME("runlengthMembershipOnLine");
|
|
|
|
if (!buffer)
|
|
return ERROR_INT("buffer not defined", procName, 1);
|
|
if (!start)
|
|
return ERROR_INT("start not defined", procName, 1);
|
|
if (!end)
|
|
return ERROR_INT("end not defined", procName, 1);
|
|
|
|
if (depth == 8)
|
|
max = 0xff;
|
|
else /* depth == 16 */
|
|
max = 0xffff;
|
|
|
|
memset(buffer, 0, 4 * size);
|
|
for (i = 0; i < n; i++) {
|
|
first = start[i];
|
|
last = end[i];
|
|
diff = last - first + 1;
|
|
diff = L_MIN(diff, max);
|
|
for (j = first; j <= last; j++)
|
|
buffer[j] = diff;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*
|
|
* Make byte position LUT *
|
|
*-----------------------------------------------------------------------*/
|
|
/*!
|
|
* \brief makeMSBitLocTab()
|
|
*
|
|
* \param[in] bitval either 0 or 1
|
|
* \return table: for an input byte, the MS bit location, starting at 0
|
|
* with the MSBit in the byte, or NULL on error.
|
|
*
|
|
* <pre>
|
|
* Notes:
|
|
* (1) If %bitval == 1, it finds the leftmost ON pixel in a byte;
|
|
* otherwise if %bitval == 0, it finds the leftmost OFF pixel.
|
|
* (2) If there are no pixels of the indicated color in the byte,
|
|
* this returns 8.
|
|
* </pre>
|
|
*/
|
|
l_int32 *
|
|
makeMSBitLocTab(l_int32 bitval)
|
|
{
|
|
l_int32 i, j;
|
|
l_int32 *tab;
|
|
l_uint8 byte, mask;
|
|
|
|
tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
|
|
for (i = 0; i < 256; i++) {
|
|
byte = (l_uint8)i;
|
|
if (bitval == 0)
|
|
byte = ~byte;
|
|
tab[i] = 8;
|
|
mask = 0x80;
|
|
for (j = 0; j < 8; j++) {
|
|
if (byte & mask) {
|
|
tab[i] = j;
|
|
break;
|
|
}
|
|
mask >>= 1;
|
|
}
|
|
}
|
|
return tab;
|
|
}
|