【树莓派 Zero 2W】数字识别
本文介绍了树莓派 Zero 2W 开发板实现数字识别的板端推理的项目设计,包括准备工作、环境搭建、MNIST 数据集和 ONNX 模型、流程图、工程代码以及效果演示等。
项目介绍
准备工作:包括所需 Python 环境、数据集训练、模型下载、软件包的安装部署等;
单数字识别:使用预训练 onnx 模型实现包含一个手写数字图片的板端推理;
多数字识别:使用预训练 onnx 模型实现包含多个手写数字图片的板端推理;
MNIST 数据集
MNIST(Modified National Institute of Standards and Technology,修改后的美国国家标准与技术研究院)数据集是一个大型手写数字数据库,通常用于训练各种图像处理系统和机器学习模型。它通过“重新混合” NIST 原始数据集中的样本而创建,并且已成为评估图像分类算法性能的基准。


主要功能
MNIST 包含 60,000 个训练图像和 10,000 个手写数字的测试图像。
该数据集包含大小为 28×28 像素的灰度图像。
这些图像经过归一化处理,以适应28×28像素的边界框并进行抗锯齿处理,从而引入灰度级别。
MNIST 广泛用于机器学习领域的训练和测试,特别是对于图像分类任务。
数据结构
MNIST 数据集分为两个子集:
训练集: 该子集包含 60,000 张手写数字图像,用于训练机器学习模型。
测试集:此子集包含 10,000 张用于测试和评估已训练模型的图像。
数据集中的每张图像都标有相应的数字(0-9),使其成为一个用于分类任务的理想监督学习数据集。
应用
MNIST数据集广泛用于训练和评估图像分类任务中的深度学习模型,例如卷积神经网络(CNN)、支持向量机(SVM)和各种其他机器学习算法。
详见:yann.lecun.com 、fashion-mnist .
ONNX
Open Neural Network Exchange (ONNX) 是一个开放的生态系统,为 AI 开发人员提供支持 随着项目的发展选择正确的工具。


ONNX 为 AI 模型(包括深度学习和传统 ML)提供开源格式。它定义了一个可扩展的计算图模型,以及内置运算符和标准的定义数据类型。
ONNX 可用于标准化模型的表示形式,使得模型可以在不同的框架(如PyTorch、TensorFlow)和推理引擎(如ONNX Runtime、TensorRT)之间转换。
详见:onnx/onnx: Open standard for machine learning interoperability .
准备工作
包括 OpenCV、 onnxruntime 库安装和预训练 onnx 模型下载。
库安装
执行指令 sudo apt install python3-opencv 安装 OpenCV;
安装 onnxruntime 库,终端执行指令
sudo apt-get install python3-onnxruntime


执行指令 python -c "import onnxruntime, sys; print(onnxruntime.__version__)" 验证安装;


若遇到问题,可根据 Linux 系统版本和 Python 版本,下载 对应的 *.whl 文件并安装
wget https://pypi.tuna.tsinghua.edu.cn/packages/1c/a1/428ee29c6eaf09a6f6be56f836213f104618fb35ac6cc586ff0f477263eb/onnxruntime-1.23.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl sudo pip install onnxruntime-1.23.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl --break-system-packages
通过 uname -a 查询Linux系统信息,python --version 指令查询Python版本。
模型下载
下载 模型文件至 ./model 文件夹;
mkdir model cd model wget https://github.com/onnx/models/blob/main/validated/vision/classification/mnist/model/mnist-12.onnx
详见:mnist | onnx/models · GitHub .
单个数字识别
采用预训练的 MNIST 轻量化的 onnx 模型实现单个数字识别的板端推理,并弹窗显示、终端打印识别结果。
流程图

代码
终端执行 touch hnr_single.py 指令新建文件,并添加如下代码
#!/usr/bin/env python3
import cv2, numpy as np, onnxruntime as ort, time, sys
model = './model/mnist-12.onnx'
img_p = sys.argv[1] if len(sys.argv) > 1 else './img/num8.jpg'
# ---------- 1. 载入模型 ----------
sess = ort.InferenceSession(model, providers=['CPUExecutionProvider'])
in_name = sess.get_inputs()[0].name # 'Input3' shape [1,1,28,28]
out_name = sess.get_outputs()[0].name # 'Plus214_Output_0' 10 类置信度
# ---------- 2. 读图并预处理 ----------
img = cv2.imread(img_p, cv2.IMREAD_GRAYSCALE)
if img is None:
raise FileNotFoundError(img_p)
# 反二值化 + Resize 28×28 + 归一化 0~1
img = cv2.bitwise_not(img) # 白底黑字→黑底白字
img = cv2.resize(img, (28, 28))
blob = img.astype(np.float32) / 255.0
blob = blob.reshape(1, 1, 28, 28) # NCHW
# ---------- 3. 推理 ----------
t0 = time.time()
pred = sess.run([out_name], {in_name: blob})[0] # [1,10]
digit = int(np.argmax(pred))
cost = (time.time() - t0) * 1000
# ---------- 4. 结果 ----------
print(f'识别结果: {digit} (置信度: {pred[0,digit]:.3f}) 耗时: {cost:.1f} ms')
# 简单弹窗(可选)
cv2.putText(img, f'{digit}', (5, 22), cv2.FONT_HERSHEY_SIMPLEX, 0.8, 255, 2)
cv2.imshow('digit', img)
cv2.waitKey(0)保存代码。
效果
终端执行 python hnr_single.py 运行程序,弹窗显示识别结果;

终端输出识别结果和置信度等信息


多数字识别
采用预训练的 MNIST 轻量化的 onnx 模型实现多个数字识别的板端推理,并弹窗显示和终端打印识别结果。
流程图

代码
终端执行 touch hnr_multi.py 指令新建文件,并添加如下代码
#!/usr/bin/env python3
import cv2, numpy as np, onnxruntime as ort, sys, time, os
sess = ort.InferenceSession('./model/mnist-12.onnx', providers=['CPUExecutionProvider'])
in_name = sess.get_inputs()[0].name
out_name = sess.get_outputs()[0].name
# ---------- 1. 读图 ----------
img_path = sys.argv[1] if len(sys.argv) > 1 else './img/numbers.jpg'
img_bgr = cv2.imread(img_path)
assert img_bgr is not None, 'cannot read '+img_path
# ---------- 2. 灰度 + 反色 ----------
gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
binary = cv2.bitwise_not(gray) # 白底黑字 → 黑底白字
# ---------- 3. 去噪 ----------
binary = cv2.medianBlur(binary, 3)
# ---------- 4. Otsu 二值 ----------
_, th = cv2.threshold(binary, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# ---------- 5. 找轮廓 ----------
cnts, _ = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print('[dbg] findContours:', len(cnts), 'pieces')
# ---------- 6. 预处理函数(单数字) ----------
def mnistify(roi_u8, save_prefix):
"""
roi_u8: 黑底白字 uint8 图
return: NCHW 1×1×28×28 已归一化
"""
# 缩到 20×20
roi20 = cv2.resize(roi_u8, (20, 20), interpolation=cv2.INTER_AREA)
canvas = np.zeros((28, 28), dtype=np.uint8)
tl = (28 - 20) // 2
canvas[tl:tl + 20, tl:tl + 20] = roi20
blob = canvas.astype(np.float32) / 255.0
return blob.reshape(1, 1, 28, 28)
# ---------- 7. 逐 ROI 推理 ----------
show = img_bgr.copy()
digits = []
for idx, c in enumerate(cnts):
x, y, w, h = cv2.boundingRect(c)
print(f'[dbg] contour{idx}: x={x} y={y} w={w} h={h} area={cv2.contourArea(c):.1f}')
if w < 5 or h < 15 or w > 200 or h > 200 or w / h > 1.2 or h / w > 4:
print(' -> skip by filter')
continue
pad = 4
x1 = max(0, x - pad); y1 = max(0, y - pad)
x2 = min(img_bgr.shape[1], x + w + pad)
y2 = min(img_bgr.shape[0], y + h + pad)
roi = th[y1:y2, x1:x2] # 黑底白字
blob = mnistify(roi, f'05_roi{idx}')
pred = sess.run([out_name], {in_name: blob})[0]
digit = int(np.argmax(pred))
conf = float(pred[0, digit])
digits.append((x1, digit, conf))
print(f' -> pred={digit} conf={conf:.3f}')
cv2.rectangle(show, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(show, str(digit), (x1, y1 - 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
# ---------- 8. 结果 ----------
digits.sort(key=lambda d: d[0])
result = ''.join(str(d[1]) for d in digits)
print('found digits:', result, ' conf:', [f'{d[2]:.3f}' for d in digits])
cv2.imshow('multi-digit', show)
cv2.waitKey(0)保存代码。
效果
终端执行 python hnr_multi.py 运行程序,弹窗显示识别结果;


终端打印识别到的数字和坐标信息


总结
本文介绍了树莓派 Zero 2W 开发板实现数字识别的板端推理的项目设计,包括准备工作、环境搭建、MNIST 数据集和 ONNX 模型、流程图、工程代码以及效果演示等,为相关产品在 AI 视觉领域的开发设计和快速应用提供了参考。
我要赚赏金
