// note that we've not computed the final labels or
// the sorted components yet
this.bComponents = false;
Gray8Image gray = (Gray8Image) image;
byte[] bData = gray.getData();
// for each pixel in the input image assign a label,
// performing equivalence operations when two labels
// are adjacent (8-connected)
for (int i = 0; i < gray.getHeight(); i++) {
int nRow = i * gray.getWidth();
// we use sUpLeft to refer to the pixel up and to
// the left of the current, etc.
EquivalenceClass eUpLeft = null,eUp = null,eUpRight = null;
// after first row, initialize pixels above and
// to the right
if (i > 0) {
eUp = reClasses[nRow - gray.getWidth()];
eUpRight = reClasses[nRow - gray.getWidth() + 1];
}
// starting a new row the pixel to the left is 0
EquivalenceClass eLeft = null;
// nBitPatt encodes the state of the pixels around the
// current pixel. The pattern is
// 8 4 2
// 1 current
int nBitPatt = ((eUp != null) ? 4 : 0) + ((eUpRight != null) ? 2 : 0);
// (at left column eLeft and eUpLeft will always be 0)
for (int j = 0; j < gray.getWidth(); j++) {
if (bData[nRow + j] != Byte.MIN_VALUE) {
switch (nBitPatt) {
// the cases below are derived from the bit
// pattern illustrated above. The general
// rule is to choose the most recently-scanned
// label when copying a label. Of course, we
// also do unions only as necessary
case 0:
// 0 0 0
// 0 X
reClasses[nRow + j] =
new EquivalenceClass();
this.sClasses++;
break;
case 1:
// 0 0 0
// X X
reClasses[nRow + j] = eLeft.find();
break;
case 2:
// 0 0 X
// 0 X
reClasses[nRow + j] = eUpRight.find();
break;
case 3:
// 0 0 X
// X X
eLeft.union(eUpRight);
reClasses[nRow + j] = eLeft.find();
break;
case 4:
// 0 X 0
// 0 X
reClasses[nRow + j] = eUp.find();
break;
case 5:
// 0 X 0
// X X
// we must already have union'ed
// eLeft and eUp
reClasses[nRow + j] = eLeft.find();
break;
case 6:
// 0 X X
// 0 X
// we must already have union'ed
// eUp and eUpRight
reClasses[nRow + j] = eUpRight.find();
break;
case 7:
// 0 X X
// X X
// we must already have union'ed
// eLeft and eUp, and eUp and eUpRight
reClasses[nRow + j] = eLeft.find();
break;
case 8:
// X 0 0
// 0 X
reClasses[nRow + j] = eUpLeft.find();
break;
case 9:
// X 0 0
// X X
// we must already have union'ed
// eLeft and eUpLeft
reClasses[nRow + j] = eLeft.find();
break;
case 10:
// X 0 X
// 0 X
eUpLeft.union(eUpRight);
reClasses[nRow + j] = eUpLeft.find();
break;
case 11:
// X 0 X
// X X
// we must already have union'ed
// eLeft and eUpLeft
eLeft.union(eUpRight);
reClasses[nRow + j] = eLeft.find();
break;
case 12:
// X X 0
// 0 X
// we must already have union'ed
// eUpLeft and eUp
reClasses[nRow + j] = eUp.find();
break;
case 13:
// X X 0
// X X
// we must already have union'ed
// eLeft and eUpLeft, and eUpLeft and eUp
reClasses[nRow + j] = eLeft.find();
break;
case 14:
// X X X
// 0 X
// we must already have union'ed
// eUpLeft, eUp, and eUpRight
reClasses[nRow + j] = eUpRight.find();
break;
case 15:
// X X X
// X X
// we must already have union'ed
// eLeft, eUpLeft, eUp, and eUpRight
reClasses[nRow + j] = eLeft.find();
break;
}
}
// shift right to next pixel
eUpLeft = eUp;
eUp = eUpRight;
eLeft = reClasses[nRow + j];
// if we're not at the right column and after the first
// row read a new right pixel
if (i > 0 && j < gray.getWidth() - 1) {
eUpRight = reClasses[nRow - gray.getWidth() + j + 2];
} else {
eUpRight = null;
}
// compute the new bit pattern. This is the old pattern
// with eUpLeft and eLeft and'ed off (& 6), shifted left,
// with the new eLeft and eUpRight or'ed in
nBitPatt = ((nBitPatt & 6) << 1) + ((eLeft != null) ? 1 : 0) +
((eUpRight != null) ? 2 : 0);
}
}
// initialize the labeled image
this.imLabeled = new Gray16Image(gray.getWidth(), gray.getHeight(),
(short) 0);
short[] sLabels = this.imLabeled.getData();
// assign label pixels their final values
for (int i = 0; i < sLabels.length; i++) {
if (reClasses[i] != null) {