1. 程式人生 > >opencv之大津法Otsu介紹

opencv之大津法Otsu介紹

一、大津法(Otsu)

        所謂大津法(Otsu)就是最大類間差方法,通過統計影象直方圖資訊來自動確定閾值T,從而來區分前景與背景,說白了就是能自動區分影象前景與背景的二值化。

演算法流程描述:

1.遍歷影象畫素,統計每個畫素值出現的次數,即255個bin,統計每個bin畫素值的個數;

2.遍歷0到255每個畫素,以i畫素值為當前分類的閾值,在(0~i)範圍內計算前景畫素平均灰度u0,前景部分畫素點數所佔比例w0;在(i~155)計算背景畫素平均灰度u1,背景部分畫素點數所佔比例w1;

3.計算當前類間方差varValueI=w0*w1*(u1-u0)*(u1-u0);

4.選擇最大類間方差varValueI時的i畫素閾值作為區分前景與背景的最佳閾值;

       可喜可賀的是opencv已經把Ostu作為一個閾值分割方法寫進了threshold函式;


#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

double Otsu(Mat&image) {

	int threshold=0;
	double maxVariance = 0;
	double w0=0,w1=0;//前景與背景畫素點所佔比例
	double u0=0,u1=0;//前景與背景畫素值平均灰度
	double histogram[256]={0};
	double Num=image.cols*image.rows;
	//統計256個bin,每個bin畫素的個數
	for(int i=0;i<image.rows;i++){
		const uchar * p = image.ptr<uchar>(i);  
	    for(int j=0;j<image.cols;j++){
			histogram[int(*p++)]++; //cout<<"Histogram[data[i*image.step+j]]++:;"<<histogram[int(*p++)]++<<endl; 
		}
	}
	//前景畫素統計
	for(int i=0;i<255;i++){
		w0=0;
		w1=0;
		u0=0;
		u1=0;
		for(int j=0;j<=i;j++){
			w0=w0+histogram[j];//以i為閾值,統計前景畫素個數
			u0=u0+j*histogram[j];//以i為閾值,統計前景畫素灰度總和
		}
		w0=w0/Num;u0=u0/w0;

	//背景畫素統計
		for(int j=i+1;j<=255;j++){
			w1=w1+histogram[j];//以i為閾值,統計前景畫素個數
			u1=u1+j*histogram[j];//以i為閾值,統計前景畫素灰度總和
		}
		w1=w1/Num;u1=u1/w1;
		double variance=w0*w1*(u1-u0)*(u1-u0); //當前類間方差計算
		 if(variance > maxVariance)     
        {      
            maxVariance = variance;      
            threshold = i;      
        }
	}
	cout<<"threshold:"<<threshold<<endl;
	return threshold;
	}

int main()
{   
    Mat img = imread("D://vvoo//fenge.jpg");
	Mat img1;
	img.copyTo(img1);
	cvtColor(img,img,CV_BGR2GRAY);
	cvtColor(img1,img1,CV_BGR2GRAY);
	double th=Otsu(img);
	cout<<"The return value of getOstu is: "<<th<<endl;  
	cout<<"The return value of opencv threshold is: "<<threshold(img1 , img1 ,0,255,CV_THRESH_OTSU);//opencv已實現的大津法  
	for(int i=0;i<img.rows;i++){
	    for(int j=0;j<img.cols;j++){
			if( img.data[i*img.step + j]<=th)
				img.data[i*img.step + j]=255;
			else
				img.data[i*img.step + j]=0;
		}
	}
	imshow("Ostu_img",img);
	imshow("Opencv_img",img1);
    waitKey(0);  
    return 0;
}

閾值是一樣的。


1. http://blog.csdn.net/ap1005834/article/details/51452516