wordpress mysql 應(yīng)用網(wǎng)站如何優(yōu)化一個關(guān)鍵詞
文章目錄
- 1、功能描述
- 2、代碼實現(xiàn)
- 3、完整代碼
- 4、效果展示
- 5、涉及到的庫函數(shù)
- 5.1、cv2.pyrMeanShiftFiltering
- 5.2、cv2.morphologyEx
- 5.3、cv2.distanceTransform
- 5.4、cv2.normalize
- 5.5、cv2.watershed
- 6、參考
1、功能描述
基于分水嶺算法對圖片進(jìn)行分割
分水嶺分割算法(WaterShed Algorithm),是一種基于拓?fù)淅碚摰臄?shù)學(xué)形態(tài)學(xué)的分割方法,廣泛應(yīng)用于數(shù)學(xué)、圖像學(xué)和電子信息學(xué)領(lǐng)域。
一、算法原理
分水嶺分割算法的基本思想是把圖像看作是測地學(xué)上的拓?fù)涞孛?#xff0c;圖像中每一點像素的灰度值表示該點的海拔高度,每一個局部極小值及其影響區(qū)域稱為集水盆,而集水盆的邊界則形成分水嶺。
分水嶺的概念和形成可以通過模擬浸入過程來說明:在每一個局部極小值表面,刺穿一個小孔,然后把整個模型慢慢浸入水中,隨著浸入的加深,每一個局部極小值的影響域慢慢向外擴展,在兩個集水盆匯合處構(gòu)筑大壩,即形成分水嶺。
二、算法步驟
分水嶺算法的計算過程是一個迭代標(biāo)注過程,主要包括排序和淹沒兩個步驟。
- 排序:對每個像素的灰度級進(jìn)行從低到高排序。
- 淹沒:在從低到高實現(xiàn)淹沒過程中,對每一個局部極小值在h階高度的影響域采用先進(jìn)先出(FIFO)結(jié)構(gòu)進(jìn)行判斷及標(biāo)注。
分水嶺變換得到的是輸入圖像的集水盆圖像,集水盆之間的邊界點即為分水嶺。
三、應(yīng)用場景
- 醫(yī)學(xué)圖像分析:用于分割MRI或CT圖像中的不同結(jié)構(gòu),如腫瘤、器官等。
- 紋理分割:將圖像分割成紋理塊,從而識別材質(zhì)。
- 物體檢測:分割圖像中的物體,從而實現(xiàn)目標(biāo)檢測。
四、優(yōu)缺點及改進(jìn)方法
-
優(yōu)點:
- 分水嶺算法對微弱邊緣具有良好的響應(yīng),是得到封閉連續(xù)邊緣的保證。
- 分水嶺算法所得到的封閉的集水盆,為分析圖像的區(qū)域特征提供了可能。
-
缺點:
- 常規(guī)的分水嶺算法由于圖像上噪聲和圖局部不連續(xù)原因常常表現(xiàn)出過度分割。
-
改進(jìn)方法:
- 利用先驗知識去除無關(guān)邊緣信息。
- 修改梯度函數(shù)使得集水盆只響應(yīng)想要探測的目標(biāo)。
- 對梯度圖像進(jìn)行閾值處理,以消除灰度的微小變化產(chǎn)生的過度分割。
五、示例
在OpenCV中,分水嶺算法通過 watershed()
函數(shù)實現(xiàn)。該函數(shù)基于圖像中的灰度級和邊緣來構(gòu)建一組標(biāo)記,將圖像分割成不同的區(qū)域或物體。雖然需要手動標(biāo)記輔助,但其效果顯著。
綜上所述,分水嶺分割算法是一種有效的圖像分割方法,但需要注意其過度分割的問題,并采取相應(yīng)的改進(jìn)方法以提高分割效果。
2、代碼實現(xiàn)
圖像前處理
import cv2 as cv
import numpy as np
import random as rngdef process_img2(img):# 轉(zhuǎn)成灰度圖img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)cv.imwrite("img_gray.jpg", img_gray)# 高斯模糊img_gray = cv.GaussianBlur(img_gray, (5, 5), 0.1)cv.imwrite("GaussianBlur.jpg", img_gray)# 中值濾波img_gray = cv.medianBlur(img_gray, 5)cv.imwrite("medianBlur.jpg", img_gray)# 二值化_, image_binary = cv.threshold(img_gray, 0, 255, cv.THRESH_OTSU + cv.THRESH_BINARY)cv.imwrite("image_binary.jpg", image_binary)# 形態(tài)學(xué)膨脹kernel = np.ones((7, 7), np.uint8)# sure_bg = cv.morphologyEx(image_binary, cv.MORPH_CLOSE, kernel, iterations=3)sure_bg = cv.dilate(image_binary, kernel, iterations=2)cv.imwrite("sure_bg.jpg", sure_bg)# 二進(jìn)制非sure_bg = cv.bitwise_not(sure_bg)cv.imwrite("bitwise_not_sure_bg.jpg", sure_bg)# 形態(tài)學(xué)變化,開運算element = cv.getStructuringElement(cv.MORPH_ELLIPSE, (3, 3))image_binary = cv.morphologyEx(image_binary, cv.MORPH_OPEN, element)cv.imwrite("morphologyEx_image_binary.jpg", image_binary)# 計算前景到背景的距離imageSC = cv.distanceTransform(image_binary, cv.DIST_L2, 5)imageSC = imageSC.astype(np.uint8)cv.imwrite("imageSC.jpg", imageSC)# 歸一化imageSC = cv.normalize(imageSC, 0, 255, cv.NORM_MINMAX)cv.imwrite("imageSC_normalize.jpg", imageSC * 255)# 二值化_, imageSC = cv.threshold(imageSC, 0, 255, cv.THRESH_OTSU + cv.THRESH_BINARY)cv.imwrite("imageSC_threshold.jpg", imageSC)return imageSC, sure_bgrng.seed(12345)
imgPath = "./images/6.jpeg"
src = cv.imread(imgPath)
shifted = cv.pyrMeanShiftFiltering(src, 7, 15)
cv.imwrite("shift.jpg", shifted)if src is None:print('Could not open or find the image:')# print('Could not open or find the image:', args.input)exit(0)
# Show source image
cv.imshow('Source Image', src)opening, sure_bg = process_img2(shifted)
# Show output image
cv.imshow('Background Image', sure_bg) # 背景
原始圖片
mean shift 后的結(jié)果
轉(zhuǎn)換為灰度圖 img_gray.jpg
高斯模糊 GaussianBlur.jpg
中值濾波 medianBlur.jpg
二值化 image_binary.jpg
形態(tài)學(xué)膨脹 sure_bg.jpg
明顯看出來前景變大了許多
二進(jìn)制非 bitwise_not_sure_bg.jpg,前景變成了背景,作為 process_img2
函數(shù)的第二個返回值 return
基于二值化的 image_binary.jpg 進(jìn)行開運算 morphologyEx_image_binary.jpg
基于二值化的 image_binary.jpg 計算前景到背景的距離,imageSC.jpg,便于計算分水嶺
不乘以 255 的效果
乘上 255 后的效果
最大最小值歸一化,得到 imageSC_normalize.jpg
乘以 255 后可視化的結(jié)果
二值化歸一化后的結(jié)果,imageSC_threshold.jpg,作為 process_img2
函數(shù)的第一個返回值 return
# Dilate a bit the dist image
kernel1 = np.ones((3, 3), dtype=np.uint8)
dist = cv.dilate(imageSC, kernel1)
cv.imwrite("dist-dilate.jpg", dist*255)
cv.imshow('Peaks', dist)
膨脹 imageSC_threshold.jpg,得到 dist-dilate.jpg
# 構(gòu)建初始markers
dist_8u = dist.astype('uint8')
# Find total markers
contours, _ = cv.findContours(dist_8u, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
找輪廓
# 創(chuàng)建即將應(yīng)用分水嶺算法的標(biāo)記圖像
# markers = np.zeros(dist.shape, dtype=np.int32)
markers = sure_bg.copy().astype(np.int32)# 標(biāo)記前景
for i in range(len(contours)):cv.drawContours(markers, contours, i, (i + 1), -1) # 輪廓標(biāo)記從1開始# 標(biāo)記背景
# cv.circle(markers, (5, 5), 3, 255, -1) # 此處背景標(biāo)記為255
# 可視化markersprint("before watershed: ", np.unique(markers)) # 0表示不確定標(biāo)記區(qū)域
markers_8u = (markers * 10).astype('uint8')
cv.imwrite('markers_8u.jpg', markers_8u)
cv.imshow('Markers', markers_8u)
output
before watershed: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 255]
繪制輪廓 markers_8u.jpg
# 應(yīng)用分水嶺分割算法
markers = cv.watershed(src, markers)print("after watershed: ", np.unique(markers)) # -1表示邊界# mark = np.zeros(markers.shape, dtype=np.uint8)
mark = markers.astype('uint8')
cv.imwrite('mark.jpg', mark)
output
after watershed: [ -1 1 2 3 4 5 6 7 8 9 10 11 12 255]
分水嶺算法 mark.jpg
mark = cv.bitwise_not(mark)
cv.imwrite('mark-bitwise_not.jpg', mark)
cv.imshow('Markers_v2', mark)
取反 mark-bitwise_not.jpg
# Generate random colors
colors = []
for contour in contours:colors.append((rng.randint(0, 256), rng.randint(0, 256), rng.randint(0, 256)))# Create the result image
dst = np.zeros((markers.shape[0], markers.shape[1], 3), dtype=np.uint8)
# Fill labeled objects with random colors
for i in range(markers.shape[0]):for j in range(markers.shape[1]):index = markers[i, j]if index > 0 and index <= len(contours): # -1表示邊界, 255表示背景dst[i, j, :] = colors[index - 1]
# Visualize the final image
cv.imshow('Final Result', dst)
cv.imwrite('Final-Result.jpg', dst)
cv.waitKey(0)
cv.destroyAllWindows()
繪制 Final-Result.jpg
3、完整代碼
輸入圖片
實現(xiàn)一,也即前面章節(jié)所描述的方法
import cv2 as cv
import numpy as np
import random as rngdef process_img2(img):# 轉(zhuǎn)成灰度圖img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)cv.imwrite("img_gray.jpg", img_gray)# 高斯模糊img_gray = cv.GaussianBlur(img_gray, (5, 5), 0.1)cv.imwrite("GaussianBlur.jpg", img_gray)# 中值濾波img_gray = cv.medianBlur(img_gray, 5)cv.imwrite("medianBlur.jpg", img_gray)# 二值化_, image_binary = cv.threshold(img_gray, 0, 255, cv.THRESH_OTSU + cv.THRESH_BINARY)cv.imwrite("image_binary.jpg", image_binary)# 形態(tài)學(xué)膨脹kernel = np.ones((7, 7), np.uint8)# sure_bg = cv.morphologyEx(image_binary, cv.MORPH_CLOSE, kernel, iterations=3)sure_bg = cv.dilate(image_binary, kernel, iterations=2)cv.imwrite("sure_bg.jpg", sure_bg)# 二進(jìn)制非sure_bg = cv.bitwise_not(sure_bg)cv.imwrite("bitwise_not_sure_bg.jpg", sure_bg)# 形態(tài)學(xué)變化,開運算element = cv.getStructuringElement(cv.MORPH_ELLIPSE, (3, 3))image_binary = cv.morphologyEx(image_binary, cv.MORPH_OPEN, element)cv.imwrite("morphologyEx_image_binary.jpg", image_binary)# 計算前景到背景的距離imageSC = cv.distanceTransform(image_binary, cv.DIST_L2, 5)imageSC = imageSC.astype(np.uint8)cv.imwrite("imageSC.jpg", imageSC)cv.imwrite("imageSC255.jpg", imageSC*255)# 歸一化cv.normalize(imageSC, imageSC, 0, 255, cv.NORM_MINMAX)cv.imwrite("imageSC_normalize.jpg", imageSC)cv.imwrite("imageSC_normalize255.jpg", imageSC*255)# 二值化# _, imageSC = cv.threshold(imageSC, 0, 255, cv.THRESH_OTSU + cv.THRESH_BINARY)_, imageSC = cv.threshold(imageSC, 0.3, 1.0, cv.THRESH_BINARY)cv.imwrite("imageSC_threshold.jpg", imageSC*255)return imageSC, sure_bgrng.seed(12345)
imgPath = "./images/6.jpeg"
src = cv.imread(imgPath)
shifted = cv.pyrMeanShiftFiltering(src, 7, 15)
cv.imwrite("shift.jpg", shifted)if src is None:print('Could not open or find the image:')# print('Could not open or find the image:', args.input)exit(0)
# Show source image
cv.imshow('Source Image', src)imageSC, sure_bg = process_img2(shifted)
# Show output image
cv.imshow('Background Image', sure_bg) # 背景# Dilate a bit the dist image
kernel1 = np.ones((3, 3), dtype=np.uint8)
dist = cv.dilate(imageSC, kernel1)
cv.imwrite("dist-dilate.jpg", dist*255)
cv.imshow('Peaks', dist)# 構(gòu)建初始markers
dist_8u = dist.astype('uint8')
# Find total markers
contours, _ = cv.findContours(dist_8u, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)# 創(chuàng)建即將應(yīng)用分水嶺算法的標(biāo)記圖像
# markers = np.zeros(dist.shape, dtype=np.int32)
markers = sure_bg.copy().astype(np.int32)# 標(biāo)記前景
for i in range(len(contours)):cv.drawContours(markers, contours, i, (i + 1), -1) # 輪廓標(biāo)記從1開始# 標(biāo)記背景
# cv.circle(markers, (5, 5), 3, 255, -1) # 此處背景標(biāo)記為255
# 可視化markersprint("before watershed: ", np.unique(markers)) # 0表示不確定標(biāo)記區(qū)域
markers_8u = (markers * 10).astype('uint8')
cv.imwrite('markers_8u.jpg', markers_8u)
cv.imshow('Markers', markers_8u)# 應(yīng)用分水嶺分割算法
markers = cv.watershed(src, markers)print("after watershed: ", np.unique(markers)) # -1表示邊界# mark = np.zeros(markers.shape, dtype=np.uint8)
mark = markers.astype('uint8')
cv.imwrite('mark.jpg', mark)mark = cv.bitwise_not(mark)
cv.imwrite('mark-bitwise_not.jpg', mark)
cv.imshow('Markers_v2', mark)# Generate random colors
colors = []
for contour in contours:colors.append((rng.randint(0, 256), rng.randint(0, 256), rng.randint(0, 256)))# Create the result image
dst = np.zeros((markers.shape[0], markers.shape[1], 3), dtype=np.uint8)
# Fill labeled objects with random colors
for i in range(markers.shape[0]):for j in range(markers.shape[1]):index = markers[i, j]if index > 0 and index <= len(contours): # -1表示邊界, 255表示背景dst[i, j, :] = colors[index - 1]
# Visualize the final image
cv.imshow('Final Result', dst)
cv.imwrite('Final-Result.jpg', dst)
cv.waitKey(0)
cv.destroyAllWindows()
實現(xiàn)二,感覺這套前處理少一些
import cv2 as cv
import numpy as np
import argparse
import random as rng
rng.seed(12345)
parser = argparse.ArgumentParser(description='Code for Image Segmentation with Distance Transform and Watershed Algorithm.\Sample code showing how to segment overlapping objects using Laplacian filtering, \in addition to Watershed and Distance Transformation')
parser.add_argument('--input', help='Path to input image.', default='./images/6.jpeg')
args = parser.parse_args()
src = cv.imread(cv.samples.findFile(args.input))
if src is None:print('Could not open or find the image:', args.input)exit(0)
# Show source image
cv.imshow('Source Image', src)# 轉(zhuǎn)灰度
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 二值化
ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
# noise removal,開運算
kernel = np.ones((5, 5), np.uint8)
opening = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations=2)# 獲取背景圖
sure_bg = opening.copy() # 背景
# Show output image
cv.imshow('Black Background Image', sure_bg) # 黑色是背景# 獲取前景圖
dist = cv.distanceTransform(opening, cv.DIST_L2, 3)
# Normalize the distance image for range = {0.0, 1.0}
# so we can visualize and threshold it
cv.normalize(dist, dist, 0, 1.0, cv.NORM_MINMAX)
cv.imshow('Distance Transform Image', dist)
_, dist = cv.threshold(dist, 0.2, 1.0, cv.THRESH_BINARY)
# Dilate a bit the dist image
kernel1 = np.ones((3, 3), dtype=np.uint8)
dist = cv.dilate(dist, kernel1)
cv.imshow('Peaks', dist)# 構(gòu)建初始markers
dist_8u = dist.astype('uint8')
# Find total markers
contours, _ = cv.findContours(dist_8u, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 創(chuàng)建即將應(yīng)用分水嶺算法的標(biāo)記圖像
markers = np.zeros(dist.shape, dtype=np.int32)
# 標(biāo)記前景
for i in range(len(contours)):cv.drawContours(markers, contours, i, (i + 1), -1) # 輪廓標(biāo)記從1開始
# 標(biāo)記背景
cv.circle(markers, (5, 5), 3, 255, -1) # 此處背景標(biāo)記為255
print("before watershed: ", np.unique(markers)) # 0表示不確定標(biāo)記區(qū)域
# 可視化markers
markers_8u = (markers * 10).astype('uint8')
cv.imshow('Markers', markers_8u)# 應(yīng)用分水嶺分割算法
markers = cv.watershed(src, markers)
print("after watershed: ", np.unique(markers)) # -1表示邊界# mark = np.zeros(markers.shape, dtype=np.uint8)
mark = markers.astype('uint8')
mark = cv.bitwise_not(mark)
# uncomment this if you want to see how the mark
# image looks like at that point
# cv.imshow('Markers_v2', mark)
# Generate random colors
colors = []
for contour in contours:colors.append((rng.randint(0, 256), rng.randint(0, 256), rng.randint(0, 256)))
# Create the result image
dst = np.zeros((markers.shape[0], markers.shape[1], 3), dtype=np.uint8)
# Fill labeled objects with random colors
for i in range(markers.shape[0]):for j in range(markers.shape[1]):index = markers[i, j]if index > 0 and index <= len(contours): # -1表示邊界, 255表示背景dst[i, j, :] = colors[index - 1]
# Visualize the final image
cv.imshow('Final Result', dst)
cv.waitKey()
4、效果展示
輸入
法二
輸入
法二
輸入
法二
5、涉及到的庫函數(shù)
5.1、cv2.pyrMeanShiftFiltering
cv2.pyrMeanShiftFiltering
是 OpenCV 中用于圖像平滑處理的一個函數(shù),它基于均值漂移(Mean Shift)算法,并通過圖像金字塔的方式來實現(xiàn)。這種濾波方法對于去除圖像中的噪聲和細(xì)節(jié)紋理非常有效,同時能夠保留圖像的邊緣信息。
一、函數(shù)原型
cv2.pyrMeanShiftFiltering(src, dst, sp, sr, maxLevel=1, termcrit=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_MAX_ITER, 5, 1))
二、參數(shù)解釋
- src: 輸入圖像,應(yīng)該是一個 8 位或 16 位的單通道或三通道圖像。
- dst: 輸出圖像,與輸入圖像具有相同的類型和大小。
- sp: 空間窗口的半徑,它決定了在進(jìn)行均值漂移計算時考慮的鄰域大小。
- sr: 顏色窗口的半徑,它決定了在顏色空間中考慮的鄰域大小。
- maxLevel: 金字塔的最大層數(shù)。默認(rèn)值為 1,表示只處理原始圖像,不進(jìn)行金字塔分解。增加層數(shù)可以在更粗的尺度上進(jìn)行濾波,但計算量也會增加。
- termcrit: 迭代過程的終止條件。它是一個元組,包含三個元素:終止條件的類型、最大迭代次數(shù)和所需滿足的精度。默認(rèn)值是 (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_MAX_ITER, 5, 1),意味著迭代將在達(dá)到最大迭代次數(shù) 5 或滿足精度 1 時停止。
三、使用示例
import cv2
import numpy as np # 讀取圖像
image = cv2.imread('example.jpg') # 使用 pyrMeanShiftFiltering 進(jìn)行濾波
filtered_image = cv2.pyrMeanShiftFiltering(image, None, 21, 31) # 顯示結(jié)果
cv2.imshow('Original Image', image)
cv2.imshow('Filtered Image', filtered_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
在這個例子中,我們讀取了一張名為 example.jpg 的圖像,然后使用 cv2.pyrMeanShiftFiltering
函數(shù)對其進(jìn)行濾波處理。其中,空間窗口的半徑設(shè)置為 21,顏色窗口的半徑設(shè)置為 31。處理后的圖像將顯示在窗口中。
四、注意事項
cv2.pyrMeanShiftFiltering
函數(shù)在計算上可能比較耗時,特別是對于大圖像和較大的窗口半徑。- 正確地選擇空間窗口和顏色窗口的半徑對于獲得良好的濾波效果至關(guān)重要。
- 濾波后的圖像可能會看起來更加平滑,但一些細(xì)節(jié)信息可能會丟失。
5.2、cv2.morphologyEx
cv2.morphologyEx
是 OpenCV 中用于執(zhí)行形態(tài)學(xué)變換的函數(shù)。形態(tài)學(xué)變換是一種基于圖像形狀的圖像處理技術(shù),可以用于提取圖像中的特定結(jié)構(gòu)或特征,如邊界、骨架、凸包等。這些變換基于圖像的集合表示,通過定義一些基本的操作(如腐蝕、膨脹、開運算、閉運算等)來實現(xiàn)對圖像的處理。
一、函數(shù)原型
cv2.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])
二、參數(shù)解釋
- src: 輸入圖像,必須是單通道的灰度圖像或二值圖像。
- op: 形態(tài)學(xué)變換的類型,可以是以下幾種之一:
- cv2.MORPH_ERODE: 腐蝕操作,使圖像中的白色區(qū)域縮小,黑色區(qū)域擴大。
- cv2.MORPH_DILATE: 膨脹操作,使圖像中的白色區(qū)域擴大,黑色區(qū)域縮小。
- cv2.MORPH_OPEN: 開運算,先進(jìn)行腐蝕再進(jìn)行膨脹,可以去除圖像中的小物體或噪聲。
- cv2.MORPH_CLOSE: 閉運算,先進(jìn)行膨脹再進(jìn)行腐蝕,可以填充圖像中的小孔或連接鄰近的物體。
- cv2.MORPH_GRADIENT: 形態(tài)學(xué)梯度,表示膨脹圖像與腐蝕圖像之差,用于突出圖像中的邊緣。
- cv2.MORPH_TOPHAT: 頂帽變換,原圖像減去膨脹后的圖像,用于分離比鄰近點亮一些的斑點。
- cv2.MORPH_BLACKHAT: 黑帽變換,膨脹后的圖像減去原圖像,用于分離比鄰近點暗一些的斑點。
- cv2.MORPH_HITMISS: 結(jié)構(gòu)元素對應(yīng)的點集比較,用于檢測圖像中的特定模式。
- kernel: 形態(tài)學(xué)變換的核,通常是一個矩形、橢圓或十字形的小矩陣。核的大小和形狀會影響變換的效果。
- dst: 輸出圖像,如果未指定,則函數(shù)會創(chuàng)建一個新的輸出圖像。
- anchor: 核的錨點,默認(rèn)是核的中心。錨點決定了核在圖像上移動時的參考點。
- iterations: 變換的次數(shù),默認(rèn)值為 1。增加迭代次數(shù)可以增強變換的效果。
- borderType: 邊界像素的外推方法,默認(rèn)值為 cv2.BORDER_CONSTANT。
- borderValue: 使用 cv2.BORDER_CONSTANT 時邊界的像素值,默認(rèn)值為 0。
三、使用示例
下面是一個簡單的使用示例,演示了如何使用 cv2.morphologyEx 函數(shù)進(jìn)行腐蝕和膨脹操作:
import cv2
import numpy as np # 讀取圖像
image = cv2.imread('example.png', 0) # 讀取為灰度圖像 # 定義核
kernel = np.ones((5, 5), np.uint8) # 腐蝕操作
eroded_image = cv2.morphologyEx(image, cv2.MORPH_ERODE, kernel) # 膨脹操作
dilated_image = cv2.morphologyEx(image, cv2.MORPH_DILATE, kernel) # 顯示結(jié)果
cv2.imshow('Original Image', image)
cv2.imshow('Eroded Image', eroded_image)
cv2.imshow('Dilated Image', dilated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
在這個例子中,我們讀取了一張名為 example.png 的灰度圖像,然后定義了一個 5x5 的矩形核。接著,我們使用 cv2.morphologyEx
函數(shù)分別進(jìn)行了腐蝕和膨脹操作,并將結(jié)果顯示在窗口中。
四、注意事項
- 形態(tài)學(xué)變換的效果取決于核的大小和形狀,以及變換的類型。
- 腐蝕操作會使圖像中的白色區(qū)域縮小,而膨脹操作會使白色區(qū)域擴大。
- 開運算和閉運算是腐蝕和膨脹的組合操作,可以用于去除小物體、填充小孔或連接鄰近物體。
- 在使用形態(tài)學(xué)變換時,需要注意選擇合適的核大小和形狀,以及變換的次數(shù),以獲得最佳的處理效果。
5.3、cv2.distanceTransform
cv2.distanceTransform 是 OpenCV 庫中的一個函數(shù),用于計算圖像中每個非零像素點到其最近的零像素點的距離。這個函數(shù)在處理二值圖像時特別有用,尤其是在圖像分割、形態(tài)學(xué)操作以及目標(biāo)檢測等任務(wù)中。
一、函數(shù)原型
cv2.distanceTransform(src, distanceType=cv2.DIST_L2, maskSize=5)
- src: 輸入的8位二值圖像,通常為單通道圖像。非零像素被視為前景(對象),而零像素被視為背景。
- distanceType: 距離類型,它決定了如何計算距離。常用的選項有:
- cv2.DIST_L1: 使用L1范數(shù)(城市街區(qū)距離)。
- cv2.DIST_L2: 使用L2范數(shù)(歐幾里得距離),這是默認(rèn)值。
- cv2.DIST_C: 使用Chebyshev距離。
- maskSize: 距離變換掩碼的大小,必須是正奇數(shù)。默認(rèn)值為5。掩碼越大,計算出的距離越精確,但計算成本也越高。
二、返回值
該函數(shù)返回一個與輸入圖像大小相同的圖像,但數(shù)據(jù)類型為32位浮點數(shù)。圖像中的每個像素值代表了該像素點到最近的零像素點的距離。
三、使用示例
import cv2
import numpy as np # 創(chuàng)建一個簡單的二值圖像
image = np.zeros((10, 10), dtype=np.uint8)
image[3:7, 3:7] = 1 # 在圖像中心創(chuàng)建一個4x4的白色方塊 # 應(yīng)用距離變換
dist_transform = cv2.distanceTransform(image, cv2.DIST_L2, 5) # 打印結(jié)果
print(image)
print(dist_transform)
output
[[0 0 0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0 0 0][0 0 0 1 1 1 1 0 0 0][0 0 0 1 1 1 1 0 0 0][0 0 0 1 1 1 1 0 0 0][0 0 0 1 1 1 1 0 0 0][0 0 0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0 0 0]]
[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][0. 0. 0. 1. 1. 1. 1. 0. 0. 0.][0. 0. 0. 1. 2. 2. 1. 0. 0. 0.][0. 0. 0. 1. 2. 2. 1. 0. 0. 0.][0. 0. 0. 1. 1. 1. 1. 0. 0. 0.][0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][0. 0. 0. 0. 0. 0. 0. 0. 0. 0.][0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
在這個示例中,我們首先創(chuàng)建了一個10x10的二值圖像,其中中心有一個4x4的白色方塊。然后,我們對這個圖像應(yīng)用了距離變換,并打印出結(jié)果。結(jié)果圖像中的每個像素值代表了該像素點到最近的零像素(即背景)的距離。
四、應(yīng)用場景
- 圖像分割:在圖像分割任務(wù)中,可以通過距離變換來確定前景和背景之間的邊界。
- 形態(tài)學(xué)操作:距離變換可以用于形態(tài)學(xué)梯度、膨脹和腐蝕等高級形態(tài)學(xué)操作的基礎(chǔ)。
- 目標(biāo)檢測:在目標(biāo)檢測中,距離變換可以幫助識別目標(biāo)物體的輪廓和形狀。
5.4、cv2.normalize
cv2.normalize
是 OpenCV 庫中的一個函數(shù),用于對數(shù)組(通常是圖像)進(jìn)行歸一化處理。歸一化是指將數(shù)據(jù)按比例縮放,使之落入一個小的特定區(qū)間,通常是[0, 1]或[-1, 1]。這種處理對于圖像預(yù)處理、特征提取和比較等任務(wù)非常重要,因為它可以幫助改善算法的收斂速度和性能,或者滿足某些特定算法對數(shù)據(jù)范圍的要求。
一、基本語法
cv2.normalize(src, dst=None, alpha=None, beta=None, norm_type=cv2.NORM_MINMAX, dtype=-1, mask=None)
二、參數(shù)解釋
- src: 輸入數(shù)組(圖像),可以是任意深度的,但通常是8位或32位浮點數(shù)。
- dst: 輸出數(shù)組,與輸入數(shù)組具有相同的形狀和深度。如果為None,則函數(shù)會創(chuàng)建一個具有適當(dāng)大小和類型的數(shù)組。
- alpha: 歸一化后的范圍下限(通常用于NORM_MINMAX和NORM_INF類型)。對于NORM_MINMAX,這個值表示歸一化后的最小值。
- beta: 歸一化后的范圍上限(同樣用于NORM_MINMAX和NORM_INF類型)。對于NORM_MINMAX,這個值表示歸一化后的最大值。
- norm_type: 歸一化類型。OpenCV提供了幾種不同的歸一化類型,如
cv2.NORM_MINMAX
(將數(shù)組縮放到指定范圍)、cv2.NORM_L2
(L2范數(shù)歸一化)等。 - dtype: 輸出數(shù)組的可選深度。當(dāng)參數(shù)為負(fù)值時(如-1),輸出數(shù)組與輸入數(shù)組具有相同的深度。
- mask: 可選的操作掩碼,用于指定哪些元素需要被歸一化。掩碼應(yīng)該是與輸入數(shù)組形狀相同的單通道數(shù)組,其中非零元素表示對應(yīng)的輸入元素需要被處理。
三、使用示例
import cv2
import numpy as np # 創(chuàng)建一個簡單的圖像(二維數(shù)組)
image = np.array([[10, 20, 30], [40, 50, 60]], dtype=np.float32) # 使用cv2.normalize進(jìn)行歸一化處理
normalized_image = cv2.normalize(image, None, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32F) print("歸一化后的圖像:")
print(normalized_image)
output
歸一化后的圖像:
[[0. 0.19999999 0.39999998][0.59999996 0.8 0.99999994]]
在這個例子中,我們將一個二維數(shù)組(模擬一個簡單的圖像)進(jìn)行了歸一化處理,將其值縮放到[0, 1]范圍內(nèi)。這種處理對于圖像處理中的許多任務(wù)都是非常有用的。
5.5、cv2.watershed
cv2.watershed 是OpenCV庫中用于圖像分割的一個函數(shù),它實現(xiàn)了基于標(biāo)記的分水嶺算法。分水嶺算法是一種圖像分割技術(shù),特別適用于從圖像中分離出觸摸或重疊的對象。
一、函數(shù)原型
cv2.watershed(image, markers) -> int, output markers
- image:輸入圖像,應(yīng)該是8位或浮點類型的三通道圖像。
- markers:輸入/輸出標(biāo)記數(shù)組,應(yīng)該是32位單通道圖像。在輸入時,標(biāo)記數(shù)組應(yīng)該包含已知的前景和背景標(biāo)記。在輸出時,函數(shù)將修改這個數(shù)組,為每個分割的區(qū)域分配不同的標(biāo)簽,并將邊界區(qū)域標(biāo)記為-1。
二、使用步驟
1、讀取和預(yù)處理圖像:
- 使用 cv2.imread 讀取圖像。
- 如果圖像是彩色的,可以轉(zhuǎn)換為灰度圖像(使用 cv2.cvtColor)。
- 應(yīng)用閾值處理(使用 cv2.threshold)或邊緣檢測(如Canny邊緣檢測)來生成二值圖像。
2、確定前景和背景標(biāo)記:
- 使用形態(tài)學(xué)操作(如膨脹和腐蝕)來增強或修正邊緣。
- 查找二值圖像中的連通組件(使用 cv2.findContours),并為每個組件分配一個唯一的標(biāo)記。
- 將背景標(biāo)記為0,前景標(biāo)記為正整數(shù)。
3、應(yīng)用分水嶺算法:
- 調(diào)用 cv2.watershed 函數(shù),傳入預(yù)處理后的圖像和標(biāo)記數(shù)組。
- 函數(shù)將修改標(biāo)記數(shù)組,為每個分割的區(qū)域分配不同的標(biāo)簽。
4、分析結(jié)果:
- 查看修改后的標(biāo)記數(shù)組,了解哪些像素被分配到了哪些區(qū)域。
- 使用這些信息在原圖上繪制分割邊界或進(jìn)行其他分析。
三、注意事項
- 分水嶺算法的效果很大程度上依賴于預(yù)處理步驟和標(biāo)記的正確性。
- 過度分割是一個常見問題,可以通過調(diào)整預(yù)處理步驟的參數(shù)或結(jié)合其他分割技術(shù)來減輕。
- 在使用分水嶺算法之前,通常需要確保圖像中的對象之間有清晰的邊界或分隔。
四、示例代碼
以下是一個簡單的示例代碼,演示了如何使用 cv2.watershed 函數(shù)進(jìn)行圖像分割:
import cv2
import numpy as np # 讀取圖像
image = cv2.imread('your_image.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 應(yīng)用閾值處理
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 查找輪廓并創(chuàng)建標(biāo)記數(shù)組
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
markers = np.zeros(gray.shape, dtype=np.int32) # 為每個輪廓分配一個唯一的標(biāo)記(從1開始)
for i, contour in enumerate(contours): cv2.drawContours(markers, [contour], -1, (i + 1), -1) # 應(yīng)用分水嶺算法
markers = cv2.watershed(image, markers) # 繪制分割邊界
image[markers == -1] = [0, 0, 255] # 將邊界設(shè)置為紅色 # 顯示結(jié)果
cv2.imshow('Segmented Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
請注意,上述示例代碼是一個簡單的演示,實際應(yīng)用中可能需要根據(jù)具體情況進(jìn)行調(diào)整和優(yōu)化。
6、參考
- 基于標(biāo)記的分水嶺分割算法
- https://anothertechs.com/programming/cpp/opencv/opencv-watershed/