概述
本博客是我的Github
上的NAO_GolfVision_ML项目的使用说明。该项目是NAO
高尔夫比赛中的视觉系统设计,主要是利用opencv
和机器学习算法对视觉系统中的目标进行分类检测。详细的代码解释见另一篇博客:NAO比赛视觉系统设计
平台
Windows 10
python2.7 32(NAO支持的版本)
文件夹结构
该文件夹主要分为三个部分,第一部分是数据集,包含图片和标签的数据集,其中图片为NAO摄像头实际拍摄的图像,像素大小为640*480
,标签为labelImg
软件标注生成的xml
文件,其中正负样本分开存放。
第二部分是代码,其中TargetDetection.py
和TargetFeature.py
是opnecv
对目标的检测和机器学习对目标的特征提取文件,Classifier,py
和ClassifierTrain,py
是机器学习的分类器和训练文件。其中ClassifierTrain,py
是主函数文件。
第三部分是其他文件,主要包含3张测试图片,即足球、红球和黄杆,3张数据文件,即总数据,正样本数据和负样本数据,另外包含一个renamefile.py
,主要是为了统一图片和标签的名字。
TargetDetection.py
该文件包含3个类,其中TargetDetection
是基类,HoughDetection
是霍夫圆检测类,ContoursDetection
是轮廓检测类。读者也可以使用其他检测算法新建自己的检测类。
TargetDetection
该类包含图像预处理函数preProcess()
,滤波函数filter()
和滑动条函数sliderObjectHSV()
。
使用方法
1 | if __name__ == '__main__': |
首先打开一张测试图片,实际比赛中可以将此替换为NAO拍摄的图片,然后实例化类,并调用滑动条函数。注意图片名字和类别名字要一致。
不断调整滑动条的参数,以得到理想效果。
HoughDetection
该类包含霍夫圆检测函数houghDetection()
,信息转换函数circle2Rect()
,显示结果函数showHoughResult()
和霍夫圆检测滑动条函数houghSlider()
。
使用方法
1 | if __name__ == '__main__': |
不断调整滑动条的参数,以得到理想效果。
ContoursDetection
该类包含轮廓检测函数contoursDetection()
,信息转换函数contour2Rect()
,显示结果函数showContourResult()
和轮廓检测滑动条函数contoursSlider()
。
使用方法
1 | if __name__ == '__main__': |
不断调整滑动条的参数,以得到理想效果。
TargetFeature.py
该文件包含2个类,其中HogFeature
是提取HOG
特征,ColorFeature
是提取颜色特征。读者也可以使用其他特征提取算法。最后统一成向量的形式即可。
使用方法
这部分主要是结合之后的分类器训练使用。
Classifier.py
该文件包含2个类,其中Logistic
是逻辑回归分类器,KNN
是K近邻分类器。读者也可以使用自己的分类器。
使用方法
这部分主要是结合之后的分类器训练使用。
ClassifierTrain.py
该文件包含以下函数:
parseXml()
:解析标注文件函数,reshapeBallRect()
:重造球类目标矩形框函数,reshapeStickRect()
:重造黄杆类目标矩形框函数,circle2Rect()
:转换信息函数,calColorFeature()
:计算颜色特征函数,calHOGFeature()
:计算HOG
特征函数,calPosVector()
:计算正样本向量函数calNegVector()
:计算负样本向量函数resultTest()
:分类结果测试函数
使用方法
首先将数据集正确的放入到文件夹中,越多越好,至少上百张,其次更改calPosVector()
函数里面的路径位置及信息。
其中画图部分的函数,即:1
2
3
4
5 # cv2.rectangle(srcImg, (newInitX, newInitY), (newEndX, newEndY), (0, 0, 255), 2) # 画矩形
# cv2.imshow("test " + str(i), srcImg)
# cv2.waitKey(300)
# cv2.destroyAllWindows()
在实际训练时需要注释掉,否则会把标注框也认为是正样本。该部分主要是为了测试标注框是否准确,读者可以先保留该部分运行一遍,再注释后运行一遍,实际的特征向量以加了注释后的为准。
1 | if __name__ == '__main__': |
运行过程中,会显示出特征向量的总数,如果是320
则是正确的,当然了如果读者自己加入了其他的特征提取,可以自己算一遍。
最后所有的特征向量会存放在data_pos.txt
的文件夹下。
负样本的使用方法同上。其最终的特征向量会存放在data_neg.txt
文件夹下,最后将这2个txt
文件的数据合并到data.txt
文件中。
最后再调用测试分类结果函数resultTest()
即可,在里面输入相应的分类器,当然也可以是自己的。
最终结果如图所示,红色为正确的,黄色为错误的。实际测试发现,只要目标不在边界上,其正确率几乎可以达到100%,对于边界上的情况,个别几个情况分类错误,但这对比赛也并没有太大的影响。整体效果还是非常不错的,可以达到比赛的实时检测要求。
注:我这里只对足球类目标进行了检测,没用对红球和黄杆测试,读者有兴趣的话可以自己采集数据并测试一下,欢迎大家留言讨论。
附:sklearn机器学习库实现分类器
这里提供一个强大的机器学习库sklearn
来实现之前的分类器,首先需要通过pip
安装(pip install -U scikit-learn
),由于NAO
本身并不支持这个第三方库,所以我们如果要使用的话,需要将下载好的sklearn
库上传至NAO
中。
和之前一样,还是新建一个py
文件,里面可以新建若干各类,每个类实现一个分类器。
实现原理
sklearn
实现机器学习算法特别简单,大致可以分为三步,1.读取数据,2.构建分类器并训练参数,3.使用分类器预测。
下面以Logistic
回归为例,详细讲解:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52import sklearn
from sklearn.linear_model import LogisticRegressionCV, LinearRegression
from sklearn.model_selection import train_test_split
import numpy as np
from sklearn.externals import joblib
class LogisticSk(object):
def __init__(self, filename):
self.filename = filename
def file2matrix(self):
fr = open(self.filename)
arrayOfLines = fr.readlines()
numberOfLines = len(arrayOfLines)
returnMat = np.zeros((numberOfLines, 320))
classLabelVector = []
index = 0
for line in arrayOfLines:
line = line.strip()
listFromLine = line.split(' ')
returnMat[index, :] = listFromLine[0:320]
if listFromLine[-1] == '0':
classLabelVector.append(0)
elif listFromLine[-1] == '1':
classLabelVector.append(1)
index += 1
return returnMat, classLabelVector
def trainClassify(self):
X, Y = self.file2matrix()
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.1, random_state=0)
lr = LogisticRegressionCV(multi_class="ovr", fit_intercept=True, Cs=np.logspace(-2, 2, 20), cv=2, penalty="l2", solver="lbfgs", tol=0.01)
lr.fit(X_train, Y_train)
return lr
def saveClassify(self, model, path):
joblib.dump(model, path)
def readClassify(self, path):
return joblib.load(path)
def predictClassify(self, lr, X_test):
Y_predict = lr.predict(X_test)
return Y_predict
这里为了和之前的变量名不一样,在分类器后面加了SK
,表示是用sklearn
库实现的。
读取数据
这部分可以参考之前的file2matrix()
函数。
构建分类器并训练
在构建分类器之前,我们可以先用train_test_split()
函数将数据集分为训练集和测试集,以便后续的分析(当然也可以直接用原来的数据)。
接下来就是使用sklearn
库中自带的分类器训练,这里注意首先要将分类器所在的类导入进来,比如这里是LogisticRegressionCV()
分类器函数,它是在sklearn.linear_model
类中的。然后可以设置分类器的参数,当然也可以使用默认值。最后使用fit()
函数训练以得到相应的参数。
保存和读取模型
训练一次数据集通常需要花费一定的时间,这对实时性的要求显然是不利的,所以通常的做法是先将训练好的模型保存下来,然后再读取。
sklearn
中保存和读取的模块是joblib
(也有其他的),其dump()
和load()
函数分别是读取和保存。
注:保存的模型一般后缀名为.m
。
使用模型预测
使用predict()
函数预测即可,输入参数为待预测的数据。
结果分析
实际测试下来发现,sklearn
库的分类器函数和自己写的分类器的效果几乎差不多,总体效果还是比较好的。