该算法就是通过直方图的特性来自动选取阈值,尤其是有的图像的灰度直方图是双峰。
cv.cvtColor(img_d1, cv.COLOR_BGR2GRAY)
# 我传入的是灰度图像
retval, dst = cv.threshold(BGR2GRAY(img_d1), 128, 255, cv.THRESH_BINARY)
plt.imshow(dst)
# 用高斯滤波是为了降噪,噪声有时候会影响直方图的结果
# 我传入的是灰度图像
blur = cv.GaussianBlur(BGR2GRAY(img_d1), (5,5), 0)
# 直接一个API调用就结束了。
ret_otsu, im_otsu = cv.threshold(blur, 0, 255, cv.THRESH_BINARY+cv.THRESH_OTSU)
plt.subplot(1,3,1), plt.imshow(BGR2GRAY(img_d1), 'gray')
plt.title('GRAY'),plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2), plt.hist(BGR2GRAY(img_d1).ravel(), 256)
plt.title('Histogram'),plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3), plt.imshow(im_otsu, 'gray')
plt.title('Otsu'),plt.xticks([]), plt.yticks([])
党妹这张图像的直方图,分布是不特别好,但是能明显的看到有双峰
要求你在0到255之间找一个灰度值t,t将图像的直方图分成前后两部分,叫做前景和后景。
我们的目标 使得 前景和后景的方差最大,或者前景与后景各自内部的方差最小。这里用第一种 w 0 ∗ w 1 ∗ ( u 0 − u 1 ) 2 w0∗w1∗(u0−u1)^2 w0∗w1∗(u0−u1)2
w0 是前景里所有像素占总像素的比例 ,u0 是前景里像素均值,w1 u1就是后景里对应的值了
def otsu_binarization(img: np.ndarray, th=128):
max_sigma, max_t = 0, 0
H, W = img.shape
# determine threshold
for _t in range(1, 255):
v0 = img[np.where(img < _t)]
u0 = np.mean(v0) if len(v0) else 0
w0 = len(v0) / (H * W)
v1 = img[np.where(img >= _t)]
u1 = np.mean(v1) if len(v1) else 0
w1 = len(v1) / (H * W)
sigma = w0 * w1 * (u0 - u1)**2
if sigma > max_sigma:
max_sigma = sigma
max_t = _t
# Binarization
print('Threshold >>', max_t)
th = max_t
out = img.copy()
out[out < th] = 0
out[out >= th] = 255
刷题的GitHub: github链接.