阈值分割算法_阈值分割算法 全局 局部 - CSDN
精华内容
参与话题
  • 图像处理——常用阈值分割方法及源码

    万次阅读 多人点赞 2018-07-18 20:37:18
    1、Otsu阈值分割 2、自适应阈值分割 3、 最大熵阈值分割法 4、 迭代阈值分割  5、测验   1、Otsu阈值分割  Otsu(大津法或最大类间方差法)使用的是聚类的思想,把图像的灰度数按灰度级分成2个部分,...

    目录

     

    1、Otsu阈值分割

    2、自适应阈值分割

    3、 最大熵阈值分割法

    4、 迭代阈值分割

     5、测验

     

    1、Otsu阈值分割

            Otsu(大津法或最大类间方差法)使用的是聚类的思想,把图像的灰度数按灰度级分成2个部分,使得两个部分之间的灰度值差异最大,每个部分之间的灰度差异最小,通过方差的计算来寻找一个合适的灰度级别来划分。 所以可以在二值化的时候采用otsu算法来自动选取阈值进行二值化。otsu算法被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。因此,使类间方差最大的分割意味着错分概率最小。

    参考链接:https://www.cnblogs.com/moon1992/p/5092726.html

    opencv调用格式为:threshold(sourceImage, dstImage, 0, 255, CV_THRESH_OTSU);

    sourceImage:输入图像;

    dstImage:输出图像;

    otsu算法的源码如下:

    Mat OtsuAlgThreshold(Mat &image)
    {
    	if (image.channels() != 1)
    	{
    		cout << "Please input Gray-image!" << endl;
    	}
    	int T = 0; //Otsu算法阈值  
    	double varValue = 0; //类间方差中间值保存
    	double w0 = 0; //前景像素点数所占比例  
    	double w1 = 0; //背景像素点数所占比例  
    	double u0 = 0; //前景平均灰度  
    	double u1 = 0; //背景平均灰度  
    	double Histogram[256] = { 0 }; //灰度直方图,下标是灰度值,保存内容是灰度值对应的像素点总数  
    	uchar *data = image.data;
    
    	double totalNum = image.rows*image.cols; //像素总数
    
    	for (int i = 0; i < image.rows; i++)
    	{
    		for (int j = 0; j < image.cols; j++)
    		{
    			if (image.at<uchar>(i, j) != 0) Histogram[data[i*image.step + j]]++;
    		}
    	}
    	int minpos, maxpos;
    	for (int i = 0; i < 255; i++)
    	{
    		if (Histogram[i] != 0)
    		{
    			minpos = i;
    			break;
    		}
    	}
    	for (int i = 255; i > 0; i--)
    	{
    		if (Histogram[i] != 0)
    		{
    			maxpos = i;
    			break;
    		}
    	}
    
    	for (int i = minpos; i <= maxpos; i++)
    	{
    		//每次遍历之前初始化各变量  
    		w1 = 0;       u1 = 0;       w0 = 0;       u0 = 0;
    		//***********背景各分量值计算**************************  
    		for (int j = 0; j <= i; j++) //背景部分各值计算  
    		{
    			w1 += Histogram[j];   //背景部分像素点总数  
    			u1 += j*Histogram[j]; //背景部分像素总灰度和  
    		}
    		if (w1 == 0) //背景部分像素点数为0时退出  
    		{
    			break;
    		}
    		u1 = u1 / w1; //背景像素平均灰度  
    		w1 = w1 / totalNum; // 背景部分像素点数所占比例
    		//***********背景各分量值计算**************************  
    
    	    //***********前景各分量值计算**************************  
    		for (int k = i + 1; k < 255; k++)
    		{
    			w0 += Histogram[k];  //前景部分像素点总数  
    			u0 += k*Histogram[k]; //前景部分像素总灰度和  
    		}
    		if (w0 == 0) //前景部分像素点数为0时退出  
    		{
    			break;
    		}
    		u0 = u0 / w0; //前景像素平均灰度  
    		w0 = w0 / totalNum; // 前景部分像素点数所占比例  
    		//***********前景各分量值计算**************************  
    
    		//***********类间方差计算******************************  
    		double varValueI = w0*w1*(u1 - u0)*(u1 - u0); //当前类间方差计算  
    		if (varValue < varValueI)
    		{
    			varValue = varValueI;
    			T = i;
    		}
    	}
    	Mat dst;
    	threshold(image, dst, T, 255, CV_THRESH_OTSU);
    	return dst;
    }

    2、自适应阈值分割

    这一部分讲解的是opencv自带的adaptiveThreshold()函数,其算法流程:

             二值化算法是用输入像素的值I与一个值C来比较,根据比较结果确定输出值。

            自适应二值化的每一个像素的比较值C都不同,比较值C由这个像素为中心的一个块范围计算在减去差值delta得到。

            其中,C的常用计算方法有两种:

             a、平均值减去差值delta(使用盒过滤boxfilter,性能会非常不错)

             b、高斯分布加权和减去差值delta (使用高斯滤波GaussionBlur)

    adaptiveThreshold()源码如下:

    void myadaptive(InputArray _src, OutputArray _dst, double maxValue,
    	int method, int type, int blockSize, double delta)
    {
    	Mat src = _src.getMat();
    
    	CV_Assert(src.type() == CV_8UC1);
    	CV_Assert(blockSize % 2 == 1 && blockSize > 1);
    	Size size = src.size();
    
    	_dst.create(size, src.type());
    	Mat dst = _dst.getMat();
    
    	if (maxValue < 0)
    	{
    		dst = Scalar(0);
    		return;
    	}
    
    	Mat mean;
    	if (src.data != dst.data)
    		mean = dst;
    	if (method == ADAPTIVE_THRESH_GAUSSIAN_C)
    	{
    		GaussianBlur(src, mean, Size(blockSize, blockSize), 0, 0, BORDER_REPLICATE);
    	}
    	else if (method == ADAPTIVE_THRESH_MEAN_C)
    	{
    		boxFilter(src, mean, src.type(), Size(blockSize, blockSize),
    			Point(-1, -1), true, BORDER_REPLICATE);
    	}
    	else
    	{
    		CV_Error(CV_StsBadFlag, "Unknown/unsupported adaptive threshold method");
    	}
    
    	int i, j;
    	uchar imaxval = saturate_cast<uchar>(maxValue);
    	int idelta = type == THRESH_BINARY ? cvCeil(delta) : cvFloor(delta);
    	uchar tab[768];
    
    	if (type == CV_THRESH_BINARY)
    		for (i = 0; i < 768; i++)
    			tab[i] = (uchar)(i - 255 > -idelta ? imaxval : 0);
    	else if (type == CV_THRESH_BINARY_INV)
    		for (i = 0; i < 768; i++)
    			tab[i] = (uchar)(i - 255 <= -idelta ? imaxval : 0);
    	else
    	{
    		CV_Error(CV_StsBadFlag, "Unknown/unsupported threshold type");
    	}
    
    	if (src.isContinuous() && mean.isContinuous() && dst.isContinuous())
    	{
    		size.width *= size.height;
    		size.height = 1;
    	}
    
    	for (i = 0; i < size.height; i++)
    	{
    		const uchar* sdata = src.data + src.step*i;
    		const uchar* mdata = mean.data + mean.step*i;
    		uchar* ddata = dst.data + dst.step*i;
    
    		for (j = 0; j < size.width; j++)
    			// 将[-255, 255] 映射到[0, 510]然后查表
    			ddata[j] = tab[sdata[j] - mdata[j] + 255];
    	}
    }

    3、 最大熵阈值分割法

    参考链接:https://blog.csdn.net/qq_27668313/article/details/77949596

    https://blog.csdn.net/robin__chou/article/details/53931442

    https://blog.csdn.net/xw20084898/article/details/22760169

    最大熵阈值分割函数源码如下:

    Mat EntropySeg(Mat src)
    {
    	int tbHist[256] = { 0 };
    	int index = 0;
    	double Property = 0.0;
    	double maxEntropy = -1.0;
    	double frontEntropy = 0.0;
    	double backEntropy = 0.0;
    	int TotalPixel = 0;
    	int nCol = src.cols*src.channels();
    	for (int i = 0; i < src.rows; i++)
    	{
    		uchar* pData = src.ptr<uchar>(i);
    		for (int j = 0; j < nCol; j++)
    		{
    			++TotalPixel;
    			tbHist[pData[j]] += 1;
    		}
    	}
    
    	for (int i = 0; i < 256; i++)
    	{
    		double backTotal = 0;
    		for (int j = 0; j < i; j++)
    		{
    			backTotal += tbHist[j];
    		}
    
    		for (int j = 0; j < i; j++)
    		{
    			if (tbHist[j] != 0)
    			{
    				Property = tbHist[j] / backTotal;
    				backEntropy += -Property*logf((float)Property);
    			}
    		}
    
    		for (int k = i; k < 256; k++)
    		{
    			if (tbHist[k] != 0)
    			{
    				Property = tbHist[k] / (TotalPixel - backTotal);
    				frontEntropy += -Property * logf((float)Property);
    			}
    		}
    
    		if (frontEntropy + backEntropy > maxEntropy) 
    		{
    			maxEntropy = frontEntropy + backEntropy;
    			index = i;
    		}
    
    		frontEntropy = 0.0;
    		backEntropy = 0.0;
    	}
    
    	Mat dst;
    	threshold(src, dst, index, 255, 0);
    	return dst;
    }

    4、 迭代阈值分割

    通过迭代方法选择阈值, 计算方法如下:

    (1)选择灰度图的平均值作为初始阈值T0 ;

    (2)计算小于等于T0的平均值T1, 和大于T0的平均值T2;

    (3)新的阈值为T = (T1 + T2)/ 2;

    (4)比较T和T0,若相等,则返回T,即为迭代阈值; 否则 T0 = T,重复(1)-(3)

    迭代阈值分割的源码如下:

    Mat IterationThreshold(Mat src)
    {
    	int width = src.cols;
    	int height = src.rows;
    	int hisData[256] = { 0 };
    	for (int j = 0; j < height; j++)
    	{
    		uchar* data = src.ptr<uchar>(j);
    		for (int i = 0; i < width; i++)
    			hisData[data[i]]++;
    	}
    
    	int T0 = 0;
    	for (int i = 0; i < 256; i++)
    	{
    		T0 += i*hisData[i];
    	}
    	T0 /= width*height;
    
    	int T1 = 0, T2 = 0;
    	int num1 = 0, num2 = 0;
    	int T = 0;
    	while (1)
    	{
    		for (int i = 0; i < T0 + 1; i++)
    		{
    			T1 += i*hisData[i];
    			num1 += hisData[i];
    		}
    		if (num1 == 0)
    			continue;
    		for (int i = T0 + 1; i < 256; i++)
    		{
    			T2 += i*hisData[i];
    			num2 += hisData[i];
    		}
    		if (num2 == 0)
    			continue;
    
    		T = (T1 / num1 + T2 / num2) / 2;
    
    		if (T == T0)
    			break;
    		else
    			T0 = T;
    	}
    
    	Mat dst;
    	threshold(src, dst, T, 255, 0);
    	return dst;
    }

     5、测验

    void main()
    {
    	Mat src = imread("1.jpg");
    	cvtColor(src, src, COLOR_RGB2GRAY);
    
    	Mat bw1, bw2, bw3, bw4;
    	myadaptive(src, bw1, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 15, 10);
    	bw2 = EntropySeg(src);
    	bw3 = OtsuAlgThreshold(src);
    	bw4 = IterationThreshold(src);
    
    	imshow("source", src);
    	imshow("自适应阈值分割", bw1);
    	imshow("最大熵阈值分割", bw2);
    	imshow("Otsu阈值分割", bw3);
    	imshow("迭代阈值分割", bw4);
    	waitKey(0);
    }

    测验结果:

     

     

     

    展开全文
  • 阈值分割方法总结

    千次阅读 2019-02-25 16:50:40
    阈值是界限的意思,阈值分割就是以一个合适的像素值作为界限将图像处理成高对比度、容易识别的图像的一种方法。 threshold() double cv::threshold( cv::InputArray src, // 输入图像 cv::OutputArray dst, // ...

    阈值是界限的意思,阈值分割就是以一个合适的像素值作为界限将图像处理成高对比度、容易识别的图像的一种方法。
    threshold()

    double cv::threshold(
        cv::InputArray src, // 输入图像
        cv::OutputArray dst, // 输出图像
        double thresh, // 阈值
        double maxValue, // 向上最大值
        int thresholdType // 阈值化操作的类型 
    )
    

    在这里插入图片描述
    举个例子理解下thresholdType,拿第一种方法为例。像素值如果大于阈值,则把像素值变成255,否则,变成0.这样以来,就把原图变成黑白两色,对比度明显。

    threshold(image, t_image,120, 255,CV_THRESH_BINARY); 
    

    自适应阈值分割

    void cv::adaptiveThreshold(  
        cv::InputArray src, // 输入图像  
        cv::OutputArray dst, // 输出图像  
        double maxValue, // 向上最大值  
        int adaptiveMethod, // 自适应方法,平均或高斯  
        int thresholdType // 阈值化类型  
        int blockSize, // 块大小  
        double C // 常量  
    ); 
    

    adaptiveThreshold()支持两种自适应方法,cv::ADAPTIVE_THRESH_MEAN_C(平均)和cv::ADAPTIVE_THRESH_GAUSSIAN_C(高斯)。

     adaptiveThreshold(image, src, 255, ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY,5,20);
    

    自适应阈值不用自己选择,该函数根据图像情况自己设定。
    在这里插入图片描述
    该图是经canny检测之后的阈值分割图。

    阈值分割还有一种方法,直接说明:

    Mat image = imread("D:\\program\\bu1.png",0);//读入模板图
    	imshow("原图",image);
    	waitKey(0);
    	Mat dstImg(image.size(), CV_8UC3, Scalar::all(0));//纯黑图像
    
        int u1 = image.rows;//阈值化处理
    	int u2 = image.cols;
    	for (int i = 0; i < image.rows; i++)
    	{
    		for (int j = 0; j < image.cols; j++)
    		{
    			
    	        sum = sum + image.at<uchar>(i, j);
    		}
    	}
    	int u = u1*u2;
    	int average = (int)sum/u*2;
    	threshold(image, t_image, average, 255,CV_THRESH_BINARY); 
    	Canny(src,t_image,50,50);  
    	imshow("阈值分割图",t_image);
    	waitKey(0);
    

    该方法简单明了,阈值取值适中,不错。

    展开全文
  • 阈值分割法——最佳阈值的选择问题

    万次阅读 多人点赞 2019-10-08 21:30:01
    阈值分割法可以说是图像分割中的经典方法,它利用图像中要提取的目标与背景在灰度上的差异,通过设置阈值来把像素级分成若干类,从而实现目标与背景的分离。 一般流程:通过判断图像中每一个像素点的特征属性是否...

        阈值分割法可以说是图像分割中的经典方法,它利用图像中要提取的目标与背景在灰度上的差异,通过设置阈值来把像素级分成若干类,从而实现目标与背景的分离。

        一般流程:通过判断图像中每一个像素点的特征属性是否满足阈值的要求,来确定图像中的该像素点是属于目标区域还是背景区域,从而将一幅灰度图像转换成二值图像。

    用数学表达式来表示,则可设原始图像f(x,y),T为阈值,分割图像时则满足下式:

                                                                         

        阈值分割法计算简单,而且总能用封闭且连通的边界定义不交叠的区域,对目标与背景有较强对比的图像可以得到较好的分割效果。但是,关键问题来了,如何获得一个最优阈值呢?????

    以下是几种最优阈值的选择方法:

    (1)人工经验选择法:

        也就是我们自己根据需要处理的图像的先验知识,对图像中的目标与背景进行分析。通过对像素的判断,图像的分析,选择出阈值值所在的区间,并通过实验进行对比,最后选择出比较好的阈值。这种方法虽然能用,但是效率较低且不能实现自动的阈值选取。对于样本图片较少时,可以选用。

    (2)利用直方图

        利用直方图进行分析,并根据直方图的波峰和波谷之间的关系,选择出一个较好的阈值。这样方法,准确性较高,但是只对于存在一个目标和一个背景的,且两者对比明显的图像,且直方图是双峰的那种最有价值。

    (3)最大类间方差法(OTSU)

        OTSU是一种使用最大类间方差的自动确定阈值的方法。是一种基于全局的二值化算法,它是根据图像的灰度特性,将图像分为前景和背景两个部分。当取最佳阈值时,两部分之间的差别应该是最大的,在OTSU算法中所采用的衡量差别的标准就是较为常见的最大类间方差。前景和背景之间的类间方差如果越大,就说明构成图像的两个部分之间的差别越大,当部分目标被错分为背景或部分背景被错分为目标,都会导致两部分差别变小,当所取阈值的分割使类间方差最大时就意味着错分概率最小。

     

        记T为前景与背景的分割阈值,前景点数占图像比例为w0,平均灰度为u0;背景点数占图像比例为w1,平均灰度为u1,图像的总平均灰度为u,前景和背景图象的方差g,则有:

                                                  

     

    联立上式得:

                                                                 

    或:

                                                                           

     

            当方差g最大时,可以认为此时前景和背景差异最大,此时的灰度T是最佳阈值。类间方差法对噪声以及目标大小十分敏感,它仅对类间方差为单峰的图像产生较好的分割效果。当目标与背景的大小比例悬殊时(例如受光照不均、反光或背景复杂等因素影响),类间方差准则函数可能呈现双峰或多峰,此时效果不好。

    (4)自适应阈值法:

        上面的最大类间方差阈值分割法在分割过程中对图像上的每个像素都使用了相等的阈值。但在实际情况中,当照明不均匀、有突发噪声或者背景变化较大时,整幅图像分割时将没有合适的单一阈值,如果仍采用单一的阈值去处理每一个像素,可能会将目标和背景区域错误划分。而自适应阈值分割的思想,将图像中每个像素设置可能不一样的阈值。

    基本原理:

        一种较为简单的自适应阈值选取方法是对每个像素确定以其自身为中心的一个领域窗口,寻找窗口内像素的最大值与最小值,并取二者的平均值作为阈值,或者将窗口内所有像素的平均值作为阈值,亦或者将窗口内的所有像素的高斯卷积作为阈值。

     

    。你发大时,可以认为此时前景和背景差异最大,此时的灰度T是最佳阈值。类间方差法对噪声以及目标大小十分敏感,它仅对类间方差为单峰的图像产生较好的分割效果。当目标与背景的大小比例悬殊时(例如受光照不均、反光或背景复杂等因素影响),类间方差准则函数可能呈现双峰或多峰,此时效果)自适应阈值法

    展开全文
  • 自适应阈值分割—大津法(OTSU算法)C++实现

    万次阅读 多人点赞 2018-12-26 16:23:37
    大津法是一种图像灰度自适应的阈值分割算法,是1979年由日本学者大津提出,并由他的名字命名的。大津法按照图像上灰度值的分布,将图像分成背景和前景两部分看待,前景就是我们要按照阈值分割出来的部分。背景和前景...

    大津法是一种图像灰度自适应的阈值分割算法,是1979年由日本学者大津提出,并由他的名字命名的。大津法按照图像上灰度值的分布,将图像分成背景和前景两部分看待,前景就是我们要按照阈值分割出来的部分。背景和前景的分界值就是我们要求出的阈值。遍历不同的阈值,计算不同阈值下对应的背景和前景之间的类内方差,当类内方差取得极大值时,此时对应的阈值就是大津法(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)


    将式(5)代入式(6),得到等价公式:


          g=ω0ω1(μ0-μ1)^2   (7) 这个就是类间方差的公式表述


    采用遍历的方法得到使类间方差g最大的阈值T,即为所求。

     

    Otsu实现思路

     

    1. 计算0~255各灰阶对应的像素个数,保存至一个数组中,该数组下标是灰度值,保存内容是当前灰度值对应像素数

    2. 计算背景图像的平均灰度、背景图像像素数所占比例

    3. 计算前景图像的平均灰度、前景图像像素数所占比例

    4. 遍历0~255各灰阶,计算并寻找类间方差极大值

     

    C++代码实现:

     

    #include <opencv2/highgui/highgui.hpp>  
    #include <opencv2/imgproc/imgproc.hpp>  
    #include <opencv2/core/core.hpp> 
    #include <iostream>
    
    using namespace cv;
    using namespace std;
    
    //***************Otsu算法通过求类间方差极大值求自适应阈值******************
    int OtsuAlgThreshold(const Mat image);
    
    int main(int argc,char *argv[])  
    {  
    	Mat image=imread(argv[1]);
    	imshow("SoureImage",image);
    	cvtColor(image,image,CV_RGB2GRAY);	
    	Mat imageOutput;
    	Mat imageOtsu;	
    	int thresholdValue=OtsuAlgThreshold(image);
    	cout<<"类间方差为: "<<thresholdValue<<endl;
    	threshold(image,imageOutput,thresholdValue,255,CV_THRESH_BINARY);
    	threshold(image,imageOtsu,0,255,CV_THRESH_OTSU); //Opencv Otsu算法
    	//imshow("SoureImage",image);
    	imshow("Output Image",imageOutput);
    	imshow("Opencv Otsu",imageOtsu);	
    	waitKey();
    	return 0;  
    }  
    int OtsuAlgThreshold(const Mat image)
    {
    	if(image.channels()!=1)
    	{
    		cout<<"Please input Gray-image!"<<endl;
    		return 0;
    	}
    	int T=0; //Otsu算法阈值
    	double varValue=0; //类间方差中间值保存
    	double w0=0; //前景像素点数所占比例
    	double w1=0; //背景像素点数所占比例
    	double u0=0; //前景平均灰度
    	double u1=0; //背景平均灰度
    	double Histogram[256]={0}; //灰度直方图,下标是灰度值,保存内容是灰度值对应的像素点总数
    	uchar *data=image.data;
    	double totalNum=image.rows*image.cols; //像素总数
    	//计算灰度直方图分布,Histogram数组下标是灰度值,保存内容是灰度值对应像素点数
    	for(int i=0;i<image.rows;i++)   //为表述清晰,并没有把rows和cols单独提出来
    	{
    		for(int j=0;j<image.cols;j++)
    		{
    			Histogram[data[i*image.step+j]]++;
    		}
    	}
    	for(int i=0;i<255;i++)
    	{
    		//每次遍历之前初始化各变量
    		w1=0;		u1=0;		w0=0;		u0=0;
    		//***********背景各分量值计算**************************
    		for(int j=0;j<=i;j++) //背景部分各值计算
    		{
    			w1+=Histogram[j];  //背景部分像素点总数
    			u1+=j*Histogram[j]; //背景部分像素总灰度和
    		}
    		if(w1==0) //背景部分像素点数为0时退出
    		{
    			continue;
    		}
    		u1=u1/w1; //背景像素平均灰度
    		w1=w1/totalNum; // 背景部分像素点数所占比例
    		//***********背景各分量值计算**************************
    
    		//***********前景各分量值计算**************************
    		for(int k=i+1;k<255;k++)
    		{
    			w0+=Histogram[k];  //前景部分像素点总数
    			u0+=k*Histogram[k]; //前景部分像素总灰度和
    		}
    		if(w0==0) //前景部分像素点数为0时退出
    		{
    			break;
    		}
    		u0=u0/w0; //前景像素平均灰度
    		w0=w0/totalNum; // 前景部分像素点数所占比例
    		//***********前景各分量值计算**************************
    
    		//***********类间方差计算******************************
    		double varValueI=w0*w1*(u1-u0)*(u1-u0); //当前类间方差计算
    		if(varValue<varValueI)
    		{
    			varValue=varValueI;
    			T=i;
    		}
    	}
    	return T;
    }

     

     

    原图像:


     

    该幅图像计算出来的大津阈值是104;

    用这个阈值分割的图像:

     

    跟Opencv threshold方法中使用CV_THRESH_OTSU参数计算出来的分割图像一致:

     

    直方图直观理解

     

    大津算法可以从图像直方图上有一个更为直观的理解:大津阈值大致上是直方图两个峰值之间低谷的值。

    对上述代码稍加修改,增加画出直方图部分:

     

    #include <opencv2/highgui/highgui.hpp>  
    #include <opencv2/imgproc/imgproc.hpp>  
    #include <opencv2/core/core.hpp> 
    #include <iostream>
    
    using namespace cv;
    using namespace std;
    
    //***************Otsu算法通过求类间方差极大值求自适应阈值******************
    int OtsuAlgThreshold(const Mat image);
    
    int main(int argc,char *argv[])  
    {  
    	Mat image=imread(argv[1]);
    	imshow("SoureImage",image);
    	cvtColor(image,image,CV_RGB2GRAY);	
    	Mat imageOutput;
    	Mat imageOtsu;	
    	int thresholdValue=OtsuAlgThreshold(image);
    	cout<<"类间方差为: "<<thresholdValue<<endl;
    	threshold(image,imageOutput,thresholdValue,255,CV_THRESH_BINARY);
    	threshold(image,imageOtsu,0,255,CV_THRESH_OTSU); //Opencv Otsu算法
    	//imshow("SoureImage",image);
    	imshow("Output Image",imageOutput);
    	imshow("Opencv Otsu",imageOtsu);	
    	waitKey();
    	return 0;  
    }  
    int OtsuAlgThreshold(const Mat image)
    {
    	if(image.channels()!=1)
    	{
    		cout<<"Please input Gray-image!"<<endl;
    		return 0;
    	}
    	int T=0; //Otsu算法阈值
    	double varValue=0; //类间方差中间值保存
    	double w0=0; //前景像素点数所占比例
    	double w1=0; //背景像素点数所占比例
    	double u0=0; //前景平均灰度
    	double u1=0; //背景平均灰度
    	double Histogram[256]={0}; //灰度直方图,下标是灰度值,保存内容是灰度值对应的像素点总数
    	int Histogram1[256]={0}; 
    	uchar *data=image.data;
    	double totalNum=image.rows*image.cols; //像素总数
    	//计算灰度直方图分布,Histogram数组下标是灰度值,保存内容是灰度值对应像素点数
    	for(int i=0;i<image.rows;i++)   //为表述清晰,并没有把rows和cols单独提出来
    	{
    		for(int j=0;j<image.cols;j++)
    		{
    			Histogram[data[i*image.step+j]]++;
    			Histogram1[data[i*image.step+j]]++;
    		}
    	}
    
    	//***********画出图像直方图********************************
    	Mat image1(255,255,CV_8UC3);
    	for(int i=0;i<255;i++)
    	{
    		Histogram1[i]=Histogram1[i]%200;
    		line(image1,Point(i,235),Point(i,235-Histogram1[i]),Scalar(255,0,0),1,8,0);
    		if(i%50==0)
    		{
    			char ch[255];
    			sprintf(ch,"%d",i);
    			string str=ch;
    			putText(image1,str,Point(i,250),1,1,Scalar(0,0,255));
    		}
    	}
    	//***********画出图像直方图********************************
    
    	for(int i=0;i<255;i++)
    	{
    		//每次遍历之前初始化各变量
    		w1=0;		u1=0;		w0=0;		u0=0;
    		//***********背景各分量值计算**************************
    		for(int j=0;j<=i;j++) //背景部分各值计算
    		{
    			w1+=Histogram[j];  //背景部分像素点总数
    			u1+=j*Histogram[j]; //背景部分像素总灰度和
    		}
    		if(w1==0) //背景部分像素点数为0时退出
    		{
    			break;
    		}
    		u1=u1/w1; //背景像素平均灰度
    		w1=w1/totalNum; // 背景部分像素点数所占比例
    		//***********背景各分量值计算**************************
    
    		//***********前景各分量值计算**************************
    		for(int k=i+1;k<255;k++)
    		{
    			w0+=Histogram[k];  //前景部分像素点总数
    			u0+=k*Histogram[k]; //前景部分像素总灰度和
    		}
    		if(w0==0) //前景部分像素点数为0时退出
    		{
    			break;
    		}
    		u0=u0/w0; //前景像素平均灰度
    		w0=w0/totalNum; // 前景部分像素点数所占比例
    		//***********前景各分量值计算**************************
    
    		//***********类间方差计算******************************
    		double varValueI=w0*w1*(u1-u0)*(u1-u0); //当前类间方差计算
    		if(varValue<varValueI)
    		{
    			varValue=varValueI;
    			T=i;
    		}
    	}
    	//画出以T为阈值的分割线
    	line(image1,Point(T,235),Point(T,0),Scalar(0,0,255),2,8);
    	imshow("直方图",image1);
    	return T;
    }


    为显示清晰,本次使用一幅对比明显的灰度图:

     

     

    OTSU分割效果:

     

    对应阈值和直方图:

     

    以上图像黑白对比度非常明显,从直方图上也可以看到只有两个波峰,求得的OTSU阈值为102。

    上图中红色的竖线标识出了OTSu阈值分割线,显见,阈值大致位于两个波峰的低谷之间。

     

     

    展开全文
  • 基于阈值的图像分割

    万次阅读 2018-05-23 14:46:00
    1. 直方图双峰法(mode 法) Prewitt 等人于六十年代中期提出的直方图双峰法(也称 mode 法) 是典型的全局单阈值分割方法。该方法的基本思想是:假设图像中有明显的目标和背景,则其灰度直方图呈双峰分布,当...
  • 最大熵阈值分割算法原理及实现

    千次阅读 热门讨论 2019-05-14 19:41:01
    前面介绍了OTSU算法,对于阈值分割法,不得不介绍另外一种较为突出的算法——最大熵阈值分割法(KSW熵算法)。 最大熵阈值分割法和OTSU算法类似,假设将图像分为背景和前景两个部分。熵代表信息量,图像信息量越大...
  • otsu阈值分割

    千次阅读 2018-11-06 21:07:16
      OTSU算法是由日本学者OTSU于1979年提出的一种对图像进行二值化的高效算法,是一种自适应的阈值确定的方法,又称大津阈值分割法,是最小二乘法意义下的最优分割。 二、单阈值OTSU法   设图像包含L个灰度级,...
  • 图像处理基本算法 动态阈值分割

    万次阅读 多人点赞 2012-02-11 16:18:32
    在图像处理时,受外界光线的干扰一般比较大,假如在阈值分割时采用固 定阈值,那么在环境改变时分割效果受影响极大,那么为了避免此影响就 必须采用动态阈值,自动求出合适的阈值进行分割。 本文的介绍几...
  • 图像的阈值分割(迭代法选择阈值)

    万次阅读 多人点赞 2015-07-27 14:46:36
    迭代法阈值选择算法是对双峰法的改进,他首先选择一个近似的阈值T,将图像分割成两个部分,R1和R2,计算出区域R1和R2的均值u1和u2,再选择新的 阈值T=(u1+u2)/2; 重复上面的过程,知道u1和u2不在变化为止, 详细...
  • 大津法和局部阈值分割

    万次阅读 2016-11-29 13:57:48
    opencv中大津法和局部阈值分割
  • 基于Otsu算法的图像自适应阈值分割

    万次阅读 多人点赞 2020-04-17 03:09:02
    其中,参数level是一个介于0~1之间的值,也就是用于分割图像的阈值。默认情况下,它可取值是0.5。 现在问题来了,有没有一种根据图像自身特点来自适应地选择阈值的方法呢?答案是肯定的!我们今天就来介绍其中...
  • 本文是对于Omar Banimelhem and Yahya Ahmed Yahya 发表论文《Multi-Thresholding Image Segmentation ...用遗传算法对图像进行多阈值分割(Multi-Thresholding Image Segmentation Using Genetic Algorithm) 摘要
  • 数字图像处理中常用图像分割算法有哪些? 1.多数的图像分割算法 2.图像边缘分割 3.图像阈值分割 4.基于区域的分割 5.形态学分水岭算法 多数的图像分割算法 均是基于灰度值的不连续和相似的性质。在前者中,...
  • 阈值分割(一)双峰法
  • %自动阈值法:Otsu法 用MATLAB实现Otsu算法: clc;clear;close; I=imread('e:\role0\003i.bmp'); subplot(1,2,1),imshow(I); title('原始图像') grid on; %显示网格线 axis on; %显示坐标系 level=graythres
  • 图像分割算法总结

    万次阅读 2015-03-31 10:20:56
    现有的图像分割方法主要分以下几类:基于阈值分割方法、基于区域的分割方法、基于边缘的分割方法以及基于特定理论的分割方法等。1998年以来,研究人员不断改进原有的图像分割方法并把其它学科的一些新理论和新方法...
  • Matlab实现图像阈值分割

    万次阅读 多人点赞 2019-08-03 11:32:54
    使用matlab实现阈值分割,实现两种方法,一是人工选择阈值进行分割,而是自动选择阈值进行分割。操作步骤 1、 打开Matlab内容自带的coins.png图像。 2、 观察它的直方图。 3、 人工选定一个阈值,并进行分割。 4...
  • 图像分割算法

    万次阅读 2019-01-18 17:31:04
    一.区域生长 算法概括:将具有相似性质的像素点连成合并在一起。每一个区域,要有一个种子点作为生长起点,然后将种子点周围所设置领域的像素点...算法概括:对于均值分割,就是迭代计算出一个T,使得阈值T能够把灰...
  • 基本全局阈值法 当图像由暗色背景和较亮物体组成时,从背景中提取出物体的方法是选择一个将两种灰度值分开的阈值T,即f(x,y)> T 的任何点(x,y)称 为一个对象点,否则将该点称为背景点。那么,分割后的...
  • 摘要:本文主要以红外图像目标检测技术为背景,在图像阈值分割中以最大熵准则及遗传算法为基础,研究了一维最大熵值法(KSW法)及基于遗传算法的KSW熵法单阈值、双阈值等三种不同的阈值分割方法,并通过实验仿真验证...
1 2 3 4 5 ... 20
收藏数 30,415
精华内容 12,166
关键字:

阈值分割算法