你是不是也想快速实现图像分类,却不想费劲搭建复杂的深度学习框架?今天就给大家分享一个超实用的技巧 ------ 用 OpenCV 的 DNN 模块直接调用训练好的模型,不管是单张图片还是批量图片,都能轻松完成分类任务,小白也能快速上手!

一、核心原理:DNN 模块到底能干啥?
简单来说,OpenCV 的 DNN 模块就像一个 "模型中转站"。它支持加载 Caffe、TensorFlow、PyTorch 等主流框架训练好的模型,不用我们再去手写网络结构、调参训练。
我们只需要做两件事:
- 准备好模型配置文件 和权重文件
- 把输入图像处理成模型能 "看懂" 的样子
- 丢给模型就能直接出分类结果
这次我们用的是经典的 GoogLeNet 模型,它是基于 ImageNet 数据集训练的,可以识别 1000 种常见物体。
二、实战步骤:从单张图片到批量预测
话不多说,直接上代码和讲解,每一步都给你掰扯明白。
1. 前期准备
先把需要的工具包导入,再处理好标签文件。这个标签文件对应了 ImageNet 的 1000 个分类,能把模型输出的数字索引转换成我们能看懂的物体名称。
python
# 导入工具包
import utils_paths
import numpy as np
import cv2
# 标签文件处理:把每行的标签提取出来,方便后续对应结果
rows = open("synset_words.txt").read().strip().split("\n")
classes = [r[r.find(" ") + 1:].split(",")[0] for r in rows]
2. 加载预训练模型
这里我们加载基于 Caffe 框架训练的 GoogLeNet 模型,需要两个文件:.prototxt是模型的配置文件,定义了网络结构;.caffemodel是训练好的权重文件,存着网络里的参数。
python
# Caffe所需配置文件:加载模型结构和权重
net = cv2.dnn.readNetFromCaffe("bvlc_googlenet.prototxt",
"bvlc_googlenet.caffemodel")
3. 单张图片预测:一步一步来
这是最基础的操作,核心就是把图片转换成模型需要的blob格式,再传入模型得到结果。
python
# 获取图像路径
imagePaths = sorted(list(utils_paths.list_images("images/")))
# 读取并预处理单张图片
image = cv2.imread(imagePaths[0])
# 第一步:调整尺寸。模型训练时用的是224×224,测试也得统一尺寸
resized = cv2.resize(image, (224, 224))
# 第二步:转换成blob。1是缩放因子,(104,117,123)是ImageNet的三通道均值
blob = cv2.dnn.blobFromImage(resized, 1, (224, 224), (104, 117, 123))
print("First Blob: {}".format(blob.shape)) # 输出blob形状,方便检查
# 传入模型做预测
net.setInput(blob)
preds = net.forward() # 前向传播,得到1000个分类的概率值
# 找出概率最大的分类
idx = np.argsort(preds[0])[::-1][0] # 排序后取第一个(概率最大)
# 生成结果文本:分类名称 + 概率百分比
text = "Label: {}, {:.2f}%".format(classes[idx],preds[0][idx] * 100)
# 把结果画到图片上
cv2.putText(image, text, (5, 25), cv2.FONT_HERSHEY_SIMPLEX,0.7, (0, 0, 255), 2)
# 显示结果
cv2.imshow("Image", image)
cv2.waitKey(0)
这里要注意两个关键点:
- 图片尺寸必须和训练时一致,不然模型会 "看不懂"
- 减均值是为了消除光照的影响,让分类更准确,这个均值是 ImageNet 数据集统计出来的,直接用就行
4. 批量图片预测:效率翻倍
如果有一堆图片要分类,一个个预测太麻烦,用blobFromImages函数(注意比单张多了个s)就能批量处理,速度更快。
python
# Batch数据制作:批量处理剩下的图片
images = []
for p in imagePaths[1:]:
image = cv2.imread(p)
image = cv2.resize(image, (224, 224))
images.append(image)
# 转换成批量blob,参数和单张一样
blob = cv2.dnn.blobFromImages(images, 1, (224, 224), (104, 117, 123))
print("Second Blob: {}".format(blob.shape))
# 批量预测
net.setInput(blob)
preds = net.forward()
# 遍历每个预测结果,画到图片上并显示
for (i, p) in enumerate(imagePaths[1:]):
image = cv2.imread(p)
idx = np.argsort(preds[i])[::-1][0]
text = "Label: {}, {:.2f}%".format(classes[idx],preds[i][idx] * 100)
cv2.putText(image, text, (5, 25), cv2.FONT_HERSHEY_SIMPLEX,0.7, (0, 0, 255), 2)
cv2.imshow("Image", image)
cv2.waitKey(0)

三、小技巧和注意事项
- 预处理要一致:训练时对图片做了啥操作(缩放、减均值、裁剪等),测试时必须一模一样,不然结果会跑偏。
- 框架选择灵活 :除了 Caffe,OpenCV 还支持
readNetFromTensorflow、readNetFromTorch等,换个函数就能加载其他框架的模型。 - 批量预测更高效:处理大量图片时,批量预测比单张循环快很多,推荐优先使用。
完整代码
python
# 导入工具包
import utils_paths
import numpy as np
import cv2
# 标签文件处理
rows = open("synset_words.txt").read().strip().split("\n")
classes = [r[r.find(" ") + 1:].split(",")[0] for r in rows]
# Caffe所需配置文件
net = cv2.dnn.readNetFromCaffe("bvlc_googlenet.prototxt",
"bvlc_googlenet.caffemodel")
# 图像路径
imagePaths = sorted(list(utils_paths.list_images("images/")))
# 图像数据预处理
image = cv2.imread(imagePaths[0])
resized = cv2.resize(image, (224, 224))
# image scalefactor size mean swapRB
blob = cv2.dnn.blobFromImage(resized, 1, (224, 224), (104, 117, 123))
print("First Blob: {}".format(blob.shape))
# 得到预测结果
net.setInput(blob)
preds = net.forward()
# 排序,取分类可能性最大的
idx = np.argsort(preds[0])[::-1][0]
text = "Label: {}, {:.2f}%".format(classes[idx],
preds[0][idx] * 100)
cv2.putText(image, text, (5, 25), cv2.FONT_HERSHEY_SIMPLEX,
0.7, (0, 0, 255), 2)
# 显示
cv2.imshow("Image", image)
cv2.waitKey(0)
# Batch数据制作
images = []
# 方法一样,数据是一个batch
for p in imagePaths[1:]:
image = cv2.imread(p)
image = cv2.resize(image, (224, 224))
images.append(image)
# blobFromImages函数,注意有s
blob = cv2.dnn.blobFromImages(images, 1, (224, 224), (104, 117, 123))
print("Second Blob: {}".format(blob.shape))
# 获取预测结果
net.setInput(blob)
preds = net.forward()
for (i, p) in enumerate(imagePaths[1:]):
image = cv2.imread(p)
idx = np.argsort(preds[i])[::-1][0]
text = "Label: {}, {:.2f}%".format(classes[idx],
preds[i][idx] * 100)
cv2.putText(image, text, (5, 25), cv2.FONT_HERSHEY_SIMPLEX,
0.7, (0, 0, 255), 2)
cv2.imshow("Image", image)
cv2.waitKey(0)