這裏會用到幾何學中的極幾何(Epipolar Geometry),它屬於立體視覺(stereo vision)幾何學,立體視覺是計算機視覺的一個分支,它從同一物體的兩張不一樣圖像提取三維信息。html
極幾何的工做原理:python
它跟蹤從攝像頭到圖像上每一個物體的虛線,而後再第二張圖像作一樣的操做,並根據同一物體對應的線的交叉來計算距離。git
在使用 OpenCV 如何使用極幾何來計算所謂的視差圖,它是如圖像中檢測到不一樣深度的基本表示,這樣就可以提取出一張圖片的前景部分而拋棄其他部分。github
注意:進行深度估計須要同一物體在不一樣視角下拍攝的兩幅圖像,可是要注意這兩幅圖像是距物體相同距離,不然計算將會失敗,視差圖也就沒有意義。算法
下圖爲工做原理示意圖segmentfault
下面例子是使用同一物體的兩幅圖像來計算視差圖,距離攝像頭近的點在視差圖中會有更明亮的顏色,黑色區域表明兩幅圖像的差別部分。數組
import numpy as np import cv2 def update(val = 0): # disparity range is tuned for 'aloe' image pair 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('tsukuba_right.jpg') imgR = cv2.imread('tsukuba_left.jpg') cv2.namedWindow('disparity') cv2.createTrackbar('speckleRange','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實例,是一種計算視圖差的算法 # 並建立幾個跟蹤條來調整算法參數,而後調用update函數 # update函數將跟蹤條的值傳給StereoSGBM實例 # StereoSGBM是semiglobal block matching 的縮寫 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 ) update() cv2.waitKey()
GrabCut是一種基於圖切割的圖像分割方法。GrabCut算法是基於Graph Cut算法的改進。dom
基於要被分割對象的指定邊界框開始,使用高斯混合模型估計被分割對象和背景的顏色分佈(注意,這裏將圖像分爲被分割對象和背景兩部分)。簡而言之,就是隻需確認前景和背景輸入,該算法就能夠完成前景和背景的最優分割。ide
該算法利用圖像中紋理(顏色)信息和邊界(反差)信息,只要少許的用戶交互操做就可獲得較好的分割效果,和分水嶺算法比較類似,但計算速度比較慢,獲得的結果比較精確。若從靜態圖像中提取前景物體(例如從一個圖像剪切到另一個圖像),採用GrabCut算法是最好的選擇。函數
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount[, mode]) -> mask, bgdModel, fgdModel
參數:
img - 8 位 3 通道圖像。這也說明輸入的爲彩色圖像
mode - 操做模式,能夠是 GrabCutModes 模式中的一種。枚舉值enmu
引用原語句
enum cv::GrabCutModes { cv::GC_INIT_WITH_RECT = 0, cv::GC_INIT_WITH_MASK = 1, cv::GC_EVAL = 2 }詳細內容
Enumerator GC_INIT_WITH_RECT The function initializes the state and the mask using the provided rectangle. After that it runs iterCount iterations of the algorithm.
該函數使用提供的矩形初始化狀態和掩碼。以後,它運行算法的iterCount迭代
GC_INIT_WITH_MASK The function initializes the state using the provided mask. Note that GC_INIT_WITH_RECT and GC_INIT_WITH_MASK can be combined. Then, all the pixels outside of the ROI are automatically initialized with GC_BGD .
該函數使用提供的掩碼初始化狀態。請注意,能夠組合GC_INIT_WITH_RECT和GC_INIT_WITH_MASK。而後,使用GC_BGD自動初始化ROI外部的全部像素。
GC_EVAL The value means that the algorithm should just resume.
該值意味着算法應該恢復
參考:
https://docs.opencv.org/3.1.0/d7/d1b/group__imgproc__misc.html
https://docs.opencv.org/3.1.0/d7/d1b/group__imgproc__misc.html#gaf8b5832ba85e59fc7a98a2afd034e558
mask - 輸入/輸出 8 位單通道掩碼, 當mode = GC_INIT_WITH_RECT時,該函數初始化掩碼。若使用掩碼進行初始化,那麼 mask 保存初始化掩碼信息,在執行分割的時候,也將用戶交互所設定的前景與背景保存到mask中,而後再傳入grabCut函數;在處理結束以後,mask中會保存結果。mask只能取如下四種值:
rect - 包含分割對象的矩形ROI(Region of Interesting,ROI,感興趣區域),ROI外部的像素標記爲背景,ROI內部的像素標記爲前景。該參數僅在mode=GC_INIT_WITH_RECT狀況下使用。(用於限定須要進行分割的圖像範圍,只有該矩形窗口內的圖像部分才被處理)。注意,矩形的形式爲(x, y, 寬, 高 ) 。
bgdModel - 背景模型的臨時數組。 處理同一圖像時,請勿修改它。
fgdModel - 前景模型的臨時數組。 處理同一圖像時,請勿修改它。
iterCount - 返回結果以前算法應該進行的迭代次數。 請注意,可使用mode == GC_INIT_WITH_MASK或mode == GC_EVAL進一步調用結果。
1.在圖片中定義含有(一個或者多個)物體的矩形
2.矩形外的區域被自動認爲是背景
3.對於用戶定義的矩形區域,可用背景中的數據來區別它裏面的前景和背景區域
4.用高斯混合模型來對背景和前景建模,並將未定義的像素標記爲可能的前景或背景
5.圖像中歐冠的每個像素都被看做經過虛擬邊與周圍像素相鏈接,而每條邊都有一個屬於前景或背景的機率,這基於它與周圍顏色上的類似性
6.每個像素(即算法中的節點)會與一個前景或背景節點連接
7.在節點完成連接後,若節點之間的邊屬於不一樣終端,則會切斷它們之間的邊,這就能將圖像各部分分割出來
import numpy as np import cv2 from matplotlib import pyplot as plt #使用分水嶺和GrabCut算法進行物體分割 img = cv2.imread('small.jpg') # img.shape=(1039, 690, 3) # img.shape[0:2]=(1039, 690) mask = np.zeros(img.shape[:2],np.uint8) # 背景色bgdModel,前景色fgdModel bgdModel = np.zeros((1,65),np.float64) fgdModel = np.zeros((1,65),np.float64) # 感興趣區域ROI的x,y,寬度,高度 rect = (100,1,500,1000) # 得到返回值mask、bgdModel、fgdModel。 # 目標圖像、掩碼、感興趣區域,背景、前景、算法迭代次數、操做模式 cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT) # 通過圖像分割法grabCut處理以後, # print(set(mask.ravel())) -> {0,2,3} # mask的掩碼元素{0}->{0,2,3} # where(condition,x,y),condition爲array_like或bool # 真yield x,假yield y # mask==0背景、==1前景、==2可能的背景、==3可能的前景 # 當爲背景/多是背景時賦0,當爲前景/可能背景賦1 mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8') # mask2.shape=(1039,690), # img.shape=(1039,690,3) # 二者乘積則報錯: # 操做數沒法與形狀一塊兒廣播 # ValueError: operands could not be broadcast together with shapes (1039,690,3) (1039,690) # 爲了保持數形一致,增長np.newaxis # mask2[:,:,np.newaxis].shape=(1039,690,1) # 這樣當行列值不相等時可進行廣播計算 # 通過計算後,將背景色賦值爲0,即爲黑色 img = img*mask2[:,:,np.newaxis] # subplot(121)建立1行2列,當前位置爲1 plt.subplot(121), plt.imshow(img) plt.title("grabcut"), plt.xticks([]), plt.yticks([]) # subplot(122)當前位置爲2 plt.subplot(122), plt.imshow(cv2.cvtColor(cv2.imread('small.jpg'), cv2.COLOR_BGR2RGB)) plt.title("original"), plt.xticks([]), plt.yticks([]) plt.show()
運行
opencv python 基於GrabCut算法的交互式前景提取
OpenCV 深度估計與分割 - 深度估計與分割 該博客與opencv 3 有些同步,挺好的
分水嶺算法 watershed algorithm方法是一種基於邊界點的分割算法。
任何灰度圖均可以當作帶有等高線的地形圖,灰度值越高,其海拔越高。若向該地貌中注水,則海拔低處優先被淹沒,同時水也會聚集,爲了防止水的聚集,則築壩對不一樣區域的水源進行分割,此時的壩就是區域的邊界。
從上述較爲形象化的分析能夠看出:因爲灰度值圖像中的噪聲和局部的不規則性,該方法可能會形成過分分割
針對上述的缺點進行優化 - 分水嶺的標記控制 Marker-controlled watershed。該方法能夠有效地防止過分分割。
詳細內容參看
圖像分割與數學形態學(IMAGE SEGMENTATION AND MATHEMATICAL MORPHOLOGY),該文章來源於 數學形態學中心 / MINES ParisTech的圖像處理實驗室
基於邊緣的圖像分割——分水嶺算法(watershed)算法分析(附opencv源碼分析)
做用:基於標記的分水嶺算法進行圖像分割
cv2.watershed(image, markers) -> markers
參數:
image - 8 位 3通道圖像
markers - 輸入 / 輸出標記的32位單通道圖像(映射),它應該與圖像大小相同。
注意:
圖像 image 參數必須提早處理,使用正(\>0)索引粗略勾畫圖像標記中的所需區域。所以,每一個區域被表示爲具備像素值1,2,3等的一個或多個連通份量。 可使用# findContours 和 # drawContours 從二進制掩碼中檢索此類標記(請參閱watershed.cpp演示)。標記是將來圖像區域的「種子」。標記中的全部其餘像素(其與輪廓區域的關係未知且應由算法定義)應設置爲0。 在函數輸出中,標記中的每一個像素設置爲「種子」組件的值,或者設置爲區域之間的邊界處的-1。
從代碼和最終結果裏能夠大體的看出算法的流程:
1.進行灰度化
2.高斯濾波以消除噪聲的干擾
3.用canny算子檢測邊緣
4.用findcontours查找輪廓
5.利用輪廓特徵,實現圖像分割
這段代碼沒有驗證。直接複製。
import numpy as np import cv2 from matplotlib import pyplot as plt #使用分水嶺算法進行圖像分割 img = cv2.imread('timg.jpg') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #將顏色轉爲灰度後,可爲圖像設一個閾值,將圖像分爲兩部分:黑色部分和白色部分 ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) # noise removal 噪聲去除,morphologyEx是一種對圖像進行膨脹以後再進行腐蝕的操做 kernel = np.ones((3,3),np.uint8) opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2) # sure background area 肯定背景區域,圖像進行膨脹操做 sure_bg = cv2.dilate(opening,kernel,iterations=3) # Finding sure foreground area,經過distanceTransform來獲取肯定的前景區域 dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5) ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0) # Finding unknown region sure_fg = np.uint8(sure_fg) unknown = cv2.subtract(sure_bg,sure_fg) # Marker labelling ret, markers = cv2.connectedComponents(sure_fg) # Add one to all labels so that sure background is not 0, but 1 markers = markers+1 # Now, mark the region of unknown with zero markers[unknown==255] = 0 markers = cv2.watershed(img,markers) img[markers == -1] = [255,0,0] plt.imshow(img) plt.show()
運行
該代碼沒有調試,沒有深究代碼中的邏輯關係,部分不理解的函數做用及參數、返回值沒有深究。
附原圖:
基於邊緣的圖像分割——分水嶺算法(watershed)算法分析(附opencv源碼分析) 從源代碼深層次理解分水嶺算法
數字圖像處理——圖像分割(五)——分水嶺算法 分水嶺算法的理解及相關的數學分析
OpenCV—圖像分割中的分水嶺算法原理與應用 對其代碼應用有較多地描寫
OpenCV 深度估計與分割 - 深度估計與分割 重要參考博文
opencv(28)---GrabCut & FloodFill圖像分割 雖然是轉載的,感受還可喲