Python+OpenCV红绿灯检测实战:HSV颜色空间与轮廓筛选
发布时间:2026/9/28 16:45:11来源:尧图网络
简介这份资源面向计算机视觉初学者与智能交通方向开发者提供一套基于Python与OpenCV的红绿灯检测完整实现可用于自动驾驶感知、交通监控等场景的入门实践。压缩包共12个文件以6个png与1个jpg示例图像、2个py核心脚本、2个md说明文档为主另含1个pyc缓存文件整体约343KB体积轻便便于快速运行与二次修改。核心脚本围绕颜色空间转换、轮廓与形状识别等思路展开配套图像覆盖红、绿、黄及箭头指示灯等多种状态方便读者对照调试检测效果。目前已有4037人学习下载说明该方案在同类入门项目中具备一定参考价值。读者可借此理解从图像预处理到目标识别的完整流程掌握HSV阈值分割与颜色判定等关键技巧并在此基础上扩展实时视频流处理或深度学习端到端检测为智能交通与自动驾驶相关项目打下实践基础。1. 红绿灯检测这件事为什么用 Python OpenCV 就能跑起来路口摄像头拍下一帧画面画面里同时有红灯、绿灯、黄灯、车尾灯、广告牌上的红色 LED、夕阳反光。你要让程序在几十毫秒内告诉你「当前是红灯」这就是红绿灯检测要解决的问题。它属于典型的传统视觉任务目标颜色固定、形状固定、位置相对固定不需要训练大模型用 Python 加 OpenCV 就能搭出一套能跑的原型。适合谁做智能小车、做交通流统计、做路口违章抓拍预研、或者单纯想找一个能落地的 opencv图像处理项目练手的工程师。很多人一上来就想上 YOLO其实在红绿灯这个场景里颜色阈值加形状过滤的性价比高得多调参也直观。下面把我自己反复调过的一套流程拆开讲从环境到代码到踩坑尽量让你照着能复现。2. 环境准备与颜色空间选型别在第一步就翻车2.1 Python 与 OpenCV 的安装以及那个经典报错新手最容易卡在环境上。python安装教程网上一大把但真正影响你后面能不能跑通红绿灯检测的是 OpenCV 装没装对、装的是哪个包。常见做法是用 pip 装opencv-python如果你还要用 CUDA 加速或者读视频流再考虑opencv-contrib-python。我一般会建一个独立虚拟环境避免和系统里的包打架。# 建虚拟环境Python 3.8 以上都行 python -m venv traffic_env # 激活Windows traffic_env\Scripts\activate # 激活Linux / macOS source traffic_env/bin/activate # 装 OpenCV 主包和 numpy pip install opencv-python numpy # 验证是否装好能打印版本号就说明没问题 python -c import cv2; print(cv2.__version__)逻辑说明虚拟环境是为了隔离依赖红绿灯检测本身依赖很轻但如果你后面要接 Flask 做可视化界面或者接爬虫拉数据依赖一多就容易冲突。参数说明opencv-python是主包包含cv2模块numpy是 OpenCV 的底层数组依赖必须一起装。验证那行命令如果报ModuleNotFoundError: No module named opencv九成是装到了另一个 Python 解释器里用which python或where python确认当前解释器路径再重新 pip 装一次。提示如果你在 vscode python环境配置里选了错误的解释器终端能 import 但编辑器里报红线去右下角切换解释器到虚拟环境那个。2.2 为什么红绿灯检测优先用 HSV 而不是 RGB这是整个方案里最关键的一个选型决定。RGB 三个通道都和亮度耦合白天和傍晚同一个红灯的 RGB 值能差出一大截你按 RGB 调好的阈值换个时段就失效。HSV 把色相H、饱和度S、明度V拆开红绿灯的颜色信息主要集中在 H 通道光照变化主要影响 V 通道。这意味着你可以对 H 设一个相对稳定的范围把 V 的下限压低一点来兼容暗光鲁棒性直接上一个台阶。import cv2 import numpy as np # 读一帧图实际项目里可能是摄像头或视频流 frame cv2.imread(intersection.jpg) # BGR 转 HSV注意 OpenCV 默认读进来是 BGR 不是 RGB hsv cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 红色的 H 在 OpenCV 里跨了 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([180, 255, 255]) mask_red1 cv2.inRange(hsv, lower_red1, upper_red1) mask_red2 cv2.inRange(hsv, lower_red2, upper_red2) mask_red cv2.bitwise_or(mask_red1, mask_red2) # 绿色相对简单H 在 40 到 90 之间 lower_green np.array([40, 80, 80]) upper_green np.array([90, 255, 255]) mask_green cv2.inRange(hsv, lower_green, upper_green)逻辑说明cv2.inRange做的是逐像素区间判断落在区间内输出 255否则 0得到一张二值掩膜。参数说明H 范围 0 到 180OpenCV 把标准 0 到 360 压缩了一半S 和 V 都是 0 到 255。红色之所以要两段是因为色相环上红色在 0 和 180 两端只取一段会漏掉一半红色。S 下限设 100 是为了滤掉发白的浅红V 下限设 100 是为了滤掉很暗的红色这两个值要根据你的实际画面调夜间场景 V 下限可以降到 50 左右。注意别用 RGB 直接卡阈值我见过太多人白天调好晚上全废血泪经验。3. 从掩膜到灯位形态学、轮廓与圆形筛选3.1 形态学去噪把零散像素连成块inRange出来的掩膜往往是一堆碎点尤其是画面有噪点或者灯有光晕的时候。直接拿去 findContours 会得到几百个小轮廓根本没法判断。常见做法是先开运算去掉小噪点再闭运算把灯的内部填实。# 开运算先腐蚀后膨胀去掉孤立小点 kernel_open cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) mask_red cv2.morphologyEx(mask_red, cv2.MORPH_OPEN, kernel_open) # 闭运算先膨胀后腐蚀填补灯内部空洞 kernel_close cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9, 9)) mask_red cv2.morphologyEx(mask_red, cv2.MORPH_CLOSE, kernel_close)逻辑说明开运算的核要比噪点大、比灯小5x5 是个稳妥起点闭运算的核要能覆盖灯内部的暗斑9x9 适合中等分辨率画面。参数说明MORPH_ELLIPSE是椭圆核比矩形核更贴合圆形灯边缘更自然。如果你的画面分辨率是 1920x1080核可以适当加大到 7x7 和 13x13如果是 640x480就缩小到 3x3 和 5x5。这一步调不好后面轮廓筛选全是白费。3.2 轮廓筛选面积、圆形度、长宽比三把尺子拿到干净的掩膜后用findContours提取轮廓然后逐个判断是不是灯。红绿灯的物理特征很明确近似圆形、面积在一个合理区间、长宽比接近 1。这三个条件能滤掉绝大部分干扰比如车尾灯长条形、广告牌面积过大、反光点面积过小。contours, _ cv2.findContours(mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: area cv2.contourArea(cnt) # 面积过滤太小是噪点太大不是灯 if area 100 or area 5000: continue # 圆形度4*pi*面积 / 周长^2越接近 1 越圆 perimeter cv2.arcLength(cnt, True) if perimeter 0: continue circularity 4 * np.pi * area / (perimeter * perimeter) if circularity 0.6: continue # 长宽比过滤接近 1 才像圆 x, y, w, h cv2.boundingRect(cnt) aspect_ratio w / float(h) if aspect_ratio 0.7 or aspect_ratio 1.4: continue # 通过筛选画出来 cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) cv2.putText(frame, RED, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)逻辑说明contourArea算轮廓面积arcLength算周长圆形度公式是标准定义。参数说明面积下限 100 是针对 640x480 画面的经验值1080p 画面要按比例放大到 400 左右圆形度阈值 0.6 是宽松值实际灯被遮挡时圆形度会下降太严会漏检长宽比 0.7 到 1.4 覆盖了轻微形变。这三个参数没有万能值必须拿你自己的画面跑一遍看效果再微调。提示cv2.contourArea()在某些 OpenCV 版本里如果传进去的轮廓点太少会返回 0加个 perimeter 判空能避免除零。4. 避坑与排查红绿灯检测最常见的五个翻车现场4.1 红灯绿灯同时被检出现象一帧画面里红色掩膜和绿色掩膜都找到了轮廓程序同时输出 RED 和 GREEN。原因黄灯过渡阶段或者画面里有绿色植物、红色车身颜色阈值没卡干净。解决先做颜色互斥判断如果同一位置附近同时有红和绿按面积大的优先或者引入位置先验——红绿灯三个灯是垂直排列的找到一组后按上下顺序判断当前亮的是哪个。4.2 夜间检测全灭现象白天跑得好好的晚上一个灯都检不出来。原因夜间画面整体 V 通道值很低你设的 V 下限 100 把灯也滤掉了。解决不要用固定阈值先算画面平均亮度暗光时把 V 下限降到 40 到 60同时把 S 下限也降一点因为夜间灯的饱和度看起来会偏低。更稳的做法是加一个自适应对 V 通道做直方图均衡再转 HSV。4.3 轮廓数量爆炸程序卡死现象findContours返回几千个轮廓for 循环跑不完。原因形态学核太小或者画面噪点太多掩膜里全是碎点。解决先做一次高斯模糊再转 HSVcv2.GaussianBlur(frame, (5, 5), 0)然后加大开运算核。另外在 for 循环里先做面积过滤再做其他计算面积不达标直接 continue能省大量计算。4.4 视频流读取中断现象用cv2.VideoCapture读 RTSP 或本地视频跑一会儿就卡住或返回空帧。原因网络抖动或者解码器缓冲。解决读帧后判断ret是否为 TrueFalse 就重连本地视频可以加cap.set(cv2.CAP_PROP_POS_FRAMES, ...)做跳帧。如果是 opencv python拉流中断常见做法是起一个独立线程专门读帧主线程只处理最新一帧避免处理速度跟不上导致缓冲堆积。4.5 检测框抖动严重现象连续帧里同一个灯的框位置跳来跳去。原因每帧独立检测没有做时序平滑。解决加一个简单的跟踪比如用上一帧的框和当前帧的框做 IoU 匹配匹配上就用滑动平均更新位置。或者直接上卡尔曼滤波但对红绿灯这种准静态目标滑动平均就够了。5. 进阶技巧用 ROI 和时序投票把准确率再拉一截前面那套流程跑通之后你会发现大部分场景已经能用了但路口场景有个天然优势没利用起来红绿灯的位置在画面里基本固定。你可以先手动或自动框出红绿灯所在的 ROI 区域只在 ROI 里做颜色检测干扰直接少一大半。自动框 ROI 的常见做法是先用霍夫圆检测找候选再按垂直排列规律聚类。# 假设已经通过某种方式拿到了红绿灯区域的 ROI 坐标 roi_x, roi_y, roi_w, roi_h 800, 100, 120, 300 roi frame[roi_y:roi_y roi_h, roi_x:roi_x roi_w] # 只对 ROI 做 HSV 转换和掩膜 roi_hsv cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) # ... 后续 inRange 和轮廓筛选都在 roi 上做逻辑说明ROI 把搜索空间从整帧缩小到一个固定区域计算量降一个数量级误检也大幅减少。参数说明ROI 坐标可以写死也可以用第一帧的检测结果自动确定之后每帧复用。如果摄像头会转动ROI 需要定期更新。时序投票是另一个便宜又好用的技巧。单帧检测可能因为遮挡或噪声出错但连续 5 帧里如果有 4 帧都判为红灯那基本可以确信。实现上维护一个长度为 5 的队列每帧把检测结果 push 进去取众数作为最终输出。from collections import deque, Counter history deque(maxlen5) def vote(current_state): history.append(current_state) # 取出现次数最多的状态 return Counter(history).most_common(1)[0][0] # 每帧调用 final_state vote(RED) # 传入当前帧的检测结果逻辑说明deque固定长度自动淘汰旧数据Counter统计频次。参数说明窗口长度 5 是延迟和稳定性的折中要求响应快就降到 3要求稳就加到 7。这个技巧对黄灯这种持续时间短的信号要慎用窗口太长会把黄灯平滑掉。我自己的习惯是任何红绿灯检测的原型先不加 ROI 和投票跑一遍记录误检和漏检的帧再针对性加这两个模块而不是一上来就堆复杂逻辑。调参的时候把中间掩膜用cv2.imshow显示出来肉眼看着调比盲猜快十倍。这套方案不敢说能直接上生产但作为 opencv识别物体 的入门到进阶练手项目性价比足够高。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网