淋过雨的不退课人士
OTSU图像分割算法使用最大类间方差作为标准,利用图像直方图的分布信息,计算出一个阈值,根据像素是否超过阈值将图像分为前景与背景。如下图分别为原图,调用open-cv的threshold接口的二值化图像及自己写的otsu算法所得二值化图像。使用OTSU算法可得出该图类间方差最大阈值为21。如图所得,直接实现的OTSU算法对图像进行二值化处理的结果和用OpenCV中OTSU阈值分割的方法对图像进行处理的结果相同。
我们输出原图的灰度直方图,可以看出原图灰度呈双峰,由图像信息可以大致推测,灰度值较低的峰值大部分为图像的黑色暗区域,然后出现的次低灰度峰值为非血管的区域,我们感兴趣的亮区域血管灰度值较高没有形成明显峰值,范围大概为(50,250)。
RCOtsu算法的大致思路是在ROI,即血管区域使用Otsu算法,达到对算法的优化。我们将需要进行Otsu阈值计算的范围缩小,设定一个阈值下限和一个阈值上限。根据多次实验,选取的下限为15,选取的上限是150。可以得到下图,很清晰的分割出了血管区域。
Otsu算法优点是计算简单快速,不受图像亮度和对比度的影响。缺点为对图像噪声敏感;只能针对单一目标分割;当目标和背景大小比例悬殊、类间方差函数可能呈现双峰或者多峰,这个时候效果不好。需要结合实际背景进行一些阈值调整才能得出更好的结果。
代码如下:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('MRA.pgm')
#img = cv2.imread('MRA_Reference.pgm')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray_hist = cv2.calcHist([gray], [0], None, [256], [0,256], False)
plt.plot(gray_hist)
plt.show()
plt.imshow(img, "gray")
plt.title("source image"), plt.xticks([]), plt.yticks([])
plt.show()
ret1, th1 = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU) #方法选择为THRESH_OTSU
plt.imshow(th1, "gray")
plt.title("OTSU-opencv " + str(ret1)), plt.xticks([]), plt.yticks([])
plt.show()
# 大津二值化算法
def otsu(gray_img):
h = gray_img.shape[0]
w = gray_img.shape[1]
threshold_t = 0
max_g = 0
# 遍历每一个灰度层
for t in range(50,250):
# 使用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
if g > max_g:
max_g = g
threshold_t = t
print('类间方差最大阈值:', threshold_t)
gray_img[gray_img < threshold_t] = 0
gray_img[gray_img >= threshold_t] = 255
return gray_img
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
otsu_img = otsu(gray_img)
plt.imshow(otsu_img , "gray")
plt.title("otsu"), plt.xticks([]), plt.yticks([])
plt.show()