OpenCV Python——圖像拼接(一)(圖像拼接原理、基礎知識、單應性矩陣 + 圖像變換 + 拼接)

  • 1 圖像拼接基礎知識
    • 1.1 特征匹配 原理及代碼示例
    • 1.2 單應性矩陣 原理及代碼示例
  • 2 圖像拼接(一)(直接拼接)
  • 3 圖像拼接(二)(單應性矩陣 + 圖像變換 + 拼接)
    • 3.1 單應性矩陣函數
    • 3.2 拼接函數 實現 及細節測試驗證
    • 3.3 圖像拼接
  • 4 后續完善(拼接縫隙過度、裁剪)
    • 4.1 輸入圖像大小一致 優化
    • 4.2 后續完善
  • 5 進階實戰--圖像拼接 (實戰項目)

P87 11

1 圖像拼接基礎知識

圖像關系,有重疊部分

原始圖像

在這里插入圖片描述

拼接結果

在這里插入圖片描述
在這里插入圖片描述

第二張圖像的左上角是原點(0,0),左邊和上邊的都是負值,不顯示
在這里插入圖片描述
在這里插入圖片描述

左邊的圖片變換后,超出尺寸的就不顯示了,
事實上顯示出來的那一部分,是與第二張圖重疊的部分

放大窗口

在這里插入圖片描述

從左上往右下拉
在這里插入圖片描述

之后將右邊的圖,平移過來

在這里插入圖片描述

1.1 特征匹配 原理及代碼示例

超詳細教程:特征點檢測與匹配(Harris角點檢測、Shi-Tomasi角點檢測、SIFT關鍵點檢測、SURF特征檢測、 ORB特征檢測、暴力特征匹配、FLANN特)

1.2 單應性矩陣 原理及代碼示例

教程:圖像查找(特征匹配 + 單應性矩陣)

2 圖像拼接(一)(直接拼接)

import cv2
import numpy as np #第一步,讀取文件,將圖片設置成一樣大小640*480
#第二步,找特征點,描述子,計算單應性矩陣
#第三部,根據單應性矩陣對圖像進行變換,然后平移
#第四部,拼接并輸出結果img1=cv2.imread('map1.png')
img2=cv2.imread('map2.png')#設置成一樣大小640*480
img1=cv2.resize(img1,(640,480))
img2=cv2.resize(img2,(640,480))#將兩圖橫向壓入棧中,即直接拼接
inputs=np.hstack((img1,img2))
cv2.imshow('input',inputs)cv2.waitKey(0)

可見,直接拼接只是簡單的把兩個圖像拼在一起,不可用
在這里插入圖片描述

3 圖像拼接(二)(單應性矩陣 + 圖像變換 + 拼接)

3.1 單應性矩陣函數

def get_homo(img1,img2):#1 創建特征轉換對象#2 通過特征轉換獲得特征點和描述子#3 創建特征匹配器#4 進行特征匹配#5 驗證過濾特征,找出有效的特征匹配點sift = cv2.xfeatures2d.SIFT_create()k1,d1=sift.detectAndCompute(img1,None)k2,d2=sift.detectAndCompute(img2,None)#創建特征匹配器bf=cv2.BFMatcher()matches=bf.knnMatch(d1,d2,k=2)verify_matches=[]verify_ratio = 0.8 #過濾器閾值 for m1,m2 in matches:if m1.destance < 0.8*m2.distance:verify_matches.append(m1)min_matches=8if len(verify_matches)>=min_matches:img1_pts=[] #img1特征坐標點img2_pts=[]for m in verify_matches:img1_pts.append(k1[m.queryIdx].pt)img2_pts.append(k2[m.trainIdx].pt)#img_pt數組格式 [(x1,y2),(x2,y2)....]#findHomography需要的數組坐標[[x1,y1],[x2,y2]...]img1_pts=np.float(img1_pts).reshape(-1,1,2)img2_pts=np.float(img2_pts).reshape(-1,1,2)H,mask=cv2.findHomography(img1_pts,img2_pts,cv2.RANSAC,5.0)return Helse :print('err: Not enough matches')exit()

3.2 拼接函數 實現 及細節測試驗證

#定義拼接函數
def stitch_image(img1,img2,H):#1 獲得每張圖片的四個角點#2 對圖片進行變換(單應性矩陣使圖進行旋轉,平移)#3 創建一張大圖,將兩張圖拼接到一起#4 輸出結果w1,h1=img1.shape[:2]#shape有3個值(高,寬,通道數),這里只取前兩個值w2,h2=img2.shape[:2]#獲取第一張圖的四個角點,OpenCV里圖像四個點順序通常喜歡逆時針#獲取的角點要變稱浮點型,數組不能是二維的,要變成能三維img1_dism=np.float32([[0,0],[0,h1-1],[w1-1,h1-1],[w1-1,0]]).reshape(-1,1,2)img2_dism=np.float32([[0,0],[0,h2-1],[w2-1,h2-1],[w2-1,0]]).reshape(-1,1,2)img1_transform=cv2.perspectiveTransform(img1_dism,H)print(img1_dism)print(img2_dism)print(img1_transform)

輸出img1,img2和變換后img1的四個角點

    print(img1_dism)print(img2_dism)print(img1_transform)

變換后img1_transform的四個角點,坐標有負值,原因超出了邊界,超出的部分不顯示;

在這里插入圖片描述
在這里插入圖片描述

    result_dism=np.concatenate((img2_dism,img1_transform),axis=0)aa=result_dism.min()#獲取最小值print(aa)

在這里插入圖片描述

只輸出了一個數,是所有數據的最小值;
要輸出最小的x值和最小y值,axis=0表示按x軸獲取數據

aa=result_dism.min(axis=0)#axis=0表示按x軸獲取數據

在這里插入圖片描述

ravel()將二維數組轉換成一維
可以看到雙括號,變成了單括號
在這里插入圖片描述
轉換為整形,

aa=np.int32(result_dism.min(axis=0).ravel())

在這里插入圖片描述

#最小值,向下取整-0.5,最大值向上取整,+0.5a=np.int32(result_dism.min(axis=0).ravel()-0.5)b=np.int32(result_dism.max(axis=0).ravel()+0.5)print(a)print(b)

在這里插入圖片描述

變換之后的圖,需要平移

#單應性矩陣變換,未平移
result_img=cv2.warpPerspective(img1,H,(max_x-min_x,max_y-min_y))#min是負值,減號,就相當于加

在這里插入圖片描述

#平移,即乘以一個齊次坐標
#[1,0,dx]
#[0,1,dy]
#[0,0,1 ]

transform_array=np.array([[1,0,transform_dist[0]],[0,1,transform_dist[1]],[0,0,1]])#單應性矩陣變換result_img=cv2.warpPerspective(img1,transform_array.dot(H),(max_x-min_x,max_y-min_y))#min是負值,減號,就相當于加return result_img

至此,圖像一的變換與平移完成
在這里插入圖片描述

3.3 圖像拼接

#找到合適的位置把圖二拼接過來result_img[transform_dist[1]:transform_dist[1]+h2,transform_dist[0]:transform_dist[0]+w2]=img2

到此處,拼過工作已經完后,證情況下運行出拼接結果。

但是結果卻出錯了,圖像的寬高顛倒了
在這里插入圖片描述
跳轉到45行,是最后寫的一個拼接位置,,好像也沒有什么毛病。
在這里插入圖片描述

反復的全文檢查了好幾遍,也沒發現問題。

最后發現這里,不一致;
在這里插入圖片描述

shape數據存儲順序為:高,寬,通道數;
而寫的是w,h,顛倒了,調換過來問題解決!

 #獲取原始圖像的高寬h1,w1=img1.shape[:2]#shape有3個值(高,寬,通道數),這里只取前兩個值h2,w2=img2.shape[:2]

運行結果;
在這里插入圖片描述

完整代碼:

import cv2
import numpy as np #根據單應性矩陣對圖像進行變換,及拼接
def stitch_image(img1,img2,H):#1 獲得每張圖片的四個角點#2 對第二張圖片進行變換(單應性矩陣使圖進行旋轉,平移)#3 創建一張大圖,將兩張圖拼接到一起#4 輸出結果#獲取原始圖像的高寬h1,w1=img1.shape[:2]#shape有3個值(高,寬,通道數),這里只取前兩個值h2,w2=img2.shape[:2]#獲取第一張圖的四個角點,OpenCV里圖像四個點順序通常喜歡逆時針#獲取的角點要變稱浮點型,數組不能是二維的,要變成能三維img1_dism=np.float32([[0,0],[0,h1],[w1,h1],[w1,0]]).reshape(-1,1,2)img2_dism=np.float32([[0,0],[0,h2],[w2,h2],[w2,0]]).reshape(-1,1,2)img1_transform=cv2.perspectiveTransform(img1_dism,H)#print(img1_dism)#print(img2_dism)#print(img1_transform)result_dism=np.concatenate((img2_dism,img1_transform),axis=0)print(result_dism)#axis=0表示按x軸獲取數據,獲得最小x值y值,ravel()將二維數組轉換成一維,轉換為整形,#最小值,向下取整-0.5,最大值向上取整,+0.5[min_x,min_y]=np.int32(result_dism.min(axis=0).ravel()-0.5)[max_x,max_y]=np.int32(result_dism.max(axis=0).ravel()+0.5)#圖像變換之后,部分數均已經超出顯示范圍,需要平移到大窗口中#平移的距離transform_dist = [-min_x,-min_y] #加負號,變成正值#平移,即乘以一個齊次坐標#[1,0,dx]#[0,1,dy]#[0,0,1 ]transform_array=np.array([[1,0,transform_dist[0]],[0,1,transform_dist[1]],[0,0,1]])#單應性矩陣變換#到此處圖像一的變換與平移完成result_img=cv2.warpPerspective(img1,transform_array.dot(H),(max_x-min_x,max_y-min_y))#min是負值,減號,就相當于加#到此處圖像一的變換與平移完成#找到合適的位置把圖二拼接過來result_img[transform_dist[1]:transform_dist[1]+h2,transform_dist[0]:transform_dist[0]+w2]=img2return result_img#定義單應性矩陣函數
def get_homo(img1,img2):#1 創建特征轉換對象#2 通過特征轉換獲得特征點和描述子#3 創建特征匹配器#4 進行特征匹配#5 驗證過濾特征,找出有效的特征匹配點sift = cv2.xfeatures2d.SIFT_create()k1,d1=sift.detectAndCompute(img1,None)k2,d2=sift.detectAndCompute(img2,None)#創建特征匹配器bf=cv2.BFMatcher()matches=bf.knnMatch(d1,d2,k=2)#過濾特征,找出有效的特征匹配點verify_matches=[]verify_ratio = 0.8 #過濾器閾值 for m1,m2 in matches:if m1.distance < 0.8 * m2.distance:verify_matches.append(m1)min_matches=8if len(verify_matches)>min_matches:img1_pts=[] #img1特征坐標點img2_pts=[]for m in verify_matches:img1_pts.append(k1[m.queryIdx].pt)img2_pts.append(k2[m.trainIdx].pt)#img_pt數組格式 [(x1,y2),(x2,y2)....]#findHomography需要的數組坐標[[x1,y1],[x2,y2]...]img1_pts=np.float32(img1_pts).reshape(-1,1,2)img2_pts=np.float32(img2_pts).reshape(-1,1,2)H,mask=cv2.findHomography(img1_pts,img2_pts,cv2.RANSAC,5.0)return Helse :print('err: Not enough matches')exit()#第一步,讀取文件,將圖片設置成一樣大小640*480
#第二步,找特征點,描述子,計算單應性矩陣
#第三部,根據單應性矩陣對圖像進行變換,然后平移
#第四部,拼接并輸出結果img1=cv2.imread('map1.png')
img2=cv2.imread('map2.png')#設置成一樣大小640*480
img1=cv2.resize(img1,(640,480))
img2=cv2.resize(img2,(640,480))#將兩圖橫向壓入棧中,即直接拼接
inputs=np.hstack((img1,img2))#獲得單應性矩陣
H=get_homo(img1,img2)#根據單應性矩陣對圖像進行變換,及拼接
result_image=stitch_image(img1,img2,H)cv2.imshow('input',result_image)
cv2.waitKey(0)

4 后續完善(拼接縫隙過度、裁剪)

4.1 輸入圖像大小一致 優化

上面的代碼示例,手動設置img1,和img的尺寸

#設置成一樣大小640*480
img1=cv2.resize(img1,(640,480))
img2=cv2.resize(img2,(640,480))

現在改為,自動確定尺寸。

判斷圖片尺寸是否一致,
如果 一樣大,不做resize;
如果不一樣大,就要resize,選擇兩幅圖中最小的寬高作為resize后的尺寸。

#判斷圖片尺寸是否一致,如果不一樣大,就要resize,這里選擇兩幅圖中最小的寬高
if (imageA.shape[0]==imageB.shape[0] and imageA.shape[1]==imageB.shape[1])!=1:h=min(imageA.shape[1],imageB.shape[1])w=min(imageA.shape[0],imageB.shape[0])imageA=cv2.resize(imageA,(h,w))#注意這里尺寸(高,寬),和平時的習慣寬高有點不一樣imageB=cv2.resize(imageB,(h,w))print('修改后尺寸:',imageA.shape,imageB.shape)#輸出調整后的尺寸

下圖輸出信息分別為:

兩張圖片原始尺寸;
resize后的尺寸;
拼接后的尺寸;
在這里插入圖片描述

4.2 后續完善

5 進階實戰–圖像拼接 (實戰項目)

上面的圖像拼接,重在展示基本原理,但存在拼接縫隙過度等一些問題。只用于實驗,不能滿足實際項目要求。

下面是,實戰項目,實現。

實戰項目:進階實戰–圖像拼接(二) (實戰一:圖像拼接 附完整代碼、實戰二:圖像拼接 附完整代碼)

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

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

相關文章

Git 中切換到指定 tag

在 Git 中切換到指定 tag&#xff08;比如 v1.22.1&#xff09;的正確做法如下&#xff1a;1?? 查看已有的 taggit tag會列出所有可用的版本&#xff0c;比如&#xff1a;v1.21.0 v1.22.0 v1.22.1 v1.23.02?? 切換到指定 taggit checkout tags/v1.22.1 -b v1.22.1解釋&…

rust 從入門到精通之變量和常量

變量和常量 隨著軟件系統安全的重要性與日俱增, rust這門集聚高并發, 安全, 適配云環境的編程語言在市場上得到了越來越高的認可和關注。但其復雜的機制使其難以學習。且其很多特性對于其他語言是全新的&#xff0c;這加劇了學習的困難程度。教程主要針對rust基礎進行講解, 雖然…

2508C++,支持rdma通信的高性能rpc庫

原文 [重磅]支持rdma通信的高性能的rpc庫–yalantinglibs.coro_rpc yalantinglibs的coro_rpc是基于C20的協程的高性能的rpc庫,提供了簡潔易用的接口,讓用戶幾行代碼就可實現rpc通信,現在coro_rpc除了支持tcp通信之外還支持了rdma通信(ibverbs). 通過簡單示例來感受一下rdma通…

FastAPI + React:現代 Web 前后端分離開發的全棧實踐指南

一、為什么選 FastAPI React&#xff1f; 性能&#xff1a;FastAPI 基于 Starlette Uvicorn&#xff0c;QPS 與 Node/Go 同級&#xff0c;實測 3 倍于 Flask&#xff1b;React 虛擬 DOM 代碼分割&#xff0c;首屏 < 1.2 s。效率&#xff1a;FastAPI 內置 Swagger/OpenAPI…

嵌入式硬件篇---電平轉換電路

電平轉換電路是電子電路中用來實現不同電壓信號之間轉換的關鍵電路&#xff0c;比如把 3.3V 的信號轉換成 5V&#xff0c;或者把 5V 轉換成 1.8V&#xff0c;確保不同電壓的芯片、模塊能正常通信。下面用通俗易懂的方式介紹幾種常見的電平轉換電路&#xff1a;一、電阻分壓電路…

SAP ABAP IS SUPPLIED

效果 此謂詞表達式用于檢查過程的某個形式參數“para”是否已賦值或被請求使用。如果在調用時實際參數被賦值給了該形式參數&#xff0c;則該表達式為真。 這種關系表達式僅能在函數模塊和方法中使用。而對于“para”而言&#xff0c;所有可選的形參都可以進行指定。 加上“NOT…

視頻內容提取與AI總結:提升學習效率的實用方法

文章目錄1、前言2、方法介紹2.1 B站視頻處理方案2.2 通用視頻處理方案2.3 AI內容總結3、實際效果4、使用建議5、技術發展趨勢6、總結&#x1f343; 作者介紹&#xff1a;25屆雙非本科網絡工程專業&#xff0c;阿里云專家博主&#xff0c;專注于 AI 原理、AI 應用開發、AI 產品設…

JVM 面試精選 20 題

目錄1. 什么是 JVM、JDK 和 JRE&#xff1f;它們之間的關系是什么&#xff1f;2. Java 內存區域&#xff08;運行時數據區&#xff09;有哪些&#xff1f;3. 說說你對 JVM 垃圾回收機制的理解。4. 常用的垃圾回收算法有哪些&#xff1f;5. 什么是 Minor GC、Major GC 和 Full G…

CMIP6 氣候模式核心特性解析

在全球氣候變化研究中&#xff0c;CMIP6&#xff08;第六次耦合模式比較計劃&#xff09;的氣候模式是關鍵工具。以下從研發背景與核心能力角度&#xff0c;解析五類主流模式的技術特點與適用場景。 一、主流模式技術特性 1. CanESM5/CanESM5-1&#xff08;加拿大環境與氣候變…

【牛客刷題】BM63 跳臺階:三種解法深度解析(遞歸/DP動態規劃/記憶化搜索)

文章目錄 一、題目介紹 1.1 題目描述 1.2 示例 二、算法設計思路 2.1 核心問題分析 2.2 斐波那契數列關系 三、流程圖 解法1:遞歸法(自頂向下) 解法2:動態規劃(自底向上) 解法3:記憶化搜索(遞歸優化) 解法4: 優化DP流程(推薦) 四、解法實現 五、復雜度分析對比 六、…

《解構WebSocket斷網重連:指數退避算法的前端工業級實踐指南》

WebSocket作為客戶端與服務器雙向通信的核心載體,支撐著從在線協作、金融行情到即時通訊等各類高實時性場景。然而,網絡環境的動態變化—從用戶設備的Wi-Fi與蜂窩網絡切換,到公共網絡的臨時擁塞,再到服務器的短暫重啟—都可能導致WebSocket連接中斷,進而引發數據傳輸停滯、…

醫療潔凈間的“隱形助手”:富唯智能復合機器人如何重塑手術器械供應鏈

當手術刀片在無影燈下傳遞時&#xff0c;0.01mm的抓取偏差可能意味著感染風險——而富唯智能復合機器人以0.02mm的重復定位精度與99.999%無菌操作的硬實力&#xff0c;正成為高端醫療產線中替代人力的關鍵技術支點。一、醫療上下料的三大痛點&#xff1a;精度、潔凈與連續性1.毫…

《設計模式》工廠方法模式

1.工廠方法模式&#xff08;Factory Method&#xff09;定義 定義一個用于創建對象的接口&#xff0c;讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。 1.1 UML圖&#xff1a; 主要有4個對象&#xff1a; 抽象工廠&#xff08;Abstract Creator&#xf…

冒泡排序——簡單理解和使用

閱前聲明&#xff1a;如果想直接了解冒泡排序的簡化思想&#xff0c;請跳至文章尾部在介紹之前&#xff0c;我們先看一個用到該功能的實戰訓練&#xff08;本人也是從中開始認識到冒泡排序這個函數定義&#xff09;對于小白來說&#xff0c;我的思路如下&#xff1a;1.題目中涉…

AI應用商業化加速落地 2025智能體爆發與端側創新成增長引擎

今年以來&#xff0c;人工智能 (AI) 正在進入從算力投入到云服務消耗再到商業化收入&#xff0c;最終回到算力再投入的良性循環&#xff0c;而 AI 應用的起量正是推動這一飛輪效應的關鍵。7 月 31 日&#xff0c;國務院常務會議審議通過了《關于深入實施 “人工智能 ” 行動的意…

Pytest測試框架基礎及進階

Pytest測試框架基礎# Pytest測試框架介紹# Pytest是Python一款三方測試框架&#xff0c;用于編寫和運行單元測試、集成測試和功能測試。Pytest測試框架具有簡單、靈活、易于擴展等特點&#xff0c;被廣泛應用于Python項目的測試工作中。 Pytest主要特點&#xff1a; 簡單易用…

航空裝備先進加工工藝與制造技術論壇——2025成都航空裝備展

300參展企業 11500㎡展區面積 7大專業展區 12000觀眾規模15同期會議 160發言嘉賓 5000參會嘉賓 100媒體報道航空工業飛速發展&#xff0c;先進加工工藝與制造技術成為了支撐航空裝備性能提升、質量保障和產能優化的核心基石。為探索前沿技術路徑、凝聚行業創新力量&#xff0c;…

為什么品牌更愿意為新品打廣告?

品牌資源向新品廣告傾斜&#xff0c;可以說是市場上的普遍現象。尤其對于沒有明星產品的品牌而言&#xff0c;新品推廣時企業的重要曝光節點。下面就讓我們一同來了解下&#xff0c;為什么品牌更愿意為新品打廣告。一、市場需求更充分新品廣告往往承擔著市場教育的功能&#xf…

電子電氣架構 --- 關于整車信息安全的一些思考

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 做到欲望極簡,了解自己的真實欲望,不受外在潮流的影響,不盲從,不跟風。把自己的精力全部用在自己。一是去掉多余,凡事找規律,基礎是誠信;二是…

報錯:Eplan無法打開數據庫的解決方法

詳細報錯信息&#xff1a;無法打開數據庫 E:\eplan\部件\Microsoft\ESS_part001.mdb。針對64位版本的EPLAN 平臺需要使用64位版本的Microsoft Office. 一、報錯及解決方法 報錯信息&#xff1a;無法打開數據庫 E:\eplan\部件\Microsoft\ESS_part001.mdb。針對64位版本的EPLAN 平…