基于前景/背景均值的ImageJ最优二值阈值计算实现与优化问询
分享我在ImageJ中实现Otsu阈值法的过程与代码
最近在研究如何在ImageJ里计算最优阈值,选定了**Otsu Thresholding(大津阈值法)**作为解决方案,但实现过程中踩了不少坑。经过排查修正了权重与均值的计算逻辑后,现在的实现能得出最优阈值77,在硬币图像上几乎可以完美分离背景与硬币(后续可以用来做自动计数、尺寸测量等任务),在光照不均的大米图像上表现也不错。
下面是我的实现代码,希望能得到社区的反馈和优化建议:
public float calculateMeanFG(int[] histogram, int t) { float sumI = 0; int total = 0; //cumulate the histogram for < 256 for (int i = t; i < 256; i++) { sumI += histogram[i] * i; total = i; } return sumI / total; } public float calculateMeanBG(int[] histogram, int t) { float sumI = 0; //cumulate the histogram for < t for (int i = 0; i < t; i++) { sumI += histogram[i] * i; } return sumI; } public float calculateWeightFG(int[] histogram, int t, int total) { int sum = 0; for (int i = t; i < 256; i++) { sum += histogram[i]; } return sum / total; } public int[] getHistogram(ImageProcessor ip, int height, int width) { byte[] outP = ((byte[]) ip.getPixels()).clone(); int[][] inDataArr = new int[width][height]; int[] histogram = new int[256]; int idx = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // fill in values inDataArr[x][y] = outP[idx]; if (inDataArr[x][y] < 0) { inDataArr[x][y] += 256; } histogram[inDataArr[x][y]] += 1; // count grayscale occurrences idx++; } // for x } // for y return histogram; } public int[][] convergeOptThresh(int[][] imgArr, int width, int height) { int BG_VAL = 0; int FG_VAL = 255; int[] histogram = getHistogram(ip, height, width); // total number of pixels int total = imgArr.length; // cumulative hist float sum = 0; for (int i = 0; i < 256; i++) sum += i * histogram[i]; float sumBG = 0; // sum background float weightBG = 0; float weightFG = 0; float varMax = 0; int threshold = 0; for (int t = 0; t < 256; t++) { weightBG = calculateMeanBG(histogram, t); weightBG /= total; weightFG = calculateWeightFG(histogram, t, total); if ((int)weightFG == 0) break; sumBG += (float) (t * histogram[t]); float meanBG = sumBG / t; float meanFG = calculateMeanFG(histogram, t); // calculate between class variance float varBetween = weightBG * weightFG * (meanBG - meanFG) * (meanBG - meanFG); // check if new max found if (varBetween > varMax) { varMax = varBetween; threshold = t; } } IJ.log("optimal threshold: " + threshold); int[][] retArr = new int[width][height]; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { if (imgArr[x][y] <= threshold) { retArr[x][y] = BG_VAL; } else { retArr[x][y] = FG_VAL; } } } return retArr; }
这次作业难度不小,但整个过程让我收获特别多,期待大家的建议!
内容的提问来源于stack exchange,提问作者gizmo




