4.3.2 图像去畸变

4.3.2 图像去畸变

参考教程:

相机标定(4) 矫正畸变 undistort()和initUndistortRectifyMap()-CSDN博客

学习笔记 – opencv图像去畸变_opencv 畸变参数-CSDN博客

下面我们将演示图像去畸变的过程,在OpenCV中提供了一个函数cv::undistort()用于对图像进行去畸变,为了加深我们的印象,我们从公式出发重新写一个畸变函数。

1. 安装OpenCV

1.1 下载OpenCV

参考教程:

无法定位软件包libjasper-dev的解决办法-CSDN博客

视觉slam14讲ch5 opencv安装 ubuntu20.04_libvtk5-dev-CSDN博客

OpenCV提供了大量的开源图像算法,是计算机视觉领域使用极广的图像处理算法库。在Ubuntu系统下,OpenCV有从源代码安装和只安装库文件两种方式可以选择:

(1)从源代码安装,是指从OpenCV网站下载所有的OpenCV源代码,并在机器上编译以便使用。好处是可以选择的版本比较丰富,而且也能看到源代码,不过需要花费一些编译时间。

(2)只安装库文件,是指通过Ubuntu安装由Ubuntu社区人员已经编译好的库文件,无须重新编译一遍。

因为我们使用较新版本的OpenCV,所以必须选择从源代码安装的方式来安装它。一来,可以调整一些编译选项,匹配编程环境(例如,需不需要GPU加速等);再者,可以使用一些额外的功能。OpenCV目前维护三个主要版本,分为OpenCV2.4系列、OpenCV 3系列和OpenCV 4系列。当前使用OpenCV 3系列。

从如下网站中下载源代码:

Releases - OpenCV

页面下滑,选择OpenCV – 3.4.16版本,点击”Sources“进行下载

下载得到如下的压缩包

opencv-3.4.16.zip文件拖拽至虚拟机的home文件夹下:

点击opencv-3.4.16.zip文件,右键,选择“提取到此处

1.2 配置依赖项并编译

编译之前,先来安装OpenCV的依赖项:

rosnoetic@rosnoetic-VirtualBox:~$ sudo add-apt-repository "deb http://security.ubuntu.com/ubuntu xenial-security main"

rosnoetic@rosnoetic-VirtualBox:~$ sudo apt update

rosnoetic@rosnoetic-VirtualBox:~$ sudo apt upgrade

rosnoetic@rosnoetic-VirtualBox:~$ sudo apt-get install build-essential libgtk2.0-dev libvtk6-dev libjpeg-dev libtiff5-dev libjasper-dev libopenexr-dev libtbb-dev libcanberra-gtk-module

事实上,OpenCV的依赖项很多,缺少某些编译项会影响它的部分功能。OpenCV在cmake阶段检查依赖项是否会安装,并调整自己的功能。如果电脑上有GPU并且安装了相关依赖项,OpenCV也会把GPU加速打开。不过当前,上述依赖项已经足够了。

安装完依赖项后进行编译:

rosnoetic@rosnoetic-VirtualBox:~$ cd opencv-3.4.16/

rosnoetic@rosnoetic-VirtualBox:~/opencv-3.4.16$ mkdir build

rosnoetic@rosnoetic-VirtualBox:~/opencv-3.4.16$ cd build/

rosnoetic@rosnoetic-VirtualBox:~/opencv-3.4.16/build$ cmake ..

接着进行编译

rosnoetic@rosnoetic-VirtualBox:~/opencv-3.4.16/build$ make -j4

整个编译过程大概需要二十分钟到一小时不等。

make之后,调用sudo make install将OpenCV安装到电脑上(而不是仅仅编译)。

rosnoetic@rosnoetic-VirtualBox:~/opencv-3.4.16/build$ sudo make install

2. 操作OpenCV图像

2.1 编写undistortImage函数

2.1.1 创建文件夹

通过终端创建一个名为undistortImage的文件夹以保存我们的VSCode项目,在/undistortImage目录下打开vscode

rosnoetic@rosnoetic-VirtualBox:~$ mkdir -p undistortImage

rosnoetic@rosnoetic-VirtualBox:~$ cd undistortImage/

rosnoetic@rosnoetic-VirtualBox:~/undistortImage$ code .

2.1.2 编写源代码

新建文件undistortImage.cpp

undistortImage.cpp粘贴如下代码并保存(Ctrl+S)

// 载入opencv的头文件
#include <opencv2/opencv.hpp>
// 载入string的头文件
#include <string>
// 使用std命名空间
using namespace std;
// 导入畸变图片地址
string image_path = "distorted.png";

// 定义main函数程序主入口
int main(int argc, char argv){
    // 图像畸变参数
    double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05;
    // 相机内参
    double fx = 485.654, fy = 457.296, cx = 367.215, cy = 248.375;
    
    // 使用cv::imread读取image_path地址下的图片文件,并使用cv::Mat进行接收,图像是灰度图
    cv::Mat image = cv::imread(image_path,0);
    
    // 我们使用了cv::Mat类型进行了接收imgae,可以通过rows和cols读取image的行数和列数
    int rows = image.rows, cols = image.cols;
    // 使用cv::Mat创建图像,其中输入行数rows和列数cols,cv_8UC1表示图像为灰度图信息,并使用cv::Mat进行接收
    // 从中我们可以看到cv::Mat既可以定义类型,也可以创建矩阵
    cv::Mat image_undistort = cv::Mat(rows, cols,CV_8UC1);
    

    /*
     接着我们计算去畸变后的图像位置点,这里是一个有意思的点:
    我们分析当前没有畸变的点,经过畸变之后所对应的位置,然后将该位置的点的像素值填充到
    没有畸变的图像位置中
    畸变前的位置点为(x, y),畸变后的点位置为(x_distorted, y_distorted)
    x_distorted = x(1 + k1*r^2 + k2*r^4 + k3*r^6) +p1*( 2*x*y) +p2* (r^2+2x^2)
    y_distorted = y(1+ k1*r^2 + k2*r^4 + k3*r^6) + p1 * (r^2+2y^2) + p2*(2*x*y)
     */

    // 使用for循环进行遍历
    // 在像素坐标系中u对应着列cols,v对应着行,我们在矩阵的遍历时首先遍历行,然后再遍历列
    for (int v = 0;v < rows;v++){
        for (int u = 0;u < cols; u++){
                // 计算u和v对应到图像坐标系中的坐标
                double x = (u - cx) / fx, y = (v - cy) / fy;
                // 计算(x,y)和原点之间的距离
                double r = sqrt(x * x + y * y);
                // 依据公式计算畸变后的(x,y)所在的位置
                double x_distorted = x * (1 + k1 * r * r + k2 * r * r * r * r) + p1 * (2 * 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)+ p2 * (2*x*y);
                // 将畸变后的(x_distorted,y_distorted)重新映射回像素坐标系
                double u_distorted = fx * x_distorted + cx;
                double v_distorted = fy * y_distorted + cy;

                // 接着分析畸变后的点是否超过了图像边界范围,所以需要进行if语句的判断,
                // 如果超过了,则对应位置的像素点为0
                //如果没有超过,则提取畸变图像中(u_distorted,v_distorted)点的像素值将其填充到未畸变图像的(u,v)处
                if (u_distorted < cols && u_distorted >= 0 && v_distorted < rows && v_distorted >= 0){
                    // 由于像素点的坐标是整型,因此需要通过(int)强制将double类型转化为整型,然后使用at<uchar>读取image的像素值,并将其填充到image_undistorted的(u,v)处
                    image_undistort.at<uchar>(v,u) = image.at<uchar>((int)v_distorted, (int)u_distorted);
                }else{
                    // 将像素值设置为0
                    image_undistort.at<uchar>(v,u) = 0;
                }
        }
    }

    // 绘制畸变去除前后的图像
    // 使用cv::imshow函数展示畸变图像
    cv::imshow("distorted", image);
    // 使用cv::imshow函数展示去除畸变后的图像
    cv::imshow("undistorted",image_undistort);
    // 等待输入回车,即可关闭窗口
    cv::waitKey();

    return 0;
}

2.2 新建CMakeLists.txt文件

新建CMakeLists.txt文件

CMakeLists.txt中添加如下内容:

# 使用cmake之前需要指定cmake的最低版本
cmake_minimum_required(VERSION 2.8)

# 为我们的项目文件命名一个名称
project(UNDISTORTIMAGE)

# 使用set设置C++版本CMAKE_CXX_FLAGS,当前指定的时C++11的版本
set(CMAKE_CXX_FLAGS,"-std=c++11")

# 我们的程序文件中使用了opencv的库,因此我们首先需要找到opencv所在的位置
find_package(OpenCV REQUIRED)

# 添加OpenCV的头文件目录OpenCV_INCLUDE_DIRS到我们的头文件目录include_directories中
include_directories(${OpenCV_INCLUDE_DIRS})

# 添加可执行文件undistortImage.cpp,并将其命名为undistortImage
add_executable(undistortImage undistortImage.cpp)

# 将我们的可执行文件undistortImage连接到Opencv的库文件中
# 库文件是为了让用户看不到源代码,而将源文件编译成库文件,库文件是二进制文件
target_link_libraries(undistortImage ${OpenCV_LIBS})

由于程序中使用了C++11标准(如nullptrchrono),因此需要设置编译器set(CMAKE_CXX_FLAGS "-std=c++11")

2.3 cmake编译

ctrl+alt+T打开终端,执行如下指令进行cmake编译

rosnoetic@rosnoetic-VirtualBox:~$ cd undistortImage/

rosnoetic@rosnoetic-VirtualBox:~/undistortImage$ mkdir build

rosnoetic@rosnoetic-VirtualBox:~/undistortImage$ cd build/

rosnoetic@rosnoetic-VirtualBox:~/undistortImage/build$ cmake ..

接着make对工程进行编译

rosnoetic@rosnoetic-VirtualBox:~/undistortImage/build$ make

2.4 运行

distorted.png文件拖拽至undistortImage/build文件夹下

进一步的调用可执行文件:

rosnoetic@rosnoetic-VirtualBox:~/undistortImage/build$ ./undistortImage 

依次显示如下内容:

可以看到原来弯的窗变成了直的窗。

3. 使用opencv自带的去畸变函数

3.1 编写use_undistort函数

新建文件use_undistort.cpp

use_undistort.cpp粘贴如下代码并保存(Ctrl+S)

// 载入opencv的头文件
#include <opencv2/opencv.hpp>
// 载入string的头文件
#include <string>
// 使用std命名空间
using namespace std;
// 导入畸变图片地址
string image_path = "distorted.png";

// 定义main函数程序主入口
int main(int argc, char argv){
    // 图像畸变参数
    double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05;
    // 相机内参
    double fx = 485.654, fy = 457.296, cx = 367.215, cy = 248.375;
    
    // 使用cv::imread读取image_path地址下的图片文件,并使用cv::Mat进行接收,图像是灰度图
    cv::Mat image = cv::imread(image_path,0);
    
    // 我们使用了cv::Mat类型进行了接收imgae,可以通过rows和cols读取image的行数和列数
    int rows = image.rows, cols = image.cols;
    // 使用cv::Mat创建图像,其中输入行数rows和列数cols,cv_8UC1表示图像为灰度图信息,并使用cv::Mat进行接收
    // 从中我们可以看到cv::Mat既可以定义类型,也可以创建矩阵
    cv::Mat image_undistort = cv::Mat(rows, cols,CV_8UC1);
    

    /*
     接着我们计算去畸变后的图像位置点,这里是一个有意思的点:
    我们分析当前没有畸变的点,经过畸变之后所对应的位置,然后将该位置的点的像素值填充到
    没有畸变的图像位置中
    畸变前的位置点为(x, y),畸变后的点位置为(x_distorted, y_distorted)
    x_distorted = x(1 + k1*r^2 + k2*r^4 + k3*r^6) +p1*( 2*x*y) +p2* (r^2+2x^2)
    y_distorted = y(1+ k1*r^2 + k2*r^4 + k3*r^6) + p1 * (r^2+2y^2) + p2*(2*x*y)
     */

    // 使用for循环进行遍历
    // 在像素坐标系中u对应着列cols,v对应着行,我们在矩阵的遍历时首先遍历行,然后再遍历列
    for (int v = 0;v < rows;v++){
        for (int u = 0;u < cols; u++){
                // 计算u和v对应到图像坐标系中的坐标
                double x = (u - cx) / fx, y = (v - cy) / fy;
                // 计算(x,y)和原点之间的距离
                double r = sqrt(x * x + y * y);
                // 依据公式计算畸变后的(x,y)所在的位置
                double x_distorted = x * (1 + k1 * r * r + k2 * r * r * r * r) + p1 * (2 * 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)+ p2 * (2*x*y);
                // 将畸变后的(x_distorted,y_distorted)重新映射回像素坐标系
                double u_distorted = fx * x_distorted + cx;
                double v_distorted = fy * y_distorted + cy;

                // 接着分析畸变后的点是否超过了图像边界范围,所以需要进行if语句的判断,
                // 如果超过了,则对应位置的像素点为0
                //如果没有超过,则提取畸变图像中(u_distorted,v_distorted)点的像素值将其填充到未畸变图像的(u,v)处
                if (u_distorted < cols && u_distorted >= 0 && v_distorted < rows && v_distorted >= 0){
                    // 由于像素点的坐标是整型,因此需要通过(int)强制将double类型转化为整型,然后使用at<uchar>读取image的像素值,并将其填充到image_undistorted的(u,v)处
                    image_undistort.at<uchar>(v,u) = image.at<uchar>((int)v_distorted, (int)u_distorted);
                }else{
                    // 将像素值设置为0
                    image_undistort.at<uchar>(v,u) = 0;
                }
        }
    }

    // 绘制畸变去除前后的图像
    // 使用cv::imshow函数展示畸变图像
    cv::imshow("distorted", image);
    // 使用cv::imshow函数展示去除畸变后的图像
    cv::imshow("undistorted",image_undistort);
    // 等待输入回车,即可关闭窗口
    cv::waitKey();

    return 0;
}

2.2 新建CMakeLists.txt文件

CMakeLists.txt中添加如下内容:

# 添加可执行文件use_undistort.cpp,并将其命名为use_undistort
add_executable(use_undistort use_undistort.cpp)

# 为可执行文件连接到opencv的库文件
target_link_libraries(use_undistort ${OpenCV_LIBS})

2.3 cmake编译

ctrl+alt+T打开终端,执行如下指令进行cmake编译

rosnoetic@rosnoetic-VirtualBox:~$ cd undistortImage/build/

rosnoetic@rosnoetic-VirtualBox:~/undistortImage/build$ cmake ..

接着make对工程进行编译

rosnoetic@rosnoetic-VirtualBox:~/undistortImage/build$ make

2.4 运行

进一步的调用可执行文件:

rosnoetic@rosnoetic-VirtualBox:~/undistortImage/build$ ./use_undistort 

依次显示如下内容:

可以看到原来弯的窗变成了直的窗。

图像畸变的原理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值