ARTICLE DETAIL

资讯详情

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

基于OpenCV的信号灯识别系统设计与实现

基于OpenCV的信号灯识别系统设计与实现 简介本资源是一套面向本科毕业设计与课程实践的智能交通信号灯控制系统完整实现方案基于Python与OpenCV构建聚焦计算机视觉在交通管控中的落地应用。系统支持自动感知与人工干预双模式涵盖实时视频采集、信号灯状态可视化、流量统计模拟、历史录像回放及操作日志记录等核心功能适用于交通工程、人工智能或嵌入式方向的学习者开展项目开发与算法验证。压缩包共35个文件含3个核心Python源码main.py、video.py、sql.py、前端HTML/CSS/JS页面、SQLite数据库脚本、配置XML及备份文件等结构清晰、模块解耦便于理解前后端交互逻辑与视觉处理流程整体大小仅1.25MB轻量易部署。目前已有45人学习下载提供可直接运行的最小可行系统包含环境配置说明、功能演示路径及模块化代码注释助力快速掌握OpenCV目标检测、GUI开发与轻量数据库集成等关键技术点。1. 项目概述与整体设计思路1.1 传统信号灯识别的痛点做智能交通方向的视觉项目第一步要搞清楚一个问题为什么信号灯识别这么难。我在调研这个项目之前其实低估了这个问题。真正把摄像头对准真实路口的信号灯之后才发现白天强反光、夜间光晕、雨天水渍、逆光剪影这些情况会让一套看起来很好用的颜色判断代码瞬间失效。传统方案里很多入门教程会告诉你“用OpenCV读摄像头转HSV颜色空间设一个红色的阈值范围再找轮廓就完事了”。这话没错但只适用于实验室里那种背景干净、灯色标准、位置居中的图片。真实场景下信号灯周围可能有树影、广告牌红色灯光、刹车尾灯、LED屏幕这些东西全是干扰。更麻烦的是同一个路口在不同天气下拍出来的图片信号灯色块的饱和度、亮度差异极大用固定阈值几乎必挂。所以这个项目的核心价值不在于“能识别红灯绿灯”而在于它把“识别”这件事做成了一套可复现、可调试、可扩展的控制系统。它解决的问题不是算法题而是工程题怎么让信号灯状态识别在不同场景下都相对稳定怎么把识别结果喂给后续的控制逻辑怎么用数据集去持续优化识别效果。适合拿来做课程设计、毕业设计也适合刚接触计算机视觉的工程师做一个完整的入行练手项目。1.2 方案选型为什么选Python加OpenCV这个项目用的是Python和OpenCV而不是TensorFlow或者PyTorch那一套深度学习全家桶这个选择有几个实际考量。第一是门槛。OpenCV的安装、调试、可视化非常直观。一个cv2.cvtColor就能把BGR转成HSV一个cv2.inRange就能提取颜色区域这些函数对新人极度友好。整个项目跑起来从环境准备到看到第一帧画面通常半小时内就能完成这对建立信心很重要。深度学习模型那一套流程光装CUDA、cuDNN、配置环境就能劝退一批人。第二是效率。传统视觉方案在简单场景下的帧率可以做到很高CPU跑实时视频流完全没问题。信号灯识别本质上不是特别复杂的任务它目标数量少、颜色特征明确、形状相对规则用传统图像处理就能覆盖掉大部分场景。在实际项目中先用传统方案做基线再根据效果决定要不要上深度学习这个思路本身就很重要。第三是代码可控性。传统的OpenCV方案每一行代码逻辑都看得见出问题了可以逐步调试。深度学习模型是一个黑盒输出的结果不好解释。对工业落地来说可解释性有时候比准确率更重要尤其是交通这种安全相关的场景。当然这个项目也没有只停留在传统方案。后面我基于同一套数据集用YOLOv8训练了一个信号灯检测模型两个方案做对比传统方法胜在轻量可控深度学习胜在复杂场景的鲁棒性。这个对比本身就是很好的学习素材。1.3 整体系统架构与检测流程整个系统的处理链路可以拆成四段图像采集、图像预处理、灯色识别、状态控制输出。图像采集支持两种来源摄像头实时视频流和本地视频文件。实时视频流适合做现场演示本地视频适合做算法调试因为视频可以反复播放看同一帧在不同参数下的表现。预处理阶段包括降噪、直方图均衡化和必要的几何校正。灯色识别阶段是核心包含颜色空间转换、阈值分割、轮廓筛选、灯色判断。最后根据识别结果向控制模块发送指令模拟信号灯的状态切换。这里有一个容易被忽略的设计细节它不是把每一帧都当成独立图片来处理而是在连续帧之间做了状态联动。比如红灯亮起后要连续若干帧都识别为红灯才会真正切换状态如果只是单帧识别异常直接忽略。这个机制可以大幅减少误触发尤其是夜间的频闪、摄像头的自动曝光切换带来的瞬时干扰。2. 环境搭建与工程结构2.1 开发环境与依赖安装先说环境版本。我测试的这套代码在Python 3.8到3.11之间都能正常运行OpenCV用的是4.x系列。这里有个重要的兼容性提醒OpenCV 4.5以上版本对部分旧代码做了接口调整如果你的环境是4.5以下部分函数可能会报错或者行为不一致。安装OpenCV的时候很多人分不清opencv-python和opencv-contrib-python的区别。简单说前者是基础版本包含常用模块后者是扩展版本额外包含一些专利保护的算法模块比如SIFT、SURF。信号灯识别用不到扩展模块所以装基础版就够了除非你后续要研究特征点匹配那再升级到contrib版本。pip install opencv-python pip install numpy顺便多提一句如果你要跑YOLOv8那个分支这样装pip install ultralyticsultralytics这个包会自动拉取PyTorch等依赖安装时间会比较长耐心等。另外一个小坑Windows下不要手动去安装PyTorch的CPU版和GPU版各来一遍容易把环境弄乱用pip自动处理就行。2.2 工程目录设计与数据准备我习惯把工程目录分成这样traffic_light_control/ ├── main.py # 主程序入口 ├── detector.py # 信号灯检测模块 ├── controller.py # 状态控制模块 ├── config.py # 参数配置文件 ├── utils/ │ ├── preprocess.py # 图像预处理 │ └── visualizer.py # 可视化辅助 ├── data/ │ ├── images/ # 图片样例 │ ├── videos/ # 视频测试集 │ └── annotations/ # 标注文件 └── models/ # 训练好的模型文件这个结构是按“模块解耦”的思路来设计的。检测模块只负责“看到什么”控制模块只负责“怎么响应”。这样做有两个好处第一你可以单独替换检测算法而不影响控制逻辑第二调试时能快速定位问题到底出在识别环节还是控制环节。数据集是网上开源信号灯数据集加自己采集的图片混合使用的。开源数据集里的图片大多来自美国、欧洲的路口灯型和国内有一定差异所以我额外补充了一些中国路口的图片保证模型能适应国内常见的信号灯样式。标注文件用的YOLO格式一个类别对应一个txt文件记录了物体的类别编号和归一化坐标。这个会在后面详细说。3. 核心算法实现与代码解读3.1 图像预处理去噪、直方图均衡化信号灯识别的大敌是噪声和光照不均。我见过不少人在预处理阶段偷懒摄像头图像直接送进颜色识别里结果晴天一个效果、阴天一个效果根本没法用。预处理这一步不能省。图像去噪我选的是高斯滤波核大小设为5x5。为什么不选中值滤波因为中值滤波对椒盐噪声效果好但会破坏边缘细节信号灯这种圆形边缘特征边缘信息很重要。高斯滤波在去噪和保留边缘之间能做到平衡处理速度也快。import cv2 import numpy as np def preprocess_frame(frame): # 高斯滤波降噪 blurred cv2.GaussianBlur(frame, (5, 5), 0) # 将BGR转换到HSV颜色空间 hsv cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV) # 对V通道亮度做直方图均衡化提升全局对比度 h, s, v cv2.split(hsv) v_equalized cv2.equalizeHist(v) hsv_equalized cv2.merge([h, s, v_equalized]) return hsv_equalized直方图均衡化这一段值得展开说。HSV空间里H是色相S是饱和度V是明度。对V通道做equalizeHist可以拉伸亮度分布让暗处更暗、亮处更亮整体对比度提升。这样做能有效缓解逆光和阴影的影响。但是这里有个坑就是搜索热词中反复出现的opencv equalizeHist 掩膜。equalizeHist默认是对整张图做全局均衡化这在很多场景下是有问题的。比如画面里有一大片天空天空部分会主导直方图分布导致地面目标区域的对比度反而变差。解决办法是只对感兴趣区域做均衡化或者是用掩膜限制处理范围。我在代码里留了一个参数可以切换全局均衡化和带掩膜的局部均衡化。实测下来在用掩膜只对ROI区域做均衡化的情况下夜间场景的识别率能提升15%左右。这个改进在代码注释里写明了你们可以自己切换对比看效果。3.2 颜色空间与阈值范围的选择逻辑信号灯识别的核心步骤是颜色分割。BGR空间里直接设阈值做判断为什么不靠谱因为BGR三个通道的相关性太强受光照影响很大。比如一个红色的信号灯在不同色温下它的B、G、R三个分量都会变你很难找到一组固定的阈值同时覆盖所有情况。HSV空间的设计逻辑就科学多了。色相H表示的是什么颜色饱和度S表示的是颜色有多纯明度V表示的是整体亮度。这样把“颜色”和“亮度”解耦只用H通道判断色相S通道和V通道辅助过滤效果会好很多。# 红灯范围 red_lower_1 np.array([0, 100, 100]) red_upper_1 np.array([10, 255, 255]) red_lower_2 np.array([160, 100, 100]) red_upper_2 np.array([180, 255, 255]) # 绿灯范围 green_lower np.array([40, 50, 50]) green_upper np.array([85, 255, 255]) # 黄灯范围 yellow_lower np.array([15, 100, 100]) yellow_upper np.array([35, 255, 255]) # 分别获取掩膜 mask_red cv2.inRange(hsv, red_lower_1, red_upper_1) cv2.inRange(hsv, red_lower_2, red_upper_2) mask_green cv2.inRange(hsv, green_lower, green_upper) mask_yellow cv2.inRange(hsv, yellow_lower, yellow_upper)红色为什么需要两个范围因为HSV空间中红色的色相H值在0度和180度两侧分布形成一个环形的区间。如果只用一组范围要么调不到0到10度要么调不到160到180度总是漏掉一部分红色像素。这里把两个范围做一个并集就完整了。实际调参的经验是S通道的最小值要设到50以上否则灰白色的物体容易被误判成彩色的目标。V通道的最小值也要有下限纯黑的东西不应该算作信号灯。但这两个下限不能设得太高否则晚上亮度不足时真的信号灯反而会被过滤掉。我最终的参数是S取50V取50你们可以根据自己采集的图片微调。3.3 轮廓筛选与置信度判断掩膜拿到之后不能直接认为“只要有红色像素就算红灯”还要做轮廓筛选。这一步是为了过滤掉误检。我用的筛选策略有四个维度轮廓面积、宽高比、填充率和位置约束。轮廓面积好理解太大的可能是车尾灯或者广告牌太小的是噪声设一个上下限区间就能滤掉大部分异常。宽高比用于过滤形状不规则的物体。信号灯的形状接近圆形宽高比接近1如果轮廓明显是一个长条形或者扁平面那大概率不是信号灯。填充率用的是轮廓面积和最小外接矩形面积的比值圆形轮廓的填充率应该在0.6到0.9之间。最后是位置约束信号灯一般位于画面中上部可以通过ROI区域直接排除掉图像下半部分的干扰比如刹车灯。def is_circle_like(contour): area cv2.contourArea(contour) if area 100 or area 5000: return False x, y, w, h cv2.boundingRect(contour) aspect_ratio w / float(h) if aspect_ratio 0.6 or aspect_ratio 1.6: return False rect_area w * h if rect_area 0: return False fill_ratio area / rect_area if fill_ratio 0.4 or fill_ratio 0.95: return False return True这个函数里用到的几个参数我是在实际测试中反复调出来的。面积下限100像素对应的是距离摄像头约50米处的一个信号灯在画面中的大小如果镜头分辨率更高这个下限可以相应调低。填充率下限0.4是为了容忍信号灯因为遮挡或者阳光角度造成的轻微形变上限0.95是为了排除那些接近矩形的物体。轮廓筛选之后还有一个步骤是“配对检查”。一个标准的信号灯组包含红、黄、绿三个灯它们的位置关系是固定的。如果某一帧识别出了红灯但它的上下方向没有对应的其他灯位这个检测结果的置信度就要打折扣。这个检查可以用在最终决策阶段能显著降低单灯误检率。3.4 状态判定与信号灯控制逻辑信号灯状态判定不只要回答“画面里有没有红灯”还要回答“当前应该让车走还是让车停”。这一层逻辑是信号灯控制系统的灵魂。我的判定策略是对每一帧算出红、黄、绿三个颜色的置信度分数然后结合连续帧的投票机制做最终决策。置信度分数综合了轮廓面积和匹配度两个因素面积越大置信度越高因为近距离的信号灯肯定比远距离的更可靠。但在多目标场景下应该取当前区域内置信度最高的那个目标作为主要信号灯。def decide_state(scores, history): # scores: {red: 0.85, green: 0.10, yellow: 0.05} # history: 最近N帧的判定结果列表 max_color max(scores, keyscores.get) # 只有当最高分超过阈值且连续多帧一致时才切换状态 if scores[max_color] CONFIDENCE_THRESHOLD: history.append(max_color) if len(history) VOTE_FRAMES: history.pop(0) recent history[-VOTE_FRAMES:] if recent.count(max_color) int(VOTE_FRAMES * 0.6): return max_color return None # 不确定状态保持上一帧结果这里有个很重要的设计理念系统不轻易改变状态。在交通控制场景中稳定性压倒一切。宁可多等一帧两帧确认结果也不能因为某帧识别异常就相位乱跳。VOTE_FRAMES我一般设成5也就是说要连续5帧里至少有3帧投同一个颜色状态才会切换。这样做实时性上有一点点损失大约100到200毫秒但换来了整体的稳定性。控制逻辑方面这个项目模拟的是一个两相位路口的信号灯控制。默认相位是“南北直行绿灯、东西直行红灯”识别到画面中的红灯亮起时说明横向车流正在通行系统需要切换相位。通过这样简单的状态机可以实现一个最基础的单路口信号控制模拟。4. 数据集构建与标注策略4.1 数据来源与开源数据集参考做信号灯识别的数据集构建我建议的策略是“开源数据集打底 自采数据补充 针对性增强”。网上能找到的开源数据集质量参差不齐但只要会筛选还是能用的。LISA交通灯数据集是加州大学圣迭戈分校发布的包含超过13万帧标注过的图像标注对象主要是美国路口的典型信号灯。这个数据集的特点是场景丰富、分辨率高适合用来训练深度学习模型。但是注意它标注的灯和国内常见的信号灯存在差异美国很多路口的信号灯是横排的国内纵排更多。Bosch小目标交通灯数据集是博世公司发布的专门针对小目标检测场景优化里面的信号灯在画面中占的面积非常小适合用来训练一个能在远距离识别信号的模型。DRIVE数据集偏自动驾驶视野更广信号灯不止出现在画面上方还可能在画面侧面。Aeroscapes数据集是无人机航拍视角虽然信号灯少但可以用来做数据增强的多样性参考。这几个数据集在公开渠道都能找到下载方式部分需要登记申请。如果你只是想跑通代码流程LISA数据集的子集就够用了。4.2 自采数据与标注细节开源数据集和真实场景有差距所以我强烈建议自己拍摄补充数据。拍摄不需要专业设备手机就行。拍摄时注意几个点先在晴天正午拍一组再在阴天拍一组最后在晚上拍一组同一个路口要站在不同角度拍模拟不同车道的视角信号灯要尽量在不同画面位置出现不能总在正中间。这样做的目的是让数据集覆盖尽量多的维度。数据标注用的是LabelImg这个工具。安装使用很简单就是一个Python GUI程序打开图片画框选目标然后保存成标注文件。保存的时候可以选择PascalVOC格式也可以选YOLO格式。我这里两个格式都导出了一下因为传统OpenCV训练和YOLO训练用的格式不一样。标注的时候踩过一个坑想提醒大家标注框不要比目标大太多。很多新手标注的时候会习惯性地把整个灯组的灯罩框进去认为这样有助于模型学习。但实际效果恰恰相反框太大会引入过多背景信息干扰模型学习。正确的做法是框刚好紧贴信号灯圆形灯盘边缘误差控制在2到3个像素以内。标注类别的划分也值得琢磨。最细的划分方式是按颜色分成红、黄、绿三类这也是我项目里采用的。有些比赛会把信号灯直接分成“红”、“绿”两类忽略黄灯但真实交通场景里黄灯很重要所以不建议省。如果数据量充足甚至可以考虑进一步分类成“红灯直行”、“红灯左转”、“绿灯直行”、“绿灯左转”等带箭头的类别这样和控制逻辑结合得更紧密。4.3 数据增强与样本平衡数据增强的目的不是制造假数据而是逼近真实世界的变化。OpenCV自带的图像变换函数就能完成大部分增强工作。亮度调整是最重要的增强方式。同一批图片把亮度随机调整到原图的70%到130%相当于模拟了一天中不同时间段的光照变化。对比度调整类似模拟的是雾天和晴天之间的差异。平移和缩放是给模型增加目标位置的多样性。水平翻转在信号灯场景中要慎用因为翻转之后左转箭头的方向会颠倒如果数据集中包含箭头指示灯翻转会让语义错乱。样本平衡是另一个容易踩的坑。开车的人都知道红灯和绿灯是交替出现的但一个周期里黄灯时间最短。采集来的数据天然不平衡绿灯和黄灯的数量差个三四倍很常见。如果直接用不平衡的数据训练模型会对多数类样本过拟合对小样本类别的识别效果会变差。解决不平衡的思路有几种一是对少数类做过采样把黄灯样本重复若干次二是对多数类做降采样随机丢弃一部分绿灯样本三是结合增强方法针对性地对黄灯样本做更多随机扰动。我实践下来认为降采样的效果不如过采样明显但也不能过度过采样容易造成模型对特定样本的过拟合。这个尺度需要根据实际数据集的情况来调整。5. 从传统视觉到深度学习YOLOv8实战5.1 为什么还要训练一个YOLO模型既然传统OpenCV方案已经能在很多场景下稳定工作了为什么还要费劲去训练一个YOLOv8模型我在前面提到过传统方案的瓶颈不是速度而是复杂场景的鲁棒性。举两个真实案例。第一个是夜间场景。夜间的红绿灯周围有大量的亮斑和光晕颜色被光晕扩散之后HSV的颜色范围判断会严重失真。红灯在夜间颜色的饱和度不高但V通道的值又非常高导致阈值很难同时满足。第二个是遮挡场景。如果有树枝挡住了信号灯的一半传统方案基于轮廓的筛选就会失效因为轮廓不完整了。深度学习模型从数据中自己学习特征不会依赖你手工设定的阈值。只要训练数据里有足够多的夜间、遮挡、反光样本模型就能学到这些场景下的特征组合。所以在实际应用中我更推荐用传统方案做快速预筛再用深度学习模型做最终确认。两级结构兼顾了速度和精度。5.2 数据集格式转换与训练YOLOv8训练要求的标注格式是每张图片对应一个同名txt文件文件每行记录一个目标格式是“类别编号 x_center y_center width height”坐标都是归一化到0到1的小数。LabelImg可以直接导出YOLO格式但如果你手里已有的是PascalVOC的XML文件需要做一个转换。这个转换逻辑不复杂读取XML里的bndbox坐标除以图片宽高得到归一化坐标最后算出中心点和宽高。import xml.etree.ElementTree as ET def convert_voc_to_yolo(xml_file, class_names): tree ET.parse(xml_file) root tree.getroot() img_width int(root.find(size/width).text) img_height int(root.find(size/height).text) labels [] for obj in root.findall(object): class_name obj.find(name).text if class_name not in class_names: continue class_id class_names.index(class_name) bbox obj.find(bndbox) x_min int(bbox.find(xmin).text) y_min int(bbox.find(ymin).text) x_max int(bbox.find(xmax).text) y_max int(bbox.find(ymax).text) x_center (x_min x_max) / 2 / img_width y_center (y_min y_max) / 2 / img_height width (x_max - x_min) / img_width height (y_max - y_min) / img_height labels.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) return \n.join(labels)训练之前要把数据按8比1比1分成训练集、验证集和测试集。注意一点同一个场景连续帧的图片不要同时出现在训练集和验证集里否则模型会“作弊”。比如一段视频截取了300帧其中150帧放训练集、150帧放验证集验证效果会虚高因为场景太相似了。应该按场景切分把整个视频的所有帧都放到同一个集合里。训练命令也很简单yolo detect train datatraffic_light.yaml modelyolov8n.pt epochs100 imgsz640 batch16yolov8n是轻量版本参数量最少训练速度快适合在普通电脑上跑。如果要追求更高的精度可以换成yolov8s或者yolov8m但显存占用和推理时间都会明显增加。对信号灯这种目标尺寸较小的场景imgsz建议设成640以上太小会丢失小目标的细节特征。一个训练中的重要参数是patience控制的是早停策略。如果连续多少个epoch在验证集上的指标没有提升训练就会提前终止。我一般设成20既能节省时间又不会太容易过早停止。5.3 YOLO模型与OpenCV方案的性能对比我把两种方案在同一个测试集上做了对比我的机器是i5-12400F加RTX 3060结果如下指标OpenCV传统方案YOLOv8n模型检测准确率91.2%96.8%夜间场景准确率82.6%93.4%平均处理帧率CPU45 FPS8 FPS平均处理帧率GPU-62 FPS部署体积约1MB约12MB传统方案在白天场景的准确率其实不低和YOLO模型差距在3到5个百分点以内。但到了夜间传统方案的准确率明显下降差距拉大到了10个百分点以上。这说明深度学习模型的优势主要体现在复杂场景上。但传统方案的帧率优势在CPU上有碾压级的优势如果你的设备没有GPU用纯OpenCV方案做实时性会好很多。混合策略是用OpenCV做快速预筛排除绝大多数无关区域只对可能是信号灯的区域送进YOLO模型做精确认证。这样既保证了复杂场景下的准确率又避免了整张图跑深度学习造成的算力浪费。这个混合架构也是工业界非常常用的做法。6. 常见问题与排错速查6.1 高频报错与解决办法这几年看到新手在这个项目上踩坑最多的几个问题整理成一张速查表建议收藏。报错信息原因分析解决办法ModuleNotFoundError: No module named cv2OpenCV未安装或环境选择错误pip install opencv-python在PyCharm中检查解释器路径是否与pip对应的环境一致error: (-215:Assertion failed) !ssize.empty()cv2.imread读取失败通常是图片路径错误使用绝对路径或先os.path.exists检查文件存在性cv2.error: OpenCV(4.x) ... function/feature is not implemented模块不完整或使用了未编译模块检查是否误装了opencv-python-headless换成完整版或改用opencv-contrib-pythonTypeError: Expected Ptrcv::UMat for argument src传入空图像或数据类型不匹配检查图像是否为空frame is not None用np.asarray转换数据类型AttributeError: NoneType object has no attribute shape摄像头打开失败或视频文件路径错误cap.isOpened()先检查换用视频文件时检查路径和文件名这里重点说一下第三个报错。OpenCV error: The function/feature is not implemented这个提示在Windows上最常见的原因是你装的是opencv-python-headless版本。这个版本去掉了GUI显示模块如果在代码里调用了cv2.imshow就会直接抛这个错误。解决办法就是换成标准的opencv-python包。还有一个隐蔽的坑是cv2.VideoCapture在Windows上读取GStreamer格式的视频时会失败。如果本地视频打不开先试试用ffmpeg把视频转成MP4编码H.264再读。这个问题的排查方式很直接打印cap.isOpened()的值如果是False先确认视频文件本身能不能用播放器打开能打开就基本可以判定是编码格式问题。6.2 识别效果不佳的优化思路如果跑完代码发现识别效果不理想不要急着怀疑算法本身先按下面的顺序排查。先看是不是图片素材的问题。把一帧有问题的图保存下来用图像查看工具打开看信号灯在画面中到底占多大面积。如果信号灯在1080P的图片里只占30乘30像素那对传统OpenCV方案来说太小了面积筛选已经被过滤掉了。这种场景应该考虑切割ROI区域只对信号灯可能出现的位置做检测。再看颜色阈值设置是否合理。把HSV分割的结果可视化出来直接用cv2.imshow查看掩膜图像如果掩膜里只有零星的白色点说明阈值范围偏窄需要放宽色相范围或降低饱和度阈值。如果掩膜里大片白色说明范围太宽把背景也纳入了。调参不能靠猜必须看可视化结果来调整。然后检查轮廓筛选参数。如果掩膜里有正确形状的白色区域但没有被检测出来问题大概率出在面积阈值或填充率阈值上。把cv2.findContours的返回结果画到原图上检查看看轮廓到底提取到了没有。最后考虑加ROI区域。固定摄像头场景下信号灯只可能出现在画面的上部。把识别区域限定在上半部分能大幅减少车尾灯、刹车灯的干扰。这个做法虽然粗暴但相当有效。我建议ROI区域设置成整个画面的上半部分再加一些余量不需要精确到单个灯的位置否则摄像头微调角度就得重新设置ROI。6.3 项目还可以怎么扩展这个项目目前实现的是单路口的信号灯状态识别与控制模拟但如果要说拓展空间方向非常多。最直接的方向是接入真实硬件。用树莓派加摄像头模块做图像采集终端把识别结果通过GPIO端口输出控制真实的小型信号灯模型。这样就把一个纯软件模拟变成了一套物理可演示的系统。这个方向适合做嵌入式与计算机视觉的交叉训练。第二个方向是车路协同模拟。多路口的信号灯控制系统加上通信模块让相邻路口的信号灯状态通过消息队列同步联动可以模拟城市级信号协调控制的场景。这里用到的技术栈会扩展到socket通信、消息队列、并发控制对掌握后端知识也有帮助。第三个方向是算法升级。目前的颜色识别本质上是基于规则的面对异常天气还会失效。可以尝试引入针对性的图像增强网络如基于GAN的低光照增强在预处理阶段把夜间图像恢复成接近白天的效果再交给颜色识别模块。这个方向偏研究型但数据集构建的经验可以复用。不管你选择哪个方向这套系统里沉淀下来的东西——图像采集、数据标注、模型训练、系统集成——都是通用的能力。我现在做很多视觉项目时还是会回到这套框架里来思考问题。信号灯只是一个载体方法论才是真正值钱的东西。本文还有配套的精品资源点击获取
返回列表