造轮子——python手动实现OTUS
Otsu算法原理
最近研究阈值化算法,Otsu算是目前应用比较广泛的,自己想实现OTSU看和opencv对比,哪个用时短
最后经numba加速后还是失败,opencv的Otsu算法速度是自己手写的转换速度的5~10倍。果然现成的轮子是比较好用的
Otsu法又称最大类间方差法或者大津法,基本思想就是计算前景类与背景类之间的类间方差,当某个灰度级对应得到的类间方差最大时,即认为此时的阈值为最佳阈值。相关原理可以参考:
- https://blog.csdn.net/u011285477/article/details/52004513?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-2
- https://zhuanlan.zhihu.com/p/128109613
- https://blog.csdn.net/m0_38024332/article/details/104226806
- https://blog.csdn.net/u012771236/article/details/44975831
python实现
def otus_threshold(image):
""" image should be in gray"""
n, m = image.shape
total_pixel = n * m
hist = cal_hist(image) # 自己写的灰度直方计算函数 可用下面的代替
# opencv 中的灰度直方计算函数
# hist = cv.calcHist([gray], [0], None, [256], [0, 256])
# 总平均灰度
u = 0.0
for i in range(len(hist)):
u = u + i * hist[i]
u = u / total_pixel
# 背景灰度级平均灰度
u_0 = []
u_0_mid = 0.0
w_0 = []
for j in range(len(hist)):
back_pixel = np.sum(hist[:j+1])
w_0.append(back_pixel / total_pixel)
u_0_mid = u_0_mid + j * hist[j]
if back_pixel == 0:
u_0.append(0.0)
else:
u_0.append(u_0_mid / back_pixel)
# print(u_0)
# 类间方差
g = []
a = 0.0
for k in range(len(hist)):
if w_0[k] == 1:
g.append(a)
else:
u_1 = (u - u_0[k] * w_0[k]) / (1 - w_0[k])
g_mid = w_0[k] * (1.0 - w_0[k]) * (u_0[k] - u_1) * (u_0[k] - u_1)
g.append(g_mid)
# print(len(g), g)
g_index = g.index(max(g))
return g_index
注意
函数的输入必须为灰度图像,返回值为最佳阈值,函数未将图像转化为二值图,可自己写可历遍对比转化一下
opencv中的Otsu法
otsu, src_threshold = cv.threshold(image_gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
返回值为最佳阈值 与转换后的二值化图像