使用Python和OpenCV實(shí)現(xiàn)圖片拼接的方法
一、圖像拼接的基本流程
圖像拼接主要包含以下幾個(gè)步驟:
- 讀取待拼接的圖片
- 檢測(cè)圖片的特征點(diǎn)并計(jì)算描述符
- 匹配兩張圖片的特征點(diǎn)
- 計(jì)算透 視變換矩陣
- 應(yīng)用變換并拼接圖片
二、代碼實(shí)現(xiàn)詳解
1. 準(zhǔn)備工作
首先導(dǎo)入必要的庫(kù)并定義輔助函數(shù):
import cv2 import numpy as np import sys def cv_show(name, img): """顯示圖像輔助函數(shù)""" cv2.imshow(name, img) cv2.waitKey(0)
2. 特征檢測(cè)與描述
我們使用SIFT(Scale-Invariant Feature Transform)算法來(lái)檢測(cè)圖像的特征點(diǎn)并計(jì)算描述符:
def detectAndDescribe(image): """檢測(cè)圖像特征點(diǎn)并計(jì)算描述符""" gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) descriptor = cv2.SIFT_create() # 檢測(cè)SIFT特征點(diǎn),并計(jì)算描述符 (kps, des) = descriptor.detectAndCompute(gray, None) # 將關(guān)鍵點(diǎn)坐標(biāo)轉(zhuǎn)換為numpy數(shù)組 kps_float = np.float32([kp.pt for kp in kps]) return (kps, kps_float, des)
SIFT算法具有尺度不變性,能夠在不同尺度下檢測(cè)到穩(wěn)定的特征點(diǎn),非常適合用于圖像拼接。
detectAndDescribe 函數(shù)詳解
這個(gè)函數(shù)是圖像拼接或特征匹配任務(wù)中的關(guān)鍵步驟,主要用于從輸入圖像中檢測(cè)關(guān)鍵點(diǎn) (SIFT特征點(diǎn)) 并計(jì)算它們的描述符。下面我將詳細(xì)解釋每一部分的含義和作用:
(1)函數(shù)功能
該函數(shù)接收一張彩色 圖像,然后:
- 將圖像轉(zhuǎn)換為灰度圖
- 使用SIFT算法檢測(cè)圖像中的關(guān)鍵點(diǎn)(特征點(diǎn))
- 為每個(gè)關(guān)鍵點(diǎn)計(jì)算描述符(一種數(shù)學(xué)表示)
- 將關(guān)鍵點(diǎn)坐標(biāo)轉(zhuǎn)換為NumPy數(shù)組格式
- 返回關(guān)鍵點(diǎn)對(duì)象、關(guān)鍵點(diǎn)坐標(biāo)和描述符
(2)代碼解析
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
- 將輸入的BGR格式彩色 圖像轉(zhuǎn)換為灰度圖像
- 大多數(shù)特征檢測(cè)算法都在灰度圖像上工作,因?yàn)轭伾畔?duì)特征檢測(cè)通常不是必需的
descriptor = cv2.SIFT_create()
- 創(chuàng)建一個(gè)SIFT(Scale-Invariant Feature Transform,尺度不變特征變換)檢測(cè)器對(duì)象
- SIFT是一種經(jīng)典的特征檢測(cè)算法,對(duì)圖像縮放、旋轉(zhuǎn)、亮度變化等具有不變性
(kps, des) = descriptor.detectAndCompute(gray, None)
- 同時(shí)檢測(cè)關(guān)鍵點(diǎn)并計(jì)算描述符
- detectAndCompute() 是OpenCV中高效的方法,一步完成檢測(cè)和計(jì)算
- 參數(shù):
- gray: 輸入的灰度圖像
- None: 可選的掩膜參數(shù),這里不使用
- 返回值:
- kps: 檢測(cè)到的關(guān)鍵點(diǎn)列表,每個(gè)關(guān)鍵點(diǎn)是一個(gè)包含多種屬性(坐標(biāo)、尺度、方向等)的對(duì)象
- des: 關(guān)鍵點(diǎn)描述符的NumPy數(shù)組,每個(gè)描述符是一個(gè)128維的向量
kps_float = np.float32([kp.pt for kp in kps])
- 將關(guān)鍵點(diǎn)的坐標(biāo)提取出來(lái)并轉(zhuǎn)換為NumPy數(shù)組
- kp.pt: 每個(gè)關(guān)鍵點(diǎn)的(x, y)坐標(biāo)屬性
- np.float32: 轉(zhuǎn)換為32位浮點(diǎn)數(shù)格式,這是許多OpenCV函數(shù)要求的輸入格式
return (kps, kps_float, des)
- 返回三個(gè)值:
kps: 原始的關(guān)鍵點(diǎn)對(duì)象列表(包含完整信息)
kps_float: 僅包含關(guān)鍵點(diǎn)坐標(biāo)的NumPy數(shù)組
des: 關(guān)鍵點(diǎn)描述符數(shù)組
(3)為什么需要這個(gè)函數(shù)?
在圖像拼接或匹配任務(wù)中,我們需要:
- 在兩幅圖像中找到相同的特征點(diǎn)(關(guān)鍵點(diǎn))
- 通過(guò)這些對(duì)應(yīng)點(diǎn)計(jì)算圖像間的變換關(guān)系
- detectAndDescribe函數(shù)封裝了第一步的關(guān)鍵操作,為后續(xù)的匹配和變換計(jì)算提供必要數(shù)據(jù)
(4)輸出數(shù)據(jù)的用途
- kps: 包含了關(guān)鍵點(diǎn)的完整信息,可用于可視化或進(jìn)一步分析
- kps_float: 簡(jiǎn)潔的坐標(biāo)表示,用于幾何變換計(jì)算
- des: 用于特征點(diǎn)匹配,通過(guò)比較描述符可以找到兩幅圖像中對(duì)應(yīng)的特征點(diǎn)
這個(gè)函數(shù)是許多計(jì)算機(jī)視覺(jué)任務(wù)(如圖像拼接、物體識(shí)別、3D重建等)的基礎(chǔ)步驟。
3. 讀取圖片并提取特征
# 讀取待拼接圖片 imageA = cv2.imread('imageA.jpg') imageB = cv2.imread('imageB.jpg') # 計(jì)算特征點(diǎn)和描述符 (kpsA, kps_floatA, desA) = detectAndDescribe(imageA) (kpsB, kps_floatB, desB) = detectAndDescribe(imageB)
- imageA 和 imageB 圖片如下:
4. 特征點(diǎn)匹配
使用暴力匹配器(BFMatcher)進(jìn)行特征點(diǎn)匹配:
# 建立暴力匹配器 matcher = cv2.BFMatcher() rawMatcher = matcher.knnMatch(desB, desA, 2) # 篩選優(yōu)質(zhì)匹配點(diǎn) good = [] matches = [] for m in rawMatcher: # 當(dāng)最近距離跟次近距離的比值小于0.65時(shí),保留此匹配對(duì) if len(m) == 2 and m[0].distance < 0.65 * m[1].distance: good.append(m) matches.append((m[0].queryIdx, m[0].trainIdx))
這里使用了Lowe’s ratio test來(lái)篩選優(yōu)質(zhì)匹配點(diǎn),比值閾值設(shè)為0.65,可以有效去除錯(cuò)誤的匹配。
5. 可視化匹配結(jié)果
# 繪制匹配結(jié)果 vis = cv2.drawMatchesKnn(imageB, kpsB, imageA, kpsA, good, None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) cv_show("keypoint matches", vis)
- 顯示效果如下:
6. 計(jì)算透 視變換矩陣
當(dāng)篩選后的匹配點(diǎn)對(duì)大于4個(gè)時(shí),可以計(jì)算透 視變換矩陣:
if len(matches) > 4: # 獲取匹配點(diǎn)的坐標(biāo) ptsB = np.float32([kps_floatB[i] for (i, _) in matches]) ptsA = np.float32([kps_floatA[i] for (_, i) in matches]) # 使用RANSAC算法計(jì)算單應(yīng)性矩陣 (H, mask) = cv2.findHomography(ptsB, ptsA, cv2.RANSAC, 10) else: print("圖片未找到4個(gè)以上的匹配點(diǎn)") sys.exit()
findHomography函數(shù)使用RANSAC算法來(lái)魯棒地估計(jì)變換矩陣,能夠有效處理異常值。
7. 應(yīng)用變換并拼接圖像
# 對(duì)imageB應(yīng)用透 視變換 result = cv2.warpPerspective(imageB, H, (imageB.shape[1] + imageA.shape[1], imageB.shape[0])) # 將imageA放置在結(jié)果圖像的左側(cè) result[0:imageA.shape[0], 0:imageA.shape[1]] = imageA cv_show('result', result)
- 最終拼接效果圖片如下所示:
三、技術(shù)要點(diǎn)解析
- SIFT特征:尺度不變特征變換,對(duì)旋轉(zhuǎn)、尺度縮放、亮度變化保持不變性
- 特征匹配:使用k近鄰算法進(jìn)行特征匹配,并通過(guò)比值測(cè)試篩選優(yōu)質(zhì)匹配
- RANSAC算法:隨機(jī)抽樣一致算法,用于魯棒地估計(jì)變換矩陣
- 透 視變換:通過(guò)單應(yīng)性矩陣將一張圖片的視角變換到另一張圖片的視角
四、改進(jìn)方向
- 使用更高效的特征檢測(cè)算法如ORB
- 添加圖像融合技術(shù)消除拼接縫
- 優(yōu)化拼接順序處理多張圖片
- 添加曝光補(bǔ)償處理不同亮度的圖片
總結(jié)
通過(guò)本文的介紹,相信讀者已經(jīng)對(duì)基于特征點(diǎn)的圖像拼接技術(shù)有了全面的了解。這種技術(shù)在計(jì)算機(jī)視覺(jué)領(lǐng)域有著廣泛的應(yīng)用,掌握它將為你的圖像處理項(xiàng)目帶來(lái)更多可能性。
以上就是使用Python和OpenCV實(shí)現(xiàn)圖片拼接的方法的詳細(xì)內(nèi)容,更多關(guān)于Python OpenCV圖像拼接的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python利用xlrd?與?xlwt?模塊操作?Excel
這篇文章主要介紹了Python利用xlrd?與?xlwt?模塊操作?Excel,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05python實(shí)現(xiàn)簡(jiǎn)單的TCP代理服務(wù)器
這篇文章主要介紹了python實(shí)現(xiàn)簡(jiǎn)單的TCP代理服務(wù)器,包含了完整的實(shí)現(xiàn)過(guò)程及對(duì)應(yīng)的源碼與說(shuō)明文檔下載,非常具有參考借鑒價(jià)值,需要的朋友可以參考下2014-10-10基于Python開(kāi)發(fā)chrome插件的方法分析
這篇文章主要介紹了基于Python開(kāi)發(fā)chrome插件的方法,結(jié)合實(shí)例形式分析了Python實(shí)現(xiàn)chrome瀏覽器插件相關(guān)操作技巧,需要的朋友可以參考下2018-07-07Request的中斷和ErrorHandler實(shí)例解析
這篇文章主要介紹了Request的中斷和ErrorHandler實(shí)例解析,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02Python?pyecharts?Boxplot箱線圖的實(shí)現(xiàn)
本文主要介紹了Python?pyecharts?Boxplot箱線圖的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05解決PySide+Python子線程更新UI線程的問(wèn)題
今天小編就為大家分享一篇解決PySide+Python子線程更新UI線程的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01Python實(shí)現(xiàn)TCP/IP協(xié)議下的端口轉(zhuǎn)發(fā)及重定向示例
這篇文章主要介紹了Python實(shí)現(xiàn)TCP/IP協(xié)議下的端口轉(zhuǎn)發(fā)及重定向示例,以一個(gè)webpy站點(diǎn)在本機(jī)的兩個(gè)端口雙向通信下演示,需要的朋友可以參考下2016-06-06