第三部分:特征提取與目標檢測

像邊緣、角點、特定的紋理模式等都是圖像的特征。提取這些特征是許多計算機視覺任務的關鍵第一步,例如圖像匹配、對象識別、圖像拼接等。目標檢測則是在圖像中找到特定對象(如人臉、汽車等)的位置。

本部分將涵蓋以下關鍵主題:

  1. 特征點提取
    • 角點檢測(Harris 角點)
    • SIFT/SURF 特征(概念介紹)
    • ORB 特征提取與匹配
  2. 輪廓檢測與分析
    • 輪廓查找
    • 輪廓屬性(面積、周長、形狀)
    • 實戰:簡單形狀識別
  3. 目標檢測入門
    • 模板匹配
    • 級聯分類器
    • 實戰:人臉檢測

我們將提供詳細的解釋和代碼示例。公式部分將使用 LaTeX 格式方便復制和學習。


OpenCV 特征提取與目標檢測 (第三部分)

歡迎來到 OpenCV 教程的第三部分!在前兩部分,我們學習了圖像的基礎操作、像素訪問以及基本的圖像增強和濾波技術。現在,我們將提升一個層次,學習如何從圖像中提取更抽象、更有意義的信息:特征。

為什么需要特征?

想象一下,你要識別一張照片里的某個人。你不會逐個比較兩個圖像中所有像素點是否完全一致,因為光照、角度、距離等變化都會導致像素值改變。你會尋找這個人的眼睛、鼻子、嘴巴等關鍵部位的形狀、相對位置等信息,這些就是特征

特征提取就是尋找這些有區分度、相對穩定(對光照、旋轉、縮放不敏感)的圖像區域或點。提取到特征后,我們可以用它們來:

  • 匹配不同圖像中的同一個物體或場景(如圖像拼接、三維重建)。
  • 識別圖像中的特定對象(如人臉、汽車)。
  • 跟蹤視頻中移動的物體。
  • 分析圖像的結構和內容。

讓我們開始學習這些強大的技術!

1. 特征點提取

特征點是圖像中具有獨特性、易于識別和跟蹤的點,例如角點、圖像紋理變化劇烈的點等。

1.1 角點檢測 (Corner Detection)

角點是兩條或多條邊相交的點,它在各個方向上的像素強度變化都非常明顯。因此,角點是圖像中非常重要的特征點。

Harris 角點檢測

Harris 角點檢測是一種經典的角點檢測算法。其基本思想是:如果在圖像中的一個窗口沿任何方向移動,窗口內的像素灰度都發生顯著變化,那么這個窗口所在的區域可能是一個角點。

OpenCV 提供了 cv2.cornerHarris() 函數來實現 Harris 角點檢測。

Python

import cv2
import numpy as np# --- 練習 1.1: Harris 角點檢測 ---# 1. 加載圖像并轉換為灰度圖 (角點檢測通常在灰度圖上進行)
# Harris 角點檢測的輸入圖像需要是 float32 類型
image_path = 'your_image.jpg'
try:image_color = cv2.imread(image_path)if image_color is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")image = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)
except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")image_color = np.zeros((400, 600, 3), dtype=np.uint8)cv2.putText(image_color, "Placeholder Image", (100, 200), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)image = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)# 將灰度圖像轉換為 float32
image_float = np.float32(image)# 2. 進行 Harris 角點檢測
# 參數: src (輸入圖像, float32), blockSize (用于角點檢測的鄰域大小),
#       ksize (Sobel算子的大小,用于計算梯度), k (Harris檢測器參數,通常在0.04-0.06)
harris_corners = cv2.cornerHarris(image_float, blockSize=2, ksize=3, k=0.04)# 3. 標記檢測到的角點
# 結果 harris_corners 是一個浮點圖像,值越大表示越可能是角點
# 我們對其進行閾值處理,并將角點位置在原始彩色圖像上用圓圈標記出來
# 閾值可以根據 harris_corners 的最大值來設置,例如最大值的 1%
threshold = 0.01 * harris_corners.max()# 復制原始彩色圖像,用于標記
output_image = image_color.copy()# 遍歷結果圖像,在R值大于閾值的位置繪制圓圈
# 注意: harris_corners 的維度與輸入圖像相同
# np.where(condition) 會返回滿足條件的元素的索引 (y_coords, x_coords)
strong_corners_indices = np.where(harris_corners > threshold)# strong_corners_indices 是一個元組,第一個元素是所有滿足條件的行的索引,第二個是所有滿足條件的列的索引
y_coords, x_coords = strong_corners_indices# 在原彩色圖上繪制圓圈
for i in range(len(x_coords)):center = (x_coords[i], y_coords[i])cv2.circle(output_image, center, 5, (0, 255, 0), 2) # 綠色圓圈,半徑5,線寬2# 4. 顯示結果
cv2.imshow('Original Image', image_color)
cv2.imshow('Harris Corners', output_image)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 1.1 完成 ---")

練習提示:

  • 嘗試調整 blockSizeksizek 參數,觀察檢測到的角點數量和位置的變化。
  • 調整閾值 (threshold) 也會顯著影響結果。較高的閾值會檢測到更少的、更明顯的角點。
1.2 SIFT/SURF 特征 (概念介紹)

SIFT (Scale-Invariant Feature Transform)SURF (Speeded Up Robust Features) 是兩種非常著名的、對圖像縮放和旋轉具有不變性的局部特征點檢測和描述算法。

  • 它們都能在圖像中找到具有獨特性的特征點(稱為關鍵點)。
  • 對于每個關鍵點,它們都會計算一個描述符(一串數字),用來描述關鍵點周圍區域的紋理信息。這個描述符對于光照、視角、縮放、旋轉都有一定的魯棒性。
  • SIFT 和 SURF 算法相對復雜且計算量較大。

重要提示: SIFT 和 SURF 算法在某些國家是受專利保護的。OpenCV 的主模塊 (opencv-python) 可能不包含這些算法。你可能需要安裝 opencv-contrib-python 庫來使用它們。但是,由于專利問題和后續出現的更優秀的免費算法,SIFT 和 SURF 在學術界和工業界的使用受到一定限制。因此,本教程僅概念性介紹它們,我們將把重點放在一個免費且高效的替代品:ORB 特征。

1.3 ORB 特征提取與匹配

ORB (Oriented FAST and Rotated BRIEF) 是一種高效且免費的特征點檢測和描述算法。它結合了 FAST 算法來檢測關鍵點,并對 BRIEF 描述符進行了改進,使其具有方向和一定的尺度不變性。

ORB 的核心是兩個主要部分:

  1. 檢測器: 使用改進的 FAST 算法來尋找關鍵點。FAST 算法速度非常快,通過比較中心像素與周圍圓環像素的灰度值來判斷是否為關鍵點。ORB 在此基礎上增加了多尺度檢測和方向信息。
  2. 描述符: 使用改進的 BRIEF 描述符來描述關鍵點周圍的區域。BRIEF 描述符通過比較關鍵點周圍隨機選取的像素對的灰度值來生成一個二進制串。ORB 對 BRIEF 進行了旋轉使其具有旋轉不變性。

特征匹配

提取到圖像的特征點及其描述符后,我們可以進行特征匹配。特征匹配的目的是在兩幅圖像中找到對應于同一物理點的特征點對。常用的匹配方法是比較描述符之間的相似度(例如,對于 ORB 的二進制描述符,可以使用漢明距離)。

BFMatcher (Brute-Force Matcher)

BFMatcher 是一種簡單的特征匹配器。它對第一組描述符中的每個描述符,計算與第二組描述符中所有描述符的距離,然后找到最近的一個或幾個。

通常,我們會使用 KNN (K-Nearest Neighbors) 匹配,找到每個描述符的 k 個最近鄰,然后應用 Lowe's Ratio Test 進行過濾:如果最佳匹配的距離與次佳匹配的距離之比小于一個閾值(例如 0.75),則認為這是一個好的匹配點。

Python

import cv2
import numpy as np# --- 練習 1.3: ORB 特征提取與匹配 ---# 1. 加載兩張圖像 (最好包含一些相同的物體或場景)
# 請替換成你自己的圖片路徑
image_path1 = 'your_image1.jpg'
image_path2 = 'your_image2.jpg'
try:image1 = cv2.imread(image_path1, cv2.IMREAD_GRAYSCALE) # ORB通常在灰度圖上工作image2 = cv2.imread(image_path2, cv2.IMREAD_GRAYSCALE)if image1 is None:raise FileNotFoundError(f"圖片文件未找到: {image_path1}")if image2 is None:raise FileNotFoundError(f"圖片文件未找到: {image_path2}")print("成功加載兩張圖像。")
except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")print("將使用模擬圖像代替,無法演示實際匹配效果。")# 如果圖片加載失敗,創建兩個簡單的模擬圖像image1 = np.zeros((300, 400), dtype=np.uint8)cv2.putText(image1, "Image 1 Placeholder", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)image2 = np.zeros((300, 400), dtype=np.uint8)cv2.putText(image2, "Image 2 Placeholder", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)# 2. 創建 ORB 特征檢測器對象
orb = cv2.ORB_create(nfeatures=5000, # 最多檢測的特征點數量scaleFactor=1.2, # 圖像金字塔的縮放因子nlevels=8, # 圖像金字塔的層數edgeThreshold=31, # 忽略邊緣區域的像素數量firstLevel=0, # 金字塔的第一層WTA_K=2, # 描述符生成算法scoreType=cv2.ORB_HARRIS_SCORE, # 使用Harris角點響應或FAST角點響應patchSize=31 # 描述符計算的區域大小
)# 3. 檢測關鍵點和計算描述符
# kp: 關鍵點列表
# des: 描述符 (numpy數組)
kp1, des1 = orb.detectAndCompute(image1, None)
kp2, des2 = orb.detectAndCompute(image2, None)# 確保描述符是 float32 類型,有些匹配器可能需要
# ORB 描述符是二進制的,這里不需要轉換,但對于SIFT/SURF可能需要print(f"圖像1檢測到 {len(kp1)} 個關鍵點")
print(f"圖像2檢測到 {len(kp2)} 個關鍵點")# 4. 創建 Brute-Force 匹配器
# 對于ORB的二進制描述符,使用 cv2.NORM_HAMMING
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=False) # crossCheck=True 會進行雙向匹配過濾# 5. 進行特征匹配 (使用 KNN 匹配)
# k=2 表示為每個關鍵點找到 2 個最近鄰
matches = bf.knnMatch(des1, des2, k=2)# 6. 應用 Lowe's Ratio Test 過濾好的匹配點
good_matches = []
ratio_threshold = 0.75 # 比例閾值for m, n in matches:# m是最佳匹配,n是次佳匹配if m.distance < ratio_threshold * n.distance:good_matches.append(m)print(f"經過 Lowe's Ratio Test 過濾后,找到 {len(good_matches)} 個好的匹配點")# 7. 繪制匹配結果
# 參數: img1, kp1, img2, kp2, matches, outImg, flags
# flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS 表示不繪制沒有匹配點的關鍵點
match_image = cv2.drawMatchesKnn(image1, kp1, image2, kp2, [good_matches], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)# 8. 顯示結果
cv2.imshow('ORB Feature Matches', match_image)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 1.3 完成 ---")

練習提示:

  • 嘗試使用包含相同物體但拍攝角度、距離略有差異的兩張圖片。
  • 調整 ORB_create() 函數中的參數,例如 nfeatures,觀察檢測到的關鍵點數量和匹配結果的變化。
  • 調整 Lowe's Ratio Test 中的 ratio_threshold,較低的閾值會得到更嚴格(更少但更可靠)的匹配點。

2. 輪廓檢測與分析

輪廓是連接所有連續的、具有相同顏色或亮度的點的曲線。它們代表了物體的邊界。輪廓在物體檢測、形狀分析和識別、圖像分割等領域非常有用。

查找輪廓通常在二值圖像(只有黑白兩種顏色)上進行。

2.1 輪廓查找

OpenCV 提供了 cv2.findContours() 函數來查找圖像中的輪廓。

Python

import cv2
import numpy as np# --- 練習 2.1: 輪廓查找 ---# 1. 加載圖像并轉換為灰度圖
image_path = 'your_image.jpg'
try:image_color = cv2.imread(image_path)if image_color is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")image_gray = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)
except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")image_color = np.zeros((400, 600, 3), dtype=np.uint8)cv2.putText(image_color, "Placeholder Image", (100, 200), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)image_gray = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)# 2. 對灰度圖進行二值化處理 (閾值分割)
# findContours 函數通常在二值圖像上操作
# cv2.THRESH_BINARY: 大于閾值的設為最大值 (255), 否則設為0
# cv2.THRESH_OTSU: 使用Otsu算法自動尋找最佳閾值
ret, binary_image = cv2.threshold(image_gray, 127, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 3. 查找輪廓
# cv2.findContours() 函數會返回輪廓列表和對應的層級關系
# 參數: image (輸入圖像, 8位單通道,通常是二值圖), mode (輪廓檢索模式), method (輪廓近似方法)
# RETR_TREE: 檢索所有輪廓并建立完整的層級樹
# CHAIN_APPROX_SIMPLE: 壓縮水平、垂直和對角線段,只保留端點
# 注意: OpenCV 4.x 版本 findContours 返回值順序變了,前面是image,后面是輪廓和層級
contours, hierarchy = cv2.findContours(binary_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)print(f"檢測到 {len(contours)} 個輪廓")# 4. 在原始彩色圖像上繪制所有輪廓
# 參數: image (繪制目標圖像), contours (輪廓列表), contourIdx (要繪制的輪廓索引,-1表示所有),
#       color (顏色), thickness (線寬)
# 復制原始彩色圖像,以免修改原圖
image_with_contours = image_color.copy()
cv2.drawContours(image_with_contours, contours, -1, (0, 255, 0), 2) # 綠色,線寬2# 5. 顯示結果
cv2.imshow('Original Image', image_color)
cv2.imshow('Binary Image', binary_image)
cv2.imshow('Contours', image_with_contours)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 2.1 完成 ---")

練習提示:

  • cv2.findContours() 函數會改變輸入圖像,所以在查找輪廓時通常傳入二值圖像的副本或查找后不再使用該二值圖像。
  • 嘗試不同的 mode (如 cv2.RETR_LIST 不建立層級關系) 和 method (如 cv2.CHAIN_APPROX_NONE 存儲所有輪廓點) 參數,觀察它們對結果的影響。
2.2 輪廓屬性

一旦找到輪廓,我們可以計算它們的各種屬性來描述輪廓的形狀、大小、位置等。

Python

import cv2
import numpy as np# --- 練習 2.2: 輪廓屬性 ---# (沿用上一個練習的輪廓查找代碼)
image_path = 'your_image.jpg'
try:image_color = cv2.imread(image_path)if image_color is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")image_gray = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)
except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")image_color = np.zeros((400, 600, 3), dtype=np.uint8)cv2.putText(image_color, "Placeholder Image", (100, 200), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)image_gray = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)ret, binary_image = cv2.threshold(image_gray, 127, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 注意: findContours 修改了 binary_image,所以通常傳遞副本
contours, hierarchy = cv2.findContours(binary_image.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)# 復制原始彩色圖像用于可視化屬性
image_with_properties = image_color.copy()print("\n--- 輪廓屬性 ---")
# 遍歷所有找到的輪廓
for i, contour in enumerate(contours):# 忽略非常小的輪廓(可能是噪聲)if cv2.contourArea(contour) < 100:continueprint(f"\n輪廓 {i+1}:")# 面積 (Area)area = cv2.contourArea(contour)print(f"  面積: {area:.2f}")# 周長 (Perimeter / Arc Length)# 參數: curve (輪廓), closed (是否閉合)perimeter = cv2.arcLength(contour, closed=True)print(f"  周長: {perimeter:.2f}")# 矩 (Moments) - 用于計算中心、面積等# M['m00'] 就是面積M = cv2.moments(contour)# 計算中心點 (Centroid)if M['m00'] != 0:cx = int(M['m10'] / M['m00'])cy = int(M['m01'] / M['m00'])print(f"  中心點 (cx, cy): ({cx}, {cy})")# 在中心點繪制圓圈cv2.circle(image_with_properties, (cx, cy), 5, (0, 0, 255), -1) # 紅色實心圓# 外接矩形 (Bounding Rectangle) - 直立矩形# 參數: points (輪廓點)x, y, w, h = cv2.boundingRect(contour)print(f"  外接矩形 (x, y, w, h): ({x}, {y}, {w}, {h})")# 繪制外接矩形cv2.rectangle(image_with_properties, (x, y), (x+w, y+h), (255, 0, 0), 2) # 藍色# 最小外接圓 (Minimum Enclosing Circle)# 參數: points (輪廓點)(center_x, center_y), radius = cv2.minEnclosingCircle(contour)center_circle = (int(center_x), int(center_y))radius_int = int(radius)print(f"  最小外接圓 (中心: {center_circle}, 半徑: {radius_int})")# 繪制最小外接圓cv2.circle(image_with_properties, center_circle, radius_int, (0, 255, 255), 2) # 黃色# 輪廓近似 (Contour Approximation) - 簡化輪廓# 使用 Ramer-Douglas-Peucker 算法# 參數: curve (輪廓), epsilon (近似精度,原始輪廓與近似輪廓之間的最大距離), closed (是否閉合)epsilon = 0.02 * perimeter # 精度通常設為周長的百分比approx = cv2.approxPolyDP(contour, epsilon, closed=True)print(f"  近似輪廓點數量: {len(approx)}")# 繪制近似輪廓 (可選,可以用 drawContours)# 6. 顯示結果
cv2.imshow('Original Image', image_color)
cv2.imshow('Contours with Properties', image_with_properties)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 2.2 完成 ---")

練習提示:

  • 理解不同屬性的含義及其計算方法。
  • 注意如何使用這些屬性來描述輪廓的特征。例如,可以通過比較輪廓面積和其外接矩形面積的比例來判斷輪廓的“緊湊”程度。
2.3 實戰:簡單形狀識別

結合輪廓查找和輪廓屬性,我們可以嘗試識別一些簡單的幾何形狀,如矩形、圓形。

思路:


3. 目標檢測入門

目標檢測比簡單的形狀識別更進一步,它旨在圖像中找到特定類別的對象(如人臉、汽車、貓等)的位置,并通常用一個邊界框標記出來。

3.1 模板匹配 (Template Matching)

模板匹配是一種簡單直觀的目標檢測方法。它需要一個已知的模板圖像(你要尋找的對象的小圖像),然后在源圖像中滑動這個模板,計算模板與源圖像中對應區域的相似度。相似度最高的區域就被認為是找到了模板。

3.2 級聯分類器 (Cascade Classifiers)

級聯分類器是一種基于機器學習的目標檢測方法。它使用一系列經過訓練的分類器(弱分類器)組成一個級聯結構。在檢測時,圖像區域會依次通過這些分類器。如果在任何一個階段被判定為非目標,就會被立即丟棄,大大提高了檢測速度。只有通過所有分類器的區域才會被認為是目標。

OpenCV 提供了一個使用 Haar-like 特征(類似于簡單的邊緣、線條特征)訓練的級聯分類器框架。盡管現代深度學習方法在許多目標檢測任務上取得了更好的效果,但級聯分類器(尤其是用于人臉檢測)因為其速度快、實時性好,在一些場景下仍然有應用價值。

使用級聯分類器進行目標檢測需要:

Python

import cv2
import numpy as np# --- 練習 3.2: 級聯分類器入門 ---# 1. 加載預訓練的級聯分類器模型
# 你需要找到 OpenCV 安裝目錄下的 haarcascades 文件夾,或者從網上下載
# 例如: '/usr/local/share/opencv4/haarcascades/haarcascade_frontalface_default.xml'
# 或者在項目目錄下放置下載好的xml文件
cascade_path = 'haarcascade_frontalface_default.xml' # 替換成你的xml文件路徑# 創建級聯分類器對象
face_cascade = cv2.CascadeClassifier()# 加載分類器模型
if not face_cascade.load(cv2.samples.findFile(cascade_path)):print(f"錯誤: 無法加載級聯分類器文件: {cascade_path}")print("請檢查文件路徑是否正確,或從以下位置下載:")print("https://github.com/opencv/opencv/tree/master/data/haarcascades")# 如果加載失敗,創建一個空的分類器,后續檢測會失敗face_cascade = None# 2. 加載圖像并轉換為灰度圖 (級聯分類器通常在灰度圖上工作)
image_path = 'your_image_with_faces.jpg' # 最好找一張包含人臉的圖片
try:image_color = cv2.imread(image_path)if image_color is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")print(f"成功加載圖像: {image_path}")image_gray = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)image_gray = cv2.equalizeHist(image_gray) # 可選: 直方圖均衡化,有時能提高檢測效果except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")print("將使用模擬圖像代替,無法演示實際檢測效果。")# 創建模擬圖像,無法包含真實人臉image_color = np.zeros((400, 600, 3), dtype=np.uint8)cv2.putText(image_color, "Placeholder Image", (100, 200), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)image_gray = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)# 3. 進行目標檢測 (人臉檢測)
# 參數: image (灰度圖), scaleFactor (圖像縮放因子), minNeighbors (每個矩形應該具有的鄰近個數),
#       minSize (最小可能對象大小), maxSize (最大可能對象大小)
if face_cascade: # 確保分類器加載成功faces = face_cascade.detectMultiScale(image_gray,scaleFactor=1.1, # 每次圖像尺寸減小的比例minNeighbors=5, # 每個目標至少被檢測到5次才被確認為目標minSize=(30, 30) # 最小目標尺寸 (寬, 高)# maxSize=(200, 200) # 最大目標尺寸)
else:faces = [] # 如果分類器加載失敗,檢測結果為空列表print(f"檢測到 {len(faces)} 個人臉")# 4. 在原始彩色圖像上繪制檢測到的目標 (矩形框)
output_image = image_color.copy()
for (x, y, w, h) in faces:cv2.rectangle(output_image, (x, y), (x+w, y+h), (255, 0, 0), 2) # 藍色矩形框# 5. 顯示結果
cv2.imshow('Original Image', image_color)
cv2.imshow('Detected Faces', output_image)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 3.2 完成 ---")

練習提示:

3.3 實戰:人臉檢測

這個實戰練習就是將上一節學習的級聯分類器應用到人臉檢測任務上。你只需要確保有包含人臉的圖片以及正確加載人臉分類器的 XML 文件。

  1. 找到輪廓。
  2. 過濾掉過小或過大的輪廓。
  3. 對輪廓進行近似。
  4. 根據近似輪廓的頂點數量來判斷形狀:
    • 如果頂點數量接近 4 且是凸多邊形,可能是矩形/正方形。
    • 如果頂點數量很多(近似后變化不大)且是凸多邊形,可能是圓形。
  5. 進一步細化:對于頂點數為 4 的,可以檢查長寬比來區分正方形和矩形。對于圓形,可以檢查面積與最小外接圓面積的比例。Python

    import cv2
    import numpy as np# --- 實戰練習: 簡單形狀識別 ---# 1. 加載圖像并轉換為灰度圖
    image_path = 'your_image.jpg' # 最好找一張有明顯幾何圖形的圖片
    try:image_color = cv2.imread(image_path)if image_color is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")image_gray = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)
    except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")# 創建一個包含簡單形狀的模擬圖像image_color = np.zeros((400, 400, 3), dtype=np.uint8)cv2.rectangle(image_color, (50, 50), (150, 150), (255, 0, 0), -1) # 藍色方塊cv2.circle(image_color, (300, 100), 50, (0, 255, 0), -1) # 綠色圓圈cv2.rectangle(image_color, (200, 200), (350, 300), (0, 0, 255), -1) # 紅色矩形image_gray = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)# 2. 二值化圖像
    ret, binary_image = cv2.threshold(image_gray, 127, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 3. 查找輪廓 (注意使用副本)
    contours, hierarchy = cv2.findContours(binary_image.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)# 復制原始彩色圖像用于繪制結果
    output_image = image_color.copy()print("\n--- 形狀識別結果 ---")# 4. 遍歷輪廓并進行識別
    for contour in contours:# 過濾小輪廓area = cv2.contourArea(contour)if area < 1000: # 過濾面積小于1000的輪廓continue# 計算周長perimeter = cv2.arcLength(contour, closed=True)# 輪廓近似epsilon = 0.04 * perimeter # 提高 epsilon 可以減少近似點數量,有助于識別多邊形approx = cv2.approxPolyDP(contour, epsilon, closed=True)# 獲取外接矩形屬性x, y, w, h = cv2.boundingRect(approx)aspect_ratio = float(w) / hbbox_area = w * h# 識別形狀shape = "未知"M = cv2.moments(contour)if M['m00'] != 0:cx = int(M['m10'] / M['m00'])cy = int(M['m01'] / M['m00'])center = (cx, cy)else:center = (x + w // 2, y + h // 2) # 使用外接矩形中心代替# 繪制輪廓并標注形狀cv2.drawContours(output_image, [contour], -1, (0, 255, 0), 2) # 繪制當前輪廓num_vertices = len(approx)if num_vertices == 3:shape = "三角形"elif num_vertices == 4:# 判斷是正方形還是矩形if 0.9 <= aspect_ratio <= 1.1 and 0.85 <= area / bbox_area <= 1.15: # 檢查長寬比和面積比例shape = "正方形"else:shape = "矩形"elif num_vertices == 5:shape = "五邊形"else:# 對于頂點數量多的輪廓,可能是圓形或其它復雜形狀# 可以通過檢查面積與最小外接圓面積的比例來判斷是否接近圓形(center_circle_x, center_circle_y), radius = cv2.minEnclosingCircle(contour)circle_area = np.pi * radius**2if area / circle_area > 0.8: # 如果輪廓面積占最小外接圓面積的大部分shape = "圓形"else:shape = "其他" # 或者 "復雜形狀"print(f"  輪廓點數量: {len(contour)}, 近似點數量: {num_vertices}, 識別為: {shape}")# 在中心點附近顯示形狀名稱cv2.putText(output_image, shape, (center[0] - 20, center[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)# 5. 顯示結果
    cv2.imshow('Original Image', image_color)
    cv2.imshow('Simple Shape Recognition', output_image)cv2.waitKey(0)
    cv2.destroyAllWindows()print("\n--- 實戰練習: 簡單形狀識別 完成 ---")
    

    實戰提示:

  6. 這個簡單的形狀識別方法對于理想的幾何圖形效果較好,對于真實照片中形狀會受光照、遮擋、變形等影響,效果會下降。
  7. 調整 epsilon 參數會影響近似輪廓的頂點數量,從而影響識別結果。
  8. 圓形識別可以通過檢查面積與最小外接圓面積的比例、或者周長與半徑的關系等多種方法來實現。
  9. Python

    import cv2
    import numpy as np# --- 練習 3.1: 模板匹配 ---# 1. 加載源圖像和模板圖像
    # 請替換成你自己的圖片路徑,確保 template_image.jpg 是 source_image.jpg 的一部分
    source_image_path = 'your_source_image.jpg'
    template_image_path = 'your_template_image.jpg' # 模板圖像要小于源圖像try:source_image = cv2.imread(source_image_path)template_image = cv2.imread(template_image_path)if source_image is None:raise FileNotFoundError(f"源圖片文件未找到: {source_image_path}")if template_image is None:raise FileNotFoundError(f"模板圖片文件未找到: {template_image_path}")print("成功加載源圖像和模板圖像。")
    except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")print("將使用模擬圖像代替,無法演示實際匹配效果。")# 創建模擬圖像source_image = np.zeros((300, 400, 3), dtype=np.uint8)cv2.circle(source_image, (200, 150), 50, (0, 255, 0), -1)cv2.putText(source_image, "Source (Green Circle)", (50, 250), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)template_image = np.zeros((100, 100, 3), dtype=np.uint8)cv2.circle(template_image, (50, 50), 40, (0, 255, 0), -1)cv2.putText(template_image, "Template", (10, 80), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)# 確保兩張圖像都是單通道或三通道,且類型一致
    # 模板匹配也可以在灰度圖上進行,通常更快
    source_gray = cv2.cvtColor(source_image, cv2.COLOR_BGR2GRAY)
    template_gray = cv2.cvtColor(template_image, cv2.COLOR_BGR2GRAY)# 獲取模板圖像的寬度和高度
    w, h = template_gray.shape[::-1] # shape返回(h, w),[::-1]反轉得到(w, h)# 2. 進行模板匹配
    # 參數: image (源圖像), templ (模板圖像), method (匹配方法)
    # cv2.TM_CCOEFF_NORMED: 歸一化相關系數匹配,值越接近1越好
    # cv2.TM_SQDIFF_NORMED: 歸一化平方差匹配,值越接近0越好
    method = cv2.TM_CCOEFF_NORMED
    result = cv2.matchTemplate(source_gray, template_gray, method)# 3. 查找最佳匹配位置
    # cv2.minMaxLoc() 找到匹配結果矩陣中的最小值、最大值及其位置
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)# 根據匹配方法,最佳匹配位置可能是 min_loc 或 max_loc
    # 對于 TM_CCOEFF_NORMED, TM_CCORR_NORMED, TM_CCOEFF,最大值表示最佳匹配
    # 對于 TM_SQDIFF, TM_SQDIFF_NORMED, TM_CCORR,最小值表示最佳匹配
    if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:top_left = min_loc # 最佳匹配的左上角坐標
    else:top_left = max_loc# 計算最佳匹配區域的右下角坐標
    bottom_right = (top_left[0] + w, top_left[1] + h)# 4. 在源圖像上繪制匹配結果 (矩形框)
    # 復制源圖像以免修改原圖
    output_image = source_image.copy()
    cv2.rectangle(output_image, top_left, bottom_right, (0, 0, 255), 2) # 紅色矩形框# 5. 顯示結果
    cv2.imshow('Source Image', source_image)
    cv2.imshow('Template Image', template_image)
    cv2.imshow('Template Matching Result', output_image)
    # 匹配結果矩陣通常數值范圍較大,直接顯示可能不直觀,可以先歸一化再顯示
    # cv2.imshow('Match Result Matrix', cv2.normalize(result, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U))cv2.waitKey(0)
    cv2.destroyAllWindows()print("\n--- 練習 3.1 完成 ---")
    

    練習提示:

  10. 模板匹配對模板圖像的大小、旋轉、光照變化非常敏感。如果模板和目標在源圖像中的外觀有較大差異,匹配可能會失敗。
  11. 嘗試不同的 method 參數,比較它們的效果。
  12. cv2.matchTemplate 也可以返回多個匹配區域(例如,使用閾值過濾匹配結果矩陣)。
  13. 一個預先訓練好的分類器模型文件(通常是 XML 格式)。OpenCV 庫自帶了一些常用的分類器模型,如人臉、眼睛等。你可以在 OpenCV 的 GitHub 倉庫中找到它們(opencv/data/haarcascadesopencv_extra/testdata/cv/face/)。
  14. 使用 cv2.CascadeClassifier 類加載模型。
  15. 使用 cascade.detectMultiScale() 方法在圖像中進行檢測。
  16. 找到并替換 cascade_path 為你環境中正確的 XML 文件路徑。
  17. 嘗試不同的 scaleFactorminNeighbors 參數。減小 scaleFactor 可以檢測到更小的目標,但速度變慢;增大 minNeighbors 可以減少誤檢,但可能漏掉一些目標。
  18. 嘗試使用不同的圖像進行測試。
  19. 除了人臉,OpenCV 還提供了眼睛、身體等其他預訓練的級聯分類器。
  20. Python

    import cv2
    import numpy as np# --- 實戰練習: 人臉檢測 ---# 1. 指定人臉分類器模型文件路徑
    # 這是 OpenCV 官方提供的模型,你需要確保你的OpenCV安裝目錄有這個文件,或者手動下載放在項目目錄
    # 例如: '/usr/local/share/opencv4/haarcascades/haarcascade_frontalface_default.xml'
    # 或者直接放在當前腳本同一目錄下
    # 你可以從這里下載: https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xml
    cascade_filename = 'haarcascade_frontalface_default.xml'# 創建人臉分類器對象
    face_cascade = cv2.CascadeClassifier()# 嘗試加載模型文件
    if not face_cascade.load(cv2.samples.findFile(cascade_filename)):print(f"錯誤: 無法加載人臉分類器文件: {cascade_filename}")print("請確保文件存在,并可能需要手動下載或找到OpenCV安裝路徑下的haarcascades文件夾。")print("使用一個空的分類器,后續檢測將不會找到任何目標。")face_cascade = None # 設置為 None 表示加載失敗# 2. 加載你要檢測人臉的圖像
    image_path = 'your_image_to_detect_faces.jpg' # 請替換為你想要檢測人臉的圖片路徑
    try:image_color = cv2.imread(image_path)if image_color is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")print(f"成功加載圖像: {image_path}")
    except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")# 創建一個空白圖像作為備用image_color = np.zeros((400, 600, 3), dtype=np.uint8)cv2.putText(image_color, "Placeholder Image", (100, 200), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)# 3. 將圖像轉換為灰度圖 (級聯分類器工作在灰度圖上)
    gray_image = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)
    # 可以選擇性地進行直方圖均衡化以增強對比度
    # gray_image = cv2.equalizeHist(gray_image)# 4. 進行人臉檢測
    # 使用 detectMultiScale 方法
    # 參數: scaleFactor, minNeighbors, minSize, maxSize (這些參數會影響檢測精度和速度)
    if face_cascade: # 只有在分類器加載成功時才執行檢測faces = face_cascade.detectMultiScale(gray_image,scaleFactor=1.1, # 圖像縮小比例minNeighbors=5,  # 構成檢測目標的最小鄰近矩形數量minSize=(30, 30) # 最小檢測窗口大小)
    else:faces = [] # 如果分類器加載失敗,結果為空print(f"檢測到 {len(faces)} 個人臉")# 5. 在原始圖像上繪制檢測到的人臉矩形框
    output_image = image_color.copy()
    # faces 變量是一個包含檢測到的人臉矩形信息的列表,每個元素是 (x, y, w, h)
    for (x, y, w, h) in faces:cv2.rectangle(output_image, (x, y), (x+w, y+h), (0, 255, 0), 2) # 綠色矩形框# 6. 顯示結果
    cv2.imshow('Original Image', image_color)
    cv2.imshow('Face Detection Result', output_image)# 7. 等待按鍵,然后關閉窗口
    cv2.waitKey(0)
    cv2.destroyAllWindows()print("\n--- 實戰練習: 人臉檢測 完成 ---")
    

    實戰提示:

  21. 最關鍵的一步是正確指定 cascade_filename 的路徑。
  22. 嘗試使用不同光照、角度、表情的人臉圖片進行測試,觀察檢測效果。
  23. 調整 detectMultiScale 的參數,尤其是 scaleFactorminNeighbors,會顯著影響檢測結果。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/78963.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/78963.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/78963.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Canvas基礎篇:圖形繪制

Canvas基礎篇&#xff1a;圖形繪制 圖形繪制moveTo()lineTo()lineTo繪制一條直線代碼示例效果預覽 lineTo繪制平行線代碼示例效果預覽 lineTo繪制矩形代碼示例效果預覽 arc()arc繪制一個圓代碼實現效果預覽 arc繪制一段弧代碼實現效果預覽 arcTo()rect()曲線 結語 圖形繪制 在…

瑞芯微芯片算法開發初步實踐

文章目錄 一、算法開發的一般步驟1.選擇合適的深度學習框架2.對于要處理的問題進行分類&#xff0c;是回歸問題還是分類問題。3.對數據進行歸納和整理4.對輸入的數據進行歸一化和量化&#xff0c;保證模型運行的效率和提高模型運行的準確度5.在嵌入式處理器上面運行模型&#x…

計算機畢業設計--基于深度學習(U-Net與多尺度ViT)的模糊車牌圖像清晰化復原算法設計與實現(含Github代碼+Web端在線體驗鏈接)

基于深度學習的U-Net架構下多尺度Transformer車牌圖像去模糊算法設計與實現 如果想對舊照片進行模糊去除&#xff0c;劃痕修復、清晰化&#xff0c;請參考這篇CSDN作品&#x1f447; 計算機畢業設計–基于深度學習的圖像修復&#xff08;清晰化劃痕修復色彩增強&#xff09;算…

(Go Gin)Gin學習筆記(四)Gin的數據渲染和中間件的使用:數據渲染、返回JSON、淺.JSON()源碼、中間件、Next()方法

1. 數據渲染 1.1 各種數據格式的響應 json、結構體、XML、YAML類似于java的properties、ProtoBuf 1.1.1 返回JSON package mainimport ("github.com/gin-gonic/gin""net/http" )func main() {r : gin.Default()r.POST("/demo", func(res *gi…

實驗:串口通信

/************************************************* * AT89C52 串口通信實驗&#xff08;實用修正版&#xff09; * 特點&#xff1a; * 1. 解決所有編譯警告 * 2. 保持代碼簡潔 * 3. 完全功能正常 ************************************************/ #include <re…

智駕賽道的諾曼底登陸,Momenta上海車展雄起

作者 |蘆葦 編輯 |德新 今年的上海車展依舊熱鬧非凡&#xff0c;但火熱的車市背后也是暗流涌動。尤其對智駕供應商而言&#xff0c;「智駕平權」帶動了解決方案大量上車&#xff0c;各大主機廠紛紛選定各自的主要供應商&#xff0c;這也意味著賽道機會越發收斂。 正如汽車品牌…

Java 事務詳解

目錄 一、事務的基本概念1.1 什么是事務?1.2 事務的 ACID 特性二、Java 事務管理的實現方式2.1 JDBC 事務管理2.2 Spring 事務管理2.2.1 添加 Spring 依賴2.2.2 配置 Spring 事務管理2.2.3 使用 Spring 事務注解三、事務隔離級別四、最佳實踐4.1 盡量縮小事務范圍4.2 合理選擇…

DirectX12(D3D12)基礎教程七 深度模板視圖\剔除\謂詞

本章主要講遮擋&#xff0c;作者認為比較復雜有難度的知識點&#xff0c;作為基礎教程不會深入講解。 GPU渲染管線 主要包括以下階段 輸入裝配&#xff08;IA&#xff09;&#xff1a;讀取頂點數據 &#xff0c;定義頂點數據結構頂點著色&#xff08;VS&#xff09;&#xf…

溫補晶振(TCXO)穩定性優化:從實驗室到量產的關鍵技術

在現代通信、航空航天、5G基站等對頻率穩定性要求極高的領域&#xff0c;溫補晶振&#xff08;TCXO&#xff09;扮演著不可或缺的角色。其穩定性直接影響系統的性能與可靠性&#xff0c;因此&#xff0c;對TCXO穩定性優化技術的研究與實踐至關重要。 一、溫度補償算法&#xff…

C++,設計模式,【建造者模式】

文章目錄 通俗易懂的建造者模式&#xff1a;手把手教你造電腦一、現實中的建造者困境二、建造者模式核心思想三、代碼實戰&#xff1a;組裝電腦1. 產品類 - 電腦2. 抽象建造者 - 裝機師傅3. 具體建造者 - 電競主機版4. 具體建造者 - 辦公主機版5. 指揮官 - 裝機總控6. 客戶端使…

前端基礎之《Vue(13)—重要API》

重要的API 一、nextTick() 1、寫法 Vue.$nextTick()或者this.$nextTick() 原因&#xff1a; set操作代碼是同步的&#xff0c;但是代碼背后的行為是異步的。set操作修改聲明式變量&#xff0c;觸發re-render生成新的虛擬DOM&#xff0c;進一步執行diff運算&#xff0c;找到…

Windows 中搭建 browser-use WebUI 1.4

目錄 1. 背景介紹2. 搭建過程3. 補充 1. 背景介紹 背景&#xff1a;想要在 Windows 中復現 browser-use WebUI pickle反序列化漏洞&#xff0c;該漏洞在 v1.7 版本中已經修復&#xff0c;所以需要搭建 小于 1.7 版本的環境&#xff0c;我這里搭建的是 1.4 版本。 項目地址&am…

【數據通信完全指南】從物理層到協議棧的深度解析

目錄 1. 通信技術演進與核心挑戰1.1 從電報到5G的技術變遷1.2 現代通信系統的三大瓶頸 2. 通信系統架構深度解構2.1 OSI七層模型運作原理2.2 TCP/IP協議棧實戰解析 3. 物理層關鍵技術實現3.1 信號調制技術演進路線3.2 信道復用方案對比 4. 數據傳輸可靠性保障4.1 CRC校驗算法數…

CMD與PowerShell:Windows命令行工具的對比與使用指南

CMD與PowerShell&#xff1a;Windows命令行工具的對比與使用指南 文章目錄 CMD與PowerShell&#xff1a;Windows命令行工具的對比與使用指南引言1. CMD&#xff08;命令提示符&#xff09;簡介1.1 什么是CMD&#xff1f;1.2 CMD的特點1.3 常用CMD命令示例1.4 CMD的優勢與局限 2…

93. 后臺線程與主線程更新UI Maui例子 C#例子

在.NET MAUI開發中&#xff0c;多線程是常見的需求&#xff0c;但UI更新必須在主線程上執行。今天&#xff0c;我們來探討一個簡單而優雅的解決方案&#xff1a;MainThread.InvokeOnMainThreadAsync。 一、背景 在跨平臺應用開發中&#xff0c;后臺線程常用于執行耗時操作&am…

海思正式公開了星閃BS21E的SDK

今天海思正式在Gitee平臺發布了BS21E的SDK&#xff1a;fbb_bs2x: fbb_bs2x代碼倉為支持bs21e解決方案SDK。技術論壇&#xff1a;https://developers.hisilicon.com/forum/0133146886267870001 fbb_bs2x代碼倉為支持bs21e解決方案SDK&#xff0c;該SDK包從統一開發平臺FBB&#…

QML學習:使用QML實現抽屜式側邊欄菜單

文章目錄 前言一、環境配置二、實現步驟三、示例完整代碼四、注意事項總結 前言 最近在進行QML的學習&#xff0c;發現一個比較有意思的交互設計&#xff1a;抽屜式側邊欄菜單&#xff0c;出于開發實戰需求&#xff0c;最終實現了一個支持手勢拖拽、彈性動畫、蒙層效果和??智…

峰終定律——AI與思維模型【85】

一、定義 峰終定律思維模型是指人們對一段經歷的評價主要取決于這段經歷中的高峰時刻&#xff08;無論是正向的還是負向的&#xff09;以及結束時的感受&#xff0c;而不是整個經歷的平均感受。也就是說&#xff0c;如果在一段體驗的高峰和結尾階段給人們留下積極、強烈的印象…

【補題】Codeforces Round 664 (Div. 1) A. Boboniu Chats with Du

題意&#xff1a;給出n&#xff0c;d&#xff0c;m三個值&#xff0c;分別代表&#xff0c;有多少個值ai&#xff0c;使用超過m的ai&#xff0c;需要禁言d天&#xff0c;如果不足也能使用&#xff0c;m代表區分點&#xff0c;問能得到最大的值有多少。 思路&#xff1a; …

單片機與上位機串口通信:原理、應用與實踐

注&#xff1a;本文為 “單片機與上位機串口通信” 相關文章合輯。 略作重排&#xff0c;未整理去重。 如有內容異常&#xff0c;請看原文。 單片機與上位機的串行通信 饕餮 tt 于 2019 - 12 - 06 14:47:19 發布 寫在前面 本文主要記錄單片機通過 TXD、RXD 與上位機進行數據…