尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

单目摄像头测距实战:从相机标定到距离计算的完整指南

单目摄像头测距实战:从相机标定到距离计算的完整指南 1. 项目概述从“看见”到“测距”的视觉实践在计算机视觉的众多应用里让机器“看见”并理解三维世界是一个核心挑战。我们日常使用的摄像头本质上是一个将三维空间投影到二维平面的设备它丢失了至关重要的深度信息。而“摄像头测距”这个项目正是要尝试从二维图像中重新找回或估算出目标物体到摄像头的距离。这听起来像是某种魔法但背后其实是一系列严谨的数学和图像处理技术的结合。我之所以在2022年初重新梳理这个项目是因为当时在做一个智能小车避障的原型需要一种低成本、轻量级的距离感知方案激光雷达太贵超声波精度和视野有限于是基于单目摄像头的测距就成了一个非常值得深入研究的折中方案。这个项目适合谁呢如果你是对计算机视觉感兴趣的开发者、正在做机器人或无人机相关项目的学生、或者任何想为你的智能设备比如一个自动跟随行李箱、一个防撞提醒装置添加“眼睛”的硬件爱好者那么这篇内容会为你提供一个从理论到实践的完整路线图。它不要求你具备高深的数学功底但需要你有一些Python和OpenCV的基础更重要的是要有动手验证和调试的耐心。我们将从最基础的相机模型讲起一步步拆解如何仅用一个普通的USB摄像头或笔记本自带摄像头实现对一个已知尺寸物体的距离测量。2. 核心原理拆解针孔模型与相似三角形要实现测距我们首先得搞清楚摄像头是如何“看”世界的。这里最核心、最经典的模型就是针孔相机模型。你可以把它想象成一个没有镜头的、只在暗箱前端有一个极小孔的相机。现实世界的光线穿过这个小孔在暗箱后端的成像平面上形成一个倒立的实像。这个模型虽然简单却完美地揭示了透视投影的几何关系也是我们所有计算的基础。在针孔模型中一个关键的概念是焦距。在我们接下来的讨论里焦距通常指的是图像坐标系中的焦距单位是像素。它不是一个物理长度而是反映了物理焦距与图像传感器单位像素尺寸之间的关系。这个参数至关重要因为它直接关联了现实世界中的距离与图像中的像素位移。那么如何从一个二维图像反推深度呢对于单目摄像头如果我们知道一个物体的真实物理尺寸就可以利用相似三角形原理进行计算。这是本项目最核心的数学工具直观且强大。想象一个场景你站在路上手里拿着一根已知长度为W比如20厘米的棍子伸直手臂。你闭上一只眼睛模拟单目用另一只眼睛去看这根棍子。棍子在你的视网膜成像平面上占据了一定的宽度w以某种单位衡量。现在如果你将棍子向远处移动它在视网膜上成像的宽度w就会变小。这里存在两个相似的三角形一个是由你的眼睛、棍子两端构成的物理世界三角形另一个是由眼睛的光心、棍子在成像平面上的两个端点构成的像平面三角形。这两个三角形的比例关系是恒定的物体实际宽度 W / 物体到相机的距离 D 物体在图像中的像素宽度 w / 焦距 f。因此我们可以推导出距离公式D (W * f) / w。这个公式就是整个项目的基石。它告诉我们只要我们知道物体的真实宽度W提前标定好相机的焦距f然后在实时图像中检测出该物体的像素宽度w就能立刻计算出距离D。注意这个公式成立有几个重要前提。第一物体必须与相机成像平面大致平行否则物体的成像宽度会因透视而失真。第二我们测量的是物体的“宽度”理论上任何已知的物理尺寸如高度、对角线长度都可以但需要确保在图像中测量的方向与物理尺寸的方向一致。第三焦距f必须准确这需要通过相机标定来获取。3. 工具选型与环境搭建工欲善其事必先利其器。这个项目的工具链非常清晰核心就是OpenCV。3.1 为什么选择OpenCVOpenCV是一个开源的计算机视觉库它几乎包含了我们从图像读取、预处理、特征检测到相机标定所需的所有工具。其接口丰富C Python Java等社区活跃资料众多。对于这个测距项目我们需要用到它的视频捕获、颜色空间转换、轮廓查找、相机标定等功能模块。用Python版本的OpenCVopencv-python进行原型开发速度快代码简洁非常适合验证想法和快速迭代。3.2 开发环境配置清单我的开发环境是Windows 10 Python 3.8但以下步骤在macOS和Linux上同样适用只需注意包管理器的命令差异。安装Python确保你的系统安装了Python 3.6或以上版本。推荐使用Anaconda来管理Python环境和包它能很好地处理依赖问题。安装OpenCV打开终端或命令提示符使用pip安装是最简单的方式。pip install opencv-python这个包包含了OpenCV的主要模块。如果你还需要一些额外的贡献模块某些标定工具可能在其中可以安装opencv-contrib-python但本项目基础版已足够。安装NumPyOpenCV的很多函数都依赖NumPy数组。通常安装OpenCV时会自动安装但为了保险可以手动安装。pip install numpy验证安装创建一个简单的Python脚本test_opencv.py来测试。import cv2 import numpy as np print(f“OpenCV版本 {cv2.__version__}”) print(f“NumPy版本 {np.__version__}”) # 尝试读取摄像头 cap cv2.VideoCapture(0) if cap.isOpened(): print(“摄像头打开成功”) cap.release() else: print(“无法打开摄像头。”)运行这个脚本如果能看到版本号并成功打开摄像头确保摄像头未被其他程序占用说明环境配置成功。3.3 硬件准备摄像头一个普通的USB网络摄像头或笔记本电脑内置摄像头即可。分辨率建议在720p1280x720以上太低的分辨率会影响检测和测量的精度。拍摄时尽量保证环境光线充足、均匀避免强烈的逆光或阴影这对后续的图像处理至关重要。标定物我们需要一个已知尺寸的物体作为“参照物”。一个边长为W厘米的正方形A4纸可以打印一个黑色方块、一个已知直径的圆形硬币、或者一个特定尺寸的盒子都可以。我强烈建议使用一个高对比度的物体比如一个黑色的矩形卡片放在白色背景上这样在图像中更容易被稳定、准确地检测出来。4. 相机标定获取关键参数f根据我们的距离公式D (W * f) / w焦距f是一个必须预先知道的常数。这个f并不是摄像头规格书上的物理焦距单位毫米而是以像素为单位的焦距值。它包含了传感器尺寸等信息需要通过标定来获取。4.1 标定原理简述我们使用棋盘格标定法。棋盘格提供了大量规则分布的角点黑白方格相交的点这些角点在三维空间中的坐标是已知的我们可以定义棋盘格所在平面为Z0然后为每个角点分配其X, Y, 0坐标。通过让摄像头从不同角度拍摄多张棋盘格图片OpenCV可以建立这些三维点与它们在二维图像中投影点的对应关系从而求解出相机的内参矩阵和畸变系数。内参矩阵K是一个3x3的矩阵其中就包含了我们需要的f_x和f_y图像x轴和y轴方向的焦距通常两者接近以及主点坐标(c_x, c_y)图像中心点。对于大多数应用我们可以取f_x和f_y的平均值作为焦距f。4.2 实操步骤生成与拍摄标定板生成标定板图片你可以从网上下载一个标准棋盘格图片例如9x6的内部角点或者用代码生成。我选择打印一张出来。确保打印时没有缩放可以用尺子测量一下方格边长是否与设计值一致例如每格2厘米。拍摄标定图片将打印好的棋盘格贴在一个平坦的硬纸板上。打开摄像头从不同的角度、距离、方向拍摄棋盘格。关键是要让棋盘格充满画面的不同区域。数量建议拍摄15-25张。太少可能导致标定不准确太多则增加计算量收益递减。角度包含正面、左右倾斜、上下倾斜、远近变化。要求棋盘格必须清晰对焦准确光照均匀避免反光。技巧可以写一个简单的OpenCV程序来辅助拍摄按空格键保存当前帧这样比用手机拍屏幕更精准。4.3 标定代码实现以下是一个完整的相机标定Python脚本。你需要将拍摄好的图片放在一个目录下例如calibration_images/。import cv2 import numpy as np import glob import pickle # 定义棋盘格的尺寸内部角点数量例如9x6表示每行10个方格每列7个方格内部交点就是9x6 CHECKERBOARD (9, 6) # 请根据你实际打印的棋盘格修改 # 终止条件 criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) # 准备对象点例如 (0,0,0), (1,0,0), (2,0,0) ..., (8,5,0) objp np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) objp[:, :2] np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2) # 如果你知道棋盘格的实际物理尺寸例如每个方格边长2.5厘米可以在这里乘以缩放因子 # objp * 2.5 # 单位厘米 # 用于存储所有图像的对象点和图像点的数组 objpoints [] # 真实世界中的3D点 imgpoints [] # 图像中的2D点 images glob.glob(‘./calibration_images/*.jpg’) for fname in images: img cv2.imread(fname) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 查找棋盘格角点 ret, corners cv2.findChessboardCorners(gray, CHECKERBOARD, None) # 如果找到添加对象点和图像点细化后 if ret True: objpoints.append(objp) corners2 cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria) imgpoints.append(corners2) # 绘制并显示角点可选 img cv2.drawChessboardCorners(img, CHECKERBOARD, corners2, ret) cv2.imshow(‘img’, img) cv2.waitKey(500) # 显示500毫秒 cv2.destroyAllWindows() # 开始标定 ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None) print(“标定完成”) print(f“相机内参矩阵 K:\n{mtx}”) print(f“畸变系数 dist:\n{dist}”) # 计算重投影误差评估标定质量 mean_error 0 for i in range(len(objpoints)): imgpoints2, _ cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist) error cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2) mean_error error print(f“平均重投影误差 {mean_error/len(objpoints)} 像素”) print(“误差越小标定越精确。通常应小于0.5像素。”) # 保存标定结果供后续测距程序使用 calibration_data {‘camera_matrix’: mtx, ‘dist_coeff’: dist} with open(‘camera_calibration.pkl’, ‘wb’) as f: pickle.dump(calibration_data, f) print(“标定数据已保存到 camera_calibration.pkl”)运行这段代码后你会得到内参矩阵mtx。它通常长这样[[fx, 0, cx], [0, fy, cy], [0, 0, 1]]其中fx和fy就是我们需要的焦距像素单位。对于大多数摄像头fx和fy非常接近。我们取它们的平均值f (fx fy) / 2作为后续计算的焦距。记下这个f值它是你摄像头的一个固有属性只要不改变分辨率这个值就是固定的。实操心得标定是测距准确性的基石。务必保证棋盘格平整拍摄图片质量高。重投影误差是重要的质量指标如果误差大于1个像素建议检查标定图片质量或重新拍摄。另外标定使用的分辨率必须和后续测距程序使用的分辨率一致否则焦距参数无效。5. 目标检测与轮廓提取在图像中找到并测量“w”获取焦距f后下一步就是在实时视频流中稳定、准确地检测出我们的目标物体并测量它在图像中的像素宽度w。这里我们以检测一个已知宽度的矩形卡片为例。5.1 图像预处理流程直接从摄像头读取的原始图像包含大量噪声和无关信息。我们需要通过一系列处理来“净化”图像让目标物体凸显出来。颜色空间转换与阈值分割这是最关键的步骤。如果目标物体颜色鲜明比如亮黄色卡片可以转换到HSV颜色空间更容易分离颜色。hsv cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 定义黄色的HSV范围需要根据实际物体调整 lower_yellow np.array([20, 100, 100]) upper_yellow np.array([30, 255, 255]) mask cv2.inRange(hsv, lower_yellow, upper_yellow)如果目标物体是黑白色比如我们的A4纸黑方块直接在灰度图上做阈值处理可能更简单。gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 二值化将灰度图转为黑白图物体为白色255背景为黑色0 _, thresh cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)得到的是一个二值化的掩膜图像白色区域代表可能是目标物体。形态学操作阈值分割后的图像可能有空洞、毛刺或小的噪声点。使用形态学操作腐蚀、膨胀、开运算、闭运算来改善图像质量。kernel np.ones((5,5), np.uint8) # 先闭运算填充物体内部的小黑洞 mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 再开运算去除外围小的白色噪声点 mask cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)内核大小需要根据图像分辨率和噪声情况调整。轮廓查找在处理好的二值图像上使用OpenCV的findContours函数找出所有白色区域的轮廓。contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)这里使用RETR_EXTERNAL只检索最外层轮廓CHAIN_APPROX_SIMPLE压缩水平、垂直和对角线段只保留端点节省内存。5.2 筛选目标轮廓并计算像素宽度找到的轮廓可能有很多个我们需要从中筛选出最可能是我们目标矩形卡片的那个。# 假设目标物体是最大的那个轮廓且面积大于某个阈值以过滤噪声 if contours: # 按面积排序 contours sorted(contours, keycv2.contourArea, reverseTrue) for cnt in contours: area cv2.contourArea(cnt) if area 500: # 面积阈值根据实际情况调整 continue # 计算轮廓的近似多边形减少轮廓点数 peri cv2.arcLength(cnt, True) approx cv2.approxPolyDP(cnt, 0.02 * peri, True) # 如果近似多边形有4个顶点我们认为它近似一个矩形 if len(approx) 4: target_contour cnt # 计算该轮廓的最小外接矩形 rect cv2.minAreaRect(target_contour) box cv2.boxPoints(rect) box np.int0(box) # 计算最小外接矩形的宽度和高度像素 width_px rect[1][0] # 宽度 height_px rect[1][1] # 高度 # 取较小的一个作为“宽度”w因为我们假设物体是水平放置的矩形 w_pixels min(width_px, height_px) break # 找到第一个符合条件的矩形就退出这里有几个关键点面积过滤避免将图像噪声或小物体误判为目标。多边形近似cv2.approxPolyDP用更少的点来逼近轮廓0.02 * peri是逼近精度值越小越接近原始轮廓。对于矩形卡片近似后应该有4个顶点。最小外接矩形cv2.minAreaRect返回一个能完全包围轮廓的、可以旋转的矩形。这个矩形的宽高属性 (rect[1]) 非常稳定即使物体在图像中有轻微旋转也能准确反映其像素尺寸。选择宽度我们使用min(width_px, height_px)作为公式中的w。这是基于一个假设我们已知的是物体的物理宽度W并且物体在放置时其宽度边大致平行于成像平面。如果物体旋转角度很大这个假设就不成立了这也是该方法的局限性之一。6. 距离计算与实时测距程序整合现在我们有了焦距f从标定数据加载知道了物体的物理宽度W例如我的黄色卡片宽度是10.5厘米也能从每帧图像中实时计算出物体的像素宽度w_pixels。将它们代入公式D (W * f) / w_pixels就能得到实时的距离D单位与W一致这里是厘米。6.1 完整测距程序流程下面是将所有模块整合在一起的完整Python程序框架。你需要准备好标定文件camera_calibration.pkl和已知物体的物理宽度KNOWN_WIDTH。import cv2 import numpy as np import pickle # 参数配置 KNOWN_WIDTH 10.5 # 已知物体的物理宽度单位厘米 # 加载相机标定参数 with open(‘camera_calibration.pkl’, ‘rb’) as f: calib_data pickle.load(f) mtx calib_data[‘camera_matrix’] dist calib_data[‘dist_coeff’] # 计算平均焦距 f fx mtx[0, 0] fy mtx[1, 1] FOCAL_LENGTH (fx fy) / 2.0 # 初始化摄像头 cap cv2.VideoCapture(0) # 设置分辨率必须与标定时使用的分辨率一致 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) # 主循环 while True: ret, frame cap.read() if not ret: break # 可选进行镜头畸变校正如果标定得到的畸变系数较大 # frame cv2.undistort(frame, mtx, dist) # 1. 图像预处理 (以检测黄色物体为例) hsv cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_yellow np.array([20, 100, 100]) upper_yellow np.array([30, 255, 255]) mask cv2.inRange(hsv, lower_yellow, upper_yellow) # 2. 形态学操作 kernel np.ones((5,5), np.uint8) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) mask cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 3. 轮廓查找与筛选 contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) distance_cm None if contours: contours sorted(contours, keycv2.contourArea, reverseTrue) for cnt in contours: if cv2.contourArea(cnt) 500: continue peri cv2.arcLength(cnt, True) approx cv2.approxPolyDP(cnt, 0.02 * peri, True) if len(approx) 4: # 找到目标矩形 rect cv2.minAreaRect(cnt) box cv2.boxPoints(rect) box np.int0(box) # 绘制轮廓和外接矩形 cv2.drawContours(frame, [box], 0, (0, 255, 0), 2) # 4. 计算像素宽度和距离 width_px rect[1][0] height_px rect[1][1] w_pixels min(width_px, height_px) if w_pixels 0: # 使用相似三角形公式计算距离 distance_cm (KNOWN_WIDTH * FOCAL_LENGTH) / w_pixels break # 只处理找到的第一个大矩形 # 5. 在图像上显示结果 if distance_cm is not None: cv2.putText(frame, f“Distance: {distance_cm:.2f} cm”, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) else: cv2.putText(frame, “No target detected”, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2) cv2.imshow(‘Distance Measurement’, frame) cv2.imshow(‘Mask’, mask) # 显示处理后的掩膜用于调试 if cv2.waitKey(1) 0xFF ord(‘q’): break cap.release() cv2.destroyAllWindows()6.2 代码关键点解析分辨率一致性cap.set设置的分辨率必须与相机标定时使用的分辨率完全一致。因为焦距f是以像素为单位的分辨率改变意味着每个像素代表的物理尺寸变了f值也就失效了。畸变校正如果标定得到的畸变系数 (dist) 较大图像边缘的物体会发生弯曲影响轮廓查找和宽度测量的精度。使用cv2.undistort函数可以校正这种畸变得到更“正”的图像。但校正运算有一定开销如果实时性要求高且畸变不严重可以省略。宽度测量我们使用了cv2.minAreaRect来获得旋转矩形并取其短边作为宽度w_pixels。这种方法比直接使用轮廓的边界矩形 (cv2.boundingRect) 更鲁棒因为它考虑了物体的旋转。距离计算公式distance_cm (KNOWN_WIDTH * FOCAL_LENGTH) / w_pixels是核心。确保KNOWN_WIDTH和FOCAL_LENGTH的单位一致这里都是厘米和像素的混合最终距离单位与KNOWN_WIDTH一致。7. 精度影响因素分析与优化策略在实际测试中你会发现测得的距离存在误差。误差来源是多方面的理解它们才能有针对性地优化。7.1 主要误差来源误差来源影响描述优化方向相机标定误差焦距f不准确是系统性误差的主要来源。重投影误差大、标定图片质量差、拍摄角度不足等都会导致此问题。严格按照标准流程进行多角度、高质量标定确保重投影误差0.5像素。目标检测误差像素宽度w测量不准。光照变化导致阈值分割不稳定轮廓提取受噪声干扰物体倾斜导致minAreaRect测量的不是真实物理宽度方向。优化图像预处理如使用自适应阈值、背景减除使用更稳定的检测器如基于深度学习的物体检测确保物体尽量正对相机。物理假设误差公式基于物体与成像平面平行的假设。如果物体倾斜其成像宽度会变窄导致计算出的距离大于实际距离。在实际应用中尽量保证测量场景符合假设。对于无法避免的倾斜可考虑使用双目视觉或引入姿态估计进行修正。镜头畸变广角镜头边缘畸变严重导致图像边缘的物体尺寸测量失真。进行镜头畸变校正 (cv2.undistort)。使用畸变较小的镜头。参考物尺寸误差KNOWN_WIDTH测量不精确。使用高精度工具如游标卡尺测量参考物尺寸。7.2 提升测距稳定性的技巧多帧平滑单帧测量容易受到噪声影响。可以对连续多帧计算出的距离进行平滑处理如使用移动平均或卡尔曼滤波能有效输出更稳定的距离值。# 简单的移动平均示例 distance_history [] HISTORY_SIZE 5 if distance_cm is not None: distance_history.append(distance_cm) if len(distance_history) HISTORY_SIZE: distance_history.pop(0) smoothed_distance sum(distance_history) / len(distance_history) # 显示平滑后的距离动态阈值调整固定的HSV或灰度阈值在光照变化时容易失效。可以尝试使用自适应阈值 (cv2.adaptiveThreshold) 或OTSU阈值法 (cv2.THRESH_OTSU) 来应对光照不均。轮廓稳定性判断不是所有找到的矩形都是目标。可以增加额外的判断条件比如矩形的宽高比已知物体的物理宽高比、轮廓的紧密度面积/外接矩形面积等来过滤掉错误的检测。相机与场景固定对于固定场景的应用如流水线检测尽量固定相机的位置、角度和光照条件可以极大提高测距的重复精度。8. 项目扩展与进阶思路基于单目和已知尺寸的测距是一个很好的起点但它的局限性也很明显依赖已知尺寸、要求物体正对。你可以在此基础上进行扩展探索更强大的视觉感知能力。8.1 从“已知宽度”到“已知高度”或“已知面积”原理是相通的。如果你知道物体的高度H并在图像中测得其像素高度h那么距离公式变为D (H * f) / h。如果你知道物体的面积A物理单位和在图像中的像素面积a那么关系会稍微复杂一些因为面积与距离的平方成反比D sqrt((A * f^2) / a)。使用面积可能对轮廓的完整性要求更高。8.2 结合ArUco标记进行鲁棒检测使用OpenCV内置的ArUco模块生成和检测特定的二维码标记。ArUco标记有明确的边界和ID检测算法非常鲁棒能提供精确的四个角点像素坐标。通过已知标记的物理尺寸和检测到的角点不仅可以计算距离还可以估算标记相对于相机的三维姿态旋转和平移。这比我们自己写轮廓检测要稳定和精确得多。8.3 迈向双目立体视觉这是摆脱“已知尺寸”限制的根本方法。使用两个摄像头模拟人眼通过计算同一物体在两个摄像头图像中的视差像素位置的差异直接解算出物体的三维坐标和距离。OpenCV提供了完整的双目标定、校正和视差计算的功能 (cv2.StereoBM,cv2.StereoSGBM)。虽然 setup 更复杂需要精确同步的双目相机、严格的标定但它能实现真正的被动式三维测距是机器人、自动驾驶等领域的主流方案之一。8.4 集成到实际应用将这个测距模块作为一个功能集成到更大的系统中。例如智能小车避障在小车前方安装摄像头检测障碍物可视为已知大概宽度的物体当距离小于阈值时发送指令让小车转向或停止。仓库货架间距测量固定摄像头测量货架间通道的宽度。简单的人体距离估计假设成年人的肩宽在一定范围内可以粗略估计人与摄像头之间的距离用于互动展示或安全距离提醒。这个项目的真正价值不在于它达到了多高的工业级精度而在于它清晰地揭示了计算机视觉中从2D到3D映射的基本原理。通过动手实现它你会对相机模型、图像处理、几何计算有更深刻的理解。我在调试过程中最大的体会是视觉系统对细节的敏感性远超想象一个光照的变化、一个阈值参数的调整都可能让结果天差地别。它要求开发者不仅有编码能力更要有系统性的思维和耐心调试的精神。当你第一次看到屏幕上稳定地显示出不断变化的距离数字时那种将数学公式转化为实际感知能力的成就感正是驱动我们不断探索的动力。
返回列表