连通组件标记算法–算法修正版
昨天使用自己修改的连通组件标记算法代码去对一个稍微复杂点的图片做皮肤的最大连通
区域识别,发现Java报了栈溢出错误,自己想了想,感觉是合并标记时其中一段递归的代
码的问题,修改为非递归以后,运行结果吓我一跳,发现更加的不对,最后发现原来我读
取标记时候使用了逻辑操作符&,导致标记超过255时候被低位截取。为了找到这个错误,
花了一个下午的时间重新完成了代码与测试。
一:基本原理如下
以前的有问题的算法版本连接,看这里:
http://blog.csdn.net/jia20003/article/details/7596443
二:完整的算法代码如下:
package com.gloomyfish.face.detection;
import java.util.Arrays;
import java.util.HashMap;
/**
* fast connected component label algorithm
*
* @date 2012-05-23
* @author zhigang
*
*/
public class AdjustableConnectedComponentLabelAlg {
private int bgColor;
public int getBgColor() {
return bgColor;
}
public void setBgColor(int bgColor) {
this.bgColor = bgColor;
}
private int[] outData;
private int dw;
private int dh;
public AdjustableConnectedComponentLabelAlg() {
bgColor = 255; // black color
}
public int[] doLabel(int[] inPixels, int width, int height) {
dw = width;
dh = height;
int nextlabel = 1;
int result = 0;
outData = new int[dw * dh];
for(int i=0; i<outData.length; i++) {
outData[i] = 0;
}
// we need to define these two variable arrays.
int[] eightNeighborhoodPixels = new int[8];
int[] eightNeighborhoodLabels = new int[8];
int[] knownLabels = new int[8];
int srcrgb = 0, index = 0;
boolean existedLabel = false;
for(int row = 0; row < height; row ++) {
for(int col = 0; col < width; col++) {
index = row * width + col;
srcrgb = inPixels[index] & 0x000000ff;
if(srcrgb == bgColor) {
result = 0; // which means no labeled for this pixel.
} else {
// we just find the eight neighborhood pixels.
eightNeighborhoodPixels[0] = getPixel(inPixels, row-1, col); // upper cell
eightNeighborhoodPixels[1] = getPixel(inPixels, row, col-1); // left cell
eightNeighborhoodPixels[2] = getPixel(inPixels, row+1, col); // bottom cell
eightNeighborhoodPixels[3] = getPixel(inPixels, row, col+1); // right cell
// four corners pixels
eightNeighborhoodPixels[4] = getPixel(inPixels, row-1, col-1); // upper left corner
eightNeighborhoodPixels[5] = getPixel(inPixels, row-1, col+1); // upper right corner
eightNeighborhoodPixels[6] = getPixel(inPixels, row+1, col-1); // left bottom corner
eightNeighborhoodPixels[7] = getPixel(inPixels, row+1, col+1); // right bottom corner
// get current possible existed labels
eightNeighborhoodLabels[0] = getLabel(outData, row-1, col); // upper cell
eightNeighborhoodLabels[1] = getLabel(outData, row, col-1); // left cell
eightNeighborhoodLabels[2] = getLabel(outData, row+1, col); // bottom cell
eightNeighborhoodLabels[3] = getLabel(outData, row, col+1); // right cell
// four corners labels value
eightNeighborhoodLabels[4] = getLabel(outData, row-1, col-1); // upper left corner
eightNeighborhoodLabels[5] = getLabel(outData, row-1, col+1); // upper right corner
eightNeighborhoodLabels[6] = getLabel(outData, row+1, col-1); // left bottom corner
eightNeighborhoodLabels[7] = getLabel(outData, row+1, col+1); // right bottom corner
int minLabel = 0;
int count = 0;
for(int i=0; i<knownLabels.length; i++) {
if(eightNeighborhoodLabels[i] > 0) {
existedLabel = true;
knownLabels[i] = eightNeighborhoodLabels[i];
minLabel = eightNeighborhoodLabels[i];
count++;
}
}
if(existedLabel) {
if(count == 1) {
result = minLabel;
} else {
int[] tempLabels = new int[count];
int idx = 0;
for(int i=0; i<knownLabels.length; i++) {
if(knownLabels[i] > 0){
tempLabels[idx++] = knownLabels[i];
}
if(minLabel > knownLabels[i] && knownLabels[i] > 0) {
minLabel = knownLabels[i];
}
}
result = minLabel;
mergeLabels(index, result, tempLabels);
}
} else {
result = nextlabel;
nextlabel++;
}
outData[index] = result;
// reset and cleanup the known labels now...
existedLabel = false;
for(int kl = 0; kl < knownLabels.length; kl++) {
knownLabels[kl] = 0;
}
}
}
}
// labels statistic
HashMap<Integer, Integer> labelMap = new HashMap<Integer, Integer>();
for(int d=0; d<outData.length; d++) {
if(outData[d] != 0) {
if(labelMap.containsKey(outData[d])) {
Integer count = labelMap.get(outData[d]);
count+=1;
labelMap.put(outData[d], count);
} else {
labelMap.put(outData[d], 1);
}
}
}
// try to find the max connected component
Integer[] keys = labelMap.keySet().toArray(new Integer[0]);
Arrays.sort(keys);
int maxKey = 1;
int max = 0;
for(Integer key : keys) {
if(max < labelMap.get(key)){
max = labelMap.get(key);
maxKey = key;
}
System.out.println( "Number of " + key + " = " + labelMap.get(key));
}
System.out.println("maxkey = " + maxKey);
System.out.println("max connected component number = " + max);
return outData;
}
private void mergeLabels (int index, int result, int[] labels) {
for(int i=0; i<labels.length; i++) {
if(labels[i] == result) continue;
mergeLabel(index, result, labels[i]);
}
}
private void mergeLabel(int index, int result, int i) {
int row = index / dw;
int idx = 0;
for(int k = 0; k <= row; k++) {
for(int col = 0; col < dw; col++) {
idx = k * dw + col;
if(outData[idx] == i) {
outData[idx] = result;
}
}
}
}
private int getLabel(int[] data, int row, int col) {
// handle the edge pixels
if(row < 0 || row >= dh) {
return 0;
}
if(col < 0 || col >= dw) {
return 0;
}
int index = row * dw + col;
return (data[index] & 0xffffffff);
// it's difficulty for me to find the data overflow issue......
// return (data[index] & 0x000000ff); defect, @_@
}
private int getPixel(int[] data, int row, int col) {
// handle the edge pixels
if(row < 0 || row >= dh) {
return bgColor;
}
if(col < 0 || col >= dw) {
return bgColor;
}
int index = row * dw + col;
return (data[index] & 0x000000ff);
}
/**
* binary image data:
*
* 255, 0, 0, 255, 0, 255, 255, 0, 255, 255, 255,
* 255, 0, 0, 255, 0, 255, 255, 0, 0, 255, 0,
* 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0,
* 255, 255, 0, 255, 255, 255, 0, 255, 0, 0, 255
* 255, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0
*
* height = 5, width = 11
* @param args
*/
public static int[] imageData = new int[]{
255, 0, 0, 255, 0, 255, 255, 0, 255, 255, 255,
255, 0, 0, 255, 0, 255, 255, 0, 0, 255, 0,
255, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0,
255, 255, 0, 255, 255, 255, 0, 255, 0, 0, 255,
255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255,
255, 255, 0, 0, 255, 255, 255, 0, 0, 255, 255
};
public static void main(String[] args) {
AdjustableConnectedComponentLabelAlg ccl = new AdjustableConnectedComponentLabelAlg();
int[] outData = ccl.doLabel(imageData, 11, 8);
for(int i=0; i<8; i++) {
System.out.println("--------------------");
for(int j = 0; j<11; j++) {
int index = i * 11 + j;
if(j != 0) {
System.out.print(",");
}
System.out.print(outData[index]);
}
System.out.println();
}
}
}