图像投影模型:针孔[fx, fy, cx, cy]
图像畸变模型:切向径向畸变[k1, k2, p1, p2]
说明:用于备忘
- 第一部分是常规的去畸变操作,在已知内参的情况下对鱼眼相机进行去畸变,这里使用的是remap映射
- 在对图像去畸变后,对于图像边缘的像素全部造成了丢失,所以进行了一次没有意义的尝试,想尽可能的保留图像边缘像素,实现后发现边缘像素部分比较模糊而且还对中心位置的去畸变效果造成了影响
图像来源和参考链接:https://github.com/HLearning/fisheye
1.第一部分:常规去畸变操作
因为只是一个简单验证,所以将内参、图像加载路径和保存路径都写在了代码里面
代码片段:
cv::Mat K = (cv::Mat_<double>(3, 3) << 652.8609862494474, 0.0, 1262.1021584894233,
0.0, 653.1909758659955, 928.0871455436396,
0.0, 0.0, 1.0);
cv::Mat D = (cv::Mat_<double>(4, 1) << -0.024092199861108887, 0.002745976275100771, 0.002545415522352827, -0.0014366825722748522);
cv::Mat raw_image = cv::imread("../data/pig.jpg");
cout << raw_image.cols << " " << raw_image.rows << endl;
int width = raw_image.cols;
int height = raw_image.rows;
cv::Mat map1, map2;
cv::Mat undistortImg;
cv::Size imageSize(width, height);
cv::fisheye::initUndistortRectifyMap(K, D, cv::Mat(), K, imageSize, CV_16SC2, map1, map2);
cv::remap(raw_image, undistortImg, map1, map2, cv::INTER_LINEAR, cv::BORDER_CONSTANT);
cv::imwrite("../data/dst.png", undistortImg);
原图像和结果如下,因为remap
函数要求输入与输出的图像大小一致,所以这个过程造成了信息丢失,边缘部分的小猪崽们都缺少了很多
2.第二部分:保留图像边界信息
思路很简单,既然像素丢失是因为在remap后像素坐标超出了边界,那直接对原图像进行扩边处理再去畸变就好了,图像的高和宽分别为height,width,对图像的上下边界各扩充 1 4 ⋅ h e i g h t \frac{1}{4} \cdot height 41⋅height个像素,对图像左右边界各扩充 1 4 ⋅ w i d t h \frac{1}{4} \cdot width 41⋅width个像素
图像大小改变后,投影内参也要对应的改变,主要指像素坐标远点的偏移距离cx, cy
,上下左右各增加了高和宽的1/4,所以整体上高和宽增加了1/2,即变为原来的1.5倍
扩边函数使用cv::copyMakeBorder
代码片段如下:
// 扩边处理
cv::Mat K_ = (cv::Mat_<double>(3, 3) << 652.8609862494474, 0.0, 1.5 * 1262.1021584894233,
0.0, 653.1909758659955, 1.5 * 928.0871455436396,
0.0, 0.0, 1.0);
cv::Mat D_ = (cv::Mat_<double>(4, 1) << -0.024092199861108887, 0.002745976275100771, 0.002545415522352827, -0.0014366825722748522);
int width_ = raw_image.cols + raw_image.cols/2;
int height_ = raw_image.rows + raw_image.rows/2;
cv::Mat temp(height_, width_, raw_image.type());
cv::copyMakeBorder(raw_image, temp, raw_image.rows/4, raw_image.rows/4, raw_image.cols/4, raw_image.cols/4, cv::BORDER_ISOLATED);
cout << temp.cols << " " << temp.rows << endl;
cv::imwrite("../data/src2.png", temp);
cv::Mat map3, map4;
cv::Mat undistortImg_;
cv::Size imageSize_(width_, height_);
cv::fisheye::initUndistortRectifyMap(K_, D_, cv::Mat(), K_, imageSize_, CV_16SC2, map3, map4);
cv::remap(temp, undistortImg_, map3, map4, cv::INTER_LINEAR, cv::BORDER_CONSTANT);
cv::imwrite("../data/dst2.png", undistortImg_);
结果如下,可以看到图像边界部分的像素保留的更多了:
3.讨论
但是这种思路是错误的,因为在对图像扩边处理后,进行remap的输入图像发生了变化,remap映射要对扩边后的整个图像处理,这样会影响图像中心区域的去畸变效果,如果继续增加扩便的大小,比如将增加的边界由原来高和宽的1/4变为高和宽的1/2,会很明显的发现中心区域的去畸变效果受到影响(两条栏杆变的更弯了),这不是我们想要的。
正确的做法是在remap中做文章,在函数中新建一个大的空白图像,将去畸变后的像素映射到该图像上的对应位置,在remap结束后返回这张图像
4.完整代码
源代码文件fisheye.cpp
#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
// using namespace cv;
int main(int argc, char **argv)
{
cv::Mat K = (cv::Mat_<double>(3, 3) << 652.8609862494474, 0.0, 1262.1021584894233,
0.0, 653.1909758659955, 928.0871455436396,
0.0, 0.0, 1.0);
cv::Mat D = (cv::Mat_<double>(4, 1) << -0.024092199861108887, 0.002745976275100771, 0.002545415522352827, -0.0014366825722748522);
cv::Mat raw_image = cv::imread("../data/pig.jpg");
cout << raw_image.cols << " " << raw_image.rows << endl;
int width = raw_image.cols;
int height = raw_image.rows;
cv::Mat map1, map2;
cv::Mat undistortImg;
cv::Size imageSize(width, height);
cv::fisheye::initUndistortRectifyMap(K, D, cv::Mat(), K, imageSize, CV_16SC2, map1, map2);
cv::remap(raw_image, undistortImg, map1, map2, cv::INTER_LINEAR, cv::BORDER_CONSTANT);
cv::imwrite("../data/dst.png", undistortImg);
// 扩边处理
cv::Mat K_ = (cv::Mat_<double>(3, 3) << 652.8609862494474, 0.0, 1.5 * 1262.1021584894233,
0.0, 653.1909758659955, 1.5 * 928.0871455436396,
0.0, 0.0, 1.0);
cv::Mat D_ = (cv::Mat_<double>(4, 1) << -0.024092199861108887, 0.002745976275100771, 0.002545415522352827, -0.0014366825722748522);
int width_ = raw_image.cols + raw_image.cols/2;
int height_ = raw_image.rows + raw_image.rows/2;
cv::Mat temp(height_, width_, raw_image.type());
cv::copyMakeBorder(raw_image, temp, raw_image.rows/4, raw_image.rows/4, raw_image.cols/4, raw_image.cols/4, cv::BORDER_ISOLATED);
cout << temp.cols << " " << temp.rows << endl;
cv::imwrite("../data/src2.png", temp);
cv::Mat map3, map4;
cv::Mat undistortImg_;
cv::Size imageSize_(width_, height_);
cv::fisheye::initUndistortRectifyMap(K_, D_, cv::Mat(), K_, imageSize_, CV_16SC2, map3, map4);
cv::remap(temp, undistortImg_, map3, map4, cv::INTER_LINEAR, cv::BORDER_CONSTANT);
cv::imwrite("../data/dst2.png", undistortImg_);
return 0;
}
CMakeLists.txt文件
cmake_minimum_required(VERSION 2.8)
project(fisheye_cali)
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(${PROJECT_NAME} fisheye.cpp)
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})