目录[-]
目录
- 情景:为什么要做这个?
- 任务:测试人员需要做什么?
- 行动:如何设计测试方案?
- 报告:测试报告分析
————————————————
一、情景:为什么要做这个?
公司设计了一个业务,类似于大厦内部停车场,需判断车牌号是否满足抬杆要求,整体规则如下:
- 内部车辆:无条件开放
- 预约车辆:指定时间段开放
- 非内部非预约车辆:不开放
公司采购了几套识别设备, 但是部署之后,用户投诉其识别不准确,导致车辆无法进入,更棘手的是:
- 每次尝试新的物联网设备,都要重新和业务对接
- 测试人员,只会用一两个车牌实物进行功能验证,无法覆盖更多场景
- 公司希望挑选一款性价比最高的设备,既稳定又可靠
————————————————
二、任务:测试人员需要做什么?
梳理业务流程
- 公司APP,上传内部车辆的车牌号如:辽A 1234Y
- 使用物联网硬件摄像头,识别车牌号
- 抬杆
但是上述的流程有个致命的问题,只测了一个车牌号,并且还是个蓝牌的车牌号。
测试的第一步,分析车牌号组成和颜色:
- 蓝底白字(蓝牌车):最常见的民用车
- 格式:辽A · 1234Y(5位字符,数字+字母组合)
- 黄底黑字(黄牌):大型/特殊车辆
- 格式:辽A · 12345(5位)、教练车—辽A·1234(学)
- 绿底黑字(绿牌):新能源专用
- 格式:辽 A・D12345(6 位,比蓝牌多 1 位),D=纯电动,F=非纯电(插混/增程)
- 白底黑字/红字(白牌):政法、军队等
- 警车:辽A · 1234警(最后一位警为红色)
- 军车:军A · 12345(白底黑字,军队专用)
- 武警:WJ · 辽 12345(白底黑字,WJ为红色)
- 应急救援:辽 · X1234 应急(消防/森林消防)
- 黑底白字(黑牌):涉外、港澳车辆
- 领事馆:辽A · 1234 使(红字),驻华使馆、领事馆等
- 港澳入境:粤Z · 123 港,港澳入内地车辆
- 其他专用/临时号牌
- 临时车牌(纸质)
- 挂车号牌:黄底黑字,如 辽A · 挂 1234
大厦内部停车场,最常见的,也就是1、2 两种类型,我们以这两个举例。
测试第二步,既然已经分析出我们需要什么数据,那我们如何设计更多的测试数据?
解决方案:
- CCPD:github上有项目,可以查看多个真实的车牌号
- 手动拍照:测试人员在各式各样的停车场或是路边拍照
- 数据模拟:通过代码模拟生成车牌号
本文三种方法都采用了,从代码角度说明一下方法3:
# 1. 画"辽A"
draw.text((x, y), first_part, fill=config["text"], font=font)
# 2. 画点(在"辽A"后面)
dot_x = x + w1 + dot_offset_x
dot_y = y + 0
draw.text((dot_x, dot_y), "·", fill=config["text"], font=font)
# 3. 画"12345"
x2 = x + w1 + dot_space
draw.text((x2, y), second_part, fill=config["text"], font=font)
生成效果如下
测试第三步,车牌号做好了,这次作为DEMO,手动收集一下车牌号,并作为车牌号图片的图片名称,再用下面代码读取,就可以得到如:京A12345
# blue_京A12345.jpg -> 期望文字 = "京A12345"
expected = filename.split('_', 1)[1].replace('.jpg', '').replace('.png', '')
# 生成关键词(每个字符作为关键词)
keywords = list(expected)
————————————————
三、行动:如何设计测试方案?
def run_test(self, image_dir: str, answer_file: str = None, generate_html=True):
"""运行车牌测试"""
print("🚀 开始车牌识别测试")
print("=" * 60)
start_time = time.time()
# 获取测试用例
test_cases = get_plate_test_cases(image_dir, answer_file)
print(f"📸 加载了 {len(test_cases)} 张测试图片")
results = []
for case_id, image_path, reference, keywords in test_cases:
try:
# 1.调用API识别
translation = self.translator.translate(image_path)
# 2.创建评估器实例
evaluator = SimpleEvaluator()
# 3.处理识别结果(去掉点再比对)
clean_translation = translation.replace('·', '')
# 4.评估
eval_result = evaluator.evaluate_case(
clean_translation, # 去掉点的结果
reference, # 正确答案
keywords, # 关键词
source=image_path # 图片路径
)
# 5.记录原始结果(带点)
result = {
'case_id': case_id,
'source': str(image_path),
'reference': reference, # 添加这一行,方便后面使用
'translation': translation, # 原始结果(带点)
'clean_translation': clean_translation, # 去点后的结果
'keywords': keywords,
'scores': eval_result['scores'],
'final_score': eval_result['final_score'],
'passed': eval_result['passed'],
'llm_explanation': eval_result.get('llm_explanation', '无说明')
}
results.append(result)
# 6.打印结果
status = "✅" if result['passed'] else "❌"
print(f"{status} 用例{case_id}: {Path(image_path).name}")
print(f" 原始车牌: {reference}")
print(f" 识别结果: {translation} (去点后: {clean_translation})")
print(f" 关键词命中: {eval_result['scores']['keyword_pos']:.2f}")
print(f" BLEU分数: {eval_result['scores']['bleu']:.2f}")
print(f" 综合得分: {result['final_score']:.2f}")
print()
except Exception as e:
print(f"❌ 用例{case_id} 处理异常:{e}")
continue
# 如果没有成功处理的用例,直接返回
if not results:
print("❌ 没有成功处理的用例")
return []
# 计算汇总
summary = calculate_summary(results)
# 7.打印汇总报告
print("=" * 60)
print(f"📊 测试汇总:{summary['passed']}/{summary['total']} 通过")
print(f" 通过率: {summary['pass_rate'] * 100:.1f}%")
print(f" 综合得分: {summary['avg_final']:.2f}")
print(f" 平均关键词得分: {summary['avg_scores'].get('keyword', 0):.2f}")
print(f" 平均BLEU得分: {summary['avg_scores'].get('bleu', 0):.2f}")
print("=" * 60)
# 8.生成HTML报告
if generate_html:
model_name = self.translator.get_name()
html_path = HTMLReportGenerator.generate_html_report(results, summary, model_name)
print(f"📁 HTML报告已保存: {html_path}")
# 9.保存详细结果到Excel
self._save_to_excel(results, summary)
elapsed = time.time() - start_time
print(f"⏱️ 测试耗时: {elapsed:.2f} 秒")
return results
————————————————
四、测试报告分析
在分析之前,先把测试报告贴出来
1.整体情况
| 类型 | 数量 | 占比 |
| 成功识别 | 7 | 36.8% |
| 失败识别 | 12 | 63.2% |
| 总数 | 19 | 100% |
2.成功案例分析
成功识别的7个车牌号,都是使用Python代码模拟生成的车牌,它们都是标准字体、无干扰,100%成功率。但是最大的问题在于,这7个是假数据。
3.失败案例分析
类型1:省份识别错误
| 原始车牌 | 识别结果 | 问题 |
| 皖A37X93 | 峯A·37X93 | 皖 → 峯(完全错误) |
| 苏AA0G87 | 5A-A0687 | 苏 → 5A(完全错误) |
| 辽A22NN4 | 一盏-六众 | 辽A → 一盏(完全乱码) |
| 鄂A07012 | MA07012 | 鄂 → MA(字母替代汉字) |
| 辽AD75582 | 1A·D75592 | 辽 → 1A(数字替代汉字) |
结论:省份识别是最大问题,以上5例,全部都是省份识别错误。
类型2:乱码
| 原始车牌 | 识别车牌 |
| 皖A3E388 | 225204 |
| 皖AAV951 | 版1 5 百 3 百 3 百 3 |
| 皖ABG382 | 中国工商银行 |
| 辽AF46066 | 255155 R20 H 长安深蓝 LAF46066 |
这些属于完全识别失败,模型把车牌号当成了文字识别,最大的问题就在于此。
类型3:部分正确但省份错误
| 原始车牌 | 识别结果 | 正确部分 |
| 苏K751F8 | 第K-751F8 | K-751F8正确 |
| 辽A9MQ03 | LIA·9MQ03 | 9MQ03正确 |
| 苏AA0G87 | 5A-A0687 | A0687部分正确 |
这些结果,如果只看后面字符,识别的不错,但是前面两位只要错了,那就是0分,后面识别再准确也没用。
后续优化推荐
- 放弃假数据:删除 Python 生成的假图,只使用真图
- 省份样本:收集更多省份样本,连最简单的“辽”、“苏”字都能识别错误,更何况复杂一些的“冀”、“赣”,每个省份收集50张照片
- 倾斜角度:寻找倾斜30°、45°等照片
- 暗光环境:夜间、地库场景图片
- 不同颜色:增加绿牌、黄牌等真实图片(尤其是绿牌,有6个字符)
4.给公司带来的价值
这套车辆识别测试框架,帮助公司避免了一次采购失误。它的优点有:
- 有明确的标准,验证采购商的识别效果
- 用同一套数据,跑通所有设备
- 哪家设备对绿牌支持好,哪家设备夜间识别高,一目了然
- 问题定位更加精准
- 测试报告直接指出:省份识别率低
- 测试资产沉淀
- 后续的测试可慢慢优化,但这套体系成功的保留下来
- 提升测试效率
- 提升回归测试效率,并且可以很好的验证其稳定性
to be continued。。。