4.3.2 图像去畸变

news2024/9/21 14:30:34

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 

依次显示如下内容:

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

图像畸变的原理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2042437.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

买新能源怕自燃?法院这判决我举双手赞成

文 | AUTO芯球 作者 | 雷慢 大快人心&#xff01;终于有法院为新能源车主做主了&#xff0c; 你们看啊&#xff0c;某新能源车主开车半路自燃&#xff0c;报了保险&#xff0c; 保险公司赔了18万&#xff0c;转身又去告汽车公司&#xff0c; 汽车公司又被法院判决赔偿保险…

【软件逆向】第2课,软件逆向安全工程师之区分应用32位和64位,每天5分钟学习逆向吧!

目标学习使用StudyPE区分应用 在软件逆向中区分应用类型是关键性的一部分 &#xff0c;只有区分类型后才能选择对应工具进行后续处理。 1.打开StudyPE工具。 2.将我们需要逆向的软件&#xff0c;拖拽到StudyPE中&#xff0c;查看应用信息。 以上用一款视觉AI软件举例&#…

UCOSIII信号量详解

目录 ​编辑 前言 一、信号量的类型 二、信号量的使用方法 2.1创建信号量 2.2请求信号量&#xff1a; 2.3释放信号量&#xff1a; 三、信号量的作用 四、注意事项 五、信号量的API函数 六、代码实现 6.1 创建信号量 6.2 使用信号量 前言 UCOSIII信号量是UCOSIII操作…

【Vue3】路由基础

【Vue3】路由基础 背景简介开发环境开发步骤及源码总结 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的日子。本…

如何用Python求素数之和

计算两个正整数x&#xff0c;y(x<y&#xff0c;包括x&#xff0c;y)素数和。首先通过isPrime函数来判断一个数是否为素数&#xff0c;再使用primeSum函数来返回素数和。 以下为源码&#xff1a; def isPrime(n) :for i in range(2,n):if n % i 0:return 0breakelse:return…

在VB.net中,SortedList有什么方法与属性

标题 在VB.net中&#xff0c;SortedList有什么方法与属性 正文 在VB.NET中&#xff0c;SortedList 类是一个基于键值对的集合&#xff0c;它允许元素按照键的顺序进行排序&#xff0c;并可以通过键或索引来访问元素。SortedList 类是 System.Collections.Generic 命名空间中的一…

物流快递外卖管理平台系统-计算机毕设Java|springboot实战项目

&#x1f34a;作者&#xff1a;计算机毕设残哥 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目、 源…

python绘制蕨菜叶分形

一花一叶一世界,一草一木一浮生. 使用了四个不同的线性变换&#xff0c;根据概率选择其中一个变换并更新 x 和 y 坐标。然后将生成的绿色点绘制出来&#xff0c;形成一片蕨菜叶。 import numpy as np import matplotlib.pyplot as pltdef fern_fractal(num_points):# 初始化坐…

手把手教你接口性能测试之JMeter性能测试篇

一、Jmeter 简介 Jmeter是由Apache公司开发的一个纯Java开源项目&#xff0c;即可以用于做接口测试也可以用于做性能测试&#xff0c;具备高移植性和扩展性&#xff0c;可以实现跨平台运行&#xff0c;可以实现分布式负载。 采用多线程&#xff0c;允许通过多个线程并发取样或…

【c++】 C语言的输入与输出C++的IO流STL空间配置器

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;c_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.C语言的输入与输出 2.流是什么 3.CIO流 3.1 C标准IO流 3.2 C文件IO流 4.stringstream的简单介绍 5.什么是空间配置器 6.为什么需要…

RAG系统优化思路

图片来源&#xff1a;https://huggingface.co/learn/cookbook/zh-CN/rag_evaluation 该图展示了可以从哪些部分去优化RAG。 先摘录一些比较好的思路&#xff1a; https://zhuanlan.zhihu.com/p/681421145 https://www.zhihu.com/question/643138720/answer/3495870046 http…

Spring-data-redis

一、spring-data-redis 介绍 spring-data-jpa spring-data-jdbc spring-data-redis 说明&#xff1a; 在 SpringBoot2.x 之后&#xff0c;原来使用的jedis 被替换为了 lettuce jedis : 采用的直连&#xff0c;多个线程操作的话&#xff0c;是不安全的&#xff0c;如果想要…

vue前后端交互学习问题记录2

1.在使用定时任务时报如下错误&#xff1a;No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton. This is an invalid application configuration. 看报错是ThreadContext未绑定Secur…

Java调用Python的简单运用

这里提供两种调用方法&#xff1a; 1、通过Jython来实现Java调用Python (目前只支持Python2) 2、通过ProcessBuilder来实现Java执行Python脚本&#xff08;既支持Python2也支持Python3&#xff09; 通过Jython来实现Java调用Python Jython目前只支持Python2 一、准备好Pytho…

如何高效记录并整理编程学习笔记

目录 1.概述 1.1. 选择合适的工具 1.2. 分类整理 1.3. 制定标准格式 1.4. 定期复习和更新 1.5. 利用图形和视觉辅助 1.6. 记录问题和解决过程 1.7. 使用版本控制 1.8. 与他人分享和讨论 2.笔记工具选择 2.1. 印象笔记 2.2. 语雀 2.3. 有道云笔记 2.4. 腾讯文档 …

基于Spring Boot的企业员工薪酬关系系统的设计

TOC springboot229基于Spring Boot的企业员工薪酬关系系统的设计 第一章 课题背景及研究内容 1.1 课题背景 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#…

Spring好坑!为什么代理对象的属性没有值?

先看代码&#xff1a; Service Transactional public class ZhouyuService {private String name "zhouyu";public final void test() {System.out.println(name);} }关键点&#xff1a; 加了Transactional&#xff0c;所以ZhouyuService会生成代理对象作为Bean对…

HAProxy理论+实验

目录 一、基于cookie的会话保持 1、配置选项 2、配置示例 3、验证cookie信息 二、IP透传 1、layer4 与 layer7 &#xff08;1&#xff09;四层:IPPORT转发 &#xff08;2&#xff09;七层:协议内容交换 三、haproxy的ACL应用 1、ACL配置选项 &#xff08;1&#xf…

应用案例:劳易测传感器助力宝马集团莱比锡工厂锂电池生产

位于德国萨克森州的宝马集团莱比锡工厂&#xff0c;是全球领先的汽车制造基地之一&#xff0c;不仅生产燃油车&#xff0c;也致力于电动汽车的生产。随着电动汽车及混合动力车辆的普及&#xff0c;锂电池的需求日益增长&#xff0c;宝马集团在莱比锡工厂内部设立了锂电池生产线…

Python之简单了解pylab绘图工具和汇编语言

《Python入门经典以解决计算问题为导向的Python编程实践》89-93页的笔记。 用pylab对数据绘图最小的通用计算 用pylab对数据绘图 PyLab是Matplotlib面向对象绘图库的过程界面。Matplotlib是整个软件包&#xff1b; matplotlib.pyplot是Matplotlib中的一个模块&#xff1b;而P…