1.简介:
一维Otsu算法也叫最大类间方差法,是由日本学者大津(Nobuyuki Otsu)于1979年提出的,
是一种图像灰度自适应阈值的分割算法,间称OTSU。
2.算法思想:
根据图像灰度值的特性,将图像分成背景和前景2个部分。背景和前景之间的类间方差越大,
说明构成图像的2部分的差别越大,当部分前景错分背景或者背景错分为前景的时候,会导致2部分的方差变小。
因此,类间方差最 大意味着错分概率最小。
3.算法过程:
(1)对于图像I(x,y),将前景与背景的分割阈值设为T。
(2)将属于前景的像素点的个数占整个图像的比例设为w0,其平均灰度设为u0。
(3)将属于背景的像素点的个数占整个图像的比例设为w1,其平均灰度设为u1。
扫描二维码关注公众号,回复:
648227 查看本文章
(4)图像的总平均灰度设为u,类间方差设为S。
假设图片的大小为M*N,图像中像素灰度值小于阈值T的像素个数记为N0,
像素灰度大于阈值T的像素个数记为N1。则它们之间的关系如下。
4.代码实现(opencv3):
#include "opencv.hpp" #include "imgproc.hpp" #include "highgui.hpp" #include "iostream" #include "core.hpp" using namespace cv; using namespace std; int Otsu(Mat srcimage); //一维Otsu算法 int main() { Mat srcimage, grayimage, dstimage; srcimage = imread("lena.jpg"); namedWindow("原图", 0); imshow("原图", srcimage); //显示原图 cvtColor(srcimage, grayimage, COLOR_RGB2GRAY); //得到灰度图 //imshow("灰度图", grayimage); double time0 = static_cast<double>(getTickCount()); //记录程序开始时间 int thresholdValue = Otsu(grayimage); //调用Otsu函数 time0 = ((double)getTickCount() - time0) / getTickFrequency(); cout << "算法运行时间为:" << time0 << endl; cout << "Otsu阈值为:" << thresholdValue << endl; threshold(grayimage, dstimage, thresholdValue, 255, THRESH_BINARY); //将得到的阈值传入函数,得到分割效果图 namedWindow("Otsu算法结果", 0); imshow("Otsu算法结果", dstimage); waitKey(); return 0; } //一维Otsu算法 int Otsu(Mat srcimage) { if (srcimage.channels() != 1) //图片的通道数为1,即灰度图片 { cout << "请输入灰度图片" << endl; return 0; } int height = srcimage.rows; //rows为图片的行数,相当于高度,对应.y int width = srcimage.cols; //cols为图片的列数,相当于宽度,对应.x long number = height*width; //像素总数 int T = 0; //Otsu算法阈值 double varValue = 0; //类间方差中间值 double w1 = 0; //前景像素点所占比例 double w2 = 0; //背景像素点所占比例 double u1 = 0; //前景平均灰度 double u2 = 0; //背景平均灰度 int Histogram[256] = { 0 }; //灰度直方图,下标是灰度值,保存内容是灰度值对应的像素点总数。 uchar *data = srcimage.data; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { Histogram[data[i*srcimage.step + j]]++;//step指向每行的字节总量,date访问每个像素的值 } } for (int i = 1; i < 255 ;i++)//从1开始遍历,寻找最合适的值 { //每次遍历前需要初始化各变量 w1 = 0; u1 = 0; w2 = 0; u2 = 0; for (int j = 0; j <= i; j++)//背景部分各值计算 { w1 += Histogram[j]; //背景部分像素点总数 u1 += j*Histogram[j]; //背景部分像素总灰度和 } u1 = u1 / w1; //背景像素平均灰度 w1 = w1 / number; //背景部分像素点所占比例 for (int k = i + 1; k < 255; k++) { w2 += Histogram[k]; //前景部分像素点总数 u2 += k*Histogram[k]; //前景部分像素总灰度和 } u2 = u2 / w2; //前景像素平均灰度 w2 = w2 / number; //前景部分像素所占比例 //类间方差计算 double varValueI = w1*w2*(u1 - u2)*(u1 - u2); if ( varValueI>varValue) { varValue = varValueI; T = i; } } cout << T<<endl; return T; }
5.运行结果: