基于 OpenCV 与 sklearn 的数字识别:KNN 算法实践

发布于:2025-07-29 ⋅ 阅读:(18) ⋅ 点赞:(0)

在计算机视觉领域,数字识别是一个经典问题,广泛应用于邮政编码识别、车牌识别等场景。本文将介绍如何使用 OpenCV 进行图像处理,并结合 KNN(K 近邻)算法实现数字识别,同时对比 OpenCV 内置 KNN 与 scikit-learn 库中 KNN 的实现差异。

准备工作

首先,我们需要导入所需的库:

import numpy as np
import cv2
from sklearn.neighbors import KNeighborsClassifier

其中,numpy用于数值计算,cv2(OpenCV)用于图像处理,KNeighborsClassifier则是 scikit-learn 库中的 KNN 分类器。

图像读取与预处理

我们需要读取两张图像:一张包含大量数字样本的训练图像,另一张作为测试图像。

# 读取训练图像和测试图像
img = cv2.imread(r'D:\pythonProject11\class\aa.png')
c_img = cv2.imread(r'D:\pythonProject11\class\ccc.png')

# 将彩色图像转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
c_gray = cv2.cvtColor(c_img, cv2.COLOR_BGR2GRAY)

彩色图像包含 RGB 三个通道,转换为灰度图像可以减少计算量,同时保留图像的主要特征。cv2.cvtColor函数用于颜色空间转换,COLOR_BGR2GRAY参数表示从 BGR 格式(OpenCV 默认的彩色图像格式)转换为灰度格式。

数据准备

图像分割

我们假设训练图像是一个包含 50 行 100 列数字的网格图像,每个数字占据一个 20×20 像素的区域。我们需要将这个大图像分割成多个小图像,每个小图像对应一个数字样本。

python

运行

# 先垂直分割成50行,再对每行水平分割成100列
cell = np.array([np.hsplit(row, 100) for row in np.split(gray, 50)])

这里使用了np.splitnp.hsplit两个函数:

  • np.split(gray, 50)将灰度图像垂直分割成 50 个等高度的子数组
  • np.hsplit(row, 100)将每个子数组水平分割成 100 个等宽度的子数组

最终得到的cell是一个形状为 (50, 100, 20, 20) 的数组,表示 50 行 100 列,每个元素是 20×20 像素的数字图像。

训练集和测试集划分

我们将前 50 列作为训练集,后 50 列作为测试集:

python

运行

# 划分训练集和测试集
train_ma = cell[:, :50]  # 前50列作为训练集
test_ma = cell[:, 50:100]  # 后50列作为测试集

# 转换为二维数组(样本数×特征数)
train_ma = train_ma.reshape(-1, 400).astype(np.float32)  # 50×50=2500个样本,每个样本20×20=400个特征
test_ma = test_ma.reshape(-1, 400).astype(np.float32)

reshape(-1, 400)将每个 20×20 的图像转换为一个长度为 400 的一维数组,便于作为机器学习算法的输入。-1表示自动计算该维度的大小,这里计算结果为 2500(50×50)。

标签生成

我们需要为每个样本生成对应的标签(即该样本对应的数字)。假设图像中的数字是按 0-9 的顺序重复排列的:

python

运行

# 生成标签
kernel = np.arange(10)  # 生成0-9的数字
train_la = np.repeat(kernel, 250)[:, np.newaxis]  # 每个数字重复250次,形成2500个标签
test_la = np.repeat(kernel, 250)[:, np.newaxis]

np.repeat(kernel, 250)将 0-9 每个数字重复 250 次,得到一个长度为 2500 的数组,与我们的样本数量一致。[:, np.newaxis]将一维数组转换为二维列向量,以满足 OpenCV 中 KNN 算法对标签格式的要求。

测试图像预处理

我们需要对测试图像进行同样的预处理:

python

运行

# 预处理测试图像
c_test = np.array(c_gray).reshape(-1, 400).astype(np.float32)

使用 OpenCV 的 KNN 进行识别

OpenCV 库中内置了 KNN 算法的实现:

python

运行

# 创建并训练OpenCV的KNN模型
knn = cv2.ml.KNearest_create()
knn.train(train_ma, cv2.ml.ROW_SAMPLE, train_la)  # ROW_SAMPLE表示每行是一个样本

# 预测
ret, results, neigh, dist = knn.findNearest(c_test, 4)  # 寻找4个最近邻
print("OpenCV KNN预测结果:", results)

cv2.ml.KNearest_create()创建一个 KNN 模型实例,train方法用于训练模型,findNearest方法用于预测。findNearest的第二个参数表示要寻找的最近邻数量 K。

使用 scikit-learn 的 KNN 进行识别

我们也可以使用 scikit-learn 库中的 KNN 实现:

python

运行

# 为scikit-learn准备标签(一维数组)
train_la = np.repeat(kernel, 250)
test_la = np.repeat(kernel, 250)

# 创建并训练scikit-learn的KNN模型
knnl = KNeighborsClassifier(n_neighbors=5)  # K=5
knnl.fit(train_ma, train_la)

# 评估模型准确率
a = knnl.score(test_ma, test_la)
print("模型准确率:", a)

# 预测
b = knnl.predict(c_test)
print("scikit-learn KNN预测结果:", b)

scikit-learn 的 KNN 使用起来更加简洁,KNeighborsClassifiern_neighbors参数指定 K 值,fit方法用于训练,score方法用于评估模型准确率,predict方法用于预测。

总代码

import numpy as np
import cv2
from sklearn.neighbors import KNeighborsClassifier
img = cv2.imread(r'D:\pythonProject11\class\aa.png')
c_img = cv2.imread(r'D:\pythonProject11\class\ccc.png')
# c_img = cv2.imread('img_1.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#转化为灰度图像
c_gray = cv2.cvtColor(c_img,cv2.COLOR_BGR2GRAY)
c_test = np.array(c_gray).reshape(-1,400).astype(np.float32)

cell =np.array([np.hsplit(row,100) for row in
             np.split(gray,50)])
train_ma = cell[:,:50]
test_ma = cell[:,50:100]
train_ma = train_ma.reshape(-1,400).astype(np.float32)
test_ma = test_ma.reshape(-1,400).astype(np.float32)
kernel = np.arange(10)
train_la = np.repeat(kernel,250)[:,np.newaxis]#cv2的方法
test_la = np.repeat(kernel,250)[:,np.newaxis]
knn = cv2.ml.KNearest_create()
knn.train(train_ma,cv2.ml.ROW_SAMPLE,train_la)#cv2.mL.ROW_SAMPLE:这是一个标志,告诉0pencv训练数据是按行组织的,即每一行是一个样本。
ret, results, neigh, dist = knn.findNearest(c_test,4)
print(results)
train_la = np.repeat(kernel,250)#sklearn的方法
test_la = np.repeat(kernel,250)
knnl = KNeighborsClassifier(n_neighbors=5)
knnl.fit(train_ma,train_la)
a=knnl.score(test_ma,test_la)
b=knnl.predict(c_test)
print(a,b)

两种实现的对比

  1. 接口设计:OpenCV 的 KNN 接口更偏向于计算机视觉领域的使用习惯,而 scikit-learn 的接口则更符合机器学习的通用范式。

  2. 输入格式:OpenCV 的 KNN 要求标签是列向量,而 scikit-learn 的 KNN 要求标签是一维数组。

  3. 功能:scikit-learn 的 KNN 提供了更多的评估方法和参数设置,而 OpenCV 的 KNN 则更轻量,与图像处理功能结合更紧密。

  4. 结果:在相同的 K 值设置下,两种实现的预测结果应该是相似的,但可能会因为具体实现细节的不同而略有差异。

总结

本文介绍了如何使用 OpenCV 进行图像预处理,以及如何使用 KNN 算法实现数字识别。通过对比 OpenCV 和 scikit-learn 中 KNN 的实现,我们可以看到不同库在接口设计和使用方式上的差异。

在实际应用中,我们可以根据具体需求选择合适的库和算法。如果需要处理图像并进行简单的分类,OpenCV 的 KNN 可能是一个不错的选择;如果需要更复杂的机器学习功能和更全面的评估方法,scikit-learn 则更为适合。

此外,KNN 算法虽然简单易懂,但在处理大规模数据集时效率较低。在实际应用中,我们可能需要考虑使用更高效的算法,如 SVM、神经网络等,以获得更好的性能。


网站公告

今日签到

点亮在社区的每一天
去签到