《机器学习》——运用OpenCV库中的KNN算法进行图像识别

news2024/11/15 22:46:47

文章目录

  • KNN算法的简单介绍
  • 下载OpenCV库
  • 实验内容
  • 实验结果
  • 完整代码
  • 自己手写数字传入模型中测试

KNN算法的简单介绍

  • 一、KNN算法的基本要素
    • K值的选择:K值代表选择与新测试样本距离最近的前K个训练样本数,通常K是不大于20的整数。K值的选择对算法结果有重要影响,需要通过交叉验证等方法来确定最优的K值。
    • 距离度量:常用的距离度量方式包括闵可夫斯基距离、欧氏距离、曼哈顿距离、切比雪夫距离、余弦距离等。其中,欧氏距离在KNN算法中最为常用。
    • 分类决策规则:一般采用多数投票法,即选择K个最相似数据中出现次数最多的类别作为新数据的分类。
  • 二、KNN算法的工作流程
    • 准备数据:对数据进行预处理,包括收集、清洗和归一化等步骤,以确保所有特征在计算距离时具有相等的权重。
    • 计算距离:计算测试样本点到训练集中每个样本点的距离。
    • 排序与选择:根据距离对样本点进行排序,并选择距离最小的K个样本点作为测试样本的邻居。
    • 分类决策:根据K个邻居的类别信息,采用多数投票法确定测试样本的类别。

下载OpenCV库

pip install opencv-python
# 可以根据自己python的版本选择下载适配的opencv,可以在后面加上 == 指定版本 
# 例如
pip install opencv-python==3.4.17.63
  • 调用包的时候有点写法上的区别
import cv2

实验内容

  • 实验目的
    • 通过构建模型,传入训练的数据后,让模型达到尽可能高的准确率,并对传入的测试数据可以得出正确的结果
  • 实验大致流程
    • 下面是一张已经经过一些初步处理过的图片,其中含有0~9的手写数字,且每一个数字都是5行,100列共有2500个数字
    • 本次实验需要通过对这张2000*1000像素的图片进行切分处理
    • 将其划分成独立的数字每个数字大小为20*20像素,共计5000个;并平均切分为左右两个等份,一份作为训练集,一份作为测试集
    • 将训练集放到模型中训练后,再传入测试集进行测试,得到结果后,通过与正确结果比较得出准确率
    • 最后我们可以自己手写出一些数字,放入实验项目下,并处理后放入模型,测试出结果
      在这里插入图片描述
  • 实验步骤
    • 1、获取数据
    • 2、处理数据
    • 3、分配标签
    • 4、模型构建和训练
    • 5、测试
    • 6、通过测试集校验准确率

- 1、获取数据
- 因为这次的实验已经给定了数据,所以只需要将这张图片存放在实验项目下,并读取到代码中即可

# 通过opencv中imread方法,读取图片
img = cv2.imread('digits.png')
  • 2、处理数据
    在完成一个实验项目过程中,获取数据和处理数据需要花费很多时间

    • 因为给定的这张图片中,背景是黑色的,数字是白色的,除了黑色,其他任何颜色的图片都会有不同程度的亮度,而且白色是亮度最高的颜色(255),为了简化图片信息和提高计算效率,我们需要将图片转化为灰度图
    • 将原始图像划分成独立的数字
    • 用numpy的方法将划分后的图像块重塑为一个四维数组,其中最后两个维度对应于每个图像块的宽度和高度。
    • 划分训练集和测试集
    • 将数据构造为符合KNN算法的输入
      gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度图
      # 将原始图像划分成独立的数字,每个数字大小20*20,共计5000个
      cells = [np.hsplit(row, 100) for row in np.vsplit(gray, 50)]
      # 转换成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)
      
    • 注意:
      • .astype(np.float32): 是为了将reshape后的数组的数据类型转换为np.float32,即32位浮点数。这是因为在机器学习或深度学习中,通常会使用浮点数来表示特征或标签,而np.float32相比于64位浮点数(np.float64)可以节省内存,同时对于大多数应用来说,其精度已经足够。
  • 3、分配标签

    • 分别为训练集数据、测试集数据分配标签(图像对应的实际值)

    • 因为这里由10种数字,每类数字都需要重复分配标签250次

      # 分配标签:分别为训练数据、测试数据分配标签(图像对应的实际值)
      k = np.arange(10)  # (0123456789)
      labels = np.repeat(k, 250) # repeat->重复数组中的元素,每个元素重复250次
      # np.newaxis是NumPy库中的一个特殊对象,用于在数组中增加一个新的维度
      train_labels = labels[:, np.newaxis]  # 在训练集种加入标签维度
      test_labels = np.repeat(k, 250)[:, np.newaxis] # 生成一个测试标签,就是正确的结果,用于后面计算准确率
      
  • 4、模型构建和训练

    • 因为opencv库中有KNN算法,所以我们可以直接调用

    • 在KNN算法中传入训练集和标签

      # 模型构建+训练 
      knn = cv2.ml.KNearest_create()  # 通过cv2创建一个KNN模型
      # train训练方法
      knn.train(train_new, cv2.ml.ROW_SAMPLE, train_labels) # cv2.ml.ROW_SAMPLE:这是一个标志,告诉OpenCV训练数据是按行组织的,即每一行代表一个样本,每一列代表一个特征
      
  • 5、测试

    • 传入训练集,并指定K的值,可以更改不同的K值来找到最佳的测试结果
      # 测试
      # findNearest测试方法
      res, result, neighbours, dist = knn.findNearest(test_new, k=3)
      # ret: 表示查找操作是否成功
      # result:浮点数数组,表示测试样本的预测标签
      # neighbours:这是一个整数数组,表示与测试样本最接近的K个邻居的索引。这些索引对应于训练集中的样本,可以用来检查哪些训练样本对预测结果产生了影响
      # dist:这是一个浮点数组,表示测试样本与每个最近邻居之间的距离。这些距离可以帮助理解预测结果的置信度;距离越近,预测通常越可靠
      
  • 6、通过测试集校验准确率

    matches = result == test_labels
    correct = np.count_nonzero(matches)
    accuracy = correct * 100.0 / result.size
    print("当前使用KNN识别手写数字的准确率为:", accuracy)
    
    • 代码解释
      • matches = result == test_labels:这行代码通过比较result(KNN算法预测的结果)和test_labels(测试集的真实标签)来生成一个布尔数组matches。如果result中的某个预测值与test_labels中对应的真实标签相等,则matches中对应位置的值为True,否则为False。
      • correct = np.count_nonzero(matches):这行代码使用np.count_nonzero函数计算matches数组中True的数量,即正确预测的数量。np.count_nonzero函数会统计数组中所有非零元素(在这个场景下,即True)的数量。
      • accuracy = correct * 100.0 / result.size:这行代码计算准确率。首先,将正确预测的数量correct乘以100.0(为了得到百分比),然后除以result.size(即预测结果的总数,也就是测试集的大小)。这样得到的accuracy就是准确率,以百分比形式表示。
      • print(“当前使用KNN识别手写数字的准确率为:”, accuracy):最后,这行代码将计算得到的准确率打印出来。

实验结果

  • 打印出准确率
    在这里插入图片描述

完整代码

import numpy as np
import cv2

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)]
# 转换成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]

# 模型构建+训练  opencv里面也有KNN算法
knn = cv2.ml.KNearest_create()  # 通过cv2创建一个KNN模型
knn.train(train_new, cv2.ml.ROW_SAMPLE, train_labels) # cv2.ml.ROW_SAMPLE:这是一个标志,告诉OpenCV训练数据是按行组织的,即每一行代表一个样本,每一列代表一个特征

# 测试
res, 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识别手写数字的准确率为:", accuracy)

自己手写数字传入模型中测试

  • 下图是通过电脑自带的画图工具,写出的数字6,并且已经将大小调整为20*20像素大小的图片
    在这里插入图片描述

  • 将图片经过与实验中相同的处理方法,加以处理并传入到模型中进行测试

    # 读取图片
    img_6 = cv2.imread('6.png')
    # 将图片转换为灰度图
    gray_6 = cv2.cvtColor(img_6, cv2.COLOR_BGR2GRAY)
    # 将图片转换为数组结构
    test_gray_6 = np.array(gray_6)
    # 将尺寸由20*20调整为1*400(一行400个像素),才能符合KNN的输入结构
    test_6 = test_gray_6.reshape(-1, 400).astype(np.float32)
    # 将处理好的测试图片放入模型中
    res_6, result_6, neighbours_6, dist_6 = knn.findNearest(test_6, k=3)
    print(result_6)
    
  • 得出结果

    • 因为模型中训练是以数组的形式进行的,所以结果也会以数组的形式返回出来
    • 由此可以看出,此次实验的模型还是相对比较准确的
      在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2046859.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电压检测之比较电路

设计这款电路主要是本人在锂电池充电电路中挖了一个坑,对电源显示芯片的数据手册内容撰写不够详细的不好感受,所以自己根据比较电路的思想设计出了电压检测并反馈的电路,亦在提供一种电压检测的思想不需要借助ADC采集,在电路硬件上…

基于hive的海鲜交易数据分析系统设计与实现【hadoop、Flask、某东爬虫、sqoop、flume、mysql、hdfs】商品可换

文章目录 有需要本项目的代码或文档以及全部资源,或者部署调试可以私信博主项目介绍研究背景国内外研究现状研究目的研究意义 关键技术理论介绍数据采集及预处理数据采集字段介绍数据预处理hadoop集群搭建及实现过程hive建表hive大数据分析 可视化展示店铺维度画像分…

AR 眼镜之-开关机定制-实现方案

目录 📂 前言 AR 眼镜系统版本 开关机定制 1. 🔱 技术方案 1.1 技术方案概述 1.2 实现方案 1)开机 Logo 2)开机音效 3)开机动画 4)关机动画 5)关机弹窗 2. 💠 开机 Logo…

C++笔试题汇总

C笔试题汇总记录 一、概述二、概念分类1. 结构体1. C 和 C 中 struct 有什么区别?2. C中的 struct 和 class 有什么区别? 2. 类相关1. 类的大小1. 空类的大小2. 一般非空类大小3. 有虚函数类4. 有虚函数类的继承5. 只有虚函数6. 静态数据成员 2. C的三大…

【分享】格力手机色界G0245D 刷REC、root、 救砖、第三方rom教程和资源

开门见山 帮别人弄了一台 格力G0245D,把找到的资源和教程分享一下 教程 这个写的很详细了格力手机色界G0245D-Root-最简指南 不过教程里刷rec这一步漏了加上电源键,加上就行了。 附加参考:格力手机2刷机 格力手机二代刷机 GREE G0215D刷机…

C++ 特殊类设计以及单例模式

目录 1 不能被拷贝 2 只能在堆上创建对象 3 只能在栈上创建对象 4 禁止在堆上创建对象 5 不能被继承的类 6 单例类 特殊类就是一些有特殊需求的类。 1 不能被拷贝 要设计一个防拷贝的类,C98之前我们只需要将拷贝构造以及拷贝赋值设为私有,同时只声明…

在HFSS中对曲线等结构进行分割(Split)

在HFSS中对曲线进行分割 我们往往需要把DXF等其他类型文件导入HFSS进行分析,但是有时需要对某一个曲线单独进行分割成两段修改。 如果是使用HFSS绘制的曲线,我们修改起来非常方便,修改参数即可。但是如果是导入的曲线,则需要使用…

代码随想录训练营 Day31打卡 贪心算法 part05 56. 合并区间 738. 单调递增的数字 968. 监控二叉树

代码随想录训练营 Day31打卡 贪心算法 part05 一、 力扣56. 合并区间 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中…

【JavaEE】JVM 内存区域划分,以及 Java 垃圾回收机制引用计数器,可达性分析等

目录 1. JVM执行流程 2. JVM运行时数据区 2.1 堆 2.2 Java虚拟机栈(线程私有) 2.3本地方法栈(线程私有) 2.4 程序计数器 2.5 元数据区 3. JVM的类加载机制 1) 加载 2) 验证 3) 准备 4) 解析 5) 初始化 双亲委派模型 4. java垃圾回收 4.1 死亡对象判断方法 a) …

超精细CG杰作:8K壁纸级官方艺术插画,展现极致美丽与细节的汉服女孩

极致精美的数字艺术杰作:8K壁纸级别的官方插画,展现超高清细节与和谐统一的美感,女孩的精致面容与眼神在光影下熠熠生辉,汉服主题下的超高分辨率作品,文件巨大,细节丰富,令人惊叹。 正向提示词…

内存泄漏之如何使用Visual Studio的调试工具跟踪内存泄漏?

使用Visual Studio的调试工具跟踪内存泄漏是一个系统性的过程,主要包括启用内存泄漏检测、运行程序、分析内存使用情况以及定位泄漏源等步骤。 Visual Studio提供了多种方式来检测内存泄漏,你可以根据自己的需求选择合适的方法。 注意:下面…

父页面选项式api,子页面组合式api,子页面如何获取父页面的方法以及传值到将子页面的值传给父页面

开发的项目中是vue3的项目,但是有些同事用vue2中的选项式api写法,有些同事使用的是vue3组合式api的写法,此时子页面需要获取父页面的方法把数据传入父页面的方法中 父页面: 在父页面中order-item组件中创建自定义方法navigation和…

Leetcode每日刷题之剑指offer 57.和为s的两个数字(C++)

1.题目解析 现在题目改名为LCR.查找总价值为目标值的两个商品,虽然题目改变但是核心并未变化,都是需要寻找出和为指定数字的两数 2.算法原理 我们由题目知道给出的数组是递增的,所以在数组的首尾固定两个指针,判断其和是否为指定数…

Ceph篇之利用shell脚本实现批量创建bucket桶

Ceph创建bucket桶 在 Ceph 中创建桶(bucket)需要使用 Ceph 对象网关(RGW)。 注:如果查看shell批量创建脚本请直接参见目录3 1. 利用radosgw-admin工具创建桶 确保 Ceph 集群和对象网关已正确配置 确保你的 Ceph 集群…

快速了解Vi 和 Vim 编辑器三种模式及快捷键使用

😀前言 本篇博文是关于Vi 和 Vim 编辑器的三种模式及快捷键使用,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您的满意…

大数据产业链图谱_产业链全景图_大数据行业市场分析

数据作为新型生产要素,是数字化、网络化、智能化的基础,已快速融入生产、分配、流通、消费和社会服务管理等各环节,影响着千行百业,推动着我国数字经济的蓬勃发展。 大数据又称巨量数据、海量数据,是由数量巨大、结构…

C语言 | Leetcode C语言题解之第341题扁平化嵌套列表迭代器

题目&#xff1a; 题解&#xff1a; struct NestedIterator {int *vals;int size;int cur; };void dfs(struct NestedIterator *iter, struct NestedInteger **nestedList, int nestedListSize) {for (int i 0; i < nestedListSize; i) {if (NestedIntegerIsInteger(neste…

Sprache:轻量级C#解析器构建,可用于字符串验证等。

我们在开发中&#xff0c;经常需要对一些结构化文本进行解析&#xff0c;用于验证是否符合规则。我们一般会使用正则表达式&#xff0c;同时正则表达式也非常强大&#xff0c;但正则表达式在语法不便阅读与维护。 下面介绍一个简单、轻量级的库&#xff0c;方便我们在C#代码中…

React 学习——打包后,包体积可视化

1、安装插件 &#xff08; source-map-explorer &#xff09; npm i source-map-explorer 2、在配置文件package.json中加入 &#xff08; "analyze": "source-map-explorer build/static/js/*.js" &#xff09;&#xff0c;位置截图 "analyze&q…

Flask 线上高并发部署方案实现

目录 1、Flask默认多线程执行 2、使用gevent.pywsgi实现 3、是用uWSGI服务器实现 1、Flask默认多线程执行 前言&#xff1a;在Flask的较早版本中&#xff0c;默认并不支持多线程模式。然而&#xff0c;从Flask 0.9版本开始&#xff0c;引入了多线程模式的支持&#xff0c;并…