在計算機視覺領域,OpenCV 是一款功能強大且應用廣泛的開源庫,它提供了豐富的 API,支持圖像讀取、預處理、特征檢測等多種操作。本文將結合實際代碼案例,詳細講解如何使用 OpenCV 實現輪廓檢測、輪廓近似、模板匹配等常用功能,同時介紹如何通過argparse
模塊配置命令行參數,讓程序更具靈活性和可擴展性。
一、OpenCV 圖像處理核心功能實戰
(一)圖像輪廓檢測與篩選
輪廓是圖像中物體邊界的重要表示,通過檢測輪廓,我們可以獲取物體的形狀、大小等關鍵信息。以下代碼實現了從圖像讀取、預處理到輪廓檢測、篩選與繪制的完整流程。
import cv2# 1. 讀取圖像并進行預處理
phone = cv2.imread('lunkuo.png') # 替換為你的圖片路徑
# 轉為灰度圖,減少計算量并為后續二值化做準備
gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)
# 二值化處理:將灰度圖轉為黑白二值圖,突出物體輪廓
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)# 2. 檢測輪廓(兼容OpenCV 3.x和4.x版本)
img, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)# 3. 計算輪廓的面積與周長
# 計算前兩個輪廓的面積
area_0 = cv2.contourArea(contours[0])
print(f"第一個輪廓的面積:{area_0}")
area_1 = cv2.contourArea(contours[1])
print(f"第二個輪廓的面積:{area_1}")
# 計算第一個輪廓的周長(closed=True表示輪廓是閉合的)
length = cv2.arcLength(contours[0], closed=True)
print(f"第一個輪廓的周長:{length}")# 4. 篩選面積大于10000的輪廓(去除小噪聲輪廓)
filtered_contours = []
for contour in contours:if cv2.contourArea(contour) > 10000:filtered_contours.append(contour)
# 繪制篩選后的輪廓(綠色,線寬3)
image_copy = phone.copy()
cv2.drawContours(image=image_copy, contours=filtered_contours,contourIdx=-1, color=(0, 255, 0), thickness=3)
cv2.imshow('Contours_show_10000', image_copy)
cv2.waitKey(0) # 等待按鍵,按下任意鍵關閉窗口# 5. 找到面積最大的輪廓并繪制(紅色,線寬3)
# 按面積降序排序輪廓
sorted_contours = sorted(contours, key=cv2.contourArea, reverse=True)
largest_contour = sorted_contours[0]
# 繪制最大輪廓
image_copy = phone.copy()
cv2.drawContours(image=image_copy, contours=[largest_contour],contourIdx=-1, color=(0, 0, 255), thickness=3)
cv2.imshow('largest_contour', image_copy)
cv2.waitKey(0)# 6. 為指定輪廓繪制外接圓和外接矩形
cnt = contours[2] # 選擇第三個輪廓(可根據實際需求調整索引)
# 繪制外接圓(綠色,線寬2)
(x, y), radius = cv2.minEnclosingCircle(cnt)
center = (int(x), int(y)) # 圓心坐標(需轉為整數,圖像像素坐標為整數)
phone_circle = cv2.circle(phone, center, int(radius), (0, 255, 0), 2)
cv2.imshow('phone_circle', phone_circle)
cv2.waitKey(0)
# 繪制外接矩形(綠色,線寬2)
x, y, w, h = cv2.boundingRect(cnt) # x,y為矩形左上角坐標,w為寬,h為高
phone_rectangle = cv2.rectangle(phone, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('phone_rectangle', phone_rectangle)
cv2.waitKey(0)cv2.destroyAllWindows() # 關閉所有OpenCV窗口
關鍵知識點解析:
- 圖像預處理:
cvtColor
將 BGR 格式(OpenCV 默認讀取格式)轉為灰度圖,threshold
通過設定閾值(此處為 127)將灰度圖轉為二值圖,讓輪廓更清晰。 - 輪廓檢測:
findContours
函數中,RETR_TREE
表示獲取輪廓的層級關系,CHAIN_APPROX_SIMPLE
會簡化輪廓,去除冗余點,減少內存占用。 - 輪廓篩選與排序:通過
contourArea
計算輪廓面積,結合條件判斷篩選出目標輪廓;sorted
函數配合cv2.contourArea
可按面積對輪廓排序,輕松找到最大輪廓。
(二)輪廓近似:簡化復雜輪廓
當輪廓邊緣過于復雜(如包含大量細小鋸齒)時,我們可以通過輪廓近似算法,用更少的點表示輪廓,同時保留其整體形狀。以下代碼演示了輪廓近似的實現過程:
import cv2# 1. 讀取圖像并預處理(步驟與上一部分類似)
phone = cv2.imread('lunkuo.png')
gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)# 2. 檢測輪廓(兼容不同OpenCV版本,取返回值的后兩個元素)
contours = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2]# 3. 輪廓近似
# epsilon為近似精度,是輪廓周長的百分比(此處為1%),值越小,近似輪廓越接近原輪廓
epsilon = 0.01 * cv2.arcLength(contours[0], True)
# approxPolyDP函數實現輪廓近似,True表示輪廓閉合
approx = cv2.approxPolyDP(contours[0], epsilon, True)# 4. 對比原輪廓與近似輪廓的形狀(輸出點的數量)
print(f"原輪廓的點數量:{contours[0].shape}") # 格式為(N, 1, 2),N為點的數量
print(f"近似輪廓的點數量:{approx.shape}")# 5. 繪制近似輪廓并顯示
phone_new = phone.copy()
image_contours = cv2.drawContours(phone_new, [approx], contourIdx=-1, color=(0, 255, 0), thickness=3)
cv2.imshow('原始圖像', phone)
cv2.waitKey(0)
cv2.imshow('近似輪廓', image_contours)
cv2.waitKey(0)
cv2.destroyAllWindows()
核心原理:輪廓近似基于Douglas-Peucker 算法,通過設定epsilon
值控制近似程度。epsilon
越小,近似輪廓與原輪廓的差異越小,但點的數量越多;epsilon
越大,輪廓越簡化,但可能丟失細節。實際應用中需根據需求調整該參數。
(三)模板匹配:在圖像中查找目標物體
模板匹配是通過滑動模板圖像在待檢測圖像上移動,計算模板與待檢測圖像各區域的相似度,從而找到目標物體位置的技術。以下代碼實現了在 “可樂” 圖像中查找 “瓶蓋” 模板的功能:
import cv2# 1. 讀取待檢測圖像(kele.png)和模板圖像(keke.png)
kele = cv2.imread('kele.png')
keke = cv2.imread('keke.png')# 2. 顯示原始圖像,確認圖像讀取成功
cv2.imshow('可樂圖像', kele)
cv2.imshow('瓶蓋模板', keke)
cv2.waitKey(0)# 3. 獲取模板圖像的高和寬(用于后續繪制矩形)
h, w = keke.shape[:2] # shape返回(高, 寬, 通道數),取前兩個元素# 4. 執行模板匹配
# 匹配方法:cv2.TM_CCORR_NORMED(歸一化相關匹配),返回相似度矩陣
res = cv2.matchTemplate(kele, keke, cv2.TM_CCORR_NORMED)# 5. 找到相似度最高的位置(歸一化匹配中,最大值對應最相似區域)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
top_left = max_loc # 匹配區域的左上角坐標
bottom_right = (top_left[0] + w, top_left[1] + h) # 匹配區域的右下角坐標# 6. 在待檢測圖像上繪制矩形框,標記目標位置(綠色,線寬2)
kele_keke = cv2.rectangle(kele, top_left, bottom_right, (0, 255, 0), 2)# 7. 顯示匹配結果
cv2.imshow('模板匹配結果', kele_keke)
cv2.waitKey(0)
cv2.destroyAllWindows()
注意事項:
- 模板匹配對尺度和旋轉敏感,如果目標物體在待檢測圖像中發生縮放或旋轉,匹配效果會顯著下降,此時需結合尺度不變特征變換(SIFT)、旋轉不變特征變換(SURF)等算法。
- 匹配方法的選擇:除
TM_CCORR_NORMED
外,OpenCV 還提供TM_SQDIFF
(平方差匹配,最小值為最佳匹配)、TM_CCOEFF
(相關系數匹配)等方法,需根據實際場景選擇。
二、用 argparse 配置命令行參數,提升程序靈活性
在實際項目中,我們常需要調整程序參數(如閾值、端口號等),如果每次修改都直接改動代碼,效率較低。argparse
是 Python 標準庫中的模塊,可實現命令行參數的解析,讓參數配置更便捷。以下代碼演示了其基本用法:
import argparse# 1. 創建參數解析器對象
parser = argparse.ArgumentParser(description='命令行參數配置示例') # description為程序描述# 2. 添加命令行參數
# --SERIAL_PORT1:第一個報警器的串口號,字符串類型,默認值為COM5,help為參數說明
parser.add_argument('--SERIAL_PORT1', type=str, default='COM5', help='第一個報警器的串口號')
# --area_thred:物體面積閾值,整數類型,默認值1600
parser.add_argument('--area_thred', type=int, default=1600, help='物體面積的閾值')
# --confid_level:識別置信度,浮點類型,默認值0.8
parser.add_argument('--confid_level', type=float, default=0.8, help='識別的置信度')
# --aaa:自定義整數參數,默認值100(無短選項)
parser.add_argument('--aaa', type=int, default=100)
# -b/--bbb:自定義整數參數,支持短選項(-b)和長選項(--bbb),默認值10
parser.add_argument('-b', '--bbb', type=int, default=10)# 3. 解析命令行參數
opt = parser.parse_args()# 4. 使用解析后的參數
a = opt.aaa
b = opt.bbb
print(f"參數aaa與bbb的和為:{a + b}")
使用方法:
- 將代碼保存為
argparse_demo.py
。 - 在命令行中運行,可直接使用默認參數:
bash
python argparse_demo.py
輸出:參數aaa與bbb的和為:110
- 也可在命令行中指定參數值,覆蓋默認值:
bash
python argparse_demo.py --aaa 200 -b 50 --SERIAL_PORT1 COM3 --area_thred 2000
輸出:參數aaa與bbb的和為:250
優勢:通過argparse
,我們無需修改代碼,即可在命令行中靈活調整參數,尤其適合批量運行程序或在服務器環境中使用。
三、總結與拓展
本文通過實際代碼案例,講解了 OpenCV 中輪廓檢測、輪廓近似、模板匹配等核心功能的實現,同時介紹了argparse
模塊的使用,幫助提升程序的靈活性。這些技術在目標檢測、圖像分割、物體識別等場景中應用廣泛,例如:
- 工業質檢:通過輪廓檢測判斷產品是否存在缺陷。
- 智能監控:通過模板匹配查找特定目標(如可疑物品)。
- 機器人視覺:通過輪廓近似簡化物體形狀,便于機器人抓取。