OpenCV学习30--阈值分割的OTSU算法

OTSU算法又叫最大类间方差阈值分割算法,也叫大津算法,是在1980年由日本的大津展之提出,是由最小二乘法推导而来,用于一些简单的阈值确定。


对于一个灰度图,我们有时候非常想把他用一个阈值将他的前景和背景区分开来。我们可以合理的假设为如果将图像的像素分布图画出来,那么图像上应该有两个峰,即前景色和背景色。在这两个峰之间肯定有一个谷,那么我们就可以将阈值设在这里,从而对图像达到一个良好的分割效果。


怎样确定这个阈值呢?OTSU算法说,我们可以求出用这个阈值分割后的两个图像的类间方差。对于每一个可能的阈值,我们计算并取出类间方差最大的那个像素值,此时这个值就可以较好的对图像进行分割。


算法

1、将灰度值分为0−m,对于0−m的每一个灰度t,将他作为阈值将图像分割为灰度为0−t以及t+1−m这两部分。


2、计算每一部分的所占比例w1,w2,每一部分的平均灰度值u1,u2,以及总的平均灰度值u。


3、计算他们的类间方差δ2=w1(u1−u)2+w2(u2−u)2=w1w2(u1−u2)2

4、取出类间方差最大时对应的阈值t,这就可以作为我们最终所取的阈值。


Python实现:

import Image
import numpy as np

im=Image.open('test.png')
im.show()
im=im.convert('L')
im.show()
arr=np.array(im)
width,height=arr.shape
arr=arr.reshape(width*height)

freq=np.zeros(256)*1.0

total=0.
point=0.
for i in arr:
    freq[i]+=1
    total+=i
    point+=1

u=total/point

w1=0.
w2=1.
u1=0.
u2=u
eps=0.
threshold=0

for i in range(255):
    if freq[i]==0 or w2*point-freq[i]==0:
        continue
    u1=(u1*w1*point+i*freq[i])/(w1*point+freq[i])
    u2=(u2*w2*point-i*freq[i])/(w2*point-freq[i])
    w1=w1+freq[i]/point
    w2=w2-freq[i]/point
    eps_now=w1*(u1-u)*(u1-u)+w2*(u-u2)*(u-u2)
    if(eps_now>eps):
        eps=eps_now
        threshold=i

table=[]
for i in range(256):
    if i>threshold :
        table.append(255)
    else:
        table.append(0)
im=im.point(table,'L')
im.show()


这个话题勇哥再引用一篇:


OTSU算法原理

Otsu算法(大津法或最大类间方差法)使用的是聚类的思想。它把图像的灰度数按灰度级分成2个部分,使得两个部分之间的灰度值差异最大,每个部分之间的灰度差异最小。

通过方差的计算来寻找一个合适的灰度级别来划分。 所以可以在二值化的时候采用otsu算法来自动选取阈值进行二值化。

otsu算法被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。因此,使类间方差最大的分割意味着错分概率最小。

设t为设定的阈值。


w0: 分开后前景像素点数占图像的比例

u0: 分开后前景像素点的平均灰度

w1: 分开后背景像素点数占图像的比例

u1: 分开后背景像素点的平均灰度


图像总平均灰度为: u = w0∗u0 + w1∗u1


从L(灰度图为0-255)个灰度级遍历 t,使得 t 为某个值的时候,前景和背景的方差最大,则 这个 t 值便是我们要求得的阈值。其中,方差的计算公式如下:


g = wo∗(u0−u)∗(u0−u) + w1∗(u1−u)∗(u1−u)


此公式计算量较大,可以采用:


g = w0∗w1∗(u0−u1)∗(u0−u1)


由于Otsu算法是对图像的灰度级进行聚类,因此在执行Otsu算法之前,需要计算该图像的灰度直方图。


C++/C代码实现

int avg256cal::getOTSUthread(Mat& src)
{
	int size = 256;

	int *NoArry = new int[size];// 直方图矩阵

	for (int i = 0; i < size; ++i)// 初始化
		NoArry[i] = 0;

	int r = src.rows;
	int c = src.cols;
	int sum = r*c;

//  建立直方图矩阵
	for (int i = 0; i < r; ++i)
	{
		for (int j = 0; j < c; ++j)
		{	
			uchar pix = src.at<uchar>(i, j);
			NoArry[pix]++;
		}
	}
	//delete[] NoArry;

	int thd = 0; // 阈值
	float w1 = 0, w2 = 0, u1 = 0, u2 = 0, u = 0, thg = 0, MaxTh = 0;
	int cnt1 = 0, cnt2 = 0;
	for (int i = 1; i <= 255; ++i)
	{
		u1 = u2 = w1 = w2 = 0; // 均值,比例初始化
		cnt1 = 0;cnt2 = 0;
		int wt1 = 0, wt2 = 0;// weight初始化

		for (int j = 0; j <i; ++j)
		{
			cnt1 += NoArry[j];
			wt1 += j*NoArry[j];
		}
		u1 = wt1 / cnt1;
		w1 = cnt1*1.0 / sum;

		for (int j = i; j < 256; ++j)
		{
			cnt2 += NoArry[j];
			wt2 += j*NoArry[j];
		}
		u2 = wt2 / cnt2;
		w2 = cnt2*1.0 / sum;

		thg = w1*w2*(u1 - u2)*(u1 - u2);
		if (MaxTh < thg)// 找最大类间方差阈值
		{
			MaxTh = thg;
			thd = i;
		}
	}

	return thd;
}



————————————————

版权声明:本文为CSDN博主「卧_听风雨」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/birenxiaofeigg/article/details/98481019




————————————————

版权声明:本文为CSDN博主「南山二毛」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_16481211/article/details/79911360


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2025年4月    »
123456
78910111213
14151617181920
21222324252627
282930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864