首页 > 其他分享 >视觉slam笔记_去畸变理论与流程

视觉slam笔记_去畸变理论与流程

时间:2024-05-29 23:32:02浏览次数:14  
标签:Mat int 图像 笔记 畸变 slam pmatrix distorted cv

去畸变理论

(具体内容见视觉slam14讲P97,且由于空间受限,本文推导均不放图片,有需要去查看电子书或实体书)

首先,把一会要用到的量先列出来

  • 现实世界中 P P P点( P P P在相机坐标系下的)坐标 [ X , Y , Z ] T [X, Y, Z]^T [X,Y,Z]T
  • 落在物理成像平面 O ′ − x ′ − y ′ O' - x' - y' O′−x′−y′ 上,成像点 P ′ P' P′为 [ X ′ , Y ′ , Z ′ ] T [X', Y', Z']^T [X′,Y′,Z′]T
    • 注意:从图像中提取出来的点的坐标是像素坐标,而这个物理成像平面的坐标 X ′ = X Z X'=\frac{X}{Z} X′=ZX​
    • 去畸变时公式操控的点也是在成像平面上的点,而不是像素平面
  • 相机的世界坐标 P w P_w Pw​
  • 像素坐标 P u v = [ u , v ] T P_{uv}=[u, v]^T Puv​=[u,v]T
  • 透镜焦距为 f f f

其次,理论推导

通过小孔成像的模型,我们有如下公式

Z f = − X X ′ = − Y Y ′ (5.1) \frac{Z}{f} = -\frac{X}{X'} = -\frac{Y}{Y'}\tag{5.1} fZ​=−X′X​=−Y′Y​(5.1)

放到归一化平面上,就得到了
Z f = X X ′ = Y Y ′ (5.2) \frac{Z}{f} = \frac{X}{X'} = \frac{Y}{Y'}\tag{5.2} fZ​=X′X​=Y′Y​(5.2)
化简,有
X ′ = f X Z Y ′ = f Y Z (5.3) X' = f\frac{X}{Z} \quad Y' = f\frac{Y}{Z}\tag{5.3} X′=fZX​Y′=fZY​(5.3)
由于像素平面和图像坐标系不完全重合,因此
u = α X ′ + c x v = β Y ′ + c y (5.4) \begin{aligned} u &= \alpha X' + c_x \\ v &= \beta Y' + c_y \\ \end{aligned}\tag{5.4} uv​=αX′+cx​=βY′+cy​​(5.4)

把 ( 5.3 ) (5.3) (5.3)带入,并合并一下,有
{ u = f x X Z + c x v = f y Y Z + c y (5.5) \begin{cases} u = f_x \frac{X}{Z} + c_x \\ v = f_y \frac{Y}{Z} + c_y \\ \end{cases} \tag{5.5} {u=fx​ZX​+cx​v=fy​ZY​+cy​​(5.5)
写成矩阵的形式,有
Z ( u v 1 ) = ( f x 0 c x 0 f y c y 0 0 1 ) ( X Y Z ) (5.6) Z \begin{pmatrix} u \\ v \\ 1 \end{pmatrix} = \begin{pmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} X \\ Y \\ Z \end{pmatrix}\tag{5.6} Z ​uv1​ ​= ​fx​00​0fy​0​cx​cy​1​ ​XYZ​ ​(5.6)

其中

( u v 1 ) = 1 Z K P (5.6) \begin{pmatrix} u \\ v \\ 1 \end{pmatrix}= \frac{1}{Z} K P \tag{5.6} ​uv1​ ​=Z1​KP(5.6)
对于 ( 5 , 6 ) (5,6) (5,6)这个公式,我们需要特别注意,并且以后将他作为结论来记住。 P P P是现实世界的坐标,而 P Z \frac{P}{Z} ZP​就代表归一化平面上的点。所以这个结论我们记为,归一化平面上的点乘 K K K这个矩阵,就变成了像素平面上的的点,相反的,像素平面上的点乘 K − 1 K^{-1} K−1就变成了归一化平面上的点
按照传统习惯,将 Z 移到左侧:

Z ( u v 1 ) = ( f x 0 c x 0 f y c y 0 0 1 ) ( X Y Z ) = K P (5.7) Z\begin{pmatrix} u \\ v \\ 1 \end{pmatrix} = \begin{pmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} X \\ Y \\ Z \end{pmatrix} = K P \tag{5.7} Z ​uv1​ ​= ​fx​00​0fy​0​cx​cy​1​ ​XYZ​ ​=KP(5.7)

去畸变步骤

首先,我们肯定有一张畸变的图像

上边的像素点我们都已知,为 p u v p_{uv} puv​

我们再来看 ( 5.6 ) 式 (5.6)式 (5.6)式

中间的 Z Z Z实质上就是一个常数,我们可以把它忽略不看,那么这个已知的 P u v P_{uv} Puv​其实就是 P P P乘上一个矩阵 K K K,那么这个时候我们对 P u v P_{uv} Puv​乘一个 K − 1 K^{-1} K−1就得到了 P P P,也就是归一化平面上每个点的坐标

利用去畸变公式

公式如下
x d i s t o r t e d = x ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + 2 p 1 x y + p 2 ( r 2 + 2 x 2 ) x_{distorted} = x(1 + k_1r^2 + k_2r^4 + k_3r^6) + 2p_1xy + p_2(r^2 + 2x^2) xdistorted​=x(1+k1​r2+k2​r4+k3​r6)+2p1​xy+p2​(r2+2x2)
y d i s t o r t e d = y ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + p 1 ( r 2 + 2 y 2 ) + 2 p 2 x y y_{distorted} = y(1 + k_1r^2 + k_2r^4 + k_3r^6) + p_1(r^2 + 2y^2) + 2p_2xy ydistorted​=y(1+k1​r2+k2​r4+k3​r6)+p1​(r2+2y2)+2p2​xy
我们就可以在原图像上利用这个公式对原图像进行变换,从根源上把这个图像”改对“,这样子,再对修改后的图片乘 K K K,就得到了去畸变后的 P u v P_{uv} Puv​

公式解读

原码

#include <opencv2/opencv.hpp>
#include <string>

using namespace std;

string image_file = "./distorted.png";   // 请确保路径正确

int main(int argc, char **argv) {

  // 本程序实现去畸变部分的代码。尽管我们可以调用OpenCV的去畸变,但自己实现一遍有助于理解。
  // 畸变参数
  double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05;
  // 内参
  double fx = 458.654, fy = 457.296, cx = 367.215, cy = 248.375;

  cv::Mat image = cv::imread(image_file, 0);   // 图像是灰度图,CV_8UC1
  int rows = image.rows, cols = image.cols;
  cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1);   // 去畸变以后的图

  // 计算去畸变后图像的内容
  for (int v = 0; v < rows; v++) {
    for (int u = 0; u < cols; u++) {
      // 按照公式,计算点(u,v)对应到畸变图像中的坐标(u_distorted, v_distorted)
      double x = (u - cx) / fx, y = (v - cy) / fy;
      double r = sqrt(x * x + y * y);
      double x_distorted = x * (1 + k1 * r * r + k2 * r * r * r * r) + 2 * p1 * x * y + p2 * (r * r + 2 * x * x);
      double y_distorted = y * (1 + k1 * r * r + k2 * r * r * r * r) + p1 * (r * r + 2 * y * y) + 2 * p2 * x * y;
      double u_distorted = fx  x_distorted + cx;
      double v_distorted = fy * y_distorted + cy;

      // 赋值 (最近邻插值)
      if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows) {
        image_undistort.at<uchar>(v, u) = image.at<uchar>((int) v_distorted, (int) u_distorted);
      } else {
        image_undistort.at<uchar>(v, u) = 0;
      }
    }
  }

  // 画图去畸变后图像
  cv::imshow("distorted", image);
  cv::imshow("undistorted", image_undistort);
  cv::waitKey();
  return 0;
}


解读

  1. cv::Mat
    • cv::Mat 是 OpenCV(开放源代码计算机视觉库)中的一个类,用于表示图像或矩阵数据。cv::Mat 类提供了许多功能,例如创建、加载、保存和处理图像,以及进行矩阵运算等。
    • 在 OpenCV 中,图像被表示为 cv::Mat 对象,可以通过该对象来读取、修改和操作图像数据。cv::Mat 对象包含图像的像素值、通道数、尺寸等信息,并提供了一系列函数来访问和处理图像数据。
  2. cv::imread
    cv::imread 函数是 OpenCV 中用于读取图像文件的函数。其函数原型为:
    cv::Mat cv::imread(const std::string& filename, int flags = cv::IMREAD_COLOR);
    该函数接受两个参数:图像文件名和读取标志。其中,图像文件名是一个字符串,指定要读取的图像文件的路径和名称;读取标志是一个整数,指定要读取的图像类型。
    常用的读取标志包括:
    cv::IMREAD_COLOR:默认值,读取三通道彩色图像。
    cv::IMREAD_GRAYSCALE:读取单通道灰度图像。
    cv::IMREAD_UNCHANGED:读取原图像,包括 alpha 通道(如果有)。
    cv::imread 返回一个 cv::Mat 对象,表示读取的图像。如果无法读取图像或读取的文件格式不受支持,则该函数返回一个空的 cv::Mat 对象。
  3. cv::Mat构造函数
    cv::Mat的构造函数有多个不同形式,可以接受不同的参数。常用的形参如下:
    1. cv::Mat():默认构造函数,创建一个空的cv::Mat对象。
    2. cv::Mat(int rows, int cols, int type):指定行数、列数和数据类型创建一个cv::Mat对象。
      • rows:矩阵的行数。
      • cols:矩阵的列数。
      • type:矩阵的数据类型,例如CV_8UC1表示8位无符号单通道图像。
    3. cv::Mat(int rows, int cols, int type, void* data, size_t step = AUTO_STEP):指定行数、列数、数据类型以及指向数据内存的指针创建一个cv::Mat对象。
      • rows:矩阵的行数。
      • cols:矩阵的列数。
      • type:矩阵的数据类型。
      • data:指向数据内存的指针。
      • step:可选参数,指定每行数据在内存中的步长。默认值是AUTO_STEP,表示根据矩阵的行数、数据类型自动计算步长。
    4. cv::Mat(cv::Size size, int type):通过指定尺寸和数据类型创建一个cv::Mat对象。
      • size:矩阵的尺寸,使用cv::Size类型表示。
      • type:矩阵的数据类型。
    5. cv::Mat(cv::Size size, int type, void* data, size_t step = AUTO_STEP):通过指定尺寸、数据类型和数据内存的指针创建一个cv::Mat对象。
      • size:矩阵的尺寸,使用cv::Size类型表示。
      • type:矩阵的数据类型。
      • data:指向数据内存的指针。
      • step:可选参数,指定每行数据在内存中的步长。默认值是AUTO_STEP,表示根据矩阵的行数、数据类型自动计算步长。
        这些构造函数可以根据需要选择合适的形参来创建cv::Mat对象,并根据实际情况进行初始化和操作。
  4. 最难理解的地方:去畸变究竟是怎么操作的
    理解这个问题的关键在于去畸变时公式操控的点也是在成像平面上的点,而不是像素平面
    我们可以看double x = (u - cx) / fx, y = (v - cy) / fy这行代码,这里的 X X X实际上对应着我们公式推导过程中的 X ′ X' X′,因为使用去畸变公式的时候,我们操作的是 X X X,这也就意味着 X X X是成像平面上的点

标签:Mat,int,图像,笔记,畸变,slam,pmatrix,distorted,cv
From: https://blog.csdn.net/2301_76587898/article/details/139233095

相关文章

  • vscode技巧笔记3-调试编译
    1.概述vscode作为一个编辑器,同样也是开发工具,本文介绍运行调试环境2.通用配置vscode支持多种语言,这里介绍下通用配置,c和python(我自己用的)作为章节介绍2.1.运行相关界面2.1.1.语言解释器(languagemode)和解释器vscode会自动识别文件的后缀名然后进入对应的语言模式,如果是......
  • Git使用笔记
    Git使用笔记什么是git,git介绍和svc对比https://fishc.com.cn/forum.php?mod=viewthread&tid=71107&extra=page%3D1%26filter%3Dtypeid%26typeid%3D599 https://www.liaoxuefeng.com/wiki/896043488029600/896067008724000 git安装(win)下载安装包https://git-scm.com/downl......
  • 前端学习笔记——JavaScript基础语言的学习(三)
    作者简介:大家好,本人刚专升本上岸,是个前端小菜鸟。每日都会虚心的学习。有什么写的不好的,大家都可以帮我提出来。我跟着pink老师学的!!!强烈推荐:https://www.bilibili.com/video/BV14J4114768p=1&vd_source=38cea56ff3dcca4556d2d0f0f2c18c0c1,类型转换:把一种数据类型的变量转换成......
  • 前端学习笔记——JavaScript基础语言的学习(二)
    作者简介:大家好,本人刚专升本上岸,是个前端小菜鸟。每日都会虚心的学习。有什么写的不好的,大家都可以帮我提出来。我跟着pink老师学的!!!强烈推荐:https://www.bilibili.com/video/BV14J4114768p=1&vd_source=38cea56ff3dcca4556d2d0f0f2c18c0c1.数组的基本使用2.常量的基本使用......
  • Personalized Prompt for Sequential Recommendation论文阅读笔记
    PersonalizedPromptforSequentialRecommendation论文阅读笔记Abstract​ 由于推荐的标记(即项目)是百万级的,没有具体的可解释语义,推荐中的序列建模应该是个性化的,因此将NLP的提示调整到推荐中的序列调整是具有挑战性的。​ 在这项工作中,我们首先介绍了提示推荐模型,并提出了一......
  • 矩阵树定理学习笔记
    矩阵树定理学习笔记真的,我这辈子都没有想过行列式还能用到这种地方。定义图的关联矩阵对于一张有\(n\)个点、\(m\)条边的图(对于无向图,可以随便定义边的方向,因为相反的边只需要将对应列乘以\(-1\)即可),我们定义其关联矩阵\(M\)满足:\[M_{i,j}=\left\{\begin{matrix}1&e_j......
  • 04-Excel基础操作-学习笔记
    制作下拉列表应用场景:限制B列数据,只能输入现金、转账、支票具体操作:选中B列——数据选项卡——数据工具——数据验证——弹出界面如下图所示——点击设置——在允许栏下拉选择序列——在来源栏中填入依次“填入现金、转账、支票”,并用英文状态的逗号隔开——点击确定操作......
  • 【论文笔记】机器遗忘:错误标签方法
    错误标签方法来自论文:Machine Unlearning:ASurvey中总结的方法。通过给遗忘样本提供随机的错误标签,混淆模型对样本的理解,从而无法在模型中保留任何正确的信息,以达到机器遗忘的目的。这里总结了以下论文中的方法:[1]LauraGraves,VineelNagisetty,andVijayGanesh.Am......
  • 【Windows】笔记本电池用到40%左右的时候突然断电。
    目前可能的原因:电源设置问题,三个电池水平设置错误导致到BIOS错乱?一开始我老是以为是笔记本电池问题,换了新的电池也还是到36-37%附近就断电。由于一开始我设置的是水平低>关键电池电量水平>保留电池电量电脑经常到40%附近的时候(大概36-37%,具体不定)就直接电脑断电了。具体日志......
  • QGIS开发笔记(三):Windows安装版二次开发环境搭建(下):将QGis融入QtDemo,添加QGis并加载tif遥
    前言  使用QGis的目的是进行二次开发,或者说是融入我们的应用(无人车、无人船、无人机),本片描述搭建QGis二次基础开发环境,由于实在是太长了,进行了分篇:上半部分:主要是安装好后,使用QtCreator可以使用QGIs的apps下的Qt使用对应的编译器编译不带qgis的空工程。下半部分:在上半......