基于局部曲率的图像边界彩色映射算法(OpenCV C/C++)咨询
刚好做过类似的需求,给你一套完整的OpenCV C++实现方案,分步骤来,每一步都有具体代码和思路:
整体实现流程
核心逻辑是:先从边界图像中提取连续的边界点序列 → 计算每个点的局部曲率 → 将曲率值映射为红-黄渐变的颜色 → 把颜色绘制到图像上可视化。
1. 提取边界点序列
首先假设你的输入是二值边界图(边界为白色,背景黑色),如果不是的话先转成二值图。用OpenCV的findContours提取轮廓,注意用CHAIN_APPROX_NONE保留所有边界点,这样后续计算曲率更准确。
#include <opencv2/opencv.hpp> #include <vector> #include <algorithm> #include <cmath> using namespace cv; using namespace std; int main() { // 读取边界图像,转灰度 Mat src = imread("boundary_image.png", IMREAD_GRAYSCALE); if (src.empty()) { cerr << "Failed to read image!" << endl; return -1; } // 转二值图(如果原图不是二值的话) Mat binary; threshold(src, binary, 127, 255, THRESH_BINARY); // 提取轮廓 vector<vector<Point>> contours; vector<Vec4i> hierarchy; findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE); // 取第一个轮廓(如果是多轮廓,可遍历所有contours处理) vector<Point> boundary = contours[0]; int n = boundary.size();
2. 计算局部曲率
这里给你两种常用的计算方法,按需选择:
方法一:向量夹角法(简单高效,适合快速可视化)
对于每个边界点,取它前后k个点(k=1时就是相邻点),计算相邻向量的夹角,曲率用π - 夹角表示——夹角越小,转弯越急,曲率越高;夹角接近π时,边界接近直线,曲率极低。
// 计算每个点的曲率(向量夹角法) vector<double> curvatures(n); int k = 1; // 邻域步长,可根据图像调整,k越大越平滑 for (int i = 0; i < n; ++i) { // 处理循环边界(第一个点的前一个点是最后一个,反之亦然) int prev_idx = (i - k + n) % n; int next_idx = (i + k) % n; Point P_prev = boundary[prev_idx]; Point P_curr = boundary[i]; Point P_next = boundary[next_idx]; // 计算向量 Vec2d v1(P_curr.x - P_prev.x, P_curr.y - P_prev.y); Vec2d v2(P_next.x - P_curr.x, P_next.y - P_curr.y); // 避免除以0 double len1 = norm(v1); double len2 = norm(v2); if (len1 < 1e-6 || len2 < 1e-6) { curvatures[i] = 0; continue; } // 计算向量夹角的余弦值 double dot_product = v1[0] * v2[0] + v1[1] * v2[1]; double cos_theta = dot_product / (len1 * len2); // 限制范围防止数值溢出 cos_theta = max(min(cos_theta, 1.0), -1.0); double theta = acos(cos_theta); // 曲率值:π - θ,范围0~π,值越大曲率越高 curvatures[i] = M_PI - theta; }
方法二:三点拟合圆法(更精准,适合需要定量曲率的场景)
通过三个点拟合圆,曲率是圆半径的倒数,半径越小曲率越高,还能区分凹凸方向(保留正负)。
// 辅助函数:三点拟合圆计算曲率 double fitCircleCurvature(Point p1, Point p2, Point p3) { double x1 = p1.x, y1 = p1.y; double x2 = p2.x, y2 = p2.y; double x3 = p3.x, y3 = p3.y; double A = x2 - x1; double B = y2 - y1; double C = x3 - x1; double D = y3 - y1; double E = A*(x1+x2) + B*(y1+y2); double F = C*(x1+x3) + D*(y1+y3); double G = 2*(A*(y3-y2) - B*(x3-x2)); if (fabs(G) < 1e-6) return 0; // 三点共线,曲率为0 double cx = (D*E - B*F)/G; double cy = (A*F - C*E)/G; double r = sqrt(pow(x1 - cx, 2) + pow(y1 - cy, 2)); if (r < 1e-6) return 0; // 曲率值,保留正负可区分凹凸,取绝对值则只看大小 return 1.0 / r; } // 计算曲率(圆拟合法) vector<double> curvatures(n); int k = 3; // 取前后3个点,平滑噪声 for (int i = 0; i < n; ++i) { int prev_idx = (i - k + n) % n; int next_idx = (i + k) % n; curvatures[i] = fabs(fitCircleCurvature(boundary[prev_idx], boundary[i], boundary[next_idx])); }
3. 曲率归一化与颜色映射
把曲率值归一到0~1范围,然后映射到红-黄渐变:黄色对应低曲率(接近直线),红色对应高曲率(急转弯)。这里用BGR通道直接插值,直观易懂。
// 归一化曲率到0~1范围 double max_curv = *max_element(curvatures.begin(), curvatures.end()); double min_curv = *min_element(curvatures.begin(), curvatures.end()); double range = max_curv - min_curv; if (range < 1e-6) range = 1; // 防止所有曲率相同导致除以0 // 创建可视化图像,白色背景 Mat curvature_vis = Mat::zeros(src.size(), CV_8UC3); curvature_vis.setTo(Scalar(255, 255, 255)); // 遍历每个边界点,绘制对应颜色 for (int i = 0; i < n; ++i) { double t = (curvatures[i] - min_curv) / range; // t=0→低曲率(黄),t=1→高曲率(红) uchar green = 255 * (1 - t); uchar red = 255; // OpenCV是BGR顺序,所以蓝色通道设为0 Vec3b color(0, green, red); // 画实心圆,点更大更明显 circle(curvature_vis, boundary[i], 2, color, -1); } // 显示结果 imshow("Boundary Curvature Color Map", curvature_vis); imwrite("curvature_map.png", curvature_vis); waitKey(0); return 0; }
4. 优化小技巧
- 去噪声:如果边界有毛刺,先对原图做高斯滤波(
GaussianBlur)再提取轮廓,或者用approxPolyDP对轮廓做平滑(注意参数ε不要太大,避免丢失细节)。 - 邻域调整:k值越大,曲率结果越平滑,但会损失局部细节;k越小越敏感,噪声也越多,根据你的图像调整。
- 多轮廓处理:如果输入图像有多个边界,遍历
contours数组,逐个计算曲率并绘制即可。
内容的提问来源于stack exchange,提问作者Saania
相关产品推荐
相关产品推荐

