透视变换--点对应变换
透视变换原理我也不细说,原理可以参考: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(a11.0/a3,a2*1.0/a3);
哈哈,果真出来了!!!
还有个问题,就是已知透视变换之后的图上点,如何知道该点变换之前的坐标?