简介:智能停车场车牌识别计费系统是一份基于Python的实战项目源码,面向需要完成课程设计、毕业设计或希望提升自动化与图像处理能力的开发者,完整解决车辆入场识别、出场计费、流水记录等实际管理问题。压缩包共包含两千个文件,以近一千八百个源码文件为核心,另附多个可执行程序、动态库以及图像、字体、配置等辅助资源,整体体积约一百九十一兆,目录结构清晰,便于直接运行和二次修改。系统综合运用了图像预处理、车牌字符识别、时间戳计费、数据库存储、网络爬取收费标准及数据分析等技术,形成了从车辆进场到费用结算的完整业务闭环。资源已有189人学习,适合初学者借鉴整体架构,也适合进阶者研究自动化与数据处理的落地方式;同时附带的可执行文件降低了环境配置门槛,解压后即可体验完整功能。
1. 智能停车场车牌识别计费系统:这套源码能帮你把什么跑起来
车牌识别听着像大厂才玩得起的东西,但用 Python 的 OpenCV 加 Tesseract,一个普通开发者完全能在自己电脑上搭出能跑通的完整链路——从摄像头抓拍、车牌定位、字符识别,到入场出场时间差计算、按规则计费、账单入库,最后还能用 PyInstaller 打成 exe 扔给门卫用。这套项目的价值在于它是「一条龙」的:不是只给你一个人工智障识别脚本,而是把识别、计费、存储、报表串成了一个可以真实运转的小系统。适合三类人:正在找 Python 课程设计或毕设题目的学生、想了解工业级流程如何拆解的初级工程师、以及真的需要一套低成本停车场管理方案的个体户。你不需要有深厚的算法基础,但最好能读得懂 Python 代码,知道基本的函数调用。
2. 先看整体架构:识别、计费、数据、抓取四个模块怎么串成一条链路
2.1 模块划分与技术选型:为什么是 OpenCV 加 Tesseract 而不是端到端深度学习
拿到这份源码,第一步别急着运行,先把目录结构梳理清楚。我拆过的项目不少,凡是「下载即跑」的资源,一半以上的坑都出在没搞明白模块边界就乱改参数。这个项目的典型结构是四大模块:图像识别模块负责从摄像头帧或图片中找车牌、读字符;计费模块维护车辆入场出场记录并计算费用;数据存储模块通过 SQLite 把车辆信息和收费记录落盘;辅助模块里跑着爬虫,定时抓取收费标准或天气信息。
选型上,车牌识别环节用的是 OpenCV 做图像预处理,配合 Tesseract OCR 读字符,这是老牌且文档最全的技术组合。为什么不直接上 YOLO 或 SSD 训练一个端到端检测模型?因为对学习型项目来说,深度学习方案的「黑匣子」属性太强——你调了半天 loss,却说不清误识别是因为光线还是因为标注质量。而 OpenCV 的方案每一步都可解释:灰度化丢颜色信息、高斯滤波降噪、Sobel 边缘检测找轮廓、HSV 颜色空间筛选车牌底色,每一行代码改了什么、效果如何,肉眼直接可见。这和计费系统的数据处理思路是一致的——先保证可解释,再追求准确率。
2.2 数据链路:一张车牌照片到一条计费记录的完整旅程
理解这个系统的关键不是看单个函数,而是看数据怎么流动。一次完整的停车周期是这样的:车辆驶入时,摄像头抓拍一张含车牌的图像,送进识别模块。图像先被缩放和灰度化,再经过滤波和二值化,算法尝试定位车牌区域——通常用颜色特征(蓝色车牌对应 HSV 中特定色相区间)或边缘密度特征。定位成功后,把车牌区域裁切出来,做字符分割,再逐个字符丢给 OCR 识别引擎,最终拼出一串车牌号字符串。
这串车牌号马上被写入数据库中,同时记录当前时间戳作为入场时间。车辆出场时,同样的识别流程再跑一遍,拿到车牌号后去数据库查这条车辆的入场记录,用 datetime 算出停车时长,套用计费规则得到费用,更新记录状态,把账单信息打印或展示给用户。整个过程看似简单,但每个环节都有坑:识别错了车牌,后面全错了;时区没处理对,计费就多算一小时;数据库没加索引,车辆多了以后查询越来越慢。我把这些坑都放在第 5 章细说,先把每个模块的实现原理讲透。
3. 车牌识别模块:图像预处理与字符识别的关键参数
3.1 图像预处理:灰度化、二值化和滤波的调参思路
车牌识别的第一个环节是让图像「变干净」。摄像头拍到的原始帧通常是 1920×1080 的彩色图,直接做字符识别会非常慢且准确率低。常见做法是先把图像缩放到统一尺寸——我一般缩放到宽度 800 像素左右,这个宽度下车牌字符还能保持清晰,处理速度也能接受。然后用 OpenCV 的cvtColor转成灰度图,再用高斯滤波去掉噪点——这里的核心参数是高斯核的大小,太小时噪点压制不住,太大时字符边缘也被抹平了。
import cv2 def preprocess_image(image_path): # 读取原始图像,保持色彩信息,后续 HSV 筛选用 img = cv2.imread(image_path) # 缩放:统一宽度,保持宽高比,减少计算量 scale_ratio = 800 / img.shape[1] img = cv2.resize(img, (800, int(img.shape[0] * scale_ratio))) # 转灰度:识别字符不需要颜色,但后续颜色筛选必须用原图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯滤波:核大小 (5,5),标准差 0 表示由核大小自动计算 blurred = cv2.GaussianBlur(gray, (5, 5), 0) return img, blurred这段代码里最值得注意的参数是高斯核(5, 5)。实测中,如果摄像头分辨率较低或夜间噪点多,我会把它调到(7, 7)但代价是字符边缘变糊;如果图像本身非常清晰,用(3, 3)更好,能保留更多细节。灰度化和滤波是后续一切操作的地基,地基没打好,后面怎么调阈值都是玄学。
滤波之后是二值化。常见做法是用自适应阈值或大津法(Otsu),前者能应对光照不均的场景,后者在均匀光照下更快。
# 大津法二值化:自动计算最佳阈值,适合光照均匀的场景 _, binary = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 自适应阈值:blockSize 必须为奇数,C 是常数修正项 adaptive_binary = cv2.adaptiveThreshold( blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2 )在停车场的真实场景里,白天太阳斜射会造成半边亮半边暗,我常用自适应阈值——blockSize=11表示每个像素参考周围 11×11 区域的加权平均值,C=2是减去的一个常数,抑制噪声。这两个参数需要反复试,没有「万能值」,关键是要理解它们各自控制什么。
3.2 车牌定位:颜色空间与边缘检测的配合定位法
二值化只是预处理,真正的难点是「怎么从整张图里找到车牌在哪」。主流方案有两种:一是基于边缘密度——车牌区域字符密集、边缘响应强;二是基于颜色特征——国内蓝牌、绿牌的底色在 HSV 空间有非常固定的范围。这个项目用的是颜色加边缘的混合策略:先用 HSV 筛出蓝色区域,再在候选区域里做边缘检测确认。
import numpy as np def locate_plate_by_color(img): # 转 HSV:H 色相、S 饱和度、V 明度,分开处理 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 蓝色车牌的色相范围:H 在 100~124 之间,S 和 V 放宽 lower_blue = np.array([100, 80, 80]) upper_blue = np.array([124, 255, 255]) mask = cv2.inRange(hsv, lower_blue, upper_blue) # 形态学闭运算:填平车牌字符间的空隙,让区域连成整体 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 5)) closed = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 找轮廓,按面积筛选候选区域 contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) candidates = [] for c in contours: x, y, w, h = cv2.boundingRect(c) if w > 100 and h > 25 and w / h > 2.5: # 车牌宽高比约 3:1 到 4:1 candidates.append((x, y, w, h)) return candidates这里的避坑关键在 HSV 的取值范围。网上很多教程给蓝色车牌的下限直接写[100, 43, 46],那是标准 HSV 定义里的通用值,但实际摄像头拍出来的颜色会被白平衡和光照影响。晴天、阴天、夜间补光灯下的蓝色完全不同。我一般先抓几张现场图,用cv2.cvtColor转成 HSV 后取像素值实测,再来定上下限——这个习惯帮我避开了无数次「白天好用晚上失灵」的翻车。
宽高比筛选w / h > 2.5也是一个关键参数。标准车牌的长宽比接近 3:1,但摄像头角度稍微倾斜时,这个比值会变小。如果你发现车牌定位经常漏检,把 2.5 放宽到 2.0 试试;反过来,如果误检太多(把广告牌、车标都当成车牌),就收紧到 3.0。
3.3 OCR 识别与后处理:置信度过滤与字符纠错
定位到车牌区域后,裁出这块图像,放大再送进 OCR 引擎。Tesseract 是主流选择,支持中文车牌字符,但需要额外下载语言包。这里有个细节:Tesseract 默认配置对「多行文本」的识别假设会让它把车牌字符拆得乱七八糟,所以必须显式指定单行模式。
import pytesseract from PIL import Image def ocr_plate(plate_img): # 放大 2 倍,让小字符在 OCR 眼里更清晰 scale = cv2.resize(plate_img, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC) # 转 PIL 格式,Tesseract 通过 pytesseract 直接吃 PIL pil_img = Image.fromarray(cv2.cvtColor(scale, cv2.COLOR_BGR2RGB)) # 单行模式 psm=7,只识别一行字符;config 里指定语言包 text = pytesseract.image_to_string( pil_img, config='--psm 7 -c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ) return text.strip()--psm 7这个参数是血泪经验的结晶——默认的 PSM 3(全自动分页)会把车牌当成一份文档来处理,识别结果里经常混入换行符和空白字符。tessedit_char_whitelist限制了只识别大写字母和数字,这个白名单能显著降低误识别率,因为国内车牌里本来就不包含小写字母和特殊符号。
后处理阶段一个容易忽略的坑是字符替换。OCR 引擎经常把数字0认成字母O,把1认成I。由于白名单里这些字符都存在,OCR 不会自己纠正,所以必须在代码里做一次映射替换,例如text.replace('O', '0')之类的规则。更严谨的做法是比对数据库里已有的车牌格式(第几位是省份简称、第几位是字母),不符合格式的识别结果直接丢弃重新抓拍。
4. 计费系统与数据持久化:入场出场时间差与费用计算
4.1 计费规则设计:用策略模式应对阶梯价和免费时段
车牌的识别结果最终要服务于收费。计费系统的核心是一个数据结构和一套规则:每辆车有入场时间和出场时间,费用等于规则函数的输出。不同的停车场有完全不同的收费策略——有的按小时计费,有的按次计费,还有分时段计费(白天 5 元/小时、夜间 10 元封顶)。如果把规则写死在业务代码里,每改一次收费策略就要重新打包,这在真实场景中不可接受。
我拆这个项目时的第一个建议是:把计费规则抽象成一个独立的函数,输入是入场时间、出场时间和一个规则字典,输出是费用。这样规则调整时只需要改配置,不动主逻辑。
from datetime import datetime def calc_fee(entry_time: datetime, exit_time: datetime, rule: dict) -> float: """按规则字典计算停车费用。 rule = { 'unit_price': 3.0, # 每小时的单价 'free_minutes': 15, # 免费时长 'daily_cap': 30.0, # 单日封顶 'night_discount': 0.5 # 夜间折扣 } """ duration_minutes = (exit_time - entry_time).total_seconds() / 60 if duration_minutes <= rule['free_minutes']: return 0.0 # 向上取整到小时,不足一小时按一小时算 hours = math.ceil(duration_minutes / 60) fee = hours * rule['unit_price'] # 如果跨夜,按夜间折扣重新计算(简化逻辑) if entry_time.hour >= 22 or entry_time.hour <= 6: fee *= rule['night_discount'] return min(fee, rule['daily_cap'] * (duration_minutes // 1440 + 1))这个实现里最容易被忽略的边界是free_minutes——很多停车场的免费时长是 15 分钟,但系统只记录到秒,如果入场出场时间差是 14 分 59 秒,duration_minutes <= rule['free_minutes']判断为 True,正确免费。但如果你把免费时长的判定写成了反方向,就会闹出「停了 16 分钟收了 3 块钱」的笑话。math.ceil向上取整同样要注意,停 61 分钟和停 120 分钟都是 2 小时费,这是符合常理的,但如果产品要求按分钟精确计费,这里就要改用round加位数控制。
4.2 SQLite 存储:车辆信息表与计费记录表的设计
计费数据必须落盘,否则程序退出就全丢了。SQLite 是这类小型系统的最优解——不需要单独安装数据库服务,数据存在单个文件里,Python 标准库sqlite3直接操作,备份就是复制文件。表结构的设计会直接影响后续的查询效率。
CREATE TABLE IF NOT EXISTS vehicles ( plate_number TEXT PRIMARY KEY, -- 车牌号,唯一索引,车辆查重靠它 entry_time TEXT NOT NULL, -- ISO 格式时间戳,方便排序 exit_time TEXT, -- 可能为空,表示车辆还在场内 status INTEGER DEFAULT 1 -- 1=在场, 0=已出场 ); CREATE INDEX IF NOT EXISTS idx_entry_time ON vehicles (entry_time);这张表的设计有两个关键点。第一,plate_number设为主键意味着同一辆车不能有两条同时在场的记录,这正好符合停车场语义——一辆车不可能同时从两个口进来。如果出现重复入场,说明识别模块第二次把同一个车牌当成了新车辆,此时应该走「更新入场时间」而不是「插入新记录」。第二,entry_time上建索引,是因为最常见的查询是「这个月停了哪些车」「高峰期是几点」,没有索引的话数据量超过十万条后查询会明显变慢。
写入和读取的代码不用复杂,但要保证时间戳格式统一。我用的是datetime.now().isoformat(),存进 SQLite 后按字符串排序也能正确比较,因为 ISO 格式里年月日时分秒的顺序就是字典序。
import sqlite3 from datetime import datetime def record_entry(db_path, plate_number): conn = sqlite3.connect(db_path) c = conn.cursor() now = datetime.now().isoformat() # INSERT OR REPLACE:如果车辆已在场内,更新入场时间 c.execute( "INSERT OR REPLACE INTO vehicles (plate_number, entry_time, status) VALUES (?, ?, 1)", (plate_number, now) ) conn.commit() conn.close()INSERT OR REPLACE这个 SQL 的语义需要想清楚:它会把同主键的整条记录删除后重新插入。这意味着如果车辆已经在场内,再次识别时会直接刷新入场时间,而不是新增记录。这在进出口各有一个摄像头的场景下是合理的,但如果是同一摄像头反复拍到同一辆车,就会导致入场时间不断刷新,后续计费严重偏少。所以实际项目中我会加一层判断:如果查询到该车已处于入场状态且距离上次入场不超过 2 分钟,就忽略这次识别。
4.3 网络爬虫动态获取收费标准:为什么用轮询而不是实时抓取
计费规则不一定写死在代码里,有些运营方希望系统自动抓取最新的收费标准公告。这个场景就是爬虫发挥作用的地方。一个典型做法是定时从物业官网或政府公示页面抓取收费政策文本,解析出关键数字,比如「首小时 5 元,之后每小时 3 元,24 小时封顶 30 元」,然后更新到本地配置表里。
import requests from bs4 import BeautifulSoup import re def fetch_fee_rule(url): headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'} resp = requests.get(url, headers=headers, timeout=10) resp.encoding = resp.apparent_encoding # 处理中文编码 soup = BeautifulSoup(resp.text, 'html.parser') text = soup.get_text() # 正则匹配收费标准关键字,简单粗暴但可靠 patterns = { 'unit_price': r'每小时[((]?(\d+(?:\.\d+)?)元', 'daily_cap': r'(\d+(?:\.\d+)?)元.{0,4}封顶' } rule = {} for key, pattern in patterns.items(): match = re.search(pattern, text) if match: rule[key] = float(match.group(1)) return rule这里有两个常见的坑。第一个是网页编码——很多政务网站是 GBK 编码,不用apparent_encoding的话抓出来全是乱码,正则匹配直接失败。第二个是抓取频率——我见过有人写成每 5 秒请求一次,结果 IP 被对方封禁。正确做法是每天凌晨跑一次,或者手动触发更新。计费标准不是股票行情,一天一变已经算频繁了,不需要实时拉取。更稳妥的方案是把这个爬虫做成独立脚本,和主系统解耦,抓取结果写入一张fee_config表,主系统每次刷新配置时读取这张表,这样爬虫挂了也不影响主业务跑。
5. 实战避坑:车牌识别计费系统最常见的六个翻车现场
5.1 蓝色车牌阈值不通用:白天识别正常、晚上集体失灵
现象:同一个 HSV 筛选参数,白天能正确框出车牌,到了晚上补光灯一开,蓝牌在画面上变成深蓝色甚至偏黑,inRange筛选出的区域要么面积不够、要么根本筛不到,识别率断崖式下跌。
原因:HSV 里的 V(明度)和 S(饱和度)受光照强度影响极大,补光灯直射会让色相 H 基本不变但饱和度显著下降。更麻烦的是,不同摄像头的色彩调校差异很大,同一块车牌用海康和大华拍出来的 HSV 值能差出 20 个色阶。
解决:不要只依赖一组 HSV 阈值。我的做法是设置两套参数——白天用高饱和度范围,夜间用低饱和度范围,根据整张图的平均亮度自动切换。亮度阈值建议设在 120 左右(0-255 灰度),低于 120 切夜间参数。如果摄像头支持,优先开启它的宽动态(WDR)模式,能大幅压缩明暗差距。
5.2 新能源绿牌识别率低:字符是白的但底色是绿的
现象:新能源车牌(绿牌)的识别准确率明显低于蓝牌,经常漏检或者把后面几个字符识别错。
原因:绿牌是渐变底色,HSV 空间里绿色区域跨度大,如果按蓝牌的思路固定一个色相范围,会漏掉渐变边缘的部分区域。另一个问题是 OCR 阶段,绿牌字符是白色,对比度不如蓝牌的黑字白底,二值化后容易出现字符断裂。
解决:颜色筛选中单独为绿牌增加一组 HSV 范围,色相 H 放在 35 到 90 之间,并适当降低 S 和 V 的下限以覆盖渐变区。二值化阶段不要直接对灰度图做全局阈值,改用自适应阈值,或者先提取车牌区域的彩色图,用通道相减的方法增强白色字符和绿色背景的对比度。
5.3 PyInstaller 打包后找不到模型文件和数据文件
现象:源码在 IDE 里运行一切正常,用 PyInstaller 打包成 exe 后,双击运行报错,提示找不到 tesseract 的数据目录或者 OpenCV 的配置文件。
原因:PyInstaller 默认只打包 Python 代码和依赖库,不会自动把项目里的图片、模型、语言包等资源文件带进来。程序在 IDE 里运行时使用的是绝对路径或相对源码目录的路径,打包后当前工作目录变成了 exe 所在目录,相对路径全部失效。
解决:在打包命令中用--add-data显式把资源目录加进去,同时在代码里用sys._MEIPASS判断运行时是脚本模式还是打包模式,动态拼接资源路径。
pyinstaller -F -w main.py \ --add-data "tessdata;tessdata" \ --add-data "config.json;." \ --name ParkingSystemimport sys, os def resource_path(relative_path): # PyInstaller 打包后资源在 _MEIPASS 临时目录中 base = getattr(sys, '_MEIPASS', os.path.abspath('.')) return os.path.join(base, relative_path)判断_MEIPASS是否存在,这是打包类项目挂掉的头号原因。-F打成单文件,-w去掉控制台窗口,--add-data的格式在 Windows 下是分号分隔源和目标,在 Linux 和 macOS 下是冒号。如果不加resource_path处理,即使 add-data 加了也会因为路径拼接不对而失败。
5.4 计费时间算错:跨天停车被收了两天钱
现象:晚上 11 点入场、第二天凌晨 2 点出场,系统按两天收费,日封顶逻辑失效,客户投诉。
原因:daily_cap的实现里用duration_minutes // 1440 + 1计算封顶倍数,但我见过不少版本直接写min(fee, daily_cap),这样一旦时长超过 24 小时,总费用被截断到封顶值——相当于停 25 小时和停 3 天收费一样。更隐蔽的是时区问题,如果服务器时区没设置好,datetime.now()返回的是 UTC 时间,凌晨 2 点会被算成前一天晚上,跨天判断直接错位。
解决:封顶必须按「自然日」而不是「累计时长」计算。先判断入场和出场是否跨天,再分别计算每天的费用。时区问题在代码里显式设置timezone,或者用time.localtime()取本地时间,不要依赖系统默认时区。SQLite 存时间戳建议统一用带时区的 ISO 格式。
5.5 Tesseract 中文识别乱码:缺 chi_sim 语言包
现象:代码跑通了,但识别结果不是乱码就是一片空白,偶尔能出来几个英文字母,中文省份简称全丢。
原因:Tesseract 默认只装了英文语言包eng,识别中文字符时没有对应模型,直接跳过。很多人以为白名单里写了大写字母和数字就够用,但实际上车牌里的「京」「沪」「鲁」这些汉字必须有chi_sim中文简体语言包才能识别。
解决:下载chi_sim.traineddata放到 Tesseract 的tessdata目录下,然后修改调用参数:lang='chi_sim+eng'。另外注意--psm 7必须是单行模式,多行模式对车牌这种紧凑文本的识别率极低。语言包版本要匹配 Tesseract 主版本,4.x 和 5.x 的 traineddata 不通用。
5.6 摄像头图像模糊导致识别率波动:预处理救了不了低质量输入
现象:识别率在 80% 到 95% 之间剧烈波动,检查代码没有问题,但发现摄像头本身对焦不准或镜头起雾。
原因:图像识别是「垃圾进、垃圾出」,上面所有预处理手段能做的只是增强对比度和去噪点,无法修复根本性的失焦模糊。有时误识别不是算法问题,而是物理层面的采集问题。代码再优化,也救不回一张分辨率 320×240 且严重模糊的图。
解决:先检查摄像头参数——分辨率至少 720p,曝光模式设为室外自动,关闭自动白平衡以免颜色漂移。在代码里加入图像质量评估:计算拉普拉斯算子的方差,低于阈值直接判为模糊帧并重新抓拍。这是性价比最高的改进,准入门槛比换深度学习模型低得多。
6. 进阶玩法:用 Pandas 分析停车数据,反向优化计费策略和车位管理
大部分停车场的管理者只把系统当成一个「收钱的工具」,但落库的数据才是最值钱的资产。SQLite 里存了每辆车的入场时间、出场时间、停车时长、实收费用,这些数据用 Pandas 读出来做透视分析,能直接回答几个运营问题:高峰期集中在几点、平均停车时长是多长、月卡用户和临停用户的比例是多少。
import pandas as pd import sqlite3 import matplotlib.pyplot as plt conn = sqlite3.connect('parking.db') df = pd.read_sql_query("SELECT * FROM vehicles WHERE status=0", conn) # 停车时长按小时分组,看分布集中在哪个区间 df['entry_time'] = pd.to_datetime(df['entry_time']) df['exit_time'] = pd.to_datetime(df['exit_time']) df['duration_hours'] = (df['exit_time'] - df['entry_time']).dt.total_seconds() / 3600 # 按入场小时统计车流量 df['entry_hour'] = df['entry_time'].dt.hour hourly_counts = df.groupby('entry_hour').size() hourly_counts.plot(kind='bar', figsize=(10, 4)) plt.title('Entry Traffic by Hour') plt.savefig('hourly_traffic.png') # 停车时长分布直方图 df['duration_hours'].hist(bins=24, range=(0, 24)) plt.title('Parking Duration Distribution') plt.savefig('duration_dist.png')这段分析的价值在于:运营者发现高峰集中在 8 点到 10 点和 17 点到 19 点后,可以针对性调整个时段的停车费;发现 60% 的车辆停车时长低于 1 小时后,说明这是一个办事型停车场,按次收费比按小时收费更能提升周转率。这些结论直接反哺到第 4 章的rule配置里,形成「采集数据 → 分析 → 优化策略 → 再采集」的闭环。
最后说一个我拆这类项目时的现成教训:这个系统上手后我第一时间干了件事——把摄像头挪到不同位置、不同光照下各拍 50 张图,形成自己的测试集,每次改完参数先拿这批图跑一遍回归测试,再放上门真机验证。从那以后我再也没有出现过「在家调好的参数,到了现场一测全完」的窘况。这套测试习惯比源码本身更值钱。希望这份源码的拆解能帮你少走几个弯路,把 Python 的技能树真正点到自己手上去。
本文还有配套的精品资源,点击获取