opecv 计算曲线斜率

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文将介绍如何使用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;
}
在 MATLAB 中计算曲线上某点或两点之间的斜率是一个常见的任务,尤其是在数据分析和信号处理领域。这里我们将讨论几种方法来实现这一目的: ### 1. **简单差分法** 如果已知离散的数据点 (x,y) 并想要估算每个数据点处的切线斜率,那么可以使用简单的向前、向后或中心差分近似求解导数(即斜率)。 - 向前差分:\[ m_{i} = \frac{y(i+1)-y(i)}{x(i+1)-x(i)}\] - 向后差分:\[ m_{i} = \frac{y(i)-y(i-1)}{x(i)-x(i-1)}\] - 中心差分:\[ m_{i}=\frac{\left[y(i+1)-y(i)\right]-\left[y(i)-y(i-1)\right]}{(x(i+1)-x(i))+(x(i)-x(i-1))}\] **示例代码:** ```matlab % 差分方法 diff_y = diff(y); diff_x = diff(x); slope_fd = [NaN; diff_y ./ diff_x]; % 前向差分 slope_bd = [diff_y ./ diff_x; NaN]; % 后向差分 slope_cd = [-Inf; diff_y(2:end)./diff_x; Inf]; % 中心差分 ``` 请注意,此方法仅提供了一阶精度估计值,并且两端点无法直接应用上述公式,因此常会返回 `NaN` 或其他特殊标记表示无效结果。 ### 2. **polyfit 函数拟合直线** 对于更精确的结果以及平滑曲线情况下的斜率估计,我们可以考虑先通过对局部区域内的若干样本进行多项式拟合再求其一阶导数的方式来得到斜率。最常用的就是一次函数的形式了(也就是所谓的“最小二乘法”),它能很好地适应噪声较小的情况。 **示例代码:** ```matlab p=polyfit(xdata, ydata, n); % xdata 和 ydata 分别代表横纵坐标序列,n=1 表明是一次项模型 f=@(xi)(polyval(p, xi)); % 定义一个匿名函数 f(xi)=a*xi+b , 其中 a,b 即为 p 的系数 dfdx=@(xi)p(1)+0.*xi; % df/dx=a 对所有位置都成立 m=fprime(f,xpos); % 计算特定点的位置上的斜率 ``` 其中 `n=1` 显示我们假设的是线性的关系;而在实际操作过程中可根据具体情况适当调整次数以更好地匹配原始趋势变化规律。 ### 3. **梯度命令 gradient()** Matlab 内置了一个名为 `gradient()` 的功能强大的工具箱函数可以直接用于快速高效地求数组沿各个维度的方向导数,默认情况下是基于有限差异方案工作的。该指令非常适合于那些由规则网格构成的空间分布型场量资料集。 **示例代码:** ```matlab [~, dydx] = gradient(Y,X); % X,Y分别为自变量矩阵与因变量响应列向量形式输入 slopes_at_points_of_interest = interp1(X,dydx,[point_1 point_2 ...]); % 插值得到关心的具体位置上的斜率 ``` 这种方法不需要手动编写复杂的循环结构就能完成批量作业并且能够自动处理边界条件的问题。 综上所述,在 MATLAB 环境下有多种途径可用于解决不同类型场景的需求,选择合适的技术取决于您的具体应用场景和个人偏好等因素。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值