Python+OpenCV实现红绿灯识别与控制系统完整实战

📅 发布时间:2026/10/13 0:03:53
Python+OpenCV实现红绿灯识别与控制系统完整实战
简介这是一套基于Python与OpenCV设计的交通路口红绿灯控制系统源码面向正在学习计算机视觉、图像处理及Python编程的开发者尤其适合需要课程设计或毕业设计参考的读者。包内共34个文件涵盖py源码、pyc编译文件、xml配置、html/css/js前端页面、jpeg与png图像素材、txt说明及README等整体压缩包约1.35MB目录结构清晰便于按功能模块查阅。已有128人学习下载。源码围绕OpenCV展开涉及颜色空间转换RGB转HSV、阈值分割、Canny边缘检测、轮廓提取、VideoCapture实时视频流处理以及基于识别结果的红绿灯切换逻辑同时附带模拟路口素材和简单的控制面板可帮助使用者直观理解从图像采集到状态判断、再到信号控制的完整链路具有较强的实战参考价值。1. 交通路口红绿灯识别这套PythonOpenCV源码到底能跑出什么效果做计算机视觉项目最怕的不是算法难而是拿到一份源码不知道从哪下手、跑起来又全是坑。这套基于Python和OpenCV的交通路口红绿灯控制系统好就好在它是一个完整的闭环——从视频流里抓帧、用HSV颜色空间识别红绿灯颜色、通过轮廓检测锁定灯位、再结合状态机逻辑决定信号灯切换最后还能把状态记录到数据库、用Web页面查看。它不是单纯教你调一个cv2.inRange的demo而是把“识别”和“控制”串在了一起这对于做课程设计、毕业设计或者刚入行想完整跑通一个OpenCV项目的开发者来说性价比很高。你不需要一台带GPU的机器也不用懂深度学习只要会基础的Python语法跟着把环境装好、把main.py跑起来就能看到一个模拟路口的红绿灯被实时识别和切换。2. 先把环境立起来OpenCV、Flask与这份工程的结构拿到Python_OpenCV_TrafficLights这份源码先别急着双击main.py第一步是把运行环境弄清楚。这个项目依赖OpenCV做图像识别但我看你机器上装了什么版本的OpenCV跑起来完全是两种体验。我自己常用的是一套相对稳妥的组合你照着装基本不会翻车。2.1 识别思路为什么选HSV而不选RGB在开始敲代码之前先花两分钟把识别原理说透不然你后面调参的时候会非常痛苦。交通路口的红绿灯在摄像头拍到的画面里通常是几百个像素大小的小区域直接用RGB判断颜色会受到光照、反光、白平衡的影响。同一个红色在晴天和阴天拍出来的RGB值可能差很多阈值范围不好定。OpenCV里默认读进来的图像是BGR顺序但做颜色分割时我一般会转成HSV空间。HSV把颜色的色调Hue、饱和度Saturation、明度Value拆开了其中H分量对人眼感知的颜色差异最敏感红色和绿色的H值区间在HSV空间里分得很开不容易被亮度干扰。这也是这份源码选择HSV作为识别核心的原因——告诉你不要直接在BGR上硬切阈值。2.2 安装依赖与跑通第一帧假设你已经装好了Python 3.8到3.10之间的版本用pip安装依赖就行。项目里有requirements.txt内容通常是OpenCV和Flask这类基础库但我不建议直接pip install -r requirements.txt一把梭因为国内网络环境下OpenCV这个包比较大很容易超时或装到一半报错。我一般会分两步装pip install opencv-python4.5.5.64 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install flask numpy -i https://pypi.tuna.tsinghua.edu.cn/simple先装OpenCV再装Flask和NumPy。这里锁版本是有原因的OpenCV 4.5.5在Windows和Linux上都很稳定不会出现高版本OpenCV在某些旧摄像头驱动下打不开设备的问题。装完验证一下python -c import cv2; print(cv2.__version__)能打印出版本号就说明OpenCV装好了。如果这一步就报ModuleNotFoundError: No module named cv2别往后继续走了先解决环境问题——大概率是Python解释器路径不对或者pip和python不是同一个环境。2.3 目录里每个文件是干什么的把工程解压之后你会看到一堆文件我按功能帮你捋清楚。main.py是主程序入口红绿灯识别的核心逻辑都在里面video.py负责读取视频流或者本地模拟视频文件sql.py管理SQLite数据库用来记录红绿灯状态切换的历史数据index.html和admin.html是两个Web页面前者展示当前路口的状态后者可以看成管理面板你看状态日志和统计数据都在这两个页面上。剩下的static文件夹放前端资源__pycache__是Python自动生成的缓存目录不用管它。requirements.txt里我建议你打开看一眼具体版本要求没有锁版本的话就按我上面给的方式装。整个项目的运行逻辑是main.py启动后会循环读取视频帧帧画面传入识别函数得出当前灯色状态后写入sql.py对应的表同时Flask路由把最新状态推给Web页面展示。3. 核心识别模块HSV阈值分割与轮廓锁定这一章是整个源码最值钱的部分也是你拿去答辩或者写报告时必须能讲清楚的地方。红绿灯识别本质上就是一个“找目标颜色验证形状”的过程OpenCV把这两步都封装成了现成函数但会用和用得对是两码事。3.1 从视频流里抓帧、转HSV、做掩码先看main.py里最核心的一段逻辑这段代码做的事情是读取一帧图像、把BGR转成HSV、然后拉出一块感兴趣区域ROI做后续判断。之所以要先裁区域是因为整个画面里天空、地面、行人都会干扰颜色识别只保留信号灯所在区域能大幅度降低误判率。import cv2 import numpy as np cap cv2.VideoCapture(0) # 0表示默认摄像头换成视频文件路径则可离线测试 while True: ret, frame cap.read() if not ret: break # 裁剪信号灯区域假设灯在上方1/4区域 roi frame[0:int(frame.shape[0] * 0.25), :] hsv cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) # 红色在HSV空间中因为色相环环绕需要两个范围 red_lower_1 np.array([0, 70, 50]) red_upper_1 np.array([10, 255, 255]) red_lower_2 np.array([170, 70, 50]) red_upper_2 np.array([180, 255, 255]) mask_red cv2.inRange(hsv, red_lower_1, red_upper_1) \ cv2.inRange(hsv, red_lower_2, red_upper_2) # 绿色范围 green_lower np.array([35, 70, 50]) green_upper np.array([77, 255, 255]) mask_green cv2.inRange(hsv, green_lower, green_upper) cv2.imshow(mask_red, mask_red) cv2.imshow(mask_green, mask_green) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()代码逻辑上注意三点。第一红色的CV2的H通道范围在0到180Hue值0到10和170到180都是红色所以需要两个区间做加法合并这是一个非常容易踩的坑——你只写[0, 10]会发现黄灯或者肤色也被当成红色。第二roi的裁剪比例0.25是经验值正常路口的信号灯都位于画面上部但如果你用的是俯拍视角或者摄像头装得低这个比例必须重新调节。第三cv2.inRange返回的是二值掩码图白色像素为目标区域后续所有轮廓检测都在这个掩码上进行。3.2 轮廓过滤与状态机控制逻辑掩码图出来之后直接用cv2.findContours找轮廓然后用面积和外接矩形长宽比过滤掉噪点。这一步的目的是区分“真的红绿灯”和“路边的红色招牌、红色汽车”。加一个最小面积阈值能把远处的小噪点过滤掉再加一个宽高比范围能把圆形灯和横条状的招牌区分开。contours, _ cv2.findContours(mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: area cv2.contourArea(cnt) if area 300: # 过滤小噪点300是经验值根据分辨率调整 continue x, y, w, h cv2.boundingRect(cnt) aspect_ratio w / float(h) if 0.5 aspect_ratio 1.8: # 红绿灯接近圆形宽高比接近1 cv2.rectangle(roi, (x, y), (x w, y h), (0, 255, 0), 2) current_state RED识别到当前灯色之后系统就要进入“控制逻辑”了。这个项目的控制逻辑不是一个简单的if-else它内部维护了一个状态机当前是红灯时记录亮灯起始时间当识别到绿灯时先判断红灯是否已经亮了足够长的时间比如30秒如果没到最短时长就继续等待到了才切换。这个用时间戳来避免频繁切换的设计很关键否则识别结果稍微抖动一下信号灯就会来回跳。3.3 阈值不通用是常态参数怎么调你在实际跑的时候会发现源码自带的HSV阈值在你的摄像头画面里不一定准。这不是源码的问题是每一个视觉项目的宿命——不同的摄像头色彩倾向、不同的光照条件、不同的信号灯LED材质拍出来的HSV值都会有偏移。我的做法是先把cv2.imshow把掩码图显示出来然后一点点调np.array里的数值。别想着一次到位先粗调H通道影响颜色区分度再微调S和V影响亮度和颜色浓度。调参的标准就一条保证掩码图上目标灯的范围完整、背景尽量为纯黑。4. 把结果管起来sql.py持久化与Web管理面板识别模块只是这个系统的眼睛要让一个红绿灯控制系统闭环必须把识别结果记录下来、展示出来这就是sql.py和两个HTML页面的职责。很多初学者做完识别拿到灯色就打印在终端然后项目就结束了这套源码好就好在把“识别”和“可视化管理”连起来了更像一个能落地的系统。4.1 sqlite记录灯态切换时序sql.py做的事情很简单但很有用把每一次信号灯状态切换的时间点写入数据库方便后续统计每个灯时长是否合理。我把核心逻辑抽出来给你看import sqlite3 from datetime import datetime def init_db(): conn sqlite3.connect(traffic.db) c conn.cursor() c.execute(CREATE TABLE IF NOT EXISTS light_log (id INTEGER PRIMARY KEY AUTOINCREMENT, color TEXT NOT NULL, start_time TEXT NOT NULL, end_time TEXT)) conn.commit() conn.close() def log_switch(color): conn sqlite3.connect(traffic.db) c conn.cursor() now datetime.now().strftime(%Y-%m-%d %H:%M:%S) # 先关闭上一条未关闭的记录 c.execute(UPDATE light_log SET end_time? WHERE end_time IS NULL, (now,)) c.execute(INSERT INTO light_log (color, start_time) VALUES (?, ?), (color, now)) conn.commit() conn.close()用SQLite而不是MySQL是聪明做法——这个项目不需要高并发不需要远程访问SQLite是一个单文件数据库零配置、免维护。log_switch函数的核心思路是每次检测到灯色变化就把上一条记录的end_time补齐再插入一条新记录。这样查数据库的时候每一条记录都对应一个完整的“绿灯持续时段”做统计非常直观。4.2 video.py视频流处理与Flask路由video.py这个文件有两种用途如果你接的是摄像头它负责打开摄像头并逐帧读取如果你想离线测试它可以读取一个录好的路口的视频文件。我推荐你先用视频文件测因为摄像头会受到光线、角度影响调试时很难复现问题视频文件可以反复播放同一画面改代码后能对比效果差异。Flask部分则是起一个轻量级的Web服务把当前灯色状态通过接口推送到前端from flask import Flask, render_template import sqlite3, json app Flask(__name__) app.route(/) def index(): conn sqlite3.connect(traffic.db) c conn.cursor() c.execute(SELECT color, start_time FROM light_log WHERE end_time IS NULL) current c.fetchone() conn.close() return render_template(index.html, current_colorcurrent[0], start_timecurrent[1]) if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)host0.0.0.0这个参数意味着局域网内其他设备也能通过你的电脑IP访问这个管理页面比如你用手机浏览器打开http://192.168.1.100:5000看路口状态。端口5000是Flask默认端口如果你同时跑着其他Flask服务会冲突那就改成5001或者别的。4.3 admin.html和index.html各自看什么两个HTML页面的分工比较明确。index.html是展示页显示当前路口信号灯是什么颜色、已经亮了多久给路口执勤或者监控室的人看。admin.html是管理页可以查历史记录、统计每个颜色的平均持续时长、甚至手动触发一次信号灯切换测试。前端页面本身用的是原生HTML加一点简单的JavaScript轮询不需要构建工具用浏览器直接打开就能用这对于不想折腾前端的人来说非常友好。5. 避坑与排查cv2安装、国内镜像、灯色误判这部分是我拿到这套源码之后实际跑出来的血泪经验。每个坑都是现象先出现、排查半天才发现原因的你提前看完能少走很多弯路。5.1 装了OpenCV还是报No module named cv2现象执行import cv2直接提示ModuleNotFoundError: No module named cv2但pip list里明明能看到opencv-python。原因这台机器上装了不止一个Python环境pip和python指向的环境不一致。最常见的是用系统自带的Python跑命令却用pip3装了包或者存在Anaconda和系统Python共存的情况。解决用where python和where pip看两个路径是否指向同一个解释器。如果指向不一致直接用绝对路径运行——C:\Python39\python.exe -m pip install opencv-python这样装到的包必定是当前解释器能import到的。装完再import验证一次通过后再跑main.py。5.2 OpenCV编译报错路径指向pip-req-build现象在Windows上执行pip install opencv-python时报错cv2.error: OpenCV(4.4.0) C:\Users\AppVeyor\AppData\Local\Temp\1\pip-req-build-...好像是OpenCV在编译某个模块时出了问题一串红色日志看着很吓人。原因这个报错不是你的代码问题是pip在安装OpenCV时尝试从源码构建某些依赖失败。通常是因为网络不稳定导致下载的安装包不完整或者本机Visual C运行库版本太低。解决改用国内镜像源并固定版本安装如我第2章给的命令。如果还是失败去[Visual C Redistributable]官方页面下载最新运行库装上重启终端再安装。另外检查你是否装了opencv-contrib-python这个包和opencv-python共存会导致冲突卸载其中一个。5.3 HSV阈值把红灯和黄灯认混现象红绿灯明明亮的是黄灯系统却识别成了红色在状态日志里频繁跳变。原因黄灯在HSV空间里的H值大约在15到35之间而红色区间我定义了0~10和170~180按道理不会重叠。但实际场景里LED黄灯因为色温偏差、摄像头白平衡的影响H值可能降到10以下就撞进了红色的第一个区间。解决两个方案一是缩小红色区间把red_upper_1从[10, 255, 255]改成[5, 255, 255]牺牲一点点红色识别鲁棒性来换取黄红分离更好的办法是增加一个黄灯区间[15, 255, 255]到[35, 255, 255]识别出黄色后单独做一次判定状态机里增加YELLOW分支。5.4 摄像头打不开、画面全黑现象cap.read()返回的ret一直是False程序直接break退出。或者打开之后画面是全黑的但摄像头指示灯是亮的。原因摄像头被其他程序占用比如微信、腾讯会议后台偷偷调用了摄像头或者OpenCV默认打开了索引0的设备但你的电脑多个摄像头外接摄像头占了其他索引。全黑的情况则大多是因为笔记本摄像头的隐私开关被关掉或者驱动异常。解决检查系统任务管理器里的摄像头占用情况关掉所有可能用摄像头的程序。然后把cv2.VideoCapture(0)的参数从0换成1试试外接摄像头通常占用更高的索引。做一个小脚本打印摄像头索引列表逐个尝试找到能读出画面的那个索引再填回去。另外注意某些Windows笔记本摄像头需要cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)设置分辨率后才会正常工作。6. 验证你的系统模拟视频、状态日志和参数调优最后给你一个我每次拿到视觉项目都会做一遍的验证流程把风险前置到上线之前。这套流程看起来简单但能帮你把“偶尔灵偶尔不灵”的识别系统变成“稳定可交付”的工程。先用项目自带的“模拟路口”视频做离线验证。把main.py里的cv2.VideoCapture(0)改成视频文件的路径这样视频会按固定帧率反复播放每次播放的效果完全一致方便你对比不同参数下的识别差异。验证时盯着两个地方一是红灯切绿灯的时刻系统要在一秒内响应二是整个视频播放过程中状态日志里不应出现“红-绿-红-绿”这种高频抖动。如果抖动了大概率是阈值范围太宽、把不该算目标颜色区域算进来了缩小HSV区间再看。验证完了把你的识别参数固化到配置文件里。别每次跑程序改main.py里的代码把HSV阈值、ROI裁剪比例、最小轮廓面积都挪到一个config.py或者.json文件里程序启动时读取。这样做的好处是同一个算法代码可以适配不同的路口A路口摄像头高、画面亮B路口摄像头低、有树荫遮挡你只需要为每个路口写一份对应的参数配置文件而不需要改一行逻辑代码。从那以后我每次拿到OpenCV项目都会强制走一遍这套流程先固定视频源做离线回归再写配置文件隔离参数最后才接真实摄像头。这套习惯帮我避开了无数次“线上效果和本地不一样”的玄学问题。识别阈值这种参数写死在代码里就是埋雷切到配置文件里就是可维护的资产。希望这个项目的源码和这份拆解能帮你把红绿灯识别这条路走顺以后再做颜色识别类项目你就有参考模板了。本文还有配套的精品资源点击获取