本文长度为2722字,预计阅读8分钟
前言
原来的文章《C OpenCV透视变换改进---直线拟合的应用》,通过RotatedRect旋转矩形获取到透视变换的4个点,再进行透视变换。结果昨天重新运行程序的时候发现透视变换后的图像坐标点是不对的,图像过完全不一样了。
问题现象
当时的效果
现在的效果
从上面图可以看出,现在运行的透视变换中坐标点整个颠倒了,那就只能一个一个排查原因,通过程序跟踪后发现RotatedRect::points这个函数获取到的点的顺序不一样了。
首先保证代码没有修改过,中间OpenCV应该是升级过4.5.1的版本,由于没留以前的版本源码,所以不好分析是不是这个函数改过。那这里就不考虑源码的事了,直接分析下遇到的情况及怎么解决。
原因分析
微卡智享
在RotatedRect成员函数中,points()函数求矩形的4个顶点;原来4个顶点在图形中的对应关系,可以看下图:
Opencv采用通用的图像坐标系,左上角为原点O(0,0),X轴向右递增,Y轴向下递增,单位为像素。
矩形4个顶点位置的确定,是理解其它各变量的基础,其中p[0]点是关键。
顶点p[0]的位置可以这样理解:
- 如果没有对边与Y轴平行,则Y坐标最大的点为p[0]点,如矩形(2)(3)(4);
- 如果有对边与Y轴平行,则有两个Y坐标最大的点。此时,左侧的点为p[0]点。如矩形(1)。
通俗的说就是RotatedRect的坐标点,Y轴最大的为P[0],p[0]围着center顺时针旋转, 旋转角度为负的话即是P[0]在左下角,为正P[0]是右下角
所以根据这个情况,我们要计算透视变换的点时就要对这个点进行重新排序(左上,右上,右下,左下的顺序),代码如下:
代码语言:javascript复制//重新排序旋转矩形坐标点
void SortRotatedRectPoints(Point2f vetPoints[], RotatedRect rect)
{
rect.points(vetPoints);
cout << vetPoints[0] << vetPoints[1] << vetPoints[2] << vetPoints[3] << endl;
cout << rect.angle << endl;
Point2f curpoint;
//根据Rect的坐标点,Y轴最大的为P[0],p[0]围着center顺时针旋转,
//旋转角度为负的话即是P[0]在左下角,为正P[0]是右下角
//重新排序坐标点
if (rect.angle > 0) {
curpoint = vetPoints[0];
vetPoints[0] = vetPoints[2];
vetPoints[2] = curpoint;
curpoint = vetPoints[1];
vetPoints[1] = vetPoints[3];
vetPoints[3] = curpoint;
}
else if (rect.angle < 0) {
curpoint = vetPoints[0];
vetPoints[0] = vetPoints[1];
vetPoints[1] = vetPoints[2];
vetPoints[2] = vetPoints[3];
vetPoints[3] = curpoint;
}
}
跟踪输出后的结果
从上图中可以看到按原来的原理,P0点应该是左下角,结果输出的P0点为左上角,后来我又换了几张图测试后发现,RotatedRect的坐标点,原来说的是Y轴最大的为P[0],现在实际输出后变为X轴最小的为P[0]。
解决方法
微卡智享
测试后的结果发现这个问题后,那我们就重新修改一下自已的这个SortRotatedRectPoints函数,不改动原来的函数,我们直接重载一个新的同步函数。
代码语言:javascript复制//重新排序旋转矩形坐标点
void SortRotatedRectPoints(Point2f vetPoints[], RotatedRect rect, int flag)
{
rect.points(vetPoints);
cout << vetPoints[0] << vetPoints[1] << vetPoints[2] << vetPoints[3] << endl;
cout << rect.angle << endl;
Point2f curpoint;
if (flag == 0) {
//按X轴排序
for (int i = 0; i < 4; i) {
for (int k = i 1; k < 4; k) {
if (vetPoints[i].x > vetPoints[k].x) {
curpoint = vetPoints[i];
vetPoints[i] = vetPoints[k];
vetPoints[k] = curpoint;
}
}
}
//判断X坐标前两个定义左上左下角
if (vetPoints[0].y > vetPoints[1].y) {
curpoint = vetPoints[0];
vetPoints[0] = vetPoints[1];
vetPoints[1] = vetPoints[3];
vetPoints[3] = curpoint;
}
else {
curpoint = vetPoints[3];
vetPoints[3] = vetPoints[1];
vetPoints[1] = curpoint;
}
//判断X坐标后两个定义右上右下角
if (vetPoints[1].y > vetPoints[2].y) {
curpoint = vetPoints[1];
vetPoints[1] = vetPoints[2];
vetPoints[2] = curpoint;
}
}
else {
//根据Rect的坐标点,Y轴最大的为P[0],p[0]围着center顺时针旋转,
//旋转角度为负的话即是P[0]在左下角,为正P[0]是右下角
//重新排序坐标点
if (rect.angle < 0) {
curpoint = vetPoints[0];
vetPoints[0] = vetPoints[2];
vetPoints[2] = curpoint;
curpoint = vetPoints[1];
vetPoints[1] = vetPoints[3];
vetPoints[3] = curpoint;
}
else if (rect.angle > 0) {
curpoint = vetPoints[0];
vetPoints[0] = vetPoints[1];
vetPoints[1] = vetPoints[2];
vetPoints[2] = vetPoints[3];
vetPoints[3] = curpoint;
}
}
}
原来调用的方法后面再加上一个新的flag为0的参数
最后我们看看运行结果
从上面可以看出,修改后运行起来,透视变换的位置已经正常了。
源码地址
https://github.com/Vaccae/opencvPerspective.git
GitHub上不去的朋友,可以击下方的原文链接跳转到码云的地址,关注【微卡智享】公众号,回复【源码】可以下载我的所有开源项目。
完