【OpenCV】利用OpenCV中的KNN算法实现手写数字和手写字母的识别

【OpenCV】利用OpenCV中的KNN算法实现手写数字和手写字母的识别
一、KNN算法(K近邻)

  • KNN(K Near Neighbor):k个最近的邻居,即每个样本都可以用它最接近的k个邻居来代表。

  • 距离度量:一般采用欧式距离度量,常用的距离度量方式还有:曼哈顿距离、切比雪夫距离

  • K值是可以设置的变量:

    • K值过小:k值小,特征空间被划分为更多子空间,整体模型变复杂,容易发生过拟合,k值越小,选择的范围就比较小,训练的时候命中率较高,近似误差小,而用测试集的时候就容易出错,估计误差大,容易过拟合
    • K值过大:无论输入,都将简单的预测它属于训练实例中最多的类。
  • 举例分析:

  • 有两个类,红色、蓝色。将红色点标记为0,蓝色点标记为1。创建25个训练数据,把它们分别标记为0或者1。Numpy中随机数产生器可以帮助我们完成这个任务

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    # 包含25个已知/训练数据的(x,y)值的特征集
    trainData = np.random.randint(0, 100, (25, 2)).astype(np.float32)
    
    # 用数字0和1分别标记红色和蓝色
    responses = np.random.randint(0, 2, (25, 1)).astype(np.float32)
    
    # 画出红色的点
    red = trainData[responses.ravel() == 0]
    plt.scatter(red[:, 0], red[:, 1], 80, 'r', '^')
    
    # 画出蓝色的点
    blue = trainData[responses.ravel() == 1]
    plt.scatter(blue[:, 0], blue[:, 1], 80, 'b', 's')
    
    plt.show()
    

    得到:
    【OpenCV】利用OpenCV中的KNN算法实现手写数字和手写字母的识别_第1张图片

  • 下面就是KNN的初始化,通过cv2.ml.KNearest_create()调用KNN传入一个训练数据集,以及对应的label。给它一个测试数据即测试集,让它来进行分类。OpenCV中使用knn.findNearest()函数

    • 参数1:测试数据
    • 参数2:k的值
    • 返回值1:由kNN算法计算得到的测试数据的类别标志
    • 返回值2:k的最近邻居的类别标志
    • 返回值3:每个最近邻居到测试数据的距离

    测试数据被标记为绿色。
    得到:
    【OpenCV】利用OpenCV中的KNN算法实现手写数字和手写字母的识别_第2张图片结果为:

    result:  [[0.]] #测试数据被分为红色
    
    neighbours:  [[0. 1. 0.]] #测试数据有三个邻居,其中两红一蓝
    
    distance:  [[ 185.  338. 2205.]]#测试数据与邻居之间的距离
    

二、KNN识别手写数字

KNN算法需要训练集和测试集。OpenCV 安装包中有一张图片digits.png,其中有5000个手写数字(每个数字重复500次)。每个数字是一个20x20的小图。所以第一步就是将这个图像分割成5000个不同的数字,将拆分后的每一个数字图像展成400(20x20)个像素点的图像。这个就是我们的特征集,所有像素的灰度值。我们使用每个数字的前250个样本做训练数据,剩余的250个做测试数据。

import numpy as np
import cv2

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

# 我们把图片分成5000张,每张20 x 20个像素
cells = [np.hsplit(row, 100) for row in np.vsplit(gray, 50)] #np.hsplit是将数组纵向划分

# 使cells变成一个numpy数组,它的维度为(50, 100, 20, 20)
x = np.array(cells)

# 训练数据和测试数据
train = x[:, :50].reshape(-1, 400).astype(np.float32) # size = (2500, 400)
test = x[:, 50:100].reshape(-1, 400).astype(np.float32) # size = (2500, 400)

# 为训练集和测试集创建一个label
k = np.arange(10)
train_labels = np.repeat(k, 250)[:, np.newaxis]
test_labels = train_labels.copy()

# 初始化KNN,训练数据、测试KNN,k=5
knn = cv2.ml.KNearest_create()
knn.train(train, cv2.ml.ROW_SAMPLE, train_labels)
ret, result, neighbours, dist = knn.findNearest(test, k=5)

# 分类的准确率
# 比较结果和test_labels
matches = result==test_labels
correct = np.count_nonzero(matches)
accuracy = correct * 100.0 / result.size
print(accuracy)

#结果为:91.76

digits.png:

三、KNN识别英文字母

OpenCV自带的数据文件letter-recognition.data。有20000行,每一行的第一列是我们的一个字母标记,接下来的16个数字是它的不同特征。取前10000个作为训练集,剩下的10000个作为测试集。先把字母换成ASCII码,因为我们不直接处理字母。

import cv2
import numpy as np

# 加载数据,并转换字母为ASCII码
data = np.loadtxt('letter-recognition.data', dtype='float32', delimiter=',',
                  converters={0: lambda ch: ord(ch) - ord('A')})

# 拆分数据为俩份,训练数据,测试数据各10000
train, test = np.vsplit(data, 2) # np.vsplit(data, 2) 将data横向分成两组,20000/2 = 10000

# 拆分训练数据集为特征及分类
responses, trainData = np.hsplit(train, [1]) # np.hsplit(train,[1])函数的作用是在第1个索引处将数组train纵向划分成两组,即responses保存的是原来的字母

labels, testData = np.hsplit(test, [1])

# 初始化KNN,训练模型,度量其准确性
knn = cv2.ml.KNearest_create()
knn.train(trainData, cv2.ml.ROW_SAMPLE, responses) #训练,以以trainData去标记response
ret, result, neighbours, dist = knn.findNearest(testData, k=5)

correct = np.count_nonzero(result == labels)#result为训练测试集特征得到的预测结果,labels存储的是本来的真实值,result==labels得到的为1的话,即为预测正确,np.count_nonzero(result == labels)即得到其中为1的总个数
accuracy = correct * 100.0 / 10000

print('OCR alphabet accuracy: ', accuracy)

#结果为:93.06

你可能感兴趣的:(笔记,opencv,算法,python)