int otsu_threshold(unsigned char *image, int width, int height) {
int hist[256] = {0};
int i, j, total = width * height;
// 计算直方图
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
hist[image[i * width + j]]++;
}
}
float sum = 0; //用于计算灰度级总和
for (i = 0; i < 256; i++) {
sum += i * hist[i];
}
int sumB = 0, wB = 0, wF = 0, threshold = 0;
float varMax = 0;
for (i = 0; i < 256; i++) {
wB += hist[i];
if (wB == 0)
continue;
wF = total - wB;
if (wF == 0)
break;
sumB += i * hist[i];
float mB = (float)sumB / wB; // 直方图左边的平均值
float mF = (float)(sum - sumB) / wF; // 直方图右边的平均值
float varBetween = (float)wB * (float)wF * (mB - mF) * (mB - mF);
if (varBetween > varMax) {
varMax = varBetween;
threshold = i;
}
}
return threshold;
}
以上代码用到了以下变量:
image
: 存储图像数据的指针,类型为unsigned char*
。
width
: 图像宽度。
height
: 图像高度。
hist
:用于存储直方图的数组。
total
:总像素数,即width * height
。
sum
:灰度级总和。
sumB
:前景色像素的灰度级总和。
wB
:前景色像素数。
wF
:背景色像素数。
threshold
:阈值。
varMax
:类间方差最大值。
函数中首先计算出图像的直方图,然后进行阈值迭代计算,最后返回计算得到的阈值。
在主函数中,可以如下使用该函数:
// 加载图像数据
unsigned char *image = load_image(filename, &width, &height);
// 计算阈值
int threshold = otsu_threshold(image, width, height);
// 应用阈值进行二值化
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
if (image[i * width + j] < threshold) {
image[i * width + j] = 0;
} else {
image[i * width + j] = 255;
}
}
}
// 保存二值图像
save_image(bmp, filename, image, width, height);
上述代码示例中的load_image
和save_image
函数没有给出,需要根据实际需求自己实现。