19、Camera测试与自动化:从CTS到生产流水线

做Camera驱动开发这么多年,我越来越觉得:测试不是终点,而是起点。你想想看,一个Camera HAL写出来,跑一遍CTS就完事了?那太天真了。真正的坑,往往藏在压力测试和图像质量评估里。

今天这一章,我带你完整走一遍Camera测试的体系。从Google官方的CTS/CTS Verifier,到自动化框架CameraITS,再到图像质量分析工具Imatest/IE,最后咱们亲手搭一条自动化测试流水线。

19.1 CTS与CTS Verifier:Camera测试的“入场券”

CTS(Compatibility Test Suite)是Google给Android设备发的“通行证”。你的Camera HAL能不能过CTS,直接决定了设备能不能预装GMS。说白了,这是底线。

CTS里跟Camera相关的测试项,主要分三块:

  • Camera CTS:纯自动化测试,覆盖基本功能、参数、格式转换等。跑一遍大概2-3小时。
  • CTS Verifier:半自动化测试,需要人工参与。比如测试闪光灯同步、对焦准确性、多摄像头切换等。
  • Camera ITS:图像测试套件,后面单独讲。

关键测试项速查表

测试模块测试内容常见失败原因
CameraManagerTest摄像头枚举、打开/关闭HAL返回的摄像头ID不匹配
CaptureRequestTest各种参数设置(AE/AWB/AF)3A算法未收敛就返回结果
StreamConfigurationMapTest输出格式、分辨率支持漏掉了某些必须的格式(如YUV420)
ReprocessTestYUV→JPEG重处理重处理管线实现有bug

我记得有一次,一个项目CTS跑了三天都没过。最后发现是HAL里把摄像头朝向(facing)写反了。前置摄像头写成了BACK,后置写成了FRONT。这种低级错误,在CTS面前一抓一个准。

我的习惯:每次提交HAL代码前,先跑一遍Camera CTS的“冒烟测试”子集。大概20分钟就能知道有没有致命问题。别等到集成测试才发现,那太晚了。

19.2 CameraITS:自动化图像测试框架

CameraITS(Image Test Suite)是Google专门用来评估图像质量的自动化框架。它跟CTS不同,CTS测的是“能不能用”,ITS测的是“好不好用”。

ITS的测试场景包括:

  • 色彩还原准确性
  • 曝光一致性
  • 噪声水平
  • 动态范围
  • 几何畸变

它的工作流程是这样的:

# ITS测试流程伪代码
1. 控制摄像头拍摄特定场景(色卡、灰卡、分辨率卡)
2. 从HAL获取RAW或YUV图像
3. 用Python脚本分析图像指标
4. 与参考值对比,给出pass/fail

ITS的测试脚本是用Python写的,放在tools/tradefederation/contrib/its/目录下。你可以直接跑:

# 运行单个ITS测试项
python3 its_main.py --test test_sensor_noise.py --camera 0

# 运行全部ITS测试
python3 its_main.py --all --camera 0

我曾经踩过的坑:ITS测试对光照环境要求极高。有一次我在普通办公室灯光下跑色彩还原测试,结果全红。后来才发现,ITS要求用D65标准光源,照度在500-1000 lux之间。换了灯箱之后,一次就过了。

19.3 压力测试与稳定性测试

压力测试,说白了就是“往死里用”。我见过太多HAL在单次调用时表现完美,但连续开关1000次摄像头后就崩了。

常用的压力测试手段:

  • Camera Stress Test:连续打开/关闭摄像头,每次随机切换参数
  • Memory Leak Test:长时间录像,监控内存增长
  • Concurrent Test:多路摄像头同时工作
  • Thermal Test:高温环境下长时间运行

我自己写过一个简单的压力测试脚本,核心逻辑就几行:

# 压力测试核心循环
for i in range(10000):
    camera = Camera.open(camera_id)
    session = camera.create_capture_session()
    session.capture(request)
    session.close()
    camera.close()
    # 检查是否有ANR或crash
    check_logcat_for_errors()

你想想看,如果这个循环能跑完10000次不报错,那稳定性基本就稳了。但现实往往是:跑到第500次左右,内存就涨上去了。

避坑指南:我曾经遇到一个案例,压力测试跑到第3000次时,Camera HAL返回了-EBUSY。查了三天才发现,是底层驱动在关闭摄像头时没有完全释放DMA buffer。这种问题,单次测试根本发现不了。

19.4 图像质量测试:Imatest与IE

图像质量测试,是Camera开发的“最后一公里”。你用Imatest或者Image Engineering(IE)的软件,对着标准测试卡拍一张,软件就能自动算出MTF、SNR、色彩误差等指标。

常用的测试卡和对应指标:

测试卡测试指标典型要求
ISO 12233分辨率卡MTF50、MTF30中心MTF50 > 0.5
X-Rite ColorCheckerΔE、饱和度ΔE < 5
灰阶卡(20阶)动态范围、Gamma动态范围 > 60dB
均匀光源Shading、Lens Roll-off亮度均匀性 > 80%

Imatest的操作流程大致是:

  1. 拍摄测试卡图像(建议用RAW格式)
  2. 导入Imatest软件
  3. 选择对应的测试模块(SFR、Colorcheck、Uniformity等)
  4. 软件自动分析,生成报告

我的经验:图像质量测试最怕“人肉对比”。我见过有人用肉眼判断两张照片哪个更清晰,结果两个人给出完全不同的结论。用Imatest量化指标,才能让测试结果可复现、可追溯。

19.5 实战:搭建Camera自动化测试流水线

好了,前面讲了这么多测试工具,现在咱们把它们串起来,搭一条完整的自动化测试流水线。

流水线的核心流程:

  1. 代码提交触发:Git hook或CI系统检测到HAL代码变更
  2. 编译部署:编译HAL模块,刷入测试设备
  3. CTS冒烟测试:跑Camera CTS子集(约20分钟)
  4. 压力测试:跑1000次开关+录像循环(约30分钟)
  5. ITS图像测试:在灯箱环境下跑ITS核心项(约40分钟)
  6. 报告生成:汇总所有测试结果,发送邮件通知

下面是我用Python写的一个简化版流水线调度脚本:

#!/usr/bin/env python3
# camera_test_pipeline.py

import subprocess
import sys
import time

def run_cts_smoke():
    print("[Pipeline] 开始CTS冒烟测试...")
    result = subprocess.run([
        "cts-tradefed", "run", "cts", "-m", "Camera",
        "--include-filter", "android.hardware.camera2.cts.CameraManagerTest"
    ], capture_output=True)
    return result.returncode == 0

def run_stress_test():
    print("[Pipeline] 开始压力测试...")
    result = subprocess.run([
        "python3", "stress_test.py", "--iterations", "1000"
    ], capture_output=True)
    return result.returncode == 0

def run_its_tests():
    print("[Pipeline] 开始ITS图像测试...")
    result = subprocess.run([
        "python3", "its_main.py", "--test", "test_color_reproduction.py",
        "--camera", "0"
    ], capture_output=True)
    return result.returncode == 0

def main():
    results = {}
    results['cts'] = run_cts_smoke()
    results['stress'] = run_stress_test()
    results['its'] = run_its_tests()
    
    # 生成报告
    for test, passed in results.items():
        status = "PASS" if passed else "FAIL"
        print(f"[Pipeline] {test}: {status}")
    
    if all(results.values()):
        print("[Pipeline] 所有测试通过!")
        sys.exit(0)
    else:
        print("[Pipeline] 有测试失败,请检查日志。")
        sys.exit(1)

if __name__ == "__main__":
    main()

关键点:流水线里最容易被忽略的是“环境一致性”。我建议用Docker或者专门的测试机柜,保证每次测试的光照、温度、设备状态都一样。否则你今天测的结果和明天测的,可能完全不一样。

下面这张图展示了整个测试流水线的架构:

Camera自动化测试流水线架构 代码提交 Git Hook / CI触发 编译部署 编译HAL → 刷机 CTS冒烟测试 ~20分钟 压力测试 1000次开关+录像 ~30分钟 ITS图像测试 色彩/噪声/动态范围 ~40分钟 内存泄漏检测 长时间录像监控 ~60分钟 报告生成与通知 汇总结果 → 邮件/钉钉 构建阶段 验证阶段 测试阶段

这条流水线跑起来之后,你每天上班第一件事就是看邮件报告。哪个测试项挂了,直接定位到对应的HAL模块。效率比人工测试高出一个数量级。

最后提醒一句:自动化测试不是万能的。它只能发现你预设好的问题。真正的“硬骨头”,比如特定场景下的图像闪烁、低温下的帧率下降,往往需要人工去复现。所以,自动化测试 + 人工抽检,才是最佳实践。


公众号:蓝海资料掘金营,微信deep3321