文章目录

  • 引言
  • 透视变换(projective transform)
  • 单应性(Homography)
  • opencv代码
    • 仿射变换相关函数
    • 投影变换相关的函数
    • 鸟瞰图代码示例
  • 小结

引言

图像的几何变换通常包括拉伸、缩放、扭曲和旋转等操作。

对于平面区域来说,分为两类几何转换:1

  • ⭐️仿射变换(affine transform),基于2x3矩阵进行变换。指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够保持图像的平直性和平行性。平直性是指图像经过仿射变换后,直线仍然是直线;平行性是指图像在完成仿射变换后,平行线仍然是平行线。
  • ⭐️透视变换(projective transform),基于3x3矩阵进行变换。透视变换将视锥体转换为长方体形状,视锥体的近端比远端小,具有扩大相机附近物体的效果。透视变换可以改变平行关系,将矩形映射为任意四边形。

透视变换通常被用于当作从特定角度观察三维平面的计算方法(非垂直观测),在三维视觉领域具有广泛的应用。

本文主要介绍opencv中的透视变换(projective transform)原理和代码。

透视变换(projective transform)

透视变换,又称为投影变换,是指将坐标为(X,Y,Z)(X,Y,Z)(X,Y,Z)的物理点QQQ映射到投影平面上坐标为(x,y)(x,y)(x,y)的点qqq的过程1


基本投影几何

将定义摄像机参数的矩阵(包含fx,fy,cx,cyf_x,f_y,c_x,c_yfx​,fy​,cx​,cy​)称为摄像机的内参数矩阵(camera intrinsics matrix)。

物理世界中的点QQQ投影到摄像机上的点qqq的过程,可以用下式表示:

q=M⋅Q,其中q=[xyw],M=[fx0cx0fycy001],Q=[XYZ]q=M\cdot{Q},其中q= \begin{bmatrix} x \\ y \\ w \end{bmatrix} , M=\begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix} ,Q=\begin{bmatrix} X \\ Y \\ Z \end{bmatrix} q=M⋅Q,其中q=⎣⎡​xyw​⎦⎤​,M=⎣⎡​fx​00​0fy​0​cx​cy​1​⎦⎤​,Q=⎣⎡​XYZ​⎦⎤​

单应性(Homography)

计算机视觉中,平面的单应性被定义为从一个平面到另一个平面的投影映射。(注:“单应性”在不同学科有不同含义,在数学上有更通用的意思。这里仅说明计算机视觉中的单应性。1

二维平面上的点映射到摄像机成像画面上的映射,就是平面单应性的典型例子。

利用齐次坐标,可以对物理世界的点Q到成像画面上的点q的映射进行表示:

q=s⋅M⋅W⋅Qq =s \cdot{M}\cdot{W} \cdot{Q} q=s⋅M⋅W⋅Q
其中,参数s表示尺度比例,M=[fx0cx0fycy001]M=\begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix}M=⎣⎡​fx​00​0fy​0​cx​cy​1​⎦⎤​ ,W表示旋转R和平移t的影响之和W=[Rt]W = [R \space\space\space t]W=[R   t],旋转矩阵R=[r1⃗r2⃗r3⃗]R=\begin{bmatrix} \vec{r_1} & \vec{r_2} & \vec{r_3} \end{bmatrix}R=[r1​​​r2​​​r3​​​]

由于我们只关注这个物体平面,为了简化计算,令Z=0:
[xy1]=s⋅M⋅[r1⃗r2⃗r3⃗t⃗]⋅[XY01]=s⋅M⋅[r1⃗r2⃗t⃗]⋅[XY1]\begin{bmatrix} x \\ y \\ 1 \end{bmatrix} =s \cdot{M}\cdot{\begin{bmatrix} \vec{r_1} & \vec{r_2} & \vec{r_3} & \vec{t} \end{bmatrix}} \cdot{\begin{bmatrix} X \\ Y \\ 0 \\ 1 \end{bmatrix}} =s \cdot{M}\cdot{\begin{bmatrix} \vec{r_1} & \vec{r_2} & \vec{t} \end{bmatrix}} \cdot{\begin{bmatrix} X \\ Y \\ 1 \end{bmatrix}} ⎣⎡​xy1​⎦⎤​=s⋅M⋅[r1​​​r2​​​r3​​​t​]⋅⎣⎢⎢⎡​XY01​⎦⎥⎥⎤​=s⋅M⋅[r1​​​r2​​​t​]⋅⎣⎡​XY1​⎦⎤​

用H表示转换矩阵:H=s⋅M⋅[r1⃗r2⃗t⃗]H = s \cdot{M}\cdot{\begin{bmatrix} \vec{r_1} & \vec{r_2} & \vec{t} \end{bmatrix}} H=s⋅M⋅[r1​​​r2​​​t​]
H由两部分组成:用于定位观察的物体平面的物理变换和使用摄像机内参数矩阵的投影。

对于同一摄像头拍摄的画面来说,无需计算内参矩阵和物理变换。仅通过下面这个简化的方程,就能利用单应性矩阵H将源平面上的点与目标像平面上的点联系起来:
Pdst=HPsrc,Psrc=H−1PdstP_{dst} = HP_{src},P_{src} = H^{-1}P_{dst} Pdst​=HPsrc​,Psrc​=H−1Pdst​
Pdst=[xdstydst1],Psrc=[xsrcysrc1]P_{dst} = \begin{bmatrix} x_{dst} \\ y_{dst} \\ 1 \end{bmatrix}, P_{src} = \begin{bmatrix} x_{src} \\ y_{src} \\ 1 \end{bmatrix} Pdst​=⎣⎡​xdst​ydst​1​⎦⎤​,Psrc​=⎣⎡​xsrc​ysrc​1​⎦⎤​
有了这个简化的公式,我们就能明白opencv中求解单应性矩阵的原理了。

opencv代码

仿射变换相关函数

  • cv::transform:对一组点进行仿射变换
  • cv::warpAffine:对整幅图像进行仿射变换
  • cv::getAffineTransform:从一组点计算仿射变换矩阵
  • cv::getRotationMatrix2D:计算旋转矩阵

投影变换相关的函数

  • cv::perspectiveTransform:对一组点进行透射变换/投影变换
  • cv::warpPerspective:对整幅图像进行透视变换/投影变换
  • cv::getPerspectiveTransform:获取透视变换/投影变换矩阵
  • cv::findHomography:计算单应性矩阵

其中,findHomography的method参数用于选择计算单应性矩阵的算法。

  • cv::RANSAC:随机抽样方法( random sampling with consensus),随机选择所提供点的子集,并计算一个同源矩阵。RANSAC算法计算许多这样的随机抽样,并保留具有最大部分的抽样。该方法在实际应用中在拒绝噪声离群数据和寻找正确答案方面非常有效
  • cv::LMEDS:最小二乘中位数(least median of squares method),顾名思义,LMeDS背后的想法是最小化中值误差,而不是用默认方法基本上最小化的均方误差。这种方法的缺点是,只有当插入器至少构成数据点的大多数时,它才能表现良好。相比之下,RANSAC可以正常工作,并在给出几乎任何信噪比时给出令人满意的答案。
  • cv::RHO:RHO算法,在OpenCV3中使用,它基于一种被称为PROSAC的“加权”RANSAC修改,在许多异常值的情况下运行得更快。

鸟瞰图代码示例

《Learning OpenCV》中有一个经典的例子,是将前置车载摄像头拍摄的图像,利用透视变换转换成鸟瞰图的视角。

这里的做法是,选取前置摄像头拍摄的透视图中的一块区域的四个点,获取图像中这四个点对应在俯瞰视角中的坐标点,计算转换矩阵,然后进行重映射,就能将前视图重映射到鸟瞰图。

在没有相机标定参数的情况下,可以利用物理平面和像平面中的四个对应点计算单应性矩阵,从而实现从透视图变换到鸟瞰图的效果。

#mermaid-svg-JZUvkz68ikHg50VP .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-JZUvkz68ikHg50VP .label text{fill:#333}#mermaid-svg-JZUvkz68ikHg50VP .node rect,#mermaid-svg-JZUvkz68ikHg50VP .node circle,#mermaid-svg-JZUvkz68ikHg50VP .node ellipse,#mermaid-svg-JZUvkz68ikHg50VP .node polygon,#mermaid-svg-JZUvkz68ikHg50VP .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-JZUvkz68ikHg50VP .node .label{text-align:center;fill:#333}#mermaid-svg-JZUvkz68ikHg50VP .node.clickable{cursor:pointer}#mermaid-svg-JZUvkz68ikHg50VP .arrowheadPath{fill:#333}#mermaid-svg-JZUvkz68ikHg50VP .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-JZUvkz68ikHg50VP .flowchart-link{stroke:#333;fill:none}#mermaid-svg-JZUvkz68ikHg50VP .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-JZUvkz68ikHg50VP .edgeLabel rect{opacity:0.9}#mermaid-svg-JZUvkz68ikHg50VP .edgeLabel span{color:#333}#mermaid-svg-JZUvkz68ikHg50VP .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-JZUvkz68ikHg50VP .cluster text{fill:#333}#mermaid-svg-JZUvkz68ikHg50VP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-JZUvkz68ikHg50VP .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-JZUvkz68ikHg50VP text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-JZUvkz68ikHg50VP .actor-line{stroke:grey}#mermaid-svg-JZUvkz68ikHg50VP .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-JZUvkz68ikHg50VP .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-JZUvkz68ikHg50VP #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-JZUvkz68ikHg50VP .sequenceNumber{fill:#fff}#mermaid-svg-JZUvkz68ikHg50VP #sequencenumber{fill:#333}#mermaid-svg-JZUvkz68ikHg50VP #crosshead path{fill:#333;stroke:#333}#mermaid-svg-JZUvkz68ikHg50VP .messageText{fill:#333;stroke:#333}#mermaid-svg-JZUvkz68ikHg50VP .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-JZUvkz68ikHg50VP .labelText,#mermaid-svg-JZUvkz68ikHg50VP .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-JZUvkz68ikHg50VP .loopText,#mermaid-svg-JZUvkz68ikHg50VP .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-JZUvkz68ikHg50VP .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-JZUvkz68ikHg50VP .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-JZUvkz68ikHg50VP .noteText,#mermaid-svg-JZUvkz68ikHg50VP .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-JZUvkz68ikHg50VP .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-JZUvkz68ikHg50VP .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-JZUvkz68ikHg50VP .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-JZUvkz68ikHg50VP .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JZUvkz68ikHg50VP .section{stroke:none;opacity:0.2}#mermaid-svg-JZUvkz68ikHg50VP .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-JZUvkz68ikHg50VP .section2{fill:#fff400}#mermaid-svg-JZUvkz68ikHg50VP .section1,#mermaid-svg-JZUvkz68ikHg50VP .section3{fill:#fff;opacity:0.2}#mermaid-svg-JZUvkz68ikHg50VP .sectionTitle0{fill:#333}#mermaid-svg-JZUvkz68ikHg50VP .sectionTitle1{fill:#333}#mermaid-svg-JZUvkz68ikHg50VP .sectionTitle2{fill:#333}#mermaid-svg-JZUvkz68ikHg50VP .sectionTitle3{fill:#333}#mermaid-svg-JZUvkz68ikHg50VP .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JZUvkz68ikHg50VP .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-JZUvkz68ikHg50VP .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JZUvkz68ikHg50VP .grid path{stroke-width:0}#mermaid-svg-JZUvkz68ikHg50VP .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-JZUvkz68ikHg50VP .task{stroke-width:2}#mermaid-svg-JZUvkz68ikHg50VP .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JZUvkz68ikHg50VP .taskText:not([font-size]){font-size:11px}#mermaid-svg-JZUvkz68ikHg50VP .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JZUvkz68ikHg50VP .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-JZUvkz68ikHg50VP .task.clickable{cursor:pointer}#mermaid-svg-JZUvkz68ikHg50VP .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-JZUvkz68ikHg50VP .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-JZUvkz68ikHg50VP .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-JZUvkz68ikHg50VP .taskText0,#mermaid-svg-JZUvkz68ikHg50VP .taskText1,#mermaid-svg-JZUvkz68ikHg50VP .taskText2,#mermaid-svg-JZUvkz68ikHg50VP .taskText3{fill:#fff}#mermaid-svg-JZUvkz68ikHg50VP .task0,#mermaid-svg-JZUvkz68ikHg50VP .task1,#mermaid-svg-JZUvkz68ikHg50VP .task2,#mermaid-svg-JZUvkz68ikHg50VP .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-JZUvkz68ikHg50VP .taskTextOutside0,#mermaid-svg-JZUvkz68ikHg50VP .taskTextOutside2{fill:#000}#mermaid-svg-JZUvkz68ikHg50VP .taskTextOutside1,#mermaid-svg-JZUvkz68ikHg50VP .taskTextOutside3{fill:#000}#mermaid-svg-JZUvkz68ikHg50VP .active0,#mermaid-svg-JZUvkz68ikHg50VP .active1,#mermaid-svg-JZUvkz68ikHg50VP .active2,#mermaid-svg-JZUvkz68ikHg50VP .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-JZUvkz68ikHg50VP .activeText0,#mermaid-svg-JZUvkz68ikHg50VP .activeText1,#mermaid-svg-JZUvkz68ikHg50VP .activeText2,#mermaid-svg-JZUvkz68ikHg50VP .activeText3{fill:#000 !important}#mermaid-svg-JZUvkz68ikHg50VP .done0,#mermaid-svg-JZUvkz68ikHg50VP .done1,#mermaid-svg-JZUvkz68ikHg50VP .done2,#mermaid-svg-JZUvkz68ikHg50VP .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-JZUvkz68ikHg50VP .doneText0,#mermaid-svg-JZUvkz68ikHg50VP .doneText1,#mermaid-svg-JZUvkz68ikHg50VP .doneText2,#mermaid-svg-JZUvkz68ikHg50VP .doneText3{fill:#000 !important}#mermaid-svg-JZUvkz68ikHg50VP .crit0,#mermaid-svg-JZUvkz68ikHg50VP .crit1,#mermaid-svg-JZUvkz68ikHg50VP .crit2,#mermaid-svg-JZUvkz68ikHg50VP .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-JZUvkz68ikHg50VP .activeCrit0,#mermaid-svg-JZUvkz68ikHg50VP .activeCrit1,#mermaid-svg-JZUvkz68ikHg50VP .activeCrit2,#mermaid-svg-JZUvkz68ikHg50VP .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-JZUvkz68ikHg50VP .doneCrit0,#mermaid-svg-JZUvkz68ikHg50VP .doneCrit1,#mermaid-svg-JZUvkz68ikHg50VP .doneCrit2,#mermaid-svg-JZUvkz68ikHg50VP .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-JZUvkz68ikHg50VP .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-JZUvkz68ikHg50VP .milestoneText{font-style:italic}#mermaid-svg-JZUvkz68ikHg50VP .doneCritText0,#mermaid-svg-JZUvkz68ikHg50VP .doneCritText1,#mermaid-svg-JZUvkz68ikHg50VP .doneCritText2,#mermaid-svg-JZUvkz68ikHg50VP .doneCritText3{fill:#000 !important}#mermaid-svg-JZUvkz68ikHg50VP .activeCritText0,#mermaid-svg-JZUvkz68ikHg50VP .activeCritText1,#mermaid-svg-JZUvkz68ikHg50VP .activeCritText2,#mermaid-svg-JZUvkz68ikHg50VP .activeCritText3{fill:#000 !important}#mermaid-svg-JZUvkz68ikHg50VP .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JZUvkz68ikHg50VP g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-JZUvkz68ikHg50VP g.classGroup text .title{font-weight:bolder}#mermaid-svg-JZUvkz68ikHg50VP g.clickable{cursor:pointer}#mermaid-svg-JZUvkz68ikHg50VP g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-JZUvkz68ikHg50VP g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-JZUvkz68ikHg50VP .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-JZUvkz68ikHg50VP .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-JZUvkz68ikHg50VP .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-JZUvkz68ikHg50VP .dashed-line{stroke-dasharray:3}#mermaid-svg-JZUvkz68ikHg50VP #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JZUvkz68ikHg50VP #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JZUvkz68ikHg50VP #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-JZUvkz68ikHg50VP #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-JZUvkz68ikHg50VP #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JZUvkz68ikHg50VP #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JZUvkz68ikHg50VP #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JZUvkz68ikHg50VP #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-JZUvkz68ikHg50VP .commit-id,#mermaid-svg-JZUvkz68ikHg50VP .commit-msg,#mermaid-svg-JZUvkz68ikHg50VP .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JZUvkz68ikHg50VP .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JZUvkz68ikHg50VP .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JZUvkz68ikHg50VP g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JZUvkz68ikHg50VP g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-JZUvkz68ikHg50VP g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-JZUvkz68ikHg50VP g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-JZUvkz68ikHg50VP g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-JZUvkz68ikHg50VP g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-JZUvkz68ikHg50VP .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-JZUvkz68ikHg50VP .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-JZUvkz68ikHg50VP .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-JZUvkz68ikHg50VP .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-JZUvkz68ikHg50VP .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-JZUvkz68ikHg50VP .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-JZUvkz68ikHg50VP .edgeLabel text{fill:#333}#mermaid-svg-JZUvkz68ikHg50VP .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-JZUvkz68ikHg50VP .node circle.state-start{fill:black;stroke:black}#mermaid-svg-JZUvkz68ikHg50VP .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-JZUvkz68ikHg50VP #statediagram-barbEnd{fill:#9370db}#mermaid-svg-JZUvkz68ikHg50VP .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-JZUvkz68ikHg50VP .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-JZUvkz68ikHg50VP .statediagram-state .divider{stroke:#9370db}#mermaid-svg-JZUvkz68ikHg50VP .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-JZUvkz68ikHg50VP .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-JZUvkz68ikHg50VP .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-JZUvkz68ikHg50VP .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-JZUvkz68ikHg50VP .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-JZUvkz68ikHg50VP .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-JZUvkz68ikHg50VP .note-edge{stroke-dasharray:5}#mermaid-svg-JZUvkz68ikHg50VP .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-JZUvkz68ikHg50VP .error-icon{fill:#522}#mermaid-svg-JZUvkz68ikHg50VP .error-text{fill:#522;stroke:#522}#mermaid-svg-JZUvkz68ikHg50VP .edge-thickness-normal{stroke-width:2px}#mermaid-svg-JZUvkz68ikHg50VP .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-JZUvkz68ikHg50VP .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-JZUvkz68ikHg50VP .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-JZUvkz68ikHg50VP .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-JZUvkz68ikHg50VP .marker{fill:#333}#mermaid-svg-JZUvkz68ikHg50VP .marker.cross{stroke:#333}:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}#mermaid-svg-JZUvkz68ikHg50VP {color: rgba(0, 0, 0, 0.75);font: ;}

未标定
已标定
开始
获取透视图中的点坐标和物理世界中的点坐标
计算单应性矩阵
透视变换
直接计算转换矩阵

python代码如下:

import numpy as np
import cv2 as cv# 读取图像
src = cv.imread(img_path)# 获取四个对应点
dstPts = np.array([(0,0),(640,0),(0,520),(640,520)])
srcPts = np.array([(265,99),(696,99),(129,473),(832,473)])# 获取单应性矩阵
H = cv.findHomography(srcPts,dstPts,method=cv.RANSAC)
#M = cv.getPerspectiveTransform(objPts,imgPts)
print(f"Homography: {H.shape} \n{H}")
if len(H)>1:H = H[0]# 利用转换矩阵进行重映射(Remap)
birdView = cv.warpPerspective(src,H,None,cv.INTER_LINEAR | cv.WARP_INVERSE_MAP | cv.WARP_FILL_OUTLIERS)# 显示重映射后的鸟瞰图
while (1):cv.imshow("perspective", birdView)if cv.waitKey(100) == ord('q'):  # 按下q退出break
cv.destroyAllWindows()

如果需要对图像进行反变换,可以利用numpy对矩阵求逆np.linalg.inv(H),作为cv.warpPerspective的转换矩阵。

小结

本文整理了计算机视觉中透视变换 / 投影变换 / 单应性的基本概念,以及OpenCV中的函数和使用示例。

如果对你有帮助的话,欢迎一键三连支持下博主~


  1. 《Learning OpenCV 3》 ↩︎ ↩︎ ↩︎

OpenCV中的「透视变换 / 投影变换 / 单应性」—cv.warpPerspective、cv.findHomography相关推荐

  1. OpenCV学习:仿射变换+投射变换+单应性矩阵

    OpenCV学习:仿射变换+投射变换+单应性矩阵 estimateRigidTransform():计算多个二维点对或者图像之间的最优仿射变换矩阵 (2行x3列),H可以是部分自由度,比如各向一致的切 ...

  2. OpenCV中的特征匹配+单应性以查找对象

    OpenCV中的特征匹配+单应性以查找对象 1. 效果图 2. 源码 参考 这篇博客将混合calib3d模块中的特征匹配和单应性,在复杂图像中查找已知对象. 1. 效果图 特征匹配 & 单应性 ...

  3. Homography matrix(单应性矩阵)在广告投放中的实践

    原文首发于微信公众号「3D视觉工坊」. 前言 由于近期在研究相机与投影仪的标定程序时,需要将结构光图片与灰点相机拍摄得到的图片中,找出角点之间的对应性,使用了如下一条代码: Mat HomoMatri ...

  4. opencv求两张图像光流_OpenCV单应性矩阵发现参数估算方法详解

    点击上方蓝字关注我们 微信公众号:OpenCV学堂 关注获取更多计算机视觉与深度学习知识 单应性矩阵计算函数与应用 OpenCV在通过特征描述子完成描述子匹配之后,会得到一些关键点对,我们会把这些关键 ...

  5. 透视变换 单应性矩阵怎么求 matlab,单应性(homography)变换的推导

    矩阵的一个重要作用是将空间中的点变换到另一个空间中.这个作用在国内的<线性代数>教学中基本没有介绍.要能形像地理解这一作用,比较直观的方法就是图像变换,图像变换的方法很多,单应性变换是其中 ...

  6. python+OpenCV笔记(三十五):特征匹配——基于FLANN的匹配、基于FLANN进行单应性匹配

    目录 一.基于FLANN的匹配 FLANN匹配流程: 代码编写 二.基于FLANN进行单应性匹配 什么是单应性? FLANN进行单应性匹配流程 代码编写 FLANN库全称是Fast Library f ...

  7. 单应性变换与仿射变换

    经典的仿射变换 初始来自于 ABB实习的项目:目前看有关于多帧去噪论文 Burst Image Deblurring,发现论文作者使用单应性变换进行多帧图片之间的粗对齐. 1.详细的总结性文章 知乎专 ...

  8. sift算法_单应性Homograph估计:从传统算法到深度学习

    点击上方"CVer",选择加"星标"置顶 重磅干货,第一时间送达 本文作者:白裳 https://zhuanlan.zhihu.com/p/74597564 本 ...

  9. 计算机视觉学习笔记(四)homography 单应性矩阵的理解及求解

    单应性矩阵的理解及求解 1. 齐次坐标(Homogeneous Coordinate) 一幅2D图像上的非齐次坐标为(x,y),而齐次坐标为(x,y,1),也可以写成(x/z,y/z,1)或(x,y, ...

最新文章

  1. echarts 动态改变数据_Echarts的使用
  2. 温习了一下java线程状态方面的知识总结一
  3. iOS录音后播放声音变小的解决方法
  4. Django之创建应用以及配置路由
  5. 文件的读操作 c# 1614526130
  6. html equls比较方法,编写高质量equals方法
  7. WinSocket的一些主要函数.
  8. PowerBI切换日期维度
  9. 【过一下19】学了git,看了算法竞赛讲解
  10. 实体门店为什么要做共享股东模式
  11. error converting to execution character set illegal byte sequence报错解决办法
  12. 程序员编程的专业名言
  13. 卷积神经网络中特征图大小计算公式总结
  14. 看看什么叫老牛吃嫩草
  15. mysql 嵌套查询性能_MySQL数据库之嵌套查询与连接查询的性能详解
  16. oracle数据库system01,system01坏块的问题
  17. java-opencv 米粒数_opencv学习之米粒分割 #201906121549
  18. (笔记)MLDN魔乐科技--五子棋
  19. uos性能测试软件,国产操作系统UOS全面深度测评,安装体验教程都在这里!
  20. 医院集成平台超融合基础架构转型方案

热门文章

  1. 用位运算实现求绝对值-有效避开if-else判断
  2. Nginx使用HTTPS建立与上游服务器的网络通信
  3. IDEA 设置自动导入包,以及手动导入包
  4. ZooKeeper管理员指南
  5. Docker安装FireBird数据库
  6. OSPF——多区域概念及配置、ABR简介、ASBR简介、路由重分发
  7. 国开本科计算机应用基础操作题,2019秋国开大学计算机应用基础Windows7操作系统形考题目及答案...
  8. Ubuntu 16.04 + Nginx + Django 项目部署
  9. 知网直接下载pdf文档
  10. 大数据 Hive spark Flink 关系