OpenCV通过使用来自cameraCalibrate的内在和外在来获得平面图案的自上而下视图

时间:2017-10-11 03:41:07

标签: opencv camera-calibration

最初我的图像带有完美的圆形网格,表示为 A enter image description here 我添加了一些镜头失真和透视变换,它变成 B enter image description here 在相机校准中, A 将是我的目标图像, B 将是我的源图像。 假设我在两个图像中都有所有圆心坐标,存储在 stdPts disPts 中。

//25 center pts in A
vector<Point2f> stdPts;
for (int i = 0; i <= 4; ++i) {
    for (int j = 0; j <= 4; ++j) {
        stdPts[i * 5 + j].x = 250 + i * 500;
        stdPts[i * 5 + j].y = 200 + j * 400;
    }
}
//25 center pts in B
vector<Point2f> disPts = FindCircleCenter();

我想从输入中生成与 A 一样接近的图像 C B stdPts disPts 。 我试图使用cv :: calibrateCamera生成的内在和外在。这是我的代码:

//prepare object_points and image_points
vector<vector<Point3f>> object_points;
vector<vector<Point2f>> image_points;
object_points.push_back(stdPts);
image_points.push_back(disPts);


//prepare distCoeffs rvecs tvecs
Mat distCoeffs = Mat::zeros(5, 1, CV_64F);
vector<Mat> rvecs;
vector<Mat> tvecs;


//prepare camera matrix
Mat intrinsic = Mat::eye(3, 3, CV_64F);

//solve calibration
calibrateCamera(object_points, image_points, Size(2500,2000), intrinsic, distCoeffs, rvecs, tvecs);


//apply undistortion
string inputName = "../B.jpg";
Mat imgB = imread(imgName);
cvtColor(imgB, imgB, CV_BGR2GRAY)
Mat tempImgC;
undistort(imgB, tempImgC, intrinsic, distCoeffs);


//apply perspective transform
double transData[] = { 0, 0, tvecs[0].at<double>(0), 0, 0,,tvecs[0].at<double>(1), 0, 0,  tvecs[0].at<double>(2) };
Mat translate3x3(3, 3, CV_64F, transData);
Mat rotation3x3;
Rodrigues(rvecs[0], rotation3x3);

Mat transRot3x3(3, 3, CV_64F);
rotation3x3.col(0).copyTo(transRot3x3.col(0));
rotation3x3.col(1).copyTo(transRot3x3.col(1));
translate3x3.col(2).copyTo(transRot3x3.col(2));

Mat imgC;
Mat matPerspective = intrinsic*transRot3x3;
warpPerspective(tempImgC, imgC, matPerspective, Size(2500, 2000));

//write
string outputName = "../C.jpg";
imwrite(outputName, imgC); // A JPG FILE IS BEING SAVED

这是结果图像 C ,它根本不涉及透视变换。 enter image description here

有人可以教我如何恢复 A 吗?感谢。

1 个答案:

答案 0 :(得分:0)

<强>加

好的,伙计们,简单的错误。我之前使用 warpPerspective 来扭曲图像而不是恢复。由于它以这种方式工作,我没有彻底阅读the doc。事实证明,如果要进行恢复,则应设置标志 WARP_INVERSE_MAP 。将函数调用更改为this,就是这样。

warpPerspective(tempImgC, imgC, matPerspective, Size(2500, 2000), WARP_INVERSE_MAP);

以下是新结果图片 C enter image description here

我现在唯一关心的是中间件 tempImgC ,它是 undistort 之后和 warpPerspective 之前的图片。在一些使用不同人工 B 的测试中,此图片可能会成为 B 的放大版本>删除失真。这意味着在外地区域丢失了大量信息。 warpPerspective 没有多少用处。我想也许可以缩小 undistort 中的图像并在 warpPerspective 中进行缩放。但我不确定如何计算正确的比例以保留 B 中的所有信息。

已添加2

最后一块拼图已经到位。在 undistort 之前调用 getOptimalNewCameraMatrix 以生成保留 B 中所有信息的新相机矩阵。并将此新相机矩阵传递给 undistort warpPerspective。

Mat newIntrinsic=getOptimalNewCameraMatrix(intrinsic, distCoeffs, Size(2500, 2000), 1);
undistort(imgB, tempImgC, intrinsic, distCoeffs, newIntrinsic);
Mat matPerspective = newIntrinsic*transRot3x3;
warpPerspective(tempImgC, imgC, matPerspective, Size(2500, 2000), WARP_INVERSE_MAP);

在这种情况下,结果图像 C 是相同的。但其他案例则有很大差异。例如,使用另一个扭曲的图像 B1 enter image description here 没有新相机矩阵的结果图像 C1 如下所示。 enter image description here 使用新相机矩阵的结果图像 C1 可以在 B1 中保留信息 enter image description here

已添加3

我意识到,由于相机捕获的每个帧需要处理和效率很重要,我不能为每个帧使用undistortwarpPerspective。拥有一张地图并为每一帧使用remap是合理的。

实际上,有一种直接的方式,即projectPoints。由于它直接生成从目标图像到源图像的地图,不需要中间图像,因此可以避免信息丢失。

// ....
//solve calibration

//generate a 3-channel mat with each entry containing it's own coordinates
Mat xyz(2000, 2500, CV_32FC3);
float *pxyz = (float*)xyz.data;
for (int y = 0; y < 2000; y++)
    for (int x = 0; x < 2500; x++)
    {
        *pxyz++ = x;
        *pxyz++ = y;
        *pxyz++ = 0;
    }

// project coordinates of destination image,
// which generates the map from destination image to source image directly
xyz=xyz.reshape(0, 5000000);
Mat mapToSrc(5000000, 1, CV_32FC2);
projectPoints(xyz, rvecs[0], tvecs[0], intrinsic, distCoeffs, mapToSrc);
Mat maps[2];
mapToSrc = mapToSrc.reshape(0, 2000);
split(mapToSrc, maps);

//apply map
remap(imgB, imgC, maps[0], maps[1], INTER_LINEAR);