Android 陀螺仪与图像坐标系处理
最近在做项目的时候需要处理陀螺仪和摄像头两个传感器,并将其对齐,在这里记录一下处理过程。仅是个人理解,如有错误,欢迎批评指正。
1 陀螺仪坐标系与图像坐标
首先,Android手机中的陀螺仪的坐标方向如上图所示,在写程序时调用的监听器可以获得到的陀螺仪信息event.value[0]
,event.value[1]
,event.value[2]
分别对应陀螺仪的x,y,z轴。
那么如何与手机拍摄到的图像进行匹配呢。
首先如果我们向上图那样竖着拿手机,拍摄到的图片如果在程序中不进行任何的旋转,则会得到如下方向的图片。
如果将手机横过来:
则拍摄到的是和看到的相同方向的图片:
这表明,从摄像头中获取到的图片就是该方向的图片,对于CMOS摄像头,图像是从上向下进行刷新的。
关于相机的拍摄方向:
-
无论如何摆放手机,拍摄出的画面总是和你看到的是一致的,之所以会出现获取到的图片与你看到的不一致的现象,是因为设备自动将图片旋转了
-
摄像头有一个默认的取景方向,手机一般是横屏放置,也就是竖着拿手机时,面向手机屏幕,摄像头的上边缘与手机边框的右边缘平行,从背面看就是下图:
-
横屏拍摄时,摄像头是正的,所见即所得,而手机竖过来,拍摄到的景物与你看到的是一致的,但是摄像头是以横屏为基准,则默认将其逆时针旋转了90度
-
无论如何,相机的刷新方向都应该看摄像头的方向,也就是如果正放摄像头,则是从上到下一行一行刷新,也就是在手机上显示正确的图片都是从右向左一列一列刷新
为了便于描述,下面以手机横过来时的情况作为基准,可以得到:
其中,蓝色坐标系为手机陀螺仪坐标系,橙色坐标系为我们自己假设的图像坐标系,那么可以得到如下关系:
pic_x = gyro_y;
pic_y = -gyro_x;
pic_z = gyro_z;
在获取陀螺仪角速度时可以将其直接按照上述关系进行保存,方便后续对图像的计算,也就是说,我们成功将陀螺仪的坐标系转化为图像上坐标系。
2 陀螺仪的数据处理
这里假设我们从陀螺仪中读取到的值都是较为精准的(这里暂时不考虑需要滤波的问题)。
对陀螺仪的数据处理分为两部分:
- 将陀螺仪的角速度信息转化为旋转矩阵
- 确保此旋转矩阵描述的是该帧图像(与图像的对齐问题)
2.1 陀螺仪->旋转矩阵
由于陀螺仪获取到的是角速度信息,在实际应用中,我们需要将其转化为可以描述物体旋转的旋转矩阵(这里使用李群so(3))(一定要注意左右手坐标系!!坑了我好久)
首先是李群so(3)的计算,这里放下代码:
cv::Mat ThetaHelper::getRotationMat(cv::Vec<double, 3> theta)
{
cv::Mat cvmat(3, 3, CV_64F);
cv::Mat skew_mat(3, 3, CV_64F);
int eq=0;
double th=theta[0]*theta[0]+theta[1]*theta[1]+theta[2]*theta[2];
th=sqrt(th);
if(th==0)
eq=1;
th=th+eq;
theta[0]=theta[0]/th;
theta[1]=theta[1]/th;
theta[2]=theta[2]/th;
//计算旋转矩阵
skew_mat.at<double>(0,0)=0;
skew_mat.at<double>(0,1)=theta[2];
skew_mat.at<double>(0,2)=-theta[1];
skew_mat.at<double>(1,0)=-theta[2];
skew_mat.at<double>(1,1)=0;
skew_mat.at<double>(1,2)=theta[0];
skew_mat.at<double>(2,0)=theta[1];
skew_mat.at<double>(2,1)=-theta[0];
skew_mat.at<double>(2,2)=0;
cv::Mat e=cv::Mat::eye(3,3,CV_64F);
cvmat =e+sin(th)*skew_mat.t()+(1-cos(th))*(skew_mat*skew_mat).t();
return cvmat;
}
由于需要将获取到的图片通过OpenGL ES渲染到手机屏幕上,而OpenGL ES使用的是右手坐标系ÿ