OpenCV進階操作:圖像的透視變換


文章目錄

  • 前言
  • 一、什么是透視變換?
  • 二、透視變換的過程
  • 三、OpenCV透視變換核心函數
  • 四、文檔掃描校正(代碼)
    • 1、預處理
    • 2、定義輪廓點的排序函數
    • 3、定義透視變換函數
    • 4、讀取原圖并縮放
    • 5、輪廓檢測
    • 6、繪制最大輪廓
    • 7、對最大輪廓進行透視變換
    • 8、旋轉、二值化處理
  • 總結


前言

在圖像處理中,透視變換(Perspective Transformation) 是一種強大的技術,能夠校正因視角傾斜導致的圖像變形。無論是掃描文檔的自動矯正、車牌識別,還是增強現實(AR)中的虛擬物體疊加,透視變換都扮演著重要角色。本文將通過OpenCV庫,手把手教你掌握透視變換的核心原理與代碼實現。


一、什么是透視變換?

透視變換是一種將圖像從任意視角投影到新視角的幾何變換。與僅能處理平移、旋轉和縮放的仿射變換不同,透視變換可以處理三維視角變化,徹底改變圖像的投影關系,實現“視角拉正”的效果。
在這里插入圖片描述
核心特點

  • 可將傾斜拍攝的圖像轉換為正視圖

  • 需要提供4組對應點(原圖坐標 + 目標坐標)

  • 通過變換矩陣實現非線性映射

二、透視變換的過程

對一張我們即將做透視變換圖像,首先要獲取到圖像中的4個坐標點,用于與目標圖像中的坐標對應,這四個點還是有順序的以坐標軸原點為參照點,距離原點最近的點為0號坐標,最遠的為2號坐標,這兩個點是最容易區分出來的;1號和3號位置可以通過坐標相減作為區分,距離X軸近的坐標的y值小于x值,所以按照x坐標減去y坐標得到的值1號坐標的值大于3號坐標的值。

區分0和2號坐標點:對四個點每個點坐標的x和y的值相加求和,我們發現,針對任意圖片輪廓,如果被四個點描繪,距離原點最近的點求和的值最小,在右下點的值求和的數值最大,可以區分出左上和右下兩個點

區分1和3號坐標點:對四個點每個點坐標的x和y的值相減(x-y),針對任意圖片輪廓,如果被四個點描繪,位于右上角做差的值為一個很大的正數,在左下點的值做差的數值為負數,可以區分出左下和右上兩個點。

在這里插入圖片描述
水平為x軸
垂直為y軸

三、OpenCV透視變換核心函數

cv2.getPerspectiveTransform()

  • 作用:根據4組對應點計算3x3透視變換矩陣。

  • 輸入參數:

  • src: 原圖4個點的坐標(格式:np.float32([[x1,y1], [x2,y2], …]))

  • dst: 目標圖像對應4個點的坐標

cv2.warpPerspective()

  • 作用:應用變換矩陣執行透視變換。

  • 參數:

  • src: 輸入圖像

  • M: 變換矩陣

  • dsize: 輸出圖像尺寸

四、文檔掃描校正(代碼)

目的:將傾斜文檔轉為正視圖

1、預處理

import numpy as np
import cv2
def cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)
# 調整圖像高寬,保持圖像寬高比不變
def resize(image,width=None,height=None ,inter=cv2.INTER_AREA):  # 輸入參數為圖像、可選寬度、可選高度、插值方式默認為cv2.INTER_AREA,即面積插值dim = None   # 存儲計算后的目標尺寸w、h(h,w) = image.shape[:2]  # 返回輸入圖像高寬if width is None and height is None:   # 判斷是否指定了寬和高大小,如果沒有指定則返回原圖return imageif width is None:   # 判斷如果沒有指定寬度大小,則表示指定了高度大小,那么運行內部代碼r = height/float(h)   # 指定高度與原圖高度的比值dim = (int(w*r),height)   # 寬度乘以比值得到新的寬度,此處得到新的寬高else:  # 此處表示為width不是None,即指定了寬度,與上述方法一致,計算比值r = width/float(w)dim = (width,int(h*r))resized = cv2.resize(image,dim,interpolation=inter)     # 指定圖像大小為上述的dim,inter默認為cV2.INTER_AREA,即面積插值,適用于縮放圖像。return resized

2、定義輪廓點的排序函數

def order_points(pts):   # 對輸入的四個點按照左上、右上、右下、左下進行排序rect = np.zeros((4,2),dtype='float32')   # 創建一個4*2的數組,用來存儲排序之后的坐標位置# 按順序找到對應坐標0123分別是左上、右上、右下、左下s = pts.sum(axis=1)   # 對pts矩陣的每個點的x y相加rect[0] = pts[np.argmin(s)]    # np.argmin(s)表示數組s中最小值的索引,表示左上的點的坐標rect[2] = pts[np.argmax(s)]    # 返回最大值索引,即右下角的點坐標diff = np.diff(pts,axis=1)   # 對pts矩陣的每一行的點求差值rect[1] = pts[np.argmin(diff)]   # 差值最小的點為右上角點rect[3] = pts[np.argmax(diff)]   # 差值最大表示左下角點return rect   # 返回排序好的四個點的坐標

3、定義透視變換函數

# 將透視扭曲的矩形變換成一個規則的矩陣
def four_point_transform(image,pts):# 獲取輸入坐標點rect = order_points(pts)  # 為上述排序的四個點(tl,tr,br,bl) = rect   # 分別返回給四個值,分別表示為左上、右上、右下、左下# 計算輸入的w和h值widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1]-bl[1]) ** 2))   # 計算四邊形底邊的寬度widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1]-tl[1]) ** 2))   # 計算頂邊的寬度maxWidth = max(int(widthA), int(widthB))   # 返回最大寬度heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))   # 計算左上角到右下角的對角線長度heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))   # 計算右上角到左下角的高的長度maxHeight = max(int(heightA),int(heightB))   # 返回最長的高度# 變換后對應坐標位置dst = np.array([[0,0],   # 定義四個點,表示變換后的矩陣的角點[maxWidth-1,0],[maxWidth-1,maxHeight-1],[0,maxHeight-1]],dtype='float32')M = cv2.getPerspectiveTransform(rect,dst)  # 根據原始點和變換后的點計算透視變換矩陣Mwarped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))  # 對原始圖像,針推變換矩陣和輸出圖像大小進行透視變換,返回變換后的圖片# 返回變換后的結果return warped

4、讀取原圖并縮放

# # 讀取輸入
image = cv2.imread('fapiao.jpg')   # 讀取原圖
cv_show('image',image)   # 展示原圖# 圖片過大,進行縮小處理
ratio = image.shape[0] / 500.0  # 計算縮小比率,[0]表示圖像的高
orig = image.copy()   # 對原圖復制生成副本
image = resize(orig, height=500)   # 更改圖像尺寸,輸入高度自動生成寬度
cv_show('1',image)   # 展示縮放后的圖片

在這里插入圖片描述

5、輪廓檢測

gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)  # 灰度圖edged = cv2.threshold(gray,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]  # 進行二值化,cv2.THRESH_OTSU自動尋找最優全局閾值,255表示高于最優閾值時將其更改為255
cnts = cv2.findContours(edged.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[1]  # 輪廓檢測
# cv2.RETR_LIST表示檢索所有輪廓,但是不建立層次關系
# cv2.CHAIN_APPROX_SIMPLE 表示只保存輪廓拐點的信息
# 總體返回處理的圖像、輪廓列表、層次結構,這里返回索引為1,表示返回輪廓列表image_contours = cv2.drawContours(image.copy(),cnts,-1,(0,0,255),1)  # 繪制所有輪廓
# 在原始圖像的副本上繪制了輪廓
# 繪制輪廓的位置為上述獲取的拐點信息,繪制線條顏色為紅色BRG(0,0,255),線條粗細為1個像素cv_show('image_contours',image_contours)  # 展示繪制好的圖片

在這里插入圖片描述

6、繪制最大輪廓

screenCnt = sorted(cnts,key = cv2.contourArea,reverse=True)[0]   # 對上述獲取的輪廓列表,排序依據是輪廓面積,reverse=True表示降序,[0]表示獲取面積最大的輪廓
peri = cv2.arcLength(screenCnt,True)   # 計算最大輪廓的周長
screenCnt = cv2.approxPolyDP(screenCnt,0.02*peri,True)  # 輪廓近似,近似為一個多邊形,表示新的輪廓與原來的輪廓最大距離不超過原始輪廓寬度的0.02倍,True表示輪廓為閉合的
image_contour = cv2.drawContours(image.copy(),[screenCnt],-1,(0,255,0),2)  # 繪制輪廓,將上述找到的輪廓繪制到原圖的副本上
cv2.imshow('image_contour',image_contour)
cv2.waitKey(0)

在這里插入圖片描述

7、對最大輪廓進行透視變換

warped = four_point_transform(orig,screenCnt.reshape(4,2)*ratio)  # 輸入參數原圖,將最大輪廓圖形狀改變為4*2的格式,即四個點,然后乘以上述定義的比率來縮放輪廓
cv2.imwrite('invoice_new.jpg',warped)   # 將經過透視變換處理的圖片存入本地
cv2.namedWindow('xx',cv2.WINDOW_NORMAL)  # 設置一個窗口,名稱為xx,這個窗口大小用戶可通過拖動隨意調節大小
cv2.imshow('xx',warped)  # 展示經過透視變換處理的圖片
cv2.waitKey(0)

在這里插入圖片描述

8、旋轉、二值化處理

# 二值處理
warped = cv2.cvtColor(warped,cv2.COLOR_BGR2GRAY)   # 導入新的圖片的灰度圖
ref = cv2.threshold(warped,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]  # 對灰度圖進行二值化處理kernel = np.ones((2,2),np.uint8)   # 設置一個單位矩陣,大小為2*2,表示設置核kernel的大小
ref_new = cv2.morphologyEx(ref,cv2.MORPH_CLOSE,kernel)   # 閉運算,先膨脹再腐蝕
ref_new = resize(ref_new.copy(),width=500)   # 對閉運算處理完的圖像重置大小
cv_show('yy',ref_new)
rotated_image = cv2.rotate(ref_new,cv2.ROTATE_90_COUNTERCLOCKWISE)  # 對圖像逆時針旋轉90度
cv2.imshow('result',rotated_image)
cv2.waitKey(0)

在這里插入圖片描述


總結

通過OpenCV的透視變換,我們能夠輕松解決因拍攝角度導致的圖像形變問題。無論是手動標定點還是結合自動檢測算法,這一技術都為復雜場景下的圖像處理提供了基礎支持。

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

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

相關文章

【python】基礎知識點100問

以下是Python基礎語法知識的30條要點整理,涵蓋數據類型、函數、控制結構等核心內容,結合最新資料歸納總結: 基礎30問 一、函數特性 函數多返回值 支持用逗號分隔返回多個值,自動打包為元組,接收時可解包到多個變量 def func(): return 1, "a" x, y = func()匿…

采用AI神經網絡降噪算法的通信語音降噪(ENC)模組性能測試和應用

采用AI降噪的語言通話環境抑制模組性能效果測試 隨著AI時代來臨.通話設備的環境噪音抑制也進入AI降噪算法時代. AI神經網絡降噪技術是一款革命性的語音處理技術,他突破了傳統單麥克風和雙麥克風降噪的局限性,利用采集的各種日常環境中的噪音樣本進行訓練學習.讓降噪…

openwrt目錄結構(部分)

1,openwrt 原始目錄需要注意的目錄 tools: 該目錄下存放著一些,編譯工程的自動化工具包和一些在編譯過程用到的命令包, 查看目錄下的Makefile,知道其會在編譯過程中將依賴包下載 例如: autoconf / lzma / mkimage/ …

RDB和AOF的區別

Redis提供兩種主要的持久化機制:RDB(Redis Database)和AOF(Append Only File),它們在數據持久化方式、性能影響及恢復策略上各有特點。以下是兩者的對比分析及使用建議: RDB(快照持久…

基于大模型的甲狀腺結節診療全流程預測與方案研究報告

目錄 一、引言 1.1 研究背景與目的 1.2 研究意義 1.3 國內外研究現狀 二、大模型預測原理與方法 2.1 相關大模型概述 2.2 數據收集與預處理 2.3 模型訓練與驗證 三、術前預測與評估 3.1 結節性質預測 3.1.1 良惡性判斷 3.1.2 與傳統診斷方法對比 3.2 手術風險預測…

逆向破解:x64dbg

文章目錄 一、CPU窗口1、反匯編窗口2、寄存器窗口3、棧地址窗口4、十六進制數據窗口5、堆棧參數解析窗口 二、常用快捷鍵三、字符串檢索功能四、調試功能1、上一步 一、CPU窗口 1、反匯編窗口 2、寄存器窗口 寄存器窗口用于顯示和解釋當前線程環境下CPU寄存器的各種狀態值和內…

免布線視頻樁如何重塑停車管理模式

傳統停車管理常因布線復雜、維護成本高而難以推廣,而“免布線視頻樁”通過無線設計、低功耗與高精度檢測,為城市停車提供高效解決方案。作為智慧城市建設的創新工具,免布線視頻樁以即裝即用、長效續航等特性,正在重塑停車管理模式…

【CTFer成長之路】舉足輕重的信息搜集

舉足輕重的信息搜集 信息搜集 常見的搜集 題目描述: 一共3部分flag docker-compose.yml version: 3.2services:web:image: registry.cn-hangzhou.aliyuncs.com/n1book/web-information-backk:latestports:- 80:80啟動方式 docker-compose up -d 題目Flag n1book{info_…

springboot3+vue3融合項目實戰-大事件文章管理系統-更新用戶密碼

大致分為這三步 首先在usercontroller中增加updatePwd方法 PatchMapping ("/updatePwd")public Result updatePwd(RequestBody Map<String,String> params){//1.校驗參數String oldPwd params.get("old_pwd");String newPwd params.get("n…

OpenCV進階操作:指紋驗證、識別

文章目錄 前言一、指紋驗證1、什么是指紋驗證2、流程步驟 二、使用步驟&#xff08;案例&#xff09;三、指紋識別&#xff08;案例&#xff09;1、這是我們要識別的指紋庫2、這是待識別的指紋圖3、代碼4、結果 總結 前言 指紋識別作為生物識別領域的核心技術之一&#xff0c;…

ECLIC中斷流程及實際應用 —— RISC-V中斷機制(二)

在長期的嵌入式開發實踐中&#xff0c;對中斷機制的理解始終停留在表面層次&#xff0c;特別當開發者長期局限于純軟件抽象層面時&#xff0c;對中斷機制的理解極易陷入"知其然而不知其所以然"的困境&#xff0c;這種認知的局限更為明顯&#xff1b;隨著工作需要不斷…

計算機網絡-LDP標簽發布與管理

前面學習了LDP建立鄰居&#xff0c;建立會話&#xff0c;今天來學習在MPLS中的標簽發布與管理。 在MPLS網絡中&#xff0c;下游LSR決定標簽和FEC的綁定關系&#xff0c;并將這種綁定關系發布給上游LSR。LDP通過發送標簽請求和標簽映射消息&#xff0c;在LDP對等體之間通告FEC和…

Go語言運算符詳解

文章目錄 1. 算術運算符2. 關系運算符3. 邏輯運算符4. 位運算符5. 賦值運算符6. 其他運算符運算符優先級注意事項 Go語言提供了與其他語言類似的運算符&#xff0c;包括算術運算符、關系運算符、邏輯運算符、位運算符、賦值運算符等。這些運算符即可滿足基本的運算需求。 1. 算…

Selenium模擬人類行為,操作網頁的方法(全)

看到有朋友評論問&#xff0c;用selenium怎么模仿人類行為&#xff0c;去操作網頁的頁面呢&#xff1f; 我想了想&#xff0c;這確實是一個很大的點&#xff0c;不應該是一段代碼能解決的&#xff0c; 就像是,如果讓程序模擬人類的行為。例如模擬人類買菜&#xff0c;做飯&am…

RabbitMQ的工作隊列模式和路由模式有什么區別?

RabbitMQ 的工作隊列模式&#xff08;Work Queues&#xff09;和路由模式&#xff08;Routing&#xff09;是兩種不同的消息傳遞模式&#xff0c;主要區別在于消息的分發邏輯和使用場景。以下是它們的核心差異&#xff1a; 1. 工作隊列模式&#xff08;Work Queues&#xff09…

牛客練習賽138(首篇萬字題解???)

賽時成績如下&#xff1a; 1. 小s的簽到題 小s拿到了一個比賽榜單&#xff0c;他要用最快的速度找到簽到題&#xff0c;但是小s腦子還是有點暈&#xff0c;請你幫幫小s&#xff0c;助力他找到簽到題。 比賽榜單是一個 2 行 n 列的表格&#xff1a; 第一行是 n 個大寫字母&#…

linux0.11內核源碼修仙傳第十六章——獲取硬盤信息及根目錄掛載

&#x1f680; 前言 書接第十四章&#xff1a;linux0.11內核源碼修仙傳第十四章——進程調度之fork函數&#xff0c;在這一節博客中已經通過fork進程創建了一個新的進程1&#xff0c;并且可以被調度&#xff0c;接下來接著主線繼續走下去。希望各位給個三連&#xff0c;拜托啦&…

mobile自動化測試-appium webdriverio

WebdriverIO是一款支持mobile app和mobile web自動化測試框架&#xff0c;與appium集成&#xff0c;完成對mobile應用測試。支持ios 和android兩種平臺&#xff0c;且功能豐富&#xff0c;是mobile app自動化測試首選框架。且官方還提供了mobile 應用測試example代碼&#xff0…

Kubernetes排錯(十):常見網絡故障排查

通用排查思路 Kubernetes 集群內不同服務之間的網絡通信出現異常&#xff0c;表現為請求超時、連接失敗或響應緩慢&#xff0c;導致服務間依賴關系中斷&#xff0c;依賴服務的功能不可用或性能下降&#xff0c;甚至可能波及整個微服務架構&#xff0c;引發連鎖反應&#xff0c…

PyTorch 張量與自動微分操作

筆記 1 張量索引操作 import torch ? # 下標從左到右從0開始(0->第一個值), 從右到左從-1開始 # data[行下標, 列下標] # data[0軸下標, 1軸下標, 2軸下標] ? def dm01():# 創建張量torch.manual_seed(0)data torch.randint(low0, high10, size(4, 5))print(data->,…