透视变换--点对应变换

透视变换原理我也不细说,原理可以参考:https://blog.csdn.net/xiaowei_cqu/article/details/26471527

在opencv中只要调两个函数就可以了。
cv::Mat warpMatrix = cv::getPerspectiveTransform(src_pt, dst_pt);
cv::warpPerspective(SrcImg, m_src_correct, warpMatrix, SrcImg.size(), cv::INTER_NEAREST, cv::BORDER_CONSTANT);
src_pt是原来物体的4个点坐标,dst_pt是根据原来的坐标计算得来的,一般是原来坐标的外接矩形。

#include <iostream>
#include <opencv2/opencv.hpp>
bool correct_cjh_3(cv::Point2f pt_tl,cv::Point2f pt_bl,cv::Point2f pt_tr,cv::Point2f pt_br,cv::Mat &SrcImg,cv::Mat &m_MingP,bool b_debug)
{
    //   0   2
    //   1   3
    Mat m_src_correct;
    cv::Point2f src_pt[4];
    cv::Point2f dst_pt[4];
    src_pt[0] = pt_tl;
    src_pt[1] = pt_bl;
    src_pt[2] = pt_tr;
    src_pt[3] = pt_br;
    int T_1 = 0;
    int T_2 = 0;
    dst_pt[0] = cv::Point(MIN(src_pt[0].x,src_pt[1].x) - T_1,MIN(src_pt[0].y,src_pt[2].y));
    dst_pt[1] = cv::Point(MIN(src_pt[0].x,src_pt[1].x) -T_1,MAX(src_pt[1].y,src_pt[3].y));
    dst_pt[2] = cv::Point(MAX(src_pt[2].x,src_pt[3].x) + T_2,MIN(src_pt[0].y,src_pt[2].y));
    dst_pt[3] = cv::Point(MAX(src_pt[2].x,src_pt[3].x) + T_2,MAX(src_pt[1].y,src_pt[3].y));
    Point ptout_tl(dst_pt[0].x,dst_pt[0].y);
    Point ptout_br(dst_pt[3].x,dst_pt[3].y);
    Rect roi_mingp = Rect(ptout_tl,ptout_br);

    cv::Mat warpMatrix = cv::getPerspectiveTransform(src_pt, dst_pt);
    cout<<"warpMatrix=\n"<<warpMatrix<<endl;
    cv::warpPerspective(SrcImg, m_src_correct, warpMatrix, SrcImg.size(), cv::INTER_NEAREST, cv::BORDER_CONSTANT);
    RoiCorrect(m_src_correct,roi_mingp);
    m_MingP = m_src_correct(roi_mingp).clone();

    Point pt_center = Point(SrcImg.cols/2,SrcImg.rows/2);
    if(b_debug)
    {
        Mat m_show = m_src_correct.clone();
        rectangle(m_show,roi_mingp,cv::Scalar(0,255,255),1);
        Mat SrcImg_cp = SrcImg.clone();
        circle(SrcImg_cp,pt_center,9,Scalar(255,0,0),3);
        cv::namedWindow("SrcImg",cv::WINDOW_NORMAL);
        cv::imshow("SrcImg",SrcImg_cp);
        cv::Mat_<double> mat_pt(3,1);
        mat_pt(0,0) = pt_center.x;
        mat_pt(0,1) = pt_center.y;
        mat_pt(0,2) = 1;
        cout<<"mat_pt==\n"<<mat_pt<<endl;
        Point pt_correct;
        Mat mat_tmp = warpMatrix * mat_pt;
        std::cout<<"mat_tmp=\n"<<mat_tmp<<std::endl;
        double a1 = mat_tmp.at<double>(0,0);
        double a2 = mat_tmp.at<double>(1,0);
        double a3 = mat_tmp.at<double>(2,0);
        cout<<"a1="<<a1<<"   a2="<<a2<<"   a3="<<a3<<endl;
        pt_correct = Point(a1,a2);
        circle(m_show,pt_correct,10,cv::Scalar(0,255,255),8);

        cv::namedWindow("correctPic",cv::WINDOW_NORMAL);
        cv::imshow("correctPic",m_show);
        cv::namedWindow("correctRoi",cv::WINDOW_NORMAL);
        cv::imshow("correctRoi",m_MingP);
        waitKey(0);
    }
    return true;
}

int main() {
    Mat img = imread("/data_2/everyday/0317/bugall_snapshot22.png");
    cv::Point2f pt_tl = Point2f(367,0);
    cv::Point2f pt_tr = Point2f(605,58);
    cv::Point2f pt_bl = Point2f(11,162);
    cv::Point2f pt_br = Point2f(281,351);

    Mat m_roi;
    bool b_debug = true;
    correct_cjh_3(pt_tl,pt_bl,pt_tr, pt_br,img,m_roi,b_debug);
}

一般情况下我们用透视变换到这里就可以了,拿到透视变换后的图继续处理就可以,但是在某些情况下需要点的映射。比如原图的中心点经过透视变换之后该点在哪里?按照下面的矩阵相乘,按理说,点坐标乘以透视变换矩阵就可以了。

但是,实践起来并不是这样的,透视变换矩阵是33的,点坐标是(x,y),为了相乘,点坐标需要补上1,(x,y,1)。因为透视变换是三维里面的变换,需要z坐标。代码中可以看到我把各个矩阵都打印出来了:
warpMatrix=
[2.175396430644075, 4.822550437851066, -786.7332303651573;
-0.5767283475053204, 2.366574943211475, 211.6593035344521;
0.0001578547637398106, 0.004169585228612301, 1]
mat_pt==
[316;
186;
1]
mat_tmp=
[797.6864231586686;
469.5960851601052;
1.825424957863668]
a1=797.686 a2=469.596 a3=1.82542
在变换之后的图上并没有找到我画的变换之后的中心点。应该是超出图像范围了。。。经过一顿乱操作,左乘右乘还是不行。再仔细看看图片,透视变换之后好像被裁剪了一点,感觉是因为裁剪导致坐标对不上了的。。。。后来同事帮忙,弄对了。说需要归一化,让我把得到的点第三个坐标变为1,这样归一化同一个平面。
pt_correct = Point(a1
1.0/a3,a2*1.0/a3);

哈哈,果真出来了!!!
还有个问题,就是已知透视变换之后的图上点,如何知道该点变换之前的坐标?

posted @ 2020-03-17 14:04  无左无右  阅读(1774)  评论(3编辑  收藏  举报