图像处理|OTSU 大津分割
1.图像分类
RGB | 灰度 | 二值 | |
---|---|---|---|
通道数 | 3 | 1 | 1 |
取值范围 | 0-255/0-1 | 0-255/0-1 | 0/1 |
主要信息 | 颜色 | 亮度 | 黑白 |
备注 | 可看成R\G\B三幅灰度图合成 | 亮度将黑白分为0-8个梯度 |
2.算法原理
otsu 大津算法是一种图像二值化算法,作用是确定将图像的前景与背景分成黑和白两个部分的阈值。引入
方差
的概念,方差越大,相关性越低,就能实现最大程度上的黑白分明.
因此,算法的核心思想为根据图像的灰度值信息自适应确定使最大类间方差最大(max_g)的灰度值作为阈值(threshold_t),大于threshold_t的像素我们将它的灰度设置1,小于threshold_t的设置为0
故核心算法伪代码为:
- 遍历灰度值(value),计算并记录value所对应的方差
- n0 n1
- w0 w1
- u0 u1
- g
- 排序获取类间最大方差(max_g)及其对应灰度值(threshold_t)
- 使用threshold_t作为阈值对灰度图进行二值化
2.1 3D plot展示阈值
(鸽)
2.2 2D plot展示阈值
(debug展示)
2.3 符号及公式说明
-
h w t
h:图像的宽度
w:图像的高度(h*w 得到图像的像素数量)
h = gray_img.shape[0]
w = gray_img.shape[1]
t :灰度阈值(我们要求的值,大于t的像素我们将它的灰度设置1,小于的设置为0)
-
n0 n1-分类矩阵
n0:小于阈值的像素,前景
n1:大于等于阈值的像素,背景
n0 = gray_img[np.where(gray_img < t)]
n1 = gray_img[np.where(gray_img >= t)]
n0 + n1 == h * w
-
w0 w1-矩阵占比
w0:前景像素数量占总像素数量的比例
w0 = n0 / (h * w)
w1:背景像素数量占总像素数量的比例
w1 = n1 / (h * w)
w0 = len(n0) / (h * w)
w1 = len(n1) / (h * w)
w0 + w1 == 1
-
u0 u1-矩阵平均值
u0:前景平均灰度
u0 = n0灰度累加和 / n0
u1:背景平均灰度
u1 = n1灰度累加和 / n1
u0 = np.mean(n0) if len(n0) > 0 else 0.
u1 = np.mean(n1) if len(n0) > 0 else 0.
u:平均灰度
u = (n0灰度累加和 + n1灰度累加和) / (h * w) 根据上面的关系
u = w0 * u0 + w1 * u1
- g
g:类间方差(那个灰度的g最大,哪个灰度就是需要的阈值t)
g = w0 * (u0 – u)^2 + w1 * (u1 – u)^2
根据上面的关系,可以推出目标公式:
g = w0 * w1 * (u0 – u1) ^ 2
g = w0 * w1 * (u0 - u1) ** 2
然后,遍历每一个灰度值,找到这个灰度值对应的 g
找到max_g对应的threshold_t
3.代码实现
import cv2
import numpy as np
def sortByIdex(g_list):
sorted_glist = sorted(enumerate(g_list), key=lambda x: x[1])
#i(idx,value)
#将sorted_glist中idx与value分开,并赋值
t = [i[0] for i in sorted_glist]
g = [i[1] for i in sorted_glist]
#升序排列,最大方差取最后一个
max_g=g[-1]
threshold_t=t[-1]
print('类间最大方差-g:', max_g)
print('类间最大方差对应阈值-t:', threshold_t)
return max_g,threshold_t
# 大津二值化算法
def otsu(gray_img):
h = gray_img.shape[0]
w = gray_img.shape[1]
#初始化
otsu_img=np.zeros((h,w))
threshold_t = 0
max_g = 0
#接收所有灰度值的结果
g_list=[]
t_list=[]
#核心算法实现
# 遍历每一个灰度层
for t in range(255):
# 使用numpy直接对数组进行运算
n0 = gray_img[np.where(gray_img < t)]
n1 = gray_img[np.where(gray_img >= t)]
w0 = len(n0) / (h * w)
w1 = len(n1) / (h * w)
u0 = np.mean(n0) if len(n0) > 0 else 0.
u1 = np.mean(n1) if len(n0) > 0 else 0.
g = w0 * w1 * (u0 - u1) ** 2
g_list.append(g)
#获取间类最大方差,及对应灰度值(阈值)
max_g, threshold_t=sortByIdex(g_list)
# 根据threshold_t进行二值化
#掩模矩阵操作
otsu_img[gray_img < threshold_t] = 0
otsu_img[gray_img >= threshold_t] = 255
otsu_img=otsu_img.astype(np.uint8)
return otsu_img
if __name__ == '__main__':
#输入图片
img = cv2.imread(r'BLACKPINK_OSTU.jpeg').astype(np.uint8)
#BGR-Gary
gray_img =cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
#Gray-OSTU
otsu_img = otsu(gray_img)
# save img
cv2.imwrite('/home/whealther/PycharmProjects/OSTU/output/' + 'bgr_img.png', img)
cv2.imwrite('/home/whealther/PycharmProjects/OSTU/output/' + 'gray_img.png', gray_img)
cv2.imwrite('/home/whealther/PycharmProjects/OSTU/output/' + 'ostu_img.png', otsu_img)
print('BLACKPINK IN YOUR AREA!')
#可视化
# cv2.imshow('bgr_B_img ', img[:,:,0])
# cv2.imshow('bgr_G_img ', img[:, :, 1])
# cv2.imshow('bgr_R_img ', img[:, :, 2])
cv2.imshow('bgr_img ', img)
cv2.imshow('gray_img ', gray_img)
cv2.imshow('ostu_img ', otsu_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
4.结果展示
RGB |
---|
![]() |
Gary |
---|
![]() |
OTSU |
---|
![]() |
5.应用(鸽)
6.参考文章
cv2.ColorConvert
排序并返回下标 python
3d plot python实现
原理及matlab代码参考
原理及python代码参考
图像分割应用论文