你說得對,if cv2.countNonZero(binary) == 0: break
這個條件確實表示圖像中已經沒有非零像素,即圖像完全變為空白。這并不是骨架化完成的標志,而是表示圖像已經被腐蝕到沒有任何內容了。
在骨架化過程中,我們需要一個更合適的停止條件,以確保圖像被正確地簡化為單像素寬度的骨架,而不是被完全腐蝕掉。
骨架化的正確停止條件
在骨架化過程中,每次迭代都會從圖像的邊緣移除一層像素,直到只剩下單像素寬度的骨架。正確的停止條件是當圖像中不再有可以被移除的像素時,即圖像已經不能再被腐蝕,但仍然有非零像素存在。
改進的骨架化代碼
以下是一個更完整的骨架化代碼示例,其中包含了正確的停止條件:
import cv2
import numpy as npdef skeletonize(image):# 確保輸入圖像是二值化的_, binary = cv2.threshold(image, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)# 創建結構元素element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))# 初始化骨架圖像skeleton = np.zeros_like(binary)# 初始化前一次的骨架圖像prev_skeleton = np.zeros_like(binary)# 迭代直到骨架圖像不再變化while True:# 腐蝕操作eroded = cv2.erode(binary, element)# 膨脹操作temp = cv2.dilate(eroded, element)# 計算差值temp = cv2.subtract(binary, temp)# 更新骨架skeleton = cv2.bitwise_or(skeleton, temp)# 更新二值圖像binary = eroded.copy()# 檢查骨架圖像是否發生變化if np.array_equal(skeleton, prev_skeleton):break# 更新前一次的骨架圖像prev_skeleton = skeleton.copy()return skeleton# 讀取圖像
image = cv2.imread('your_image.png', cv2.IMREAD_GRAYSCALE)# 骨架化處理
skeleton = skeletonize(image)# 顯示結果
cv2.imshow('Skeleton', skeleton)
cv2.waitKey(0)
cv2.destroyAllWindows()
代碼解釋
-
初始化骨架圖像和前一次骨架圖像:
skeleton
用于存儲當前的骨架圖像。prev_skeleton
用于存儲上一次迭代的骨架圖像,以便比較是否發生變化。
-
迭代處理:
- 每次迭代中,先對當前的二值圖像進行腐蝕操作,然后進行膨脹操作。
- 計算腐蝕和膨脹后的圖像的差值,這個差值就是當前迭代的骨架部分。
- 將差值與之前的骨架圖像進行邏輯或操作,更新骨架圖像。
- 更新二值圖像為腐蝕后的圖像,繼續下一次迭代。
-
停止條件:
- 使用
np.array_equal(skeleton, prev_skeleton)
檢查當前的骨架圖像是否與上一次的骨架圖像相同。 - 如果相同,說明骨架圖像已經不再變化,此時已經完成了骨架化,退出循環。
- 使用
為什么這樣可以
在骨架化過程中,每次迭代都會從圖像的邊緣移除一層像素,直到只剩下單像素寬度的骨架。當骨架圖像不再變化時,說明已經無法再從邊緣移除像素,此時的骨架圖像就是最終的骨架。
結果
使用上述代碼,你可以得到一個單像素寬度的骨架圖像,而不是一個完全空白的圖像。這種骨架化方法在許多計算機視覺任務中非常有用,尤其是在需要提取形狀特征的場景中。