智能车图像处理之透视变换

透视变换介绍

透视变换被广泛应用于投影仪器、倒车影像等视觉设备当中。在智能车竞赛中,通过摄像头对赛道信息的采集,在单片机上提取赛道两边的边缘,经过处理后控制小车在赛道边缘内行驶。但由于摄像头与赛道之间存在一定的仰角,使得摄像头采集回来的赛道得两条边缘不再平行,甚至在特殊赛道元素(如长直道、弯道、圆环)出现赛道边缘相交的形况。这使得单片机处理具有一定的难度。 本文拟对摄像头采集回来的图片进行透视变换,在vs中计算出透视变换的矩阵,带入到单片机中处理图像的像素点,使之进行一个视场转换,将世界坐标系转变到摄像头坐标系,从而使摄像头采集回来的赛道边缘趋于平行,简而言之使摄像头直接“俯视”赛道,方便智能车对赛道信息的提取。

一、透视变换原理

透视变换(Perspective Transformation)是指利用透视中心、像点、目标点三点共线的条件,按透视旋转定律使承影面(透视面)绕迹线(透视轴)旋转某一角度,破坏原有的投影光线束,仍能保持承影面上投影几何图形不变的变换。
如下图所示,通过透视变换ABC变换到A’B’C’。

在这里插入图片描述

二、通用公式

经过变换后可得到透视变换的通用公式为:

在这里插入图片描述
变换后的坐标x,y分别为:在这里插入图片描述
展开之后:
在这里插入图片描述
其中,
在这里插入图片描述
为透视变换矩阵。

三、计算透视变换矩阵

(1)在摄像头坐标系取点:
在这里插入图片描述
如上图所示为摄像头采集赛道的图片简图,图片的像素原点在左上角,向右为摄像头坐标系的X轴正方向,向下为摄像头坐标系Y轴正方向。两条对称但非平行的直线为赛道边缘,在赛道边缘从左到右、从下到上分别取两边关于赛道正中间对称的四点A、B、C、D,并求得其坐标值。
在本设计中,在赛道上水平放置两根黑色碳素棒,与赛道边缘交点即为所要选取的点。借助opencv的鼠标事件,即可获得A、B、C、D四点坐标值。

(2)在世界坐标系取点:
在世界坐标系中(赛道平面)确定上述所选四点的位置,如下图所示。以智能车车头中心为坐标原点,向右为X轴正方向,向上为Y轴正方向。通过测量确定A’、B’、C’、D’四点的坐标。
在这里插入图片描述
(3)计算变换矩阵
利用opencv函数库里的getPerspectiveTransform()函数,带入摄像头坐标系选取的四点坐标和和世界坐标系坐标,即可计算出变换矩阵。

四、软件设计

(1)创建MFC应用程序
在这里插入图片描述
①利用鼠标事件确定摄像头坐标系里的点
代码如下:

void on_mouse(int event, int x, int y, int flags, void *ustc)  
{
	  switch (event)
    {
        case EVENT_LBUTTONDOWN://鼠标左键按下
			{
			c_dot[i].x=x;
			c_dot[i].y=y;		
			}
			break;
		case EVENT_LBUTTONUP://鼠标左键弹起
			{
			i++;
			}
        break;
    }

 }
void Mydia::OnBnClickedButton1()
{
	UpdateData(TRUE); 
	src=imread("1.png");
    resize (src, src,Size(length, width));
	namedWindow("取点");
	imshow("取点",src);
	setMouseCallback("取点", on_mouse, (void*)&src);
	waitKey(0); 
	 for(i=0;i<4;i++)//将所取的点坐标显示到控件中
	{
            if(i==0)
			{
			c_1x=c_dot[i].x;
			c_1y=c_dot[i].y;
			UpdateData(FALSE);
			}
			if(i==1)
			{
			c_2x=c_dot[i].x;
			c_2y=c_dot[i].y;
			UpdateData(FALSE); 
			}
			if(i==2)
			{
			c_3x=c_dot[i].x;
			c_3y=c_dot[i].y;
			UpdateData(FALSE); 
			}
			if(i==3)
			{
			c_4x=c_dot[i].x;
			c_4y=c_dot[i].y;
			UpdateData(FALSE); 
			}
	}
}

②透视变换与矩阵输出
代码如下:

void Mydia::OnBnClickedButton3()
{
	UpdateData(TRUE); 
	w_dot[0].x=w_1x+offset_x; //世界坐标系坐标+偏移量
	w_dot[0].y=w_1y+offset_y;
	w_dot[1].x=w_2x+offset_x;
	w_dot[1].y=w_2y+offset_y;
	w_dot[2].x=w_3x+offset_x;
	w_dot[2].y=w_3y+offset_y;
	w_dot[3].x=w_4x+offset_x;
	w_dot[3].y=w_4y+offset_y;
	UpdateData(FALSE); 
	warpPerspective(src, perspective, get_perspective_mat(), 			             cv::Size(length,width),cv::INTER_LINEAR);
}

 Mat get_perspective_mat()
{
       cv::Point2f src_points[] = 
{
		cv::Point2f(c_dot[0].x, c_dot[0].y),
		cv::Point2f(c_dot[1].x, c_dot[1].y),
		cv::Point2f(c_dot[2].x, c_dot[2].y),
		cv::Point2f(c_dot[3].x, c_dot[3].y)
		 };	
		cv::Point2f dst_points[] = 
{
		cv::Point2f(w_dot[0].x, w_dot[0].y),
		cv::Point2f(w_dot[1].x, w_dot[1].y),
		cv::Point2f(w_dot[2].x, w_dot[2].y),
		cv::Point2f(w_dot[3].x, w_dot[3].y)
		 };
		Mat M = getPerspectiveTransform(src_points, dst_points);
return M;//返回变换矩阵
}


void Mydia::OnBnClickedButton4()//显示透视变换矩阵到控件
{
	 ostringstream cout;
    cout << get_perspective_mat();
    string str = cout.str();
    matrix_out = (CString)str.c_str();
    UpdateData (FALSE); 
}

(2)单片机软件设计
代码如下:

#define a11 (-4.205882352941158f)
#define a12 (0.568627450980387f)
#define a13 (192.138188608776f)
#define a21 (0.2585667600373426f)
#define a22 (1.389495798319328f)
#define a23 (-241.9822595704937f)
#define a31 (0.004878618113912095f)
#define a32 (-0.401914098972921f)
#define a33 (1.0f)
#define getx(u,v) (a11*(u)+a12*(v)+a13) 
#define gety(u,v) (a21*(u)+a22*(v)+a23)
#define getw(u,v) (a31*(u)+a32*(v)+a33)

void Pespective(point_t * edge, point_t * edge_fix)
//带入提取出来的两条边线,得到透视变换后的两条边线
{
	float x, y, w;
	for (int16_t i = 0; i < LCDH; i++)
	{
		x = getx((edge[i].x), (edge[i].y));
		y = gety((edge[i].x), (edge[i].y));
		w = getw((edge[i].x), (edge[i].y));
		edge_fix[i].x = x / w;
		edge_fix[i].y = y / w;
	}
}

五、联合调试

1.确定摄像头采集图片的像素大小:
默认值为374 X 240,像素大小决定了在摄像头坐标系取点的坐标值大小。

2.摄像头坐标系取点:
点击在这里插入图片描述
在弹出“取点”的窗口图片中,从左到右、从下到上依次单机鼠标左键进行选取A、B、C、D四个点。选取完毕后关闭“取点”窗口,A、B、C、D四个点的坐标值将会显示到控件中。
在这里插入图片描述
在这里插入图片描述
3.世界坐标系取点:
此步骤由实际测量获得A’、B’、C’、D’坐标,且与世界坐标系的选定有关系。如下图所示,本设计以智能车车头中心位置为原点,经过测量,获得的实际坐标值如下:
在这里插入图片描述
4.逆变换与矩阵输出:
在上述取完点之后,点击
在这里插入图片描述
即完成了透视变换。
点击
在这里插入图片描述
将在其下方显示出变换矩阵。复制后即可直接引用。
在这里插入图片描述
5.图像偏移:
此时点击
在这里插入图片描述
发现变换后的图片中赛道不在中间位置,且只显示一条边缘,还是倒立状态。
在这里插入图片描述
这是因为我们所选取的摄像头坐标系和世界坐标系不一致,所以为了显示透视变换后的效果,还需对透视变换后的图片进行适当偏移。
首先我们在X轴正方向进行平移,使赛道显示在图片中央;
在这里插入图片描述
此时图片还是倒立状态,我们只要对世界坐标系的Y坐标取反,图片就能翻转回来;
在这里插入图片描述
这时在屏幕上并未能看到任何赛道信息。这是由于世界坐标系取反为负后,图像显示在摄像头坐标系Y轴的负方向,我们只要再把图片往摄像头坐标系的Y轴正方向平移,就可以见到逆透视后的赛道效果。
在这里插入图片描述
显示透视变换后的图片只是为了验证透视变换的效果,从图片中可以看出,我们所选取的四个点在赛道两边趋于竖直且相互水平。由于取点的误差以及测量误差可能会导致效果不够理想。只要误差不是太大,完全适用于智能车。

六、透视变换效果对比

(1)直道:
原图像:
原图像
透视变换:
在这里插入图片描述
(2)弯道:
原图像:
在这里插入图片描述
透视变换:
在这里插入图片描述
再将图片往Y轴正方向平移30效果如下:
在这里插入图片描述
(3)圆环:
原图像:
在这里插入图片描述
透视变换:
在这里插入图片描述
再将图片再往Y轴正方向偏移50如下:
在这里插入图片描述
(4)十字路口:
原图像:
在这里插入图片描述
透视变换:
在这里插入图片描述
(5)斑马线与车库:
原图像:
在这里插入图片描述

透视变换:
在这里插入图片描述

总结

这是我处理透视变换的全过程,才识浅薄,多多指教!

  • 31
    点赞
  • 293
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值