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













网友评论