视觉SLAM十四讲笔记-5-2

视觉SLAM十四讲笔记-5-2

5.3 实践:计算机中的图像

5.3.1 OpenCV的基本使用用法

参考链接:link
安装 OpenCV,网站:
在Ubuntu下,有两种安装方式:
1.从源代码安装,指从OpenCV网站下载所有的OpenCV源代码,并在机器上编译安装,以便使用。好处是可以选择的版本比较丰富,而且能看到源代码,不过需要编译。还可以调整一些编译选项,匹配编程环境(例如,需不需要GPU加速等),还可以使用一些额外的功能。 源代码安装OpenCV 目前维护了两个主要版本,分为 OpenCV2.4系列和 OpenCV3系列。
2.只安装库文件,指通过Ubuntu来安装由Ubuntu社区人员已经编译好的库文件,这样无须编译。
这里选择从源代码进行安装.
首先安装OpenCV的依赖项:

sudo apt−get install build−essential libgtk2.0−dev libvtk5−dev libjpeg−dev libtiff4−dev libjasper−dev libopenexr−dev libtbb−dev

参考链接:link
去官网下载,官网链接: link,找到对应版本后,点击 sources进行下载,
请添加图片描述
下载下来解压,然后进入文件夹,用下面命令进行安装:

mkdir my_built_file
cd my_built_file
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
sudo make
sudo make install

OpenCV默认存储在/usr/local目录下.安装完后,进行opencv的环境配置:
添加opencv库的路径:

//这里可以利用你熟悉的编辑器
sudo vim /etc/ld.so.conf.d/opencv.conf

添加内容:(文件可以是一个空文件,直接在文件末尾加上内容)

/usr/local/lib

保存文件修改,然后执行命令使配置内容生效

sudo ldconfig  

配置bash

sudo vim /etc/bash.bashrc  

添加内容:

PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig  
export PKG_CONFIG_PATH  

保存文件修改,然后执行命令使配置内容生效

source /etc/bash.bashrc

上述OpenCV安装是从别人博客中借鉴的,自己没有尝试,因为以前已经安装过OpenCV,等下次重新安装时再更新上述安装过程.
操作OpenCV图像
新建文件夹,并在该文件夹下打开 VS Code.

mkdir imageBasics
cd imageBasics/
code .

在该文件夹下新建imageBasics.cpp,CMakeLists.txt和build文件夹
请添加图片描述
在该工程中编写launch.json,tasks.json和CMakeLists.txt
参考链接:link

//launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "g++ - 生成和调试活动文件",
            "type": "cppdbg",
            "request":"launch",
            "program":"${workspaceFolder}/build/imageBasics",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "为 gdb 启动整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "Build",
            "miDebuggerPath": "/usr/bin/gdb"
        }
    ]
}
//tasks.json
{
	"version": "2.0.0",
	"options":{
		"cwd": "${workspaceFolder}/build"   //指明在哪个文件夹下做下面这些指令
	},
	"tasks": [
		{
			"type": "shell",
			"label": "cmake",   //label就是这个task的名字,这个task的名字叫cmake
			"command": "cmake", //command就是要执行什么命令,这个task要执行的任务是cmake
			"args":[
				".."
			]
		},
		{
			"label": "make",  //这个task的名字叫make
			"group": {
				"kind": "build",
				"isDefault": true
			},
			"command": "make",  //这个task要执行的任务是make
			"args": [

			]
		},
		{
			"label": "Build",
			"dependsOrder": "sequence", //按列出的顺序执行任务依赖项
			"dependsOn":[				//这个label依赖于上面两个label
				"cmake",
				"make"
			]
		}
	]
}
#CMakeLists.txt
cmake_minimum_required(VERSION 3.0)

project(IMAGEBASICS)

#在g++编译时,添加编译参数,比如-Wall可以输出一些警告信息
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "-std=c++11")

#一定要加上这句话,加上这个生成的可执行文件才是可以Debug的,不然不加或者是Release的话生成的可执行文件是无法进行调试的
set(CMAKE_BUILD_TYPE Debug)

#此工程要调用opencv库,因此需要添加opancv头文件和链接库
#寻找OpenCV库
find_package(OpenCV REQUIRED)

#添加头文件
include_directories(${OpenCV_INCLUDE_DIRS})

add_executable(imageBasics imageBasics.cpp)

#链接OpenCV库
target_link_libraries(imageBasics ${OpenCV_LIBS})

然后开始编写 imageBasics.cpp

#include <iostream>
#include <chrono>
using namespace std;

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

int main(int argc, char **argv)
{
    //读取argv[1]指定的图像
    cv::Mat image;
    image = cv::imread(argv[1]); //cv::imread函数读取指定路径下的图像

    //判断图像文件是否正确读取
    if(image.data == nullptr)
    {
        cout << "文件" << argv[1] << "不存在." << endl;
        return 0;
    }

    //文件顺利读取,首先输出一些基本信息
    cout << "图像宽为" << image.cols << ",高为" << image.rows << ",通道数为" << image.channels() << endl;
    cv::imshow("image", image); //用cv::imshow显示图像
    cv::waitKey(0); //暂停程序,等待一个按键键入

    //判断image的类型
    if(image.type() != CV_8UC1 && image.type() != CV_8UC3)
    {
        //图像类型不符合要求
        cout << "请输入一张彩色图或灰度图." << endl;
        return 0;
    }

    //遍历图像,请注意以下遍历方式也可以使用随机像素访问
    //使用std::chrono给算法计时
    chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
    for(size_t y=0; y<image.rows; ++y)
    {
        //用cv::Mat::ptr获得图像的行指针
        unsigned char *row_ptr = image.ptr<unsigned char>(y); //row_ptr是第y行的头指针
        for(size_t x = 0; x < image.cols; ++x)
        {
            //访问位于x,y处的像素
            unsigned char *data_ptr = &row_ptr[x*image.channels()]; //data_ptr指向待访问的像素数据
            //输出该像素的每个通道,如果是灰度图就只有一个通道
            for(int c = 0; c != image.channels(); ++c)
            {
                unsigned char data = data_ptr[c]; //data为I(x,y)第c个通道的值
            }
        }
    }
    chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
    chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double> > (t2- t1);
    cout << "遍历图像用时:" << time_used.count() << "秒." << endl;


    //关于cv::Mat的拷贝
    //直接赋值并不会拷贝数据
    cv::Mat image_another = image;
    //修改image_another会导致image发生变化
    image_another(cv::Rect(0, 0, 100, 100)).setTo(0); //将左上角100*100的块置0
    cv::imshow("image", image);
    cv::waitKey(0);

    //使用clone函数拷贝数据
    cv::Mat image_clone = image.clone();
    image_clone(cv::Rect(0, 0, 100, 100)).setTo(255);
    cv::imshow("image", image);
    cv::imshow("image_clone", image_clone);
    cv::waitKey(0);

    //对于图像还有很多基本的操作,如剪切,旋转,缩放等,限于篇幅就不一一介绍了,请查看OpenCV官方文档查询每个函数的调用方法
    cv::destroyAllWindows();
    return 0;

}

编译,然后在终端输入:

cd build
./imageBasics ../autumn_oak.jpg(图片路径)

请添加图片描述
请添加图片描述

5.3.2 图像去畸变

在理论部分介绍了径向和切向畸变,这里演示一个去畸变的过程.OpenCV提供了一个去畸变的函数cv::Undistort(),但本例从公式出发计算畸变前后的图像坐标.
新建文件夹,并在该文件夹下打开 VS Code.

mkdir undistortImage
cd undistortImage/
code .

在该文件夹下新建undistortImage.cpp,CMakeLists.txt和build文件夹
这个工程中launch.json,tasks.json,CMakeLists.txt和上个工程保持一致,只需要改一下文件名,可执行文件的名字.因此,直接复制上述工程更改即可.
然后编写undistortImage.cpp

#include <iostream>
#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; //书上p99式5.5
            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 + 2 * x * x); //书上p102式5.12
            double y_distorted = y * (1 + k1 * r * r + k2 * r * r * r * r) + p1 * (r * r + 2 * y * y) + 2 * p2 * x * y;     //书上p102式5.12
            double u_distorted = fx * x_distorted + cx;                                                                     //书上p102式5.13
            double v_distorted = fy * y_distorted + cy;                                                                     //书上p102式5.13

            // 赋值 (最近邻插值)
            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); //将image(v_distorted,u_distorted)处的颜色信息赋值到image_undistort(v,u)处
            }
            else
            {
                image_undistort.at<uchar>(v, u) = 0;
            }
        }
    }
    //画出去畸变后图像
    cv::imshow("distorted", image);
    cv::imshow("undistorted", image_undistort);
    cv::waitKey();
    return 0;
}

运行结果:
请添加图片描述

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值