opecv 计算曲线斜率

本文介绍了在Windows10环境下,利用VisualStudio2019和OpenCV4.5.4进行图像处理,通过读取图像、提取轮廓、椭圆拟合、分割椭圆轮廓和计算每个小段的曲率的步骤。主要涉及的技术包括findContours、fitEllipse和二次曲线拟合来计算曲率。
摘要由CSDN通过智能技术生成

本文将介绍如何使用OpenCV C++将一张图像中的椭圆轮廓分割成小段,并计算每个小段的曲率。

环境

本文使用的开发环境为:

  • 操作系统:Windows 10(64位)

  • IDE:Visual Studio 2019

  • OpenCV版本:4.5.4

步骤

1. 读取图像

首先,我们需要读取一张图像,本文使用的是一张椭圆形状的图像,代码如下:

cppCopy codecv::Mat image = cv::imread("ellipse.jpg", cv::IMREAD_GRAYSCALE);

2. 提取轮廓

接下来,我们需要使用OpenCV的轮廓提取函数findContours提取图像中的轮廓,代码如下:

cppCopy codestd::vector<std::vector<cv::Point>> contours;
cv::findContours(image, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

这里使用的是RETR_EXTERNAL模式,表示只提取最外层轮廓;CHAIN_APPROX_SIMPLE表示使用简单的轮廓逼近算法。

3. 拟合椭圆

接下来,我们需要使用OpenCV的椭圆拟合函数fitEllipse对轮廓进行椭圆拟合,代码如下:

cppCopy codestd::vector<cv::RotatedRect> ellipses;
for (constauto& contour : contours)
{
    if (contour.size() < 5) continue;
    cv::RotatedRect ellipse = cv::fitEllipse(contour);
    ellipses.push_back(ellipse);
}

这里需要注意的是,椭圆拟合需要至少5个点,所以我们需要对轮廓进行判断,如果点数小于5,则跳过该轮廓。

4. 分割椭圆轮廓

接下来,我们需要将椭圆轮廓分割成若干小段。这里我们可以使用参数方程的方式计算椭圆上的点坐标,并将其等分成若干小段,代码如下:

cppCopy codeconstint segments = 100;
std::vector<std::vector<cv::Point2f>> points(ellipses.size());
for (size_t i = 0; i < ellipses.size(); ++i)
{
    const cv::RotatedRect& ellipse = ellipses[i];
    std::vector<cv::Point2f>& pts = points[i];
    pts.reserve(segments);
    const cv::Point2f center = ellipse.center;
    constfloat a = ellipse.size.width / 2.0f;
    constfloat b = ellipse.size.height / 2.0f;
    constfloat angle = ellipse.angle * CV_PI / 180.0f;
    for (int j = 0; j < segments; ++j)
    {
        constfloat t = 2.0f * CV_PI * j / segments;
        constfloat x = a * std::cos(t);
        constfloat y = b * std::sin(t);
        constfloat x1 = std::cos(angle) * x - std::sin(angle) * y + center.x;
        constfloat y1 = std::sin(angle) * x + std::cos(angle) * y + center.y;
        pts.emplace_back(x1, y1);
    }
}

这里我们将椭圆上等分成100段,并计算每个点的坐标。

5. 计算曲率

最后,我们需要计算每个小段的曲率。这里可以使用cv::fitEllipse函数返回的椭圆拟合结果来计算曲率,代码如下:

cppCopy codestd::vector<std::vector<double>> curvatures(points.size());
for (size_t i = 0; i < points.size(); ++i)
{
    const std::vector<cv::Point2f>& pts = points[i];
    std::vector<double>& curv = curvatures[i];
    curv.reserve(segments);
    for (int j = 0; j < segments; ++j)
    {
        const cv::Point2f& pt = pts[j];
        cv::Matx21f a(2 * pt.x, 2 * pt.y);
        cv::Matx21f b(1, 1);
        cv::Matx21f c(pt.x * pt.x + pt.y * pt.y);
        cv::Matx31f abc(a(0), a(1), c(0));
        cv::Matx31f result = -abc.inv() * b;
        constfloat a2 = result(0, 0);
        constfloat b2 = result(1, 0);
        constfloat curvature = std::sqrt(a2 * a2 + b2 * b2);
        curv.push_back(curvature);
    }
}

这里我们使用了二次曲线拟合的方法来计算曲率。对于每个小段的点集,我们可以将其看作一个二次曲线,并使用最小二乘法来拟合这个二次曲线,得到拟合后的系数,从而计算曲率。

完整代码

cppCopy code#include<opencv2/opencv.hpp>#include<iostream>intmain(){
    cv::Mat image = cv::imread("ellipse.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty())
    {
        std::cerr << "Failed to read image" << std::endl;
        return-1;
    }
    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(image, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
    std::vector<cv::RotatedRect> ellipses;
    for (constauto& contour : contours)
    {
        if (contour.size() < 5) continue;
        cv::RotatedRect ellipse = cv::fitEllipse(contour);
        ellipses.push_back(ellipse);
    }
    constint segments = 100;
    std::vector<std::vector<cv::Point2f>> points(ellipses.size());
    for (size_t i = 0; i < ellipses.size(); ++i)
    {
        const cv::RotatedRect& ellipse = ellipses[i];
        std::vector<cv::Point2f>& pts = points[i];
        pts.reserve(segments);
        const cv::Point2f center = ellipse.center;
        constfloat a = ellipse.size.width / 2.0f;
        constfloat b = ellipse.size.height / 2.0f;
        constfloat angle = ellipse.angle * CV_PI / 180.0f;
        for (int j = 0; j < segments; ++j)
        {
            constfloat t = 2.0f * CV_PI * j / segments;
            constfloat x = a * std::cos(t);
            constfloat y = b * std::sin(t);
            constfloat x1 = std::cos(angle) * x - std::sin(angle) * y + center.x;
            constfloat y1 = std::sin(angle) * x + std::cos(angle) * y + center.y;
            pts.emplace_back(x1, y1);
        }
    }
    std::vector<std::vector<double>> curvatures(points.size());
    for (size_t i = 0; i < points.size(); ++i)
    {
        const std::vector<cv::Point2f>& pts = points[i];
        std::vector<double>& curv = curvatures[i];
        curv.reserve(segments);
        for (int j = 0; j < segments; ++j)
        {
            const cv::Point2f& pt = pts[j];
            cv::Matx21f a(2 * pt.x, 2 * pt.y);
            cv::Matx21f b(1, 1);
            cv::Matx21f c(pt.x * pt.x + pt.y * pt.y);
            cv::Matx31f abc(a(0), a(1), c(0));
            cv::Matx31f result = -abc.inv() * b;
            constfloat a2 = result(0, 0);
            constfloat b2 = result(1, 0);
            constfloat curvature = std::sqrt(a2 * a2 + b2 * b2);
            curv.push_back(curvature);
        }
    }
    return0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值