2. ostu自适应二值化
Otsu算法原理:
对于图像 I(x,y),前景(即目标)和背景的分割阈值记作 T,属于前景的像素点数占整幅图像的比例记为 ω0,平均灰度为 μ0;背景像素点数占整幅图像的比例为 ω1,平均灰度为 μ1;整幅图像的平均灰度记为μ,类间方差记为g。
假设图像大小为M×N,图像中像素的灰度值小于阈值 T 的像素个数为 N0,像素灰度大于阈值T的像素个数为 N1,那么:
ω0=N0/ M×N (1)
ω1=N1/ M×N (2)
N0+N1=M×N (3)
ω0+ω1=1 (4)
μ=ω0*μ0+ω1*μ1 (5)
g=ω0(μ0-μ)^2+ω1(μ1-μ)^2 (6)
g=ω0ω1(μ0-μ1)^2 (7)
特别说明:
【1】实现过程中构造了三个容器,s[i] 表示像素值小于 i 的像素个数;m[i] 表示像素值小于 i 的平均像素;p[i] 表示像素值小于 i 的 m[i] *i 的所有值;
【2】我本想利用中值法来确定最佳的分割点,但没有很好的思路。当前使用的是遍历法来求值。
代码如下:
#include "opencv.h" //大津展之算法 void otsu(cv::Mat& src, cv::Mat& dst) { if (src.empty()) { cout << "Source image is empty ..." << endl; return; } cv::resize(src, dst, cv::Size(src.cols, src.rows)); vector<int> s(256, 0);//积分 像素个数 vector<int> m(256, 0);//存储前n个像素均值 vector<long> p(256, 0);//积分 像素*个数 for (int i = 0; i < src.rows; i++) { uchar* temp = src.ptr<uchar>(i); for (int j = 0; j < src.cols; j++) s[temp[j]]++; } for (int i = 1; i < 256; i++) { s[i] += s[i - 1]; p[i] = p[i - 1] + s[i] * i; m[i] = p[i] / s[i]; } int result = 0; int t; for (int i = 0; i < 256; i++) { float w0 = 1.0*s[i] / s[255]; float w1 = 1 - w0; float u0 = m[i]; float u1 = p[255] / s[i] - m[i]; if (result < (int)(w0*w1*(u1 - u0)*(u1 - u0))) { result = (int)(w0*w1*(u1 - u0)*(u1 - u0)); t = i; } } for (int i = 0; i < dst.rows; i++) { uchar* temp = dst.ptr<uchar>(i); for (int j = 0; j < dst.cols; j++) temp[j] = temp[j] < t ? 0 : 255; } } int main() { string filename = "autum.jpg"; cv::Mat src, dst; src = cv::imread(filename); cv::cvtColor(src, src, cv::COLOR_RGB2GRAY); otsu(src, dst); cv::namedWindow("img", 0); cv::imshow("img", src); cv::namedWindow("otsu", 0); cv::imshow("otsu", dst); cv::waitKey(); return 0; }