做车牌识别主要还是数字识别,简单的搞个数字识别试试效果
由于本开发版比较早,只能使用maixpy-v1的固件,好几个KPU的函数与新版本不一样,在使用的时候要注意版本
直接上代码,用AI注释一下方便快捷
# ==================== 导入必要的库 ====================
import sensor, image, time, lcd # 导入摄像头、图像处理、时间和LCD模块
import KPU as kpu # 导入KPU神经网络处理单元模块
import math # 导入数学库,用于softmax计算
# ==================== LCD初始化 ====================
lcd.init() # 初始化LCD显示屏
lcd.clear(lcd.WHITE) # 将LCD清屏为白色背景
# ==================== 摄像头初始化 ====================
sensor.reset() # 复位摄像头
sensor.set_vflip(1) # 垂直翻转图像(根据摄像头安装方向调整)
sensor.set_hmirror(1) # 水平翻转图像(根据摄像头安装方向调整)
sensor.set_pixformat(sensor.RGB565) # 设置像素格式为RGB565(16位彩色)
sensor.set_framesize(sensor.QVGA) # 设置帧大小为QVGA (320x240像素)
sensor.set_windowing((224, 224)) # 设置窗口大小为224x224(居中裁剪)
sensor.set_contrast(2) # 设置对比度为2(适当增加,范围通常是-3到3)
sensor.set_brightness(0) # 设置亮度为0(保持默认)
sensor.set_auto_gain(False) # 关闭自动增益控制,保持图像稳定
sensor.set_auto_whitebal(False) # 关闭自动白平衡,避免颜色变化
sensor.skip_frames(time=1000) # 跳过前1000ms的帧,等待摄像头稳定
clock = time.clock() # 创建时钟对象,用于计算FPS
# ==================== 加载KPU模型 ====================
print("正在加载模型...")
task = kpu.load("/sd/uint8_mnist_cnn_model.kmodel") # 从SD卡加载模型文件
kpu.set_outputs(task, 0, 1, 1, 10) # 设置模型输出层形状:
# task: 模型任务句柄
# 0: 第0个输出层
# 1,1,10: 输出形状为(1,1,10),即10个类别的概率
print("模型加载成功!")
# ==================== Softmax函数定义 ====================
def softmax(x):
"""
将logits转换为概率值
参数:
x: 输入的原始输出值列表(logits)
返回:
概率值列表,总和为1
"""
max_x = max(x) # 找到最大值,用于数值稳定性
exp_sum = 0 # 初始化指数和
exp_vals = [] # 存储指数值
# 计算每个值的指数(减去最大值防止溢出)
for v in x:
e = math.exp(v - max_x) # 计算exp(v - max_x)
exp_vals.append(e) # 存储指数值
exp_sum += e # 累加指数和
# 计算概率(每个指数值除以总和)
return [e/exp_sum for e in exp_vals]
# ==================== 图像预处理函数 ====================
def preprocess_for_model(img):
"""
为模型预处理图像(不修改原图)
步骤:灰度化 -> 直方图均衡 -> 缩放到28x28 -> 反转颜色 -> 二值化 -> 缩放到112x112
参数:
img: 输入的原始图像
返回:
img_input: 处理后的模型输入图像(112x112)
img_28: 28x28的二值化图像(用于调试显示)
"""
# 1. 转换为灰度图(单通道图像)
img_gray = img.to_grayscale()
# 2. 直方图均衡化增强对比度
# 让图像的灰度分布更均匀,提高数字和背景的对比度
img_gray.histeq()
# 3. 调整到28x28(MNIST数据集的原始大小)
img_28 = img_gray.resize(28, 28)
# 4. 反转颜色(确保白底黑字)
# MNIST数据集是白底黑字,而摄像头通常得到的是黑底白字
img_28.invert()
# 5. 二值化处理
# 将图像转换为纯粹的黑白两色,减少噪声干扰
for y in range(28):
for x in range(28):
if img_28.get_pixel(x, y) > 200: # 如果像素值大于200(接近白色)
img_28.set_pixel(x, y, 255) # 设置为白色背景
else: # 否则(像素值<=200,接近黑色)
img_28.set_pixel(x, y, 0) # 设置为黑色数字
# 6. 放大到模型输入大小(112x112)
# 因为我们的模型可能是在112x112的图像上训练的
img_input = img_28.resize(112, 112)
img_input.pix_to_ai() # 将图像数据复制到AI内存区域,供KPU使用
return img_input, img_28 # 返回处理后的图像
# ==================== 主程序开始 ====================
print("开始识别...")
print("请使用黑笔在白纸上写数字")
while True:
clock.tick() # 记录开始时间,用于计算FPS
# 获取原始图像(用于LCD显示)
# 这保持原始亮度,不会过黑
img_display = sensor.snapshot()
# 复制一份用于模型预处理
# 这样就不会修改用于显示的原始图像
img_copy = img_display.copy()
# 对副本进行预处理(不修改原图)
img_input, img_28 = preprocess_for_model(img_copy)
# 运行模型推理
# 将预处理后的图像输入KPU,得到输出结果
out = kpu.forward(task, img_input)
# 如果推理成功,处理输出结果
if out is not None:
# 获取输出数据
# 输出可能是元组形式,取第一个元素
if isinstance(out, tuple):
logits = out[0] # 取第一个输出张量
else:
logits = out # 直接使用输出
# 提取10个数字的原始值(logits)
values = [float(logits[i]) for i in range(10)]
# 使用softmax转换为概率
probs = softmax(values)
# 找到最大概率及其对应的数字
max_prob = max(probs) # 最大概率值
digit = probs.index(max_prob) # 最大概率对应的数字(0-9)
# 在原始图像上显示识别结果
if max_prob > 0.5: # 只显示置信度>50%的结果
# 识别结果 - 使用红色大字体
display_str = "Num: %d" % digit
prob_str = "%.1f%%" % (max_prob * 100)
# 在图像左上角显示数字和置信度
img_display.draw_string(10, 10, display_str, color=(255, 0, 0), scale=2)
img_display.draw_string(10, 40, prob_str, color=(255, 0, 0), scale=2)
# 显示所有数字的概率分布(小字体,绿色)
# 每行显示两个数字的概率,共5行
for i in range(0, 10, 2):
if i+1 < 10: # 如果有两个数字可以显示
line = "%d:%.2f %d:%.2f" % (i, probs[i], i+1, probs[i+1])
else: # 最后一个数字单独显示
line = "%d:%.2f" % (i, probs[i])
# 计算y坐标:70 + 行号*15
img_display.draw_string(10, 70 + (i//2)*15, line, color=(0, 255, 0), scale=1)
# 打印识别结果到串口终端
print("识别: %d (%.1f%%)" % (digit, max_prob*100))
else:
# 置信度太低,显示提示信息
img_display.draw_string(10, 10, "Low confidence", color=(255, 255, 0), scale=1)
# 在右下角显示预处理后的缩略图(用于调试)
# 将28x28的二值化图像放大到56x56,方便观察预处理效果
img_thumb = img_28.resize(56, 56)
img_display.draw_image(img_thumb, 224-70, 224-70) # 放在右下角
# 将最终图像显示到LCD上
lcd.display(img_display)
# 打印当前的FPS(帧率)到串口终端
print("FPS: %.2f" % clock.fps())实际效果如下,还是不错的。


本次分享到这,下次分享一下如何完成自己的模型与训练
我要赚赏金
