【问题标题】:Bird's eye view perspective transformation from camera calibration opencv python从相机校准opencv python的鸟瞰视角转换
【发布时间】:2018-07-12 13:34:12
【问题描述】:

我正在尝试从相机内在、外在矩阵和失真系数中获得鸟瞰视角变换。

我尝试使用this问题的答案。

使用的图片是来自opencv官方github repo的示例图片left02.jpg

我校准了相机并找到了内在、外在矩阵和失真系数。

我未扭曲图像并找到姿势。检查参数是否正确。

我用来求透视变换矩阵的方程是(参考上面的链接):

Hr = K * R.inv() * K.inv() 其中 R 是旋转矩阵(来自 cv2.Rodrigues()),K 来自 cv2.getoptimalnewcameramatrix()

     [ 1  0  |         ]
Ht = [ 0  1  | -K*C/Cz ]
     [ 0  0  |         ]

C=-R.inv()*T 哪里 T 是来自cv2.solvePnP() 的平移向量 Cz 是 C 向量的第三个分量

所需的转换是:H = Ht * Hr

我用来构造上述等式的代码是:

K = newcameramtx # from cv2.getoptimalnewcameramatrix()
ret,rvec,tvec = cv2.solvePnP(world_points,corners2,K,dist) 
R,_ = cv2.Rodrigues(rvec)
_,R_inv = cv2.invert(R)
_,K_inv = cv2.invert(K)
Hr = np.matmul(K,np.matmul(R_inv,K_inv))
C = np.matmul(-R_inv,tvec)
Cz = C[2]
temp_vector = np.matmul(-K,C/Cz)
Ht = np.identity(3)
for i,val in enumerate(temp_vector):
    Ht[i][2] = val
homography = np.matmul(Ht,Hr)
warped_img =cv2.warpPerspective(img,homography,(img.shape[1],img.shape[0]))
# where img is the above undistored image with visualized pose

生成的扭曲图像不正确。

如果我使用以下代码从单应性中删除翻译

homography = Hr.copy()
warped_img =cv2.warpPerspective(img,homography,(img.shape[1],img.shape[0]))

我收到以下图片

我认为上图显示我的旋转部分是正确的,但我的翻译是错误的。

由于平移矩阵 (Ht) 是增广矩阵,我不确定上述矩阵的构造是否正确。

我特别想从相机校准中找出鸟瞰透视变换。

那么,我该如何修正上述等式,以便获得棋盘图像的完美鸟瞰图

还有谁能解释一下上述 Ht 和 Hr 等式是如何推导出来的?我没有太多接触线性代数,所以这些方程对我来说不是很明显。

更新:

homography = np.matmul(Ht,Hr)
warped_img =cv2.warpPerspective(img,homography,(img.shape[1],img.shape[0]),flags=cv2.WARP_INVERSE_MAP)

cv2.WARP_INVERSE_MAP 标志给了我不同的结果

仍然不是我想要的结果!

【问题讨论】:

  • 您的代码中的R 是什么?校准的外在因素?您需要的是当前姿势的相机和导致鸟瞰图的相机之间的旋转。看看这个tutorial
  • R 是我从 cv2.Rodrigues() 获得的旋转矩阵。 SolvePnP 返回旋转向量,并使用上述函数将其转换为旋转矩阵。而外在矩阵是[R|T]

标签: python opencv matrix camera-calibration perspective


【解决方案1】:

教程中解释了您想要实现的目标:Demo 3: Homography from the camera displacement

您有当前的相机姿势(旋转 + 平移),您可以计算所需的相机姿势,以便从鸟瞰图查看棋盘。

由于棋盘框与相机框不同(请参阅here 了解相机框),允许鸟瞰的相机姿势所需的旋转是:

按照教程,你应该得到一个类似的单应矩阵:

H:
[0.935, -0.337, 40.383;
 -0.116, 0.729, 64.381;
 0.000408, -0.001299, 1]

warpPerspective:

另一个例子:

由于棋盘是平的(对于通用场景,单应性仅对纯旋转相机运动有效),您也可以使用平移:

编辑:源自教程的代码

#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>

using namespace std;
using namespace cv;

namespace
{
enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };

void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners, Pattern patternType = CHESSBOARD)
{
    corners.resize(0);

    switch (patternType)
    {
    case CHESSBOARD:
    case CIRCLES_GRID:
        //! [compute-chessboard-object-points]
        for( int i = 0; i < boardSize.height; i++ )
            for( int j = 0; j < boardSize.width; j++ )
                //To try to center the chessboard frame, we substract the image size
                corners.push_back(Point3f(float((j-boardSize.width/2)*squareSize),
                                          float((i-boardSize.height/2)*squareSize), 0));
        //! [compute-chessboard-object-points]
        break;

    case ASYMMETRIC_CIRCLES_GRID:
        for( int i = 0; i < boardSize.height; i++ )
            for( int j = 0; j < boardSize.width; j++ )
                corners.push_back(Point3f(float((2*j + i % 2)*squareSize),
                                          float(i*squareSize), 0));
        break;

    default:
        CV_Error(Error::StsBadArg, "Unknown pattern type\n");
    }
}

void computeC2MC1(const Mat &R1, const Mat &tvec1, const Mat &R2, const Mat &tvec2,
                  Mat &R_1to2, Mat &tvec_1to2)
{
    //c2Mc1 = c2Mo * oMc1 = c2Mo * c1Mo.inv()
    R_1to2 = R2 * R1.t();
    tvec_1to2 = R2 * (-R1.t()*tvec1) + tvec2;
}
} //namespace

int main()
{
    Mat img = imread("left02.jpg");
    Mat img_corners = img.clone(), img_pose = img.clone(), img_bird_eye_view = img.clone();
    vector<Point2f> corners;
    Size patternSize(9,6);
    bool found = findChessboardCorners(img, patternSize, corners);

    drawChessboardCorners(img_corners, patternSize, corners, found);
    imshow("Chessboard corners detection", img_corners);

    vector<Point3f> objectPoints;
    float squareSize = 2.5e-2;
    calcChessboardCorners(patternSize, squareSize, objectPoints);

    FileStorage fs("left_intrinsics.yml", FileStorage::READ);
    Mat cameraMatrix, distCoeffs;
    fs["camera_matrix"] >> cameraMatrix;
    fs["distortion_coefficients"] >> distCoeffs;

    Mat rvec, tvec;
    solvePnP(objectPoints, corners, cameraMatrix, distCoeffs, rvec, tvec);

    aruco::drawAxis(img_pose, cameraMatrix, distCoeffs, rvec, tvec, 2*squareSize);
    imshow("Pose", img_pose);

    Mat R_desired = (Mat_<double>(3,3) <<
                    0, 1, 0,
                    -1, 0, 0,
                    0, 0, 1);
    Mat R;
    Rodrigues(rvec, R);
    Mat normal = (Mat_<double>(3,1) << 0, 0, 1);
    Mat normal1 = R*normal;
    Mat origin(3, 1, CV_64F, Scalar(0));
    Mat origin1 = R*origin + tvec;
    double d_inv1 = 1.0 / normal1.dot(origin1);
    Mat R_1to2, tvec_1to2;
    Mat tvec_desired = tvec.clone();

    computeC2MC1(R, tvec, R_desired, tvec_desired, R_1to2, tvec_1to2);
    Mat H = R_1to2 + d_inv1 * tvec_1to2*normal1.t();
    H = cameraMatrix * H * cameraMatrix.inv();
    H = H/H.at<double>(2,2);
    std::cout << "H:\n" << H << std::endl;

    warpPerspective(img_pose, img_bird_eye_view, H, img.size());
    Mat compare;
    hconcat(img_pose, img_bird_eye_view, compare);
    imshow("Bird eye view", compare);
    waitKey();

    return 0;
}

【讨论】:

  • 您能再解释一下吗?我的意思是教程对我来说还不是很清楚。本教程使用 2 个棋盘位置。但我想做的是从计算的相机内在和外在的逆透视变换。
  • 能否请您提供一个 Python 示例代码,说明您是如何做到的?谢谢。
  • 外部参数为您提供当前棋盘位置(旋转+平移)的相机姿势。如果您想以鸟瞰图的形式查看棋盘,则意味着更改相机姿势以将棋盘视为鸟瞰图。这只是可能的,因为棋盘是平的,否则只有旋转相机运动对单应变换有效。抱歉,我只有一个 C++ 代码,和教程代码差不多。
  • 一个更简单的解决方案是仅估计 4 个当前棋盘角和 4 个所需棋盘角之间的单应性,以执行“透视校正”。
  • 感谢您的回复。我特别想从相机外部参数中找到解决方案。因此,您的第二个建议不适合该应用程序。正如您提到的,我尝试了本教程但我无法重现您的结果。具体来说,我给出的 R2 作为您给出的矩阵(鸟瞰图的旋转)。我如何选择 tvec2?我让 tvec2 = tvec2 给出了一些结果,但它与您的结果不匹配。您介意分享用于获得上述结果的 C++ 代码吗?谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-29
  • 1970-01-01
相关资源
最近更新 更多