图像分割与提取
图像分割与提取的概念
在图像处理的过程中, 经常需要从图像中将前景对象作为目标图像提取出来。例如无人驾驶技术, 我们关心的是周围的交通工具, 其他障碍物等, 而对于背景本身并不关注, 故而, 我们需要将这些东西从图片(视频)中提取出来, 而忽略那些只有背景的图像。
常见的图像分割方法
- 基于阈值的分割方法:全局阈值法、局部阈值法等
- 基于区域的分割方法:分水岭方法、区域生长法等
- 基于边缘的分割方法:Canny边缘检测、轮廓检测等
- 基于特定理论的分割方法:聚类、模糊集等(机器学习)
分水岭算法的概念
图像的灰度空间很像地球表面的整个地理结构,每个像素的灰度值代表高度。其中的灰度值较大的像素连成的线可以看做山脊,也就是分水岭。
当水平面上升到一定高度时,水就会溢出当前山谷,可以通过在分水岭上修大坝,从而避免两个山谷的水汇集,这样图像就被分成2个像素集,一个是被水淹没的山谷像素集,一个是分水岭线像素集。最终这些大坝形成的线就对整个图像进行了分区,实现对图像的分割。
分水岭算法的步骤
原图
二值化
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread("coins.jpg")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
plt.imshow(thresh,cmap='gray'
plt.show()
形态学操作(移除噪声)
先使用开运算去除图像中的细小白色噪点,然后通过腐蚀运算移除边界像素,得到的图像中的白色区域肯定是真实前景,即靠近硬币中心的区域;膨胀运算使得一部分背景成为了物体的边界,得到的图像中的黑色区域肯定是真实背景,即远离硬币傅区域。剩下的区域(硬币的边界附近)还不能确定是前景函数背景,可通过膨胀图减去腐蚀图得到。
开运算
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN,kernel,iterations=2)
plt.imshow(opening,cmap='gray')
plt.show()
膨胀-确认背景区域
sure_bg = cv2.dilate(opening,kernel,iterations=2)
plt.imshow(sure_bg,cmap='gray')
plt.show()
腐蚀-确定前景区域
sure_fg = cv2.erode(opening,kernel,iterations=2)
plt.imshow(sure_fg,cmap='gray')
plt.show()
减法-不确定区域
unknown = cv2.subtract(sure_bg,sure_fg)
plt.imshow(unknown,cmap='gray')
plt.show()
距离变换
剩下的区域不确定是硬币还是背景,这些区域通常在前景和背景接触的区域(或者两个不同硬币接触的区域),我们称之为边界。通过分水岭算法应该能找到确定的边界。
由于硬币之间彼此接触,我们使用另一个确定前景的方法,就是带阈值的距离变换。
下面左边的图为得到的距离转换图像,其实每个像素的值为其到最近的背景像素(灰度值为0)的距离,可以看到硬币的中心像素值最大(中心离背景像素最远)。对其进行二值处理就得到了分离的前景图(下面中间的图),白色区域肯定是硬币区域,而且还相互分离,下面右边的图为之前的膨胀图减去中间这个表示前景的图。
dist_transform=cv2.distanceTransform(opening,cv2.DIST_L2,5)
cv2.normalize(dist_transform,dist_transform,0,1.0,cv2.NORM_MINMAX)
plt.imshow(dist_transform,cmap='gray')
plt.show()
阈值变换-寻找前景区域
ret,sure_fg = cv2.threshold(dist_transform,0.5*dist_transform.max(),255,0)
plt.imshow(sure_fg,cmap='gray')
plt.show()
减法-寻找不确定区域
sure_fg = np.uint8(sure_fg