去畸变后的图像四周会出现黑色区域,如果畸变较大,如鱼眼镜头,四周黑色区域将会更大。
opencv中给我们提供了一个函数getOptimalNewCameraMatrix()
,用于去除畸变矫正后图像四周黑色的区域。
该函数根据给定参数alpha计算最优的新相机内参矩阵。alpha=0,则去除所有黑色区域,alpha=1,则保留所有原始图像像素,其他值则得到介于两者之间的效果。
通过该函数,我们得到新的相机内参矩阵,然后利用initUndistortRectifyMap()
即可获得用于remap()
的映射矩阵。
主要步骤
获取内切和外切矩形
通过将整个图像划分9*9网格,然后对每个网格点去畸变。
通过比较这些去畸变后的网格点坐标,确定内切和外切矩形。
源码中提到:Get inscribed and circumscribed rectangles in normalized (independent of camera matrix) coordinates.
这是因为,在调用icvGetRectangles()
获取内切和外切矩形时,R和newCameraMatrix为空。从cvUndistortPoints()
源码可以知道,当R和newCameraMatrix为空,尤其是newCameraMatrix为空时,像素点去畸变后得到的是相机坐标系下的坐标。
static void
icvGetRectangles(const CvMat* cameraMatrix, const CvMat* distCoeffs,
const CvMat* R, const CvMat* newCameraMatrix, CvSize imgSize,
cv::Rect_<float>& inner, cv::Rect_<float>& outer)
{
const int N = 9;
int x, y, k;
cv::Ptr<CvMat> _pts(cvCreateMat(1, N * N, CV_32FC2));
CvPoint2D32f* pts = (CvPoint2D32f*)(_pts->data.ptr);
for (y = k = 0; y < N; y++)
for (x = 0; x < N; x++)
pts[k++] = cvPoint2D32f((float)x * imgSize.width / (N - 1),
(float)y * imgSize.height / (N - 1));
// 由于R和newCameraMatrix为0,因此,去畸变后的坐标为相机坐标系下的坐标,(如 mm)
cvUndistortPoints(_pts, _pts, cameraMatrix, distCoeffs, R, newCameraMatrix);
float iX0 = -FLT_MAX, iX1 = FLT_MAX, iY0 = -FLT_MAX, iY1 = FLT_MAX;
float oX0 = FLT_MAX, oX1 = -FLT_MAX, oY0 = FLT_MAX, oY1 = -FLT_MAX;
// find the inscribed rectangle.
// the code will likely not work with extreme rotation matrices (R) (>45%)
for (y = k = 0; y < N; y++)
for (x = 0; x < N; x++)
{
CvPoint2D32f p = pts[k++];
oX0 = MIN(oX0, p.x);
oX1 = MAX(oX1, p.x);
oY0