以下為該學習地址的學習筆記:Distance transformation in image - Python OpenCV - GeeksforGeeks
?其他學習資料:Morphology - Distance Transform
簡介
距離變換是一種用于計算圖像中每個像素與最近的非零像素之間距離的技術。它通常用于圖像分割和物體識別任務,因為它可以幫助識別圖像中物體的邊界。(如下圖片來源于Chamani, H., Rabbani, A., Russell, K. P., Zydney, A. L., Gomez, E. D., Hattrick-Simpers, J., & Werber, J. R. (2023). Rapid reconstruction of 3-D membrane pore structure using a single 2-D Micrograph.?arXiv preprint arXiv:2301.10601.)
1. 距離變換在OpenCV中的實現
OpenCV 中的距離變換函數 cv2.distanceTransform()
接收二值圖像并返回兩個數組:距離圖像和標簽圖像(the distance image and the label image)。距離圖像包含每個像素與最近的非零像素的距離值,標簽圖像包含最近的非零像素的標簽。
#簡單表示一下
dist_transform, labels = cv2.distanceTransform(binary_image, distance_type, mask_size)
binary_image
: 輸入的二值圖像,非零像素通常表示前景對象。distance_type
: 距離類型,可以使用常量如cv2.DIST_L1
(曼哈頓距離)或cv2.DIST_L2
(歐幾里得距離)。mask_size
: 掩碼大小,確定用于計算距離的掩碼的大小,值越大計算越精確但速度越慢。
距離類型
cv2.DIST_L1
: 曼哈頓距離the Manhattan distance,即只計算水平和垂直方向的距離。cv2.DIST_L2
: 歐幾里得距離the Euclidean distance,即計算實際的幾何距離,包含所有方向。
掩碼大小
掩碼大小(mask size)決定了計算距離時使用的鄰域范圍,常用的大小有3、5等。較大的掩碼可以提供更精確的距離值,但計算開銷也會增加。
距離變換的結果
距離變換的結果是一個與原始圖像大小相同的距離圖像,每個像素值表示該像素到最近前景像素的距離。通過這種方式,可以有效地識別圖像中對象的邊界。
距離圖像的歸一化
為了方便可視化,OpenCV提供了cv2.normalize()
函數,可以將距離圖像的值歸一化到0到255的范圍內。
歸一化
#簡單表示一下
normalized_dist_transform = cv2.normalize(dist_transform, None, 0, 255, cv2.NORM_MINMAX)
dist_transform
: 輸入的距離圖像。None
: 輸出數組,通常設為None表示原地操作。0
: 歸一化后的最小值。255
: 歸一化后的最大值。cv2.NORM_MINMAX
: 歸一化類型,將最小值和最大值歸一化到指定范圍。
除了距離變換函數外,OpenCV 還提供了 cv2.normalize()
函數,用于對距離圖像進行歸一化處理,使距離值在 0 到 255 之間。這對距離圖像的可視化非常有用。
距離變換是一種圖像處理技術,可用于獲取圖像中每個像素與最近的非零像素之間的距離。它常用于圖像分割和物體識別任務。
2. 具體步驟
在 OpenCV 中,我們可以使用 cv2.distanceTransform()
函數執行距離變換。
需要遵循的步驟:
- 加載圖像:使用
cv2.imread()
函數加載圖像。 - 轉換為灰度圖像:使用
cv2.cvtColor()
函數將圖像轉換為灰度圖像。這是因為距離變換函數僅適用于單通道圖像 - 創建二值圖像:進行閾值處理,使用
cv2.threshold()
函數對圖像進行二值化,將像素強度值大于某個閾值的像素設為255,其他像素設為0。 - 計算距離變換:使用
cv2.distanceTransform()
函數計算每個像素到最近非零像素的距離。該函數接受三個參數:二值圖像、距離類型(如cv2.DIST_L2
表示歐幾里得距離)和掩碼大小(如3表示3×3掩碼)。 - 返回結果:距離變換函數返回兩個數組:距離圖像和標簽圖像。距離圖像包含每個像素與最近的非零像素的距離值,標簽圖像包含最近的非零像素的標簽。
3. 示例
3.1 示例 1
import cv2 # 導入OpenCV庫# 加載圖像
# 你可以提供圖像的路徑
image = cv2.imread(r"Mandala.jpg") # 讀取圖像文件"Mandala.jpg"# 將圖像轉換為灰度圖像
grayScaleImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 將彩色圖像轉換為灰度圖像# 對圖像進行二值化處理,創建二值圖像
_, threshold = cv2.threshold(grayScaleImage, 123, 255, cv2.THRESH_BINARY) # 將灰度圖像進行二值化,閾值為123# 計算距離變換
# distTransform = cv2.distanceTransform(threshold, cv2.DIST_C, 3) # 使用Chebyshev距離計算距離變換
# distTransform= cv2.distanceTransform(threshold, cv2.DIST_L1, 3) # 使用曼哈頓距離計算距離變換
distTransform= cv2.distanceTransform(threshold, cv2.DIST_L2, 3) # 使用歐幾里得距離計算距離變換# 顯示距離變換后的圖像
cv2.imshow('Transformed Distance Image1', distTransform) # 顯示距離變換后的圖像# 按任意鍵結束進程
cv2.waitKey(0) # 等待按鍵輸入
cv2.destroyAllWindows() # 銷毀所有窗口# 保存距離變換后的圖像
cv2.imwrite('distTransformed.jpg', distTransform) # 將距離變換后的圖像保存為"distTransformed.jpg"
此代碼的輸出將是一個包含距離圖像的窗口,其中每個像素包含與最近非零像素的距離值。距離值的范圍為 0 到 255,值越大表示距離越大。
3.2 示例2(標簽圖像)
下面是OpenCV中距離變換的第二個示例代碼,演示了如何使用標簽圖像獲取圖像中每個像素的最近非零像素的坐標:
# importing necessary libraries
# 導入必要的庫
import cv2 # 導入OpenCV庫
import numpy as np # 導入NumPy庫,用于數組操作# Load the image
# you can specify the path to image
# 加載圖像
# 你可以指定圖像的路徑
image = cv2.imread(r"Visual_arts.jpg") # 讀取圖像文件"Visual_arts.jpg"# Convert the image to grayscale
# 使用cv2.cvtColor()函數將圖像轉換為灰度圖像
grayScaleImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Threshold the image to create a binary image
# 使用cv2.threshold()函數對灰度圖像進行二值化處理,創建二值圖像
_, threshold = cv2.threshold(grayScaleImage, 127, 255, cv2.THRESH_BINARY) # Calculate the distance transform
# 使用cv2.distanceTransform()函數計算二值圖像的距離變換
dist, labels, *other_vars = cv2.distanceTransform(threshold, cv2.DIST_L2, 3) # Find the coordinates of the nearest non-zero pixels for each pixel in the image
# 使用np.where()和np.column_stack()函數找到每個像素的最近非零像素的坐標
coords = np.column_stack(np.where(labels > 0)) # Print the coordinates
# squeeze is used to remove the undesired dimension
# 打印坐標
# 使用squeeze()函數去除不需要的維度
print(coords.squeeze())
輸出:
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1718 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 3536 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 5354 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 7172 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 8990 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143144 151 152 156 157 158 159 160 161 162 163 167 168 169 170 171 172 179180 200 201 202 203 204 240 241 242 243 244 254 255 256 257 287 290 297]
代碼片段補充介紹
_, threshold = cv2.threshold(grayScaleImage, 127, 255, cv2.THRESH_BINARY)
_
:這個下劃線通常用作占位符,表示在此處接收一個值但不需要使用它。在這里,它用于接收cv2.threshold()
函數返回的第一個返回值,即閾值化操作后的閾值(threshold),但實際上在后續代碼中并沒有使用這個值。threshold
:這是一個變量名,用于接收cv2.threshold()
函數返回的第二個返回值,即二值化后的圖像。這是我們感興趣的結果,它是一個灰度圖像,其中像素值只有兩種可能:0(黑色,表示背景)和255(白色,表示前景)。cv2.THRESH_BINARY
:這是cv2.threshold()
函數的一個參數,用于指定閾值化類型。cv2.THRESH_BINARY
表示二值閾值化類型,即將大于閾值的像素設置為一個值(這里是255),小于或等于閾值的像素設置為另一個值(這里是0)。
dist, labels, *other_vars = cv2.distanceTransform(threshold, cv2.DIST_L2, 3)
dist
:這是一個變量名,用于接收cv2.distanceTransform()
函數返回的第一個數組,即距離圖像(distance image)。距離圖像包含了每個像素到最近非零像素的距離值。labels
:這是一個變量名,用于接收cv2.distanceTransform()
函數返回的第二個數組,即標簽圖像(label image)。標簽圖像包含了每個像素所屬的最近非零像素的標簽。other_vars
:這個是Python中的可變參數形式,用于接收cv2.distanceTransform()
函數可能返回的額外的參數。在這個特定的情況下,cv2.distanceTransform()
函數實際上只返回兩個值(距離圖像和標簽圖像),所以other_vars
在這里實際上是一個空列表,因為沒有額外的參數需要接收。cv2.distanceTransform()
:這是OpenCV庫中的一個函數,用于計算圖像的距離變換。它接受以下參數:threshold
:輸入的二值化圖像,這里是前面閾值化得到的二值圖像。cv2.DIST_L2
:距離的類型,這里選擇歐幾里得距離(L2范數),表示每個像素到最近非零像素的歐幾里得距離。3
:掩碼大小,即用于計算距離的卷積核的大小,這里是3×3的卷積核。
補充掩碼:
- 控制掩碼的大小和形狀可以影響操作的結果,例如在距離變換中,掩碼的大小決定了計算每個像素到最近非零像素距離的精度和效率。
- 在距離變換中,掩碼用于計算每個像素到最近非零像素的距離。通常使用的掩碼大小是一個正方形(3×3、5×5等),它定義了像素周圍的鄰域。
coords = np.column_stack(np.where(labels > 0))
np.where(labels > 0)
labels > 0
:這是一個條件表達式,返回一個布爾類型的數組,數組的每個元素都是labels
中對應位置的像素值是否大于0的判斷結果。如果labels
中的像素值大于0,則對應位置為True,否則為False。np.where()
:這是一個NumPy函數,用于根據指定的條件返回符合條件的元素的索引。它可以接受一個條件表達式作為參數,并返回一個包含符合條件元素索引的元組。- 如果條件表達式是一個二維數組,
np.where()
返回的是一個包含兩個一維數組的元組,分別代表符合條件的行索引和列索引。 - 如果條件表達式是一個一維數組,
np.where()
直接返回符合條件的元素的索引。
- 如果條件表達式是一個二維數組,
在這里,np.where(labels > 0)
返回的是一個元組,其中包含了 labels
中大于0的像素點的索引。
np.column_stack()
np.column_stack()
:這是NumPy函數,用于將多個一維數組按列堆疊成一個二維數組。它接受一個元組或列表作為參數,參數中的每個元素是一個一維數組。
在這里,np.column_stack(np.where(labels > 0))
接受了 np.where(labels > 0)
返回的元組作為參數,將其中的每個一維數組按列堆疊成一個二維數組。因為 np.where(labels > 0)
返回的是一個包含行索引和列索引的元組,所以 np.column_stack()
的作用是將這兩個一維數組按列排列成一個二維數組,即將行索引和列索引對應堆疊在一起。
coords
coords
:這是一個變量名,用于接收np.column_stack(np.where(labels > 0))
的結果。它表示了所有labels
中大于0的像素點的坐標。
綜合起來,coords = np.column_stack(np.where(labels > 0))
這行代碼的作用是找到 labels
圖像中所有像素值大于0的像素點的坐標,并將這些坐標按列堆疊成一個二維數組,存儲在 coords
變量中。