当前位置: 首页 > news >正文

用KNN实现手写数字识别:基于 OpenCV 和 scikit-learn 的实战教学 (超级超级超级简单)

用KNN实现手写数字识别:基于 OpenCV 和 scikit-learn 的实战教学

在这篇文章中,我们将使用 KNN(K-Nearest Neighbors)算法对手写数字进行分类识别。我们会用 OpenCV 读取图像并预处理数据,用 scikit-learn 构建并训练模型,最终识别新的数字图像。

为什么像素可以被代码读取为数据:

图像的本质:像素的数字矩阵

任何数字图像(如照片、截图、手写数字图片)都是由无数个微小的 “像素点”(Pixel)组成的

  • 每个像素点的数值含义

    • 对于灰度图(如代码中的手写数字),每个像素用一个 0-255 的整数表示亮度:0 代表纯黑,255 代表纯白,中间值表示不同深浅的灰色。
    • 对于彩色图(如 RGB 格式),每个像素由三个数值(R、G、B)组成,分别对应红、绿、蓝三种颜色的亮度,组合后呈现出各种颜色。

(可以在调试时看一看代码里的‘gray’参数里面 单个数字图像的矩阵)

此20×20 像素的数字图像就是一个数字矩阵显示出的一个大大的 0

使用的数据集

我们使用的是一个包含 5000 个手写数字(0-9) 的图像文件(digits5000.png),每种数字500个,总共10类。图像被排布成了一个 50 行 × 100 列 的网格,每个小格是一个 20×20 像素的数字图像

数据图像:

保存下面代码所需的三张图片:

 

上面的 ‘3’,‘6’ 图片可在‘开始’里的‘画图’中,可以创建我们想要自定义的数字图片:


然后使用画笔写一个数字后(笔粗一点):

把比例调到   20×20 像素

即可得到。

 完整代码:

import numpy as np
from sklearn.neighbors import KNeighborsClassifier
import cv2# 读取包含5000个手写数字的大图,每个数字为20x20像素
img = cv2.imread('digits5000.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度图# 将大图分割为50行100列的小单元格,每个单元格包含一个手写数字
cells = [np.hsplit(row,100) for row in np.vsplit(gray,50)]# 将单元格列表转换为四维数组: (50行, 100列, 20像素高, 20像素宽)
x = np.array(cells)# 准备训练集和测试集数据
# 前50列作为训练集,后50列作为测试集
# 数据重塑为: (样本数, 特征数),每个样本包含400个像素值
train = x[:,:50].reshape(-1,400)
test = x[:,50:].reshape(-1,400)# 创建标签数据
n = np.arange(10)  # 创建数字0-9的数组
# 每个数字对应250个样本(50行×5列),生成训练标签
tags = np.repeat(n,250)
tag = tags[:,np.newaxis]  # 转换为二维数组,形状为(2500, 1)# 创建并训练KNN分类器,使用k=5最近邻
knn = KNeighborsClassifier(n_neighbors=5)    #给初学者的建议:在此处设置断点 或 直接在import处设置断点,开启调试一行一行地运行,更好的看到每一个参数的变化
knn.fit(train, tag)# 评估模型在训练集上的准确率
predictions_train = knn.predict(train)    #此行结果没有运行,建议在开启调试,可看到
accuracy_train = knn.score(train, tag)
print(f"训练集准确率: {accuracy_train:.4f}")# 评估模型在测试集上的准确率
predictions_test = knn.predict(test)
accuracy_test = knn.score(test, tag)
print(f"测试集准确率: {accuracy_test:.4f}")# 预测外部数字3的图像
digit3 = cv2.imread('digit3.png')
digit3gray = cv2.cvtColor(digit3, cv2.COLOR_BGR2GRAY)
digit3test = digit3gray.reshape(-1,400)  # 重塑为模型期望的输入格式
predictions_digit3 = knn.predict(digit3test)
print(f"预测数字3的结果: {predictions_digit3}")# 预测外部数字6的图像
digit6 = cv2.imread('digit6.png')
digit6gray = cv2.cvtColor(digit6, cv2.COLOR_BGR2GRAY)
digit6test = digit6gray.reshape(-1,400)  # 重塑为模型期望的输入格式
predictions_digit6 = knn.predict(digit6test)
print(f"预测数字6的结果: {predictions_digit6}")

红色断点:

调试:

点击 单步执行 或 其他   ,多尝试尝试

点击“作为...查看”即可清晰查看


所需库导入

import numpy as np
from sklearn.neighbors import KNeighborsClassifier
import cv2
  • numpy: 用于矩阵操作。

  • sklearn.neighbors.KNeighborsClassifier: 实现KNN分类器。

  • cv2: OpenCV库,用于图像读取与处理。


图像加载与预处理

img = cv2.imread('digits5000.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

  • cv2.imread() 读取图像。

  • cv2.cvtColor() 将图像从彩色(BGR)转换为灰度(GRAY),便于处理。


图像分割成小数字图块

cells = [np.hsplit(row,100) for row in np.vsplit(gray,50)]
x = np.array(cells)
  • 使用 np.vsplit() 将图像竖直切成50行(每行包含100个数字)。

  • 对每行使用 np.hsplit() 水平切分成100列,最终每个小格是一个20x20的数字图像。

  • 得到的 x 是一个形状为 (50, 100, 20, 20) 的数组。


构建训练集与测试集

train = x[:,:50].reshape(-1,400)
test = x[:,50:].reshape(-1,400)
  • 将每个 20×20 图像展开为 1×400 的一维向量。

  • 前 50 列作为训练集,后 50 列作为测试集。

  • traintest 的形状均为 (2500, 400)


构造标签

n = np.arange(10)                   #(0123456789)
tags = np.repeat(n,250)            #每个数字重复250次 -> [0,...0,1,...,1,...9,...9]
tag = tags[:,np.newaxis]           #添加新维度,变成列向量
# test_tag = np.repeat(n,250)[:np.newaxis]   
  • tags 是长度为 2500 的一维标签数组。

  • tag 是形状为 (2500, 1) 的列向量,作为训练和测试的真实标签。

为什么重复250次,因为我们把数据从中间对半切开,每个数字有500个,左二百五十为训练集,右二百五十个为测试集。

所以其实训练集和测试集的标签是一样的,标签就是每一个20x20数字图像所显示的数字。


训练模型并评估准确率

knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(train, tag)predictions_train = knn.predict(train)
accuracy_train = knn.score(train, tag)
print(accuracy_train)predictions_test = knn.predict(test)
accuracy_test = knn.score(test, tag)
print(accuracy_test)
  • 初始化一个 KNN 分类器,选择 k=5

  • 使用 .fit() 训练模型。

  • 使用 .predict().score() 对训练集和测试集进行预测和评分。

  • 打印准确率:理论上训练集精度应很高,测试集略低(但通常也超过 97%)。


识别自定义手写数字

digit3 = cv2.imread('digit3.png')   #图片中的数字是3
digit3gray = cv2.cvtColor(digit3, cv2.COLOR_BGR2GRAY)
digit3test = digit3gray.reshape(-1,400)
predictions_digit3 = knn.predict(digit3test)
print(predictions_digit3)digit6 = cv2.imread('digit6.png')   #图片中的数字是6
digit6gray = cv2.cvtColor(digit6, cv2.COLOR_BGR2GRAY)
digit6test = digit6gray.reshape(-1,400)
predictions_digit6 = knn.predict(digit6test)
print(predictions_digit6)
  • 读取两张额外图像 digit3.pngdigit6.png

  • 将其转换为灰度、再reshape成与训练数据一致的 (1, 400) 形状。

  • 使用模型预测数字类别。


总结

我们用 KNN 成功实现了手写数字的分类识别,关键步骤包括:

  1. 图像预处理和切分

  2. 标签构造与数据 reshape

  3. 使用 KNeighborsClassifier 建模

  4. 预测未知图像的数字类别

http://www.xdnf.cn/news/1196461.html

相关文章:

  • Kafka——消费者组消费进度监控都怎么实现?
  • 牛客周赛101 D题 题解
  • 五、搭建springCloudAlibaba2021.1版本分布式微服务-gateway网关
  • 力扣热题100----------53最大子数组和
  • 零基础学习性能测试第五章:Tomcat的性能分析与调优-Tomcat原理,核心配置项,性能瓶颈分析,调优
  • RAG(检索增强生成)
  • 探秘CommonJS:Node.js模块化核心解析
  • redis主从复制、哨兵机制底层原理
  • XML Schema 指示器:全面解析与深度应用
  • 齐护Ebook科技与艺术Steam教育套件 可图形化micropython Arduino编程ESP32纸电路手工
  • xgboost 机器学习在生物信息学中的应用
  • 【橘子分布式】gRPC(番外篇-客户端重试机制)
  • PostGIS面试题及详细答案120道之 (021-030 )
  • Java面试精进:测试、监控与序列化技术全解析
  • Netty中 ? extends Future<? super V>这种的写法的理解
  • 51c自动驾驶~合集9
  • Java面试宝典:MySQL执行原理二
  • Spring AI 项目实战(二十一):Spring Boot + AI +DeepSeek驱动的智能题库系统(附完整源码)
  • bash的特性-常用的通配符
  • AWS免费套餐全面升级:企业降本增效与技术创新解决方案
  • HCIP---MGRE实验
  • 电子电气架构 --- 软件bug的管理模式
  • logstash采集springboot微服务日志
  • 【奔跑吧!Linux 内核(第二版)】第4章:内核编译和调试
  • 商汤发布具身智能平台,让机器人像人一样和现实世界交互
  • Agent大模型大厂面试题及讲解答案
  • 【分享】外国使馆雷电综合防护系统改造方案(一)
  • 不坑盒子:Word里1秒制作“花括号”题目,多音字组词、形近字组词……
  • 【最新版】防伪溯源一体化管理系统+uniapp前端+搭建教程
  • 【Qt开发】信号与槽(二)-> 信号和槽的使用