date: 2018-07-16 09:39:40
图像分割-大津法
最大类间方差法是1979年由日本学者大津提出的,是一种自适应阈值确定的方法,又叫大津法,简称OTSU
算法公式
#include "stdio.h"
#include "cv.h"
#include "highgui.h"
#include "Math.h"
int Otsu(IplImage* src);
int main()
{
IplImage* img = cvLoadImage("lena.jpg",0); //获取灰度图像img
IplImage* dst = cvCreateImage(cvGetSize(img), 8, 1);
int threshold = Otsu(img); //调用大津法求出最佳阈值
printf("otsu threshold = %d\n", threshold);
cvThreshold(img, dst, threshold, 255, CV_THRESH_BINARY); //用otsu的阈值二值化
cvNamedWindow( "img", 1 );
cvNamedWindow( "dst", 1 );
cvShowImage("img", img);
cvShowImage("dst", dst);
cvWaitKey(-1);
cvReleaseImage(&img);
cvReleaseImage(&dst);
cvDestroyWindow( "img" );
cvDestroyWindow( "dst" );
return 0;
}
int Otsu(IplImage* src)
{
int height=src->height;
int width=src->width;
//histogram
float histogram[256] = {0};
for(int i=0; i < height; i++)
{
unsigned char* p=(unsigned char*)src->imageData + src->widthStep * i;
for(int j = 0; j < width; j++)
{
histogram[*p++]++;
}
}
//normalize histogram & average pixel value
int size = height * width;
float u =0;
for(int i = 0; i < 256; i++)
{
histogram[i] = histogram[i] / size;
u += i * histogram[i]; //整幅图像的平均灰度
}
int threshold;
float maxVariance=0;
float w0 = 0, avgValue = 0;
for(int i = 0; i < 256; i++)
{
w0 += histogram[i]; //假设当前灰度i为阈值, 0~i 灰度像素所占整幅图像的比例即前景比例
avgValue += i * histogram[i]; //avgValue/w0 = u0
float t = avgValue/w0 - u; //t=u0-u
float variance = t * t * w0 /(1 - w0);
if(variance > maxVariance)
{
maxVariance = variance;
threshold = i;
}
}
return threshold;
}
#include <opencv2/opencv.hpp>
#include <cv.h>
#include <highgui.h>
#include <cxcore.h>
using namespace std;
using namespace cv;
Mat otsuGray(const Mat src) {
Mat img = src;
int c = img.cols; //图像列数
int r = img.rows; //图像行数
int T = 0; //阈值
uchar* data = img.data; //数据指针
int ftNum = 0; //前景像素个数
int bgNum = 0; //背景像素个数
int N = c*r; //总像素个数
int ftSum = 0; //前景总灰度值
int bgSum = 0; //背景总灰度值
int graySum = 0;
double w0 = 0; //前景像素个数占比
double w1 = 0; //背景像素个数占比
double u0 = 0; //前景平均灰度
double u1 = 0; //背景平均灰度
double Histogram[256] = {0}; //灰度直方图
double temp = 0; //临时类间方差
double g = 0; //类间方差
//灰度直方图
for(int i = 0; i < r ; i ++) {
for(int j = 0; j <c; j ++) {
Histogram[img.at<uchar>(i,j)]++;
}
}
//求总灰度值
for(int i = 0; i < 256; i ++) {
graySum += Histogram[i]*i;
}
for(int i = 0; i < 256; i ++) {
ftNum += Histogram[i]; //阈值为i时前景个数
bgNum = N - ftNum; //阈值为i时背景个数
w0 = (double)ftNum/N; //前景像素占总数比
w1 = (double)bgNum/N; //背景像素占总数比
if(ftNum == 0) continue;
if(bgNum == 0) break;
//前景平均灰度
ftSum += i*Histogram[i];
u0 = ftSum/ftNum;
//背景平均灰度
bgSum = graySum - ftSum;
u1 = bgSum/bgNum;
g = w0*w1*(u0-u1)*(u0-u1);
if(g > temp) {
temp = g;
T = i;
}
}
for(int i=0; i<img.rows; i++)
{
for(int j=0; j<img.cols; j++)
{
if((int)img.at<uchar>(i,j)>T)
img.at<uchar>(i,j) = 255;
else
img.at<uchar>(i,j) = 0;
}
}
return img;
}