透视变换原理实例代码详解

article/2025/10/3 21:36:06

导读

在上篇文章中,我们介绍了仿射变换,我们只需要通过一个两行三列的变换矩阵M就能够对图像实现平移缩放翻转旋转操作。我们发现这些变换其实都属于平面变换,如果我们想要进行空间变换呢?
在这里插入图片描述
将上图的扑克牌单独提取出来,如下图所示
在这里插入图片描述
这时候我们应该如何来实现这个功能呢?这个其实就涉及到了图像的一个空间变换,就需要用到我们所说的透视变换了。

透视变换

透视变换(Perspective Transformation)是指利用透视中心像点目标点三点共线的条件,按透视旋转定律使承影面(透视面)绕迹线(透视轴)旋转某一角度,破坏原有的投影光线束,仍能保持承影面上投影几何图形不变的变换。简而言之,就是将一个平面通过一个投影矩阵投影到指定平面上
在这里插入图片描述

  • 原理解析
    透视变换通用的变换公式:
    [ x ′ y ′ ω ′ ] = [ u v ω ] [ a 11 a 12 a 13 a 21 a 22 a 23 a 31 a 32 a 33 ] \left[ \begin{matrix} x' &y'&\omega' \\ \end{matrix} \right] = \left[ \begin{matrix} u &v&\omega \\ \end{matrix} \right] \left[ \begin{matrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \\ \end{matrix} \right] [xyω]=[uvω]a11a21a31a12a22a32a13a23a33
    u u u v v v是原始图片,参数 ω \omega ω等于1,通过透视变换后得到的图片坐标 x x x, y y y,其中
    x = x ′ ω ′ y = y ′ ω ′ x = \frac{x'}{\omega'}\\ y = \frac{y'}{\omega'} x=ωxy=ωy
    上式中的变换矩阵,可以将其拆成四个部分,第一部分表示线性变换
    [ a 11 a 12 a 21 a 22 ] \left[ \begin{matrix} a_{11} & a_{12}\\ a_{21} & a_{22}\\ \end{matrix} \right] [a11a21a12a22]
    这部分矩阵主要用于图像的缩放旋转操作,在仿射变换中我们也介绍过。第二部分用来进行平移操作 [ a 31 a 32 ] \left[\begin{matrix}a_{31} & a_{32}\end{matrix}\right] [a31a32],第三部分用来产生透视变换 [ a 13 a 23 ] \left[\begin{matrix} a_{13} & a_{23}\end{matrix}\right] [a13a23],第四部分参数 a 33 a_{33} a33等于1。
    在上篇文章中我们介绍的仿射变换矩阵一共有6个参数,所以我们只需要3个坐标对(6个方程)就能求解,而透视变换矩阵一共有8个参数,所以需要4个坐标对(8个方程)才能求解,其实仿射变换也是透视变换的一种特殊形式。
    所以变换后 x x x y y y的表达式为
    x = x ′ ω ′ = a 11 ∗ u + a 21 ∗ v + a 31 ∗ 1 a 13 ∗ u + a 23 ∗ v + 1 ∗ 1 y = y ′ ω ′ = a 12 ∗ u + a 22 ∗ v + a 32 ∗ 1 a 13 ∗ u + a 23 ∗ v + 1 ∗ 1 x=\frac{x'}{\omega'}=\frac{a_{11}*u+a_{21}*v+a_{31}*1}{a_{13}*u+a_{23}*v+1*1}\\ y=\frac{y'}{\omega'}=\frac{a_{12}*u+a_{22}*v+a_{32}*1}{a_{13}*u+a_{23}*v+1*1} x=ωx=a13u+a23v+11a11u+a21v+a311y=ωy=a13u+a23v+11a12u+a22v+a321
    接下来我们看一个例子,原始图像的四个点的坐标分别为 ( 0 , 0 ) 、 ( 1 , 0 ) 、 ( 1 , 1 ) 、 ( 0 , 1 ) (0,0)、(1,0)、(1,1)、(0,1) (0,0)(1,0)(1,1)(0,1)与之对应变换后的四个点坐标分别为 ( x 1 , y 1 ) 、 ( x 2 , y 2 ) 、 ( x 3 , y 3 ) 、 ( x 4 , y 4 ) (x_1,y_1)、(x_2,y_2)、(x_3,y_3)、(x_4,y_4) (x1,y1)(x2,y2)(x3,y3)(x4,y4),根据上面的公式和对应的四个点坐标可得下面的方程式
    x 1 = a 31 y 1 = a 32 x 2 = a 11 + a 31 − a 13 ∗ x 2 y 2 = a 12 + a 32 − a 13 ∗ y 2 x 3 = a 11 + a 21 + a 31 − a 13 ∗ x 3 − a 23 ∗ x 3 y 3 = a 12 + a 22 + a 32 − a 13 ∗ y 3 − a 23 ∗ y 3 x 4 = a 21 + a 31 − a 23 ∗ x 4 y 4 = a 22 + a 32 − a 23 ∗ y 4 \begin{aligned} & x_1 = a_{31}\\ & y_1 = a_{32}\\ & x_2 = a_{11}+a_{31}-a_{13}*x_2\\ & y_2 = a_{12}+a_{32}-a_{13}*y_2\\ & x_3 = a_{11} + a_{21} + a_{31} - a_{13}*x_3 - a_{23}*x_3\\ & y_3 = a_{12} + a_{22} + a_{32} - a_{13}*y_3 - a_{23} * y_3 \\ & x_4 = a_{21} + a_{31} - a_{23} * x_4\\ & y_4 = a_{22} + a_{32} - a_{23}*y_4 \\ \end{aligned} x1=a31y1=a32x2=a11+a31a13x2y2=a12+a32a13y2x3=a11+a21+a31a13x3a23x3y3=a12+a22+a32a13y3a23y3x4=a21+a31a23x4y4=a22+a32a23y4
    通过上面的8个方程,我们可以解出8个参数求出透视变换矩阵,最后我们通过opencv的warpPerspective方法利用透视变换矩阵来实现透视变换,接下来我们通过结合一个实例来具体运用一下。

透视变换实例讲解

这里我们主要通过opencv来实现上面介绍的那个功能

  • 读取图像
#读取图像
img = cv2.imread("poker.jpeg")
#将原图转为灰度图
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
  • Canny边缘检测

Canny函数参数解析:

  • image:输入图像数组
  • threshold1:最低的阈值
  • threshold2:最高的阈值
  • edges:输出的边缘图像,单通道8位图像
  • apertureSize:Sobel算子的大小
  • L2gradient:表示一个布尔值,如果为真,则使用更精确的L2范数进行计算,否则使用L1范数
#Canny边缘检测
canny_img = cv2.Canny(gray_img,100,150,3)
#显示边缘检测后的图像
cv2.imshow("canny_img",canny_img)
cv2.waitKey(0)

在这里插入图片描述

  • 霍夫直线检测

HoughLinesP函数参数解析:

  • image:经过Canny边缘检测后的输出图像
  • rho:极坐标的半径r以像素值为单位的分辨率,一般使用1像素
  • theta:极坐标的极角 θ \theta θ以弧度为单位的分辨率,一般使用1度
  • threshold:检测一条直线所需最少的曲线交点
  • lines:存储检测到的直线,包含直线的起点和终点坐标
  • minLineLength:组成一条直线的最少点的数量,点数量不足的直线将被抛弃
  • maxLineGap:在一条直线上的点的最大距离
def draw_line(img,lines):# 绘制直线for line_points in lines:cv2.line(img,(line_points[0][0],line_points[0][1]),(line_points[0][2],line_points[0][3]),(0,255,0),2,8,0)cv2.imshow("line_img", img)cv2.waitKey(0)
# #Hough直线检测
lines = cv2.HoughLinesP(canny_img,1,np.pi/180,70,minLineLength=30,maxLineGap=10)
#基于边缘检测的图像来检测直线
draw_line(img,lines)

在这里插入图片描述

  • 计算顶点坐标

通过直线两个端点的坐标来计算直线的交点坐标,找出扑克牌的四个顶点位置

#计算四条直线的交点作为顶点坐标
def computer_intersect_point(lines):def get_line_k_b(line_point):"""计算直线的斜率和截距:param line_point: 直线的坐标点:return:"""#获取直线的两点坐标x1,y1,x2,y2 = line_point[0]#计算直线的斜率和截距k = (y1 - y2)/(x1 - x2)b = y2 - x2 * (y1 - y2)/(x1 - x2)return k,b#用来存放直线的交点坐标line_intersect = []for i in range(len(lines)):k1,b1 = get_line_k_b(lines[i])for j in range(i+1,len(lines)):k2,b2 = get_line_k_b(lines[j])#计算交点坐标x = (b2 - b1) / (k1 - k2)y = k1 * (b2 - b1)/(k1 -k2) + b1if x > 0 and y > 0:line_intersect.append((int(np.round(x)),int(np.round(y))))return line_intersect
def draw_point(img,points):for position in points:cv2.circle(img,position,5,(0,0,255),-1)cv2.imshow("draw_point",img)cv2.waitKey(0)
#计算直线的交点坐标
line_intersect = computer_intersect_point(lines)
#绘制交点坐标的位置
draw_point(img,line_intersect)

在这里插入图片描述

  • 对顶点坐标进行排序

在计算透视变换矩阵之前我们需要对元素图像的坐标与变换后图像的坐标一一对应,按照->->->的顺序

def order_point(points):"""对交点坐标进行排序:param points::return:"""points_array = np.array(points)#对x的大小进行排序x_sort = np.argsort(points_array[:,0])#对y的大小进行排序y_sort = np.argsort(points_array[:,1])#获取最左边的顶点坐标left_point = points_array[x_sort[0]]#获取最右边的顶点坐标right_point = points_array[x_sort[-1]]#获取最上边的顶点坐标top_point = points_array[y_sort[0]]#获取最下边的顶点坐标bottom_point = points_array[y_sort[-1]]return np.array([left_point,top_point,right_point,bottom_point],dtype=np.float32)
def target_vertax_point(clockwise_point):#计算顶点的宽度(取最大宽度)w1 = np.linalg.norm(clockwise_point[0]-clockwise_point[1])w2 = np.linalg.norm(clockwise_point[2]-clockwise_point[3])w = w1 if w1 > w2 else w2#计算顶点的高度(取最大高度)h1 = np.linalg.norm(clockwise_point[1]-clockwise_point[2])h2 = np.linalg.norm(clockwise_point[3]-clockwise_point[0])h = h1 if h1 > h2 else h2#将宽和高转换为整数w = int(round(w))h = int(round(h))#计算变换后目标的顶点坐标top_left = [0,0]top_right = [w,0]bottom_right = [w,h]bottom_left = [0,h]return np.array([top_left,top_right,bottom_right,bottom_left],dtype=np.float32)
#对原始图像的交点坐标进行排序
clockwise_point = order_point(line_intersect)
#获取变换后坐标的位置
target_clockwise_point = target_vertax_point(clockwise_point)
  • 计算变换矩阵进行透视变换
#计算变换矩阵
matrix = cv2.getPerspectiveTransform(clockwise_point,target_clockwise_point)
print(matrix)
#计算透视变换后的图片
perspective_img = cv2.warpPerspective(img,matrix,(target_clockwise_point[2][0],target_clockwise_point[2][1]))
cv2.imshow("perspective_img",perspective_img)
cv2.waitKey(0)
[[ 1.34133640e+00 -8.01200936e-01 -2.49362538e+01][ 7.10053715e-01  1.67369804e+00 -1.62145838e+02][ 2.49859580e-04  3.44969838e-03  1.00000000e+00]]

http://chatgpt.dhexx.cn/article/UGurayNE.shtml

相关文章

[解疑]图像、矩阵的二维空间变换

本文经过参考多个文章整理而成,感谢各位博主的无私分享。 综述 图像(2维平面)到图像(2维平面)的四种变换包括:等距变换,相似变换,仿射变换,投影变换。对图像的几何变换…

数字图像处理(入门篇)十四 透视变换

目录 一 透视变换 二 实践 (1)代码 (2)结果图 一 透视变换 现实生活中的空间是三维的,图像中的物体存在近大远小的特征,这种畸变仿射变换不能矫正。因此,我们需要使用到三维空间的变化&…

【图像理论】透视变换

透视变换: 定义:本质是将图像投影到一个新的视平面。仿射变换可以理解为透视变换的特殊形式。利用透视中心、像点、目标点三点共线的条件,按透视旋转定律使承影面(透视面)绕迹线(透视轴)旋转某…

Python图像处理之透视变换

1 引言 如果你想对图像进行校准,那么透视变换是非常有效的变换手段。透视变换的定义为将图像投影到一个新的视平面,通常也被称之为投影映射。 2 公式 一般来说,通用的图像变换公式如下所示: 上述公式中,u,v代表原…

维特比算法 python_维特比算法理解与实现(Python)

前言 写这篇文章就是想以通俗易懂的方式解析维特比算法,最后给出Python代码的实现。下面的公式和原理均出自《统计学习方法》。 算法的原理 算法的原理1.PNG 算法的原理2.PNG 上面写了一大堆,意思就是:每个时刻选择出概率最大的路径&#xf…

viterbi-algorithm 维特比算法的例子解析

维特比算法的目的: 寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states) 关于原理的讲解可以参考下面两篇文章,讲的比较清楚 小白给小白详解维特比算法1. 小白给小白详解维特比算法2. 本文通过分析维特比算法的例子&#xff0c…

维特比算法代码

维特比算法实现python语言版 本文主要写一个关于维特比算法的代码,具体理论请参考一文搞懂HMM(隐马尔可夫模型): HMM(隐马尔可夫模型)是用来描述隐含未知参数的统计模型,举一个经典的例子&…

维特比算法学习

参考文章1: 简直不要太通俗易懂,这篇文章,很值得看 参考文章2: 解释一些概念性的问题,我把他的一些内容写下来 维特比(Viterbi)算法的核心是动态规划。 对于 HMM 而言,其中一个重要的任务就是要找出最有…

5分钟理解维特比算法

安德鲁维特比老人家发明了维特比算法,用非常巧妙的方法简化了隐马尔可夫第二个问题运算过程。维特比先生后来发明了CDMA技术并与人一起创办了高通公司,高通现在是通信巨头,不生产产品却每年收取大量的专利费。 下面我们用简单的例子&#xff…

Viterbi-Algorithm(维特比算法)

Viterbi-Algorithm 维特比算法是一个特殊但应用最广的动态规划算法。利用动态规划,可以解决任何一个图中的最短路径问题。而维特比算法是针对一个特殊的图-篱笆网了(Lattice)的有向图最短路径问题而提出来的。它之所以重要,是因为…

NLP学习笔记06-维特比算法

一序 本文属于NLP学习笔记系列。 上一篇整理了前向最大匹配算法与所有组合算法缺点(时间复杂度太高了)。 二 维特比算法 log(x*y*z) log(x)log(y)log(z) 概率上为了避免小数练乘出现的超范围溢出,改用log,改用-log,使得原来求概…

HMM-维特比算法

HMM-维特比算法(viterbi) HMM回顾隐马科夫链解法:维特比算法(Viterbi) HMM回顾 最终的公式可以解释主要分为两个部分: P(xi|yi),发射概率,字面意思是从一个词性中发射/生成出某一个…

Viterbi算法(维特比算法)

维特比算法背景: 安德鲁维特比(Andrew J. Viterbi),CDMA之父,IEEE Fellow,高通公司创始人之一,高通首席科学家。他开发了卷积码编码的最大似然算法而享誉全球。1991年香农奖(Claude …

HMM+维特比算法

一、简介 Viterbi 算法 考虑到穷举方法的缺点,可以采用:Viterbi 算法: 动态搜索最优状态序列,这样每个节点保存的是到当前节点的局部最优概率;依据最后一个时刻中概率最高的状态,逆向找其路径中的上一个最大部分最优路…

维特比算法的java实现_原创:维特比算法

看了宗成庆博士的《统计自然语言处理(中文信息处理)》的第六章,对维特比算法有着非常精辟的讲解。把其中的讲解上传上来,个人感觉比较正统。 今天用Java实现了这个算法,也可以转换为C代码: package com.nlp.hmm.algorithm.viterbi…

隐马尔可夫(HMM)、前/后向算法、Viterbi算法 再次总结

本总结是是个人为防止遗忘而作,不得转载和商用。 说明:此篇是作者对“隐马尔可夫模型”的第二次总结,因此可以算作对上次总结的查漏补缺以及更进一步的理解,所以很多在第一次总结中已经整理过的内容在本篇中将不再重复&#xff0c…

维特比(Viterbi)算法

维特比算法 (Viterbi algorithm) 是机器学习中应用非常广泛的动态规划算法,在求解隐马尔科夫、条件随机场的预测以及seq2seq模型概率计算等问题中均用到了该算法。实际上,维特比算法不仅是很多自然语言处理的解码算法,也是现代数字通信中使用…

小白给小白详解维特比算法(一)

小白给小白详解维特比算法(一) 小白给小白详解维特比算法一 篱笆网络Lattice的最短路径问题 这个问题长什么样子这个问题难在哪里简化成这个模样你总能回答了吧下一步我们该干什么 别倒立了我们再从头想一下这个问题 我们是怎么走过来的来我们从A开始走这…

viterbi算法

维特比算法(Viterbi Algorithm) 标签(空格分隔): 未分类 维特比算法(Viterbi Algorithm) viterbi算法用于寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states) 对于…

维特比算法

1. 概述 维特比算法是安德鲁.维特比(Andrew Viterbi)于1967年为解决通信领域中的解码问题而提出的,它同样广泛用于解决自然语言处理中的解码问题,隐马尔可夫模型的解码是其中典型的代表。无论是通信中的解码问题还是自然语言处理中的解码问题&#xff0c…