1. 在树莓派上部署TensorFlow Lite图像分类模型

任务目标

在树莓派(Raspberry Pi 4B/5,64位系统)上安装TensorFlow Lite运行时,使用Python API加载MobileNet V2图像分类模型,调用USB摄像头或树莓派Camera Module拍摄单帧图像,完成预处理、推理,并在终端输出Top-1类别标签与置信度。

环境与依赖

  • 硬件:树莓派4B/5(建议2GB+内存),USB摄像头或CSI摄像头
  • 系统:Raspberry Pi OS(64-bit,Bookworm或更新版本)
  • Python:3.9+
  • 关键依赖:tflite-runtime(无需完整TensorFlow)、opencv-python-headless、numpy、Pillow

安装命令:

sudo apt update
sudo apt install -y python3-pip libatlas-base-dev libopenblas-dev
pip3 install --break-system-packages tflite-runtime opencv-python-headless numpy Pillow

模型与标签文件

下载MobileNet V2量化模型(float16)及ImageNet标签文件:

wget https://storage.googleapis.com/download.tensorflow.org/models/tflite/mobilenet_v2_1.0_224_quantized.tgz
tar -xzf mobilenet_v2_1.0_224_quantized.tgz
wget https://storage.googleapis.com/download.tensorflow.org/models/tflite/mobilenet_v2_1.0_224_quantized_labels.txt

模型文件为mobilenet_v2_1.0_224_quantized.tflite,标签文件每行一个类别名,共1001行(含背景类)。

完整实现代码

将以下代码保存为classify_camera.py:

import cv2
import numpy as np
from PIL import Image
import tflite_runtime.interpreter as tflite

MODEL_PATH = "mobilenet_v2_1.0_224_quantized.tflite"
LABEL_PATH = "mobilenet_v2_1.0_224_quantized_labels.txt"
CAMERA_ID = 0  # USB摄像头通常为0,CSI摄像头请使用libcamera配置

def load_labels(path):
    with open(path, "r") as f:
        return [line.strip() for line in f.readlines()]

def preprocess_image(image):
    # 输入尺寸224x224,MobileNet V2要求像素值范围[0,1]
    img = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
    img = img.resize((224, 224))
    img = np.array(img, dtype=np.float32) / 255.0
    # 增加batch维度: (1, 224, 224, 3)
    return np.expand_dims(img, axis=0)

def main():
    # 加载模型
    interpreter = tflite.Interpreter(model_path=MODEL_PATH)
    interpreter.allocate_tensors()
    input_details = interpreter.get_input_details()
    output_details = interpreter.get_output_details()

    # 检查输入类型(量化模型可能需要uint8输入)
    input_dtype = input_details[0]["dtype"]
    print(f"模型输入类型: {input_dtype}")

    # 打开摄像头
    cap = cv2.VideoCapture(CAMERA_ID)
    if not cap.isOpened():
        raise RuntimeError("无法打开摄像头,请检查设备ID或连接")

    ret, frame = cap.read()
    cap.release()
    if not ret:
        raise RuntimeError("摄像头采集失败")

    # 预处理
    input_data = preprocess_image(frame)
    if input_dtype == np.uint8:
        input_data = (input_data * 255.0).astype(np.uint8)

    # 推理
    interpreter.set_tensor(input_details[0]["index"], input_data)
    interpreter.invoke()
    output_data = interpreter.get_tensor(output_details[0]["index"])[0]

    # 后处理:量化模型输出为uint8,需反量化
    output_scale, output_zero_point = output_details[0]["quantization"]
    if output_scale != 0.0:
        output_data = (output_data.astype(np.float32) - output_zero_point) * output_scale

    # 获取Top-1结果
    labels = load_labels(LABEL_PATH)
    top_index = np.argmax(output_data)
    confidence = output_data[top_index]

    print(f"类别: {labels[top_index]}")
    print(f"置信度: {confidence:.4f}")

if __name__ == "__main__":
    main()

运行方式

python3 classify_camera.py

验证方法与通过标准

  • 基本运行验证:程序能正常启动,摄像头指示灯亮起,约2~5秒后终端输出一行类别和一行置信度,无报错退出。
  • 功能正确性验证:将摄像头对准常见物体(如键盘、水杯、手机),输出的类别应与物体语义一致。例如对准键盘时,预期输出类别包含"keyboard";对准水杯时,预期输出包含"coffee mug"或"water bottle"等相近类别。
  • 置信度合理性:在光线充足、物体完整入镜时,Top-1置信度应大于0.5;若置信度持续低于0.3,说明预处理或模型输入类型可能不匹配,需检查input_dtype打印值是否为uint8。
  • 异常路径验证:拔掉摄像头后运行,程序应抛出RuntimeError并提示"无法打开摄像头",而非静默崩溃。

常见问题

  • 摄像头无法打开:执行ls /dev/video*查看设备节点,若为/dev/video1则修改CAMERA_ID=1。CSI摄像头需先运行sudo libcamera-hello确认驱动正常。
  • 模型输入类型不匹配:本模型为uint8量化模型,代码已自动适配。若使用其他模型,需根据input_details[0]["dtype"]调整预处理。
  • 推理速度慢:树莓派4B上CPU推理约需200~400ms/帧,属正常现象。若需加速,可改用GPU delegate或Edge TPU(需额外硬件)。