第4章 深度估计与分割
使用深度摄像头的数据来识别前景区域和背景区域。
4.1 创建模块
cameo中的捕获和处理摄像头的代码可以重用,所以讲这部分代码分离放在depth.py中。
4.2 捕获深度摄像头的帧
深度相关通道的概念
深度图:灰度图像;每个像素值都是摄像头到物体表面之间距离的估计值。
点云图:彩色图像;每种颜色对应一个(x、y、z)维度空间。
视差图:灰度图像;每个像素值代表物体表面的立体视差。(立体视差:同一场景在不同视角下感觉不同。针对两个物体之间任一相互对应的两个像素点,可以度量这些像素点之间的距离,这个度量度量就是立体视差。离摄像头近的立体视差小,远的大。因此近距离的物体在视差图中会更明亮)。
有效深度掩模:表明一个给定的像素的深度信息是否有效(有效非0,无效为0)。比如,如果深度摄像头依赖红外闪光灯,那么灯光被遮挡处就无效。
4.3 从视差图得到掩模
4.4 对复制操作执行掩模
4.5 用普通摄像头进行深度估计
4.6 使用分水岭和GrabCut算法进行物体分割
GrabCut算法实现步骤:
1.定义矩形:在图片中定义含有物体的矩形。
2.定义背景:矩形外的被自动认为是背景。
3.区别前景和背景
4.建模,标记未定义像素:用高斯混合模型对背景和前景建模,并将未定义的像素标记为可能的前景或背景。
5.相邻边:每一个像素都被看作通过虚拟边和周围像素相连接,每条边都有一个属于前景或背景的概率,概率基于它与周围像素颜色上的相似性。
6.连接像素:每一个像素会与一个前景或背景节点相连接。
7.节点连接后,若节点之间的边属于不同终端,则会切断他们之间的边,这就能将图像各部分分割出来。
实例:
import numpy as np
import cv2
from matplotlib import pyplot as plt
#加载图像
img = cv2.imread('statue_small.jpg')
#创建同形状的掩模
mask = np.zeros(img.shape[:2],np.uint8)
#创建以0填充的前景和背景模型
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (100,50,421,378)
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,
cv2.GC_INIT_WITH_RECT)
#将掩模值为0和2的转换成0,过滤掉0值像素
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]
#显示图片
plt.subplot(121), plt.imshow(img)
plt.title('grabcut'), plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(cv2.cvtColor(cv2.imread('statue_small.jpg'),
cv2.COLOR_BGR2RGB))
plt.title('original'), plt.xticks([]),plt.yticks([])
plt.show()
4.6.2 使用分水岭算法进行图像分割
分水岭算法:
把图像中低密度(变化少)的区域想象成山谷,高密度的区域想象成山峰。
开始向山谷中注入水,直到不同山谷中的水开始汇聚。
为了阻止不同山谷的水汇聚,可以设置一些栅栏,最后得到的栅栏就是图像分割。
import numpy as npimport cv2from matplotlib import pyplot as plt#加载图片,转换为灰度图片img = cv2.imread("lion.jpg") gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#为图像设置一个阈值,这个操作将图像分为两部分:黑色部分和白色部分ret, thresh = cv2.threshold( gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)#通过morphologyEx变换来去除噪声数据。这是一种对图像进行膨胀之后再进行腐蚀的操作,它可以提取图像特征kernel = np.ones((3,3),np.uint8) opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2)#通过morphologyEx变换之后的图像进行膨胀,得到背景区域sure_bg = cv2.dilate(opening,kernel,iterations=3)#通过distanceTransform获取前景区域dist_transfrom = cv2.distanceTransform(opening,cv2.DIST_L2,5) ret,sure_fg = cv2.threshold(dist_transfrom,0.7*dist_transfrom.max(),255,0)#找到前景和背景重合部分sure_fg = np.uint8(sure_fg) unkown = cv2.subtract(sure_bg,sure_fg)#ret,markers = cv2.connectedComponents(sure_fg)#在背景区域加1,将unkown区域设置为0markers = markers+1 markers[unkown==255] = 0#让水漫起来将栅栏绘成红色markers = cv2.watershed(img,markers) img[markers==-1] = [255,0,0] plt.imshow(img) plt.show()