OpenCV旋转矩形RotatedRect的Points函数遇到的问题

学更好的别人,

做更好的自己。

——《微卡智享》

f5974c9baa60e75ab1879e46238d1142.png

本文长度为2722,预计阅读8分钟

前言

原来的文章《C++ OpenCV透视变换改进---直线拟合的应用》,通过RotatedRect旋转矩形获取到透视变换的4个点,再进行透视变换。结果昨天重新运行程序的时候发现透视变换后的图像坐标点是不对的,图像过完全不一样了。

b9082b2432934417a3ba666753dae326.png

问题现象

974d84483fc4de43c1dbb4401ffe2240.png

当时的效果

d452f6e1e22a646d627208b35f783ccc.png

现在的效果

从上面图可以看出,现在运行的透视变换中坐标点整个颠倒了,那就只能一个一个排查原因,通过程序跟踪后发现RotatedRect::points这个函数获取到的点的顺序不一样了。

首先保证代码没有修改过,中间OpenCV应该是升级过4.5.1的版本,由于没留以前的版本源码,所以不好分析是不是这个函数改过。那这里就不考虑源码的事了,直接分析下遇到的情况及怎么解决。

原因分析

cbcba876e9cfffcf7907256720f67445.png

微卡智享

在RotatedRect成员函数中,points()函数求矩形的4个顶点;原来4个顶点在图形中的对应关系,可以看下图:

7d6753d34ec5416c130a3e7d28a86276.png

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]是右下角

所以根据这个情况,我们要计算透视变换的点时就要对这个点进行重新排序(左上,右上,右下,左下的顺序),代码如下:

//重新排序旋转矩形坐标点
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;
  }


}


a88905b6d8933aee35e7a4a6d18d8c52.png

跟踪输出后的结果

从上图中可以看到按原来的原理,P0点应该是左下角,结果输出的P0点为左上角,后来我又换了几张图测试后发现,RotatedRect的坐标点,原来说的是Y轴最大的为P[0],现在实际输出后变为X轴最小的为P[0]。

解决方法

8c067b302eb8b27d9da3c0c075647df8.png

微卡智享

测试后的结果发现这个问题后,那我们就重新修改一下自已的这个SortRotatedRectPoints函数,不改动原来的函数,我们直接重载一个新的同步函数。

6085875b295200dd6dcaee4620f93b76.png

//重新排序旋转矩形坐标点
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的参数

97b8f2f4b4edd78cf5e3bba07905daf0.png

最后我们看看运行结果

75b759486c0b58e5b6416b9b05ce2695.png

从上面可以看出,修改后运行起来,透视变换的位置已经正常了。

源码地址

https://github.com/Vaccae/opencvPerspective.git

GitHub上不去的朋友,可以击下方的原文链接跳转到码云的地址,关注【微卡智享】公众号,回复【源码】可以下载我的所有开源项目。

652e4ff5009f1be38ec3bbcc4865ef70.png

扫描二维码

获取更多精彩

微卡智享

82cab40493c393cdb36ff9b7f29796b1.png

「 往期文章 」

C++ OpenCV使用大津法求自适应阈值

C++ OpenCV自适应阈值Canny边缘检测

C++ OpenCV4.5版本SIFT特征检测及匹配

 

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vaccae

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值