本示例是关于图像去畸变的,采用的是投影模型是最一般的针孔相机模型。
不要试图去全面的掌握多个不同抽象层级之间的复杂繁复的细节,理清每一个抽象层级内的逻辑,以及相关抽象层级之间的联系。
—— 著名计算机科学家 达丛明
原理:
本示例中,有原图image,这个cv::Mat我们作为是畸变之后的图像,也就是我们平常用相机实际采集到的图像。
同时我们还有空图像image_undistorted,也就是去畸变之后的图像,我们的目标就是找到去畸变之后的图像和原图图像中像素点的一一对应关系。
注意,像素坐标u和v都是整数,没有浮点数。
怎么找到这种一一对应关系呢,就要利用到相机的畸变原理。
相机畸变分为多种类型,主要有以下几种畸变形式:
1. 径向畸变
又可分为枕型畸变(下图左)和桶型畸变(下图右)。
径向畸变的矫正公式如下:
2. 切向畸变
切向畸变通常是由于制造工艺缺陷导致的镜头与成像平面不平行造成的。
切向畸变根据和上面径向畸变的对比也能发现是和径向垂直的方向上发生的畸变。如下图所示:
切向畸变的公式如下:
对
综上,畸变依赖k1, k2, k3, p1, p2等个参数。
有了畸变原理,就可以获得未畸变的图像和畸变图像之间逐个像素的对应关系。
这里的实际效果也要考虑是何种畸变类型,因为畸变也伴随着未畸变图像的放大或者缩小,这意味着,去畸变后的图像会比原图的尺寸扩大或者缩小以及部分像素的损失。
下面的例子,只是作为一个例子,演示如何找到上文所述的去畸变之后的图像和畸变图像(也就是原图)中的像素一一对应的关系,所采用的去畸变之后的图像尺寸和原图一致。
// Created by ouyangzizhou on 2019/04/28.
// For pinhole cameras
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char** argv)
{
// 相机的内参和畸变参数
double k1 = -0.0454286, k2 = 0.00676248, p1 = -0.00931325, p2 = 0.00221027;
double fx = 678.0467841929283, fy = 677.2937841093414, cx = 961.1753112084883, cy = 640.059204416268;
// 原图,从输入的路径中读取
cv::Mat image = cv::imread(argv[1], 0); // 0代表读取为gray image
// 做一步判断,如果读取结果为不存在,则返回并输出信息
if(image.empty())
{
cerr << "No image " << endl;
return 0;
}
// cout << "Image type is " << image.type() << endl;
int rows = image.rows, cols = image.cols;
cout << "Image size: " << cols << " " << rows << endl;
cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1); //构造一个空图,作为去畸变后的图像
for(int v = 0; v < rows; v++)
for(int u = 0; u < cols; u++)
{
double u_distorted = 0, v_distorted = 0;
// 获得真实的x和y归一化坐标
double x_ud = (u - cx) / fx;
double y_ud = (v - cy) / fy;
// 计算r^2
double r2 = x_ud * x_ud + y_ud * y_ud;
double r4 = r2 * r2;
// 根据畸变参数和公式计算有畸变的x和y的归一化坐标
double x_d = x_ud*(1 + k1*r2 + k2*r4) + 2*p1*x_ud*y_ud + p2*(r2+2*x_ud*x_ud);
double y_d = y_ud*(1+k1*r2+k2*r4)+p1*(r2+2*y_ud*y_ud)+2*p2*x_ud*y_ud;
// 找到了对应关系(u, v) 对应 (u_distorted, v_distorted)
u_distorted = fx*x_d + cx;
v_distorted = fy*y_d + cy;
// 直接插值获取去畸变后图像像素点的像素值
if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows)
{ // image_undistort.at<uchar>(v,u)是行和列,u和v中u对应列,v对应行
image_undistort.at<uchar>(v,u) = image.at<uchar>((int) v_distorted, (int) u_distorted);
}
else
{
image_undistort.at<uchar>(v,u) = 0;
}
}
cv::imwrite("./image Undistorted.jpg", image_undistort);
// cv::waitKey();
return 0;
}
此外,opencv中也提供了相应的API供调用者对图像进行去畸变操作。
见下面的例子: