opencv の K 最近傍モジュールの基本的な使用手順と例
OpenCV では、K 最近傍アルゴリズムを実装するために複雑な関数を記述する必要はなく、独自のモジュール関数を直接呼び出すことができます。このセクションでは、簡単な例を使用して、OpenCV に付属する K 最近傍モジュールの使用方法を紹介します。
この例では、図 20-14 に示すように、異なる場所でのトレーニングに使用される 2 つのデータ セットが存在します。2 つのデータ セットのセットのうち、1 つのセットは左下隅に位置し、もう 1 つのセットは右上隅に位置します。値をランダムに生成し、OpenCV の K 最近傍モジュールを使用して、乱数がどのグループに属するかを決定します。
上記 2 つのデータセットのうち、左下隅に位置するデータセットは、x 座標値と y 座標値がともに (0, 30) の範囲内にあります。右上隅にあるデータの x 座標値と y 座標値は (70, 100) の範囲にあります。
上記の分析に基づいて、それぞれに 20 組の乱数 (20 個のランダム データ ポイント) を含む 2 つのデータ セットを作成します。
rand1 = np.random.randint(0, 30, (20, 2)).astype(np.float32)
rand2 = np.random.randint(70, 100, (20, 2)).astype(np.float32)
- 最初の乱数グループ rand1 では、その x 座標と y 座標はすべて (0, 30) の範囲内にあります。
- 2 番目の乱数グループ rand2 では、その x 座標と y 座標はすべて (70, 100) 間隔内にあります。
次に、2 つの乱数セットにラベルを割り当てます。 - 乱数ペアの最初のグループをタイプ 0、ラベル 0 に分割します。
- 乱数ペアの 2 番目のグループをタイプ 1、ラベル 1 に分割します。
次に、(0, 100) の値を持つ乱数のペアを生成します。
テスト = np.random.randint(0, 100, (1, 2)).astype(np.float32)
例: OpenCV の K-Nearest Neighbor モジュールを使用して、生成された乱数ペア テストが rand1 が属するタイプ 0 に属するか、rand2 が属するタイプ 1 に属するかを判定します。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 用于训练的数据
# rand1 数据位于(0,30)
rand1 = np.random.randint(0, 30, (20, 2)).astype(np.float32)
# rand2 数据位于(70,100)
rand2 = np.random.randint(70, 100, (20, 2)).astype(np.float32)
# 将 rand1 和 rand2 拼接为训练数据
trainData = np.vstack((rand1, rand2))
# 数据标签,共两类:0 和 1
# r1Label 对应着 rand1 的标签,为类型 0
r1Label=np.zeros((20,1)).astype(np.float32)
# r2Label 对应着 rand2 的标签,为类型 1
r2Label=np.ones((20,1)).astype(np.float32)
tdLable = np.vstack((r1Label, r2Label))
# 使用绿色标注类型 0
g = trainData[tdLable.ravel() == 0]
plt.scatter(g[:,0], g[:,1], 80, 'g', 'o')
# 使用蓝色标注类型 1
b = trainData[tdLable.ravel() == 1]
plt.scatter(b[:,0], b[:,1], 80, 'b', 's')
# plt.show()
# test 为用于测试的随机数,该数在 0 到 100 之间
test = np.random.randint(0, 100, (1, 2)).astype(np.float32)
plt.scatter(test[:,0], test[:,1], 80, 'r', '*')
# 调用 OpenCV 内的 K 近邻模块,并进行训练
knn = cv2.ml.KNearest_create()
knn.train(trainData, cv2.ml.ROW_SAMPLE, tdLable)
# 使用 K 近邻算法分类
ret, results, neighbours, dist = knn.findNearest(test, 5)
# 显示处理结果
print("当前随机数可以判定为类型:", results)
print("距离当前点最近的 5 个邻居是:", neighbours)
print("5 个最近邻居的距离: ", dist)
# 可以观察一下显示,对比上述输出
plt.show()
操作結果:
当前随机数可以判定为类型: [[1.]]
距离当前点最近的 5 个邻居是: [[1. 1. 1. 1. 1.]]
5 个最近邻居的距离: [[ 5. 17. 113. 136. 178.]]
図からわかるように、ランダムな点(星印の点)は、右側の小四角(タイプ1)の点に近いため、小四角のタイプ1に属すると判断される。
例: OpenCV に付属の関数を使用して手書き数字の認識を完了する
画像コレクションのセクション 05 にダウンロード リンクがあります。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取样本(特征)图像的值
s='image_number\\' # 图像所在的路径
num=100 # 共有的样本数量
row=240 # 特征图像的行数
col=240 # 特征图像的列数
a=np.zeros((num,row,col)) # 存储所有样本的数值
n=0 # 用来存储当前图像的编号
for i in range(0,10):
for j in range(1,11):
a[n,:,:]=cv2.imread(s+str(i)+'\\'+str(i)+'-'+str(j)+'.bmp',0)
n=n+1
# 提取样本图像的特征
feature=np.zeros((num,round(row/5),round(col/5))) # 用来存储所有样本的特征值
#print(feature.shape) # 看看特征值的形状是什么样子
#print(row) # 看看 row 的值,有多少个特征值(100)
# 从 0 开始,到 num-1 结束,每次加 1,
for ni in range(0,num):
for nr in range(0,row):
for nc in range(0,col):
if a[ni,nr,nc]==255:
feature[ni,int(nr/5),int(nc/5)]+=1
f = feature #简化变量名称
# 将 feature 处理为单行形式,并转换为 float32 类型,以便后面的 kNN 算法使用
train = feature[:,:].reshape(-1,round(row/5)*round(col/5)).astype(np.float32)
print(train.shape)
# 贴标签,要注意,是 range(0,100)而非 range(0,101)
train_labels = [int(i/10) for i in range(0,100)]
train_labels = np.asarray(train_labels)
test_labels = train_labels.copy()
#print(*trainLabels) # 打印测试看看标签值
##读取图像值,并提取特征,以便后面的 kNN 算法使用,这里只读取一个图像,即待识别图像,所以只有一个特征值,即 test
o=cv2.imread('image_number\\test\\5.bmp',0) # 读取待识别图像
of=np.zeros((round(row/5),round(col/5))) # 用来存储待识别图像的特征值
for nr in range(0,row):
for nc in range(0,col):
if o[nr,nc]==255:
of[int(nr/5),int(nc/5)]+=1
# 将 of 处理为单行形式,并转换为 float32 类型,以便后面的 kNN 算法使用
test=of.reshape(-1,round(row/5)*round(col/5)).astype(np.float32)
# 调用函数识别图像
knn=cv2.ml.KNearest_create()
knn.train(train,cv2.ml.ROW_SAMPLE, train_labels)
ret,result,neighbours,dist = knn.findNearest(test,k=5)
print("当前随机数可以判定结果是:", str(result[0][0]))
print("距离当前点最近的 5 个邻居是:", neighbours)
print("5 个最近邻居的距离: ", dist)
操作結果:
当前随机数可以判定结果是: 5.0
距离当前点最近的 5 个邻居是: [[5. 3. 5. 3. 5.]]
5 个最近邻居的距离: [[77185. 78375. 79073. 79948. 82151.]]
見てください、おっと!結果は非常に正確です。その後、他のデジタル画像をテストしますか?
o=cv2.imread('image_number\\test\\6.bmp',0) # 读取待识别图像
改めて効果を見てみると、
当前随机数可以判定结果是: 1.0
距离当前点最近的 5 个邻居是: [[6. 1. 1. 1. 1.]]
5 个最近邻居的距离: [[90739. 92107. 92312. 92652. 93016.]]
何度か続けて変更した後、認識の準備がまだ非常に低いことがわかりました。
例: 次に、MNIST データセットを使用して再度検証します。
ステップ 1: データセットをダウンロードし、モデルをトレーニングし、モデルをローカルに保存します
コードは以下のように表示されます:
import cv2
import numpy as np
from keras.datasets import mnist
if __name__ == '__main__':
# 直接使用Keras载入的训练数据(60000, 28, 28) (60000,)
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
# 变换数据的形状并归一化
train_images = train_images.reshape(train_images.shape[0], -1) # (60000, 784)
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape(test_images.shape[0], -1)
test_images = test_images.astype('float32') / 255
print(test_images)
# 将标签数据转为float32
train_labels = train_labels.astype(np.float32)
test_labels = test_labels.astype(np.float32)
# 传入knn的训练数据形状为(60000, 784) 训练标签为(60000,)
# 创建knn对象
knn = cv2.ml.KNearest_create()
# 设置k值 默认的k=10
knn.setDefaultK(5)
# 设置是分类还是回归
knn.setIsClassifier(True)
# 开始训练,训练数据的形状为(60000, 784) 训练标签为(60000,),训练数据必须是float32类型,标签必须是int32类型,并且标签必须是单通道,不能是多通道,否则会报错
knn.train(train_images, cv2.ml.ROW_SAMPLE, train_labels)
# 手写数字识别保存的knn模型非常大 有两百多兆
knn.save('mnist_knn.xml')
# 进行模型准确率的测试 结果是一个元组 第一个值为数据1的结果
test_pre = knn.predict(test_images)
test_ret = test_pre[1]
# 计算准确率
test_ret = test_ret.reshape(-1, )
test_sum = (test_ret == test_labels)
print(test_sum)
acc = test_sum.mean()
print(acc)
検証モデル:
import cv2
import numpy as np
if __name__=='__main__':
#读取图片
img=cv2.imread('D:\\ai\\test\\6.png', 0) # 读取待识别图像
#重新设置图片大小
img=cv2.resize(img,(28,28))
cv2.imshow('img',img)
img_sw=img.copy()
#将数据类型由uint8转为float32
img=img.astype(np.float32)
#图片形状由(28,28)转为(784,)
img=img.reshape(-1,)
#增加一个维度变为(1,784)
img=img.reshape(1,-1)
#图片数据归一化
img=img/255
#载入knn模型
knn=cv2.ml.KNearest_load('mnist_knn.xml')
#进行预测
img_pre=knn.predict(img)
print('img_pre:',img_pre)
print(img_pre[0])
cv2.waitKey()
cv2.destroyAllWindows()
操作結果:
ここでテストした画像は、圧縮形式でダウンロードされた mnist データ セットからのものです。検証したい場合は、下の画像からスクリーンショットを撮り、検証用に個別に保存する必要があります。全体的な正解率は依然として比較的高いです。**ただし、自分で数字を引いて検証すると、準備完了率は比較的低くなります。** 興味のある友達は、自分でさらに試してみることができます。
以下の画像を表示するコード
import cv2
import numpy as np
from keras.datasets import mnist
from matplotlib import pyplot as plt
# 加载数据
(train_dataset, train_labels), (test_dataset, test_labels) = mnist.load_data()
train_labels = np.array(train_labels, dtype=np.int32)
# 打印数据集形状
print(train_dataset.shape, test_dataset.shape)
# 图像预览
for i in range(40):
plt.subplot(4, 10, i+1)
plt.imshow(train_dataset[i], cmap='gray')
plt.title(train_labels[i], fontsize=10)
plt.axis('off')
plt.show()
欠点: モデル ファイルが大きい、認識速度が遅い