第 11 章 形狀表示與描述
教材P554頁,第11.1題
重新定義鏈碼的一個起始點,以便所得的數字序列形成一個最小值整數.請證明該編碼與邊界上的初始起點無關.
設鏈碼
.另選一個初始起點,相當於迴圈位移該鏈碼.設迴圈左移
位,得到鏈碼
重新定義鏈碼的一個起始點,相當於將該鏈碼迴圈移動若干位.假設最小數字為
.
迴圈左移
位得到最小值
;由於最小值唯一,所以將
迴圈左移
位得到
.
求編碼10176722335422 的歸一化起始點.
編碼 10176722335422 歸一化後為01767223354221,起始點為原始鏈碼的第2 個數字.
教材P554頁,第11.2題
- 如11.1.2節中解釋的那樣,證明鏈碼的一次差分會將該鏈碼關於旋轉歸一化.
計算編碼 0110233210332322111 的一次差分.
故編碼
的一次差分為
;用迴圈首差鏈碼計算的一次差分為
.
求圖 1 中圖形的鏈碼、一階差分、形狀數和形狀數的階(起點在左上角,按照順時針方向).
圖 1 形狀 表 1 答案 教材P556頁,第11.26題
一家使用瓶子盛裝各種工業化學品的公司在聽說您成功地解決了影象處理問題後,僱用您來設計一種檢測瓶子未裝滿的方法.瓶子在傳送帶上移動並透過自動裝填和封蓋機時的情形如下圖所示.當液位低於瓶頸底部和瓶子肩部的中間點時,則認為瓶子未裝滿.瓶子橫斷面的側面與傾斜面的區域定義為瓶子的肩部.瓶子在不斷移動,但該公司有一個成像系統,該系統裝備了一個前端照明閃光燈,可有效地停止瓶子的移動,所以您可以得到非常接近於這裡顯示的樣例影象圖 2.基於上述資料,請您提出一個檢測未完全裝滿的瓶子的解決方案.清楚地陳述您所做的那些可能會影響到解決方案的所有假設.
圖 2 原圖 假設
- 瓶子位置和尺寸一致:假設瓶子不傾斜,垂直位置和尺寸大致一致.
- 圖片拍攝條件一致:假設拍攝條件一致,瓶子在傳送帶上的位置和角度相對固定.
- 灰度對比明顯:假設影象中瓶子和背景有明顯的灰度對比,能夠透過二值化處理有效區分.
- 傳送帶速度合適、恆定:假設傳送帶的速度恆定,瓶子在傳送帶上的運動速度和方向一致,瓶子中液體不會搖晃.
- 整齊排列:假設瓶子在傳送帶上排列整齊,水平位置和大小差異不大,便於排序和檢測.
方案
- 影象預處理:讀取灰度影象並進行二值化處理,將影象轉換為黑白圖,以區分瓶子和背景.
- 形態學操作:對二值化影象進行形態學開操作,去除影象噪聲,確保瓶子輪廓清晰.
- 連通分量分析:使用連通分量分析技術檢測瓶子位置,計算各瓶子的統計資訊,如面積和位置.
- 液位檢測:根據瓶子的高度和液麵位置判斷瓶子是否裝滿.如果液麵低於設定的閾值,則認為瓶子未裝滿.
- 結果顯示:在影象上標註檢測結果,使用不同顏色框和編號表示檢測結果.
實現
影象預處理與二值化
gray_image = cv2.imread("bottles-assembly-line.tif", 0) # 0 表示以灰度模式讀取
BW_THRESHOLD = 170 # 二值化閾值
_, binary_image = cv2.threshold(gray_image, BW_THRESHOLD, 255, cv2.THRESH_BINARY)讀取灰度影象,並使用閾值170進行二值化處理,將影象轉換為黑白圖.
形態學操作
kernel = np.ones((3, 7), np.uint8)
opened_image = cv2.morphologyEx(binary_image, cv2.MORPH_OPEN, kernel)使用形態學開操作去除噪聲,確保瓶子的輪廓更加清晰.若不進行形態學操作,可能會導致連通分量分析不準確,如圖 3 所示.
圖 3 未進行形態學開操作,直接處理的結果 連通分量分析
num_labels, labels_im, stats, centroids = cv2.connectedComponentsWithStats(opened_image)使用
cv2.connectedComponentsWithStats獲取連通分量及其統計資訊.液位檢測與結果顯示
AREA_THRESHOLD = 900 # 面積閾值
components = [
stats[i]
for i in range(1, num_labels)
if stats[i, cv2.CC_STAT_AREA] > AREA_THRESHOLD
]
components.sort(key=lambda x: x[cv2.CC_STAT_LEFT]) # 按水平位置排序
for i, component in enumerate(components):
x, y, width, height, area = component
bottom = y + height
is_full = bottom < 100 # 液麵閾值
color = (0, 255, 0) if is_full else (0, 0, 255) # 滿的為綠色,未滿的為紅色
cv2.rectangle(output_image, (x, y), (x + width, y + height), color, 2)
cv2.putText(output_image, f"{i + 1}", (x, y + height + 25), cv2.FONT_HERSHEY_SIMPLEX, 0.75, color, 2, cv2.LINE_AA)根據連通分量的面積和位置篩選出有效的瓶子,計算液麵位置,判斷是否裝滿,並在影象上繪製矩形框和編號.液麵低於100畫素認為瓶子未裝滿,用紅色框表示;否則用綠色框表示.
結果輸出
最終輸出圖片結果如圖 4 所示.
圖 4 檢測結果 控制檯輸出結果如下:
第 0 個連通分量的面積為 2607,高度為 65,液麵位置的 y 座標為 83,是滿的
第 1 個連通分量的面積為 3132,高度為 63,液麵位置的 y 座標為 81,是滿的
第 2 個連通分量的面積為 8606,高度為 119,液麵位置的 y 座標為 136,不是滿的
第 3 個連通分量的面積為 3132,高度為 63,液麵位置的 y 座標為 81,是滿的
第 4 個連通分量的面積為 1871,高度為 65,液麵位置的 y 座標為 83,是滿的透過以上方法,能夠有效檢測傳送帶上未完全裝滿液體的瓶子,並在影象上直觀展示檢測結果.
附:程式完整程式碼
import cv2
import numpy as np
# 讀取影象
gray_image = cv2.imread("bottles-assembly-line.tif", 0) # 0 表示以灰度模式讀取
# 二值化
BW_THRESHOLD = 170 # 閾值
_, binary_image = cv2.threshold(gray_image, BW_THRESHOLD, 255, cv2.THRESH_BINARY)
# 對圖片進行開操作
kernel = np.ones((3, 7), np.uint8)
opened_image = cv2.morphologyEx(
binary_image,
cv2.MORPH_OPEN, # = 2: 開操作
# cv2.MORPH_ERODE = 0:腐蝕
# MORPH_DILATE = 1:膨脹處理
# MORPH_CLOSE = 3:閉運算處理
kernel,
)
# 獲取連通分量及其統計資訊
num_labels, labels_im, stats, centroids = cv2.connectedComponentsWithStats(opened_image)
# 顯示每個連通分量
output_image = np.zeros((gray_image.shape[0], gray_image.shape[1], 3), dtype=np.uint8)
# 為每個連通分量賦予隨機顏色
for label in range(1, num_labels): # 從1開始,0是背景
mask = labels_im == label
output_image[mask] = np.random.randint(0, 255, 3)
AREA_THRESHOLD = 900 # 面積閾值
components = [
stats[i]
for i in range(1, num_labels)
# 儲存面積大於閾值的連通分量
if stats[i, cv2.CC_STAT_AREA] > AREA_THRESHOLD
]
components.sort(
key=lambda x: x[cv2.CC_STAT_LEFT],
) # 按照左上角 x 的座標排序,從左到右
for i, component in enumerate(components):
x, y, width, height, area = component
bottom = y + height # 液麵位置的 y 座標
is_full = bottom < 100
print(
f"第 {i+1} 個連通分量的面積為 {area},高度為 {height},液麵位置的 y 座標為 {bottom},"
+ ("是滿的" if is_full else "不是滿的")
)
# 繪製矩形框
color = (0, 255, 0) if is_full else (0, 0, 255)
cv2.rectangle(output_image, (x, y), (x + width, y + height), color, 2)
cv2.putText(
output_image,
f"{i + 1}",
(x, y + height + 25),
cv2.FONT_HERSHEY_SIMPLEX,
0.75,
color,
2,
cv2.LINE_AA,
)
# 顯示結果
cv2.imshow("Output", output_image)
# 儲存到檔案
cv2.imwrite("output.png", output_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
评论