OpenCV 3计算机视觉 python语言实现笔记(四)

#深度估计与分割

#深度图的定义:它是灰度图像,该图像的每个像素值都是摄像头到物体表面之间距离的估计值。

#视差图的定义:它是灰度图像,该图像的每个像素值代表物体表面的立体视差。
#立体视差是指针对同一场景的两个不同视角产生的不同图像,两个孪生的物体之间任意一堆相互相应的两个像素点,
#可以度量这些像素之间的距离。这个度量就是立体视差。近距离的物体会产生较大的立体视差,因此,近距离的物体在视差图中更明亮一些。

#有效深度掩模:它是表明一个给定的像素的深度信息是否有效(非零值表示有效,零值表示无效)。

#下面写一个生成掩模的函数,使得面部矩形中想要的区域的掩模值为0,想要的区域掩模值为1.
#这个函数会将视差图、有效深度掩模和一个矩形作为参数。

def createMedianMask(disparityMap,validDepthMask,rect=None):
    if rect is not None:
        x,y,w,h = rect
        disparityMap = disparityMap[y:y+h,x:x+w]
        validDepthMask = validDepthMask[y:y+h,x:x+w]
    #计算视差图的中位数
    median = numpy.median(disparityMap)
    #当有效的视差值与平均视差值相差12或更多时,就可将像素看作噪声。
    return numpy.where((validDepthMask == 0 ) | (abs(disparityMap - median) < 12),1.0,0.0)


#对复制操作执行掩模
#将源图像的矩形区域赋值给目标图像的矩形区域,用掩模来判断是否进行有效的赋值

def copyRect(src,dst,srcRect,dstRect,mask=None,interpolation=cv2.INTER_LINEAR):
    x0,y0,w0,h0 = srcRect
    x1,y1,w1,h1 = dstRect
    
    #若mask为None则进行简单的赋值操作,否则根据掩模进行判断
    if mask is None:
        dst[y1:y1+h1,x1,x1+w1:] = cv2.resize(src[y0:y0+h0,x0:x0+w1],(w1,h1),interpolation=interpolation)
    else:
        #如果源图为3通道图像,则将掩模也转换为3通道
        if not utils.isGray(src):
            mask = mask.repeat(3).reshape(h0,w0,3)
        dst[y1:y1+h1,x1:x1+w1] = numpy.where(cv2.resize(mask,(w1,h1),interpolation=cv2.INTER_LINEAR),
                                             cv2.resize(src[y0:y0+h0,x0:x0+w0],(w1,h1),interpolation=interpolation),
                                             dst[y1:y1+h1,x1:x1+w1])

#上面是实现将一个矩形区域赋值给另一个矩形区域,现在我们完成一组矩形区域的循环交换。

def swapRects(src,dst,rects,masks=None,interpolation = cv2.INTER_LINEAR):
    if dst is not src:
        dst[:] = src
        
        numRects = len(rects)
        if numRects < 2 :
            return
        if masks is None:
            mask = [None] * numRects
        #保存最后一个矩形区域的值
        x,y,w,h = rects[numRects - 1]
        temp = src[y:y+h,x:x+w].copy()
        
        #循环赋值
        i = numRects - 2
        while i >= 0 :
            copyRect(src,dst,rects[i],rect[i+1],masks[i],interpolation)
            i -= 1
        
        #将暂时保存的最后一个矩形赋值个第一个矩形
        copyRect(temp,dst,[0,0,w,h],rects[0],masks[numRects - 1],interpolation)
        

        
#使用普通摄像机进行深度估计
#首先需要同一物体在不同视角下拍摄的两幅图像,但是要注意着两幅图像是距物体相同距离拍摄的,否则计算将会失败,视差图也就没有意义。

import numpy as np 
import cv2

def updata(val = 0):
    stereo.setBlockSize(cv2.getTrackbarPos('window_size','disparity'))
    stereo.setUniquenessRatio(cv2.getTrackbarPos('uniquenessRatio','disparity'))
    stereo.setSpeckleWindowSize(cv2.getTrackbarPos('speckleWindowSize','disparity'))
    stereo.setSpeckleRange(cv2.getTrackbarPos('speckleRange','disparity'))
    stereo.setDisp12MaxDiff(cv2.getTrackbarPos('disp12MaxDiff','disparity'))
    
    print('computing disparity...')
    disp = stereo.compute(imgL,imgR).astype(np.float32) / 16.0
    
    cv2.imshow('left',imgL)
    cv2.imshow('disparity',(disp - min_disp)/num_disp)

if __name__ == "__main__"    :
    window_size = 5
    min_disp = 16
    num_disp = 192 - min_disp
    blockSize = window_size
    uniquenessRatio = 1
    speckleRange = 3
    speckleWindowSize = 3
    disp12MaxDiff = 200
    p1 = 600
    p2 = 2400
    imgL = cv2.imread('images/color1_small.jpg')
    imgR = cv2.imread('image/color2_small.jpg')
    cv2.namedWindow('disparity')
    cv2.createTrackbar('window_size','disparity',speckleRange,50,update)
    cv2.createTrackbar('window_size','disparity',window_size,21,update)
    cv2.createTrackbar('speckleWindowSize','disparity',speckleWindowSize,200,update)
    cv2.createTrackbar('uniquenessRatio','disparity',uniquenessRatio,50,update)
    cv2.createTrackbar('disp12MaxDiff','disparity',disp12MaxDiff,250,update)
    #创建一个StereoSGBM实例(这是一种计算视差图的算法)
    stereo = cv2.StereoSGBM_create(minDisparity = min_disp,
                                   numDisparities = num_disp,
                                   blockSize = window_size,
                                   uniquenessRatio = uniquenessRatio,
                                   speckleRange = speckleRange,
                                   speckleWindowSize = speckleWindowSize,
                                   disp12MaxDiff = disp12MaxDiff,
                                   p1 = p1,
                                   p2 = p2
                                  )
    updata()
    cv2.waitKey()
    
    
 #===================================================================   
 #使用分水岭和GrabCut算法进行物体分割

#Grabcut算法的实现步骤为:
#1)在图像中定义含有(一个或多个)物体的矩形
#2)矩形外的区域被自动认为是背景
#3)对于用户定义的矩形区域,可用背景中的数据来区别它里面的是前景还是背景区域
#4)使用高斯混合模型来对背景和前景建模,并将为定义的像素标记为可能的前景或背景
#5)图像中的每一个像素都被看作通过虚拟与周围像素想连接,而每条边都有一个属于前景或背景的概率,这是基于它与周围像素颜色上的相似性。
#6)每一个像素会与一个前景或背景节点连接
#7)在节点完成连接后(可能与背景或前景连接),若节点之间的边属于不同终端,则会切断它们之间的边,这就将图像的各部分分割出来。

import numpy as np
import cv2
from matplotlib import pyplot as plt

#首先加载想要处理的图像,然后创建一个与所加载图像同形状的掩模,并用0填充。
img = cv2.imread('images/statue_small.jpg')
mask = np.zeros(img,shape[0:2],np.uint8)
#然后创建以0填充的前景和背景模型
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
#定义矩形区域
rect = (100,50,421,378)
#使用指定的空模型和掩模来运行GrabCut算法,实际上是用一个矩形来初始化这个操作
#5表示算法的迭代次数
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
#得到的掩模已经变成包含0-3之间的值,我们将值为0和2的转为0,值为1和3的转换为1。
#然后保存在mask2中,这样就可以用mask2过滤出所有的0值像素(理论上会完整保留所有前景像素)
mask2 = np.where((mask == 2) | (mask == 0),0,1).astype('uint8')
img = img * mask2[:,:,newaxis]

plt.subplot(121),plt.imshow(img)
plt.title("grabcut"),plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(cv2.cvtColor(cv2.imread('images/statue_small.jpg'),cv2.COLOR_BGR2RGB))#OpenCV中的图像格式是RGB
plt.title("original"),plt.xticks([]),plt.yticks([])
plt.show()


#使用分水岭算法进行图像分割

#介绍一下分水岭算法,算法叫做分水岭是因为他里面有水的概念。
#把图像中低密度的区域(变化很少)想象成山谷,图像的高密度区域(变化很多)想象成山峰。
#开始向山谷中注入水直到不同的山谷中的水开始汇聚。为了阻止不同山谷的水汇聚,可以设置一些珊栏,
#最后得到的珊栏就是图像分割。

import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('images/basil.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)

#获得背景部分
sure_bg = cv2.dilate(opening,kernel,iterations=3)

#获取前景部分
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)

#获取前景和背景重合的部分
sure_fg = np.uint8(sure_fg)
unknow = cv2.subtract(sure_bg,sure_fg)

#现在有了这些区域,就可以设定栏珊来阻止水汇聚了,通过connectedComponents函数完成。
#把图像看成有边相连的节点集。给出一些确定的前景区域,其中一些节点会连接在一起,
#而另一些节点并没有连接在一起。这意味着,它们属于不同的山谷,在它们之间应该有一个栏珊。

ret,markers = cv2.connectedComponents(sure_fg)

#在背景区域加1,这会将unknow区域设为0:
markers = markers+1
markers[unknow==255] = 0

#最后打开门,让水漫起来并把栏珊绘成红色
markers = cv2.watershed(img,markers)
img[markers == -1] = [255,0,0]
plt.imshow(img)
plt.show()

你可能感兴趣的:(OpenCV3,计算机视觉)