1. 利用opencv对原始图片进行畸变矫正
原始图片进行畸变矫正主要用到了opencv中的cv::getOptimalNewCameraMatrix函数、cv::fisheye::initUndistortRectifyMap(针孔模型用cv::initUndistortRectifyMap函数)函数以及cv::remap函数。下面详细介绍这三个函数的作用:
1.1 cv::getOptimalNewCameraMatrix函数
参数说明:
Mat cv::getOptimalNewCameraMatrix
( InputArray cameraMatrix, // 相机内参矩阵
InputArray distCoeffs, // 相机畸变参数
Size imageSize, // 图像尺寸
double alpha, // 缩放比例
Size newImgSize = Size(), // 校正后的图像尺寸
Rect * validPixROI = 0, // 输出感兴趣区域设置
bool centerPrincipalPoint = false // 可选标志
)
这个函数的功能是,“Return the new camera matrix based on the free scaling parameter”,即根据根据比例因子返回相应的新的相机内参矩阵。
这里的"比例因子"就是函数的第四个参数(alpha),这个参数的范围是(0, 1)。调节alpha的值能够控制得到的新矩阵中的fx和fy的大小。
当alpha=1的时候,原图像中的所有像素能够得到保留,因此这个时候得到的矫正后的图像是带黑框的,如后面图1所示。
当alpha=0时,得到的图像是不带黑色边框的,相对于原图像,此时的图像损失了部分像素,如后面图2所示。alpha的值控制着具体损失多少像素。
alpha和newImageSize是两个互不干扰的参数,alpha只管是否对图像进行裁剪,而newImageSize只负责把图像进行缩放,这二者都会对返回的新的相机参数造成影响.
1.2 cv::initUndistortRectifyMap函数
参数说明:
void cv::initUndistortRectifyMap
( InputArray cameraMatrix, // 原相机内参矩阵
InputArray distCoeffs, // 原相机畸变参数
InputArray R, // 可选的修正变换矩阵
InputArray newCameraMatrix, // 新相机内参矩阵
Size size, // 去畸变后图像的尺寸
int m1type, // 第一个输出的映射(map1)的类型,CV_32FC1 or CV_16SC2
OutputArray map1, // 第一个输出映射
OutputArray map2 // 第二个输出映射
)
这个函数用于计算原始图像和矫正图像之间的转换关系,将结果以映射的形式表达,映射关系存储在map1和map2中。
在单目相机例子中,newCameraMatrix可以用cv::getOptimalNewCameraMatrix来计算,或者直接与cameraMatrix相等。在双目相机例子中,newCameraMatrix一般是用cv::stereoRectify计算而来的。
cv::initUndistortRectifyMap是针对针孔模型的相机进行矫正的函数,对于鱼眼相机,需要使用cv::fisheye::initUndistortRectifyMap函数获取原始图像和矫正图像之间的转换关系。
1.3 cv::remap函数
函数说明:
void cv::remap
( InputArray src, // 原始图像
OutputArray dst, // 矫正图像
InputArray map1, // 第一个映射
InputArray map2, // 第二个映射
int interpolation, // 插值方式
int borderMode=BORDER_CONSTANT, // 边界模式
const Scalar& borderValue=Scalar() // 边界颜色,默认Scalar()黑色
)
函数功能:把原始图像中某位置的像素映射到矫正后的图像指定位置。
这里的map1和map2就是上面cv::initUndistortRectifyMap()计算出来的结果。
以上就是利用opencv进行原始图像矫正的步骤,除此之外,还可以使用 cv::undistort()函数直接进行原始图片矫正,该函数内部调用了initUndistortRectifyMap和remap函数。
整体代码实现:
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace std;
using namespace cv;
int main()
{
// 读入原始图像
Mat img = imread("fish_eye.jpg");
// 设置相机参数
Mat K = (Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1); // 内参矩阵
Mat D = (Mat_<double>(1, 4) << 0.1, 0.2, 0.3, -0.1); // 畸变系数
// 畸变矫正
Mat mapx, mapy;
Mat new_K = getOptimalNewCameraMatrix(K, D, img.size(), 1, img.size());
initUndistortRectifyMap(K, D, Mat(), new_K, img.size(), CV_32FC1, mapx, mapy);
Mat new_img;
remap(img, new_img, mapx, mapy, INTER_LINEAR);
// 显示矫正后的图像
namedWindow("fish_eye", WINDOW_NORMAL);
namedWindow("corrected", WINDOW_NORMAL);
imshow("fish_eye", img);
imshow("corrected", new_img);
waitKey();
return 0;
}
2 对resize后的图片进行矫正
对resize之后的图片进行畸变矫正,需要对内参矩阵进行处理:
在知道原始相机内参 (fx, 0, cx, 0, fy, cy, 0, 0, 1) 的情况下,可以通过以下公式计算resize后的相机内参 (fx′, 0, cx′, 0, fy′,cy′, 0, 0,1):
其中,w 和 h 是原始图片的宽和高,w′和 h′是resize后图片的宽和高。
实现代码如下:
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
int main(){
input:
// 原始内参矩阵:
cv::Mat intrinsic_matrix_cv = ...;
// 原始畸变系数
cv::Mat dist_coeffs = ...;
// resize后的图片
cv::Mat img_resized = ...;
// resize后的图片的尺寸与原始图像的尺寸的比例关系
double raw_cols_scale = resized_img_cols/static_cast<double>(raw_img_cols);
double raw_rows_scale = resized_img_rows/static_cast<double>(raw_img_rows);
// 矫正后的图像
cv::Mat undistort_img;
cv::Mat mapx;
cv::Mat mapy;
// 依据resize后的图片的尺寸以及原始图像的尺寸的比例关系,计算resize后的图片的内参矩阵:
cv::Mat resized_intrinsic_matrix_cv = cv::Mat::eye(3, 3, CV_64F);
resized_intrinsic_matrix_cv.at<double>(0,0) = intrinsic_matrix_cv.at<double>(0,0) * raw_cols_scale;
resized_intrinsic_matrix_cv.at<double>(1,1) = intrinsic_matrix_cv.at<double>(1,1) * raw_rows_scale;
resized_intrinsic_matrix_cv.at<double>(0,2) = intrinsic_matrix_cv.at<double>(0,2) * raw_cols_scale;
resized_intrinsic_matrix_cv.at<double>(1,2) = intrinsic_matrix_cv.at<double>(1,2) * raw_rows_scale;
// 根据比例因子计算新的内参矩阵
cv::Mat new_intrinsic_matrix_cv = cv::getOptimalNewCameraMatrix(resized_intrinsic_matrix_cv, dist_coeffs, img_resized.size(), 0, img_resized.size());
// 计算resized图像和矫正图像之间的转换关系,将结果以映射的形式表达,映射关系存储在mapx和mapy中。
cv::fisheye::initUndistortRectifyMap(resized_intrinsic_matrix_cv, dist_coeffs, cv::Mat(), new_intrinsic_matrix_cv, img_resized.size(), CV_32F, mapx, mapy);
// 把resized_img图像中某位置的像素映射到矫正后的图像指定位置。
cv::remap(img_resized, undistort_img, mapx, mapy, cv::InterpolationFlags::INTER_LINEAR);
// 显示resize后的图片和畸变矫正后的图片
cv::imshow("Resized image", img_resized);
cv::imshow("Undistorted image", undistort_img);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}