import numpy as np
import cv2
import os
# 配置参数
CONFIG = {
'template_size': (57, 88),
'binary_thresh': 10,
'min_contour_width': 40,
'min_contour_height': 10,
'kernel_size': (9, 9),
'match_threshold': 0.35 # 匹配置信度阈值
}
# 方法1: 使用绝对路径
template_image_path =r"opencv_实例\digital recognition\template.png" # 模板图像
input_image_path = r"opencv_实例\digital recognition\test.png" # 待识别图像
# 方法2: 使用相对路径(推荐)
# template_image_path = "test.png" # 模板图像
# input_image_path = "template.png" # 待识别图像
def cv_show(name, img):
"""显示图像的辅助函数"""
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def list_directory_contents():
"""列出当前目录和可能的图像文件"""
current_dir = os.getcwd()
print(f"\n当前工作目录: {current_dir}")
# 列出当前目录的文件
try:
files = os.listdir(current_dir)
image_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp']
image_files = [f for f in files if any(f.lower().endswith(ext) for ext in image_extensions)]
if image_files:
print("\n当前目录中的图像文件:")
for img_file in image_files:
print(f" - {img_file}")
else:
print("\n当前目录中没有找到图像文件")
# 检查是否存在数字识别文件夹
digit_recognition_dirs = [d for d in files if '数字识别' in d or 'digital' in d.lower()]
if digit_recognition_dirs:
print(f"\n找到相关目录: {digit_recognition_dirs}")
except Exception as e:
print(f"列出目录内容时出错: {e}")
def extract_digit_templates(img):
"""从模板图像中提取数字模板"""
# 转换为灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化
ref = cv2.threshold(ref, CONFIG['binary_thresh'], 255, cv2.THRESH_BINARY_INV)[1]
# 查找轮廓
refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(refCnts) == 0:
print("警告:未在模板图像中找到任何轮廓")
return {}
# 绘制轮廓用于调试
debug_img = img.copy()
cv2.drawContours(debug_img, refCnts, -1, (0, 0, 255), 3)
# cv_show('template_contours', debug_img)
# 按左到右排序轮廓
refCnts = sorted(refCnts, key=lambda c: cv2.boundingRect(c)[0])
digits = {}
# 遍历每个轮廓提取数字模板
for (i, c) in enumerate(refCnts):
(x, y, w, h) = cv2.boundingRect(c)
roi = ref[y:y + h, x:x + w]
roi = cv2.resize(roi, CONFIG['template_size'])
digits[i] = roi
print(f"提取模板数字 {i}: 原始尺寸({w}x{h}) -> 标准尺寸{CONFIG['template_size']}")
print(f"成功提取 {len(digits)} 个数字模板")
return digits
def preprocess_input_image(image):
"""预处理输入图像"""
# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值化
tophat = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV)[1]
# Sobel边缘检测
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
gradX = np.absolute(gradX)
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
if maxVal > minVal: # 避免除零错误
gradX = (255 * ((gradX - minVal) / (maxVal - minVal))).astype("uint8")
else:
gradX = gradX.astype("uint8")
# 形态学操作
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, CONFIG['kernel_size'])
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, CONFIG['kernel_size'])
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
thresh = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, sqKernel)
return tophat, thresh
def find_digit_groups(thresh, image):
"""查找数字组的位置"""
# 查找轮廓
threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(threshCnts) == 0:
print("警告:未找到任何轮廓")
return []
# 绘制所有轮廓用于调试
debug_img = image.copy()
cv2.drawContours(debug_img, threshCnts, -1, (0, 0, 255), 3)
# cv_show('all_contours', debug_img)
locs = []
# 筛选符合条件的轮廓
for (i, c) in enumerate(threshCnts):
(x, y, w, h) = cv2.boundingRect(c)
if w > CONFIG['min_contour_width'] and h > CONFIG['min_contour_height']:
locs.append((x, y, w, h))
print(f"找到符合条件的区域 {len(locs)}: 位置({x},{y}), 尺寸({w}x{h})")
# 按左到右排序
locs = sorted(locs, key=lambda x: x[0])
print(f"共找到 {len(locs)} 个符合条件的数字组区域")
return locs
def count_holes(roi):
"""统计二值图像中的“洞”数"""
contours, hierarchy = cv2.findContours(roi, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
hole_count = 0
if hierarchy is not None:
for i in range(len(contours)):
# hierarchy[0][i][3] == -1 表示外轮廓,!= -1 表示洞
if hierarchy[0][i][3] != -1:
hole_count += 1
return hole_count
def recognize_digits_in_group(group_roi, digits):
"""识别单个组中的所有数字,增加洞数辅助判别6和8"""
# 预处理组图像
group_binary = cv2.threshold(group_roi, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# 查找数字轮廓
digitCnts, hierarchy = cv2.findContours(group_binary.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(digitCnts) == 0:
print("警告:在数字组中未找到轮廓")
return []
# 按左到右排序
digitCnts = sorted(digitCnts, key=lambda c: cv2.boundingRect(c)[0])
group_output = []
for c in digitCnts:
(x, y, w, h) = cv2.boundingRect(c)
roi = group_binary[y:y + h, x:x + w]
roi_resized = cv2.resize(roi, CONFIG['template_size']) # 用于模板匹配
# 计算与所有模板的匹配分数
scores = []
for (digit, digitROI) in digits.items():
result = cv2.matchTemplate(roi_resized, digitROI, cv2.TM_CCOEFF_NORMED)
(_, score, _, _) = cv2.minMaxLoc(result)
scores.append(score)
# 找到最佳匹配
if scores:
best_match_idx = int(np.argmax(scores))
best_score = scores[best_match_idx]
# 洞数辅助判别6和8
if best_match_idx in [6, 8]:
roi_for_hole = roi.copy()
if roi_for_hole.shape[0] > 0 and roi_for_hole.shape[1] > 0:
holes = count_holes(roi_for_hole)
if best_match_idx == 8 and holes < 2:
best_match_idx = 6
print(f"洞数辅助修正: 8→6 (洞数={holes})")
elif best_match_idx == 6 and holes > 1:
best_match_idx = 8
print(f"洞数辅助修正: 6→8 (洞数={holes})")
if best_score > CONFIG['match_threshold']:
group_output.append(str(best_match_idx))
print(f"识别数字: {best_match_idx}, 置信度: {best_score:.3f}")
else:
group_output.append("?")
print(f"识别不确定: 最佳匹配 {best_match_idx}, 置信度: {best_score:.3f} (低于阈值 {CONFIG['match_threshold']})")
return group_output
def main():
"""主函数"""
print("开始数字识别处理...")
print("=" * 50)
# 列出当前目录内容,帮助用户确认文件位置
list_directory_contents()
# 加载图像
print("\n=== 加载图像 ===")
template_img = cv2.imdecode(np.fromfile(template_image_path, dtype=np.uint8), cv2.IMREAD_COLOR)
input_img = cv2.imdecode(np.fromfile(input_image_path, dtype=np.uint8), cv2.IMREAD_COLOR)
# 提取数字模板
print("\n=== 提取数字模板 ===")
digits = extract_digit_templates(template_img)
if not digits:
print("错误:未能提取到任何数字模板")
print("建议:检查模板图像是否包含清晰的数字")
return
# 预处理输入图像
print("\n=== 预处理输入图像 ===")
tophat, thresh = preprocess_input_image(input_img)
# 查找数字组
print("\n=== 查找数字组 ===")
locs = find_digit_groups(thresh, input_img)
if not locs:
print("错误:未找到任何数字组")
print("建议:调整参数或检查输入图像质量")
return
# 识别每个组中的数字
print("\n=== 识别数字 ===")
output = []
result_img = input_img.copy()
for (i, (gX, gY, gW, gH)) in enumerate(locs):
print(f"\n处理第 {i+1} 个数字组...")
# 提取组区域
group = tophat[gY:gY + gH, gX:gX + gW]
# 识别组中的数字
group_output = recognize_digits_in_group(group, digits)
if group_output:
# 在结果图像上绘制
cv2.rectangle(result_img, (gX - 5, gY - 5), (gX + gW + 5, gY + gH + 5), (0, 0, 255), 2)
result_text = "".join(group_output)
cv2.putText(result_img, result_text, (gX, gY - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
output.extend(group_output)
print(f"第 {i+1} 组识别结果: {result_text}")
else:
print(f"第 {i+1} 组识别失败")
# 显示最终结果
print(f"\n=== 最终识别结果 ===")
print(f"识别出的所有数字: {''.join(output)}")
print("=" * 50)
cv2.imshow("result", result_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 运行程序
if __name__ == "__main__":
main()
如何修改可以使这个代码能实时识别摄像头的数字
最新发布