前言
数字识别是计算机视觉入门的经典实战场景,本文以「摄像头实时数字识别」为例,从核心原理、代码实现、运行调试到核心知识点总结,完整讲解基于模板匹配 + 轮廓分析的数字识别技术,适合 OpenCV 初学者理解视觉识别的基础逻辑。
目录
[1. 依赖安装](#1. 依赖安装)
[2. 数字模板制作(关键!)](#2. 数字模板制作(关键!))
[1. 模板加载模块(init)](#1. 模板加载模块(init))
[2. 相似度计算模块(calculate_similarity)](#2. 相似度计算模块(calculate_similarity))
[3. 帧处理模块(process_frame)](#3. 帧处理模块(process_frame))
[4. 结果显示模块(run)](#4. 结果显示模块(run))
[4. 结果显示模块(run)](#4. 结果显示模块(run))
[1. 关键参数调优](#1. 关键参数调优)
[2. 常见问题与解决方案](#2. 常见问题与解决方案)
[1. 基础视觉识别流程](#1. 基础视觉识别流程)
[2. 模板匹配的优缺点](#2. 模板匹配的优缺点)
[3. 进阶优化方向](#3. 进阶优化方向)
论文投稿:
第六届能源、电力与先进热力系统国际学术会议
大会官网:https://ais.cn/u/qmy2Mr
大会时间:2026年2月6-8日
大会地点:中国-广州-戴斯温德姆酒店(广州国际金融城科韵路地铁站店)



一、核心原理
本案例的数字识别核心是「轮廓定位 + 模板匹配」,整体逻辑分为两步:
- 轮廓定位:通过二值化、轮廓分析,从摄像头画面中找到数字所在的区域(ROI);
- 模板匹配:将定位到的数字区域与提前制作的 0-9 数字模板逐一对比,通过像素相似度判断数字类别。
关键概念解析
| 概念 | 作用 |
|---|---|
| 二值化 | 将灰度图转为纯黑 / 纯白,突出数字轮廓,消除背景干扰 |
| 轮廓分析 | 查找图像中的连续边界,筛选出数字的轮廓,定位数字位置 |
| 模板匹配 | 将待识别数字与标准模板对比,通过像素相似度判断数字类别 |
| 轮廓排序 | 按「从上到下、从左到右」排序数字区域,符合人眼阅读习惯 |
二、环境准备
1. 依赖安装
仅需 OpenCV 和 NumPy 两个核心库,执行以下命令安装:
2. 数字模板制作(关键!)
模板质量直接决定识别精度,按以下要求准备:
- 在代码同级目录创建
digits文件夹; - 制作 10 张 PNG 图片(命名为
0.png~9.png):- 背景:纯白色(255),数字:纯黑色(0);
- 尺寸:建议统一为
50x80像素(宽 x 高),数字居中; - 字体:印刷体 / 粗体无衬线字体(如 Arial Bold),避免手写体、艺术体。
三、核心代码实现(基础版)
以下是保留核心逻辑的基础版代码,结构清晰,适合初学者理解:
python
import cv2
import numpy as np
from operator import itemgetter
class DigitRecognizer:
def __init__(self):
"""初始化:加载数字模板 + 配置摄像头"""
# 1. 加载0-9数字模板(黑数字、白背景)
self.templates = []
for i in range(10):
# 读取灰度模板
template = cv2.imread(f'digits/{i}.png', cv2.IMREAD_GRAYSCALE)
if template is not None:
# 模板二值化(统一格式:黑数字,白背景)
_, template_bin = cv2.threshold(template, 150, 255, cv2.THRESH_BINARY)
self.templates.append(template_bin)
# 2. 初始化摄像头(0=内置,1=外接)
self.cap = cv2.VideoCapture(0)
# 设置摄像头分辨率
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
def calculate_similarity(self, src, template):
"""计算待识别数字与模板的像素相似度"""
# 调整模板尺寸与待识别区域一致
resized_template = cv2.resize(template, (src.shape[1], src.shape[0]))
# 计算相同像素数量占比(相似度:0~1,值越高越匹配)
same_pixels = np.sum(src == resized_template)
total_pixels = src.size
return same_pixels / total_pixels
def process_frame(self, frame):
"""处理单帧画面:定位数字 + 识别数字"""
# 步骤1:预处理(灰度化 + 二值化)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 固定阈值二值化(150为分界,>150为白,<150为黑)
_, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
# 步骤2:查找数字轮廓(反向二值图,让数字成为前景)
contours, _ = cv2.findContours(255 - binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 步骤3:筛选有效轮廓(过滤小面积噪声)
digit_contours = []
for contour in contours:
if cv2.contourArea(contour) > 50: # 面积>50视为有效数字
x, y, w, h = cv2.boundingRect(contour) # 获取轮廓外接矩形
center_x = x + w/2
center_y = y + h/2
digit_contours.append({
'center_x': center_x,
'center_y': center_y,
'rect': (x, y, w, h)
})
# 步骤4:按位置排序(从上到下,从左到右)
digit_contours.sort(key=lambda c: (c['center_y'] // 50, c['center_x']))
# 步骤5:逐轮廓识别数字
recognized_digits = []
for contour_info in digit_contours:
x, y, w, h = contour_info['rect']
# 提取数字区域(ROI)
roi_gray = gray[y:y+h, x:x+w]
if roi_gray.size == 0:
continue
# ROI二值化(与模板格式一致)
_, roi_bin = cv2.threshold(roi_gray, 150, 255, cv2.THRESH_BINARY)
# 查找ROI内数字的最大轮廓(数字主体)
roi_contours, _ = cv2.findContours(255 - roi_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(roi_contours) == 0:
continue
digit_contour = max(roi_contours, key=lambda cnt: cv2.contourArea(cnt))
# 转换轮廓坐标为原图绝对坐标
digit_contour = digit_contour + np.array([[x, y]])
# 与0-9模板逐一对比,找相似度最高的数字
results = []
for num, template in enumerate(self.templates):
try:
similarity = self.calculate_similarity(roi_bin, template)
results.append({'num': num, 'similarity': similarity})
except:
continue
# 筛选相似度>0.7的最佳匹配
if results:
best_match = max(results, key=itemgetter('similarity'))
if best_match['similarity'] > 0.7:
recognized_digits.append({
'num': best_match['num'],
'similarity': best_match['similarity'],
'rect': (x, y, w, h),
'contour': digit_contour
})
return binary, recognized_digits
def run(self):
"""主循环:读取摄像头帧 + 识别 + 显示结果"""
while True:
# 读取摄像头帧
ret, frame = self.cap.read()
if not ret:
break
# 处理帧,获取识别结果
binary, digits = self.process_frame(frame)
# 绘制识别结果
for digit in digits:
x, y, w, h = digit['rect']
# 1. 绘制数字边界框(绿色)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
# 2. 绘制识别的数字(红色)
cv2.putText(frame, str(digit['num']), (x, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
# 3. 绘制相似度(蓝色)
cv2.putText(frame, f"{digit['similarity']:.2f}", (x, y+h+20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
# 4. 绘制数字轮廓(洋红色)
cv2.drawContours(frame, [digit['contour']], -1, (255, 0, 255), 2)
# 显示窗口
cv2.imshow('Original', frame)
cv2.imshow('Binary', binary)
# 按q键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
self.cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
# 实例化并运行
recognizer = DigitRecognizer()
recognizer.run()
四、代码核心模块解析
1. 模板加载模块(init)
- 核心逻辑:读取 0-9 灰度模板→二值化(统一为「黑数字、白背景」)→存入列表;
- 关键注意:模板路径必须正确(
digits/0.png~digits/9.png),否则会跳过加载。
2. 相似度计算模块(calculate_similarity)
- 核心逻辑:将模板缩放到与待识别区域同尺寸→计算相同像素占比;
- 相似度范围:0~1,值越接近 1 表示数字与模板越匹配(如 1 表示完全一致)。
3. 帧处理模块(process_frame)
这是整个程序的核心,分为 5 个关键步骤:

4. 结果显示模块(run)
- 二值化:
cv2.threshold固定阈值 150,将图像转为纯黑 / 纯白,突出数字; - 轮廓筛选:面积 > 50 过滤小噪声(如灰尘、光斑);
- 轮廓排序:
center_y // 50按行分组,保证数字按阅读顺序识别。 - 绘制元素:边界框(绿)、识别数字(红)、相似度(蓝)、数字轮廓(洋红);
- 退出逻辑:按
q键释放摄像头、关闭窗口,避免资源泄漏。
4. 结果显示模块(run)
- 绘制元素:边界框(绿)、识别数字(红)、相似度(蓝)、数字轮廓(洋红);
- 退出逻辑:按
q键释放摄像头、关闭窗口,避免资源泄漏。
五、调试技巧
1. 关键参数调优
| 参数 | 作用 | 调优建议 |
|---|---|---|
| 二值化阈值(150) | 区分数字和背景 | 光照强→提高到 180;光照弱→降低到 120 |
| 轮廓面积阈值(50) | 过滤小噪声 | 数字小→降低到 30;噪声多→提高到 80 |
| 相似度阈值(0.7) | 筛选有效识别结果 | 误识别多→提高到 0.8;漏识别多→降低到 0.6 |
2. 常见问题与解决方案
| 问题现象 | 原因分析 | 解决方案 |
|---|---|---|
| 识别不到数字 | 模板加载失败 / 轮廓面积阈值过高 | 检查模板路径;降低轮廓面积阈值;确保数字在画面中清晰可见 |
| 识别错误(相似度低) | 模板与数字字体 / 尺寸不一致 | 重新制作与识别数字字体一致的模板;统一模板尺寸 |
| 画面卡顿 | 摄像头分辨率过高 | 降低分辨率(如 1280x720→640x480);增大轮廓面积阈值 |
| 数字排序混乱 | 排序分组参数(50)不合适 | 调整center_y // 50中的 50(数字行间距大→增大,小→减小) |
六、核心知识点总结
1. 基础视觉识别流程
摄像头实时识别的通用流程:读取帧→预处理(灰度化 / 二值化)→特征提取(轮廓)→特征匹配(模板)→结果展示,该流程适用于大部分简单视觉识别场景。
2. 模板匹配的优缺点
| 优点 | 缺点 |
|---|---|
| 逻辑简单,易上手 | 对字体、尺寸、角度敏感 |
| 计算量小,实时性好 | 抗光照干扰能力弱 |
| 无需训练,直接匹配 | 模板制作繁琐,通用性差 |
3. 进阶优化方向
如果需要提升识别鲁棒性,可尝试以下优化:
- 自适应二值化 :用
cv2.adaptiveThreshold替代固定阈值,抗光照干扰更强; - 形态学操作 :添加开运算(
cv2.morphologyEx)去除二值图中的小噪点; - 多尺度模板匹配:对模板进行多尺度缩放,匹配不同大小的数字;
- 机器学习替代:使用 MNIST 数据集训练 CNN 模型,实现更通用的数字识别。
七、学习收获
通过本案例的学习,可掌握 OpenCV 的核心基础操作:
- 摄像头视频流的读取与显示;
- 图像预处理(灰度化、二值化);
- 轮廓的查找、筛选与排序;
- 模板匹配的核心逻辑;
- 视觉识别结果的绘制与展示。
该案例是 OpenCV 入门的经典实践,理解其核心逻辑后,可扩展到字母识别、简单图形识别等更多场景。