blob: efd829bc8d77260cd2c7e20948738d86d9189f5d [file] [log] [blame]
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
- This software is distributed in the hope that it will be
- useful, but with NO WARRANTY OF ANY KIND.
- No author or distributor accepts responsibility to anyone for the
- consequences of using this software, or for whether it serves any
- particular purpose or works at all, unless he or she says so in
- writing. Everyone is granted permission to copy, modify and
- redistribute this source code, for commercial or non-commercial
- purposes, with the following restrictions: (1) the origin of this
- source code must not be misrepresented; (2) modified versions must
- be plainly marked as such; and (3) this notice may not be removed
- or altered from any source or modified source distribution.
*====================================================================*/
/*
* seedfilllow.c
*
* Seedfill:
* Gray seedfill (source: Luc Vincent:fast-hybrid-grayscale-reconstruction)
* void seedfillBinaryLow()
* void seedfillGrayLow()
* void seedfillGrayInvLow()
* void seedfillGrayLowSimple()
* void seedfillGrayInvLowSimple()
*
* Distance function:
* void distanceFunctionLow()
*
* Seed spread:
* void seedspreadLow()
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "allheaders.h"
struct L_Pixel
{
l_int32 x;
l_int32 y;
};
typedef struct L_Pixel L_PIXEL;
/*-----------------------------------------------------------------------*
* Vincent's Iterative Binary Seedfill *
*-----------------------------------------------------------------------*/
/*!
* seedfillBinaryLow()
*
* Notes:
* (1) This is an in-place fill, where the seed image is
* filled, clipping to the filling mask, in one full
* cycle of UL -> LR and LR -> UL raster scans.
* (2) Assume the mask is a filling mask, not a blocking mask.
* (3) Assume that the RHS pad bits of the mask
* are properly set to 0.
* (4) Clip to the smallest dimensions to avoid invalid reads.
*/
void
seedfillBinaryLow(l_uint32 *datas,
l_int32 hs,
l_int32 wpls,
l_uint32 *datam,
l_int32 hm,
l_int32 wplm,
l_int32 connectivity)
{
l_int32 i, j, h, wpl;
l_uint32 word, mask;
l_uint32 wordabove, wordleft, wordbelow, wordright;
l_uint32 wordprev; /* test against this in previous iteration */
l_uint32 *lines, *linem;
PROCNAME("seedfillBinaryLow");
h = L_MIN(hs, hm);
wpl = L_MIN(wpls, wplm);
switch (connectivity)
{
case 4:
/* UL --> LR scan */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < wpl; j++) {
word = *(lines + j);
mask = *(linem + j);
/* OR from word above and from word to left; mask */
if (i > 0) {
wordabove = *(lines - wpls + j);
word |= wordabove;
}
if (j > 0) {
wordleft = *(lines + j - 1);
word |= wordleft << 31;
}
word &= mask;
/* No need to fill horizontally? */
if (!word || !(~word)) {
*(lines + j) = word;
continue;
}
while (1) {
wordprev = word;
word = (word | (word >> 1) | (word << 1)) & mask;
if ((word ^ wordprev) == 0) {
*(lines + j) = word;
break;
}
}
}
}
/* LR --> UL scan */
for (i = h - 1; i >= 0; i--) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = wpl - 1; j >= 0; j--) {
word = *(lines + j);
mask = *(linem + j);
/* OR from word below and from word to right; mask */
if (i < h - 1) {
wordbelow = *(lines + wpls + j);
word |= wordbelow;
}
if (j < wpl - 1) {
wordright = *(lines + j + 1);
word |= wordright >> 31;
}
word &= mask;
/* No need to fill horizontally? */
if (!word || !(~word)) {
*(lines + j) = word;
continue;
}
while (1) {
wordprev = word;
word = (word | (word >> 1) | (word << 1)) & mask;
if ((word ^ wordprev) == 0) {
*(lines + j) = word;
break;
}
}
}
}
break;
case 8:
/* UL --> LR scan */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < wpl; j++) {
word = *(lines + j);
mask = *(linem + j);
/* OR from words above and from word to left; mask */
if (i > 0) {
wordabove = *(lines - wpls + j);
word |= (wordabove | (wordabove << 1) | (wordabove >> 1));
if (j > 0)
word |= (*(lines - wpls + j - 1)) << 31;
if (j < wpl - 1)
word |= (*(lines - wpls + j + 1)) >> 31;
}
if (j > 0) {
wordleft = *(lines + j - 1);
word |= wordleft << 31;
}
word &= mask;
/* No need to fill horizontally? */
if (!word || !(~word)) {
*(lines + j) = word;
continue;
}
while (1) {
wordprev = word;
word = (word | (word >> 1) | (word << 1)) & mask;
if ((word ^ wordprev) == 0) {
*(lines + j) = word;
break;
}
}
}
}
/* LR --> UL scan */
for (i = h - 1; i >= 0; i--) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = wpl - 1; j >= 0; j--) {
word = *(lines + j);
mask = *(linem + j);
/* OR from words below and from word to right; mask */
if (i < h - 1) {
wordbelow = *(lines + wpls + j);
word |= (wordbelow | (wordbelow << 1) | (wordbelow >> 1));
if (j > 0)
word |= (*(lines + wpls + j - 1)) << 31;
if (j < wpl - 1)
word |= (*(lines + wpls + j + 1)) >> 31;
}
if (j < wpl - 1) {
wordright = *(lines + j + 1);
word |= wordright >> 31;
}
word &= mask;
/* No need to fill horizontally? */
if (!word || !(~word)) {
*(lines + j) = word;
continue;
}
while (1) {
wordprev = word;
word = (word | (word >> 1) | (word << 1)) & mask;
if ((word ^ wordprev) == 0) {
*(lines + j) = word;
break;
}
}
}
}
break;
default:
ERROR_VOID("connectivity must be 4 or 8", procName);
}
return;
}
/*-----------------------------------------------------------------------*
* Vincent's Hybrid Grayscale Seedfill *
*-----------------------------------------------------------------------*/
/*!
* seedfillGrayLow()
*
* Notes:
* (1) The pixels are numbered as follows:
* 1 2 3
* 4 x 5
* 6 7 8
* This low-level filling operation consists of two scans,
* raster and anti-raster, covering the entire seed image.
* This is followed by a breadth-first propagation operation to
* complete the fill.
* During the anti-raster scan, every pixel p whose current value
* could still be propagated after the anti-raster scan is put into
* the FIFO queue.
* The propagation step is a breadth-first fill to completion.
* Unlike the simple grayscale seedfill pixSeedfillGraySimple(),
* where at least two full raster/anti-raster iterations are required
* for completion and verification, the hybrid method uses only a
* single raster/anti-raster set of scans.
* (2) The filling action can be visualized from the following example.
* Suppose the mask, which clips the fill, is a sombrero-shaped
* surface, where the highest point is 200 and the low pixels
* around the rim are 30. Beyond the rim, the mask goes up a bit.
* Suppose the seed, which is filled, consists of a single point
* of height 150, located below the max of the mask, with
* the rest 0. Then in the raster scan, nothing happens until
* the high seed point is encountered, and then this value is
* propagated right and down, until it hits the side of the
* sombrero. The seed can never exceed the mask, so it fills
* to the rim, going lower along the mask surface. When it
* passes the rim, the seed continues to fill at the rim
* height to the edge of the seed image. Then on the
* anti-raster scan, the seed fills flat inside the
* sombrero to the upper and left, and then out from the
* rim as before. The final result has a seed that is
* flat outside the rim, and inside it fills the sombrero
* but only up to 150. If the rim height varies, the
* filled seed outside the rim will be at the highest
* point on the rim, which is a saddle point on the rim.
* (3) Reference paper :
* L. Vincent, Morphological grayscale reconstruction in image
* analysis: applications and efficient algorithms, IEEE Transactions
* on Image Processing, vol. 2, no. 2, pp. 176-201, 1993.
*/
void
seedfillGrayLow(l_uint32 *datas,
l_int32 w,
l_int32 h,
l_int32 wpls,
l_uint32 *datam,
l_int32 wplm,
l_int32 connectivity)
{
l_uint8 val1, val2, val3, val4, val5, val6, val7, val8;
l_uint8 val, maxval, maskval, boolval;
l_int32 i, j, imax, jmax, queue_size;
l_uint32 *lines, *linem;
L_PIXEL *pixel;
L_QUEUE *lq_pixel;
PROCNAME("seedfillGrayLow");
imax = h - 1;
jmax = w - 1;
/* In the worst case, most of the pixels could be pushed
* onto the FIFO queue during anti-raster scan. However this
* will rarely happen, and we initialize the queue ptr size to
* the image perimeter. */
lq_pixel = lqueueCreate(2 * (w + h));
switch (connectivity)
{
case 4:
/* UL --> LR scan (Raster Order)
* If I : mask image
* J : marker image
* Let p be the currect pixel;
* J(p) <- (max{J(p) union J(p) neighbors in raster order})
* intersection I(p) */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < w; j++) {
if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
maxval = 0;
if (i > 0)
maxval = GET_DATA_BYTE(lines - wpls, j);
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maxval = L_MAX(maxval, val4);
}
val = GET_DATA_BYTE(lines, j);
maxval = L_MAX(maxval, val);
val = L_MIN(maxval, maskval);
SET_DATA_BYTE(lines, j, val);
}
}
}
/* LR --> UL scan (anti-raster order)
* Let p be the currect pixel;
* J(p) <- (max{J(p) union J(p) neighbors in anti-raster order})
* intersection I(p) */
for (i = imax; i >= 0; i--) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = jmax; j >= 0; j--) {
boolval = FALSE;
if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
maxval = 0;
if (i < imax)
maxval = GET_DATA_BYTE(lines + wpls, j);
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maxval = L_MAX(maxval, val5);
}
val = GET_DATA_BYTE(lines, j);
maxval = L_MAX(maxval, val);
val = L_MIN(maxval, maskval);
SET_DATA_BYTE(lines, j, val);
/*
* If there exists a point (q) which belongs to J(p)
* neighbors in anti-raster order such that J(q) < J(p)
* and J(q) < I(q) then
* fifo_add(p) */
if (i < imax) {
val7 = GET_DATA_BYTE(lines + wpls, j);
if ((val7 < val) &&
(val7 < GET_DATA_BYTE(linem + wplm, j))) {
boolval = TRUE;
}
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
if (!boolval && (val5 < val) &&
(val5 < GET_DATA_BYTE(linem, j + 1))) {
boolval = TRUE;
}
}
if (boolval) {
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
}
}
/* Propagation step:
* while fifo_empty = false
* p <- fifo_first()
* for every pixel (q) belong to neighbors of (p)
* if J(q) < J(p) and I(q) != J(q)
* J(q) <- min(J(p), I(q));
* fifo_add(q);
* end
* end
* end */
queue_size = lqueueGetCount(lq_pixel);
while (queue_size) {
pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
i = pixel->x;
j = pixel->y;
FREE(pixel);
lines = datas + i * wpls;
linem = datam + i * wplm;
if ((val = GET_DATA_BYTE(lines, j)) > 0) {
if (i > 0) {
val2 = GET_DATA_BYTE(lines - wpls, j);
maskval = GET_DATA_BYTE(linem - wplm, j);
if (val > val2 && val2 != maskval) {
SET_DATA_BYTE(lines - wpls, j, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i - 1;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maskval = GET_DATA_BYTE(linem, j - 1);
if (val > val4 && val4 != maskval) {
SET_DATA_BYTE(lines, j - 1, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j - 1;
lqueueAdd(lq_pixel, pixel);
}
}
if (i < imax) {
val7 = GET_DATA_BYTE(lines + wpls, j);
maskval = GET_DATA_BYTE(linem + wplm, j);
if (val > val7 && val7 != maskval) {
SET_DATA_BYTE(lines + wpls, j, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i + 1;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maskval = GET_DATA_BYTE(linem, j + 1);
if (val > val5 && val5 != maskval) {
SET_DATA_BYTE(lines, j + 1, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j + 1;
lqueueAdd(lq_pixel, pixel);
}
}
}
queue_size = lqueueGetCount(lq_pixel);
}
break;
case 8:
/* UL --> LR scan (Raster Order)
* If I : mask image
* J : marker image
* Let p be the currect pixel;
* J(p) <- (max{J(p) union J(p) neighbors in raster order})
* intersection I(p) */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < w; j++) {
if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
maxval = 0;
if (i > 0) {
if (j > 0)
maxval = GET_DATA_BYTE(lines - wpls, j - 1);
if (j < jmax) {
val3 = GET_DATA_BYTE(lines - wpls, j + 1);
maxval = L_MAX(maxval, val3);
}
val2 = GET_DATA_BYTE(lines - wpls, j);
maxval = L_MAX(maxval, val2);
}
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maxval = L_MAX(maxval, val4);
}
val = GET_DATA_BYTE(lines, j);
maxval = L_MAX(maxval, val);
val = L_MIN(maxval, maskval);
SET_DATA_BYTE(lines, j, val);
}
}
}
/* LR --> UL scan (anti-raster order)
* Let p be the currect pixel;
* J(p) <- (max{J(p) union J(p) neighbors in anti-raster order})
* intersection I(p) */
for (i = imax; i >= 0; i--) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = jmax; j >= 0; j--) {
boolval = FALSE;
if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
maxval = 0;
if (i < imax) {
if (j > 0) {
maxval = GET_DATA_BYTE(lines + wpls, j - 1);
}
if (j < jmax) {
val8 = GET_DATA_BYTE(lines + wpls, j + 1);
maxval = L_MAX(maxval, val8);
}
val7 = GET_DATA_BYTE(lines + wpls, j);
maxval = L_MAX(maxval, val7);
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maxval = L_MAX(maxval, val5);
}
val = GET_DATA_BYTE(lines, j);
maxval = L_MAX(maxval, val);
val = L_MIN(maxval, maskval);
SET_DATA_BYTE(lines, j, val);
/* If there exists a point (q) which belongs to J(p)
* neighbors in anti-raster order such that J(q) < J(p)
* and J(q) < I(q) then
* fifo_add(p) */
if (i < imax) {
if (j > 0) {
val6 = GET_DATA_BYTE(lines + wpls, j - 1);
if ((val6 < val) &&
(val6 < GET_DATA_BYTE(linem + wplm, j - 1))) {
boolval = TRUE;
}
}
if (j < jmax) {
val8 = GET_DATA_BYTE(lines + wpls, j + 1);
if (!boolval && (val8 < val) &&
(val8 < GET_DATA_BYTE(linem + wplm, j + 1))) {
boolval = TRUE;
}
}
val7 = GET_DATA_BYTE(lines + wpls, j);
if (!boolval && (val7 < val) &&
(val7 < GET_DATA_BYTE(linem + wplm, j))) {
boolval = TRUE;
}
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
if (!boolval && (val5 < val) &&
(val5 < GET_DATA_BYTE(linem, j + 1))) {
boolval = TRUE;
}
}
if (boolval) {
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
}
}
/* Propagation step:
* while fifo_empty = false
* p <- fifo_first()
* for every pixel (q) belong to neighbors of (p)
* if J(q) < J(p) and I(q) != J(q)
* J(q) <- min(J(p), I(q));
* fifo_add(q);
* end
* end
* end */
queue_size = lqueueGetCount(lq_pixel);
while (queue_size) {
pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
i = pixel->x;
j = pixel->y;
FREE(pixel);
lines = datas + i * wpls;
linem = datam + i * wplm;
if ((val = GET_DATA_BYTE(lines, j)) > 0) {
if (i > 0) {
if (j > 0) {
val1 = GET_DATA_BYTE(lines - wpls, j - 1);
maskval = GET_DATA_BYTE(linem - wplm, j - 1);
if (val > val1 && val1 != maskval) {
SET_DATA_BYTE(lines - wpls, j - 1, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i - 1;
pixel->y = j - 1;
lqueueAdd(lq_pixel, pixel);
}
}
if (j < jmax) {
val3 = GET_DATA_BYTE(lines - wpls, j + 1);
maskval = GET_DATA_BYTE(linem - wplm, j + 1);
if (val > val3 && val3 != maskval) {
SET_DATA_BYTE(lines - wpls, j + 1, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i - 1;
pixel->y = j + 1;
lqueueAdd(lq_pixel, pixel);
}
}
val2 = GET_DATA_BYTE(lines - wpls, j);
maskval = GET_DATA_BYTE(linem - wplm, j);
if (val > val2 && val2 != maskval) {
SET_DATA_BYTE(lines - wpls, j, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i - 1;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maskval = GET_DATA_BYTE(linem, j - 1);
if (val > val4 && val4 != maskval) {
SET_DATA_BYTE(lines, j - 1, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j - 1;
lqueueAdd(lq_pixel, pixel);
}
}
if (i < imax) {
if (j > 0) {
val6 = GET_DATA_BYTE(lines + wpls, j - 1);
maskval = GET_DATA_BYTE(linem + wplm, j - 1);
if (val > val6 && val6 != maskval) {
SET_DATA_BYTE(lines + wpls, j - 1, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i + 1;
pixel->y = j - 1;
lqueueAdd(lq_pixel, pixel);
}
}
if (j < jmax) {
val8 = GET_DATA_BYTE(lines + wpls, j + 1);
maskval = GET_DATA_BYTE(linem + wplm, j + 1);
if (val > val8 && val8 != maskval) {
SET_DATA_BYTE(lines + wpls, j + 1, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i + 1;
pixel->y = j + 1;
lqueueAdd(lq_pixel, pixel);
}
}
val7 = GET_DATA_BYTE(lines + wpls, j);
maskval = GET_DATA_BYTE(linem + wplm, j);
if (val > val7 && val7 != maskval) {
SET_DATA_BYTE(lines + wpls, j, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i + 1;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maskval = GET_DATA_BYTE(linem, j + 1);
if (val > val5 && val5 != maskval) {
SET_DATA_BYTE(lines, j + 1, L_MIN(val, maskval));
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j + 1;
lqueueAdd(lq_pixel, pixel);
}
}
}
queue_size = lqueueGetCount(lq_pixel);
}
break;
default:
ERROR_VOID("connectivity must be 4 or 8", procName);
lqueueDestroy(&lq_pixel, TRUE);
}
lqueueDestroy(&lq_pixel, TRUE);
return;
}
/*!
* seedfillGrayInvLow()
*
* Notes:
* (1) The pixels are numbered as follows:
* 1 2 3
* 4 x 5
* 6 7 8
* This low-level filling operation consists of two scans,
* raster and anti-raster, covering the entire seed image.
* During the anti-raster scan, every pixel p such that its
* current value could still be propogated during the next
* raster scanning is put into the FIFO-queue.
* Next step is the propagation step where where we update
* and propagate the values using FIFO structure created in
* anti-raster scan.
* (2) The "Inv" signifies the fact that in this case, filling
* of the seed only takes place when the seed value is
* greater than the mask value. The mask will act to stop
* the fill when it is higher than the seed level. (This is
* in contrast to conventional grayscale filling where the
* seed always fills below the mask.)
* (3) An example of use is a basin, described by the mask (pixm),
* where within the basin, the seed pix (pixs) gets filled to the
* height of the highest seed pixel that is above its
* corresponding max pixel. Filling occurs while the
* propagating seed pixels in pixs are larger than the
* corresponding mask values in pixm.
* (4) Reference paper :
* L. Vincent, Morphological grayscale reconstruction in image
* analysis: applications and efficient algorithms, IEEE Transactions
* on Image Processing, vol. 2, no. 2, pp. 176-201, 1993.
*/
void
seedfillGrayInvLow(l_uint32 *datas,
l_int32 w,
l_int32 h,
l_int32 wpls,
l_uint32 *datam,
l_int32 wplm,
l_int32 connectivity)
{
l_uint8 val1, val2, val3, val4, val5, val6, val7, val8;
l_uint8 val, maxval, maskval, boolval;
l_int32 i, j, imax, jmax, queue_size;
l_uint32 *lines, *linem;
L_PIXEL *pixel;
L_QUEUE *lq_pixel;
PROCNAME("seedfillGrayInvLow");
imax = h - 1;
jmax = w - 1;
/* In the worst case, most of the pixels could be pushed
* onto the FIFO queue during anti-raster scan. However this
* will rarely happen, and we initialize the queue ptr size to
* the image perimeter. */
lq_pixel = lqueueCreate(2 * (w + h));
switch (connectivity)
{
case 4:
/* UL --> LR scan (Raster Order)
* If I : mask image
* J : marker image
* Let p be the currect pixel;
* tmp <- max{J(p) union J(p) neighbors in raster order}
* if (tmp > I(p))
* J(p) <- tmp
* end */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < w; j++) {
if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
maxval = GET_DATA_BYTE(lines, j);
if (i > 0) {
val2 = GET_DATA_BYTE(lines - wpls, j);
maxval = L_MAX(maxval, val2);
}
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maxval = L_MAX(maxval, val4);
}
if (maxval > maskval)
SET_DATA_BYTE(lines, j, maxval);
}
}
}
/* LR --> UL scan (anti-raster order)
* If I : mask image
* J : marker image
* Let p be the currect pixel;
* tmp <- max{J(p) union J(p) neighbors in anti-raster order}
* if (tmp > I(p))
* J(p) <- tmp
* end */
for (i = imax; i >= 0; i--) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = jmax; j >= 0; j--) {
boolval = FALSE;
if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
val = maxval = GET_DATA_BYTE(lines, j);
if (i < imax) {
val7 = GET_DATA_BYTE(lines + wpls, j);
maxval = L_MAX(maxval, val7);
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maxval = L_MAX(maxval, val5);
}
if (maxval > maskval)
SET_DATA_BYTE(lines, j, maxval);
val = GET_DATA_BYTE(lines, j);
/*
* If there exists a point (q) which belongs to J(p)
* neighbors in anti-raster order such that J(q) < J(p)
* and J(p) > I(q) then
* fifo_add(p) */
if (i < imax) {
val7 = GET_DATA_BYTE(lines + wpls, j);
if ((val7 < val) &&
(val > GET_DATA_BYTE(linem + wplm, j))) {
boolval = TRUE;
}
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
if (!boolval && (val5 < val) &&
(val > GET_DATA_BYTE(linem, j + 1))) {
boolval = TRUE;
}
}
if (boolval) {
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
}
}
/* Propagation step:
* while fifo_empty = false
* p <- fifo_first()
* for every pixel (q) belong to neighbors of (p)
* if J(q) < J(p) and J(p) > I(q)
* J(q) <- min(J(p), I(q));
* fifo_add(q);
* end
* end
* end */
queue_size = lqueueGetCount(lq_pixel);
while (queue_size) {
pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
i = pixel->x;
j = pixel->y;
FREE(pixel);
lines = datas + i * wpls;
linem = datam + i * wplm;
if ((val = GET_DATA_BYTE(lines, j)) > 0) {
if (i > 0) {
val2 = GET_DATA_BYTE(lines - wpls, j);
maskval = GET_DATA_BYTE(linem - wplm, j);
if (val > val2 && val > maskval) {
SET_DATA_BYTE(lines - wpls, j, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i - 1;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maskval = GET_DATA_BYTE(linem, j - 1);
if (val > val4 && val > maskval) {
SET_DATA_BYTE(lines, j - 1, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j - 1;
lqueueAdd(lq_pixel, pixel);
}
}
if (i < imax) {
val7 = GET_DATA_BYTE(lines + wpls, j);
maskval = GET_DATA_BYTE(linem + wplm, j);
if (val > val7 && val > maskval) {
SET_DATA_BYTE(lines + wpls, j, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i + 1;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maskval = GET_DATA_BYTE(linem, j + 1);
if (val > val5 && val > maskval) {
SET_DATA_BYTE(lines, j + 1, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j + 1;
lqueueAdd(lq_pixel, pixel);
}
}
}
queue_size = lqueueGetCount(lq_pixel);
}
break;
case 8:
/* UL --> LR scan (Raster Order)
* If I : mask image
* J : marker image
* Let p be the currect pixel;
* tmp <- max{J(p) union J(p) neighbors in raster order}
* if (tmp > I(p))
* J(p) <- tmp
* end */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < w; j++) {
if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
maxval = GET_DATA_BYTE(lines, j);
if (i > 0) {
if (j > 0) {
val1 = GET_DATA_BYTE(lines - wpls, j - 1);
maxval = L_MAX(maxval, val1);
}
if (j < jmax) {
val3 = GET_DATA_BYTE(lines - wpls, j + 1);
maxval = L_MAX(maxval, val3);
}
val2 = GET_DATA_BYTE(lines - wpls, j);
maxval = L_MAX(maxval, val2);
}
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maxval = L_MAX(maxval, val4);
}
if (maxval > maskval)
SET_DATA_BYTE(lines, j, maxval);
}
}
}
/* LR --> UL scan (anti-raster order)
* If I : mask image
* J : marker image
* Let p be the currect pixel;
* tmp <- max{J(p) union J(p) neighbors in anti-raster order}
* if (tmp > I(p))
* J(p) <- tmp
* end */
for (i = imax; i >= 0; i--) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = jmax; j >= 0; j--) {
boolval = FALSE;
if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
maxval = GET_DATA_BYTE(lines, j);
if (i < imax) {
if (j > 0) {
val6 = GET_DATA_BYTE(lines + wpls, j - 1);
maxval = L_MAX(maxval, val6);
}
if (j < jmax) {
val8 = GET_DATA_BYTE(lines + wpls, j + 1);
maxval = L_MAX(maxval, val8);
}
val7 = GET_DATA_BYTE(lines + wpls, j);
maxval = L_MAX(maxval, val7);
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maxval = L_MAX(maxval, val5);
}
if (maxval > maskval)
SET_DATA_BYTE(lines, j, maxval);
val = GET_DATA_BYTE(lines, j);
/*
* If there exists a point (q) which belongs to J(p)
* neighbors in anti-raster order such that J(q) < J(p)
* and J(p) > I(q) then
* fifo_add(p) */
if (i < imax) {
if (j > 0) {
val6 = GET_DATA_BYTE(lines + wpls, j - 1);
if ((val6 < val) &&
(val > GET_DATA_BYTE(linem + wplm, j - 1))) {
boolval = TRUE;
}
}
if (j < jmax) {
val8 = GET_DATA_BYTE(lines + wpls, j + 1);
if (!boolval && (val8 < val) &&
(val > GET_DATA_BYTE(linem + wplm, j + 1))) {
boolval = TRUE;
}
}
val7 = GET_DATA_BYTE(lines + wpls, j);
if (!boolval && (val7 < val) &&
(val > GET_DATA_BYTE(linem + wplm, j))) {
boolval = TRUE;
}
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
if (!boolval && (val5 < val) &&
(val > GET_DATA_BYTE(linem, j + 1))) {
boolval = TRUE;
}
}
if (boolval) {
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
}
}
/* Propagation step:
* while fifo_empty = false
* p <- fifo_first()
* for every pixel (q) belong to neighbors of (p)
* if J(q) < J(p) and J(p) > I(q)
* J(q) <- min(J(p), I(q));
* fifo_add(q);
* end
* end
* end */
queue_size = lqueueGetCount(lq_pixel);
while (queue_size) {
pixel = (L_PIXEL *)lqueueRemove(lq_pixel);
i = pixel->x;
j = pixel->y;
FREE(pixel);
lines = datas + i * wpls;
linem = datam + i * wplm;
if ((val = GET_DATA_BYTE(lines, j)) > 0) {
if (i > 0) {
if (j > 0) {
val1 = GET_DATA_BYTE(lines - wpls, j - 1);
maskval = GET_DATA_BYTE(linem - wplm, j - 1);
if (val > val1 && val > maskval) {
SET_DATA_BYTE(lines - wpls, j - 1, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i - 1;
pixel->y = j - 1;
lqueueAdd(lq_pixel, pixel);
}
}
if (j < jmax) {
val3 = GET_DATA_BYTE(lines - wpls, j + 1);
maskval = GET_DATA_BYTE(linem - wplm, j + 1);
if (val > val3 && val > maskval) {
SET_DATA_BYTE(lines - wpls, j + 1, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i - 1;
pixel->y = j + 1;
lqueueAdd(lq_pixel, pixel);
}
}
val2 = GET_DATA_BYTE(lines - wpls, j);
maskval = GET_DATA_BYTE(linem - wplm, j);
if (val > val2 && val > maskval) {
SET_DATA_BYTE(lines - wpls, j, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i - 1;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maskval = GET_DATA_BYTE(linem, j - 1);
if (val > val4 && val > maskval) {
SET_DATA_BYTE(lines, j - 1, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j - 1;
lqueueAdd(lq_pixel, pixel);
}
}
if (i < imax) {
if (j > 0) {
val6 = GET_DATA_BYTE(lines + wpls, j - 1);
maskval = GET_DATA_BYTE(linem + wplm, j - 1);
if (val > val6 && val > maskval) {
SET_DATA_BYTE(lines + wpls, j - 1, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i + 1;
pixel->y = j - 1;
lqueueAdd(lq_pixel, pixel);
}
}
if (j < jmax) {
val8 = GET_DATA_BYTE(lines + wpls, j + 1);
maskval = GET_DATA_BYTE(linem + wplm, j + 1);
if (val > val8 && val > maskval) {
SET_DATA_BYTE(lines + wpls, j + 1, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i + 1;
pixel->y = j + 1;
lqueueAdd(lq_pixel, pixel);
}
}
val7 = GET_DATA_BYTE(lines + wpls, j);
maskval = GET_DATA_BYTE(linem + wplm, j);
if (val > val7 && val > maskval) {
SET_DATA_BYTE(lines + wpls, j, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i + 1;
pixel->y = j;
lqueueAdd(lq_pixel, pixel);
}
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maskval = GET_DATA_BYTE(linem, j + 1);
if (val > val5 && val > maskval) {
SET_DATA_BYTE(lines, j + 1, val);
pixel = (L_PIXEL *)CALLOC(1, sizeof(L_PIXEL));
pixel->x = i;
pixel->y = j + 1;
lqueueAdd(lq_pixel, pixel);
}
}
}
queue_size = lqueueGetCount(lq_pixel);
}
break;
default:
lqueueDestroy(&lq_pixel, TRUE);
ERROR_VOID("connectivity must be 4 or 8", procName);
}
lqueueDestroy(&lq_pixel, TRUE);
return;
}
/*-----------------------------------------------------------------------*
* Vincent's Iterative Grayscale Seedfill *
*-----------------------------------------------------------------------*/
/*!
* seedfillGrayLowSimple()
*
* Notes:
* (1) The pixels are numbered as follows:
* 1 2 3
* 4 x 5
* 6 7 8
* This low-level filling operation consists of two scans,
* raster and anti-raster, covering the entire seed image.
* The caller typically iterates until the filling is
* complete.
* (2) The filling action can be visualized from the following example.
* Suppose the mask, which clips the fill, is a sombrero-shaped
* surface, where the highest point is 200 and the low pixels
* around the rim are 30. Beyond the rim, the mask goes up a bit.
* Suppose the seed, which is filled, consists of a single point
* of height 150, located below the max of the mask, with
* the rest 0. Then in the raster scan, nothing happens until
* the high seed point is encountered, and then this value is
* propagated right and down, until it hits the side of the
* sombrero. The seed can never exceed the mask, so it fills
* to the rim, going lower along the mask surface. When it
* passes the rim, the seed continues to fill at the rim
* height to the edge of the seed image. Then on the
* anti-raster scan, the seed fills flat inside the
* sombrero to the upper and left, and then out from the
* rim as before. The final result has a seed that is
* flat outside the rim, and inside it fills the sombrero
* but only up to 150. If the rim height varies, the
* filled seed outside the rim will be at the highest
* point on the rim, which is a saddle point on the rim.
*/
void
seedfillGrayLowSimple(l_uint32 *datas,
l_int32 w,
l_int32 h,
l_int32 wpls,
l_uint32 *datam,
l_int32 wplm,
l_int32 connectivity)
{
l_uint8 val2, val3, val4, val5, val7, val8;
l_uint8 val, maxval, maskval;
l_int32 i, j, imax, jmax;
l_uint32 *lines, *linem;
PROCNAME("seedfillGrayLowSimple");
imax = h - 1;
jmax = w - 1;
switch (connectivity)
{
case 4:
/* UL --> LR scan */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < w; j++) {
if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
maxval = 0;
if (i > 0)
maxval = GET_DATA_BYTE(lines - wpls, j);
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maxval = L_MAX(maxval, val4);
}
val = GET_DATA_BYTE(lines, j);
maxval = L_MAX(maxval, val);
val = L_MIN(maxval, maskval);
SET_DATA_BYTE(lines, j, val);
}
}
}
/* LR --> UL scan */
for (i = imax; i >= 0; i--) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = jmax; j >= 0; j--) {
if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
maxval = 0;
if (i < imax)
maxval = GET_DATA_BYTE(lines + wpls, j);
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maxval = L_MAX(maxval, val5);
}
val = GET_DATA_BYTE(lines, j);
maxval = L_MAX(maxval, val);
val = L_MIN(maxval, maskval);
SET_DATA_BYTE(lines, j, val);
}
}
}
break;
case 8:
/* UL --> LR scan */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < w; j++) {
if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
maxval = 0;
if (i > 0) {
if (j > 0)
maxval = GET_DATA_BYTE(lines - wpls, j - 1);
if (j < jmax) {
val2 = GET_DATA_BYTE(lines - wpls, j + 1);
maxval = L_MAX(maxval, val2);
}
val3 = GET_DATA_BYTE(lines - wpls, j);
maxval = L_MAX(maxval, val3);
}
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maxval = L_MAX(maxval, val4);
}
val = GET_DATA_BYTE(lines, j);
maxval = L_MAX(maxval, val);
val = L_MIN(maxval, maskval);
SET_DATA_BYTE(lines, j, val);
}
}
}
/* LR --> UL scan */
for (i = imax; i >= 0; i--) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = jmax; j >= 0; j--) {
if ((maskval = GET_DATA_BYTE(linem, j)) > 0) {
maxval = 0;
if (i < imax) {
if (j > 0)
maxval = GET_DATA_BYTE(lines + wpls, j - 1);
if (j < jmax) {
val8 = GET_DATA_BYTE(lines + wpls, j + 1);
maxval = L_MAX(maxval, val8);
}
val7 = GET_DATA_BYTE(lines + wpls, j);
maxval = L_MAX(maxval, val7);
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maxval = L_MAX(maxval, val5);
}
val = GET_DATA_BYTE(lines, j);
maxval = L_MAX(maxval, val);
val = L_MIN(maxval, maskval);
SET_DATA_BYTE(lines, j, val);
}
}
}
break;
default:
ERROR_VOID("connectivity must be 4 or 8", procName);
}
return;
}
/*!
* seedfillGrayInvLowSimple()
*
* Notes:
* (1) The pixels are numbered as follows:
* 1 2 3
* 4 x 5
* 6 7 8
* This low-level filling operation consists of two scans,
* raster and anti-raster, covering the entire seed image.
* The caller typically iterates until the filling is
* complete.
* (2) The "Inv" signifies the fact that in this case, filling
* of the seed only takes place when the seed value is
* greater than the mask value. The mask will act to stop
* the fill when it is higher than the seed level. (This is
* in contrast to conventional grayscale filling where the
* seed always fills below the mask.)
* (3) An example of use is a basin, described by the mask (pixm),
* where within the basin, the seed pix (pixs) gets filled to the
* height of the highest seed pixel that is above its
* corresponding max pixel. Filling occurs while the
* propagating seed pixels in pixs are larger than the
* corresponding mask values in pixm.
*/
void
seedfillGrayInvLowSimple(l_uint32 *datas,
l_int32 w,
l_int32 h,
l_int32 wpls,
l_uint32 *datam,
l_int32 wplm,
l_int32 connectivity)
{
l_uint8 val1, val2, val3, val4, val5, val6, val7, val8;
l_uint8 maxval, maskval;
l_int32 i, j, imax, jmax;
l_uint32 *lines, *linem;
PROCNAME("seedfillGrayInvLowSimple");
imax = h - 1;
jmax = w - 1;
switch (connectivity)
{
case 4:
/* UL --> LR scan */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < w; j++) {
if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
maxval = GET_DATA_BYTE(lines, j);
if (i > 0) {
val2 = GET_DATA_BYTE(lines - wpls, j);
maxval = L_MAX(maxval, val2);
}
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maxval = L_MAX(maxval, val4);
}
if (maxval > maskval)
SET_DATA_BYTE(lines, j, maxval);
}
}
}
/* LR --> UL scan */
for (i = imax; i >= 0; i--) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = jmax; j >= 0; j--) {
if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
maxval = GET_DATA_BYTE(lines, j);
if (i < imax) {
val7 = GET_DATA_BYTE(lines + wpls, j);
maxval = L_MAX(maxval, val7);
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maxval = L_MAX(maxval, val5);
}
if (maxval > maskval)
SET_DATA_BYTE(lines, j, maxval);
}
}
}
break;
case 8:
/* UL --> LR scan */
for (i = 0; i < h; i++) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = 0; j < w; j++) {
if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
maxval = GET_DATA_BYTE(lines, j);
if (i > 0) {
if (j > 0) {
val1 = GET_DATA_BYTE(lines - wpls, j - 1);
maxval = L_MAX(maxval, val1);
}
if (j < jmax) {
val2 = GET_DATA_BYTE(lines - wpls, j + 1);
maxval = L_MAX(maxval, val2);
}
val3 = GET_DATA_BYTE(lines - wpls, j);
maxval = L_MAX(maxval, val3);
}
if (j > 0) {
val4 = GET_DATA_BYTE(lines, j - 1);
maxval = L_MAX(maxval, val4);
}
if (maxval > maskval)
SET_DATA_BYTE(lines, j, maxval);
}
}
}
/* LR --> UL scan */
for (i = imax; i >= 0; i--) {
lines = datas + i * wpls;
linem = datam + i * wplm;
for (j = jmax; j >= 0; j--) {
if ((maskval = GET_DATA_BYTE(linem, j)) < 255) {
maxval = GET_DATA_BYTE(lines, j);
if (i < imax) {
if (j > 0) {
val6 = GET_DATA_BYTE(lines + wpls, j - 1);
maxval = L_MAX(maxval, val6);
}
if (j < jmax) {
val8 = GET_DATA_BYTE(lines + wpls, j + 1);
maxval = L_MAX(maxval, val8);
}
val7 = GET_DATA_BYTE(lines + wpls, j);
maxval = L_MAX(maxval, val7);
}
if (j < jmax) {
val5 = GET_DATA_BYTE(lines, j + 1);
maxval = L_MAX(maxval, val5);
}
if (maxval > maskval)
SET_DATA_BYTE(lines, j, maxval);
}
}
}
break;
default:
ERROR_VOID("connectivity must be 4 or 8", procName);
}
return;
}
/*-----------------------------------------------------------------------*
* Vincent's Distance Function method *
*-----------------------------------------------------------------------*/
/*!
* distanceFunctionLow()
*/
void
distanceFunctionLow(l_uint32 *datad,
l_int32 w,
l_int32 h,
l_int32 d,
l_int32 wpld,
l_int32 connectivity)
{
l_int32 val1, val2, val3, val4, val5, val6, val7, val8, minval, val;
l_int32 i, j, imax, jmax;
l_uint32 *lined;
PROCNAME("distanceFunctionLow");
/* One raster scan followed by one anti-raster scan.
* This does not re-set the 1-boundary of pixels that
* were initialized to either 0 or maxval. */
imax = h - 1;
jmax = w - 1;
switch (connectivity)
{
case 4:
if (d == 8) {
/* UL --> LR scan */
for (i = 1; i < imax; i++) {
lined = datad + i * wpld;
for (j = 1; j < jmax; j++) {
if ((val = GET_DATA_BYTE(lined, j)) > 0) {
val2 = GET_DATA_BYTE(lined - wpld, j);
val4 = GET_DATA_BYTE(lined, j - 1);
minval = L_MIN(val2, val4);
minval = L_MIN(minval, 254);
SET_DATA_BYTE(lined, j, minval + 1);
}
}
}
/* LR --> UL scan */
for (i = imax - 1; i > 0; i--) {
lined = datad + i * wpld;
for (j = jmax - 1; j > 0; j--) {
if ((val = GET_DATA_BYTE(lined, j)) > 0) {
val7 = GET_DATA_BYTE(lined + wpld, j);
val5 = GET_DATA_BYTE(lined, j + 1);
minval = L_MIN(val5, val7);
minval = L_MIN(minval + 1, val);
SET_DATA_BYTE(lined, j, minval);
}
}
}
}
else { /* d == 16 */
/* UL --> LR scan */
for (i = 1; i < imax; i++) {
lined = datad + i * wpld;
for (j = 1; j < jmax; j++) {
if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
val2 = GET_DATA_TWO_BYTES(lined - wpld, j);
val4 = GET_DATA_TWO_BYTES(lined, j - 1);
minval = L_MIN(val2, val4);
minval = L_MIN(minval, 0xfffe);
SET_DATA_TWO_BYTES(lined, j, minval + 1);
}
}
}
/* LR --> UL scan */
for (i = imax - 1; i > 0; i--) {
lined = datad + i * wpld;
for (j = jmax - 1; j > 0; j--) {
if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
val7 = GET_DATA_TWO_BYTES(lined + wpld, j);
val5 = GET_DATA_TWO_BYTES(lined, j + 1);
minval = L_MIN(val5, val7);
minval = L_MIN(minval + 1, val);
SET_DATA_TWO_BYTES(lined, j, minval);
}
}
}
}
break;
case 8:
if (d == 8) {
/* UL --> LR scan */
for (i = 1; i < imax; i++) {
lined = datad + i * wpld;
for (j = 1; j < jmax; j++) {
if ((val = GET_DATA_BYTE(lined, j)) > 0) {
val1 = GET_DATA_BYTE(lined - wpld, j - 1);
val2 = GET_DATA_BYTE(lined - wpld, j);
val3 = GET_DATA_BYTE(lined - wpld, j + 1);
val4 = GET_DATA_BYTE(lined, j - 1);
minval = L_MIN(val1, val2);
minval = L_MIN(minval, val3);
minval = L_MIN(minval, val4);
minval = L_MIN(minval, 254);
SET_DATA_BYTE(lined, j, minval + 1);
}
}
}
/* LR --> UL scan */
for (i = imax - 1; i > 0; i--) {
lined = datad + i * wpld;
for (j = jmax - 1; j > 0; j--) {
if ((val = GET_DATA_BYTE(lined, j)) > 0) {
val8 = GET_DATA_BYTE(lined + wpld, j + 1);
val7 = GET_DATA_BYTE(lined + wpld, j);
val6 = GET_DATA_BYTE(lined + wpld, j - 1);
val5 = GET_DATA_BYTE(lined, j + 1);
minval = L_MIN(val8, val7);
minval = L_MIN(minval, val6);
minval = L_MIN(minval, val5);
minval = L_MIN(minval + 1, val);
SET_DATA_BYTE(lined, j, minval);
}
}
}
}
else { /* d == 16 */
/* UL --> LR scan */
for (i = 1; i < imax; i++) {
lined = datad + i * wpld;
for (j = 1; j < jmax; j++) {
if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
val1 = GET_DATA_TWO_BYTES(lined - wpld, j - 1);
val2 = GET_DATA_TWO_BYTES(lined - wpld, j);
val3 = GET_DATA_TWO_BYTES(lined - wpld, j + 1);
val4 = GET_DATA_TWO_BYTES(lined, j - 1);
minval = L_MIN(val1, val2);
minval = L_MIN(minval, val3);
minval = L_MIN(minval, val4);
minval = L_MIN(minval, 0xfffe);
SET_DATA_TWO_BYTES(lined, j, minval + 1);
}
}
}
/* LR --> UL scan */
for (i = imax - 1; i > 0; i--) {
lined = datad + i * wpld;
for (j = jmax - 1; j > 0; j--) {
if ((val = GET_DATA_TWO_BYTES(lined, j)) > 0) {
val8 = GET_DATA_TWO_BYTES(lined + wpld, j + 1);
val7 = GET_DATA_TWO_BYTES(lined + wpld, j);
val6 = GET_DATA_TWO_BYTES(lined + wpld, j - 1);
val5 = GET_DATA_TWO_BYTES(lined, j + 1);
minval = L_MIN(val8, val7);
minval = L_MIN(minval, val6);
minval = L_MIN(minval, val5);
minval = L_MIN(minval + 1, val);
SET_DATA_TWO_BYTES(lined, j, minval);
}
}
}
}
break;
default:
ERROR_VOID("connectivity must be 4 or 8", procName);
break;
}
return;
}
/*-----------------------------------------------------------------------*
* Seed spread (based on distance function) *
*-----------------------------------------------------------------------*/
/*!
* seedspreadLow()
*
* See pixSeedspread() for a brief description of the algorithm here.
*/
void
seedspreadLow(l_uint32 *datad,
l_int32 w,
l_int32 h,
l_int32 wpld,
l_uint32 *datat,
l_int32 wplt,
l_int32 connectivity)
{
l_int32 val1t, val2t, val3t, val4t, val5t, val6t, val7t, val8t;
l_int32 i, j, imax, jmax, minval, valt, vald;
l_uint32 *linet, *lined;
PROCNAME("seedspreadLow");
/* One raster scan followed by one anti-raster scan.
* pixt is initialized to have 0 on pixels where the
* input is specified in pixd, and to have 1 on all
* other pixels. We only change pixels in pixt and pixd
* that are non-zero in pixt. */
imax = h - 1;
jmax = w - 1;
switch (connectivity)
{
case 4:
/* UL --> LR scan */
for (i = 1; i < h; i++) {
linet = datat + i * wplt;
lined = datad + i * wpld;
for (j = 1; j < jmax; j++) {
if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
val2t = GET_DATA_TWO_BYTES(linet - wplt, j);
val4t = GET_DATA_TWO_BYTES(linet, j - 1);
minval = L_MIN(val2t, val4t);
minval = L_MIN(minval, 0xfffe);
SET_DATA_TWO_BYTES(linet, j, minval + 1);
if (val2t < val4t)
vald = GET_DATA_BYTE(lined - wpld, j);
else
vald = GET_DATA_BYTE(lined, j - 1);
SET_DATA_BYTE(lined, j, vald);
}
}
}
/* LR --> UL scan */
for (i = imax - 1; i > 0; i--) {
linet = datat + i * wplt;
lined = datad + i * wpld;
for (j = jmax - 1; j > 0; j--) {
if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
val7t = GET_DATA_TWO_BYTES(linet + wplt, j);
val5t = GET_DATA_TWO_BYTES(linet, j + 1);
minval = L_MIN(val5t, val7t);
minval = L_MIN(minval + 1, valt);
if (valt > minval) { /* replace */
SET_DATA_TWO_BYTES(linet, j, minval);
if (val5t < val7t)
vald = GET_DATA_BYTE(lined, j + 1);
else
vald = GET_DATA_BYTE(lined + wplt, j);
SET_DATA_BYTE(lined, j, vald);
}
}
}
}
break;
case 8:
/* UL --> LR scan */
for (i = 1; i < h; i++) {
linet = datat + i * wplt;
lined = datad + i * wpld;
for (j = 1; j < jmax; j++) {
if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
val1t = GET_DATA_TWO_BYTES(linet - wplt, j - 1);
val2t = GET_DATA_TWO_BYTES(linet - wplt, j);
val3t = GET_DATA_TWO_BYTES(linet - wplt, j + 1);
val4t = GET_DATA_TWO_BYTES(linet, j - 1);
minval = L_MIN(val1t, val2t);
minval = L_MIN(minval, val3t);
minval = L_MIN(minval, val4t);
minval = L_MIN(minval, 0xfffe);
SET_DATA_TWO_BYTES(linet, j, minval + 1);
if (minval == val1t)
vald = GET_DATA_BYTE(lined - wpld, j - 1);
else if (minval == val2t)
vald = GET_DATA_BYTE(lined - wpld, j);
else if (minval == val3t)
vald = GET_DATA_BYTE(lined - wpld, j + 1);
else /* minval == val4t */
vald = GET_DATA_BYTE(lined, j - 1);
SET_DATA_BYTE(lined, j, vald);
}
}
}
/* LR --> UL scan */
for (i = imax - 1; i > 0; i--) {
linet = datat + i * wplt;
lined = datad + i * wpld;
for (j = jmax - 1; j > 0; j--) {
if ((valt = GET_DATA_TWO_BYTES(linet, j)) > 0) {
val8t = GET_DATA_TWO_BYTES(linet + wplt, j + 1);
val7t = GET_DATA_TWO_BYTES(linet + wplt, j);
val6t = GET_DATA_TWO_BYTES(linet + wplt, j - 1);
val5t = GET_DATA_TWO_BYTES(linet, j + 1);
minval = L_MIN(val8t, val7t);
minval = L_MIN(minval, val6t);
minval = L_MIN(minval, val5t);
minval = L_MIN(minval + 1, valt);
if (valt > minval) { /* replace */
SET_DATA_TWO_BYTES(linet, j, minval);
if (minval == val5t + 1)
vald = GET_DATA_BYTE(lined, j + 1);
else if (minval == val6t + 1)
vald = GET_DATA_BYTE(lined + wpld, j - 1);
else if (minval == val7t + 1)
vald = GET_DATA_BYTE(lined + wpld, j);
else /* minval == val8t + 1 */
vald = GET_DATA_BYTE(lined + wpld, j + 1);
SET_DATA_BYTE(lined, j, vald);
}
}
}
}
break;
default:
ERROR_VOID("connectivity must be 4 or 8", procName);
break;
}
return;
}