c++ - 使用 OpenCV 将帧转换为就像从上方获取的一样

标签 c++ opencv image-processing computer-vision image-rotation

我正在开展一个使用光流技术估算无人机(四轴飞行器)位置的项目。我目前有一个代码正在使用 farneback来自OpenCV的算法。当相机始终指向地面时,当前代码可以正常工作。

现在,我想添加对相机未垂直指向的情况的支持 - 这意味着四轴飞行器现在具有俯仰/滚动/偏航(欧拉角)。四轴飞行器的欧拉角是已知的,我正在寻找一种方法来根据已知的当前欧拉角计算和应用所需的变换。这样结果图像就好像是从上面拍摄的一样(见下图)。

我找到了通过 OpenCV 中的 findHomographygetPerspectiveTransform 函数在具有 4 个角的 2 组(源和目标)时计算变换的方法。但是我找不到任何只知道欧拉角的方法(因为我不知道目标图像核心)。

所以我的问题是我可以使用什么方法以及如何将帧转换为好像是从上方拍摄的,必要时仅使用欧拉角和距地面的相机高度?

为了演示我需要什么:

enter image description here

我当前代码的相关部分如下:

for(;;)
{
    Mat m, disp, warp;
    vector<Point2f> corners;
    // take out frame- still distorted
    cap >> origFrame;
    // undistort the frame using the calibration parameters
    cv::undistort(origFrame, undistortFrame, cameraMatrix, distCoeffs, noArray());
    // lower the process effort by transforming the picture to gray
    cvtColor(undistortFrame, gray, COLOR_BGR2GRAY);

    if( !prevgray.empty() )
    {
        // calculate flow
        calcOpticalFlowFarneback(prevgray, gray, uflow, 0.5, 3/*def 3 */, 10/* def 15*/, 3, 3, 1.2 /* def 1.2*/, 0);
        uflow.copyTo(flow);

        // get average
        calcAvgOpticalFlow(flow, 16, corners);

        // calculate range of view - 2*tan(fov/2)*distance
        rovX = 2*0.44523*distanceSonar*100;     // 2 * tan(48/2) * dist(cm)
        rovY = 2*0.32492*distanceSonar*100;     // 2 * tan(36/2) * dist(cm)

        // calculate final x, y location
        location[0] += (currLocation.x/WIDTH_RES)*rovX;
        location[1] += (currLocation.y/HEIGHT_RES)*rovY;
    }
    //break conditions
    if(waitKey(1)>=0)
        break;
    if(end_run)
        break;
    std::swap(prevgray, gray);
}  

更新:

成功添加旋转后,我仍然需要我的图像居中(并且不要超出框架窗口的范围,如下所示)。我想我需要某种翻译。 我希望源图像的中心位于目标图像的中心。我怎样才能添加这个?

有效的旋转函数:

void rotateFrame(const Mat &input, Mat &output, Mat &A , double roll, double pitch, double yaw){
    Mat Rx = (Mat_<double>(3, 3) <<
              1,          0,           0,
              0, cos(roll), -sin(roll),
              0, sin(roll),  cos(roll));
    Mat Ry = (Mat_<double>(3, 3) <<
              cos(pitch), 0, sin(pitch),
              0, 1,          0,
              -sin(pitch), 0,  cos(pitch));
    Mat Rz = (Mat_<double>(3, 3) <<
              cos(yaw), -sin(yaw), 0,
              sin(yaw),  cos(yaw), 0,
              0,          0,           1);

    Mat R = Rx*Ry*Rz;
    Mat trans = A*R*A.inv();

    warpPerspective(input, output, trans, input.size());
}

当我使用 rotateFrame(origFrame, processedFrame, cameraMatrix, 0, 0, 0); 运行它时,我得到了预期的图像:

enter image description here

但是当我以 10 度滚动运行它时 rotateFrame(origFrame, processedFrame, cameraMatrix, 20*(M_PI/180), 0, 0);。图像超出框架窗口:

enter image description here

最佳答案

如果你有一个 calibration intrinsics matrix A (3x3),并且相机姿势之间没有转换,您需要找到单应性 H (3x3) 到 construct rotation matrix R (3x3) from euler angles并应用以下公式:

H = A * R * A.inv()

其中.inv()是矩阵求逆。

更新:

如果你想让图片居中,你应该这样添加翻译: (这是找到中心的扭曲位置并将该点平移回中心)

|dx|       | 320 / 2 |
|dy| = H * | 240 / 2 |
|1 |       | 1       |

    | 1 0 (320/2-dx) | 
W = | 0 1 (240/2-dy) | * H
    | 0 0 1          |

W 是您的最终转换。

关于c++ - 使用 OpenCV 将帧转换为就像从上方获取的一样,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37117939/

相关文章:

c++ - 这个函数 “a::b::ptr function(value)”调用在C++中如何工作?

c++ - Windows 手机 : re-layout after software keyboard is shown

c++ - 包含文件不工作 C++

Opencv 'undefined reference to ` cv::namedWindow....'(链接错误)

java - 升级到Boofcv最新版本

c++ - 使用 * 的智能指针行为

c++ - OpenCV 功能

c++ - 断言失败CV_Assert(CV_IS_MAT(objectPoints)&& CV_IS_MAT(imagePoints)&& CV_IS_MAT(A)&& CV_IS_MAT(rvec)&& CV_IS_MAT(tvec))

c++ - OpenCV 中的 CV_8UC1 到 CV_32FC1 的转换

java - 通过删除java中的透明像素将图像裁剪为最小尺寸