ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

OpenCV单目视觉实战:用solvePnP与轮廓相交抓出三个缺陷

OpenCV单目视觉实战:用solvePnP与轮廓相交抓出三个缺陷 1. 从抓出三个缺陷说起这个案例到底在做什么第一次看到抓出三个缺陷这个标题很多人会以为是某种质检流水线的项目其实它讲的是 OpenCV 里一个非常经典、但平时很少有人系统讲清楚的能力组合用单目相机、已知几何关系的物体反推出物体在三维空间中的位姿再结合轮廓相交判断把画面里不该出现的东西标记出来。关键词里那几个函数——select3dobj、solvePnP、intersectConvexConvex、image2plane——基本就把这条技术链路串起来了。我先把这条链路用大白话讲一遍方便不同基础的读者都能跟上。假设你面前有一块平板或者一个规则零件相机拍了一张图。你知道这个物体真实的物理尺寸比如边长、孔位坐标也知道相机的大致内参。那么只要在图像里找到物体上几个已知点的像素坐标就能用solvePnP反推出物体相对于相机的位置和朝向也就是所谓的位姿pose。有了位姿就能把物体三维模型上的点投影回图像平面这个过程在示例里对应image2plane这类投影换算。投影之后你就能拿理论上应该出现的轮廓和图像里实际检测到的轮廓做比对用intersectConvexConvex算它们的相交面积交并比一低就说明这块区域有异常——可能是缺件、多件、错位、破损这就是缺陷。select3dobj在这个流程里扮演的是选点/选目标的角色它负责从三维对象上挑出用于求解位姿的对应点或者从候选里筛出符合几何约束的那一组。很多人卡住的地方不是不会调solvePnP而是不知道该喂给它哪些点、这些点怎么和图像点一一对应select3dobj解决的正是这个配对问题。所以这个案例的定位很明确它不是教你训练一个深度学习缺陷检测模型而是教你用几何 投影 轮廓运算这套传统视觉方法在结构已知、光照可控的场景下快速搭出一个能定位异常区域的检测流程。适合谁看适合已经会读写图像、会找轮廓但一碰到三维位姿投影比对就发怵的工程师也适合做工业质检、尺寸测量、装配校验方向想用轻量方案替代重型模型的朋友。它的最大好处是可解释、可复现、对样本量要求低缺点是依赖标定精度和几何先验场景一变就得重新配参数。下面我不按函数手册的方式讲而是按我实际搭这套流程的顺序把每一步为什么这么做、哪里最容易翻车掰开揉碎说清楚。2. 位姿求解这条主线solvePnP 到底在算什么2.1 为什么单目相机也能算出三维位置先破一个常见误解单张图没有深度信息凭什么能算出物体离相机多远答案是靠已知尺寸做尺度约束。如果你知道物体上两个点的真实距离是 50mm又在图像里量出它们相隔 200 像素那么结合相机焦距就能反推出这段距离对应的深度。solvePnP的本质就是解这样一个方程组给定若干组三维世界点 ↔ 二维图像点的对应关系求出一个旋转向量和一个平移向量使得三维点经过这个变换再投影后尽量贴合观测到的二维点。数学上它解的是透视投影模型下的最小化重投影误差问题。你不需要手推公式但必须理解它的输入输出输入objectPoints物体坐标系下的三维点单位通常是毫米、imagePoints对应的像素坐标、相机内参矩阵cameraMatrix、畸变系数distCoeffs。输出rvec旋转向量可用Rodrigues转成旋转矩阵、tvec平移向量即物体原点在相机坐标系下的位置。这里有个关键点很多人忽略objectPoints的坐标系是你自己定义的。你可以把物体某个角点定为原点也可以把中心定为原点只要所有三维点和图像点严格对应同一套定义就行。定义混乱是后面投影对不上的头号原因。2.2 点数够不够、分布好不好直接决定成败solvePnP最少需要几个点理论上 4 个不共面的点就能解但实际工程里我强烈建议至少 6 个且尽量分散在物体不同区域。原因很简单如果所有点都挤在一个小角落方程对旋转的约束很弱解出来的位姿在另一个方向上会剧烈抖动投影自然就飘。我踩过的一个典型坑拿一块矩形板只用了四个角点。结果板子稍微倾斜rvec就跳得厉害投影轮廓和实际轮廓能差十几个像素。后来改成四角 中心孔位 一条边中点共 7 个点稳定性立刻上来了。这就是select3dobj存在的意义——它不是随便选点而是按几何分布去挑一组良态的点。你可以把它理解成一个选点策略器优先选那些在物体上跨度大、不共线、不共面的特征点。提示判断点分布好不好有个土办法——把这些三维点投影到图像上看它们在画面里是不是铺得够开。如果全挤在一小块果断换点。2.3 内参和畸变最容易被差不多害死的地方solvePnP的精度对内参极其敏感。我见过太多人直接拿一个大概的焦距就上结果位姿怎么调都不对最后怀疑是算法问题其实是内参错了。相机内参矩阵长这样fx 0 cx 0 fy cy 0 0 1fx、fy是焦距像素单位cx、cy是主点。如果你没有标定板至少要做一次棋盘格标定把fx、fy、cx、cy和畸变系数k1, k2, p1, p2, k3都标出来。畸变系数尤其重要广角镜头边缘的桶形畸变会让图像点整体偏移solvePnP会把这个偏移误当成位姿误差去补偿结果就是解出来的姿态是歪的。实操建议标定完把重投影误差看一眼正常应该在 0.5 像素以内。如果超过 1 像素要么标定板拍得不够多角度要么图像本身模糊别急着往下做。3. 投影回图像image2plane 与轮廓比对的衔接3.1 从三维位姿到二维轮廓的完整换算拿到rvec、tvec之后把物体模型上的点投影回图像走的是标准流程先用旋转矩阵和平移向量把三维点变换到相机坐标系再用内参矩阵投影到归一化平面最后乘焦距加主点得到像素坐标。OpenCV 里可以直接用projectPoints一步到位示例里image2plane这类函数做的就是这件事的封装或变体。这一步的产出是理论轮廓如果物体在位姿正确、没有缺陷那么它应该出现在图像里的边界。接下来你要拿它和实际轮廓比对。实际轮廓怎么来常见做法是先做灰度化、滤波、二值化threshold或adaptiveThreshold再findContours提取。这里有个顺序问题先投影还是先提轮廓我的经验是先把实际轮廓提出来再投影理论轮廓因为投影依赖位姿而位姿依赖图像点图像点又来自实际特征逻辑上是先看现实再算理论。3.2 轮廓比对为什么用 intersectConvexConvexintersectConvexConvex算的是两个凸多边形的交集面积。为什么强调凸因为凸多边形的相交计算有成熟且高效的算法结果稳定。如果你的实际轮廓是凹的比如带缺口的零件直接丢进去会出错或者结果不可信。解决办法是先做凸包convexHull或者把凹轮廓拆成若干凸块分别比对。比对的核心指标是交并比IoUIoU 交集面积 / (理论面积 实际面积 - 交集面积)IoU 接近 1说明理论轮廓和实际轮廓高度重合这块区域正常IoU 明显偏低说明实际轮廓偏离了理论位置或者面积对不上——缺了一块、多了一块、整体移位都属于缺陷。这里要提醒一个坑轮廓的坐标系必须统一。findContours出来的点是图像坐标projectPoints出来的也是图像坐标看似一致但如果中间做过 ROI 裁剪、缩放、翻转坐标系就错位了。我建议在比对前把两套点都打印一下包围盒肉眼确认范围对得上再往下算。3.3 三个缺陷是怎么被抓出来的回到标题的三个缺陷。在典型实现里流程会维护一个应该正常的基准然后对画面里的多个候选区域逐一比对凡是 IoU 低于阈值的就记为异常。三个缺陷通常对应三类偏差缺陷类型表现判定依据位置偏移实际轮廓整体平移交集面积骤降但形状相似尺寸异常实际轮廓偏大或偏小面积比偏离 1 较多形状缺失局部轮廓凹陷或断裂凸包与原始轮廓差异大我实际调的时候发现阈值不能一刀切。位置偏移对 IoU 很敏感稍微移几个像素 IoU 就掉而尺寸异常可能 IoU 还很高但面积比已经不对。所以我一般同时看两个量IoU 和面积比两个都正常才算通过。这样能有效降低误报。4. 参数调优与常见翻车现场4.1 二值化阈值缺陷检测的隐形开关整套流程里最影响结果的往往不是solvePnP而是二值化阈值。阈值高一点轮廓瘦一圈低一点轮廓胖一圈。轮廓一胖一瘦IoU 就跟着变缺陷判定就跟着抖。我的做法是先用 Otsu 自动阈值跑一遍看整体效果再针对具体场景手动微调并且把阈值和 IoU 阈值绑定着调——因为两者是耦合的单独调一个没有意义。光照不均的场景全局阈值基本没戏得上adaptiveThreshold。但自适应阈值会产生很多噪点小轮廓记得用面积过滤contourArea把小于某个像素数的轮廓扔掉否则一堆碎点会干扰比对。4.2 位姿抖动为什么同一物体两次结果不一样如果你发现同一物体、同一位置连续两帧解出来的tvec差了好几毫米别急着怀疑相机。常见原因有三个图像点检测不稳定。角点检测受噪声影响像素级抖动会放大到位姿上。点太少或分布太集中。前面说过约束不足。用了SOLVEPNP_ITERATIVE但初值不好。可以换SOLVEPNP_EPNP或SOLVEPNP_SQPNP试试后者对平面物体更稳。我的经验是对平面物体优先用SOLVEPNP_IPPE系列它专门针对平面场景做了优化比通用迭代法稳得多。这个细节很多教程不讲但实测差别很明显。4.3 轮廓相交的边界情况intersectConvexConvex在两个多边形完全不相交时返回的相交面积为 0这没问题但如果两个多边形几乎重合浮点误差可能让结果出现极小负值或超出理论面积的值。稳妥做法是对结果做一次 clamp把面积限制在[0, min(areaA, areaB)]区间内。另外输入点最好是CV_32F类型且顺序一致都顺时针或都逆时针顺序乱了交集算出来是错的。注意如果你的轮廓点数特别多几千个点先做approxPolyDP简化既提速又降噪对 IoU 影响很小。5. 把这套流程落到工程里的几点心得5.1 标定一次不够要定期复核相机摔过、镜头拧过、对焦变过内参就可能变。我一般会在项目里留一个标定自检入口拿一块已知尺寸的标定板跑一遍solvePnP看重投影误差是否还在阈值内。超过就重新标定。这个习惯帮我避免过好几次算法突然不准的假故障。5.2 把正常样本的 IoU 分布先统计出来不要拍脑袋定 IoU 阈值。正确做法是先拿一批确认正常的样本跑一遍统计 IoU 的均值和标准差阈值定在均值减 3 倍标准差附近。这样阈值是有数据支撑的而不是我觉得 0.8 差不多。缺陷样本本来就少用正常样本的分布来定边界是最省事也最靠谱的办法。5.3 缺陷定位要能指出来而不只是报个数工程上光告诉用户有缺陷没用得把缺陷区域框出来。我的做法是把理论轮廓和实际轮廓的差集区域单独提取出来画成高亮掩膜叠加在原图上。这样操作员一眼就能看到哪里不对而不是对着一串 IoU 数字发呆。这个可视化步骤在调试阶段也极其有用能帮你快速判断是位姿错了还是轮廓提取错了。5.4 什么时候该放弃传统方法说句实在话这套几何方法有明确的适用边界物体结构已知、刚性好、光照可控、背景干净它又快又准。但如果是柔性物体、表面纹理复杂、缺陷形态千变万化硬套这套流程会调到你怀疑人生。这时候该上学习类方法就上别跟几何死磕。我个人的判断标准是如果正常样本的 IoU 分布都收不紧说明几何先验不成立趁早换路线。最后分享一个我调试时常用的小技巧把rvec、tvec、IoU、面积比这几个量实时打印在画面上边移动物体边观察数值变化。你会对哪个参数在什么情况下敏感建立起非常直观的感觉这比看任何文档都快。等这套感觉有了再回头调阈值基本就是几分钟的事。
返回列表