import numpy as np
import cv2
img = cv2.imread('digits.png') # 加载包含手写数字的图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换为灰度图像
digits.png
是经典的手写数字数据集图像,尺寸为2000x1000像素cells = [np.hsplit(row,100) for row in np.vsplit(gray,50)]
x = np.array(cells) # 转换为NumPy数组,形状(50,100,20,20)
train = x[:, :50] # 前50列作为训练集
test = x[:, 50:100] # 后50列作为测试集
train_new = train.reshape(-1,400).astype(np.float32) # (2500,400)
test_new = test.reshape(-1,400).astype(np.float32) # (2500,400)
k = np.arange(10) # 创建基础标签数组[0-9]
labels = np.repeat(k, 250) # 每个数字重复250次
输入:[0,1,2,...,9]
输出:[0,0,...,0,1,1,...,1,...,9,9,...,9]
250次 250次 250次
train_labels = labels[:, np.newaxis] # 形状(2500,1)
test_labels = np.repeat(k,250)[:, np.newaxis]
knn = cv2.ml.KNearest_create() # 创建KNN分类器
cv2.ml
命名空间knn.train(train_new, cv2.ml.ROW_SAMPLE, train_labels)
参数解析:
train_new
:训练数据矩阵(2500×400)cv2.ml.ROW_SAMPLE
:指示数据按行组织(每行一个样本)train_labels
:对应的标签数据(2500×1)底层实现:
ret, result, neighbours, dist = knn.findNearest(test_new, k=3)
参数说明:
test_new
:测试数据集(2500×400)k=3
:选择3个最近邻进行投票返回值解析:
ret
:操作状态标志(True/False)result
:预测结果数组(2500×1)neighbours
:最近邻索引矩阵(2500×3)dist
:对应距离矩阵(2500×3)matches = result == test_labels
correct = np.count_nonzero(matches)
accuracy = correct * 100.0 / result.size
计算过程:
典型输出:
当前使用KNN识别手写数字的准确率为93.6%
k值优化:
for k in range(1,10):
ret, result, _, _ = knn.findNearest(test_new, k=k)
# 计算并比较不同k值的准确率
距离权重优化:
# OpenCV不支持直接设置权重,可改用sklearn实现
from sklearn.neighbors import KNeighborsClassifier
knn_sk = KNeighborsClassifier(weights='distance')
特征工程:
数据增强:
# 示例:添加随机旋转(±15度)
angle = np.random.uniform(-15,15)
M = cv2.getRotationMatrix2D((10,10), angle, 1)
digit_rot = cv2.warpAffine(digit, M, (20,20))
import numpy as np
import cv2 #3.1本身就自带了机器学的函数 sklearn
img = cv2.imread('digits.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#转换为灰度图
# 将原始图像划分成独立的数字,每个数字大小20*20,共计5000个
cells = [np.hsplit(row,100) for row in np.vsplit(gray,50)]#按照顺序来看代码,python列表
# 装进array,形状(50,100,20,20),50行,100列,每个图像20*20大小
x = np.array(cells)
train = x[ : , :50]# 划分为训练集和测试集:比例各占一半
test = x[:,50:100]
# 将数据构造为符合KNN的输入,将每个数字的尺寸由20*20调整为1*400(一行400个像素)
train_new =train.reshape(-1,400).astype(np.float32) # Size = (2500,400)
test_new = test.reshape(-1,400).astype(np.float32) # Size = (2500,400)
# 分配标签:分别为训练数据、测试数据分配标签(图像对应的实际值)
k = np.arange(10)#(0123456789)
labels = np.repeat(k,250)#repeat重复数组中的元素,每个元素重复250次
train_labels = labels[:,np.newaxis]#np.newaxis是NumPy库中的一个特殊对象,用于在数组中增加一个新的维度。
test_labels = np.repeat(k,250)[:,np.newaxis]
# 模型构建+训练,sklearn knn,,opencv里面也有knn
knn = cv2.ml.KNearest_create()#通过cv2创建一个knn模型
knn.train(train_new, cv2.ml.ROW_SAMPLE, train_labels)#cv2.ml.ROW_SAMPLE: 这是一个标志,告诉OpenCV训练数据是按行组织的,即每一行是一个样本。
ret,result,neighbours,dist = knn.findNearest(test_new,k=3)
# ret: 表示查找操作是否成功。
# result: 浮点数数组,表示测试样本的预测标签。
# neighbours: 这是一个整数数组,表示与测试样本最近的k个邻居的索引。这些索引对应于训练集中的样本,可以用来检查哪些训练样本对预测结果产生了影响。
# dist: 这是一个浮点数数组,表示测试样本与每个最近邻居之间的距离。这些距离可以帮助理解预测结果的置信度;距离越近,预测通常越可靠。
# 通过测试集校验准确率
matches = result==test_labels
correct = np.count_nonzero(matches)#
accuracy = correct*100.0/result.size
print( "当前使用KNN识别手写数字的准确率为{}%:".format(accuracy))
#1、自己创建一个测试集来测试正确率
#2、改成sklearn库来实现
实时识别扩展:
# 摄像头实时识别
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
# 添加数字区域检测和识别代码
cv2.imshow('Digit Recognition', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
模型部署优化:
通过本案例,我们不仅实现了基础的手写数字识别,还探讨了计算机视觉与机器学习结合的典型工作流程。KNN算法虽简单,但配合适当的数据处理和参数调优,仍能在特定场景下发挥出色效果。