当前位置: 首页 > 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.dtcms.com/a/300490.html

相关文章:

  • Torchv Unstrustured 文档解析库
  • Mac配置本地邮件
  • 【Qt开发】信号与槽(二)-> 信号和槽的使用
  • Web Worker:解锁浏览器多线程,提升前端性能与体验
  • 29.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--用户配置服务
  • 七、搭建springCloudAlibaba2021.1版本分布式微服务-skywalking9.0链路追踪
  • 重生之我在暑假学习微服务第二天《MybatisPlus-下篇》
  • MCP + LLM + Agent 8大架构:Agent能力、系统架构及技术实践
  • 2.苹果ios逆向-Windows电脑端环境搭建-Conda安装和使用(使用Conda来管理多个Python环境)
  • Canvas实现微信小程序图片裁剪组件全攻略
  • 设计模式(七)结构型:适配器模式详解
  • 可控、安全、可集成:安防RTSP|RTMP视频播放模块工程实践参考
  • 医疗AI语义潜空间分析研究:进展与应用
  • 【机器学习深度学习】LLaMAFactory评估数据与评估参数解析
  • J3160迷你小主机 性能测试 对比i3-4170 以及服务器
  • C++ 多线程 std::thread::join
  • Window 部署 coze-stdio(coze 开发平台)
  • GAN/cGAN中到底要不要注入噪声
  • InfluxDB 与 MQTT 协议集成实践(二)
  • Element表格单元格类名动态设置
  • Linux网络
  • libomxil-bellagio移植到OpenHarmony
  • Ubuntu简述及部署系统
  • MybatisPlus-19.插件功能-通用分页实体
  • JDK 11.0.16.1 Windows 安装教程 - 详细步骤+环境变量配置
  • Day44 Java数组08 冒泡排序
  • AI与区块链Web3技术融合:重塑数字经济的未来格局
  • SpringSecurity实战:核心配置技巧
  • 【前端】【vscode】【.vscode/settings.json】为单个项目配置自动格式化和开发环境
  • 【C++基础】类型转换:static_cast/dynamic_cast 面试高频考点与真题解析