cv::minAreaRect() 返回的angle角度问题

角度范围的表现


在阅读apollo::percetion 模块时发现,apollo在对点云取box时 使用了 cv::minAreaRect()寻找面积最小的外接矩形方法。 但是此方法return 出来的 angle 却不确定。

/apollo/modules/perception/base/point_cloud_util.cc

bool GetPointCloudMinareaBbox(const PointFCloud& pc, BoundingCube* box,
                              const int& min_num_points, const bool& verbose) {
  if (pc.size() <= static_cast<size_t>(min_num_points)) {
    return false;
  }
  std::vector<cv::Point2f> pts;
  float min_z = std::numeric_limits<float>::max();
  float max_z = -std::numeric_limits<float>::max();
  for (size_t i = 0; i < pc.size(); ++i) {
    pts.push_back(cv::Point2f(pc[i].x, pc[i].y));
    min_z = std::min(min_z, pc[i].z);
    max_z = std::max(max_z, pc[i].z);
  }
  // compute MinAreaRect
  cv::RotatedRect mar = cv::minAreaRect(pts);
  // adjust angle
  /* Remarque: this only works on old version opencv
    in version 4.5.1 : the return value range is (0,90]
    旋转角是顺时针的,取点顺序也是顺时针的
    先寻找 x 方向上值最小的点作为 起始点
    如果 x 方向上值相同, 再选 y 方向上值最小的点
    输出值的范围是 (0,90] 0 对应 负Y 方向
    O ——> x
    |
    v
    y
  */
  if (mar.size.width < mar.size.height) {
    mar.angle -= 90;
    float tmp = mar.size.width;
    mar.size.width = mar.size.height;
    mar.size.height = tmp;
  }
  if (verbose) {
    AINFO << "center = " << mar.center.x << " " << mar.center.y << std::endl;
    AINFO << "size = " << mar.size.height << " " << mar.size.width << std::endl;
    AINFO << "yaw = " << mar.angle << std::endl;
    AINFO << "height = " << max_z - min_z << std::endl;
  }
  box->x = mar.center.x;
  box->y = mar.center.y;
  box->z = static_cast<float>((min_z + max_z) / 2.0);
  box->length = mar.size.width;
  box->width = mar.size.height;
  box->height = max_z - min_z;
  box->yaw = static_cast<float>((M_PI * (mar.angle + 180)) / 180);
  return true;
}

网上大部分的教程,给出的都是老版本 Opencv的解释, 即返回的angle 在[-90,0) 区间内。

参考:https://theailearner.com/tag/angle-of-rotation-by-cv2-minarearect/

 但是我测试发现在4.5.2 版本中 返回值其实 在 (0,90] 区间内 :

外接矩形四个角点的颜色由浅入深,红色直线为过起始点的X轴垂线。

angle = 67.8781

angle = 88.2194

angle = 1.01398

(输入摆正的矩形) angle = 90

由此推测:

  1. 新的算法改变了矩形起始点(P1)的选择。优先 x 方向上值最小的点,如果 x 方向上值相同, 再选 y 方向上值最小的点 (经验论,有待源码阅读确认)

  2. 确定P1后,后续点按照 clockwise 的顺序选取

  3. 以 Y轴负方向为0度,返回的角度是过<P1的X轴垂线>和 <P1P2的连线>clockwise的夹角

git Issue:: https://github.com/opencv/opencv/issues/19749

代码参考:OpenCV学习笔记(十八):凸包,最小包围区域算子:convexHull(),minAreaRect(),minEnclosingTriangle(),minEnclosingCircle()_Vichael_Chan的博客-CSDN博客

角度是如何计算的


/opencv/modules/imgproc/src/rotcalipers.cpp

cv::RotatedRect cv::minAreaRect( InputArray _points )
{
    CV_INSTRUMENT_REGION();

    Mat hull;
    Point2f out[3];
    RotatedRect box;

    // 找凸包
    convexHull(_points, hull, false, true);
    // depth()返回的是矩阵元素类型,和维度无关
    // 转到32bits float
    if( hull.depth() != CV_32F )
    {
        Mat temp;
        hull.convertTo(temp, CV_32F);
        hull = temp;
    }
    // 获取凸包点数
    int n = hull.checkVector(2);
    const Point2f* hpoints = hull.ptr<Point2f>();

    if( n > 2 )
    {
    //                    In case CV_CALIPERS_MINAREARECT
    //                    ((CvPoint2D32f*)out)[0] - corner
    //                   
  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值