在计算机视觉领域,数字识别是一个经典问题,广泛应用于邮政编码识别、车牌识别等场景。本文将介绍如何使用 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.split
和np.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 使用起来更加简洁,KNeighborsClassifier
的n_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 innp.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)
两种实现的对比
接口设计:OpenCV 的 KNN 接口更偏向于计算机视觉领域的使用习惯,而 scikit-learn 的接口则更符合机器学习的通用范式。
输入格式:OpenCV 的 KNN 要求标签是列向量,而 scikit-learn 的 KNN 要求标签是一维数组。
功能:scikit-learn 的 KNN 提供了更多的评估方法和参数设置,而 OpenCV 的 KNN 则更轻量,与图像处理功能结合更紧密。
结果:在相同的 K 值设置下,两种实现的预测结果应该是相似的,但可能会因为具体实现细节的不同而略有差异。
总结
本文介绍了如何使用 OpenCV 进行图像预处理,以及如何使用 KNN 算法实现数字识别。通过对比 OpenCV 和 scikit-learn 中 KNN 的实现,我们可以看到不同库在接口设计和使用方式上的差异。
在实际应用中,我们可以根据具体需求选择合适的库和算法。如果需要处理图像并进行简单的分类,OpenCV 的 KNN 可能是一个不错的选择;如果需要更复杂的机器学习功能和更全面的评估方法,scikit-learn 则更为适合。
此外,KNN 算法虽然简单易懂,但在处理大规模数据集时效率较低。在实际应用中,我们可能需要考虑使用更高效的算法,如 SVM、神经网络等,以获得更好的性能。