快速将梯形转换为矩形用于视频

4
我想把梯形区域转换成矩形。 示例图片(不是完美的梯形,但你可以理解): enter image description here 转换为: enter image description here 我已经能够标记梯形区域的角落,并使用getPerspectiveTransform计算正确的矩阵以便使用warpPerspective来转换图像。 不幸的是,这个转换非常缓慢。在一个720p网络摄像头流的大约80%的区域上使用它会导致帧率下降到约5fps。我怀疑这可能是因为warpPerspective允许进行比我需要的更多的转换。 有没有更快的方法将图像从梯形转换为矩形?(最好使用OpenCV) 更多信息:
  • warpAffine 可用于进行仿射变换。它比warpPerspective更快,但(如果我理解正确)你只能改变平行四边形的角度。
  • 有点相关但没有答案:梯形到矩形

一个基于AldurDisciple答案的最小工作示例(不完全有效),使用我帖子中的第一张图片。

#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/opencv.hpp"
using namespace cv;
int main() {

    //Mat img = imread("EQ9in.png");
    Mat img = imread("C:\\ss819729\\Aufnahmen\\arbeiten\\EQ9in.png");
    int height = img.rows;
    int width = img.cols;

    vector<Point2f> corners_rectangle, corners_trapezoid;

    corners_rectangle.push_back(Point2f(0, 0));
    corners_rectangle.push_back(Point2f(img.cols, 0));
    corners_rectangle.push_back(Point2f(img.cols, img.rows));
    corners_rectangle.push_back(Point2f(0, img.rows));

    corners_trapezoid.push_back(Point2f(35, 6));
    corners_trapezoid.push_back(Point2f(419, 55));
    corners_trapezoid.push_back(Point2f(404, 44));
    corners_trapezoid.push_back(Point2f(10, 477));

    Mat_<float> H_rectangle_to_trapezoid = cv::getPerspectiveTransform(corners_rectangle, corners_trapezoid);

    cv::Mat_<float> mapx_32f(height, width), mapy_32f(height, width);
    for(int y = 0; y<height; ++y) {
        float *buff_mapx = ((float*) mapx_32f.data)+y*width;
        float *buff_mapy = ((float*) mapy_32f.data)+y*width;
        for(int x = 0; x<width; ++x) {
            cv::Mat_<float> pt(3, 1);
            pt(0) = x;
            pt(1) = y;
            pt(2) = 1;
            pt = H_rectangle_to_trapezoid*pt;
            pt /= pt(2);
            buff_mapx[x] = pt(0);
            buff_mapy[x] = pt(1);
        }
    }
    cv::Mat map1_16u, map2_16u;
    cv::convertMaps(mapx_32f, mapy_32f, map1_16u, map2_16u, CV_16SC2);

    cv::Mat img_rectified;
    cv::remap(img, img_rectified, map1_16u, map2_16u, cv::INTER_LINEAR);

    namedWindow("Rectangle Image", CV_WINDOW_AUTOSIZE);
    while(waitKey(1)!='q') {
        cv::remap(img, img_rectified, map1_16u, map2_16u, cv::INTER_LINEAR);
        imshow("Rectangle Image", img_rectified);
    }
    return 1;
}

1
摄像机的位置和变形是否恒定不变,还是需要每帧重新计算? - spoorcc
这是一个常量。我曾考虑过进行某种静态映射,但不知道如何实现。我的C++知识很基础。 - Sebastian Schmitz
进行一次初始化,创建转换矩阵并将其存储在cv :: Mat中,在主循环中使用它。如果您提供一些代码或链接,更容易看到任何问题。 - spoorcc
计算已经完成了一次。但是我会构建一个最小工作示例。 - Sebastian Schmitz
如果质量不是很重要,您可以在任何处理之前将高清图像采样降低。 - baci
1个回答

4
如果变形转换是恒定的,那么比每帧使用warpPerspective更快的方法是使用函数remap(文档链接)。可以按照以下方式使用此函数。
首先,在程序开始时,计算变换映射,其中包含源图像中每个像素的(x,y)坐标:
cv::Mat_<float> corners_rectangle, corners_trapezoid;
// TODO: fill corners_rectangle and corners_trapezoid
cv::Mat_<float> H_rectangle_to_trapezoid = cv::getPerspectiveTransform(corners_rectangle, corners_trapezoid);
cv::Mat_<float> mapx_32f(height,width), mapy_32f(height,width);
for(int y=0; y<height; ++y)
{
    float *buff_mapx=((float*)mapx_32f.data)+y*width;
    float *buff_mapy=((float*)mapy_32f.data)+y*width;
    for(int x=0; x<width; ++x)
    {
        cv::Mat_<float> pt(3,1);
        pt(0) = x;
        pt(1) = y;
        pt(2) = 1;
        pt = H_rectangle_to_trapezoid*pt;
        pt /= pt(2);
        buff_mapx[x] = pt(0);
        buff_mapy[x] = pt(1);
    }
}
cv::Mat map1_16u,map2_16u;
cv::convertMaps(mapx_32f,mapy_32f,map1_16u,map2_16u,CV_16SC2);
// Keep map1_16u & map2_16u, discard the rest

然后在每一帧中,您只需要使用remap函数进行插值:

cv::Mat img_rectified;
cv::remap(img_src, img_rectified, map1_16u, map2_16u, cv::INTER_LINEAR);

由于坐标转换的计算是离线完成的,因此比重复使用 warpPerspective 要快得多。

看起来非常有前途!只有两个问题:
  1. 如何填充Mat_<float> corners_rectangle?我一直使用vector<Point>。
  2. 我认为mapx应该是mapx_32f。
- Sebastian Schmitz
好的,我已经修正了它,还有填充buff_mapxbuff_mapy时的一个小错误。对于你的第一个问题,你可以像之前调用getPerspectiveTransform一样操作:vector<Point>参数也应该可以工作。 - BConic
只是一个小备注:map1_16u的类型实际上是CV_16SC2,只有map2_16u的类型是CV_16UC1,所以变量名不是100%正确的 ;) - Sebastian Schmitz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接