当前位置:   article > 正文

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

rotatedrectangleintersection

问题

语言 :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轴(虚线)上方的反正切值为负数数且角度越大反正切值越小,这样就可以对坐标点排序了。

代码

  1. #include <vector>
  2. #include <math.h>
  3. #include <opencv2/highgui/highgui.hpp>
  4. #include <opencv2/imgproc.hpp>
  5. typedef struct{
  6. float x;
  7. float y;
  8. float w;
  9. float h;
  10. float theta;
  11. float score;
  12. int label;
  13. }detection;
  14. // 第一步找到最左边的点
  15. int find_leftmost_point(vector<cv::Point2f> intersectingRegion)
  16. {
  17. int index = 0;
  18. float tmp = intersectingRegion[0].x;
  19. for(int i=1; i<intersectingRegion.size(); i++)
  20. {
  21. if(intersectingRegion[i].x < tmp)
  22. {
  23. tmp = intersectingRegion[i].x;
  24. index = i;
  25. }
  26. }
  27. return index;
  28. }
  29. //第二步对所有点进行排序
  30. vector<cv::Point2f> sort_points(vector<cv::Point2f> intersectingRegion)
  31. {
  32. vector<cv::Point2f> sort_intersectingRegion;
  33. int leftmost_index = find_leftmost_point(intersectingRegion);
  34. vector<float> arctan;
  35. for(int i=0; i<intersectingRegion.size(); i++)
  36. {
  37. arctan.push_back(atan2(intersectingRegion[i].y - intersectingRegion[leftmost_index].y, intersectingRegion[i].x - intersectingRegion[leftmost_index].x));
  38. }
  39. vector<int> index;
  40. for(int i=0; i<arctan.size(); i++)
  41. {
  42. index.push_back(i);
  43. }
  44. sort(index.begin(), index.end(), [&](const int& a, const int& b) {return (arctan[a] < arctan[b]);});
  45. for(int i=0; i<index.size(); i++)
  46. {
  47. sort_intersectingRegion.push_back(intersectingRegion[index[i]]);
  48. }
  49. return sort_intersectingRegion;
  50. }
  51. // 计算两个旋转框的IOU
  52. float rbox_iou(detection d1, detection d2)
  53. {
  54. float inter_area;
  55. float area_r1 = d1.w * d1.h;
  56. float area_r2 = d2.w * d2.h;
  57. cv::RotatedRect rect1;
  58. rect1.center = cv::Point2f(d1.x, d1.y);
  59. rect1.size = cv::Size(d1.w, d1.h);
  60. rect1.angle = d1.theta;
  61. cv::RotatedRect rect2;
  62. rect2.center = cv::Point2f(d2.x, d2.y);
  63. rect2.size = cv::Size(d2.w, d2.h);
  64. rect2.angle = d2.theta;
  65. vector<cv::Point2f> intersectingRegion;
  66. cv::rotatedRectangleIntersection(rect1, rect2, intersectingRegion);
  67. if (intersectingRegion.empty())
  68. {
  69. inter_area = 0;
  70. }
  71. else
  72. {
  73. vector<cv::Point2f> sort_intersectingRegion = sort_points(intersectingRegion);
  74. inter_area = cv::contourArea(sort_intersectingRegion);
  75. }
  76. return inter_area / (area_r1 + area_r2 - inter_area + 0.00000001);
  77. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/114129
推荐阅读
相关标签
  

闽ICP备14008679号