Python 计算机视觉(十二)—— OpenCV 进行图像分割
參考的一些文章以及論文我都會給大家分享出來 —— 鏈接就貼在原文,論文我上傳到資源中去,大家可以免費下載學習,如果當天資源區找不到論文,那就等等,可能正在審核,審核完后就可以下載了。大家一起學習,一起進步!加油!!??
目錄
前言
(1)圖像分割
(2)讀取圖像信息
1. 基于閾值的圖像分割
(1)基本概念
(2)二值化操作
a. 函數
b. 代碼實現
2. 基于邊緣檢測的圖像分割
3. 基于 K-Means 聚類的區域分割
(1)基本概念
(2)代碼實現
4. 基于分水嶺算法的圖像分割
(1)基本概念
(2)代碼實現
5.整體代碼
結束語
前言
(1)圖像分割
? ? ? ? 圖像分割是 AI 領域中一個重要的分支,是機器視覺技術中的關于圖像理解的重要一環。近幾年興起的自動駕駛技術中,也需要用到這種技術。車載攝像頭探查到圖像,后臺計算機可以自動將圖像分割歸類,以避讓行人和車輛等障礙。
(2)讀取圖像信息
無需多言,直接讀取圖像信息:
""" Author:XiaoMa date:2021/11/2 """ import cv2 import numpy as np import matplotlib.pyplot as pltimg0 = cv2.imread("E:\From Zhihu\For the desk\cvtwelve3.jpg") img1 = cv2.resize(img0, dsize = None, fx = 0.5, fy = 0.5) img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) h, w = img1.shape[:2] print(h, w) cv2.namedWindow("W0") cv2.imshow("W0", img2) cv2.waitKey(delay = 0)得到圖像信息如下:
540 960
1. 基于閾值的圖像分割
(1)基本概念
? ? ? ?根據圖像的整體或部分信息選擇閾值,把圖像依據灰度級別劃分,前面說過的圖像二值化就是一種基于閾值的圖像分割,當像素點的灰度值高于閾值時將其設置為1,低于閾值時將其設置為0,通過這種方法達到將感興趣的圖像和背景進行分離的操作,所以說如何選取合適的閾值對于這種方法來說比較重要,如果背景和圖像亮度區別較大我們可以使用全局閾值分割,但是背景和圖像亮度區別不大時得使用局部閾值分割。
(2)二值化操作
a. 函數
ret,dst = cv2.threshold(src,thresh,maxval,type)
res:分割閾值
dst:分割后圖像
scr:輸入的原圖
thresh:分割時的像素分界點值(和閾值等值)
maxval:給大于閾值的像素點安排的灰度值(如定為240,那么大于閾值的點都置為240)
type:閾值的類型,包括四種不同的閾值類型
OpenCV 提供的幾種閾值類型:
cv2.THRESH_BINARY? ? ? ? ? ?#小于閾值的像素點置0,大于閾值的像素點置maxval;?
cv2.THRESH_BINARY_INV?? #小于閾值的像素點置maxval,大于閾值的像素點置0;
cv2.THRESH_TRUNC? ? ? ? ? ? # 小于閾值的像素點保持原數值,大于閾值的像素點置閾值;?
cv2.THRESH_TOZERO? ? ? ? ? # 小于閾值的像素點置0,大于閾值的像素點保持原數值;?
cv2.THRESH_TOZERO_INV? #小于閾值的像素點保持原數值,大于閾值的像素點置0。??
b. 代碼實現
本文中將閾值都設置為 127 ,對不同的閾值類型都進行嘗試:
將下面的代碼復制過去改一下圖像的讀取路徑就可以直接運行了,重要的地方都添加了注釋,應該可以看懂。
""" Author:XiaoMa date:2021/11/2 """ import cv2 import numpy as np import matplotlib.pyplot as pltimg0 = cv2.imread("E:\From Zhihu\For the desk\cvtwelve0.jpg") img1 = cv2.resize(img0, dsize = None, fx = 0.5, fy = 0.5) img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) h, w = img1.shape[:2] print(h, w) cv2.namedWindow("W0") cv2.imshow("W0", img2) cv2.waitKey(delay = 0) #圖像進行二值化 ##第一種閾值類型 ret0, img3 = cv2.threshold(img2, 127, 255, cv2.THRESH_BINARY) print(ret0) ##第二種閾值類型 ret1, img4 = cv2.threshold(img2, 127, 255, cv2.THRESH_BINARY_INV) print(ret1) ##第三種閾值類型 ret2, img5 = cv2.threshold(img2, 127, 255, cv2.THRESH_TRUNC) print(ret2) ##第四種閾值類型 ret3, img6 = cv2.threshold(img2, 127, 255, cv2.THRESH_TOZERO) print(ret3) ##第五種閾值類型 ret4, img7 = cv2.threshold(img2, 127, 255, cv2.THRESH_TOZERO) print(ret4) #將所有閾值類型得到的圖像繪制到同一張圖中 plt.rcParams['font.family'] = 'SimHei' #將全局中文字體改為黑體 figure = [img2, img3, img4, img5, img6, img7] title = ["原圖", "第一種閾值類型", "第二種閾值類型", "第三種閾值類型", "第四種閾值類型", "第五種閾值類型"] for i in range(6):figure[i] = cv2.cvtColor(figure[i], cv2.COLOR_BGR2RGB) #轉化圖像通道順序,這一個步驟要記得plt.subplot(3, 2, i+1)plt.imshow(figure[i])plt.title(title[i]) #添加標題 plt.savefig("E:\From Zhihu\For the desk\cvtwelven.jpg") #保存圖像,如果不想保存也可刪去這一行 plt.show()??
2. 基于邊緣檢測的圖像分割
這一部分在我前面的文章種已經介紹過了:Python 計算機視覺(十)—— OpenCV 圖像銳化及邊緣檢測
這里我們就拿其中的一個算子簡單試一下:
#邊緣檢測之Sobel 算子 img8 = cv2.Sobel (img2, cv2.CV_64F, 0, 1, ksize=5) cv2.namedWindow("W1") cv2.imshow("W1", img8) cv2.waitKey(delay = 0)得到的結果如下:
3. 基于 K-Means 聚類的區域分割
(1)基本概念
此處參考:《K-Means聚類算法研究綜述_楊俊闖》
? ? ? ? K-Means算法是一種無監督學習,同時也是基于劃分的聚類算法,一般用歐式距離(兩點間的直線距離)作為衡量數據對象間相似度的指標,相似度與數據對象間的距離成反比,相似度越大,距離越小。算法需要預先指定初始聚類數目k (需要分割的份數)以及 k 個初始聚類中心,根據數據對象與聚類中心之間的相似度,不斷更新聚類中心的位置,不斷降低類簇的誤差平方和(Sum of Squared Error,SSE),當SSE不再變化或目標函數收斂時,聚類結束,得到最終結果。
對于該算法的理解,也可以參考 OpenCV 官網給出的解釋:K-means聚類
(2)代碼實現
此處參考:OpenCV 官網
#K-means均值聚類 Z = img1.reshape((-1, 3)) Z = np.float32(Z) #轉化數據類型 c = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0) k = 4 #聚類中心個數,一般來說也代表聚類后的圖像中的顏色的種類 ret, label, center = cv2.kmeans(Z, k, None, c, 10, cv2.KMEANS_RANDOM_CENTERS) center = np.uint8(center) res = center[label.flatten()] img9 = res.reshape((img1.shape)) cv2.namedWindow("W2") cv2.imshow("W2", img9) cv2.waitKey(delay = 0)4. 基于分水嶺算法的圖像分割
(1)基本概念
此處參考:IMAGE SEGMENTATION AND MATHEMATICAL MORPHOLOGY
? ? ? ?任何灰度圖像都可以視為地形表面,其中高強度表示山峰和丘陵,而低強度表示山谷。你開始用不同顏色的水(標簽)填充每個孤立的山谷(局部最小值)。隨著水位上升,以附近的山峰(梯度)作為基礎,來自不同山谷的水,明顯不同顏色的水會開始融合。為了避免這種情況,你可以在水匯合的位置建立障礙。你繼續填水和建造屏障,直到所有的山峰都在水下。然后你創建的障礙為你提供了分割結果。這就是分水嶺背后的“哲學”。
? ? ? ? 但如果圖像中噪聲比較多,那么就會出現很多的“山谷”,這樣就分割出太多的區域,所以我們在進行分水嶺操作時,一般也會對圖像進行一下平滑處理或者形態學操作,來使得圖像上的噪聲點減少,使得分割效果更加明顯。圖像平滑和形態學的部分我在前面的文章中提到過:Python 計算機視覺(九)—— OpenCV進行圖像平滑
Python 計算機視覺(十一)—— OpenCV 圖像形態學處理
(2)代碼實現
此處參考:分水嶺算法的圖像分割(官網)
#分水嶺算法 ret1, img10 = cv2.threshold(img2, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)#(圖像閾值分割,將背景設為黑色) cv2.namedWindow("W3") cv2.imshow("W3", img10) cv2.waitKey(delay = 0) ##noise removal(去除噪聲,使用圖像形態學的開操作,先腐蝕后膨脹) kernel = np.ones((3, 3), np.uint8) opening = cv2.morphologyEx(img10, cv2.MORPH_OPEN, kernel, iterations = 2) # sure background area(確定背景圖像,使用膨脹操作) sure_bg = cv2.dilate(opening, kernel, iterations=3) # Finding sure foreground area(確定前景圖像,也就是目標) dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5) ret2, 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 ret3, markers = cv2.connectedComponents(sure_fg) #用0標記所有背景像素點 # Add one to all labels so that sure background is not 0, but 1(將背景設為1) markers = markers+1 ##Now, mark the region of unknown with zero(將未知區域設為0) markers[unknown == 255] = 0 markers = cv2.watershed(img1, markers) #進行分水嶺操作 img1[markers == -1] = [0, 0, 255] #邊界區域設為-1,顏色設置為紅色 cv2.namedWindow("W4") cv2.imshow("W4", img1) cv2.waitKey(delay = 0)? ? ? 效果并不是很理想,建議大家找一些亮度相差較大而且梯度明顯但種類不多的圖像進行試驗操作。
5.整體代碼
我將本篇文章中的代碼貼在下面,大家可以直接復制進行試驗,改一下圖像的讀取路徑就可以使用了:
""" Author:XiaoMa date:2021/11/2 """ import cv2 import numpy as np import matplotlib.pyplot as pltimg0 = cv2.imread("E:\From Zhihu\For the desk\cvtwelve0.jpg") img1 = cv2.resize(img0, dsize = None, fx = 0.5, fy = 0.5) img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) h, w = img1.shape[:2] print(h, w) cv2.namedWindow("W0") cv2.imshow("W0", img1) cv2.waitKey(delay = 0) #圖像進行二值化 ##第一種閾值類型 ret0, img3 = cv2.threshold(img2, 127, 255, cv2.THRESH_BINARY) print(ret0) ##第二種閾值類型 ret1, img4 = cv2.threshold(img2, 127, 255, cv2.THRESH_BINARY_INV) print(ret1) ##第三種閾值類型 ret2, img5 = cv2.threshold(img2, 127, 255, cv2.THRESH_TRUNC) print(ret2) ##第四種閾值類型 ret3, img6 = cv2.threshold(img2, 127, 255, cv2.THRESH_TOZERO) print(ret3) ##第五種閾值類型 ret4, img7 = cv2.threshold(img2, 127, 255, cv2.THRESH_TOZERO) print(ret4) #將所有閾值類型得到的圖像繪制到同一張圖中 plt.rcParams['font.family'] = 'SimHei' #將全局中文字體改為黑體 figure = [img2, img3, img4, img5, img6, img7] title = ["原圖", "第一種閾值類型", "第二種閾值類型", "第三種閾值類型", "第四種閾值類型", "第五種閾值類型"] for i in range(6):figure[i] = cv2.cvtColor(figure[i], cv2.COLOR_BGR2RGB) #轉化圖像通道順序,這一個步驟要記得plt.subplot(3, 2, i+1)plt.imshow(figure[i])plt.title(title[i]) #添加標題 plt.savefig("E:\From Zhihu\For the desk\cvtwelven.jpg") #保存圖像,如果不想保存也可刪去這一行 plt.show() #邊緣檢測之Sobel 算子 img8 = cv2.Sobel(img2, cv2.CV_64F, 0, 1, ksize = 5) cv2.namedWindow("W1") cv2.imshow("W1", img8) cv2.waitKey(delay = 0) #K-means均值聚類 Z = img1.reshape((-1, 3)) Z = np.float32(Z) #轉化數據類型 c = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0) k = 4 ret, label, center = cv2.kmeans(Z, k, None, c, 10, cv2.KMEANS_RANDOM_CENTERS) center = np.uint8(center) res = center[label.flatten()] img9 = res.reshape((img1.shape)) cv2.namedWindow("W2") cv2.imshow("W2", img9) cv2.waitKey(delay = 0)#分水嶺算法 ret1, img10 = cv2.threshold(img2, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)#(圖像閾值分割,將背景設為黑色) cv2.namedWindow("W3") cv2.imshow("W3", img10) cv2.waitKey(delay = 0) ##noise removal(去除噪聲,使用圖像形態學的開操作,先腐蝕后膨脹) kernel = np.ones((3, 3), np.uint8) opening = cv2.morphologyEx(img10, cv2.MORPH_OPEN, kernel, iterations = 2) # sure background area(確定背景圖像,使用膨脹操作) sure_bg = cv2.dilate(opening, kernel, iterations=3) # Finding sure foreground area(確定前景圖像,也就是目標) dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5) ret2, 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 ret3, markers = cv2.connectedComponents(sure_fg) #用0標記所有背景像素點 # Add one to all labels so that sure background is not 0, but 1(將背景設為1) markers = markers+1 ##Now, mark the region of unknown with zero(將未知區域設為0) markers[unknown == 255] = 0 markers = cv2.watershed(img1, markers) #進行分水嶺操作 img1[markers == -1] = [0, 0, 255] #邊界區域設為-1,顏色設置為紅色 cv2.namedWindow("W4") cv2.imshow("W4", img1) cv2.waitKey(delay = 0)結束語
? ? ? ?本文介紹了使用 OpenCV 進行圖像分割的幾種常用手段,包括閾值分割、邊緣分割、K均值聚類分割以及分水嶺分割。當然還有一些其他的比如均值漂移、基于紋理分割、文本分割、水漫分割等手段并沒有在本文中提到,小伙伴們感興趣可以去進行了解學習。
總結
以上是生活随笔為你收集整理的Python 计算机视觉(十二)—— OpenCV 进行图像分割的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python 计算机视觉(十一)—— O
- 下一篇: Python 计算机视觉(十三)—— 图