ARTICLE DETAIL

资讯详情

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

Python+OpenCV红绿灯识别系统源码拆包:HSV阈值调参到GUI滤镜实战

Python+OpenCV红绿灯识别系统源码拆包:HSV阈值调参到GUI滤镜实战 简介这是一套基于Python与OpenCV实现的红绿灯识别系统源代码面向计算机视觉初学者、课程设计或自动驾驶入门研究者帮助解决交通灯颜色与形状识别的工程落地问题。资源包共10个文件以5个py脚本为核心辅以3个json配置、1张png示意图和1个gitignore整体约23KB轻量易读便于快速运行与二次修改。系统通过灰度转换、Canny边缘检测与BGR到HSV色彩空间转换完成图像预处理再借助inRange函数设定色度阈值区分红、绿、黄三色并结合圆形或矩形形状分析确认交通灯状态GUI界面支持滤镜选择与实时结果展示可应对光照变化、摄像头视角差异等实际场景。目前已有651人学习下载读者可从中获得完整的颜色阈值调试思路、形状判别逻辑与界面交互实现是理解OpenCV图像处理流程与交通灯识别算法的实用参考。1. 红绿灯识别系统源码拆包从 HSV 阈值到 GUI 滤镜的完整落地路口监控画面里红灯和刹车灯、广告牌红光混在一起这是做交通灯识别最常见的翻车现场。这份基于 Python OpenCV 的红绿灯识别系统源代码核心就是解决「颜色相近但语义不同」的筛选问题它用 HSV 色彩空间做阈值分割配合 GUI 界面让使用者实时切换滤镜、观察识别结果。压缩包find-traffic-lights.zip里包含gui_opencv.py主程序、find.py/find2.py/self.py等迭代版本以及.vscode配置目录。适合正在找 OpenCV 图像处理项目练手的人也适合想把颜色识别模块嵌进自己交通监控流程的开发者。下面按「跑起来 → 调参数 → 避坑 → 进阶」的顺序拆。2. 环境搭建与源码结构先让 gui_opencv.py 跑出第一帧2.1 依赖安装与 OpenCV 版本选择这套代码依赖很轻核心就三个包opencv-python、numpy、tkinterPython 自带。但 OpenCV 的安装方式直接决定后面会不会踩坑。我一般推荐用 pip 装官方 wheel而不是 conda 默认源因为 conda 的opencv包有时不带 GUI 后端cv2.imshow会报错。# 建议 Python 3.8 ~ 3.11太新的版本部分 wheel 还没跟上 python -m pip install opencv-python4.8.1.78 numpy # 验证安装能打印版本号且不报 ModuleNotFoundError 即可 python -c import cv2; print(cv2.__version__)这里锁4.8.1.78不是玄学是因为 4.9 之后部分inRange和cvtColor的边界行为有微调老代码里写死的阈值可能表现不一致。如果你已经装了别的版本先别急着换跑一遍看识别结果再决定。numpy是 OpenCV 的底层数组依赖版本跟着 pip 自动解析就行不用手动指定。提示如果import cv2报ModuleNotFoundError: No module named opencv八成是装到了另一个 Python 环境里。用python -m pip -V确认 pip 对应的解释器路径和运行脚本的解释器一致。2.2 源码文件逐个说明解压后目录结构大致是这样每个文件都有它的用途别一上来就删「中间版本」文件 / 目录作用是否必须gui_opencv.pyGUI 主入口含滤镜选择与识别显示是find.py基础版识别逻辑纯阈值分割参考find2.py改进版加入形态学处理参考self.py个人调试脚本参数试验用否中间版本.py迭代过程留档否yf.png测试用交通灯图片是.vscode/编辑器配置launch/settings否主程序gui_opencv.py是唯一需要直接运行的入口。find.py和find2.py更像是「算法演进笔记」——前者只有inRange硬阈值后者加了开闭运算去噪点。想理解识别逻辑怎么一步步变稳的建议按find.py → find2.py → gui_opencv.py的顺序读比直接啃主程序清晰得多。2.3 第一次运行与 GUI 交互在项目根目录执行python gui_opencv.py正常的话会弹出一个窗口左侧是原图/视频帧右侧是识别结果顶部或侧边有滤镜下拉框。如果窗口一闪而过通常是tkinter没装Linux 上需要sudo apt install python3-tk。GUI 里的滤镜选项一般包括高斯模糊、中值模糊、灰度预览、HSV 通道预览。切换滤镜时识别结果会实时刷新这就是调参的入口——先用模糊压掉噪点再看 HSV 阈值是否把红绿灯框出来。注意Windows 上如果弹窗后画面卡死多半是cv2.waitKey的刷新循环和 tkinter 主循环打架。常见做法是把 OpenCV 的显示嵌进 tkinter 的 Canvas而不是单独开imshow窗口。这份代码如果用的是独立窗口切换滤镜时别狂点等一帧处理完再操作。3. HSV 阈值与颜色分割红绿黄三色的参数怎么定3.1 为什么用 HSV 而不是 RGBRGB 三个通道都和亮度耦合白天和傍晚同一个红灯的 RGB 值能差出一大截阈值根本没法固定。HSV 把色相H、饱和度S、明度V拆开颜色判断主要看 H亮度变化主要影响 V这样光照波动时只需微调 V 的范围。这就是几乎所有颜色识别项目都先cvtColor到 HSV 的原因。import cv2 import numpy as np # 读取测试图BGR 格式 img cv2.imread(yf.png) # 转 HSV注意 OpenCV 的 H 范围是 0~179不是 0~360 hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 红色在 HSV 里跨了 0 和 180 两端所以要分两段 lower_red1 np.array([0, 100, 100]) upper_red1 np.array([10, 255, 255]) lower_red2 np.array([160, 100, 100]) upper_red2 np.array([179, 255, 255]) mask_red cv2.inRange(hsv, lower_red1, upper_red1) | \ cv2.inRange(hsv, lower_red2, upper_red2) # 绿色和黄色各一段即可 mask_green cv2.inRange(hsv, np.array([40, 80, 80]), np.array([85, 255, 255])) mask_yellow cv2.inRange(hsv, np.array([20, 100, 100]), np.array([35, 255, 255]))逻辑说明inRange返回的是二值掩膜白色255表示命中该颜色范围。红色必须分两段因为 H 在 0 和 179 附近是连续的红色区中间被其他颜色隔开。参数里第二个值是饱和度下限第三个是明度下限——把 S 和 V 的下限设太低灰色和白色物体也会被误判成红绿灯这是新手最常翻的车。3.2 阈值调参的实操方法调参不要靠猜用 GUI 里的 HSV 预览滤镜把鼠标移到红绿灯像素上看实时 H/S/V 值。我一般会这么做先切到 HSV 通道预览观察目标灯在当前光照下的 H 值落在哪个区间把 S 下限设到 80~100过滤掉发白的过曝区域V 下限设到 80 左右过滤掉阴影里的暗色块回到识别视图看掩膜里有没有残留噪点有就加形态学处理。# 形态学开运算先腐蚀后膨胀去掉小噪点 kernel np.ones((5, 5), np.uint8) mask_red cv2.morphologyEx(mask_red, cv2.MORPH_OPEN, kernel) # 闭运算先膨胀后腐蚀填补灯内部空洞 mask_red cv2.morphologyEx(mask_red, cv2.MORPH_CLOSE, kernel)kernel尺寸是关键参数5×5 适合 640×480 左右的画面画面越大核也要相应加大否则噪点去不干净但核太大又会把远处的小灯一起吃掉。常见做法是先固定 5×5识别不到远处灯再考虑上采样或分区域处理。3.3 形状过滤排除红色广告牌和车尾灯光靠颜色红色广告牌、刹车灯、红色车身都会命中。加一层形状约束能砍掉大部分误检交通灯通常是圆形或带圆角的矩形且面积在合理范围内。contours, _ cv2.findContours(mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: area cv2.contourArea(cnt) if area 100: # 太小的噪点直接丢 continue perimeter cv2.arcLength(cnt, True) # 圆形度4πA/P²越接近 1 越像圆 circularity 4 * np.pi * area / (perimeter * perimeter) if perimeter 0 else 0 if circularity 0.6: # 阈值可调0.6 是经验起点 x, y, w, h cv2.boundingRect(cnt) cv2.rectangle(img, (x, y), (x w, y h), (0, 255, 0), 2)area下限和circularity阈值是两个需要按场景调的参数。监控画面里灯很小area下限要降到 50 甚至更低圆形度阈值太高会漏掉被遮挡的灯太低又会放进方形广告牌。我的习惯是先把圆形度关掉设 0看纯颜色命中了哪些再逐步加约束观察哪个约束砍掉了真目标。4. 避坑与常见问题排查识别不稳时先查这五条4.1 现象红灯识别到了但绿灯一直不亮原因绿色 H 范围设得太窄或者 S/V 下限过高。阴天时绿灯饱和度会下降如果 S 下限卡在 150直接全被过滤掉。解决把绿色范围放宽到[35, 60, 60] ~ [90, 255, 255]先保证能命中再慢慢收紧。用 HSV 预览确认绿灯像素的实际 S 值下限设在实际值往下 20~30 的位置。4.2 现象画面里所有红色物体都被框出来原因只做了颜色分割没做形状和面积过滤或者area下限设得太小。解决加上 3.3 节的轮廓过滤area下限从 100 起调圆形度阈值从 0.5 起调。如果广告牌是方形圆形度能直接把它压到 0.3 以下。4.3 现象切换滤镜后程序卡顿甚至无响应原因每一帧都在做全图cvtColor 多次inRange 形态学分辨率高时计算量翻倍GUI 主循环和图像处理循环没分离。解决先把处理分辨率降下来比如cv2.resize到宽 640 再处理识别坐标再按比例映射回原图。滤镜只对预览图生效不要对每一帧都跑全部滤镜。4.4 现象cv2.imshow报错或窗口不显示原因装的是opencv-python-headless这个包不带 GUI 后端常见于服务器环境或 conda 自动装的版本。解决卸载 headless 版重装完整版python -m pip uninstall opencv-python-headless python -m pip install opencv-python4.5 现象视频流读取几帧后就中断原因cv2.VideoCapture的缓冲和读取节奏不匹配或者视频源本身不稳定。解决读帧后加一个cap.grab()跳帧或者用cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)减小缓冲。如果是摄像头确认没有被其他程序占用。5. 进阶技巧把识别结果变成可用的状态输出5.1 从「框出来」到「输出状态」识别到颜色只是第一步实际用的时候需要的是「当前是红灯/绿灯/黄灯/未知」这样的状态字符串。做法是统计每种掩膜里符合形状条件的轮廓数量取面积最大的那个作为主目标def detect_light(hsv): results {} for color, mask in [(red, mask_red), (green, mask_green), (yellow, mask_yellow)]: contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) best None for cnt in contours: area cv2.contourArea(cnt) if area 100: continue if best is None or area cv2.contourArea(best): best cnt results[color] best # 取面积最大的颜色作为当前状态 valid {k: v for k, v in results.items() if v is not None} if not valid: return unknown, None state max(valid, keylambda k: cv2.contourArea(valid[k])) return state, valid[state]逻辑说明这段函数返回状态字符串和对应轮廓。unknown分支很重要——当三种颜色都没命中时不要硬猜输出未知让上层逻辑决定是等待还是报警。参数上area 100这个下限要和 3.3 节保持一致否则状态判断和框选结果会对不上。5.2 用滑动窗口稳住状态输出单帧识别会有抖动某一帧红灯被遮挡状态就跳到 unknown。加一个长度为 5 的滑动窗口取出现次数最多的状态作为输出能明显减少跳变。from collections import deque, Counter history deque(maxlen5) def stable_state(hsv): state, _ detect_light(hsv) history.append(state) # 取窗口内出现最多的状态 return Counter(history).most_common(1)[0][0]maxlen是窗口大小5 帧在 30fps 下约 0.17 秒响应够快又能压抖动。如果场景变化剧烈可以降到 3如果要求极稳可以升到 10但会引入延迟。5.3 验证方法用测试图跑一遍完整链路改完参数别直接上视频先用yf.png跑一遍确认三种颜色都能正确输出状态。然后拿一段包含红绿灯的短视频逐帧看状态序列是否连续。我一般会打印每帧的状态和面积值观察有没有突然的面积跳变——那通常意味着误检了别的红色物体。从那以后我每次调完 HSV 阈值都强制用同一张测试图跑一遍回归确认没有把之前能识别的颜色调丢。这套流程看着笨但比在视频里反复试快得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表