解决rotatedRectangleIntersection计算目标检测旋转框IOU不准确问题C++、opencv

问题

语言 :C++   

OpenCV版本:3.4.0

    在目标检测中,后处理阶段会用到非极大值抑制来过滤目标框,而计算两个框的IOU(交并比)则是其关键的一环,先计算两个框相交的点,再求出这些点构成的多边形的面积就是这两个框相交的面积。

    cv::rotatedRectangleIntersection(cv::RotatedRect r1, cv::RotatedRect r1, vector<cv::Point2f> vertices)用来计算两个旋转矩形框的相交多边形,返回的结果vertices是多边形的点坐标,最多返回8个点的坐标。

    再通过double cv::contourArea(InputArray contour, bool oriented=false)计算多边形面积,这样就得到了相交多边形的面积。

    但是实际应用中发现contourArea计算的面积不正确,通过排查发现是rotatedRectangleIntersection生成的点坐标不是顺时针或者逆时针的顺序(并不是所有情况下都是,而是某些小部分情况下),画张图来解释下,rotatedRectangleIntersection函数返回的点坐标顺序可能是125463,这样的顺序会导致contourArea函数计算的面积是错误的,当然这并不是contourArea函数的bug,因为这个函数计算时是按照点的顺序(方向)来计算轮廓的面积,所以需要重新对坐标顺序进行排列,使得坐标按照顺时针或者逆时针排序,从任意一点开始都可以,比如123456,234561,543216顺序都可以。

解决 

  • 解决的方法比较简单,就是将所有点按照顺时针或逆时针排序即可,哪个点作为起始点都可以
  • 我的思路是先找到最左侧的点,然后计算该点与其他所有点的arc tan(反正切)值,然后再从大到小或从小到大排序就可以了,画个图更直观的解释下
  • 在x轴(虚线)上方的反正切值为正数且角度越大反正切值越大,在x轴(虚线)上方的反正切值为负数数且角度越大反正切值越小,这样就可以对坐标点排序了。

代码

#include <vector>
#include <math.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>

typedef struct{
    float x;
    float y;
    float w;
    float h;
    float theta;
    float score;
    int label;
}detection;

// 第一步找到最左边的点
int find_leftmost_point(vector<cv::Point2f> intersectingRegion)
{
    int index = 0;
    float tmp = intersectingRegion[0].x;
    for(int i=1; i<intersectingRegion.size(); i++)
    {
        if(intersectingRegion[i].x < tmp)
        {
            tmp = intersectingRegion[i].x;
            index = i;
        }
    }
    return index;
}

//第二步对所有点进行排序
vector<cv::Point2f> sort_points(vector<cv::Point2f> intersectingRegion)
{
    vector<cv::Point2f> sort_intersectingRegion;
    int leftmost_index = find_leftmost_point(intersectingRegion);

    vector<float> arctan;
    for(int i=0; i<intersectingRegion.size(); i++)
    {
        arctan.push_back(atan2(intersectingRegion[i].y - intersectingRegion[leftmost_index].y, intersectingRegion[i].x - intersectingRegion[leftmost_index].x));
    }

    vector<int> index;
    for(int i=0; i<arctan.size(); i++)
    {
        index.push_back(i);
    }
    
    sort(index.begin(), index.end(), [&](const int& a, const int& b) {return (arctan[a] < arctan[b]);});
    
    for(int i=0; i<index.size(); i++)
    {
        sort_intersectingRegion.push_back(intersectingRegion[index[i]]);
    }
    return sort_intersectingRegion;
}

// 计算两个旋转框的IOU
float rbox_iou(detection d1, detection d2)
{
    float inter_area;
    float area_r1 = d1.w * d1.h;
    float area_r2 = d2.w * d2.h;
    cv::RotatedRect rect1;
    rect1.center = cv::Point2f(d1.x, d1.y);
    rect1.size = cv::Size(d1.w, d1.h);
    rect1.angle = d1.theta;
    cv::RotatedRect rect2;
    rect2.center = cv::Point2f(d2.x, d2.y);
    rect2.size = cv::Size(d2.w, d2.h);
    rect2.angle = d2.theta;

    vector<cv::Point2f> intersectingRegion;
    cv::rotatedRectangleIntersection(rect1, rect2, intersectingRegion);
    
    if (intersectingRegion.empty())
    {
        inter_area = 0;
    }
    else
    {
        vector<cv::Point2f> sort_intersectingRegion = sort_points(intersectingRegion);
        inter_area = cv::contourArea(sort_intersectingRegion);
    }
    return inter_area / (area_r1 + area_r2 - inter_area + 0.00000001);
}
    

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值