OpenCV中两个旋转矩形(RotatedRect)的交集计算优化技巧

namespace cv
{

int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion )
{
    CV_INSTRUMENT_REGION();

    // L2 metric
    const float samePointEps = std::max(1e-16f, 1e-6f * (float)std::max(rect1.size.area(), rect2.size.area()));

    Point2f vec1[4], vec2[4];
    Point2f pts1[4], pts2[4];

    std::vector <Point2f> intersection; intersection.reserve(24);

    rect1.points(pts1);
    rect2.points(pts2);

    int ret = INTERSECT_FULL;

    // Specical case of rect1 == rect2
    {
        bool same = true;

        for( int i = 0; i < 4; i++ )
        {
            if( fabs(pts1[i].x - pts2[i].x) > samePointEps || (fabs(pts1[i].y - pts2[i].y) > samePointEps) )
            {
                same = false;
                break;
            }
        }

        if(same)
        {
            intersection.resize(4);

            for( int i = 0; i < 4; i++ )
            {
                intersection[i] = pts1[i];
            }

            Mat(intersection).copyTo(intersectingRegion);

            return INTERSECT_FULL;
        }
    }

    // Line vector
    // A line from p1 to p2 is: p1 + (p2-p1)*t, t=[0,1]
    for( int i = 0; i < 4; i++ )
    {
        vec1[i].x = pts1[(i+1)%4].x - pts1[i].x;
        vec1[i].y = pts1[(i+1)%4].y - pts1[i].y;

        vec2[i].x = pts2[(i+1)%4].x - pts2[i].x;
        vec2[i].y = pts2[(i+1)%4].y - pts2[i].y;
    }

    // Line test - test all line combos for intersection
    for( int i = 0; i < 4; i++ )
    {
        for( int j = 0; j < 4; j++ )
        {
            // Solve for 2x2 Ax=b
            float x21 = pts2[j].x - pts1[i].x;
            float y21 = pts2[j].y - pts1[i].y;

            float vx1 = vec1[i].x;
            float vy1 = vec1[i].y;

            float vx2 = vec2[j].x;
            float vy2 = vec2[j].y;

            float det = vx2*vy1 - vx1*vy2;

            float t1 = (vx2*y21 - vy2*x21) / det;
            float t2 = (vx1*y21 - vy1*x21) / det;

            // This takes care of parallel lines
            if( cvIsInf(t1) || cvIsInf(t2) || cvIsNaN(t1) || cvIsNaN(t2) )
            {
                continue;
            }

            if( t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f )
            {
                float xi = pts1[i].x + vec1[i].x*t1;
                float yi = pts1[i].y + vec1[i].y*t1;

                intersection.push_back(Point2f(xi,yi));
            }
        }
    }

    if( !intersection.empty() )
    {
        ret = INTERSECT_PARTIAL;
    }

    // Check for vertices from rect1 inside recct2
    for( int i = 0; i < 4; i++ )
    {
        // We do a sign test to see which side the point lies.
        // If the point all lie on the same sign for all 4 sides of the rect,
        // then there's an intersection
        int posSign = 0;
        int negSign = 0;

        float x = pts1[i].x;
        float y = pts1[i].y;

        for( int j = 0; j < 4; j++ )
        {
            // line equation: Ax + By + C = 0
            // see which side of the line this point is at
            float A = -vec2[j].y;
            float B = vec2[j].x;
            float C = -(A*pts2[j].x + B*pts2[j].y);

            float s = A*x+ B*y+ C;

            if( s >= 0 )
            {
                posSign++;
            }
            else
            {
                negSign++;
            }
        }

        if( posSign == 4 || negSign == 4 )
        {
            intersection.push_back(pts1[i]);
        }
    }

    // Reverse the check - check for vertices from rect2 inside recct1
    for( int i = 0; i < 4; i++ )
    {
        // We do a sign test to see which side the point lies.
        // If the point all lie on the same sign for all 4 sides of the rect,
        // then there's an intersection
        int posSign = 0;
        int negSign = 0;

        float x = pts2[i].x;
        float y = pts2[i].y;

        for( int j = 0; j < 4; j++ )
        {
            // line equation: Ax + By + C = 0
            // see which side of the line this point is at
            float A = -vec1[j].y;
            float B = vec1[j].x;
            float C = -(A*pts1[j].x + B*pts1[j].y);

            float s = A*x + B*y + C;

            if( s >= 0 )
            {
                posSign++;
            }
            else
            {
                negSign++;
            }
        }

        if( posSign == 4 || negSign == 4 )
        {
            intersection.push_back(pts2[i]);
        }
    }

    int N = (int)intersection.size();
    if (N == 0)
    {
        return INTERSECT_NONE;
    }

    // Get rid of duplicated points
    int Nstride = N;
    cv::AutoBuffer<float, 100> distPt(N * N);
    cv::AutoBuffer<int> ptDistRemap(N);
    for (int i = 0; i < N; ++i)
    {
        const Point2f pt0 = intersection[i];
        ptDistRemap[i] = i;
        for (int j = i + 1; j < N; )
        {
            const Point2f pt1 = intersection[j];
            float d2 = normL2Sqr<float>(pt1 - pt0);
            if(d2 <= samePointEps)
            {
                if (j < N - 1)
                    intersection[j] =  intersection[N - 1];
                N--;
                continue;
            }
            distPt[i*Nstride + j] = d2;
            ++j;
        }
    }
    while (N > 8) // we still have duplicate points after samePointEps threshold (eliminate closest points)
    {
        int minI = 0;
        int minJ = 1;
        float minD = distPt[1];
        for (int i = 0; i < N - 1; ++i)
        {
            float* pDist = distPt.data() + Nstride * ptDistRemap[i];
            for (int j = i + 1; j < N; ++j)
            {
                float d = pDist[ptDistRemap[j]];
                if (d < minD)
                {
                    minD = d;
                    minI = i;
                    minJ = j;
                }
            }
        }
        CV_Assert(fabs(normL2Sqr<float>(intersection[minI] - intersection[minJ]) - minD) < 1e-6);  // ptDistRemap is not corrupted
        // drop minJ point
        if (minJ < N - 1)
        {
            intersection[minJ] =  intersection[N - 1];
            ptDistRemap[minJ] = ptDistRemap[N - 1];
        }
        N--;
    }

    // order points
    for (int i = 0; i < N - 1; ++i)
    {
        Point2f diffI = intersection[i + 1] - intersection[i];
        for (int j = i + 2; j < N; ++j)
        {
            Point2f diffJ = intersection[j] - intersection[i];
            if (diffI.cross(diffJ) < 0)
            {
                std::swap(intersection[i + 1], intersection[j]);
                diffI = diffJ;
            }
        }
    }

    intersection.resize(N);
    Mat(intersection).copyTo(intersectingRegion);

    return ret;
}

} // end namespace

这段代码实现了OpenCV中两个旋转矩形(RotatedRect)的交集计算。以下是对代码的分析和一些优化技巧:

代码分析

​​基本思路​​:
计算两个旋转矩形的所有边之间的交点
检查一个矩形的顶点是否在另一个矩形内部
合并所有交点并去除重复点
对交点进行排序以形成凸多边形
​​

主要步骤​​:

处理完全相同的矩形特殊情况
计算所有边之间的交点(16种组合)
检查矩形顶点是否在另一个矩形内
去除重复点
对点进行排序

优化技巧

​​数学计算优化​​:
使用normL2Sqr代替norm避免平方根计算
提前计算并重用向量和线方程系数
​​内存优化​​:
使用AutoBuffer而不是动态分配内存
预分配空间intersection.reserve(24)
​​数值稳定性​​:
使用samePointEps处理浮点精度问题
检查cvIsInf和cvIsNaN避免数值不稳定
​​算法优化​​:
提前终止检查(如发现完全相同的矩形)
使用符号测试判断点是否在矩形内

可能的改进

​​更高效的凸包算法​​:
当前的点排序算法复杂度较高,可以考虑Andrew’s monotone chain算法
​​并行化​​:
边交点计算可以并行化(4x4组合)
​​更精确的数值处理​​:
使用更高精度的中间计算减少误差累积
​​提前裁剪测试​​:
在详细计算前先进行简单的包围盒测试
​​更智能的重复点处理​​:
当前方法在极端情况下可能需要多次遍历

使用建议

对于性能敏感的应用,可以考虑:
如果矩形大多是轴对齐的,使用更简单的算法
添加快速拒绝测试(如分离轴测试)
对于高精度需求:
可以增加samePointEps的值
使用双精度计算关键部分
这段代码整体设计良好,考虑了数值稳定性和性能,但在极端情况下(如非常接近的矩形)可能会有性能波动。

<think>嗯,用户想要用OpenCV4和C++实现两个长方形碰撞的检测。首先,我需要回忆一下OpenCV中处理形状碰撞的方法。通常,碰撞检测可以通过几何计算或者利用OpenCV的内置函数来完成。 首先,用户的问题是两个矩形是否相交。在计算机图形学中,判断两个矩形是否碰撞,通常的方法是检查它们的边界是否有重叠。这需要比较两个矩形的坐标范围。例如,矩形A的左右上下边界是否和矩形B的有交集。 接下来,我需要考虑如何在OpenCV中表示矩形OpenCV中的Rect类应该可以派上用场,它提供了x、y、width、height属性,可以用来计算矩形的四个边界坐标。比如,矩形A的右边界是x + width,下边界是y + height。 然后,碰撞检测的逻辑应该是这样的:如果两个矩形在x轴和y轴上的投影都有重叠,那么它们相交。具体来说,矩形A的左边界小于矩形B的右边界,并且矩形A的右边界大于矩形B的左边界;同时,在y轴上也是如此。如果这两个条件都满足,说明两个矩形发生了碰撞。 接下来,用户可能需要用OpenCV绘制这两个矩形,并实时检测它们的碰撞状态。这涉及到图像处理循环,比如在每一帧中更新矩形的位置,然后进行碰撞检测,并根据结果改变颜色或者其他视觉反馈。 实现步骤可能包括:初始化两个Rect对象,设置它们的初始位置和速度;在主循环中更新位置,检查碰撞,绘制矩形,并处理窗口事件。需要注意的是,当矩形移动到窗口边缘时要处理反弹,比如改变速度的方向。 在编写代码时,要确保正确使用OpenCV的函数,比如rectangle()来绘制矩形,以及正确的循环结构来保持窗口的持续显示。另外,碰撞检测的逻辑需要精确,避免出现误判或者漏判的情况。 可能遇到的问题包括矩形速度过快导致的穿透现象,这时候可能需要更精确的碰撞检测方法,比如使用浮点坐标或者更频繁的检测。不过对于基础实现来说,使用Rect的intersect方法或者手动比较边界条件应该足够了。 总结一下,步骤大致是:创建两个矩形,更新位置,检测碰撞,绘制并显示。关键点在于正确计算边界条件和利用OpenCV的功能进行绘制和显示。</think>好的!我将分步骤为您讲解如何使用OpenCV4和C++实现两个长方形的碰撞检测。 --- ### **一、实现思路** 1. **表示长方形**:使用OpenCV的`cv::Rect`对象存储位置和尺寸 2. **碰撞检测**:通过几何计算判断两个矩形是否相交 3. **可视化**:实时绘制矩形并改变颜色表示碰撞状态 --- ### **二、核心代码实现** ```cpp #include <opencv2/opencv.hpp> using namespace cv; // 判断两个矩形是否碰撞的两种方法 bool checkCollision(const Rect& rect1, const Rect& rect2) { // 方法1:手动计算边界重叠 bool x_overlap = (rect1.x < rect2.x + rect2.width) && (rect1.x + rect1.width > rect2.x); bool y_overlap = (rect1.y < rect2.y + rect2.height) && (rect1.y + rect1.height > rect2.y); return x_overlap && y_overlap; // 方法2:使用OpenCV内置函数 // return (rect1 & rect2).area() > 0; } int main() { Mat canvas(600, 800, CV_8UC3, Scalar(255,255,255)); // 初始化两个矩形 Rect rect1(100, 100, 100, 80); Rect rect2(300, 200, 120, 90); // 设置移动速度 Point velocity1(2, 3); Point velocity2(-3, -2); while(true) { canvas.setTo(Scalar(255,255,255)); // 更新矩形位置 rect1.x += velocity1.x; rect1.y += velocity1.y; rect2.x += velocity2.x; rect2.y += velocity2.y; // 边界反弹 auto handleBounce = [&canvas](Rect& rect, Point& vel) { if(rect.x < 0 || rect.x + rect.width > canvas.cols) vel.x *= -1; if(rect.y < 0 || rect.y + rect.height > canvas.rows) vel.y *= -1; }; handleBounce(rect1, velocity1); handleBounce(rect2, velocity2); // 碰撞检测 bool isColliding = checkCollision(rect1, rect2); // 绘制矩形(碰撞时显示红色) Scalar color1 = isColliding ? Scalar(0,0,255) : Scalar(0,255,0); Scalar color2 = isColliding ? Scalar(0,0,255) : Scalar(255,0,0); rectangle(canvas, rect1, color1, -1); rectangle(canvas, rect2, color2, -1); imshow("Collision Demo", canvas); if(waitKey(30) == 27) break; // ESC退出 } return 0; } ``` --- ### **三、关键代码解析** 1. **碰撞检测原理**: - 两个矩形在X轴和Y轴上的投影必须**同时有重叠** - 数学表达式: - X轴重叠:$$ \text{rect1.x} < \text{rect2.x} + \text{rect2.width} \quad \text{且} \quad \text{rect1.x} + \text{rect1.width} > \text{rect2.x} $$ - Y轴重叠:$$ \text{rect1.y} < \text{rect2.y} + \text{rect2.height} \quad \text{且} \quad \text{rect1.y} + \text{rect1.height} > \text{rect2.y} $$ 2. **OpenCV优化**: - 使用`rect1 & rect2`可以直接获取相交区域 - 当相交区域面积大于0时,说明发生碰撞 --- ### **四、运行效果** - 两个彩色矩形在窗口中运动 - 发生碰撞时变为红色 - 碰到窗口边界会自动反弹 --- ### **五、扩展改进建议** 1. **添加旋转支持**:使用旋转矩形(`cv::RotatedRect`),需要计算包围盒或进行多边形碰撞检测 2. **性能优化**:使用多线程处理多个物体的碰撞检测 3. **物理模拟**:添加质量、速度变化等物理参数 需要进一步优化或添加特定功能时可以随时补充说明!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值