OpenCV圖像分割之分水嶺算法與圖像金字塔算法詳解
前言
主要介紹OpenCV中的分水嶺算法、圖像金字塔對(duì)圖像進(jìn)行分割的方法。
一、使用分水嶺算法分割圖像
分水嶺算法的基本原理為:將任意的灰度圖像視為地形圖表面,其中灰度值高的部分表示山峰和丘陵,而灰度值低的地方表示山谷。用不同顏色的水(標(biāo)簽)填充每個(gè)獨(dú)立的山谷(局部最小值);隨著水平面的上升,來(lái)自不同山谷(具有不同顏色)的水將開(kāi)始合并。為了避免出現(xiàn)這種情況,需要在水匯合的位置建造水壩;持續(xù)填充水和建造水壩,直到所有的山峰和丘陵都在水下。整個(gè)過(guò)程中建造的水壩將作為圖像分割的依據(jù)。
使用分水嶺算法執(zhí)行圖像分割操作時(shí)通常包含下列步驟:
(1) 將原圖轉(zhuǎn)換為灰度圖像
(2) 應(yīng)用形態(tài)變換中的開(kāi)運(yùn)算和膨脹操作,去除圖像噪聲,獲得圖像邊緣信息,確定圖像背景
(3) 進(jìn)行距離轉(zhuǎn)換,再進(jìn)行閾值處理,確定圖像前景
(4) 確定圖像的未知區(qū)域(用圖像的背景減去前景剩余的部分)
(5) 標(biāo)記背景圖像
(6) 執(zhí)行分水嶺算法分割圖像
1、cv2.distanceTransform()函數(shù)
OpenCV中的cv2.distanceTransform()函數(shù)用于計(jì)算非0值像素點(diǎn)到0值(背景)像素點(diǎn)的距離,其基本格式如下:
dst = cv2.distanceTransform(src, distanceType, maskSize[, dstType])
dst
為返回的距離轉(zhuǎn)換結(jié)果圖像
src
為原圖像, 必須是8為單通道二值圖像
distanceType
為距離類(lèi)型
maskSize
為掩膜的大小, 可設(shè)置為0, 3或5
dstType
為返回的圖像類(lèi)型, 默認(rèn)值為CV_32F(32位浮點(diǎn)數(shù))
import cv2 import numpy as np import matplotlib.pyplot as plt # cv2.distanceTransform()函數(shù),計(jì)算非0值像素點(diǎn)到0值(背景)像素點(diǎn)的距離 img = cv2.imread('home.jpg') cv2.imshow('original', img) img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度操作 cv2.imshow('gray', img_gray) ret, thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 閾值操作 cv2.imshow('thresh', thresh) kernel = np.ones((3, 3), np.uint8) img_open = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) # 形態(tài)學(xué)操作 cv2.imshow('open', img_open) img_dilate = cv2.dilate(img_open, kernel, iterations=2) # 膨脹操作,確定背景 cv2.imshow('img_dilate', img_dilate) img_dist = cv2.distanceTransform(img_dilate , cv2.DIST_L2, 5) # 距離轉(zhuǎn)換 cv2.imshow('img_dist', img_dist) cv2.waitKey(0) cv2.destroyAllWindows()
2、cv2.connectedComponents()函數(shù)
OpenCV中的cv2.connectedComponents()函數(shù)用于將圖像中的背景標(biāo)記為0,將其他圖像標(biāo)記為從1開(kāi)始的整數(shù),其基本格式如下:
ret, labels = cv2.connectedComponents(image[, connectivity[, ltype]])
labels
為返回的標(biāo)記結(jié)果圖像, 和image大小一樣
image
為要標(biāo)記的8位單通道圖像
connectivity
為4或8(默認(rèn)值), 表示連接性
ltype
為返回的標(biāo)記結(jié)果圖像的類(lèi)型
# cv2.connectedComponents()函數(shù),將圖像中的背景標(biāo)記為0,將其他圖像標(biāo)記為從1開(kāi)始的整數(shù) ret, imgfg = cv2.threshold(img_dist, 0.7*img_dist.max(), 255, cv2.THRESH_TRUNC) imgfg = np.uint8(imgfg) ret, markers = cv2.connectedComponents(imgfg) plt.imshow(imgfg,cmap='gray') plt.title('imgfg') plt.axis('off') plt.show() plt.imshow(markers,cmap='gray') plt.title('markers') plt.axis('off') plt.show()
3、cv2.watershed()函數(shù)
OpenCV中的cv2.watershed()函數(shù)用于執(zhí)行分水嶺算法分割圖像,其基本格式如下:
ret = cv2.watershed(image, markers)
ret
為返回的8位或32位單通道圖像
image
為輸入的8位或32位單通道圖像
markers
為輸入的32位單通道圖像
# cv2.watershed()函數(shù),執(zhí)行分水嶺算法分割圖像 img = cv2.imread('qizi.jpg') img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度操作 ret, thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 閾值操作 kernel = np.ones((3, 3), np.uint8) img_open = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) # 形態(tài)學(xué)操作 img_dilate = cv2.dilate(img_open, kernel, iterations=3) # 膨脹操作(確定背景) img_dist = cv2.distanceTransform(img_open, cv2.DIST_L2, 0) ret, img_fg = cv2.threshold(img_dist, 0.7*img_dist.max(), 255, 2) # 距離操作(確定前景) img_fg = np.uint8(img_fg) ret, markers = cv2.connectedComponents(img_fg) unknown = cv2.subtract(img_dilate, img_fg) # 確定位置未知區(qū)域 markers = markers + 1 # 加1使背景不為0 markers[unknown == 255] = 0 # 將未知區(qū)域設(shè)置為0 img_water = cv2.watershed(img, markers) # 執(zhí)行分水嶺算法 plt.imshow(img_water, cmap='gray') plt.title('watershed') plt.axis('off') plt.show() img[img_water == -1] = [0, 255, 0] # 將原圖中的被標(biāo)記點(diǎn)設(shè)置為綠色 cv2.namedWindow('watershed', cv2.WINDOW_NORMAL) cv2.imshow('watershed', img) cv2.waitKey(0) cv2.destroyAllWindows()
二、圖像金字塔
圖像金字塔從分辨率的角度分析處理圖像。圖像金字塔的底部為原始圖,對(duì)原始圖像進(jìn)行梯次向下采樣,得到金字塔的其他各層圖像。層次越高,分辨率越低,圖像越小。通常,每向上一層,圖像的寬度和高度就為下一層的一半。常見(jiàn)的圖像金字塔可分為高斯金字塔和拉普拉斯金字塔。
高斯金字塔有向上和向下兩種采樣方式。向下采樣時(shí),原始圖像為第0層,第1次向下采樣的結(jié)果為第1層,第2次向下采樣的結(jié)果為第2層,以此類(lèi)推。每次采樣圖像的寬度和高度都減小為原來(lái)的一半,所有的圖層構(gòu)成高斯金字塔。向上采樣的過(guò)程和向下采樣的過(guò)程相反,每次采樣圖像的寬度和高度都擴(kuò)大為原來(lái)的二倍。
1、高斯金字塔向下采樣
OpenCV中的cv2.pyrDown()函數(shù)用于執(zhí)行高斯金字塔構(gòu)造的向下采樣步驟,其基本格式如下:
ret = cv2.pyrDown(image[, dstsize[, borderType]])
ret
為返回的結(jié)果圖像, 類(lèi)型和輸入圖像相同
image
為輸入圖像
dstsize
為結(jié)果圖像大小
borderType
為邊界類(lèi)型
# 高斯金字塔向下采樣 img = cv2.imread('qizi.jpg') img1 = cv2.pyrDown(img) img2 = cv2.pyrDown(img1) cv2.imshow('img', img) cv2.imshow('img1', img1) cv2.imshow('img2', img2) print('0層的形狀:', img.shape) print('1層的形狀:', img1.shape) print('2層的形狀:', img2.shape) cv2.waitKey(0) cv2.destroyAllWindows()
2、高斯金字塔向上采樣
OpenCV中的cv2.pyrUp()函數(shù)用于執(zhí)行高斯金字塔構(gòu)造的向下采樣步驟,其基本格式如下:
ret = cv2.pyrUp(image[, dstsize[, borderType]])
ret
為返回的結(jié)果圖像, 類(lèi)型和輸入圖像相同
image
為輸入圖像
dstsize
為結(jié)果圖像大小
borderType
為邊界類(lèi)型
3、拉普拉斯金字塔
拉普拉斯金字塔的第n層是該層高斯金字塔圖像減去n+1層向上采樣的結(jié)果獲得的圖像。
# 拉普拉斯金字塔 img = cv2.imread('qizi.jpg') img1 = cv2.pyrDown(img) img2 = cv2.pyrDown(img1) img3 = cv2.pyrDown(img2) imgL0 = cv2.subtract(img, cv2.pyrUp(img1)) imgL1 = cv2.subtract(img1, cv2.pyrUp(img2)) imgL2 = cv2.subtract(img2, cv2.pyrUp(img3)) cv2.imshow('imgL0', imgL0) cv2.imshow('imgL1', imgL1) cv2.imshow('imgL2', imgL2) cv2.waitKey(0) cv2.destroyAllWindows()
4、應(yīng)用圖像金字塔實(shí)現(xiàn)圖像的分割和融合
# 應(yīng)用圖像金字塔實(shí)現(xiàn)圖像融合 img1 = cv2.imread('jiang1.jpg') img2 = cv2.imread('jiang2.jpg') #生成圖像1的高斯金字塔,向下采樣6次 img = img1.copy() img1Gaus = [img] for i in range(6): img = cv2.pyrDown(img) img1Gaus.append(img) #生成圖像2的高斯金字塔,向下采樣6次 img = img2.copy() img2Gaus = [img] for i in range(6): img = cv2.pyrDown(img) img2Gaus.append(img) #生成圖像1的拉普拉斯金字塔,6層 img1Laps = [img1Gaus[5]] for i in range(5,0,-1): img = cv2.pyrUp(img1Gaus[i]) lap = cv2.subtract(img1Gaus[i-1],img) #兩個(gè)圖像大小不同時(shí),做減法會(huì)出錯(cuò) img1Laps.append(lap) #生成圖像2的拉普拉斯金字塔,6層 img2Laps = [img2Gaus[5]] for i in range(5,0,-1): img = cv2.pyrUp(img2Gaus[i]) lap = cv2.subtract(img2Gaus[i-1],img) img2Laps.append(lap) #拉普拉斯金字塔拼接:圖像1每層左半部分與和圖像2每層右半部分拼接 imgLaps = [] for la,lb in zip(img1Laps,img2Laps): rows,cols,dpt = la.shape ls=la.copy() ls[:,int(cols/2):]=lb[:,int(cols/2):] imgLaps.append(ls) #從拉普拉斯金字塔恢復(fù)圖像 img = imgLaps[0] for i in range(1,6): img = cv2.pyrUp(img) img = cv2.add(img, imgLaps[i]) #圖像1原圖像的半部分與和圖像2原圖像的右左半部分直接拼接 direct = img1.copy() direct[:,int(cols/2):]=img2[:,int(cols/2):] cv2.imshow('Direct',direct) #顯示直接拼接結(jié)果 cv2.imshow('Pyramid',img) #顯示圖像金字塔拼接結(jié)果 cv2.waitKey(0) cv2.destroyAllWindows()
到此這篇關(guān)于OpenCV圖像分割之分水嶺算法與圖像金字塔算法詳解的文章就介紹到這了,更多相關(guān)OpenCV圖像分割內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳細(xì)解讀Python中解析XML數(shù)據(jù)的方法
這篇文章主要介紹了Python中解析XML數(shù)據(jù)的方法,是Python入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-10-10Django零基礎(chǔ)入門(mén)之常用過(guò)濾器詳解
這篇文章主要介紹了Django零基礎(chǔ)入門(mén)之常用過(guò)濾器的使用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09Python實(shí)現(xiàn)批量獲取當(dāng)前文件夾下的文件名
這篇文章主要為大家詳細(xì)介紹了如何利用Python實(shí)現(xiàn)批量獲取當(dāng)前文件夾下的文件名,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02Python編程快速上手——正則表達(dá)式查找功能案例分析
這篇文章主要介紹了Python正則表達(dá)式查找功能,結(jié)合具體實(shí)例形式分析了Python基于正則表達(dá)式遍歷查找指定格式文件的相關(guān)操作技巧,需要的朋友可以參考下2020-02-02Python批量對(duì)word文檔進(jìn)行操作步驟
這篇文章主要介紹了Python批量對(duì)word文檔進(jìn)行操作,一步步逐步完成創(chuàng)建文檔,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02Django項(xiàng)目中使用JWT的實(shí)現(xiàn)代碼
這篇文章主要介紹了Django項(xiàng)目中使用JWT的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11pyqt5 QScrollArea設(shè)置在自定義側(cè)(任何位置)
這篇文章主要介紹了pyqt5 QScrollArea設(shè)置在自定義側(cè)(任何位置),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09