Opencv源码之平面点集的最小包围圆

本文介绍了如何利用OpenCV实现平面点集的最小包围圆算法,通过点集的数学性质,结合三角形的外心、内心、垂心等概念,给出算法原理和具体实现细节,旨在解决机械手定位问题,最小化其到达各个点的最大距离。
摘要由CSDN通过智能技术生成

平面点集的最小包围圆

--Cracent整理 2016.5.28

目录

1、问题背景.... 1

2、算法及原理.... 1

3、算法(摘自OPENCV)... 1

4、基础数学知识.... 7

三角形的外心.... 7

三角形的三条垂直平分线必交于一点.... 7

三角形的外心的性质.... 7

三角形的内心.... 8

证明.... 8

性质.... 8

三角形的垂心.... 8

三角形的三条高必交于一点.... 9

三角形的垂心的性质.... 9

三角形的重心.... 10

三角形的三条中线必交于一点.... 10

三角形的重心的性质.... 11

三角形的旁心.... 11

介绍.... 11

三角形旁心的性质.... 11

欧拉线.... 12

欧拉线的证法1.. 12

欧拉线的证法2.. 13

欧拉线的证法3.. 13

 

 

1、问题背景

考察固定在工作平台上的一直机械手,要捡起散落在不同位置的多个零件,并送到别的地方。那么,这只机械手的底座应该选在哪里呢?根据直觉,应该选在机械手需够着的那些位置的“中心”。准确地讲,也就是包围这些点的那个最小圆的圆心----该位置的好处是,可使机械手的底座到它需要够着的那些点的最大距离最小化。于是可得如下问题:给定由平面上n个点所组成的一个集合P(对应于机械手需要够着的工作平台的那些位置),试找出P的最小包围圆(smallest enclosing disc)----亦即,包含P中所有点、半径最小的那个圆。这个最小包围圆必然是唯一的。

 

 2、算法及原理

算法介绍:我们本次算法的设计是基于这样一个简单直观的性质:在既定的给定点条件下,如果引入一张新的半平面,只要此前的最优解顶点(即唯一确定最小包围圆的几个关键顶点)能够包含于其中,则不必对此最优解进行修改,亦即此亦为新点集的最优解;否则,新的最优解顶点必然位于这个新的半空间的边界上。

定理可以通过反证法证明。

于是,基于此性质,我们便可得到一个类似于线性规划算法的随机增量式算法。定义Di为相对于pi的最小包围圆。此算法实现的关键在于对于pi∉Di-1时的处理。显然,如果pi∈Di-1,则Di= Di-1;否则,需要对Di另外更新。而且,Di的组成必然包含了pi;因此,此种情况下的最小包围圆是过pi点且覆盖点集{ p1,p2,p3……pi-1}的最小包围圆。则仿照上述处理的思路,Di={ p1,pi},逐个判断点集{ p2,p3……pi-1},如果存在pj∉Di,则Di={pj,pi}。同时,再依次对点集{ p1,p2,p3……pj-1}判断是否满足pk∈Di,若有不满足,则Di={pk,pj,pi}。由于,三点唯一地确定一个圆,故而,只需在此基础上判断其他的点是否位于此包围圆内,不停地更新pk。当最内层循环完成时,退出循环,转而更新pj;当次内层循环结束时,退出循环,更新pi。当i=n时,表明对所有的顶点均已处理过,此时的Dn即表示覆盖了给定n个点的最小包围圆。

 

3、算法(摘自OPENCV)

主函数:minEnclosingCircle_jbl(InputArray _points, Point2f& _center, float& _radius)

 

static float innerProduct(Point2f &v1, Point2f &v2)

{

    return v1.x * v2.y - v1.y * v2.x;

}

 

//三点定圆法

static void findCircle3pts(Point2f *pts, Point2f &center, float &radius)

{

    // two edges of the triangle v1, v2

    Point2f v1 = pts[1] - pts[0];

    Point2f v2 = pts[2] - pts[0];

 

    if (innerProduct(v1, v2) == 0.0f)

    {

        // v1, v2 colineation, can not determine a uniquecircle

        // find the longtest distance as diameter line

        float d1 = (float)norm(pts[0] - pts[1]);

        float d2 = (float)norm(pts[0] - pts[2]);

        float d3 = (float)norm(pts[1] - pts[2]);

        if (d1 >= d2 && d1 >= d3)

        {

            center = (pts[0] + pts[1]) / 2.0f;

            radius = (d1 / 2.0f);

        }

        else if (d2 >= d1 && d2 >= d3)

        {

            center = (pts[0] + pts[2]) / 2.0f;

            radius = (d2 / 2.0f);

        }

        else if (d3 >= d1 && d3 >= d2)

        {

            center = (pts[1] + pts[2]) / 2.0f;

            radius = (d3 / 2.0f);

        }

    }

    else

    {

        // center is intersection of midperpendicular linesof the two edges v1, v2

        // a1*x + b1*y = c1 where a1 = v1.x, b1 = v1.y

        // a2*x + b2*y = c2 where a2 = v2.x, b2 = v2.y

        Point2f midPoint1 = (pts[0] + pts[1]) / 2.0f;

        float c1 = midPoint1.x * v1.x + midPoint1.y * v1.y;

        Point2f midPoint2 = (pts[0] + pts[2]) / 2.0f;

        float c2 = midPoint2.x * v2.x + midPoint2.y * v2.y;

        float det = v1.x * v2.y - v1.y * v2.x;

        float cx = (c1 * v2.y - c2 * v1.y) / det;

        float cy = (v1.x * c2 - v2.x * c1) / det;

        center.x = (float)cx;

        center.y = (float)cy;

        cx -= pts[0].x;

        cy -= pts[0].y;

        radius = (float)(std::sqrt(cx *cx + cy * cy));

    }

}

 

const float EPS = 1.0e-4f;

 

static voidfindEnclosingCircle3pts_orLess_32f(Point2f *pts, int count, Point2f &center, float &radius)

{

    switch (count)

    {

    case 1:

        center = pts[0];

        radius = 0.0f;

        break;

    case 2:

        center.x = (pts[0].x + pts[1].x) / 2.0f;

        center.y = (pts[0].y + pts[1].y) / 2.0f;

        radius = (float)(norm(pts[0] - pts[1]) / 2.0);

        break;

    case 3:

        findCircle3pts(pts, center, radius);

        break;

    default:

        break;

    }

 

    radius += EPS;

}

 

template<typename PT>

static void findThirdPoint(const PT *pts, int i, int j, Point2f &center, float &radius)

{

    center.x = (float)(pts[j].x + pts[i].x) / 2.0f;

    center.y = (float)(pts[j].y + pts[i].y) / 2.0f;

    float dx = (float)(pts[j].x - pts[i].x);

    float dy = (float)(pts[j].y - pts[i].y);

可以使用 Welzl 算法来实现点集最小包围球。以下是使用 OpenCV 和 C++ 实现的示例代码: ```c++ #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> #include <iostream> #include <vector> #include <random> using namespace cv; using namespace std; // 计算两个点之间的距离 double dist(Point3d p1, Point3d p2) { return norm(p1 - p2); } // 判断点 p 是否在心为 center,半径为 radius 的内 bool insideCircle(Point3d p, Point3d center, double radius) { return dist(p, center) < radius + 1e-6; } // 返回最小包围球的半径和心 void minSphere(vector<Point3d>& points, double& radius, Point3d& center) { int n = points.size(); if (n == 0) { radius = 0; center = Point3d(0, 0, 0); return; } else if (n == 1) { radius = 0; center = points[0]; return; } else if (n == 2) { radius = dist(points[0], points[1]) / 2; center = (points[0] + points[1]) / 2; return; } // 随机打乱顺序 random_shuffle(points.begin(), points.end()); // 递归求解 vector<Point3d> rest(points.begin() + 1, points.end()); minSphere(rest, radius, center); if (insideCircle(points[0], center, radius)) return; rest.push_back(points[0]); radius = 0; center = points[0]; for (int i = 0; i < rest.size(); i++) { if (!insideCircle(rest[i], center, radius)) { vector<Point3d> sub(rest.begin(), rest.begin() + i); minSphere(sub, radius, center); if (insideCircle(rest[i], center, radius)) return; rest.erase(sub.begin(), sub.end()); rest.push_back(rest[i]); } } } int main() { // 生成随机点集 vector<Point3d> points; random_device rd; mt19937 gen(rd()); uniform_real_distribution<double> dis(-1.0, 1.0); for (int i = 0; i < 10; i++) { double x = dis(gen); double y = dis(gen); double z = dis(gen); points.push_back(Point3d(x, y, z)); } // 求解最小包围球 double radius; Point3d center; minSphere(points, radius, center); // 绘制点集最小包围球 Mat img(600, 600, CV_8UC3, Scalar(255, 255, 255)); for (auto p : points) { Point2d p2d(p.x * 200 + 300, p.y * 200 + 300); circle(img, p2d, 2, Scalar(0, 0, 255), -1); } Point2d center2d(center.x * 200 + 300, center.y * 200 + 300); circle(img, center2d, radius * 200, Scalar(0, 255, 0), 2); imshow("Minimum Bounding Sphere", img); waitKey(); return 0; } ``` 这里我们使用了 `Point3d` 类型来表示三维点,`minSphere` 函数实现了递归求解最小包围球的过程,`insideCircle` 函数用于判断一个点是否在内。最后,我们使用 OpenCV 绘制了点集最小包围球。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值