import os
import cv2
import numpy as np
train_path = './minist_train'
file_train = os.listdir(train_path)
mean_vectors = np.zeros((10, 50)) # 用于存储每个数字的均值向量
for file_name in file_train:
file_path = os.path.join(train_path, file_name)
data = cv2.imread(file_path, cv2.IMREAD_GRAYSCALE) / 255.0 # 归一化
digit = int(file_name[0]) # 获取图片所代表的数字
# 计算每列的均值,并将结果累加到均值向量中
column_means = np.mean(data, axis=0)
mean_vectors[digit] += column_means
# 完成均值向量的计算,除以5以获取均值
mean_vectors /= 5
# for digit, mean_vector in enumerate(mean_vectors):
# print(f"Digit {digit}: {mean_vector}")
new_image_path = 'minist_val/9-7.JPG'
new_image = cv2.imread(new_image_path, cv2.IMREAD_GRAYSCALE) / 255.0
new_mean_vector = np.mean(new_image, axis=0)
# 计算新图片的均值向量与每个数字均值向量的距离
distances = np.linalg.norm(mean_vectors - new_mean_vector, axis=1)
# 预测最接近的数字
predicted_digit = np.argmin(distances)
print(f"模型预测数字为: {predicted_digit}")
使用均值向量方法进行数字预测的错误率较高的原因可能有以下几个:
-
特征不足: 均值向量方法仅使用了每张图片的列均值作为特征,这可能会丢失很多有用的信息。数字识别通常需要更多的特征来更准确地区分不同的数字。
-
差异不明显: 不同的数字在均值向量上可能有相似之处,导致难以区分。特别是当数字之间有相似的形状或结构时,均值向量方法可能无法很好地区分它们。
-
样本不足: 如果每个数字仅有5个样本,这可能不足以捕获数字的多样性和变化。更多的样本通常会有助于提高模型的性能。
-
模型选择: 均值向量方法是一种非常简单的方法,可能不适用于复杂的数字识别任务。深度学习模型等更复杂的模型通常可以在数字识别任务中表现得更好。
下面是改进后的代码,对数字特征大小进行归一化以及按照数字位置裁剪大小相同的候选框
import os
import cv2
import numpy as np
train_path = './minist_train'
file_train = os.listdir(train_path)
# 初始化均值向量
mean_vectors = np.zeros((10, 50))
for file_name in file_train:
file_path = os.path.join(train_path, file_name)
data = cv2.imread(file_path, cv2.IMREAD_GRAYSCALE) / 255
data = (data * 255).astype(np.uint8)
digit = int(file_name[0]) # 获取图片所代表的数字
# 字体粗细标准化(示例使用形态学操作来增加粗细)
kernel = np.ones((3, 3), np.uint8)
data = cv2.dilate(data, kernel, iterations=1)
# 使用边缘检测或轮廓检测来找到数字的边界框
_, thresholded = cv2.threshold(data, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(contours) > 0:
# 找到数字区域的边界框
x, y, w, h = cv2.boundingRect(contours[0])
# 裁剪数字区域并调整大小为50x50像素
digit_region = data[y:y+h, x:x+w]
digit_resized = cv2.resize(digit_region, (50, 50))
# 计算数字区域的均值并将结果累加到均值向量中
column_means = np.mean(digit_resized, axis=0)
mean_vectors[digit] += column_means
# 完成均值向量的计算,除以样本数以获取均值
# 这里假设每个数字的样本数均相等,如果不是,需要除以实际的样本数
mean_vectors /= 5
# 预测图片的路径
new_image_path = 'minist_val/9-8.JPG'
new_image = cv2.imread(new_image_path, cv2.IMREAD_GRAYSCALE) / 255
new_image = (new_image * 255).astype(np.uint8)
# 字体粗细标准化(示例使用形态学操作来增加粗细)
kernel = np.ones((3, 3), np.uint8)
new_image = cv2.dilate(new_image, kernel, iterations=1)
# 使用边缘检测或轮廓检测来找到数字的边界框
_, thresholded = cv2.threshold(new_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
contours, _ = cv2.findContours(thresholded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(contours) > 0:
# 找到数字区域的边界框
x, y, w, h = cv2.boundingRect(contours[0])
# 裁剪数字区域并调整大小为50x50像素
digit_region = new_image[y:y+h, x:x+w]
digit_resized = cv2.resize(digit_region, (50, 50))
# 计算新图片的均值向量
new_mean_vector = np.mean(digit_resized, axis=0)
# 计算新图片的均值向量与每个数字均值向量的距离
dis = np.linalg.norm(mean_vectors - new_mean_vector, axis=1)
predicted_digit = np.argmin(dis) # 预测特征向量距离最小的
print(f"模型预测数字为: {predicted_digit}")