《视觉 SLAM 十四讲》V2 第 5 讲 相机与图像

文章目录

      • 相机 内参 && 外参
      • 5.1.2 畸变模型
      • 单目相机的成像过程
      • 5.1.3 双目相机模型
      • 5.1.4 RGB-D 相机模型
    • 实践
      • 5.3.1 OpenCV 基础操作 【Code】
        • OpenCV版本查看
      • 5.3.2 图像去畸变 【Code】
      • 5.4.1 双目视觉 视差图 点云 【Code】
      • 5.4.2 RGB-D 点云 拼合成 地图【Code】
    • 习题
      • 题1
      • √ 题2
      • 题3
      • √ 题4
      • 题5
      • 题6
      • 题7

在这里插入图片描述

空间点 投影到 相机成像平面

前面内容总结:
1、机器人如何表示自身位姿

视觉SLAM: 观测主要是指 相机成像 的过程

投影过程描述: 针孔 + 畸变

相机 内参 && 外参

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

像素坐标系 与 成像平面之间,相差了一个缩放 和一个原点的平移。

像素坐标系:
原点 o ′ o^{\prime} o 位于 图像 左上角
u u u 轴 向右 与 x x x 轴 平行
v v v 轴 向下 与 y y y 轴 平行

设像素坐标在 u u u 轴 上缩放了 α \alpha α 倍 , 在 v v v 轴 上缩放了 β \beta β 倍。同时原点 平移了 [ c x , c y ] T [c_x, c_y]^T [cx,cy]T
则 点 p ′ p^{\prime} p 的坐标 与像素坐标 [ u , v ] T [u, v]^T [u,v]T 之间的关系
{ u = α X ′ + c x = 由式 5.2 α ⋅ f X Z + c x = 令 f x = α f f x X Z + c x v = β Y ′ + c y = 由式 5.2 β ⋅ f Y Z + c x = 令 f y = β f f y Y Z + c y \begin{equation*} \begin{cases} u = \alpha X^{\prime} + c_x \overset{由式5.2}{=} \alpha ·f\frac{X}{Z} + c_x \overset{令f_x = \alpha f}{=} f_x\frac{X}{Z} + c_x \\ v = \beta Y^{\prime} + c_y \overset{由式5.2}{=} \beta·f\frac{Y}{Z} + c_x \overset{令f_y = \beta f}{=} f_y\frac{Y}{Z} + c_y \end{cases} \end{equation*} {u=αX+cx=由式5.2αfZX+cx=fx=αffxZX+cxv=βY+cy=由式5.2βfZY+cx=fy=βffyZY+cy

其中 f x = α f , f y = β f f_x = \alpha f, f_y=\beta f fx=αf,fy=βf
f f f 的单位 为
α , β \alpha, \beta α,β 的单位为 像素/米
f x , f y f_x, f_y fx,fy c x , c y c_x, c_y cx,cy 的单位为 像素

[ u v 1 ] = [ f x 0 c x 0 f y c y 0 0 1 ] [ X Z Y Z 1 ] = 1 Z [ f x 0 c x 0 f y c y 0 0 1 ] [ X Y Z ] = d e f 1 Z K P \begin{align*}\begin{bmatrix}u\\ v\\ 1\end{bmatrix} &=\begin{bmatrix}f_x & 0 & c_x\\ 0 & f_y & c_y\\ 0 & 0 &1\end{bmatrix}\begin{bmatrix}\frac{X}{Z}\\ \frac{Y}{Z}\\ 1\end{bmatrix}\\ &=\frac{1}{Z}\begin{bmatrix}f_x & 0 & c_x\\ 0 & f_y & c_y\\ 0 & 0 &1\end{bmatrix}\begin{bmatrix}X\\ Y\\ Z\end{bmatrix}\\ &\overset{\mathrm{def}}{=} \frac{1}{Z}\bm{KP} \end{align*} uv1 = fx000fy0cxcy1 ZXZY1 =Z1 fx000fy0cxcy1 XYZ =defZ1KP

相机的内参数(Camera Intrinsics) 矩阵 K \bm{K} K

K = [ f x 0 c x 0 f y c y 0 0 1 ] \bm{K} = \begin{bmatrix}f_x & 0 & c_x\\ 0 & f_y & c_y\\ 0 & 0 &1\end{bmatrix} K= fx000fy0cxcy1

标定:自己确定相机的内参【相机生产厂商未告知相机内参的情形】

  • 标定算法: 单目棋盘格张正友标定法

相机在运动 ——> P P P 的相机坐标 = 其世界坐标 P w \bm{P_\mathrm{w}} Pw 根据相机位姿转换到 相机坐标系下。

Z P u v = Z [ u v 1 ] = K ( R P w + t ) = K T P w Z\bm{P}_{uv}=Z\begin{bmatrix} u \\v \\1\end{bmatrix}=\bm{K(RP_{\mathrm{w}}+t)=KTP_\mathrm{w}} ZPuv=Z uv1 =K(RPw+t)=KTPw

相机的外参数(Camera Extrinsics):相机的位姿 R \bm{R} R t \bm{t} t

机器人 或 自动驾驶: 外参 = 相机坐标系 到机器人本体坐标系 之间的 变换。

  • 描述 相机安装在什么地方

5.1.2 畸变模型

径向畸变透镜形状引起的畸变(失真)。坐标点 距离原点的长度发生了变化。
在这里插入图片描述

桶形畸变:图像放大率 随着 与光轴之间的距离 增加 而减小
枕型畸变:图像放大率 随着 与光轴之间的距离 增加 而增加。

  • 穿过图像中心和光轴有交点的直线还能保持形状不变。

切向畸变:相机在在组装过程中能使 透镜和成像面 严格平行水平夹角发行了变化。

在这里插入图片描述

通过5个畸变系数( k 1 , k 2 , k 3 , p 1 , p 2 k_1,k_2,k_3,p_1,p_2 k1,k2,k3,p1,p2)找到某个点在像素平面的正确位置:
在这里插入图片描述

单目相机的成像过程

在这里插入图片描述

5.1.3 双目相机模型

在这里插入图片描述
z − f z = b − u L + u R b \frac{z-f}{z}=\frac{b-u_L+u_R}{b} zzf=bbuL+uR
令 d = u L − u R 令d = u_L-u_R d=uLuR 视差

z − f z = b − d b \frac{z-f}{z}=\frac{b-d}{b} zzf=bbd

1 − f z = 1 − d b 1-\frac{f}{z}=1-\frac{d}{b} 1zf=1bd

f z = d b \frac{f}{z}=\frac{d}{b} zf=bd

z = f b d z=\frac{fb}{d} z=dfb

由于计算量的原因,双目深度估计需要使用 GPU 或 FPGA 来实时计算。

5.1.4 RGB-D 相机模型

在这里插入图片描述
在这里插入图片描述
RGB-D 相机: 向探测目标 发射一束 光线(通常是红外光)。

RGB-D 不足:
1、用红外光进行深度测量,容易受到 日光或其他传感器发射的红外光干扰。不能在室外使用。
2、多个RGB-D相机之间也会相互干扰。
3、透射材质因为接收不到反射光,无法测量。

在这里插入图片描述
h h h 对应 行数
w w w 对应 列数

在这里插入图片描述
OpenCV: 通道顺序为 BGR

在这里插入图片描述

Eigen对于固定大小的矩阵使用起来效率更高。

实践

5.3.1 OpenCV 基础操作 【Code】

OpenCV版本查看
python3 -c "import cv2; print(cv2.__version__)"

可能报错

/home/xixi/Downloads/slambook2-master/ch5/basicuse/basicuse.cpp:6:9: fatal error: opencv2/core/core.cpp: No such file or directory6 | #include<opencv2/core/core.cpp>

OpenCV没安装好
gtk/gtk.h报错链接
到 OpenCV 安装包

mkdir build && cd build
cmake ..
make -j4  # 之前 -j8有误,改4试试
sudo make install

——————————————————

mkdir build && cd build 
cmake ..
make 
./basicuse ubuntu.png   ## ubuntu.png 要放在 build文件夹里; 或者提供该图片的绝对路径;或相对于build文件夹的相对路径

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)project(basicuse)# 添加C++ 11 标准支持  nullptr  chrono
set( CMAKE_BUILD_TYPE "Release" )
set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )# 寻找 OpenCV 库
find_package(OpenCV 4.2.0 REQUIRED)
#添加头文件
include_directories(${OpenCV_INCLUDE_DIRS})add_executable(basicuse basicuse.cpp)
# 链接OpenCV库
target_link_libraries(basicuse ${OpenCV_LIBS})

basicuse.cpp

#include<iostream>
#include<chrono> // 计时using namespace std;#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>// high-level graphical user interfaceusing namespace cv;int main(int argc, char **argv){// 读取argv[1] 指定的图像cv::Mat image;image = cv::imread(argv[1]);  // 从命令行的第一个参数中 读取图像位置// 判断图像是否 正确读取if (image.data == nullptr){cerr << "文件" << argv[1] << "不存在。" << endl;return 0; }// 输出文件的基本信息cout << "图像宽为" << image.cols << ",高为" << image.rows<< ", 通道数为" << image.channels()  << endl;cv::imshow("image", image);cv::waitKey(0);  // 暂停程序,等待一个按键输入cv::destroyAllWindows();return 0;
}

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

#include<iostream>
#include<chrono> // 计时using namespace std;#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>// high-level graphical user interfaceusing namespace cv;int main(int argc, char **argv){// 读取argv[1] 指定的图像cv::Mat image;image = cv::imread(argv[1]);  // 从命令行的第一个参数中 读取图像位置// 判断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;return 0;
}

在这里插入图片描述

#include<iostream>
#include<chrono> // 计时using namespace std;#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>// high-level graphical user interfaceusing namespace cv;int main(int argc, char **argv){// 读取argv[1] 指定的图像cv::Mat image;image = cv::imread(argv[1]);  // 从命令行的第一个参数中 读取图像位置// 关于 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;
}

5.3.2 图像去畸变 【Code】

cv::Undistort()

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)project(myOpenCV)# 添加C++ 11 标准支持  nullptr  chrono
set( CMAKE_BUILD_TYPE "Release" )
set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )# 寻找 OpenCV 库
find_package(OpenCV 4.2.0 REQUIRED)
#添加头文件
include_directories(${OpenCV_INCLUDE_DIRS})add_executable(myOpenCV undistortImage.cpp)
# 链接OpenCV库
target_link_libraries(myOpenCV ${OpenCV_LIBS})

undistortImage.cpp

#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_8UC1int 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;
}

在这里插入图片描述

5.4.1 双目视觉 视差图 点云 【Code】

在这里插入图片描述
在这里插入图片描述
CMakeLists.txt

cmake_minimum_required(VERSION 2.8)project(stereoVision)# 添加C++ 11 标准支持  nullptr  chrono
set( CMAKE_BUILD_TYPE "Release" )
set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )# 寻找 OpenCV 库
find_package(OpenCV 4.2.0 REQUIRED)
#添加头文件
include_directories(${OpenCV_INCLUDE_DIRS})find_package(Pangolin REQUIRED)add_executable(stereoVision stereoVision.cpp)
target_link_libraries(stereoVision ${OpenCV_LIBS} ${Pangolin_LIBRARIES})

stereoVision.cpp

#include <opencv2/opencv.hpp>
#include <vector>
#include <string>
#include <Eigen/Core>
#include <pangolin/pangolin.h>
#include <unistd.h>using namespace std;
using namespace Eigen;// 文件路径
string left_file = "../left.png";
string right_file = "../right.png";// 在pangolin中画图,已写好,无需调整
void showPointCloud(const vector<Vector4d, Eigen::aligned_allocator<Vector4d>> &pointcloud);int main(int argc, char **argv) {// 内参double fx = 718.856, fy = 718.856, cx = 607.1928, cy = 185.2157;// 基线double b = 0.573;// 读取图像cv::Mat left = cv::imread(left_file, 0);cv::Mat right = cv::imread(right_file, 0);cv::Ptr<cv::StereoSGBM> sgbm = cv::StereoSGBM::create(0, 96, 9, 8 * 9 * 9, 32 * 9 * 9, 1, 63, 10, 100, 32);    // 神奇的参数cv::Mat disparity_sgbm, disparity;sgbm->compute(left, right, disparity_sgbm);disparity_sgbm.convertTo(disparity, CV_32F, 1.0 / 16.0f);// 生成点云vector<Vector4d, Eigen::aligned_allocator<Vector4d>> pointcloud;// 如果你的机器慢,请把后面的v++和u++改成v+=2, u+=2for (int v = 0; v < left.rows; v++)for (int u = 0; u < left.cols; u++) {if (disparity.at<float>(v, u) <= 0.0 || disparity.at<float>(v, u) >= 96.0) continue;Vector4d point(0, 0, 0, left.at<uchar>(v, u) / 255.0); // 前三维为xyz,第四维为颜色// 根据双目模型计算 point 的位置double x = (u - cx) / fx;double y = (v - cy) / fy;double depth = fx * b / (disparity.at<float>(v, u));point[0] = x * depth;point[1] = y * depth;point[2] = depth;pointcloud.push_back(point);}cv::imshow("disparity", disparity / 96.0);cv::waitKey(0);// 画出点云showPointCloud(pointcloud);return 0;
}void showPointCloud(const vector<Vector4d, Eigen::aligned_allocator<Vector4d>> &pointcloud) {if (pointcloud.empty()) {cerr << "Point cloud is empty!" << endl;return;}pangolin::CreateWindowAndBind("Point Cloud Viewer", 1024, 768);glEnable(GL_DEPTH_TEST);glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);pangolin::OpenGlRenderState s_cam(pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000),pangolin::ModelViewLookAt(0, -0.1, -1.8, 0, 0, 0, 0.0, -1.0, 0.0));pangolin::View &d_cam = pangolin::CreateDisplay().SetBounds(0.0, 1.0, pangolin::Attach::Pix(175), 1.0, -1024.0f / 768.0f).SetHandler(new pangolin::Handler3D(s_cam));while (pangolin::ShouldQuit() == false) {glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);d_cam.Activate(s_cam);glClearColor(1.0f, 1.0f, 1.0f, 1.0f);glPointSize(2);glBegin(GL_POINTS);for (auto &p: pointcloud) {glColor3f(p[3], p[3], p[3]);glVertex3d(p[0], p[1], p[2]);}glEnd();pangolin::FinishFrame();usleep(5000);   // sleep 5 ms}return;
}

视差图:
在这里插入图片描述

byzanz-record -x 147 -y 76 -w 1386 -h 768  -d 15 --delay=5 -c  /home/xixi/myGIF/test.gif

在这里插入图片描述

在这里插入图片描述

5.4.2 RGB-D 点云 拼合成 地图【Code】

通过物理方法 获得 像素深度信息
在这里插入图片描述
在这里插入图片描述

mkdir build && cd build
cmake ..
make 
./joinMap

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)project(joinMap)# 添加C++ 11 标准支持  nullptr  chrono
set( CMAKE_BUILD_TYPE "Release" )
set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )# 寻找 OpenCV 库
find_package(OpenCV 4.2.0 REQUIRED)
#添加头文件
include_directories(${OpenCV_INCLUDE_DIRS})# Sophus 库
find_package(Sophus REQUIRED)
include_directories(${Sophus_INCLUDE_DIRS})#  Pangolin 库
find_package(Pangolin REQUIRED)
include_directories(${Pangolin_INCLUDE_DIRS})add_executable(joinMap joinMap.cpp)
target_link_libraries(joinMap ${OpenCV_LIBS} ${Pangolin_LIBRARIES} ${Sophus_LIBRARIES}) 
# 上面这句 一定要 链接到  Sophus

joinMap.cpp

#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
#include <boost/format.hpp>  // for formating strings
#include <pangolin/pangolin.h>
#include <sophus/se3.h>using namespace Sophus;  // 原代码少了 这句
using namespace std;
typedef vector<Sophus::SE3, Eigen::aligned_allocator<Sophus::SE3>> TrajectoryType;
typedef Eigen::Matrix<double, 6, 1> Vector6d;// 在pangolin中画图,已写好,无需调整
void showPointCloud(const vector<Vector6d, Eigen::aligned_allocator<Vector6d>> &pointcloud);int main(int argc, char **argv) {vector<cv::Mat> colorImgs, depthImgs;    // 彩色图和深度图TrajectoryType poses;         // 相机位姿ifstream fin("../pose.txt");if (!fin) {cerr << "请在有pose.txt的目录下运行此程序" << endl;return 1;}for (int i = 0; i < 5; i++) {boost::format fmt("../%s/%d.%s"); //图像文件格式  // !! 这里的路径也要改colorImgs.push_back(cv::imread((fmt % "color" % (i + 1) % "png").str()));depthImgs.push_back(cv::imread((fmt % "depth" % (i + 1) % "pgm").str(), -1)); // 使用-1读取原始图像double data[7] = {0};for (auto &d:data)fin >> d;Sophus::SE3 pose(Eigen::Quaterniond(data[6], data[3], data[4], data[5]),Eigen::Vector3d(data[0], data[1], data[2]));poses.push_back(pose);}// 计算点云并拼接// 相机内参 double cx = 325.5;double cy = 253.5;double fx = 518.0;double fy = 519.0;double depthScale = 1000.0;vector<Vector6d, Eigen::aligned_allocator<Vector6d>> pointcloud;pointcloud.reserve(1000000);for (int i = 0; i < 5; i++) {cout << "转换图像中: " << i + 1 << endl;cv::Mat color = colorImgs[i];cv::Mat depth = depthImgs[i];Sophus::SE3 T = poses[i];for (int v = 0; v < color.rows; v++)for (int u = 0; u < color.cols; u++) {unsigned int d = depth.ptr<unsigned short>(v)[u]; // 深度值if (d == 0) continue; // 为0表示没有测量到Eigen::Vector3d point;point[2] = double(d) / depthScale;point[0] = (u - cx) * point[2] / fx;point[1] = (v - cy) * point[2] / fy;Eigen::Vector3d pointWorld = T * point;Vector6d p;p.head<3>() = pointWorld;p[5] = color.data[v * color.step + u * color.channels()];   // bluep[4] = color.data[v * color.step + u * color.channels() + 1]; // greenp[3] = color.data[v * color.step + u * color.channels() + 2]; // redpointcloud.push_back(p);}}cout << "点云共有" << pointcloud.size() << "个点." << endl;showPointCloud(pointcloud);return 0;
}void showPointCloud(const vector<Vector6d, Eigen::aligned_allocator<Vector6d>> &pointcloud) {if (pointcloud.empty()) {cerr << "Point cloud is empty!" << endl;return;}pangolin::CreateWindowAndBind("Point Cloud Viewer", 1024, 768);glEnable(GL_DEPTH_TEST);glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);pangolin::OpenGlRenderState s_cam(pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000),pangolin::ModelViewLookAt(0, -0.1, -1.8, 0, 0, 0, 0.0, -1.0, 0.0));pangolin::View &d_cam = pangolin::CreateDisplay().SetBounds(0.0, 1.0, pangolin::Attach::Pix(175), 1.0, -1024.0f / 768.0f).SetHandler(new pangolin::Handler3D(s_cam));while (pangolin::ShouldQuit() == false) {glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);d_cam.Activate(s_cam);glClearColor(1.0f, 1.0f, 1.0f, 1.0f);glPointSize(2);glBegin(GL_POINTS);for (auto &p: pointcloud) {glColor3d(p[3] / 255.0, p[4] / 255.0, p[5] / 255.0);glVertex3d(p[0], p[1], p[2]);}glEnd();pangolin::FinishFrame();usleep(5000);   // sleep 5 ms}return;
}

在这里插入图片描述

在这里插入图片描述

byzanz-record -x 72 -y 64 -w 998 -h 605  -d 15 --delay=5 -c  /home/xixi/myGIF/test.gif

在这里插入图片描述

习题

待做:

  • 找OpenCV里的标定 方法
  • 整理链接里的内容

在这里插入图片描述

题1

相机内参标定

√ 题2

相机内参 K \bm{K} K 的物理意义:可将世界坐标系某点 P P P归一化坐标 转成 像素坐标 P u v = K [ X / Z , Y / Z , 1 ] T \bm{P_{uv}=K}[X/Z,Y/Z, 1]^T Puv=K[X/Z,Y/Z,1]T

图像分辨率指图像中存储的信息量,是每英寸图像内有多少个像素点,分辨率的单位为PPI(Pixels Per Inch),通常叫做像素每英寸
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当分辨率变为原来的两倍时, 显然对于同一位置,以像素为单位的 c x c_x cx c y c_y cy 均变为原来的2倍。而以 像素/每米 为单位的 α \alpha α β \beta β 变成原来的 2 倍。 f f f 不变,则 f x = α f f_x = \alpha f fx=αf f y = β f f_y = \beta f fy=βf 也变为原来的 2 倍。
综上:当相机的分辨率变为原来的2倍时, c x c_x cx c y c_y cy f x f_x fx f y f_y fy 均变为原来的 2 倍。

题3

鱼眼或全景相机 标定
链接1
链接2
————————————

√ 题4

异同:
工业相机常见的曝光方式:
1、全局曝光(Global shutter,也称全局快门、帧曝光

  • 光圈打开时,工业相机中的图像传感器上所有像素点可以在同一时刻曝光,当光圈关闭后,所有像素同时结束曝光,然后输出像素数据。全局曝光的工业相机可以一次拍摄物体的整体图像后再输出,因此在拍摄高速运动物体时图像不会偏移,能够达到无失真的效果。
  • CCD(电荷耦合)元件 为这种曝光 方式

2、卷帘曝光(Rolling shutter,也称卷帘快门、行曝光

  • 采用的是逐行扫描逐行曝光的方式,当上一行的所有像素同时曝光后,下一行的所有像素再同时曝光,直至所有行曝光完成。
  • 当曝光不当或物体移动较快时,会出现部分曝光(partial exposure)、斜坡图形(skew)、晃动(wobble) 等现象。这种Rolling shutter方式拍摄出现的现象,称为“果冻效应”。
  • 大部分CMOS相机使用卷帘快门(rolling shutter)

3、基于卷帘曝光并结合全局曝光优势的全局复位释放曝光(Global Reset Release Shutter,GRR)

优缺点:
Global shutter适用于拍摄高速运动物体;且在光线有明暗变化的时候,Global shutter sensor不会有明暗瑕疵。
Global shutter需要对每个像素都要增加一个存储单元,这样增加了sensor的生产难度以及成本
Rolling Shutter sensor适用于拍摄运动速度相对较的物体或场景,可获得更高的成像信噪比。 Rolling Shutter 在低噪、像素损失、高感、动态范围等有优势。

————————

题5

RGB-D 相机标定
在这里插入图片描述

链接
链接2

题6

遍历图像的方法
链接
链接2

题7

OpenCV官方教程学习
官方文档

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

私有云盘:lamp部署nextcloud+高可用集群

目录 一、实验准备&#xff1a; 二、配置mariadb主从复制 三台主机下载mariadb 1&#xff09;主的操作 2&#xff09;从的操作 3&#xff09;测试数据是否同步 三、配置nfs让web服务挂载 1、安装 2、配置nfs服务器 3、配置web服务的httpd 4、测试 四、web 服务器 配…

使用Jest测试Cesium源码

使用Jest测试Cesium源码 介绍环境Cesium安装Jest安装Jest模块包安装babel安装Jest的VSC插件 测试例子小结 介绍 在使用Cesium时&#xff0c;我们常常需要编写自己的业务代码&#xff0c;其中需要引用Cesium的源码&#xff0c;这样方便调试。此外&#xff0c;目前代码中直接使用…

阿里云对象存储OSS SDK的使用

官方文档 https://help.aliyun.com/zh/oss/developer-reference/java 准备工作 windows安装好JDK&#xff0c;这里使用JDK1.8为例 windows安装好IDEA&#xff0c;这里使用IDEA2022 登录阿里云控制台&#xff0c;通过免费试用OSS或开通OSS 步骤 配置访问凭证 有临时和长期…

机器人中的数值优化|【七】线性搜索牛顿共轭梯度法、可信域牛顿共轭梯度法

机器人中的数值优化|【七】线性搜索牛顿共轭梯度法、可信域牛顿共轭梯度法 Line Search Newton-CG, Trust Region Newton-CG 往期回顾 机器人中的数值优化|【一】数值优化基础 机器人中的数值优化|【二】最速下降法&#xff0c;可行牛顿法的python实现&#xff0c;以Rosenbro…

set和map的封装

目录 介绍 红黑树代码 set insert的迭代器转换问题 为什么会有这样的问题? 如何解决 代码 map 注意点 代码 介绍 set和map的底层都是红黑树,所以我们可以在自己实现的红黑树(简易版)的基础上,进行封装,成为简易的set和map 红黑树代码 #pragma once#include <…

【逐步剖C】-第十一章-动态内存管理

一、为什么要有动态内存管理 从我们平常的学习经历来看&#xff0c;所开辟的数组一般都为固定长度大小的数组&#xff1b;但从很多现实需求来看需要我们开辟一个长度“可变”的数组&#xff0c;即这个数组的大小不能在建立数组时就指定&#xff0c;需要根据某个变量作为标准。…

创建vue3工程

一、新建工程目录E:\vue\projectCode\npm-demo用Visual Studio Code 打开目录 二、点击新建文件夹按钮&#xff0c;新建vue3-01-core文件夹 三、右键vue3-01-core文件夹点击在集成终端中打开 四、初始化项目&#xff0c;输入npm init 一直敲回车直到创建成功如下图 npm init 五…

MATLAB 函数签名器

文章目录 MATLAB 函数签名器注释规范模板参数类型 kind数据格式 type选项的支持 使用可执行程序封装为m函数程序输出 编译待办事项推荐阅读附录 MATLAB 函数签名器 MATLAB 函数签名器 (FUNCSIGN) &#xff0c;在规范注释格式的基础上为函数文件或类文件自动生成函数签名&#…

select完成服务器并发

服务器 #include <myhead.h>#define PORT 4399 //端口号 #define IP "192.168.0.191"//IP地址//键盘输入事件 int keybord_events(fd_set readfds); //客户端交互事件 int cliRcvSnd_events(int , struct sockaddr_in*, fd_set *, int *); //客户端连接事件 …

国庆day5

客户端 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);socket new QTcpSocket(this);//此时&#xff0c;已经向服务器发送连接请求了&#xff0c;如果成功连…

第一百六十四回 如何实现NumberPicker

文章目录 1.概念介绍2.使用方法2.1 NumberPicker2.2 CupertinoPicker 3.示例代码4.内容总结 我们在上一章回中介绍了"如何在任意位置显示PopupMenu"相关的内容&#xff0c;本章回中将介绍如何实现NumberPicker.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1.概…

【重拾C语言】四、循环程序设计(后判断条件循环、先判断条件循环、多重循环;典例:计算平均成绩、打印素数、百钱百鸡问题)

目录 前言 四、循环程序设计 4.1 计算平均成绩——循环程序 4.1.1 后判断条件的循环 a. 语法 b. 典例 4.1.2 先判断条件的循环 a. 语法 b. 典例 4.1.3 for语句 a. 语法 b. 典例 4.2 计算全班每人平均成绩—多重循环 4.2.1 打印100以内素数 4.2.2 百钱百…

System Generator学习——使用 AXI 接口和 IP 集成器

文章目录 前言一、目标二、步骤1、检查 AXI 接口2、使用 System Generator IP 创建一个 Vivado 项目3、创建 IP 集成设计&#xff08;IPI&#xff09;4、实现设计 总结 前言 在本节中&#xff0c;将学习如何使用 System Generator 实现 AXI 接口。将以 IP 目录格式保存设计&am…

【MATLAB-基于直方图优化的图像去雾技术】

【MATLAB-基于直方图优化的图像去雾技术】 1 直方图均衡2 程序实现3 局部直方图处理 1 直方图均衡 直方图是图像的一种统计表达形式。对于一幅灰度图像来说&#xff0c;其灰度统计直方图可以反映该图像中不同灰度级出现的统计情况。一般而言&#xff0c;图像的视觉效果和其直方…

HUAWEI悦盒ec6108v9c 如何刷成海纳思系统(家用低功耗服务器,使用Home Assistant服务)

环境&#xff1a; 1.HW悦盒ec6108v9c一套 2.16G U盘 3.格式化软件USB_format.exe 4.固件 mv100-mdmo1g-usb-flash.zip&#xff08;底层是Ubuntu 20.04系统&#xff09; 5.十字螺丝刀 6.翘片/薄铲子 7.有线网络环境 8.镊子/回形针 问题描述&#xff1a; 最近玩智能家居…

OpenCV项目开发实战--使用最先进的方法“F、B、Alpha Matting”进行图像抠图--提供完整代码

示范 让我们对现实生活中的图像启动 FBA Matting 方法。要应用 FBA Matting 算法,我们首先需要生成一个 trimap(我们稍后会介绍它是什么)。在我们的演示中,我们将使用预训练的DeepLabV3生成分割掩模,其中每个像素属于前景类的概率。之后,我们将使用大量膨胀操作将边界像…

面向无线传感器网络WSN的增强型MODLEACH设计与仿真(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

QT4.8.7安装详细教程

QT4.8.7安装详细教程&#xff08;MinGW 4.8.2和QTCreator4.2.0&#xff09; 1.下载及安装2.配置环境 此文是在下方链接博文的基础上&#xff0c;按自己的理解整理的https://blog.csdn.net/xiaowanzi199009/article/details/104119265 1.下载及安装 这三个文件&#xff0c;顺序是…

微信小程序代驾系统源码(含未编译前端,二开无忧) v2.5

简介&#xff1a; 如今有越来越多的人在网上做代驾&#xff0c;打造一个代驾平台&#xff0c;既可以让司机增加一笔额外的收入&#xff0c;也解决了车主酒后不能开发的问题&#xff0c;代驾系统基于微信小程序开发的代驾系统支持一键下单叫代驾&#xff0c;支持代驾人员保证金…

Elasticsearch安装访问

Elasticsearch 是一个开源的、基于 Lucene 的分布式搜索和分析引擎&#xff0c;设计用于云计算环境中&#xff0c;能够实现实时的、可扩展的搜索、分析和探索全文和结构化数据。它具有高度的可扩展性&#xff0c;可以在短时间内搜索和分析大量数据。 Elasticsearch 不仅仅是一个…