行业资讯

线结构光中心点提取算法(原理,代码,优化)

发布时间:2026/8/2 8:27:07
线结构光中心点提取算法(原理,代码,优化) 1. 算法概述灰度重法线结构光重心点提取算法是一种用于结构光三维测量中对光条中心即重心点进行高精度、亚像素级定位的算法。2. 数学模型灰度重心横坐标计算公式取图像第行各列坐标处灰度C代码实现#include opencv2/opencv.hpp #include vector #include iostream #include algorithm using namespace cv; using namespace std; /** * 运行环境 C17 opencv 4.0 * brief 灰度重心法逐行提取激光条纹中心点 * param grayImg 输入单通道灰度图像 * param thresh 灰度阈值大于阈值才判定为激光有效像素 * param centerPoints 输出所有有效激光中心点 (x亚像素, y行号) * return 返回提取到的中心点总个数 */ int getLaserCenterByGrayCentroid(const Mat grayImg, int thresh, vectorPoint2f centerPoints) { centerPoints.clear(); int rows grayImg.rows; // 图像总行数(y方向) int cols grayImg.cols; // 图像总列数(x方向) // 逐行遍历图像每一行 y i for (int i 0; i rows; i) { double sum_xI 0.0; // 分子Σ x * I(x,i) double sum_I 0.0; // 分母Σ I(x,i) // 遍历当前行所有列像素 for (int x 0; x cols; x) { uchar grayVal grayImg.atuchar(i, x); // 灰度大于阈值属于激光区域参与重心计算 if (grayVal thresh) { sum_xI x * grayVal; sum_I grayVal; } } // 分母趋近于0该行无激光条纹跳过不计入总数 if (sum_I 1e-6) continue; // 灰度重心计算公式gi ΣxI / ΣI double g_i sum_xI / sum_I; centerPoints.emplace_back((float)g_i, (float)i); } // 返回中心点总数量 return centerPoints.size(); } int main() { // 1. 读取激光条纹图像 Mat src imread(camera_8.bmp); // 替换为你的图片路径 if (src.empty()) { cout 图片读取失败请检查文件路径 endl; return -1; } Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); // 转为单通道灰度图 // 2. 参数配置 int grayThresh 30; // 背景过滤阈值根据图像亮度自行调整 vectorPoint2f laserCenters; // 存储全部激光中心点 // 3. 提取中心线并获取中心点总数 int totalPointNum getLaserCenterByGrayCentroid(gray, grayThresh, laserCenters); // 4. 控制台输出总点数 部分坐标信息 cout endl; cout 整张图像激光中心点总数 totalPointNum endl; cout endl; // 可选打印前15个中心点坐标 cout 前15组中心坐标 (x亚像素, y行号) endl; int printCnt min(15, totalPointNum); for (int k 0; k printCnt; k) { printf(第%3d点: x%.3f , y%.0f\n, k 1, laserCenters[k].x, laserCenters[k].y); } // 5. 在原图绘制红色中心点可视化结果 Mat drawImg src.clone(); for (const auto pt : laserCenters) { circle(drawImg, pt, 1, Scalar(0, 0, 255), -1); } // 窗口展示 imshow(原图, src); imshow(灰度图像, gray); imshow(激光中心线提取结果, drawImg); waitKey(0); destroyAllWindows(); return 0; }原图线激光中心提取后3.算法优化从提取效果可知算法提取的效果在无反光处提取质量较好但当在反光处时提取精度会降低。可从两个角度优化本文只提出两点3.1 ROI约束参考裴凯.高反光表面线结构光三维测量技术研究[D].吉林大学代码为数学模型可从代码中分析#include opencv2/opencv.hpp #include vector #include iostream #include algorithm using namespace cv; using namespace std; /** * brief 带相邻行窗口约束的灰度重心法提取激光条纹中心 * 规则首行整行遍历后续每行仅在上一行中心xg±d范围内计算重心 * param grayImg 输入单通道灰度图 * param thresh 激光灰度阈值 * param searchWinD 左右搜索半宽d下一行检索区间 [xg-d, xgd] * param centerPoints 输出亚像素中心点序列 * return 有效中心点总数 */ int getLaserCenterByGrayCentroidTrack(const Mat grayImg, int thresh, int searchWinD, vectorPoint2f centerPoints) { centerPoints.clear(); int rows grayImg.rows; int cols grayImg.cols; // 记录上一行计算得到的中心x坐标初始标记为无效 double prevXg -1.0; for (int y 0; y rows; y) { double sum_xI 0.0; double sum_I 0.0; // 确定当前行需要遍历的x起止范围 int xStart, xEnd; if (prevXg 0) { // 没有上一行参考点首行/连续无激光行整行遍历 xStart 0; xEnd cols - 1; } else { // 以上一行中心为基准限定左右d像素窗口 xStart static_castint(round(prevXg)) - searchWinD; xEnd static_castint(round(prevXg)) searchWinD; // 边界钳位防止越界 xStart max(0, xStart); xEnd min(cols - 1, xEnd); } // 仅遍历限定区间内的列像素 for (int x xStart; x xEnd; x) { uchar grayVal grayImg.atuchar(y, x); if (grayVal thresh) { sum_xI x * grayVal; sum_I grayVal; } } // 当前行无有效激光像素跳过保留上一行标记不变 if (sum_I 1e-6) { continue; } // 计算本行亚像素中心 double currXg sum_xI / sum_I; centerPoints.emplace_back(static_castfloat(currXg), static_castfloat(y)); // 更新上一行x坐标给下一行做检索基准 prevXg currXg; } return centerPoints.size(); } int main() { // 1. 读取图像 Mat src imread(camera_8.bmp); if (src.empty()) { cout 图片读取失败请检查文件路径 endl; return -1; } Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); // 2. 可调参数 int grayThresh 30; // 背景灰度阈值 int d 25; // 相邻行左右搜索半宽度d可根据条纹粗细调整 vectorPoint2f laserCenters; // 3. 跟踪式重心提取 int totalPointNum getLaserCenterByGrayCentroidTrack(gray, grayThresh, d, laserCenters); // 4. 控制台信息输出 cout endl; cout 搜索窗口半宽 d d endl; cout 整张图像激光中心点总数 totalPointNum endl; cout endl; cout 前15组中心坐标 (x亚像素, y行号) endl; int printCnt min(15, totalPointNum); for (int k 0; k printCnt; k) { printf(第%3d点: x%.3f , y%.0f\n, k 1, laserCenters[k].x, laserCenters[k].y); } // 5. 绘制中心点可视化 Mat drawImg src.clone(); for (const auto pt : laserCenters) { circle(drawImg, pt, 1, Scalar(0, 0, 255), -1); } imshow(原图, src); imshow(灰度图像, gray); imshow(带行跟踪约束激光中心线, drawImg); waitKey(0); destroyAllWindows(); return 0; }3.2 能量法优化模型从代码分析能量法周渊,孟祥群,江登表,等.复杂干扰情况下的结构光条纹中心提取方法[J].中国激光,2020,47(12):172-180#include opencv2/opencv.hpp #include vector #include iostream #include algorithm #include cmath #include limits using namespace cv; using namespace std; /** * brief 基于距离-灰度代价函数约束窗口的灰度重心激光提取 * 代价公式 f d * exp(-H / k)寻找f最小位置作为下一行检索中心 * param grayImg 输入单通道灰度图 * param thresh 激光灰度阈值 * param k 指数常数(k1)控制灰度权重 * param winHalf 检索窗口半宽[A-best-winHalf, A-bestwinHalf] * param centerPoints 输出亚像素中心点序列 * return 有效中心点总数 */ int getLaserCenterCostTrack( const Mat grayImg, int thresh, double k, int winHalf, vectorPoint2f centerPoints ) { centerPoints.clear(); int rows grayImg.rows; int cols grayImg.cols; // 保存上一行有效中心x坐标初始无效标记 double prevX -1.0; for (int y 0; y rows; y) { double sum_xI 0.0; double sum_I 0.0; int xStart, xEnd; if (prevX 0) { // 首行/连续无激光行整行遍历 xStart 0; xEnd cols - 1; } else { // 1. 遍历本行所有x计算代价f寻找最小f对应的A点 double minF numeric_limitsdouble::max(); int bestA_x static_castint(round(prevX)); for (int x 0; x cols; x) { // 两点欧式距离d(x,y) 与 (prevX, y-1) double d sqrt(pow(x - prevX, 2) pow(y - (y - 1), 2)); uchar H grayImg.atuchar(y, x); // 代价函数 f d * exp(-H / k) double f 1-exp(-H/k) d; // 更新最小代价对应横坐标 // if(d) if (f minF) { minF f; bestA_x x; } } // 2. 以bestA_x为中心划定左右检索窗口 xStart bestA_x - winHalf; xEnd bestA_x winHalf; // 边界保护防止越界 xStart max(0, xStart); xEnd min(cols - 1, xEnd); } // 3. 窗口内灰度重心计算 for (int x xStart; x xEnd; x) { uchar grayVal grayImg.atuchar(y, x); if (grayVal thresh) { sum_xI (double)x * grayVal; sum_I grayVal; } } // 无有效激光像素跳过本行prevX保持不变 if (sum_I 1e-6) { continue; } // 计算亚像素中心 double currX sum_xI / sum_I; centerPoints.emplace_back((float)currX, (float)y); // 更新上一行中心供给下一行代价计算 prevX currX; } return centerPoints.size(); } int main() { // 1. 读取图像 Mat src imread(camera_8.bmp); if (src.empty()) { cout 图片读取失败请检查文件路径 endl; return -1; } Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); // 可调参数区 int grayThresh 30; // 灰度阈值 double k 8.0; // 指数常数 k1越大灰度影响越弱 int winHalf 20; // 最终重心计算窗口半宽 d // vectorPoint2f laserCenters; int totalPointNum getLaserCenterCostTrack(gray, grayThresh, k, winHalf, laserCenters); // 控制台信息输出 cout endl; cout 指数系数 k k endl; cout 重心窗口半宽 winHalf endl; cout 激光中心点总数 totalPointNum endl; cout endl; // 打印前15组坐标 cout 前15组中心坐标 (x亚像素, y行号) endl; int printCnt min(15, totalPointNum); for (int kk 0; kk printCnt; kk) { printf(第%3d点: x%.3f , y%.0f\n, kk 1, laserCenters[kk].x, laserCenters[kk].y); } // 绘制中心点可视化 Mat drawImg src.clone(); for (const auto pt : laserCenters) { circle(drawImg, pt, 1, Scalar(0, 0, 255), -1); } imshow(原图, src); imshow(灰度图像, gray); imshow(代价函数约束激光中心线, drawImg); waitKey(0); destroyAllWindows(); return 0; }优化后总结这些算法不仅仅适用于“直线型”场景下的光条还可适用于更复杂的场景但是鲁棒性待优化。