视觉SLAM十四讲-高翔 第5讲 相机和图像

相机和图像

5.1 相机模型

我们使用针孔和畸变两个模型来描述整个投影过程,这两个模型能够把外部的三维点投影到相机内部成像平面,构成了相机的内参数

5.1.1 针孔相机模型

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
按照习惯 Z Z Z移到左侧
在这里插入图片描述
我们把中间的量组成的矩阵称为相机的内参数矩阵(Camera Intrinsics)K
我们使用的是P 在相机坐标系下的坐标。由于相机在运动,所以P 的相机坐标应该是它的世界坐标(记为 P w \bm{P_w} Pw),根据相机的当前位姿,变换到相机坐标系下的结果。相机的位姿由它的旋转矩阵 R \bm{R} R 和平移向量 t \bm{t} t 来描述。那么有:
在这里插入图片描述
上式两侧都是齐次坐标。因为齐次坐标乘上非零常数后表达同样的含义,所以可以简单地把 Z Z Z 去掉: P u v = K T P w \bm{P_{uv}=KTP_w} Puv=KTPw
在这里插入图片描述

5.1.2 畸变

由透镜形状引起的畸变称之为径向畸变。它们主要分为两大类,桶形畸变和枕形畸变。
在这里插入图片描述
桶形畸变是由于图像放大率随着离光轴的距离增加而减小,而枕形畸变却恰好相反。在这两种畸变中,穿过图像中心和光轴有交点的直线还能保持形状不变。
除了透镜的形状会引入径向畸变外,在相机的组装过程中由于不能使得透镜和成像面严格平行也会引入切向畸变。
在这里插入图片描述
径向畸变可看成坐标点沿着长度方向发生了变化 , 也就是其距离原点的长度发生了变化。切向畸变可以看成坐标点沿着切线方向发生了变化,也就是水平夹角发生了变化 δ θ \delta \theta δθ

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注:因为项目不需要,暂不记录双目相机和RGB-D相机模型

2 图像

2.1 计算机中图像的表示

传统像素坐标系的定义方式。
在这里插入图片描述

3 实践:图像的存取与访问

3.1 安装OpenCV

参考此链接:

ubuntu20 安装OpenCV(c++版)

3.2 操作OpenCV图像

imageBasic.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 ) // 数据不存在,可能是文件不存在
	{
		cerr<<"文件"<<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++ )
	{
		for ( size_t x=0; x<image.cols; x++ )
		{
			// 访问位于 x,y 处的像素
			// 用 cv::Mat::ptr 获得图像的行指针
			unsigned char* row_ptr = image.ptr<unsigned char> ( y ); // row_ptr 是第 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 的块置零
	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;

}

CMakeLists.txt

cmake_minimum_required(VERSION 3.5) 
project(imageBasic)
find_package(OpenCV REQUIRED) 
include_directories(${OpenCV_INCLUDE_DIRS})
set(CMAKE_CXX_STANDARD 11)
add_executable(imageBasic imageBasic.cpp) 
target_link_libraries(imageBasic ${OpenCV_LIBS}) 

最后运行的时候记得加上图片位置(例如)

./imageBasic "../test1.png"
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玛卡巴卡_qin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值