ARTICLE DETAIL

资讯详情

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

OpenCV物体尺寸测量:参考对象与像素度标定全流程解析

OpenCV物体尺寸测量:参考对象与像素度标定全流程解析 简介这是一份面向计算机视觉入门与工业检测应用的开源项目资料基于 OpenCV 与 Python 实现物体尺寸测量与轮廓检测。其核心思路是先用边缘检测和轮廓提取锁定目标再通过已知尺寸的参考对象建立像素与实际物理尺寸的比例关系进而完成最小外接矩形绘制与像素度换算适合需要快速理解尺寸测量原理、搭建自动化检测工具的开发者。压缩包共 11 个文件包含 2 个 Python 脚本、3 张示例图像、2 个演示动图、1 份 README 文档、1 份附赠 PDF 以及 2 个说明文本整体仅 2.76MB结构简洁便于对照学习。目前已有 149 人在线浏览学习。通过这套资料读者可以掌握轮廓查找、最小外接矩形绘制、像素与物理尺寸换算等关键实现并借助示例图和演示动图直观跟踪测量流程同时结合文档了解工业质检、物体识别等场景下的应用方式是入门计算机视觉测量工具的实用参考资料。1. 从像素到毫米OpenCV 物体尺寸测量为什么非要一个参考对象工业检测里用 OpenCV 做图像处理、配合 Python 的轮廓检测来测量物体尺寸是计算机视觉中最容易被低估的需求。一个螺丝多长、一个电容宽多少、一块 PCB 边距是否超差听起来简单但在像素坐标系里没有答案像素不是毫米同一物体离相机 30cm 和 40cm 拍轮廓长度可以差出 30%。所以自动化测量工具必须引入一个已知真实尺寸的参考对象用它算出“每个毫米对应多少像素”也就是标题里的像素度pixels per metric。这套工作流最适合零件抽检、包装尺寸复核以及物体识别前把不同距离的目标归一化到同一尺度。下面从预处理到最小外接矩形绘制把每一步和踩过的坑一起讲清楚。2. 图像预处理与轮廓检测先让目标“显形”尺寸测量才不是玄学轮廓查找在 OpenCV 里叫 findContours但它只接受“前景和背景黑白分明”的二值图。直接把彩色图塞进去拿不到稳定轮廓所以每做一个尺寸测量项目第一步永远是预处理。预处理做得好后面的轮廓检测和尺寸换算就是水到渠成预处理好坏会让边界偏移一个像素在 0.01mm 级精度需求上就是废品。2.1 灰度化、高斯模糊与边缘检测为什么我推荐 Canny 而不是固定阈值摄像头读进来的图是 BGR先转灰度这是所有轮廓测量的起点。灰度图仍然包含大量噪点和高频纹理如果不做模糊Canny 会把传感器噪声当成边缘最小外接矩形会被撑大一圈。我一般用高斯模糊核大小取 (5,5)工业相机高噪点画面会加到 (7,7)。核太大会让边缘变钝小物体直接被“糊”没这是第一个需要按目标尺寸调整的参数。Canny 比固定阈值或 adaptiveThreshold 更适合尺寸测量原因是它有两个阈值能同时抑制梯度噪声和保留真实边缘。固定阈值在光照变化时很难一次调好自适应阈值又会把光照渐变也拟合成前景导致轮廓出现大面积假区域。下面是一个最基础的预处理函数import cv2 import numpy as np def preprocess(image_path): # 读图OpenCV 默认 BGR路径尽量避免中文Windows 下容易读不出来 src cv2.imread(image_path) if src is None: raise ValueError(无法读取图像检查路径和权限) gray cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) # 高斯模糊核必须为奇数(5,5) 适合大部分零件(7,7) 用于高噪点 blurred cv2.GaussianBlur(gray, (5, 5), 0) # Canny 低阈值/高阈值经验起点 50/100高阈值决定哪些强边缘保留 edges cv2.Canny(blurred, 50, 100) # 闭运算先膨胀再腐蚀把断裂的边缘连接起来 kernel np.ones((3, 3), np.uint8) closed cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel, iterations2) return src, gray, closed这里有几个参数值得说清楚。Canny 低阈值低于 30 时颗粒噪声很容易成为伪边缘高阈值高于 200 时边缘又会断续闭运算也补不回来。所以我的调参顺序是先用 (5,5) 高斯模糊Canny 从 50/100 开始观察轮廓面积统计再决定是否动阈值。闭运算的 kernel 不要一上来就给 5x5它会把两个贴在一起的零件连成同一个轮廓这种错误比阈值不精确更难排查。不同光照环境下预处理选型差异很大我通常按下表做初始选择场景灰度化后处理推荐参数备注背光剪影直接阈值或 Canny阈值 120~180 或 Canny 50/100轮廓最干净推荐自然光下金属件高斯模糊 Canny(5,5)Canny 60/120高光容易造成边缘双线纹理密集 PCB双边滤波 Canny(5,5)Canny 80/160双边滤波保边缘代价是慢2.2 轮廓查找与筛选RETR_EXTERNAL 和 RETR_TREE 在测量场景怎么选OpenCV 不同大版本的 findContours 返回值不一样3.x 返回三个值4.x 返回两个。我直接用两个变量接收遇到老代码就加一个_。检索模式默认选 RETR_EXTERNAL它只返回最外层轮廓内部孔洞不参与测量。对于零件放在传送带上、彼此不接触的场景这是最不容易出错的模式。# OpenCV 4.x 返回值是 contours, hierarchy3.x 是 image, contours, hierarchy contours, hierarchy cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 过滤面积小于 500 像素的噪声轮廓再按面积排序 min_area 500 contours [c for c in contours if cv2.contourArea(c) min_area] contours sorted(contours, keycv2.contourArea, reverseTrue)面积下限min_area是最需要按相机分辨率调参的点。200 万像素相机下500 像素能滤掉很多传感器噪声如果画面里最小目标只有 300 像素这个值必须降下去。不要用固定绝对像素来筛选我通常用min_area (image_width * image_height) / 4000这类比例值作为起点再根据目标实际大小缩放。RETR_TREE 用在物体互相遮挡或有嵌套结构的场景。它会返回完整层次关系你可以用 hierarchy 判断“某个内轮廓是不是外轮廓的孔”再做包含关系过滤。这种需求在工业测量里不常见反而容易把螺纹孔、通孔等内部轮廓也量进去。测量场景我基本只用 RETR_EXTERNAL只有需要量“外方内圆”这类组合特征时才升级到 RETR_TREE。2.3 最小外接矩形与标准外接矩形核心测量点为什么是 minAreaRect拿到轮廓后最直接的测量几何量是外接矩形。OpenCV 提供两种cv2.boundingRect返回轴对齐矩形cv2.minAreaRect返回可旋转的最小外接矩形。零件在画面里稍微歪着放boundingRect 会把旋转形成的空白也算进宽高误差随角度增加minAreaRect 会贴着轮廓旋转到面积最小的姿态宽高更接近真实尺寸。# rect 结构是 ((center_x, center_y), (width, height), angle) rect cv2.minAreaRect(contour) box cv2.boxPoints(rect) # 得到 4 个角点顺序随角度变化 box np.int0(box) # 角点转整数OpenCV 绘制需要整数坐标 # 在原图上绘制最小外接矩形绿色框用于人工确认 cv2.drawContours(src, [box], -1, (0, 255, 0), 2) # 宽高直接来自矩形但注意 angle 范围和宽高顺序的坑后面避坑章展开 (w, h) rect[1]这里要留意cv2.boxPoints返回的四个角点顺序不固定会随矩形旋转角度变化。如果要在角点旁边写尺寸文字直接取box[0]会导致标注位置每次不同。我一般取box[:, 1].min()和box[:, 0].min()定位左上角或者干脆把文字放在中心点。中心点写法是cv2.putText(src, text, (int(rect[0][0]), int(rect[0][1])), ...)简单且不会跑偏。到这一步轮廓和最小外接矩形都有了但宽高还是像素值下一步才轮到参考对象登场。3. 参考对象标定与像素尺度换算从轮廓到真实尺寸的完整流程像素坐标下的宽高对测量没有意义真正要输出的是毫米。做法是让画面里出现一个真实尺寸已知的物体用它做尺子。这就是标题里“基于参考对象计算实际物体尺寸”的核心环节整个过程可以拆成三步识别参考物、计算像素度、换算目标尺寸。3.1 选参考对象硬币、标定块还是已知零件怎么识别最稳参考对象的选择直接决定测量可靠度。最常见的做法是在画面里放一枚硬币比如国内常见的一元硬币直径是 25mm用卡尺量三次取平均作为 known_size。硬币边缘圆、对比度够Canny 提取后轮廓面积稳定。标定块的思路一模一样只不过它有严格工程公差适合精度更高的场景。识别参考对象有两条路线。第一条是位置固定把参考物放在画面右上角用 ROI 裁剪那一块区域再找轮廓第二条是特征固定按面积排序取第一或第二大的轮廓。位置固定最省事但夹具一旦松了就会失效面积排序在零件种类单一的生产线上很可靠因为参考物的面积通常比被测零件大一圈。我一般这样识别参考物def find_reference_contour(contours, roiNone): # 优先在指定 ROI 里找参考物避免被更大的被测件干扰 if roi is not None: x, y, w, h roi candidates [] for c in contours: cx, cy cv2.minAreaRect(c)[0] if x cx x w and y cy y h: candidates.append(c) return max(candidates, keycv2.contourArea) # 没有 ROI 时默认面积最大的轮廓就是参考物 return contours[0]这段代码的逻辑是有 ROI 就只在 ROI 里找面积最大的轮廓没有就退回全局最大面积。实际项目里两条路我都见过推荐优先做 ROI因为参考物一旦被遮挡或者画面里出现一个更大的零件全局最大面积策略会翻车。参考物的真实尺寸不要用产品标称值要用卡尺实际量标称值和实际值往往差 0.1mm 以上对 0.1mm 级精度要求就是不可接受的。3.2 计算 pixels_per_metric为什么用长边而不是短边参考物的轮廓拿到后取它的最小外接矩形得到像素宽高(w, h)。这里有个经验细节计算像素度时取长边也就是max(w, h)不要取短边。原因是 minAreaRect 在物体接近正方形时宽和高的判定会随微小旋转发生跳变短边受这种跳变影响更大长边相对稳定换算出来的比例不容易出现前后两次相差 0.5% 的“玄学”波动。# 参考物矩形的像素宽高 ref_rect cv2.minAreaRect(ref_contour) ref_pixel_w, ref_pixel_h ref_rect[1] ref_pixel_long max(ref_pixel_w, ref_pixel_h) # 参考物真实长边单位 mm这里用一元硬币直径 25.0mm known_size_mm 25.0 # 像素度每个毫米对应的像素数标定一次后面所有目标都用它换算 pixels_per_metric ref_pixel_long / known_size_mm print(pixels_per_metric , pixels_per_metric)如果你用的是矩形标定块长边的真实值直接用卡尺量出来的长度如果用硬币长边就是直径。算出来的 pixels_per_metric 单位是“像素/mm”。这个值要保存下来后面批量测量时直接读而不是每张图都重新找参考物。每张图重新标定会引入参考物本身检测误差反而比固定标定更容易产生波动。3.3 目标尺寸计算与最小外接矩形绘制一份可复用的 measure.py把前面的预处理、轮廓筛选、参考物标定、目标测量合并成一个函数是我在项目里实际用的模板。函数签名留一个ppm参数第一次标定时传入None后面批量测量直接传入保存好的标定值。def measure_objects(image_path, ref_known_size25.0, ref_contour_index0, area_thresh500, ppmNone): src, gray, closed preprocess(image_path) contours, _ cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours [c for c in contours if cv2.contourArea(c) area_thresh] contours sorted(contours, keycv2.contourArea, reverseTrue) if ppm is None: # 初次标定用参考物算出像素度参考物本身不参与测量 if len(contours) 1: raise RuntimeError(画面里没有找到任何轮廓检查预处理参数) ref_rect cv2.minAreaRect(contours[ref_contour_index]) ref_long max(ref_rect[1]) ppm ref_long / ref_known_size skip_index ref_contour_index print(f标定完成pixels_per_metric {ppm:.3f}) else: # 批量测量使用外部标定值画面里不应再放参考物 skip_index -1 results [] for i, cnt in enumerate(contours): if i skip_index: continue rect cv2.minAreaRect(cnt) box cv2.boxPoints(rect) box np.int0(box) cv2.drawContours(src, [box], -1, (0, 255, 0), 2) w_px, h_px rect[1] real_w w_px / ppm real_h h_px / ppm # 统一转成长边 x 短边避免宽高互换造成数据错乱 long_edge, short_edge max(real_w, real_h), min(real_w, real_h) label f{long_edge:.1f} x {short_edge:.1f} mm cv2.putText(src, label, (int(rect[0][0]), int(rect[0][1])), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) results.append((long_edge, short_edge)) return src, results这段代码里有几个参数值得展开。ref_contour_index默认 0 表示面积最大的轮廓是参考物如果你的参考物不是最大就把它改成正确下标或者改成传入 ROI。area_thresh用绝对像素还是要按实际画面调。ppm传入后函数就不再重新标定这保证了批量测量时每次换算是同一个比例不会因为个别图片的 Canny 抖动而波动。3.4 固定相机后的标定复用为什么不要每张图都找参考物很多第一次做测量项目的人会“每张图都识别一次硬币”看起来更保险实际上是在制造噪声。参考物识别本身受光照、遮挡、轮廓截断影响如果每张图重新算一遍 ppm同一个目标今天量出来 10.2mm明天可能变成 10.5mm而且你还不知道是哪一步变了。固定相机和物距后正确做法是把第一次标定的 ppm 保存到文件之后所有图片都用它。# 第一次标定后保存np.save 会生成 .npy 文件 np.save(pixels_per_mm.npy, ppm) # 批量测量时直接读进来 ppm float(np.load(pixels_per_mm.npy))这里要注意ppm 只对“物距固定、焦距固定、传感器尺寸固定”有效。相机被碰过、变焦过、或者零件离相机的高度变了ppm 就不再准确必须重新标定。工业现场我习惯在测量程序启动时加一个“标定校验”放一个已知尺寸件拍一张算出来的尺寸和标称值误差超过 0.5% 就报警要求重新标定。这个校验本身很便宜能挡住很多现场事故。4. 尺寸测量排查清单四个高频翻车点与避坑方法尺寸测量看起来流程很短真到了现场翻车点往往集中在预处理和 minAreaRect 的边界行为上。下面四条是我在类似项目里反复踩出来的血泪经验每条按“现象、原因、解决”展开排查时可以直接对着做。4.1 现象参考物轮廓被切掉所有目标尺寸偏小现象是程序不报错参考物也能识别但输出比实际小 5% 以上而且所有目标都偏小一个固定比例。原因很简单参考物放得太靠边边缘在画面边界处被截断Canny 提取出的轮廓不是完整形状minAreaRect 的像素宽高比真实值小。pixels_per_metric 算出来偏小于是后面所有目标的毫米值都被压缩。解决先把参考物移到画面内留出至少 20 像素余量或者在识别参考物前做一个 ROI只保留画面中央区域参与轮廓筛选。排查时打印参考物的 boundingRect 参数和现场目测对比ref_rect cv2.minAreaRect(contours[ref_contour_index]) print(参考物像素尺寸:, ref_rect[1]) # 如果这里比实际成像比例小一截优先检查参考物是否被裁切另外如果参考物靠近畸变明显的画面边缘即使没被裁切镜头畸变也会让长边偏大或偏小边缘区域的测量通常没有中心区域可信。所以参考物尽量放画面正中偏左不要放角落。4.2 现象测量值整体偏大且光照一变就浮动现象是同一零件上午量 15.3mm下午量 15.8mm误差跟着环境光走。原因是 Canny 阈值偏低或闭运算迭代次数过多把反光、阴影、灰尘都收进了轮廓最小外接矩形被这些伪边缘向外撑大。在自然光下拍金属件特别容易出现这种问题高光区域在边缘检测里会形成第二条假边缘外接矩形量到的是“真边缘 高光边缘”的距离。解决把 Canny 高阈值往上提比如从 100 提到 150闭运算改成先用MORPH_OPEN去掉小噪点再用一次MORPH_CLOSE迭代次数尽量控制在 1。如果工件表面太亮优先用背光照明把目标变成剪影轮廓最干净。下面是补救写法# 先用开运算去噪点再用闭运算连断口 kernel np.ones((3, 3), np.uint8) opened cv2.morphologyEx(edges, cv2.MORPH_OPEN, kernel, iterations1) closed cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel, iterations1)开运算会把细小的亮点噪声先腐蚀掉闭运算再把断开的目标边缘连起来比单纯闭运算更不容易把噪声融进轮廓。这个顺序在金属件上经常是“一把过”的解药。4.3 现象minAreaRect 的宽高互换矩形角度跳变 90°现象是同一目标上一帧输出 20.3 x 10.1下一帧输出 10.1 x 20.3数据表里同一行宽高反了。原因是 OpenCV 的 minAreaRect 返回的角度范围是 [-90, 0)它把“宽”定义为与 x 轴夹角较小的那条边。当物体稍微旋转或轮廓有微小变形时宽和高的判定会突然对调导致 rect[1][0] 和 rect[1][1] 互换。角度跳变不是错误但直接拿元组里的 w、h 当宽高数据就是错的。解决输出前统一重排所有测量结果都按“长边、短边”记录而不是按 OpenCV 给的“宽、高”记录。前面 measure.py 里已经做了max(real_w, real_h)就是这个原因。如果业务上必须区分长边短边就再根据矩形角度映射一次angle rect[2] # 范围 [-90, 0) if angle -45: w_px, h_px rect[1][1], rect[1][0] else: w_px, h_px rect[1]这段代码的逻辑是当角度小于 -45 度时OpenCV 的宽和高已经互换主动换回来。注意这只是让数据方向稳定实际计算仍然用长边/短边来记录避免后续程序对数据的理解不同。4.4 现象零件挨太近轮廓粘连成一块测量结果直接翻倍现象是两个零件靠在一起时输出一个超大矩形宽高约等于两个零件尺寸之和。原因是闭运算把两个零件之间的细小间隙连了起来或者 Canny 低阈值把相邻边缘判定为同一条连通域。轮廓粘连在测量里是最难通过调参解决的问题因为它一旦发生轮廓数都对不上。解决先看间距像素。两个零件之间至少有 2~3 个像素的暗带通常可以靠减小闭运算核大小来保留间隙如果间距小于 1 像素物理上就无法靠普通形态学分开应该改用分水岭或基于距离变换的分割。最稳妥的工程做法其实是保证零件间距让它们之间至少有 5 个像素的背景区域。不要试图用膨胀腐蚀把不可分的区域硬拆开那是这个场景里最大的“玄学”幻想。注意排查轮廓粘连时打印cv2.connectedComponents的个数会很有用它和 findContours 的轮廓数差距太大就说明预处理阶段有连通问题。5. 把脚本推向工业检测固定相机、背光与批量处理的三个调整实验室里跑通的一两张图不算数工业现场要求“同一套流程在几百张图上稳定输出”。从单张脚本到产线可用我一般做四个调整固定标定、固定光照、批量容错、视野匹配。5.1 固定相机后的标定校验一次标定、反复测量产线上相机安装好之后物距和焦距都锁死像素度应该是一个固定值。但镜头松动、温度变化、重新对焦都会让 ppm 漂移。所以我在程序启动时加一个“标定件自检”把一个已知 50mm 的工件放在画面里测量结果超出 49.75~50.25mm 就停机报警。这个自检逻辑实现很轻def check_calibration(image_path, expected_50mm): src, results measure_objects(image_path, ppmfloat(np.load(pixels_per_mm.npy))) measured results[0][0] # 长边 tolerance 0.25 # 50mm 工件允许 0.5% 误差 return abs(measured - expected_50mm) tolerance如果自检失败我会重新触发标定流程拍摄带参考物的图重新计算 ppm再跑一次自检。产线上这个流程可以做成一个按钮但绝不能在程序里每张图都重新标定。5.2 光照与镜头畸变哪些参数必须锁死哪些可以容忍光照对 Canny 的阈值影响极大。背光照明也就是工件变成黑色剪影是尺寸测量最理想的方案轮廓边界对比度最高几乎不受环境光影响。如果没有背光就尽量用同轴光或环形光并固定亮度不要让现场人员随便动光源。镜头畸变是另一个隐形误差源。普通工业镜头在画面中心区域畸变小边缘区域会把直线拉成弧线外接矩形尺寸会偏大或偏小。对 0.1mm 级精度要求建议做一次相机标定用cv2.getOptimalNewCameraMatrix和cv2.undistort校正后再测量。如果只是 1mm 级粗测可以把被测零件限制在画面中心区域避开边缘。下面表格是我经验里的参数优先级环境变量对 ppm 的影响需要锁死还是容忍物距完全改变 ppm锁死变化即重新标定焦距完全改变 ppm锁死环境光强度影响轮廓提取不改变 ppm锁死用遮光罩零件表面反光影响轮廓提取用背光/旋转角度规避相机分辨率像素尺寸不变ppm 不变稳定即可5.3 批量测量的目录遍历与异常告警别让一张坏图毁掉整批数据产线测量通常是一个文件夹几十张图程序逐张读、逐张算、逐张写 CSV。单张图片因为抖动、遮挡、移动可能产生空轮廓或面积异常轮廓如果不加保护一张坏图就会让整个程序中断前面的结果全丢。我习惯用 try/except 包住单张处理并记录跳过原因。from glob import glob import csv ppm float(np.load(pixels_per_mm.npy)) with open(results.csv, w, newline) as f: writer csv.writer(f) writer.writerow([image, long_mm, short_mm]) for img_path in glob(parts/*.jpg): try: src, results measure_objects( img_path, ref_known_size25.0, area_thresh500, ppmppm ) # 输出带尺寸标注的图存到 out 目录供人工复核 out_name img_path.replace(parts, out) cv2.imwrite(out_name, src) for long_edge, short_edge in results: writer.writerow([img_path, f{long_edge:.2f}, f{short_edge:.2f}]) except Exception as e: # 坏图单独登记避免隐性漏检 with open(errors.log, a) as log: log.write(f{img_path}: {e}\n)批量处理中还有一个常见陷阱如果一张图里找不到任何轮廓measure_objects 里的sorted(contours, ...)[0]会抛 IndexError。所以正式代码里应该在找不到轮廓时直接 raise并用上面的 except 捕获记录而不是让程序安静地跳过。对测量任务来说数据缺失比数据错误更容易被接受但你必须在日志里看到它否则你以为今天测了 100 件实际只有 80 件进了结果。异常处理时我会把“面积异常”也当作异常来抓。比如正常目标长边在 20~50mm结果算出 200mm这通常意味着轮廓粘连。在写入 CSV 前加一个范围检查比事后翻图省事得多if not (20.0 long_edge 50.0): raise ValueError(f尺寸超出合理范围: {long_edge:.2f}mm)5.4 相机分辨率与视野的匹配像素度的下限其实是物理定律很多项目调到最后发现误差压不下来根源不在代码而在相机选型。假设视野宽度 100mm横向分辨率 1280 像素那么像素度只有 12.8 像素/mm单个像素对应约 0.078mm。理论上最细能分辨 0.078mm但边缘检测、镜头畸变、轮廓提取都会吃掉精度实际稳定误差通常只能做到 3~5 个像素当量也就是 0.24~0.4mm。如果你的公差是 0.1mm这个相机方案就是错的不是调参能解决的。所以我在项目立项时会先算一笔账目标尺寸公差是多少允许的最大测量误差是多少据此反推视野宽度和相机分辨率。如果视野里有 10 个零件每个零件宽 20mm视野至少 200mm那 500 万像素相机都未必够用。与其到时候纠结 Canny 阈值不如一开始就把像素度预算算清楚这是工业测量里最不性感但最关键的约束。6. 验证测量精度与进阶技巧用棋盘格和统计误差给你的数据“上保险”最后说一个我每次交付测量脚本前必做的验证拿至少 10 个已知尺寸的零件放在相机下各拍 3 次记录测量值和标称值的差算平均绝对误差和最大误差。误差要同时看均值和极差均值接近 0 但极差很大说明存在随机抖动可能是光照或轮廓提取不稳均值整体偏移基本就是标定问题。如果要做亚像素级提升第一个方向是透视校正。零件和相机光轴不垂直时透视会让同一长度在画面左右两侧像素数不同。用一张棋盘格图片算cv2.getPerspectiveTransform把图像矫正为俯视图再走轮廓检测能让误差下降一个量级。第二个方向是边缘细化Canny 输出的是单像素边缘但轮廓提取时会有量化误差可以配合cv2.cornerSubPix对矩形角点做亚像素精化再把角点之间的距离作为测量值而不是直接用外接矩形宽高。我的习惯是每改一次预处理参数就用同一组已知样本重新跑一遍误差表把这次改动的效果记在旁边。尺寸测量太容易“看起来对”但真实误差只有统计数字能证明。改参数前后的误差表就是给这套系统上的保险比任何标定都管用。希望帮到你。本文还有配套的精品资源点击获取
返回列表