圖片拼接-動手學計算機視覺8

前言

圖片拼接(image stitching)就是將統一場景的不同拍攝出的圖片拼接到一起,如圖所示

就是拼接全景圖,是圖片拼接的應用之一,手機拍照都有全景拍攝功能

仔細觀察全景圖,尋找它們相似性,圖8-2的全景圖可以通過縮放,旋轉,射影等操作進行拼接而成,我們首先介紹幾個常用的圖像變換

圖像變換

平移變換

平移變換通過向量 ( \mathbf{t} = (t_x, t_y) ) 實現,圖像上點 ( \mathbf{p} = (i, j) ) 平移后得到新點 ( \mathbf{p}' = (i', j') ),滿足: [ \mathbf{p}' = \mathbf{p} + \mathbf{t} ] 其中 ( t_x ) 和 ( t_y ) 分別表示水平和垂直方向的平移距離。

旋轉變換

旋轉變換繞原點逆時針旋轉角度 ( \theta ),點 ( \mathbf{p} = (i, j) ) 旋轉后得到 ( \mathbf{p}' = R\mathbf{p} ),旋轉矩陣 ( R ) 為: [ R = \begin{bmatrix} \cos \theta & -\sin \theta \ \sin \theta & \cos \theta \end{bmatrix} ]

縮放變換

以原點為中心,沿 ( x ) 軸縮放 ( s_x ) 倍,沿 ( y ) 軸縮放 ( s_y ) 倍,點 ( \mathbf{p} = (i, j) ) 縮放后得到 ( \mathbf{p}' = S\mathbf{p} ),縮放矩陣 ( S ) 為: [ S = \begin{bmatrix} s_x & 0 \ 0 & s_y \end{bmatrix} ]

對稱變換

  • 關于 ( y ) 軸對稱:點 ( \mathbf{p} = (i, j) ) 變換后為 ( \mathbf{p}' = (-i, j) ),對應矩陣: [ P_y = \begin{bmatrix} -1 & 0 \ 0 & 1 \end{bmatrix} ]
  • 關于直線 ( y = x ) 對稱:點 ( \mathbf{p} = (i, j) ) 變換后為 ( \mathbf{p}' = (j, i) ),對應矩陣: [ P_{y=x} = \begin{bmatrix} 0 & 1 \ 1 & 0 \end{bmatrix} ]

射影變換(透視變換)

射影變換是更一般的線性變換,可用齊次坐標表示。對于點 ( \mathbf{p} = (i, j, 1) )(齊次坐標),變換后 ( \mathbf{p}' = H\mathbf{p} ),其中 ( H ) 為 ( 3 \times 3 ) 變換矩陣: [ H = \begin{bmatrix} h_{11} & h_{12} & h_{13} \ h_{21} & h_{22} & h_{23} \ h_{31} & h_{32} & h_{33} \end{bmatrix} ] 射影變換能實現傾斜、透視等復雜幾何變換。

幾何相似性分析

圖8-1的子圖與圖8-2全景圖的相似性體現在:

  1. 局部與全局關系:子圖通過上述變換(平移、旋轉、縮放、射影)可拼接為全景圖。
  2. 幾何一致性:變換后的子圖邊緣對齊、視角連貫,滿足幾何約束(如特征點匹配)。
  3. 變換組合:實際拼接中常組合多種變換,例如先旋轉后平移,或射影校正透視差異。

數學表達統一性

所有變換均可表示為矩陣乘法(齊次坐標下): [ \mathbf{p}' = M\mathbf{p} ] 其中 ( M ) 為對應變換矩陣。平移需擴展為仿射變換: [ M_{\text{平移}} = \begin{bmatrix} 1 & 0 & t_x \ 0 & 1 & t_y \ 0 & 0 & 1 \end{bmatrix} ]

計算變化矩陣

1.通過SIFT計算出兩幅圖片的特征點

2.將兩幅圖片的特征點進行匹配

3.更具匹配的特征點計算圖片變換矩陣

利用RANSAC算法去除誤匹配


當利用SIFT進行特征匹配時,有些時候可能會出現圖8-6的情況。圖8-6中右圖綠色圓圈
內的特征點是與左圖匹配的特征點,但利用SIFT匹配特征點時,會將左圖中部分特征點匹配到
右圖綠色圓圈之外的特征點(如紅色圓圈內的特征點)。這些特征點匹配是錯誤的匹配,應該被
移除,從而保證變換矩陣計算的魯棒性。應該如何移除錯誤的匹配點對呢?

可以用到RANSAC算法

RANSAC算法簡介

RANSAC(Random Sample Consensus)是一種魯棒的模型擬合算法,常用于處理包含大量噪聲或異常值的數據。在計算機視覺中,RANSAC常用于去除特征匹配中的誤匹配(outliers),僅保留滿足幾何約束的正確匹配(inliers)。

算法原理

RANSAC通過隨機采樣最小數據集迭代估計模型參數,并統計支持該模型的樣本數量。算法核心思想是:正確的匹配應滿足某種幾何變換(如單應性矩陣或基礎矩陣),而誤匹配則不符合該約束。

實現步驟

輸入準備

  • 兩組匹配的特征點對:points1points2(形狀為N×2的數組)
  • 模型類型:單應性矩陣(Homography)或基礎矩陣(Fundamental Matrix)
  • 最大迭代次數:max_iterations(默認1000)
  • 內點閾值:threshold(像素距離,默認3.0)

核心流程

  1. 隨機從匹配點對中選取最小樣本集(如單應性矩陣需4對點)
  2. 根據樣本集計算候選模型參數(如調用cv2.findHomography
  3. 統計所有點在該模型下的投影誤差小于閾值的內點數量
  4. 保留內點數量最多的模型參數
  5. 重復上述過程直到達到最大迭代次數

OpenCV代碼實現

import cv2
import numpy as npdef ransac_filter_matches(points1, points2, model='homography', max_iter=1000, threshold=3.0):"""points1, points2: 匹配的點坐標 (N×2 numpy數組)model: 擬合模型類型 ('homography' 或 'fundamental')"""if len(points1) < 4:return np.arange(len(points1))  # 不足4對點時返回所有索引if model == 'homography':H, mask = cv2.findHomography(points1, points2, cv2.RANSAC, threshold, maxIters=max_iter)elif model == 'fundamental':F, mask = cv2.findFundamentalMat(points1, points2, cv2.FM_RANSAC, threshold, max_iter)return mask.ravel().astype(bool)  # 返回內點掩碼

參數選擇建議

  • 閾值選擇:通常設置為1-5像素,取決于特征點定位精度。對于SIFT/SURF等特征可設為3,ORB等二進制特征建議設為5

  • 迭代次數:默認1000次可滿足大多數場景。可通過公式估算:

    $$ N = \frac{\log(1-p)}{\log(1-(1-\epsilon)^s)} $$

    其中p為置信度(如0.99),ε為異常值比例估計值,s為最小樣本數

應用示例

# 假設已有匹配結果
matches = flann.knnMatch(des1, des2, k=2)
good_matches = [m for m,n in matches if m.distance < 0.7*n.distance]# 提取匹配點坐標
pts1 = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1,2)
pts2 = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1,2)# RANSAC過濾
inlier_mask = ransac_filter_matches(pts1, pts2)
final_matches = [good_matches[i] for i in range(len(good_matches)) if inlier_mask[i]]

注意事項

  • 匹配點對數量較少時(<10),RANSAC可能失效
  • 場景中存在多個運動平面時,需改用多模型擬合方法(如PEARL)
  • 對于純旋轉相機運動,建議使用單應性矩陣;一般運動建議用基礎矩陣

圖像變換與縫合

圖像拼接的最后一步是將輸入圖像變換并縫合到一幅圖像中。對于兩幅圖像A和B,在已
經檢測出對應的特征點對,并利用RANSAC算法計算得到變換矩陣T之后,將圖像B轉換為
TB。然后,對轉換后的圖像,即TB,與圖像A在重疊部分的像素值求平均值,以優化圖像縫
合的邊界。如此,便可得到最終縫合好的拼接圖像。
綜上所述,我們把圖像拼接的全過程總結為以下4步:
(1)計算兩幅圖像的特征點;
(2)將兩幅圖像的特征點進行匹配;
(3)根據匹配的特征點對,利用RANSAC算法計算圖像變換矩陣;
(4)將圖像進行拼接。

代碼實現

方法一:使用OpenCV內置的Stitcher類(最簡單)

import cv2# 讀取圖像
image1 = cv2.imread('image1.jpeg')
image2 = cv2.imread('image2.jpeg')# 檢查圖像是否成功讀取
if image1 is None or image2 is None:print("無法讀取圖像文件")exit()# 創建拼接器 效果:拼接結果出現了邊緣黑邊和形變
stitcher = cv2.Stitcher_create() if hasattr(cv2, 'Stitcher_create') else cv2.createStitcher()# 執行拼接
(status, stitched) = stitcher.stitch([image1, image2])# 保存結果
if status == cv2.Stitcher_OK:cv2.imwrite('stitched_output.jpg', stitched)print("拼接成功,結果已保存為 'stitched_output.jpg'")
else:print(f'拼接失敗,錯誤代碼: {status}')

方法二:完整實現

import cv2
import numpy as npdef stitch_images(images, ratio=0.75, reproj_thresh=4.0, show_matches=False):"""圖像拼接函數參數:images: 要拼接的圖像列表ratio: Lowe's ratio test參數reproj_thresh: RANSAC重投影閾值show_matches: 是否顯示特征匹配結果返回:拼接后的圖像"""# 初始化OpenCV的SIFT特征檢測器sift = cv2.SIFT_create()# 檢測關鍵點和描述符(kpsA, featuresA) = sift.detectAndCompute(images[0], None)(kpsB, featuresB) = sift.detectAndCompute(images[1], None)# 匹配特征點matcher = cv2.DescriptorMatcher_create("BruteForce")raw_matches = matcher.knnMatch(featuresA, featuresB, 2)# 應用Lowe's ratio test篩選好的匹配點good_matches = []for m in raw_matches:if len(m) == 2 and m[0].distance < m[1].distance * ratio:good_matches.append((m[0].trainIdx, m[0].queryIdx))# 至少需要4個匹配點才能計算單應性矩陣if len(good_matches) > 4:ptsA = np.float32([kpsA[i].pt for (_, i) in good_matches])ptsB = np.float32([kpsB[i].pt for (i, _) in good_matches])# 計算單應性矩陣(H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, reproj_thresh)# 拼接圖像result = cv2.warpPerspective(images[0], H, (images[0].shape[1] + images[1].shape[1], images[0].shape[0]))result[0:images[1].shape[0], 0:images[1].shape[1]] = images[1]# 如果需要顯示匹配結果if show_matches:vis = np.zeros((max(images[0].shape[0], images[1].shape[0]), images[0].shape[1] + images[1].shape[1], 3), dtype=np.uint8)vis[0:images[0].shape[0], 0:images[0].shape[1]] = images[0]vis[0:images[1].shape[0], images[0].shape[1]:] = images[1]for ((trainIdx, queryIdx), s) in zip(good_matches, status):if s == 1:ptA = (int(kpsA[queryIdx].pt[0]), int(kpsA[queryIdx].pt[1]))ptB = (int(kpsB[trainIdx].pt[0]) + images[0].shape[1], int(kpsB[trainIdx].pt[1]))cv2.line(vis, ptA, ptB, (0, 255, 0), 1)cv2.imshow("Feature Matches", vis)cv2.waitKey(0)cv2.destroyAllWindows()return resultreturn None# 示例用法
if __name__ == "__main__":# 讀取兩張要拼接的圖像image1 = cv2.imread("image1.jpeg")image2 = cv2.imread("image2.jpeg")# 確保圖像讀取成功if image1 is None or image2 is None:print("無法讀取圖像文件")exit()# 調整圖像大小(可選)image1 = cv2.resize(image1, (0, 0), fx=0.5, fy=0.5)image2 = cv2.resize(image2, (0, 0), fx=0.5, fy=0.5)# 拼接圖像stitched_image = stitch_images([image1, image2], show_matches=True)if stitched_image is not None:# 顯示并保存結果cv2.imshow("Stitched Image", stitched_image)cv2.waitKey(0)cv2.destroyAllWindows()cv2.imwrite("stitched_result.jpg", stitched_image)else:print("圖像拼接失敗,可能匹配點不足")

使用建議

  • 如果只是需要快速拼接,推薦使用第一種方法(Stitcher類)
  • 如果需要了解基本原理或進行簡單定制,可以使用第二種方法
  • 確保圖像有足夠重疊區域(建議30%以上重疊)
  • 圖像大小不宜過大,可以先縮小處理

兩種方法都需要安裝OpenCV:

pip install opencv-python opencv-contrib-python

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

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

相關文章

Web第二次作業

作業一&#xff1a;學校官網1.1學校官網代碼如下&#xff1a;?<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">&l…

【CV 目標檢測】②R-CNN模型

二、R-CNN網絡基礎 2.R-CNN模型 2014年提出R-CNN&#xff01;網絡&#xff0c;該網絡不再使用暴力窮舉的方法&#xff0c;而是使用候選區域方法&#xff08;region proposal method&#xff09;創建目標檢測的區域來完成目標檢測的任務&#xff0c;R-CNN是以深度神經網絡為基礎…

STM32L051C8與STM32L151C8的主要區別

STM32L051C8與STM32L151C8 有什么區別&#xff1f; LPTIM 有什么特點,為什么STM32L151C8沒有LPTIM,而STM32L051C8有1個? 1. STM32L051C8與STM32L151C8的主要區別 STM32L051C8STM32L151C8內核Cortex-M0Cortex-M3主頻32MHz32MHz閃存/ SRAM64KB/8KB64KB/16KB工作電壓1.65V-3.6V…

【軟考中級網絡工程師】知識點之網關協議深度剖析

目錄一、網關協議基礎探秘1.1 網關協議概念1.2 網關協議作用1.3 網關協議分類總覽二、內部網關協議&#xff08;IGP&#xff09;深度解析2.1 距離矢量協議2.2 鏈路狀態協議2.3 混合型協議三、外部網關協議&#xff08;EGP&#xff09;探秘3.1 BGP 協議詳解3.2 BGP 協議的關鍵特…

JavaScript 中 call、apply 和 bind 方法的區別與使用

一、核心作用與基礎概念這三個方法都用于顯式改變函數執行時的 this 指向&#xff0c;解決 JavaScript 中函數上下文動態綁定的問題。1.call()立即執行函數&#xff0c;第一個參數為 this 指向對象&#xff0c;后續參數為逗號分隔的參數列表語法&#xff1a;func.call(thisArg,…

【Android】適配器與外部事件的交互

三三要成為安卓糕手 引入&#xff1a;在上一篇文章中我們完成了新聞展示頁面多布局案例的展示&#xff0c;感悟頗多&#xff0c;本篇文章&#xff0c;繼續去開發一些新的功能 一&#xff1a;關閉廣告 所有的view都可以和我們的用戶做交互&#xff0c;循環視圖中也給我們提供了相…

MySQL的分析查詢語句(EXPLAIN):

目錄 基本語法&#xff1a; 各個字段的含義&#xff1a; id&#xff1a; select_type&#xff1a; table&#xff1a; partitions&#xff1a; type&#xff1a; possible_keys&#xff1a; key&#xff1a; key_len&#xff1a; ref&#xff1a; row&#xff1a; …

C++ #if

在 C 中&#xff0c;#if 是 預處理器指令&#xff08;Preprocessor Directive&#xff09;&#xff0c;用于 條件編譯&#xff0c;即在編譯階段根據條件決定是否包含某段代碼。它通常與 #define、#ifdef、#ifndef、#else 和 #endif 配合使用。基本語法#if 條件表達式// 如果條件…

方案 | 動車底部零部件檢測實時流水線檢測算法改進

項目背景隨著我國高速鐵路運營里程突破4.5萬公里&#xff0c;動車組日均開行超過8000列次&#xff0c;傳統人工巡檢方式已無法滿足密集運行下的安全檢測需求。車底關鍵部件如制動系統、懸掛裝置、牽引電機等長期承受高強度振動和沖擊&#xff0c;易產生疲勞裂紋、螺栓松動、部件…

企業收款統計:驅動業務決策的核心引擎設計開發——仙盟創夢IDE

代碼完整代碼<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>黑金風格職員統計</title><style>/* 頁面基礎樣式 - 黑金風格 */body {font-family: Segoe UI, Tahoma, Geneva, Verdana, …

CIAIE 2025上海汽車內外飾展觀察:從美學到功能的產業躍遷

在智能化、電動化浪潮推動下&#xff0c;汽車產業的市場格局、技術路線、供應鏈結構與用戶體驗正被系統性重塑。汽車感知空間核心的“內外飾件”&#xff0c;正從原本的結構性、功能性部件&#xff0c;逐步躍升為智能化、情感化和差異化體驗的重要承載載體&#xff0c;開啟了從…

Spring IOC容器在Web環境中的啟動奧秘:深入源碼解析

一、為何需要關注IOC容器啟動&#xff1f;在Java Web開發中&#xff0c;Spring MVC框架的基石正是IOC容器。但你是否思考過&#xff1a;獨立的IOC模塊如何與Tomcat等Servlet容器協同工作&#xff1f; 其啟動過程與Web容器的生命周期深度綁定&#xff0c;這是構建穩定Spring應用…

前端JS處理時間,適用于聊天、操作記錄等(包含剛剛、x分鐘前、x小時前、x天前)

export default {// 首頁時間轉化formatDate(val) {var nowDate new Date()var oldDate new Date(val)const Y oldDate.getFullYear()const M oldDate.getMonth() 1const D oldDate.getDate()var diff nowDate.getTime() - oldDate.getTime()var minutes Math.floor(di…

C#---StopWatch類

老方法&#xff0c;想要全面了解和學習一個類必先看文檔 微軟文檔 1.StopWatch 提供一組方法和屬性&#xff0c;可用來測量運行時間。 1.1 屬性和方法 屬性&#xff1a; 方法&#xff1a; 1.2 使用 using System.Diagnostics;namespace Study04_反射專題 {internal cla…

3DTiles轉OSGB格式逆向轉換方法研究

一、概述 在傾斜攝影的應用領域中&#xff0c;3DTiles與OSGB格式的互轉是常見的技術需求。作為專業的GIS處理平臺&#xff0c;GISBox憑借其先進的傾斜攝影反切功能&#xff0c;為用戶提供了高效、穩定的跨格式數據轉換解決方案。 二、3DTiles轉OSGB的意義 保留原始幾何與紋理…

【門診進銷存出入庫管理系統】佳易王醫療器械零售進銷存軟件:門診進銷存怎么操作?系統實操教程 #醫藥系統進銷存

前言&#xff1a; &#xff08;一&#xff09;試用版獲取方式 資源下載路徑&#xff1a;進入博主頭像主頁第一篇文章末尾&#xff0c;點擊卡片按鈕&#xff1b;或訪問左上角博客主頁&#xff0c;通過右側按鈕獲取詳細資料。 說明&#xff1a;下載文件為壓縮包&#xff0c;使用…

華為交換機配置文件的相關命令和用法

文章目錄一、基本配置命令一、基本配置命令 1、查看當前運行的配置文件 <Huawei>display current-configuration2、配置文件保存 <Huawei>save <Huawei>save vrpcfg-20250623.zip #保存為指定文件名3、查看保存的配置 <Huawei>display saved-configu…

【汽車標定數據】動態優先級線程池在異步多文件解析中的應用

目錄 一、需求背景 項目背景&#xff1a;電控數據管理系統優化 優化方案&#xff1a;引入OLAP數據庫和動態線程池 線程池性能急需解決的問題 資源過載與閑置的平衡&#xff1a; 優先級處理與公平性&#xff1a; 任務類型適配性&#xff1a; 二、線程池介紹 2.1、線程池…

Unity人形角色IK優化指南

目錄 Unity中人形角色的IKI 站立、奔跑IK 1. 接觸面法線 2. 調整質心位置 3. 保持原本朝向 攀爬IK 1. 四肢貼合 2. 保持身體與攀爬面的距離 3. 適應外拐角 瞄準IK 1. 頭部朝向 2. 手臂朝向 尾聲 本文將嘗試僅使用Untiy內置的Animator來解決常見的幾種運動所需的IK…

基于wireshark的USB 全速硬件抓包工具USB Sniffer Lite的使用

1、前言 隨著MCU的發展和需求的增多&#xff0c;USB已成為主流MCU的標配外設&#xff0c;但很多還是全速或低速IP&#xff0c;因此往往用不上高速抓包設備。 2、安裝wireshark和拷貝抓包插件 將抓包插件拷貝到wireshark的extcap目錄里&#xff0c;可參考基于wireshark的USB …