計算機視覺----opencv實戰----指紋識別的案例

一、數據準備

src2.BMP

src1.BMP

src.bmp

model.BMP

二、識別原理講解(sift特征提取)

SIFT(Scale-Invariant Feature Transform,尺度不變特征變換)是一種經典的圖像特征提取算法,核心優勢是不受圖像尺度縮放、旋轉、光照變化的影響,能穩定提取圖像中的關鍵特征點,廣泛用于圖像匹配、目標檢測、圖像拼接等場景。以下從核心原理、OpenCV 實現步驟、關鍵特性三方面展開介紹,全程不涉及公式。

核心原理(4 個關鍵步驟)

SIFT 的本質是通過 “模擬人眼對不同尺度物體的感知”,找到圖像中 “無論放大 / 縮小、旋轉都不變” 的特征點,并為每個特征點生成唯一的 “特征描述符”(用于后續匹配)。整個過程可拆解為 4 步:

1. 尺度空間極值檢測:找 “不受尺度影響” 的候選特征點

人眼觀察物體時,近距離看細節、遠距離看整體 ——SIFT 通過 “高斯模糊 + 圖像縮放” 構建 “尺度空間”,模擬這種感知過程:

  • 對原始圖像做不同程度的高斯模糊(模糊程度逐漸增加),再對模糊后的圖像做下采樣(縮小尺寸),得到一系列 “不同尺度” 的圖像集合(稱為 “高斯金字塔”)。
  • 在相鄰尺度的圖像間做差值(得到 “差分高斯金字塔”),然后在每個像素點的 “上下左右相鄰像素 + 相鄰尺度對應位置像素” 中比較,找到 “局部極值點”—— 這些點就是 “在不同尺度下都突出” 的候選特征點(比如小尺度下的角點、大尺度下的輪廓頂點)。
2. 特征點精確定位:剔除 “不穩定” 的候選點

第一步找到的候選點中,可能包含因噪聲、邊緣干擾產生的 “假特征點”,需要進一步篩選:

  • 對每個候選極值點,分析其周圍像素的灰度變化,判斷該點是否是 “真正的特征點”(比如邊緣上的點會被剔除,因為邊緣在垂直方向的灰度變化不顯著,穩定性差)。
  • 最終保留 “灰度變化顯著、在尺度上穩定” 的點,作為最終的 SIFT 特征點。
3. 特征點方向賦值:實現 “旋轉不變性”

為了讓特征點不受圖像旋轉影響,需要給每個特征點分配一個 “主方向”:

  • 以特征點為中心,取一個小區域(比如半徑 16 像素的圓),統計該區域內所有像素的 “梯度方向”(即像素灰度變化的方向,比如從暗到亮的方向)和 “梯度大小”(灰度變化的強度)。
  • 用 “直方圖” 統計這些梯度方向的分布,找到出現次數最多的方向(主方向),將該方向作為特征點的 “基準方向”—— 后續生成描述符時,會以這個主方向為參考,從而抵消旋轉的影響。
4. 生成特征描述符:讓特征 “可匹配”

每個特征點需要一個 “唯一標識”(描述符),用于和其他圖像中的特征點對比匹配:

  • 以特征點為中心,取一個 16×16 的像素塊(按主方向對齊,避免旋轉干擾),將這個塊分成 4×4 的 16 個小格子(每個小格子 4×4 像素)。
  • 對每個小格子,統計其中像素的梯度方向分布(用 8 個方向的直方圖表示),得到 8 個數值。
  • 16 個小格子共生成 16×8=128 個數值,將這 128 個數值組成一個向量,就是該特征點的 “128 維 SIFT 描述符”。
  • 最后會對描述符做 “歸一化” 處理(比如消除光照變化的影響:讓描述符向量的長度為 1),確保其在不同光照下仍能穩定匹配

三、對比檢測指紋(簡單)

這里我們需要用到(src1.BMP,src2.BMP,model.BMP)三張圖片,在(src1.BMP,src2.BMP)找出和model.BMP匹配的圖片

代碼示例:

1. 導入依賴庫
import cv2

導入 OpenCV 庫,它提供了強大的計算機視覺處理功能,包括 SIFT 特征提取和 FLANN 匹配器。

2. 核心認證函數?verification

該函數接收三個參數:

  • src:待驗證的源圖像
  • model:作為標準的模型圖像
  • threshold:判斷認證通過的匹配點數量閾值,默認值為 500

函數執行流程:

(1)初始化 SIFT 特征提取器
sift = cv2.SIFT_create()

SIFT(尺度不變特征變換)是一種對尺度、旋轉、光照變化都具有穩健性的特征提取算法,非常適合用于圖像匹配。

(2)提取圖像特征點和描述符
kp1, des1 = sift.detectAndCompute(src, None)
kp2, des2 = sift.detectAndCompute(model, None)
  • kp1/kp2:分別是源圖像和模型圖像的特征點(KeyPoint)集合
  • des1/des2:分別是對應特征點的描述符(Descriptor),是特征點的數字表示
(3)特征點檢查
if des1 is None or des2 is None:return "認證失敗"  # 無特征點

如果任何一幅圖像無法提取到特征點,直接返回認證失敗。

(4)FLANN 特征匹配
flann = cv2.FlannBasedMatcher()
matches = flann.knnMatch(des1, des2, k=2)
  • 使用 FLANN(快速最近鄰搜索庫)匹配器進行特征匹配,比暴力匹配更高效
  • knnMatch?方法返回每個特征點的前 k 個最近鄰匹配(這里 k=2)
(5)篩選優質匹配
good_matches = [m for m, n in matches if m.distance < 0.8 * n.distance]

應用 Lowe's 比率測試篩選優質匹配:如果最佳匹配距離小于次佳匹配距離的 80%,則認為是一個好的匹配點,這能有效剔除誤匹配。

(6)返回認證結果
return "認證通過" if len(good_matches) >= threshold else "認證失敗"

如果優質匹配點數量達到或超過閾值,則認證通過,否則失敗。

3. 主程序

if __name__ == "__main__":src1 = cv2.imread("src1.BMP")src2 = cv2.imread("src2.BMP")model = cv2.imread("model.BMP")

讀取待驗證圖像(src1.BMP、src2.BMP)和模型圖像(model.BMP)。

if src1 is None or src2 is None or model is None:print("? 圖像讀取失敗,請檢查文件路徑")

檢查圖像是否成功讀取,如果有任何圖像讀取失敗,提示檢查文件路徑。

else:print("src1驗證結果:", verification(src1, model))print("src2驗證結果:", verification(src2, model))

如果所有圖像都成功讀取,則分別對 src1 和 src2 進行認證,并打印結果。

完整代碼:

import cv2def verification(src, model, threshold=500):"""使用SIFT + FLANN匹配,返回認證結果"""sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(src, None)kp2, des2 = sift.detectAndCompute(model, None)if des1 is None or des2 is None:return "認證失敗"  # 無特征點flann = cv2.FlannBasedMatcher()matches = flann.knnMatch(des1, des2, k=2)good_matches = [m for m, n in matches if m.distance < 0.8 * n.distance]return "認證通過" if len(good_matches) >= threshold else "認證失敗"# ========== 主程序 ==========
if __name__ == "__main__":src1 = cv2.imread("src1.BMP")src2 = cv2.imread("src2.BMP")model = cv2.imread("model.BMP")if src1 is None or src2 is None or model is None:print("? 圖像讀取失敗,請檢查文件路徑")else:print("src1驗證結果:", verification(src1, model))print("src2驗證結果:", verification(src2, model))

運行結果:

四、進階任務(多圖片匹配)

現在需要對(src.bmp)在指紋庫里進行匹配并繪制出匹配上的點(指紋庫:database,已經上傳,可以自行下載)

代碼詳解:

1. 導入依賴庫
import os          # 用于文件和目錄操作
import cv2         # OpenCV庫,用于圖像處理和特征提取
import numpy as np # 用于數值計算和數組操作
2. 核心函數:獲取匹配點
def get_good_matches(src, model):# 讀取源圖像和模板圖像img1 = cv2.imread(src)img2 = cv2.imread(model)if img1 is None or img2 is None:return [], [], []# 創建SIFT特征提取器并計算特征點和描述符sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(img1, None)  # kp: 關鍵點, des: 描述符kp2, des2 = sift.detectAndCompute(img2, None)if des1 is None or des2 is None:return kp1 or [], kp2 or [], []# 使用FLANN匹配器進行特征匹配flann = cv2.FlannBasedMatcher()matches = flann.knnMatch(des1, des2, k=2)  # k=2表示返回兩個最佳匹配# 應用Lowe's比例測試篩選良好的匹配點good = []for m, n in matches:# 如果最佳匹配距離小于次佳匹配的80%,則認為是好的匹配if m.distance < 0.8 * n.distance:good.append(m)return kp1, kp2, good
3. 計算匹配點個數
def getNum(src, model):_, _, good = get_good_matches(src, model)return len(good)

這個函數簡化了匹配點獲取過程,只返回良好匹配點的數量,用于比較不同模板的匹配程度。

4. 獲取指紋編號
def getID(src, database):max_num = 0best_name = "0.bmp"  # 默認值,防止未匹配時報錯# 遍歷數據庫中的所有文件for file in os.listdir(database):model = os.path.join(database, file)num = getNum(src, model)print(f"文件名:{file},匹配點個數:{num}")# 記錄匹配點最多的文件if num > max_num:max_num = numbest_name = file# 如果匹配點數量大于等于100,則認為匹配有效ID = int(best_name[0]) if max_num >= 100 else 9999return ID

該函數通過比較輸入圖像與數據庫中所有圖像的匹配點數量,找到最相似的圖像,并返回其對應的 ID。

5. 根據 ID 獲取姓名
def getName(ID):nameID = {0: '張三', 1: '李四', 2: '王五', 3: '趙六', 4: '朱老七',5: '錢八', 6: '曹九', 7: '王二麻子', 8: 'andy', 9: 'Anna',9999: "沒找到"}return nameID.get(int(ID), "未知")

這是一個簡單的 ID 與姓名映射表,根據識別出的 ID 返回對應的姓名。

6. 繪制匹配點
def drawMatchesWithCircles(src, model, show=True):img1 = cv2.imread(src)img2 = cv2.imread(model)kp1, kp2, good_matches = get_good_matches(src, model)# 在兩張圖像上繪制匹配點(紅色實心圓)for match in good_matches:pt1 = tuple(map(int, kp1[match.queryIdx].pt))  # 源圖像上的匹配點pt2 = tuple(map(int, kp2[match.trainIdx].pt))  # 模板圖像上的匹配點cv2.circle(img1, pt1, 3, (0, 0, 255), -1)  # -1表示填充圓cv2.circle(img2, pt2, 3, (0, 0, 255), -1)# 拼接兩張圖像以便對比顯示h1, w1 = img1.shape[:2]h2, w2 = img2.shape[:2]h = max(h1, h2)  # 取最大高度w = w1 + w2      # 寬度相加combined = np.zeros((h, w, 3), dtype=np.uint8)combined[:h1, :w1] = img1combined[:h2, w1:w1+w2] = img2# 顯示結果if show:cv2.imshow("Matches with Circles", combined)cv2.waitKey(0)cv2.destroyAllWindows()return len(good_matches)
7. 主程序
if __name__ == "__main__":src = "src.bmp"       # 待識別的源圖像database = "database" # 模板數據庫目錄# 執行識別流程ID = getID(src, database)name = getName(ID)print("識別結果是:", name)# 繪制最佳匹配的匹配點if ID != 9999:# 找到最佳匹配的模板文件best_model = os.path.join(database, [f for f in os.listdir(database) if f.startswith(str(ID))][0])print(f"\n正在繪制 {best_model} 與 {src} 的匹配點...")drawMatchesWithCircles(src, best_model)else:print("未找到有效匹配,不進行繪制。")
工作流程總結
  1. 讀取待識別圖像 (src.bmp) 和數據庫中的所有模板圖像
  2. 對每對圖像使用 SIFT 算法提取特征點并進行匹配
  3. 統計匹配點數量,找到匹配度最高的模板
  4. 根據模板的 ID 查找對應的姓名并輸出
  5. 可視化顯示最佳匹配的特征點對應關系
完整代碼:
import os
import cv2
import numpy as np############## 獲取匹配點(核心函數) #####################
def get_good_matches(src, model):img1 = cv2.imread(src)img2 = cv2.imread(model)if img1 is None or img2 is None:return [], [], []sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(img1, None)kp2, des2 = sift.detectAndCompute(img2, None)if des1 is None or des2 is None:return kp1 or [], kp2 or [], []flann = cv2.FlannBasedMatcher()matches = flann.knnMatch(des1, des2, k=2)good = []for m, n in matches:if m.distance < 0.8 * n.distance:good.append(m)return kp1, kp2, good############## 計算匹配個數 #####################
def getNum(src, model):_, _, good = get_good_matches(src, model)return len(good)############# 獲取指紋編號 ################
def getID(src, database):max_num = 0best_name = "0.bmp"  # 默認值,防止未匹配時報錯for file in os.listdir(database):model = os.path.join(database, file)num = getNum(src, model)print(f"文件名:{file},匹配點個數:{num}")if num > max_num:max_num = numbest_name = fileID = int(best_name[0]) if max_num >= 100 else 9999return ID############# 獲取姓名 ################
def getName(ID):nameID = {0: '張三', 1: '李四', 2: '王五', 3: '趙六', 4: '朱老七',5: '錢八', 6: '曹九', 7: '王二麻子', 8: 'andy', 9: 'Anna',9999: "沒找到"}return nameID.get(int(ID), "未知")############## 繪制匹配點(實心小圓圈) #####################
def drawMatchesWithCircles(src, model, show=True):img1 = cv2.imread(src)img2 = cv2.imread(model)kp1, kp2, good_matches = get_good_matches(src, model)# 繪制匹配點for match in good_matches:pt1 = tuple(map(int, kp1[match.queryIdx].pt))pt2 = tuple(map(int, kp2[match.trainIdx].pt))cv2.circle(img1, pt1, 3, (0, 0, 255), -1)cv2.circle(img2, pt2, 3, (0, 0, 255), -1)# 拼接顯示h1, w1 = img1.shape[:2]h2, w2 = img2.shape[:2]h = max(h1, h2)w = w1 + w2combined = np.zeros((h, w, 3), dtype=np.uint8)combined[:h1, :w1] = img1combined[:h2, w1:w1+w2] = img2if show:cv2.imshow("Matches with Circles", combined)cv2.waitKey(0)cv2.destroyAllWindows()return len(good_matches)############# 主程序 ################
if __name__ == "__main__":src = "src.bmp"database = "database"ID = getID(src, database)name = getName(ID)print("識別結果是:", name)# 繪制最佳匹配if ID != 9999:best_model = os.path.join(database, [f for f in os.listdir(database) if f.startswith(str(ID))][0])print(f"\n正在繪制 {best_model} 與 {src} 的匹配點...")drawMatchesWithCircles(src, best_model)else:print("未找到有效匹配,不進行繪制。")詳細介紹代碼
運行結果:

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

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

相關文章

npm 發布流程——從創建組件到發布到 npm 倉庫

1. 準備組件 1.1 創建一個 Vue 組件 假設我們要創建一個簡單的按鈕組件&#xff1a; src/MyButton.vue <template><button class"my-btn" click"$emit(click)"><slot /></button> </template><script setup lang"ts…

MySQL入門基礎指南

目錄 一、什么是數據庫&#xff1f; 僅依靠文件存儲數據存在以下幾個明顯缺點&#xff1a; 數據庫的存儲介質通常包括&#xff1a; 二、主流數據庫介紹 三、客戶端 VS 服務器 四、推薦看的MySQL安裝技術博客 五、數據庫的存儲介質 數據庫的存儲介質主要分為以下兩類&am…

【實戰中提升自己完結篇】分支篇之分支之無線、內網安全與QOS部署(完結)

1 1拓撲 「模擬器、工具合集」復制整段內容 鏈接&#xff1a;https://docs.qq.com/sheet/DV0xxTmFDRFVoY1dQ?tab7ulgil1 分支無線部署 說明&#xff1a;分支無線用瘦AP部署&#xff0c;通過VPN直接注冊到總部的AC上面&#xff0c;實現無線的業務提供&…

帶你了解STM32:GPIO通用輸入輸出口

目錄 3.1 GPIO簡介 3.2 GPIO基本結構 3.3 GPIO位結構 輸入部分&#xff1a; 二極管的保護作用&#xff1a; 施密特觸發器&#xff1a; 片上外設端口 輸出部分&#xff1a; MOS管 3.4 GPIO模式 3.4.1 浮空/上拉/下拉輸入 3.4.2 模擬輸入 3.4.3 開漏/推挽輸出 3.4.…

Http(自寫)

作為一個程序員&#xff0c;假設我們要在a電腦的進程里發一段數據到b電腦&#xff0c;一般使用socket編程&#xff0c;可選項也就tcp&#xff0c;udp二選一socket本質上就是一個代碼庫tcp有粘包問題&#xff08;字節流&#xff09;&#xff0c;純裸tcp不能之際拿來使用所以我們…

C#使用OpenVinoSharp和PP-Human進行行人檢測

效果 項目依賴 OpenCvSharp 4.11.0.20250507 OpenVINO.CSharp.Windows 2024.0.0.1 主要代碼 using OpenCvSharp; using OpenVinoSharp; using System; using System.Windows.Forms;namespace HelloPPHuman {public partial class Form1 : Form{public Form1(){InitializeCo…

四、Scala深入面向對象:類、對象與伴生關系

在前幾節中&#xff0c;我們學習了 Scala 的基礎語法和流程控制。現在&#xff0c;我們將深入探索 Scala 作為一門純粹的面向對象語言的核心。在 Scala 中&#xff0c;萬物皆對象&#xff0c;沒有像 Java 那樣的原始類型和靜態成員的區分。本節將重點介紹如何定義對象的藍圖&am…

【大語言模型 58】分布式文件系統:訓練數據高效存儲

分布式文件系統&#xff1a;訓練數據高效存儲 關鍵詞&#xff1a;分布式文件系統、HDFS、Lustre、GlusterFS、數據本地性、I/O優化、存儲架構、大數據存儲、訓練數據管理、存儲性能調優 摘要&#xff1a;本文深入探討大語言模型訓練中的分布式文件系統技術&#xff0c;從存儲架…

【科研繪圖系列】R語言繪制散點圖以及線性回歸擬合曲線圖

禁止商業或二改轉載,僅供自學使用,侵權必究,如需截取部分內容請后臺聯系作者! 文章目錄 介紹 加載R包 數據下載 函數 導入數據 數據預處理 畫圖 總結 系統信息 介紹 數據導入 代碼的開始部分涉及多個數據集的導入,這些數據集涵蓋了不同類型的生態學數據,包括實驗室培養…

SQL 數據庫操作語言詳解

1. SQL 語言概述SQL&#xff08;Structured Query Language&#xff09;是用于管理關系型數據庫的標準語言&#xff0c;主要分為以下幾個子語言&#xff1a;- DQL&#xff08;數據查詢語言&#xff09;&#xff1a;SELECT - 用于數據查詢 - DML&#xff08;數據操作語言&#x…

積分變換的前世今生

積分變換常應用于解微分方程微分方程的解法&#xff1a;時域經典法&#xff1b;頻域變換法&#xff1b;“積分變換”最初并不是為了解微分方程&#xff0c;而是出于更“純粹”的數學動機——理解函數的結構、求解代數或幾何問題&#xff0c;以及簡化復雜的積分運算。微分方程的…

《Linux——gflags》

一、什么是gflags&#xff1f; gflags 是一個由 Google 開發的命令行參數解析庫&#xff0c;主要用于在 C&#xff08;也支持其他語言&#xff09;程序中便捷地處理命令行參數。它的核心作用是幫助開發者快速定義、解析和使用命令行選項&#xff0c;避免手動編寫繁瑣的參數解析…

編譯器的前端中端和后端

前面說的詞法分析和語法分析&#xff0c;確實是編譯器前端 (Front End) 最核心的兩個部分。但前端的工作還沒有結束。編譯器各階段劃分 一個完整的編譯器通常可以分為三個部分&#xff1a;前端、中端 (Middle End)、后端 (Back End)。 前端 (Front End) 核心職責: 理解源代碼。…

黑馬Java進階教程,全面剖析Java多線程編程,并發和并行,筆記02

黑馬Java進階教程&#xff0c;全面剖析Java多線程編程&#xff0c;并發和并行&#xff0c;筆記02 一、并發和并行 并發&#xff1a;在同一時刻&#xff0c;有多個指令在單個CPU上交替執行 并行&#xff1a;在同一時刻&#xff0c;有多個指令在多個CPU上同時執行 二、為什么有…

20250908 背包DP總結

引子 ~ 我們都有一個家&#xff0c;名字叫背包 ~ 背包DP 顧名思義&#xff0c;背包DP是用來解決背包最值問題的。題目會給出背包的容量&#xff0c;以及幾個物品的屬性&#xff0c;比如重量&#xff0c;價值&#xff0c;限額等等&#xff0c;具體是什么看題目。 01背包 01…

Redis持久化之RDB:快照機制原理、配置與最佳實踐

Redis持久化之RDB&#xff1a;快照機制原理、配置與最佳實踐 1. RDB持久化概述 1.1 什么是RDB RDB&#xff08;Redis Database&#xff09;是Redis的默認持久化方式&#xff0c;它在指定的時間間隔內生成數據集的快照&#xff08;snapshot&#xff09;&#xff0c;并將快照保…

daily notes[44]

文章目錄基礎references基礎 hello,world是幾乎所有編程語言的第一例子&#xff0c;rust也不例外。但和其它語言不一樣&#xff0c;Rust的源碼最好擁有自己的項目目錄。 $ mkdir ~/pro $ cd ~/pro $ mkdir helloWorld $ cd helloWorld源代碼文件名為main.rs&#xff0c;內容如…

JavaScript對象創建方式完全指南:從原始到現代的演進之路

前言 作為一名前端開發者&#xff0c;JavaScript中對象創建是很重要。在JavaScript這門基于原型的語言中&#xff0c;對象幾乎無處不在。今天&#xff0c;我將帶領大家回顧JavaScript對象創建的7種方式&#xff0c;從最原始的字面量到現代的ES6 class&#xff0c;每一步演進都解…

基于單片機的無線水塔監控系統設計(論文+源碼)

本設計為基于單片機的無線水塔監控系統設計&#xff0c;主要由以下幾部分組成&#xff1a;均采用STC89C52RC單片機為主控&#xff1b;主機&#xff1a;NRF24L01無線通訊模塊&#xff0c;1602LCD液晶顯示屏。從機&#xff1a;NRF24L01無線通訊模塊&#xff0c;水位傳感器&#x…

凌晨0-3點不睡,你熬的不是夜,是人生!

“熬夜”這個詞&#xff0c;早已成為現代生活的常態。有人為了工作加班到深夜&#xff0c;有人為了娛樂刷劇到天明&#xff0c;但你知道嗎&#xff1f;熬夜最“要命”的時間段&#xff0c;其實是凌晨0點到凌晨3點。別以為只是少睡幾個小時而已&#xff0c;這個時間段不睡&#…