美文网首页@IT·互联网
C++ 判断矩形和多边形框是否相交

C++ 判断矩形和多边形框是否相交

作者: leon0514 | 来源:发表于2024-10-17 14:21 被阅读0次

C++ 判断矩形和多边形框是否相交

目标检测应用场景
检测框(bounding box)与多边形标注区域(例如分割结果)之间的重叠判断,用于确认检测是否有效。判断监控对象是否处于某个区域

线段相交

向量叉积之判断两条线段相交 - 箐茗 - 博客园 (cnblogs.com)

代码

#ifndef OBJECT_HPP__
#define OBJECT_HPP__
#include <string>
#include <vector>
#include <iostream>

namespace object{

enum class Position{ left, top, right, bottom, left_right, top_bottom, all };

struct FieldPrinter 
{
    std::ostream& os;
    bool first = true;

    template<typename T>
    void print(const std::string& name, const T& value) {
        if (!first) os << ", ";
        os << "\"" << name << "\": " << value;
        first = false;
    }
};

template <typename T>
struct PointTemplate {
    T x;
    T y;
    PointTemplate(T a, T b) : x(a), y(b){}
};

template <typename T>
struct BoxTemplate
{
    T left;
    T top;
    T right;
    T bottom;
    float confidence;
    std::string label;

    BoxTemplate(T l, T t, T r, T b, float conf, std::string name)
        : left(l), top(t), right(r), bottom(b), confidence(conf), label(name) {}
    
    // 获取目标检测框的宽度
    T width() const
    {
        return right - left;
    }

    // 获取目标检测框的高度
    T height() const
    {
        return right - left;
    }

    // 获取目标检测框的面积
    T area() const
    {
        return width() * height();
    }

    // 判断该框是否合法
    bool valid() const
    {
        return width() > 0 && height() > 0;
    }


    // 结构化输出
    friend std::ostream& operator<<(std::ostream& os, const BoxTemplate<T>& box) {
        os << "{ ";
        FieldPrinter printer{os};
        printer.print("left", box.left);
        printer.print("top", box.top);
        printer.print("right", box.right);
        printer.print("bottom", box.bottom);
        printer.print("confidence", box.confidence);
        printer.print("label", "\"" + box.label + "\"");
        os << " }";
        return os;
    }

};

// 获取两个框的iou大小
template<typename T>
float iou(const BoxTemplate<T>& a, const BoxTemplate<T>& b)
{
    T w = std::max((T)0, std::min(a.right, b.right) - std::max(a.left, b.left));
    T h = std::max((T)0, std::min(a.bottom, b.bottom) - std::max(a.top, b.top));
    T union_area = w * h;
    return 1.0f * union_area / (a.area() + b.area() - union_area);
}

// 获取重叠面积占最小面积的比例
template<typename T>
float minAreaIoU(const BoxTemplate<T>& a, const BoxTemplate<T>& b)
{
    T w = std::max((T)0, std::min(a.right, b.right) - std::max(a.left, b.left));
    T h = std::max((T)0, std::min(a.bottom, b.bottom) - std::max(a.top, b.top));
    T union_area = w * h;
    return 1.0f * union_area / (a.area() + b.area() - union_area);
}

// 拓展框 指定拓展区域,按照宽或者高长度的比例拓展框的大小
template<typename T>
BoxTemplate<T> expend(const BoxTemplate<T>& box, Position type, float ratio)
{
    T left = box.left;
    T top = box.top;
    T right = box.right;
    T bottom = box.bottom;
    T w = box.width();
    T h = box.height();
    float confidence = box.confidence;
    std::string label = box.label;
    switch (type)
    {
        case Position::left:
            return BoxTemplate<T>((T)(left - w * ratio), top, right, bottom, confidence, label);
        case Position::top:
            return BoxTemplate<T>(left, (T)(top - h * ratio), right, bottom, confidence, label);
        case Position::right:
            return BoxTemplate<T>(left, top, (T)(right + w * ratio), bottom, confidence, label);
        case Position::bottom:
            return BoxTemplate<T>(left, top, right, (T)(bottom + h * ratio), confidence, label);
        case Position::left_right:
            return BoxTemplate<T>((T)(left - w * ratio), top, (T)(right + w * ratio), bottom, confidence, label);
        case Position::top_bottom:
            return BoxTemplate<T>(left, (T)(top - h * ratio), right, (T)(bottom + h * ratio), confidence, label);
        default:
            return BoxTemplate<T>((T)(left - w * ratio), (T)(top - h * ratio), (T)(right + w * ratio), (T)(bottom + h * ratio), confidence, label);
    }
}

// 缩小框 指定拓展区域,按照宽或者高长度的比例缩小框的大小
template<typename T>
BoxTemplate<T> cut(const BoxTemplate<T>& box, Position type, float ratio)
{
    T left = box.left;
    T top = box.top;
    T right = box.right;
    T bottom = box.bottom;
    T w = box.width();
    T h = box.height();
    float confidence = box.confidence;
    std::string label = box.label;
    switch (type)
    {
        case Position::left:
            return BoxTemplate<T>((T)(left + w * ratio), top, right, bottom, confidence, label);
        case Position::top:
            return BoxTemplate<T>(left, (T)(top + h * ratio), right, bottom, confidence, label);
        case Position::right:
            return BoxTemplate<T>(left, top, (T)(right - w * ratio), bottom, confidence, label);
        case Position::bottom:
            return BoxTemplate<T>(left, top, right, (T)(bottom - h * ratio), confidence, label);
        case Position::left_right:
            return BoxTemplate<T>((T)(left + w * ratio), top, (T)(right - w * ratio), bottom, confidence, label);
        case Position::top_bottom:
            return BoxTemplate<T>(left, (T)(top + h * ratio), right, (T)(bottom - h * ratio), confidence, label);
        default:
            return BoxTemplate<T>((T)(left + w * ratio), (T)(top + h * ratio), (T)(right - w * ratio), (T)(bottom - h * ratio), confidence, label);
    }
}

template<typename T>
bool isBoundingBoxIntersect(const BoxTemplate<T>& box, const std::vector<PointTemplate<T>>& polygon) {
    T poly_min_x = (T)polygon[0].x, poly_max_x = (T)polygon[0].x;
    T poly_min_y = (T)polygon[0].y, poly_max_y = (T)polygon[0].y;

    // 遍历多边形所有顶点,找到多边形的包围盒
    for (const auto& p : polygon) {
        poly_min_x = std::min(poly_min_x, p.x);
        poly_max_x = std::max(poly_max_x, p.x);
        poly_min_y = std::min(poly_min_y, p.y);
        poly_max_y = std::max(poly_max_y, p.y);
    }

    // 判断两个包围盒是否相交
    return !(box.right < poly_min_x || box.left > poly_max_x ||
             box.bottom < poly_min_y || box.top > poly_max_y);
}

// 判断点 p 是否在多边形内(射线法)
template<typename T>
bool isPointInPolygon(const PointTemplate<T>& p, const std::vector<PointTemplate<T>>& polygon) {
    int n = polygon.size();
    int count = 0;
    for (int i = 0; i < n; ++i) 
    {
        const PointTemplate<T>& p1 = polygon[i];
        const PointTemplate<T>& p2 = polygon[(i + 1) % n];
        if (p1.y == p2.y) continue;
        if (p.y < std::min(p1.y, p2.y) || p.y >= std::max(p1.y, p2.y)) continue;
        double x = (double)(p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
        if (x > p.x) count++;
    }
    // 如果交点个数为奇数,说明点在多边形内
    return count % 2 == 1;
}

template<typename T>
bool doLineSegmentsIntersect(
    const PointTemplate<T>& p1, 
    const PointTemplate<T>& p2, 
    const PointTemplate<T>& q1, 
    const PointTemplate<T>& q2) 
{
    auto orientation = [](const PointTemplate<T>& a, const PointTemplate<T>& b, const PointTemplate<T>& c) {
        return (b.y - a.y) * (c.x - b.x) - (b.x - a.x) * (c.y - b.y);
    };

    T o1 = orientation(p1, p2, q1);
    T o2 = orientation(p1, p2, q2);
    T o3 = orientation(q1, q2, p1);
    T o4 = orientation(q1, q2, p2);

    // 判断是否发生线段相交
    return (o1 * o2 < 0) && (o3 * o4 < 0);
}


// 判断矩形和多边形是否相交
template<typename T>
bool isRectangleAndPolygonIntersect(const BoxTemplate<T>& label_box, const std::vector<PointTemplate<T>>& warn_box) {
    // 0. 检查多边形的顶点是否都在矩形内
    bool allPointsInside = true;
    for (const auto& point : warn_box) {
        if (point.x < label_box.left || point.x > label_box.right || 
            point.y < label_box.top  || point.y > label_box.bottom) {
            allPointsInside = false;
            break;
        }
    }
    if (allPointsInside) return true;
    
    // 1. 快速排除:包围盒相交检测
    if (!isBoundingBoxIntersect<T>(label_box, warn_box)) {
        return false;
    }

    // 2. 检查矩形的四个顶点是否在多边形内
    std::vector<PointTemplate<T>> rect_points = {
        {label_box.left, label_box.top},
        {label_box.right, label_box.top},
        {label_box.right, label_box.bottom},
        {label_box.left, label_box.bottom}
    };

    for (const auto& p : rect_points) 
    {
        if (isPointInPolygon(p, warn_box)) 
        {
            return true;
        }
    }

    // 3. 检查矩形和多边形的边是否相交
    for (int i = 0; i < warn_box.size(); ++i) 
    {
        PointTemplate<T> p1 = warn_box[i];
        PointTemplate<T> p2 = warn_box[(i + 1) % warn_box.size()];

        if (doLineSegmentsIntersect({label_box.left, label_box.top}, {label_box.right, label_box.top}, p1, p2) ||
            doLineSegmentsIntersect({label_box.right, label_box.top}, {label_box.right, label_box.bottom}, p1, p2) ||
            doLineSegmentsIntersect({label_box.right, label_box.bottom}, {label_box.left, label_box.bottom}, p1, p2) ||
            doLineSegmentsIntersect({label_box.left, label_box.bottom}, {label_box.left, label_box.top}, p1, p2)) 
        {
            return true;
        }
    }
    return false;
}

using Box = BoxTemplate<float>;
using BoxArray = std::vector<Box>;

using Point = PointTemplate<float>;
using PointArray = std::vector<Point>;

}

#endif

相关文章

网友评论

    本文标题:C++ 判断矩形和多边形框是否相交

    本文链接:https://www.haomeiwen.com/subject/awbfdjtx.html