Steger算法原理详解

看了好久,感觉明白一些,记录一下
Steger提取激光中心线的原理大致为:
首先将激光图片高斯模糊一下,卷积核大小要为激光线条的宽度,将激光的截面光强分布变为理想的正态函数。
实际的激光线条有一个宽度
在这里插入图片描述
要变为理想无宽度的
在这里插入图片描述
然后求出每一个点对应的二阶导数(hessian矩阵)、一阶导数的值,来构造一个该像素点附近的光强分布函数(泰勒展开)。
(一阶、二阶导数通过构造卷积核来计算,代码中体现)
求出二阶导数的同时,可以得到hessian矩阵。
在这里插入图片描述
Hessian矩阵的特征值就是形容其在该点附近特征向量方向的凹凸性,特征值越大,凸性越强。
所以hessian特征值绝对值最大的对应的特征向量就是光强变化最快的方向,我们可以向这个地方去寻找最强光强的像素点。(也就是中心线上的点)
构造出当前像素附近的光强分布的多项式(泰勒展开)如下图。
e指的是当前点坐标,I(u0,v0)代表当前像素的光强值,[Iu,Iv]代表一阶导数的值,H(u,v)代表二阶导数的值,t是一个未知参数
f(x+t*nx,y+t*ny)
现在已经知道了当前坐标(u0,v0)也知道了光强变化最快的方向(uu,vv),二阶导数(hessian矩阵)、一阶导数。
hessian矩阵特征值最大的对应的特征向量(uu,vv)表示梯度上升最快的方向(光强改变最快),要往这个方向改变
这个当前坐标附近的光强分布函数可以看作是关于t的一个分布函数
则可以对t求导,令导数为0的地方就是光强最强的地方(极值),也就是激光中心点。
在这里插入图片描述
可以改写成矩阵的形式
在这里插入图片描述
化简可以得到
在这里插入图片描述
计算出t后
(泰勒展开只能在很短的距离内适用)
如果teu和tev都小于0.5说明这个极值点就位于当前像素内。附近光强分布函数适用
如果teu和tev都很大,则说明这个光强最强点有点距离(同时附近光强分布函数不适用,不可以用t计算最强点),要跳过当前点,继续扫描距离激光中心更近的点。

代码如下

//path为一张图片的路径
std::vector<double> ProcessTool::StegerLine(std::string path)
{
	Mat img0 = imread(path, 1);
	//滤波去噪点,不需要可以注释掉,去掉之后灰度图要去注释
	cv::medianBlur(img0, img0, 5);
	cvtColor(img0, img0, CV_RGB2GRAY);
	threshold(img0, img0, 0, 255, THRESH_OTSU);
	
	Mat img;
	//灰度图
	//cvtColor(img0, img0, CV_BGR2GRAY);
	img = img0.clone();
	//高斯滤波
	img.convertTo(img, CV_32FC1);
	GaussianBlur(img, img, Size(5, 5), 2, 2);
	//高斯卷积,得到一个山峰强度的光条图
	//高斯卷积核实际上就是一个正态函数
	imshow("高斯滤波",img);
	waitKey(0);
	/**********************************/
	//构造了求导数的卷积核
	/***********************************/
	//这里使用的是数值计算的偏导数而非得到求导公式
	Mat m1, m2;
	m1 = (Mat_<float>(1, 2) << 1, -1);//x偏导一阶偏导 = f(x+1,y)-f(x,y)
	m2 = (Mat_<float>(2, 1) << 1, -1);//y偏导一阶偏导 = f(x,y+1)-f(x,y)
	Mat dx, dy;
	//使用m1卷积 当前坐标的灰度值-(x+1)的灰度值
	filter2D(img, dx, CV_32FC1, m1);//卷积
	filter2D(img, dy, CV_32FC1, m2);//卷积
	//二阶偏导数
	Mat m3, m4, m5;
	m3 = (Mat_<float>(1, 3) << 1, -2, 1);//求二阶x偏导的矩阵=f(x+1,y)+f(x-1,y)-2f(x,y)
	m4 = (Mat_<float>(3, 1) << 1, -2, 1);//二阶y偏导的矩阵
	m5 = (Mat_<float>(2, 2) << 1, -1, -1, 1);//二阶xy偏导矩阵=f(x+1,y+1)-f(x+1,y)-f(x,y+1)+f(x,y)
	Mat dxx, dyy, dxy;
	//求得对应点的偏导数值
	filter2D(img, dxx, CV_32FC1, m3);
	filter2D(img, dyy, CV_32FC1, m4);
	filter2D(img, dxy, CV_32FC1, m5);
	//hessian矩阵
	double maxD = -1;
	int imgcol = img.cols;
	int imgrow = img.rows;
	std::vector<double> Pt;
	for (int i = 0; i < imgcol; i++)
	{
		for (int j = 0; j < imgrow; j++)
		{
			//全黑的时候就不计算了,大于一个阈值的时候开始计算
			if (img0.at<uchar>(j, i) > 50)
			{
				Mat hessian(2, 2, CV_32FC1);
				//得到对应点的hessian矩阵
				//对每个点,构造出hessian矩阵
				hessian.at<float>(0, 0) = dxx.at<float>(j, i);
				hessian.at<float>(0, 1) = dxy.at<float>(j, i);
				hessian.at<float>(1, 0) = dxy.at<float>(j, i);
				hessian.at<float>(1, 1) = dyy.at<float>(j, i);
				//特征值和特征向量
				Mat eValue;
				Mat eVectors;
				// hessian矩阵2*2 对称矩阵, 有两个特征值
				// 获取当前点的特征值和特征向量
				// 存在两个特征值及对应特征向量
				eigen(hessian, eValue, eVectors);
				//std::cout << "eValue:" << eValue << std::endl << "eVectors:" << eVectors << std::endl;
				//nx为特征值最大对应的x,ny为特征值最大对应的y
				double nx, ny;
				double fmaxD = 0;
				//求特征值绝对值最大时对应的特征向量
				//Hessian矩阵的特征值就是形容其在该点附近特征向量方向的凹凸性,特征值越大,凸性越强。
				if (fabs(eValue.at<float>(0, 0)) >= fabs(eValue.at<float>(1, 0)))  
				{
					nx = eVectors.at<float>(0, 0);
					ny = eVectors.at<float>(0, 1);
					fmaxD = eValue.at<float>(0, 0);
				}
				else
				{
					nx = eVectors.at<float>(1, 0);
					ny = eVectors.at<float>(1, 1);
					fmaxD = eValue.at<float>(1, 0);
				}
				// 使用泰勒展开表示光强的分布函数f(x+t*nx,y+t*ny)可以表示当前坐标附近的光强分布
				// f(x+t*nx,y+t*ny) hessian矩阵特征值最大的对应的特征向量(nx,ny)表示梯度上升最快的方向(光强改变最快),要往这个方向改变  
				// 由于x,y,nx,ny已知,这个当前坐标附近的光强分布函数可以看作是关于t的一个分布函数
				// 则可以对t求导,令导数为0的地方就是光强最强的地方(极值),也就是激光中心点。
				// 如果t*nx和t*ny都小于0.5说明这个极值点就位于当前像素内,附近光强分布函数适用
				// 如果t*nx和t*ny都很大,附近光强分布函数不适用,则跳过这个点,继续扫描距离激光中心更近的点
				double t = -
					(nx * dx.at<float>(j, i) + ny * dy.at<float>(j, i))
					/
					(nx * nx * dxx.at<float>(j, i) + 2 * nx * ny * dxy.at<float>(j, i) + ny * ny * dyy.at<float>(j, i));
				//t* nx和t* nx代表光条中心点距离当前像素的距离
				if (fabs(t * nx) <= 0.5 && fabs(t * ny) <= 0.5)
				{
					Pt.push_back(i);
					Pt.push_back(j);
				}
			}

		}
	}
	cvtColor(img0, img0, CV_GRAY2BGR);
	//显示中心点
	ShowLine(Pt, img0);
	return Pt;
}
//显示点的函数
ShowLine(std::vector<double> Points, Mat image)
{
	for (int k = 0; k < Points.size() / 2; k++)
	{
		Point rpt;
		rpt.x = Points[2 * k + 0];
		rpt.y = Points[2 * k + 1];
		image.at<Vec3b>(rpt.y, rpt.x) = Vec3b(0, 0, 255);
		//circle(img0, rpt, 1, Scalar(0, 0, 255));
	}
	cv::imshow("GetLine", image);
	//cv::imwrite("output.bmp", img0);
	cv::waitKey(1);
}

参考:
1.https://blog.csdn.net/jiangxing11/article/details/120056536
2.https://blog.csdn.net/Dangkie/article/details/78996761
3.Steger C. An unbiased detector of curvilinear structures. IEEE Trans Pattern Anal Mach Intell[J]. IEEE Transactions on Pattern Analysis & Machine Intelligence, 1998, 20(2):113-125.

<think>好的,我现在需要处理用户关于光平面标定技术的问题。首先,用户提供了三个参考文献,我需要仔细分析这些内容,并整合出相关的技术方法、步骤和应用。 首先,引用[1]提到线结构光标定分为相机标定和光平面标定,其中相机标定已经比较成熟,比如张正友的方法,而光平面标定有不同的方法在实际中应用。这说明用户可能想了解光平面标定的具体方法,以及它与相机标定的关系。 引用[2]和[3]都提到了基于MATLAB的线激光光平面标定,特别是光平面拟合,通过线性拟合技术来估计参数,并且源码有详细注释。这里的关键点是光平面拟合的数学方法,可能涉及线性代数或最小二乘法。需要解释如何在相机坐标系下进行参数估计,可能需要坐标系转换的知识。 接下来,我需要组织这些信息。光平面标定通常的步骤可能包括数据采集(获取光条图像)、特征提取(提取光条中心线)、坐标转换(从图像坐标到相机坐标)、以及平面拟合(线性拟合得到平面方程)。每个步骤需要详细说明,比如特征提取可能使用灰度重心法或Steger算法,坐标转换涉及相机内参和畸变校正,平面拟合可能用最小二乘法。 应用方面,引用[1]提到线结构光在实际中应用广泛,比如三维重建、工业检测等。需要具体举例说明应用场景,如逆向工程、机器人导航等,同时结合参考文献中的信息。 此外,用户可能还关心不同方法的优缺点,比如传统标定方法对比基于主动视觉的方法,或者不同数学拟合方法的精度差异。但根据提供的引用,可能重点在基于特征点和拟合的方法。 最后,要生成相关问题,可能需要围绕标定步骤、优化方法、应用场景、与其他标定方法的比较等方面提问。 需要注意的是,回答中要正确使用LaTeX格式,比如数学公式用$$...$$,例如平面方程ax + by + cz + d = 0需要用正确的格式。同时,引用标识要放在句末,自然衔接。 现在需要确保结构清晰,分点说明技术方法、步骤、数学原理和应用,最后生成相关问题。还要检查是否所有数学表达式都正确使用了规定的格式,以及引用是否正确标注。</think>### 光平面标定技术详解 #### 一、技术原理与方法 1. **光平面标定目标** 通过数学建模和参数估计,确定线结构光在相机坐标系中的空间平面方程,通常表示为: $$ax + by + cz + d = 0$$ 其中$(a,b,c,d)$为待标定的平面参数[^1]。 2. **核心方法分类** - **基于特征点的标定法** 利用已知空间坐标的标定板(如棋盘格),通过光条与特征点的交点建立约束方程,求解光平面参数。 - **基于主动视觉的方法** 通过控制传感器或目标物体移动,结合多视角光条信息拟合平面,适用于动态场景[^3]。 - **线性拟合法** 从光条图像中提取离散点,利用最小二乘法拟合平面参数,MATLAB工具常采用此方法[^2]。 #### 二、标定步骤 1. **数据采集** - 使用线激光投射到标定板(如棋盘格或条纹板),采集多组光条图像。 - 标定板需在不同位姿下拍摄,以提高标定精度[^1]。 2. **特征提取** - 提取光条中心线,常用算法包括灰度重心法或Steger算法。 例如,灰度重心法计算公式为: $$x_c = \frac{\sum_{i=1}^n x_i I(x_i)}{\sum_{i=1}^n I(x_i)}$$ 其中$I(x_i)$为像素点$x_i$的灰度值[^3]。 3. **坐标转换** - 通过相机内参矩阵和畸变校正,将图像坐标$(u,v)$转换为相机坐标系下的三维坐标$(X_c,Y_c,Z_c)$。 4. **平面拟合** - 对转换后的三维点集,使用最小二乘法拟合平面方程: $$\min \sum_{i=1}^n (ax_i + by_i + cz_i + d)^2$$ 通过奇异值分解(SVD)求解最优参数[^3]。 #### 三、应用场景 1. **三维重建** 结合线结构光扫描,用于物体表面轮廓的高精度建模,如工业零件检测。 2. **机器人导航** 通过光平面标定实现环境深度感知,辅助机器人路径规划[^2]。 3. **逆向工程** 快速获取复杂曲面的几何数据,支持CAD模型重构。 #### 四、技术优势与挑战 - **优势** - 精度高:标定误差可控制在亚毫米级 - 抗干扰:线结构光受环境光影响小[^1] - **挑战** - 标定板制作精度影响结果 - 动态场景需高频数据采集
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值