《视觉 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} zz−f=bb−uL+uR
令 d = u L − u R 令d = u_L-u_R 令d=uL−uR 视差
则 z − f z = b − d b \frac{z-f}{z}=\frac{b-d}{b} zz−f=bb−d
1 − f z = 1 − d b 1-\frac{f}{z}=1-\frac{d}{b} 1−zf=1−bd
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官方教程学习
官方文档
相关文章:
《视觉 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】 习题题…...
使用libmodbus库开发modbusTcp从站(支持多个主站连接)
使用libmodbus库开发modbusTcp从站(支持多个主站连接) Chapter1 使用libmodbus库开发modbusTcp从站(支持多个主站连接)rdsmodbusslave.hrdsmodbusslave.cppmain.cpp Chapter1 使用libmodbus库开发modbusTcp从站(支持多个主站连接) 参考链接:…...
GPT系列论文解读:GPT-2
GPT系列 GPT(Generative Pre-trained Transformer)是一系列基于Transformer架构的预训练语言模型,由OpenAI开发。以下是GPT系列的主要模型: GPT:GPT-1是于2018年发布的第一个版本,它使用了12个Transformer…...
(四)激光线扫描-光平面标定
在上一章节,已经实现了对激光线条的中心线提取,并且在最开始已经实现了对相机的标定,那么相机标定的作用是什么呢? 就是将图像二维点和空间三维点之间进行互相转换。 1. 什么是光平面 激光发射器投射出一条线,形成的一个扇形区域平面就是光平面,也叫光刀面,与物体相交…...
妙不可言的Python之旅----(二)
Python基础语法 什么是字面量 字面量:在代码中,被写下来的的固定的值,称之为字面量 常用的值类型 类型 描述 说明 数字(Number) 支持 • 整数(int) • 浮点数(floatÿ…...
cartographer(1)-运行
1.下载数据集 #1.下载数据集: mkdir /home/tang/bagfiles#2.开始二维建图 cd /home/tang/carto_ws/cartographer_detailed_comments_ws/install_isolated/source install_isolated/setup.bash rospack profile #新装的包索引地址存在ros的环境里 roslaunch ca…...
C++:模板进阶与继承
模板进阶与继承 模板进阶1.非类型的模板参数2.模板的特化2.1特化的概念2.2函数模板特化2.3类模板特化2.4全特化和偏特化2.4.1全特化2.4.2偏特化 3.模板的分离编译3.1同文件分离3.2不同文件下分离 继承1.继承的概念和定义1.1继承的概念1.2继承的定义1.2.1定义格式1.2.2继承关系和…...
vue-img-cutter 实现图片裁剪[vue 组件库]
借助 vue-img-cutter 可以在网页端实现图片裁剪功能,最终功能效果如下: 组件 npm 安装 npm install vue-img-cutter2 --save-dev # for vue2 npm install vue-img-cutter3 --save-dev # for vue3vue-img-cutter使用 template模板标签模块,…...
手把手教你从零开始腾讯云服务器部署(连接建站教程)
使用腾讯云服务器搭建网站全流程,包括轻量应用服务器和云服务器CVM建站教程,轻量可以使用应用镜像一键建站,云服务器CVM可以通过安装宝塔面板的方式来搭建网站,腾讯云服务器网txyfwq.com分享使用腾讯云服务器建站教程,…...
微信开放平台第三方开发,实现代小程序备案申请
大家好,我是小悟 微信小程序备案整体流程总共分为五个环节:备案信息填写、平台初审、工信部短信核验、通管局审核和备案成功。 服务商可以代小程序发起备案申请。在申请小程序备案之前,需要确保小程序基本信息已填写完成、小程序至少存在一个…...
设计模式——11. 享元模式
1. 说明 享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在减少系统中相似对象的内存占用或计算开销,通过共享相同的对象来达到节省资源的目的。 享元模式的核心思想是将对象的状态分为内部状态(Intrinsic State)和外部状态(Extrinsic State): 内部状态是对象…...
【LLM】主流大模型体验(文心一言 科大讯飞 字节豆包 百川 阿里通义千问 商汤商量)
note 智谱AI体验百度文心一言体验科大讯飞大模型体验字节豆包百川智能大模型阿里通义千问商汤商量简要分析:仅从测试“老婆饼为啥没有老婆”这个问题的结果来看,chatglm分点作答有条理(但第三点略有逻辑问题);字节豆包…...
CSS小计
1:设置图片随窗缩放 使用百分比 width: 100%;height: 100%; 使用vmin: 将可视区域分为100vmin width: 100vmin;height: 100vmin; 2:设置字体颜色与背景色融合 mix-blend-mode: difference 3: 设置宽度自适应 width:fit-content 4:外边距合并 当两个相领的两个容…...
机器学习:决策树
决策树 决策树是一种基于树形结构的模型,决策树从根节点开始,一步步走到叶子节点(决策),所有的数据最终都会落到叶子节点,既可以做分类也可以做回归。 特征选择 根节点的选择该用哪一个特征呢ÿ…...
xxl-job的原理(2)—调度中心管理注册信息
一、调度中心管理注册信息 1.JobApiController 执行器调用调度中心的url来实现注册、下线、回调等操作;其主要的实现类是JobApiController,调用/api/registry接口注册执行器信息,调用/api/registryRemove接口下线执行器信息,调用…...
小白入门pytorch(二)----神经网络
本文为🔗[小白入门Pytorch]学习记录博客 文章目录 前言一、神经网络的组成部分1.神经元2.神经网络层3.损失函数4.优化器 二、Pytorch构建神经网络中的网络层全连接层2.卷积层3.池化层4.循环神经网络5.转置卷积层6.归一化层7.激活函数层 三、数据加载与预处理1.数据加…...
【进阶C语言】排序函数(qsort)与模拟实现(回调函数的实例)
本章大致内容目录: 1.认识回调函数 2.排序函数qsort 3.模拟实现qsort 回调函数为C语言重要知识点,以函数指针为主要知识;下面介绍回调函数的定义、回调函数的库函数举例即库函数模拟实现。 一、回调函数 1.回调函数定义 回调函数就是一…...
CentOS 7 上编译和安装 SQLite 3.9.0
文章目录 可能报错分析详细安装过程 可能报错分析 报错如下: django.core.exceptions.ImproperlyConfigured: SQLite 3.9.0 or later is required (found 3.7.17). 原因:版本为3.7.太低了,需要升级到3.9.0至少 详细安装过程 1.安装所需的…...
[GXYCTF2019]禁止套娃 无回显 RCE 过滤__FILE__ dirname等
扫除git 通过githack 获取index.php <?php include "flag.php"; echo "flag在哪里呢?<br>"; if(isset($_GET[exp])){if (!preg_match(/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i, $_GET[exp])) {if(; preg_replace(/[a-z,_]\(…...
Springboot使用Aop保存接口请求日志到mysql
1、添加aop依赖 <!-- aop日志 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency> 2、新建接口保存数据库的实体类RequestLog.java package com.example…...
网络安全面试题汇总(附答案)
作为从业多年的网络安全工程师,我深知在面试过程中面试官所关注的重点及考察的技能点。网络安全作为当前信息技术领域中非常重要的一部分,对于每一个从事网络安全工作的人员来说,不仅需要掌握一定的技术能力,更需要具备全面的综合…...
Centos7安装kvm,配置虚拟机网络
1.安装软件包,禁用防火墙(非必须) yum -y install qemu-kvm libvirt virt-install 1)禁用防火墙(非必须) systemctl stop firewalld systemctl disable firewalld 2)禁用NetworkManager syst…...
Javascript文件上传
什么是文件上传 文件上传包含两部分, 一部分是选择文件,包含所有相关的界面交互。一部分是网络传输,通过一个网络请求,将文件的数据携带过去,传递到服务器中,剩下的,在服务器中如何存储…...
golang gin——文件上传(单文件,多文件)
文件上传 单文件上传 从form-data获取文件 package uploadimport ("github.com/gin-gonic/gin""net/http" ) // 单文件上传,多文件上传 func Upload(c *gin.Context) {file, _ : c.FormFile("file") // file为字段名dst : "…...
面试题:Redis和MySQL的事务区别是什么?
大家好,我是小米!今天我要和大家聊聊一个在技术面试中经常被问到的问题:“Redis和MySQL的事务区别是什么?”这个问题看似简单,但实际上涉及到了数据库和缓存两个不同领域的知识,让我们一起来深入了解一下吧…...
Canvas绘图
Canvas绘图 Canvas的意义 随着前端的不断发展,页面特效越来越炫酷,W3C组织也不断退出新的CSS特性:例如各种渐变,瀑布流布局,各种阴影,但是随着需求越来越花哨,W3C表示:我去你妈的&…...
逻辑回归评分卡
文章目录 一、基础知识点(1)逻辑回归表达式(2)sigmoid函数的导数损失函数(Cross-entropy, 交叉熵损失函数)交叉熵求导准确率计算评估指标 二、导入库和数据集导入库读取数据 三、分析与训练四、模型评价ROC曲线KS值再做特征筛选生成报告 五、行为评分卡模型表现总结 一、基础知…...
DPDK系列之三十三DPDK并行机制的底层支持
一、背景介绍 在前面介绍了DPDK中的上层对并行的支持,特别是对多核的支持。但是,大家都知道,再怎么好的设计和架构,再优秀的编码,最终都要落到硬件和固件对整个上层应用的支持。单纯的硬件好处理,一个核不…...
LVGL_基础控件滚轮roller
LVGL_基础控件滚轮roller 1、创建滚轮roller控件 /* 创建一个 lv_roller 部件(对象) */ lv_obj_t * roller lv_roller_create(lv_scr_act()); // 创建一个 lv_roller 部件(对象),他的父对象是活动屏幕对象// 将部件(对象)添加到组,如果设置了默认组,…...
王道考研操作系统——文件管理
磁盘的基础知识 .txt用记事本这个应用程序打开,文件最重要的属性就是文件名了 保护信息:操作系统对系统当中的各个用户进行了分组,不同分组的用户对文件的操作权限是不一样的 文件的逻辑结构就是文件内部的数据/记录应该被怎么组织起来&…...
用java做网站后台怎么样/灰色seo推广
问题:spark中如果有两个DataFrame(或者DataSet),DataFrameA依赖DataFrameB,并且两个DataFrame都进行了cache,将DataFrameB unpersist之后,DataFrameA的cache也会失效,官方解释如下&a…...
wordpress文章页怎么调用网站图片/网站优化排名软件
如何查看 Oracle Solaris Cluster 命令日志的内容/var/cluster/logs/commandlog ASCII 文本文件包含在群集中执行的选定 Oracle Solaris Cluster 命令的记录。一旦设置群集,系统会自动启动对命令的日志记录,并在您关闭群集时结束。在所有已启动并以群集模…...
北京网站建设app开发/手机百度2022年新版本下载
xp登陆时登陆界面隐藏(显示)administrator超级管理员账户 机器上有administrator默认帐户和自己建的帐户a 启动后只显示帐户a或同时显示两个账户 想让administrator显示或隐藏 运行“regedit” 依次展开 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Win…...
郑州装修公司哪家好/郴州seo快速排名
pi acos(-1);double getlen(int n,double r) { return 2.0*r*tan(pi/n);}转载于:https://www.cnblogs.com/shimu/p/5929461.html...
一个网站的制作步骤/文案代写
简介:该软件 适用与 通过安装多个操作系统后,引导混乱。 或者无法引导系统。 该软件适用与如下系统:我实际环境是我在同一台服务器上安装windows 2003/2008 x32/x64四个系统,通过该软件可以恢复引导。 要取的自己下载。windows 20…...
毕业设计怎么做网站/网页制作代码大全
1、floor函数:朝负无穷大方向取整 2、用法说明: y floor(x) 函数将x中元素取整,值y为不大于本身的最小整数。对于复数,分别对实部和虚部取整 3、用法举例 >> a 2*rand(4) a 1.9298 0.9708 1.8315 0.0714…...