首页 / 资讯中心 / 文章详情

曲线拟合:图像处理算法的底层基石与工程实践

曲线拟合:图像处理算法的底层基石与工程实践 ★ FEATURED ARTICLE
曲线拟合这四个字乍一听像是数值分析课本里的老古董跟现在动辄上亿参数的视觉大模型比起来显得特别朴素。但我做了这么多年图像处理相关的项目越来越有一个体会真正决定一个视觉算法能不能落地的往往不是那些花哨的网络结构而是最底层那几根曲线拟合得好不好。边缘检测出来的点云要不要拟合成直线和圆弧、车道线在弯道里怎么保持平滑、工业检测里亚像素级的圆心怎么定、相机标定的畸变模型怎么解——这些活儿背后全是曲线拟合在撑着。这篇东西我想聊的就是这件事曲线拟合在图像处理算法里到底处在什么位置它解决的是哪些别人不愿意碰的脏活累活以及在真实项目里怎么把它用对、用稳。适合两类人看一类是刚入行做视觉、觉得拟合就是调个polyfit的年轻工程师另一类是做过一些项目、被拟合结果抖来抖去折磨过、想搞清楚背后门道的老手。我会把最小二乘的取舍、鲁棒估计的必要性、RANSAC的实操参数、以及几个典型场景边缘直线拟合、圆/椭圆拟合、车道曲线、畸变校正的完整链路都拆开讲中间穿插我自己踩过的坑。1. 为什么图像处理的底层离不开曲线拟合1.1 从像素点到几何模型的那一步跨越图像处理算法拿到的原始数据本质上是一堆离散的像素。边缘检测给你的是几百上千个边缘点霍夫变换给你的是参数空间里的峰值角点检测给你的是坐标列表。这些点单独看没什么意义真正有用的是它们背后代表的几何结构——这是一条车道线、这是一个圆孔、这是一段圆弧轮廓。而从一堆点到一个几何模型的转换数学上就是曲线拟合。我常跟团队里的新人打比方边缘检测像是把一幅画撕成了一地碎纸片曲线拟合就是把这些碎纸片重新拼回原来的形状。碎纸片本身信息量很低还带着毛刺噪声点、有的还撕错了离群点拼的过程既要还原形状又要容忍这些瑕疵。这个拼的质量直接决定了后续测量、定位、识别的精度上限。举个具体的工业上测一个零件的圆孔直径边缘提取出来的点大概有几百个每个点的定位精度受限于像素分辨率和亚像素插值单点误差可能有0.1到0.3个像素。如果你直接拿两个对径点算距离误差会被放大但如果你用几百个点做最小二乘圆拟合误差会按照点数开方的关系被平均掉理论上能压到0.01像素量级。这就是拟合的价值——它不是简单地连点成线而是在做统计意义上的误差抑制。1.2 拟合精度如何成为整条算法链的瓶颈很多视觉项目的精度瓶颈不在前端采集也不在后端决策恰恰卡在中间这一步拟合上。我见过一个典型的案例某检测设备要做亚像素级的直线度测量前端用了很好的工业相机和远心镜头边缘提取也做了亚像素插值结果测出来的直线度一直在几个微米上下跳怎么调都不稳。后来排查发现拟合用的是普通最小二乘而边缘点里混进了几个反光造成的离群点把整条直线拽偏了。换成鲁棒拟合之后重复性立刻好了三倍。这件事说明一个道理拟合环节的鲁棒性往往比前端硬件的升级更能决定最终精度。因为前端再怎么提升噪声和离群点永远存在而拟合是最后一道能把它们洗干净的关口。关口没守住前面所有的精度投入都白费。1.3 不同场景对拟合的诉求差异有多大曲线拟合不是一个一招鲜的东西不同场景对它的要求差别极大这也是为什么它值得单独拿出来讲。我大致归了几类场景类型典型任务对拟合的核心诉求常用方法精密测量圆孔直径、直线度、平面度极高精度、可重复性最小二乘 亚像素 鲁棒加权自动驾驶/辅助驾驶车道线、道路边界实时性、对遮挡和噪声鲁棒RANSAC 样条/多项式工业定位工件位姿、基准线稳定性、抗离群点鲁棒最小二乘、RANSAC相机标定畸变模型参数全局最优、数值稳定非线性最小二乘LM算法轮廓分析形状描述、缺陷检测对形状变化的敏感度样条拟合、傅里叶描述子你看同样是拟合一条曲线测量场景要的是精度和稳定驾驶场景要的是速度和抗干扰标定场景要的是全局收敛。用错方法轻则精度不够重则算法直接崩。所以后面我会针对不同诉求分别讲方法选型而不是笼统地给一个标准答案。2. 最小二乘是起点但不是终点2.1 普通最小二乘的隐含假设以及它什么时候会骗你普通最小二乘OLS几乎是所有人学拟合时接触的第一个方法它的目标很直白让所有点到拟合曲线的残差平方和最小。数学上漂亮求解也简单直线拟合甚至能写成闭式解。但它有一个非常强的隐含假设——所有点的误差服从零均值、同方差的高斯分布且没有离群点。问题就在这个假设上。真实图像里的边缘点误差分布远不是理想高斯反光会造成成片的偏移点遮挡会造成点缺失纹理复杂区域会冒出完全错误的点。这些点一旦进入平方和因为误差被平方放大一个偏离10个像素的离群点其话语权相当于100个偏离1像素的正常点。结果就是拟合结果被少数坏点绑架。我做过一个对比实验同一条边缘上500个正常点加5个离群点普通最小二乘拟合出的直线角度偏差能到0.8度而实际应该控制在0.05度以内。这个偏差放到测量任务里就是灾难。所以我的经验是只要你的数据里可能存在离群点就不要直接用OLS这是新手最容易犯的错。2.2 加权最小二乘给每个点一个可信度加权最小二乘WLS是最直接的改进思路既然有些点不可信那就给它们低权重。权重怎么来常见的有几种来源基于梯度幅值边缘点的梯度越强定位越可靠权重越高。这个在Canny边缘上很好用弱边缘点权重压低。基于局部一致性某点周围邻域内的边缘方向越一致说明它越可能是真实边缘权重越高。基于距离的迭代重加权先做一次拟合然后根据残差大小重新分配权重残差大的点权重降低反复迭代。这就是IRLS迭代重加权最小二乘的核心思想。IRLS的权重函数选择有讲究。常用的有Huber权重和Tukey双权重import numpy as np def huber_weight(residual, delta1.0): Huber权重小残差保持线性大残差降为反比 abs_r np.abs(residual) return np.where(abs_r delta, 1.0, delta / abs_r) def tukey_weight(residual, c4.685): Tukey双权重超过阈值直接归零更激进 u residual / c return np.where(np.abs(u) 1.0, (1 - u**2)**2, 0.0)Huber比较温和适合离群点不多、但噪声偏大的情况Tukey更激进会把明显偏离的点直接踢出局适合离群点比例较高但你想彻底排除它们的场景。我一般先用Huber跑一遍看残差分布如果发现残差有明显的长尾再换Tukey。注意IRLS的迭代次数不是越多越好。我见过有人设50次迭代结果在噪声大的数据上权重反复震荡反而不收敛。一般5到10次就足够配合一个收敛判据比如权重变化小于1e-4就停更稳。2.3 从残差图判断该用哪种损失函数选损失函数不要凭感觉看残差图最靠谱。我的做法是先用OLS拟合一次把每个点的残差画出来横轴是点的序号或位置纵轴是残差。如果残差均匀分布在零附近、没有明显离群OLS就够了别过度设计。如果残差里零星几个点特别大用Huber或Tukey这类鲁棒损失。如果残差呈现系统性的弯曲比如中间正两头负说明模型本身选错了——直线拟合一条其实是圆弧的边这时候换模型比换损失函数有用。这个残差图诊断的习惯是我从一位做精密测量的前辈那儿学来的看似原始但比任何自动判据都直观。很多拟合问题画一张残差图就一目了然了。3. RANSAC当离群点多到加权也救不回来3.1 RANSAC到底在赌什么加权最小二乘的前提是离群点是少数可现实里经常不是。比如在杂乱背景里找一条车道线边缘点里可能有超过一半是背景纹理产生的干扰点。这时候加权也救不回来因为少数服从多数的前提已经不成立了。RANSAC随机采样一致性就是为这种场景生的。它的逻辑说白了就是反复抽样、投票选最优每次随机抽最少数量的点直线抽2个圆抽3个拟一个模型然后数一数有多少点离这个模型足够近内点内点最多的那个模型就是赢家。它赌的是——只要内点比例不是低得离谱多抽几次总能抽到一组全是内点的样本。这个赌是有数学保证的。采样次数N和内点比例w、期望成功率p的关系是N log(1 - p) / log(1 - w^s)其中s是最小样本数。假设内点比例w0.5直线拟合s2想要99%的成功率算下来N≈16次如果w降到0.3N就要涨到约70次w0.2时更是要接近200次。这个公式非常实用它告诉你内点比例越低采样次数要指数级增加这也是RANSAC在极端场景下会变慢的根本原因。3.2 阈值、采样次数、内点判据的实操取值RANSAC有三个关键参数调不好就白搭距离阈值判断一个点是不是内点的门槛。这个不能拍脑袋要跟你的测量噪声挂钩。经验做法是先估计边缘点的定位噪声σ可以用相邻点的局部方差估阈值取2到3倍σ。比如亚像素边缘噪声约0.2像素阈值取0.5到0.6像素比较合适。阈值太小内点被误杀模型不稳太大离群点混进来失去意义。采样次数用上面的公式算但实际要留余量。我一般按估计的内点比例再打个八折来算N宁可多抽几次。同时设一个上限比如1000次防止极端情况下死循环。内点判据的度量方式直线用点到直线的垂直距离圆用点到圆心的距离与半径之差。这里有个细节——用代数距离还是几何距离。代数距离算得快但有偏几何距离准但慢。对精度要求高的场景比如测量建议用几何距离对实时性要求高的比如车道线代数距离够用。def ransac_line_fit(points, threshold0.6, max_iter500, confidence0.99): best_model, best_inliers None, [] n len(points) iter_count 0 needed max_iter while iter_count needed: # 随机抽两个点 idx np.random.choice(n, 2, replaceFalse) p1, p2 points[idx] if np.allclose(p1, p2): continue # 构造直线 axbyc0 a p2[1] - p1[1] b p1[0] - p2[0] c -(a * p1[0] b * p1[1]) norm np.hypot(a, b) # 计算所有点到直线的距离 dist np.abs(a * points[:,0] b * points[:,1] c) / norm inliers np.where(dist threshold)[0] if len(inliers) len(best_inliers): best_inliers inliers best_model (a, b, c) # 动态更新所需采样次数 w len(inliers) / n if w 0: needed min(max_iter, int(np.log(1-confidence)/np.log(1-w**2)) 1) iter_count 1 return best_model, best_inliers这段代码里有个容易被忽略的优化动态更新采样次数。每找到一组更好的内点就重新估计内点比例w重算还需要抽多少次。这样在数据好的时候能提前退出省时间。这个技巧在实时系统里很关键。3.3 RANSAC之后为什么还要再拟合一次很多人RANSAC跑完直接拿那个抽出来的两点当最终结果这是大错。RANSAC的作用是筛选内点它给出的模型只是用最少样本拟的精度很差。正确做法是拿到内点集合后用这些内点再做一次最小二乘或加权最小二乘拟合得到最终的高精度模型。这一步叫精化refinement是RANSAC标准流程里不可省的一环。我见过不少代码漏了这步结果精度比直接OLS还差还怪RANSAC不好用。记住RANSAC负责选对点最小二乘负责算准线两者是接力关系不是替代关系。4. 圆、椭圆与样条不同几何形状的拟合门道4.1 圆拟合的代数法与几何法之争圆拟合在工业检测里太常见了——孔位、轴承、镜头光心都要拟圆。方法上分两派代数法和几何法。代数法的代表是Kasa法它把圆的方程(x-a)²(y-b)²r²展开成x²y²DxEyF0然后对D、E、F做线性最小二乘。优点是有闭式解、快缺点是有偏——它最小化的是代数距离不是真实的几何距离当点分布不均匀比如只覆盖一段圆弧时偏差会很明显。几何法直接最小化点到圆周的几何距离精度高但要迭代求解通常用Gauss-Newton或LM。我的经验是点覆盖完整圆周、精度要求一般用Kasa够了点只覆盖部分圆弧、或者要求亚像素精度必须用几何法。这里有个坑要提醒当点只覆盖一小段圆弧时圆拟合是病态问题——半径和圆心位置会强耦合微小的噪声就能让拟合出的半径剧烈变化。我做过测试覆盖60度圆弧的点噪声0.1像素拟合半径的波动能到好几个像素。这种场景下要么增加约束比如已知半径范围要么干脆别拟圆改用其他几何特征。4.2 椭圆拟合为什么比圆难一个量级椭圆拟合比圆拟合难难在参数多5个自由度中心2个、长短轴2个、旋转角1个而且约束条件是非线性的比如判别式约束b²-4ac0。直接做几何拟合很容易陷入局部最优或者数值不稳定。工程上常用的折中是Fitzgibbon的直接最小二乘椭圆拟合它把椭圆约束巧妙地融进线性系统能一次性解出参数速度快、稳定性好。缺点是同样有代数距离的偏差。对精度要求高的场景可以拿它的结果做初值再用几何法精化。椭圆拟合在什么场景用最典型的是透视投影下的圆——比如斜着看一个圆孔它在图像里就是椭圆。这时候拟出椭圆再结合相机内参反投影就能还原真实的圆。这条链路在标定和位姿估计里非常常见。4.3 样条拟合当形状根本不能用解析式描述前面讲的直线、圆、椭圆都是参数化模型形状由几个参数完全确定。但现实里很多轮廓根本没有解析式——比如自由曲面零件的边缘、手写笔迹、医学图像的器官轮廓。这时候就得上样条拟合。最常用的是B样条和三次样条。B样条的好处是局部支撑——改动一个控制点只影响局部形状不会牵一发动全身这对拟合带局部起伏的轮廓特别友好。三次样条则保证二阶连续曲线足够光滑。样条拟合的关键参数是控制点数量或节点数量这个直接决定拟合的松紧。点太少曲线太僵硬拟合不足点太多曲线跟着噪声乱扭过拟合。我的调法是从少到多逐步增加控制点观察残差——当残差下降到某个平台、再增加控制点残差也不明显下降时就停。这个思路跟正则化里的肘部法则是一回事。提示样条拟合一定要加平滑约束比如惩罚二阶差分否则在噪声大的数据上会拟出蛇形曲线。平滑系数的选取同样用残差平台法别硬调。5. 把拟合塞进真实算法链路几个典型场景拆解5.1 车道线检测里的分段拟合与连续性约束车道线是曲线拟合最经典的应用场景之一。它的难点不在单帧拟合而在时序上的稳定性和空间上的连续性。单帧里车道线点云往往被遮挡、被车辆压断直接对整条线做一次多项式拟合遇到断点就会拟出诡异的形状。工程上的做法是分段拟合 连续性约束把车道线按远近分成几段每段单独拟合段与段之间加连续性约束位置连续、一阶导连续。这样即使中间某段点缺失也能靠约束撑住形状。另一个关键是模型选择。直道用一次多项式直线就够弯道得上二次甚至三次。但你不能每帧都换模型否则曲率估计会跳变。我的做法是维护一个曲率状态的滤波根据历史曲率决定当前用几阶模型模型切换时做平滑过渡。这套逻辑听起来复杂但实测下来对弯道的稳定性提升非常明显。5.2 相机标定中的畸变模型拟合相机标定的本质就是拟合一个从理想图像坐标到实际像素坐标的映射其中最核心的是畸变模型。常见的径向畸变用多项式描述r_distorted r(1 k1·r² k2·r⁴ k3·r⁶)这里的k1、k2、k3就是待拟合的参数。标定的过程就是拿一堆已知世界坐标的标定点通过非线性最小二乘通常用LM算法解出这些参数。这块的坑在于参数的可辨识性。如果标定板的姿态不够丰富比如全是正对着拍某些畸变参数就解不准甚至解出物理上不合理的值。我一般要求标定板至少覆盖画面各个角落、且有明显的倾斜姿态这样k1、k2才能被可靠地约束住。另外高阶项k3不是越多越好数据不够时加高阶项会让解发散宁可少用。5.3 亚像素边缘的直线度测量完整链路这是精密测量里最常见的任务我把完整链路走一遍粗定位用Canny或Sobel找到边缘的大致位置得到像素级边缘点。亚像素精化沿边缘法向做灰度梯度插值比如用二次拟合梯度峰值把每个点精化到0.1像素精度。离群点剔除先用RANSAC粗筛去掉明显偏离的点。加权最小二乘精拟合用内点做WLS权重取梯度幅值。残差诊断画残差图确认没有系统性弯曲有的话说明边缘不是直线得换模型。直线度计算所有内点到拟合直线的距离的最大值减最小值就是直线度。这条链路里第2步和第4步是精度关键第3步是稳定性关键。我踩过的最大的坑在第2步——亚像素插值的方向如果没取对应该沿梯度方向也就是边缘法向精化出来的点会系统性地偏后面怎么拟合都救不回来。5.4 轮廓分析中的样条平滑与特征提取在形状分析、缺陷检测里经常要先对提取出的轮廓做样条平滑再在平滑后的曲线上算曲率、找角点、测凹凸。这里的平滑不是为了好看而是因为原始轮廓的像素级锯齿会让曲率计算完全失效——曲率是二阶导对噪声极其敏感不平滑直接算出来的曲率全是毛刺。我的做法是先用B样条做适度平滑控制点数量约为轮廓点数的1/20到1/10然后在样条上解析地求导算曲率。样条的好处是导数有解析表达式比在离散点上做差分稳定得多。平滑程度要拿捏——过度平滑会把真实的缺陷比如小凹坑也抹掉这个平衡点得根据缺陷的最小尺度来定。6. 那些文档里不会写的拟合经验6.1 坐标系和数值尺度会悄悄毁掉你的拟合这是最隐蔽的坑。如果你的坐标是像素坐标动辄几百上千而拟合的是高阶多项式那x³、x⁴项会大到让数值求解直接溢出或者病态。解决办法很简单拟合前先把坐标归一化减去均值、除以尺度拟合完再变换回去。这个操作在标定、样条拟合里几乎是必须的但很多教程根本不提。我见过一个团队调了两天拟合不收敛最后发现就是没做坐标归一化矩阵条件数大到1e12。加了三行归一化代码问题当场消失。这种坑只有真做过的人才会有肌肉记忆。6.2 拟合结果的可视化验证比任何指标都直接别迷信残差RMS这种单一指标。RMS小不代表拟合对——一条被离群点拽偏的直线如果离群点恰好对称分布RMS可能还挺小。我的习惯是永远把拟合结果叠回原图看把拟合的曲线画在原始图像上放大到像素级用眼睛扫一遍。点是不是都贴着线、线有没有穿过不该穿的地方、端点处有没有翘这些一眼就能看出来。这个习惯救过我很多次。有一次一个圆拟合的RMS很漂亮但叠图一看拟合的圆明显偏了半个像素——原因是边缘点只覆盖了圆的四分之三剩下四分之一是遮挡代数法在这种情况下系统性偏移。光看指标根本发现不了。6.3 什么时候该放弃拟合改用别的方法拟合不是万能的。有几种情况硬拟合不如换思路点数太少少于模型自由度的两三倍拟合结果没有统计意义不如直接用几何关系硬算。形状根本不匹配模型拿直线去拟合一条明显弯曲的边残差图会呈现系统性弯曲这时候该换模型而不是调参数。数据退化比如所有点几乎共线时拟圆或者点集中在一个小区域问题本身病态任何拟合都不稳。识别这些情况的能力比会调多少种拟合算法更重要。我常跟新人说先判断该不该拟合再考虑怎么拟合顺序反了就是白费力气。6.4 实时系统里拟合的性能取舍在嵌入式或者高帧率的场景里拟合的开销不能忽视。几个实用的优化降采样拟合前把点云抽稀比如隔点取精度损失很小但速度快几倍。因为拟合本身就是在做平均点多到一定程度边际收益递减。增量拟合时序数据比如视频里的车道线不必每帧从头拟合可以用上一帧的结果做初值只做少量迭代精化。固定迭代次数实时场景下别用收敛判据控制迭代直接固定迭代次数比如LM固定5次时间可预测避免偶发的长尾延迟。这些取舍没有标准答案得根据你的帧率预算和精度要求现场权衡。我的原则是先保证时间确定性再在预算内榨精度反过来做在实时系统里迟早出事。曲线拟合这件事越做越觉得它像视觉算法的地基——平时看不见但楼能盖多高全看它。上面这些方法、参数、坑都是我在一个个项目里磨出来的没有哪条是课本直接给的。你要是正在被拟合结果的抖动或者精度折磨不妨从残差图和叠图验证这两个最朴素的习惯开始很多问题会自己浮出水面。
阅读完成 · 觉得有帮助?
咨询建站