本文将介绍如何使用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;
}