1. 图像分类入门
任务目标
在Jetson Nano上构建一个基于TensorRT加速的MobileNetV2图像分类程序,读取CSI或USB摄像头实时画面,对画面中央区域物体进行分类,并在输出窗口叠加显示类别名称与置信度。本案例聚焦于TensorRT引擎构建与实时推理管线两个核心环节。
环境与依赖
- Jetson Nano(JetPack 4.6+),已安装TensorRT 8.x、OpenCV 4.x(自带于JetPack)
- Python 3.6+,需安装
pycuda(pip3 install pycuda) - 预训练模型:
resnet50_mobilenetv2.uff(NVIDIA官方提供,对应ImageNet 1000类) - 类别标签文件:
imagenet_labels.txt(可从TensorRT官方样例获取)
核心实现
以下代码完整实现了从UFF模型构建TensorRT引擎、预处理、推理到结果可视化的全流程。引擎构建仅需首次运行执行,之后会缓存为.trt文件加速加载。
import cv2
import numpy as np
import pycuda.driver as cuda
import pycuda.autoinit
import tensorrt as trt
import os
import time
# ---------- TensorRT 引擎构建 ----------
class TRTEngine:
def __init__(self, uff_path, trt_path, input_name="input_1",
input_shape=(3, 224, 224), output_name="dense_1"):
self.input_name = input_name
self.output_name = output_name
self.input_shape = input_shape
self.trt_path = trt_path
if os.path.exists(trt_path):
self.engine = self._load_engine(trt_path)
else:
self.engine = self._build_engine(uff_path, trt_path)
self.context = self.engine.create_execution_context()
self._allocate_buffers()
def _build_engine(self, uff_path, trt_path):
logger = trt.Logger(trt.Logger.WARNING)
builder = trt.Builder(logger)
network = builder.create_network()
parser = trt.UffParser()
parser.register_input(self.input_name, self.input_shape)
parser.register_output(self.output_name)
parser.parse(uff_path, network)
builder.max_workspace_size = 1 << 30 # 1GB
builder.fp16_mode = True # 启用FP16加速
engine = builder.build_cuda_engine(network)
with open(trt_path, "wb") as f:
f.write(engine.serialize())
return engine
def _load_engine(self, trt_path):
logger = trt.Logger(trt.Logger.WARNING)
with open(trt_path, "rb") as f:
engine_data = f.read()
runtime = trt.Runtime(logger)
return runtime.deserialize_cuda_engine(engine_data)
def _allocate_buffers(self):
self.h_input = cuda.pagelocked_empty(
trt.volume(self.input_shape), dtype=np.float32)
self.h_output = cuda.pagelocked_empty(
trt.volume((1000,)), dtype=np.float32)
self.d_input = cuda.mem_alloc(self.h_input.nbytes)
self.d_output = cuda.mem_alloc(self.h_output.nbytes)
def infer(self, img):
# 预处理:resize -> HWC转CHW -> 归一化到[-1,1]
img = cv2.resize(img, (self.input_shape[2], self.input_shape[1]))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = img.astype(np.float32) / 127.5 - 1.0
img = np.transpose(img, (2, 0, 1)).ravel()
np.copyto(self.h_input, img)
cuda.memcpy_htod(self.d_input, self.h_input)
self.context.execute(bindings=[int(self.d_input), int(self.d_output)])
cuda.memcpy_dtoh(self.h_output, self.d_output)
return self.h_output.copy()
# ---------- 类别标签加载 ----------
def load_labels(path):
with open(path, "r") as f:
return [line.strip() for line in f.readlines()]
# ---------- 主程序 ----------
def main():
# 配置路径(请根据实际位置修改)
UFF_PATH = "resnet50_mobilenetv2.uff"
TRT_PATH = "mobilenetv2.trt"
LABEL_PATH = "imagenet_labels.txt"
labels = load_labels(LABEL_PATH)
engine = TRTEngine(UFF_PATH, TRT_PATH)
# 打开摄像头(CSI摄像头用 nvarguscamerasrc,USB用 0)
cap = cv2.VideoCapture("nvarguscamerasrc ! video/x-raw(memory:NVMM), width=640, height=480, format=NV12, framerate=30/1 ! nvvidconv ! video/x-raw, format=BGR ! appsink", cv2.CAP_GSTREAMER)
if not cap.isOpened():
cap = cv2.VideoCapture(0) # 回退到USB摄像头
print("推理开始,按Q退出")
while True:
ret, frame = cap.read()
if not ret:
break
# 取画面中央224x224区域
h, w = frame.shape[:2]
cx, cy = w//2, h//2
crop = frame[cy-112:cy+112, cx-112:cx+112]
# 推理
start = time.time()
output = engine.infer(crop)
idx = np.argmax(output)
conf = float(output[idx])
fps = 1.0 / (time.time() - start)
# 可视化
label = f"{labels[idx]}: {conf:.2%}"
cv2.rectangle(frame, (cx-112, cy-112), (cx+112, cy+112), (0,255,0), 2)
cv2.putText(frame, label, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
cv2.putText(frame, f"FPS: {fps:.1f}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)
cv2.imshow("TensorRT MobileNetV2", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
运行与验证
- 将上述代码保存为
trt_mobilenet.py,与.uff模型和标签文件置于同一目录 - 首次运行会自动构建TensorRT引擎(约需1-2分钟),生成
mobilenetv2.trt缓存文件 - 执行
python3 trt_mobilenet.py启动程序 - 通过标准:摄像头画面出现绿色框,框内物体被正确识别(如对准杯子显示"cup"、对准人显示"person"),置信度高于60%,FPS达到15以上(FP16模式)
常见问题
- 摄像头打不开:确认CSI摄像头排线连接牢固,或改用USB摄像头(将
VideoCapture参数改为0) - 构建引擎报错:检查UFF模型路径是否正确,JetPack版本需包含对应TensorRT版本
- 识别不准确:确保画面中央区域包含完整物体,光照充足;ImageNet类别偏向日常物体,极端角度或遮挡会影响效果