多边形拟合

多边形近似

API

approxPolyDP(curve, approxCurve, epsilon, closed)
  • curve:输入像素轮廓点,类型为vector<Point>或者Mat
  • approxCurve:多边形逼近结果,以多边形顶点坐标的形式给出,类型为 vector<Point2f>CV_32SC2 类型的 Nx1Mat 类矩阵
  • epsilon:逼近的精度,即原始曲线和逼近曲线之间的最大距离,类型为 double
  • closed:逼近曲线是否为封闭曲线的标志,true 表示曲线封闭,即最后一个顶点与第一个顶点相连

示例代码:

#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;

// 绘制轮廓函数
inline void drawapp(vector<Point2f> result, Mat img2)
{
    for (int i = 0; i < result.size(); i++)
    {
        // 最后一个坐标点与第一个坐标点相连
        if (i == result.size() - 1)
        {
            Point2f point1 = result.at(i);
            Point2f point2 = result.at(0);
            line(img2, point1, point2, Scalar(0, 255, 0), 2);
            break;
        }
        Point2f point1 = result.at(i);
        Point2f point2 = result.at(i + 1);
        line(img2, point1, point2, Scalar(0, 255, 0), 2);
    }
}

int main()
{
    Mat img = imread("/home/kslas/test.jpg");
    imshow("original", img);

    // 边缘检测
    Mat canny;
    Canny(img, canny, 80, 160, 3, false);

    // 膨胀运算
    Mat kernel = getStructuringElement(0, Size(3, 3));
    dilate(canny, canny, kernel);

    // 轮廓的发现与绘制
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(canny, contours, hierarchy, 0, 2);

    // 绘制多边形
    for (int i = 0; i < contours.size(); i++)
    {
        // 用最小外接矩形求取轮廓中心
        RotatedRect rrect = minAreaRect(contours[i]);
        Point2f center = rrect.center;
        circle(img, center, 2, Scalar(0, 255, 0), 2);

        vector<Point2f> result;
        approxPolyDP(contours[i], result, 4, true);  // 多边形逼近
        drawapp(result, img);
    }

    imshow("result", img);
    waitKey(0);
    destroyAllWindows();
    return 0;
}

运行结果:

凸包检测

简介

  • 有时物体的形状过于复杂,用多边形逼近后处理起来依然较为复杂,例如人手、海星等。对于形状较为复杂的物体,可以利用凸包近似表示

  • 凸包是图形学中常见的概念,将二维平面上的点集最外层的点连接起来构成的凸多边形称为凸包

  • 虽然凸包检测也是对轮廓进行多边形逼近,但是逼近结果一定为凸多边形

  • 凸包检测往往用于加强approxPolyDP()的结果

API

convexHull(points, hull, clockwise, returnPoints)
  • points:输入的二维点集或轮廓坐标,类型为 vector<Point> 或者 Mat
  • hull:输出凸包的顶点坐标或索引,类型为 vector<Point> 或者 vector<int>
  • clockwise:方向标志。当参数取值为 true 时,凸包顺序为顺时针方向;当参数取值为 false 时,凸包顺序为逆时针方向
  • returnPoints:输出数据的类型标志。当参数为 true 时,第二个参数输出的结果是凸包顶点的坐标;当参数取值为为 false 时,第二个参数输出的结果是凸包顶点的索引

示例代码:

在该程序中,首先对图像进行二值化,并利用开运算消除二值化过程中产生的较小区域,之后寻找图像的轮廓,最后对图像中的每一个轮廓进行凸包检测,并绘制凸包的顶点和每一条边

#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;

int main()
{
    Mat img = imread("/home/kslas/OpenCV/hand.jpg");

    // 二值化
    Mat gray, binary;
    cvtColor(img, gray, COLOR_BGR2GRAY);
    threshold(gray, binary, 230, 255, THRESH_BINARY);
    bitwise_not(binary, binary);

    // 开运算消除细小区域
    Mat k = getStructuringElement(MORPH_RECT, Size(3, 3));
    morphologyEx(binary, binary, MORPH_OPEN, k);
    imshow("binary", binary);

    // 轮廓发现
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(binary, contours, hierarchy, 0, 2);
    for (int i = 0; i < contours.size(); i++)
    {
        // 计算凸包
        vector<Point> hull;
        convexHull(contours[i], hull);
        // 绘制凸包
        for (int j = 0; i < hull.size(); j++)
        {
            // 绘制凸包顶点
            circle(img, hull[j], 4, Scalar(255, 0, 0), 2);
            // 连接凸包
            if (j == hull.size() - 1)
            {
                line(img, hull[j], hull[0], Scalar(0, 0, 255), 2);
                break;
            }
            line(img, hull[j], hull[j + 1], Scalar(0, 0, 255), 2);
        }
    }
    imshow("hull", img);
    waitKey(0);
    destroyAllWindows();
    return 0;
}

运行结果:

posted @ 2022-01-27 16:52  TNTksals  阅读(466)  评论(0编辑  收藏  举报