前几天有同学问我关于物体识别的问题,问我是否能写一篇相关的教程,其实最近有在做这方面的事情,我做的是一个检测指定物体,画出框框出制定物体,并进行测距,具体的应用场景是检测红绿灯,自动通过路口,其中就用到了物体识别,当然还涉及到单目测距之类的知识,这些暂且不表,下次再说。
之前就知道OpenCV有一个专门的Machine Learning库,一直没有机会学习,想着通过这次机会,正好学习一下,没成想,进了一个巨大的坑,因为大家都知道,Python是胶水语言,所以Python-OpenCV的底层也都是C语言写的,而我C语言又比较差,网上的教程有比较少,所以看官方文档的时候真的是一番煎熬,下次有时间还是试一试主流的一下机器学习框架吧,正好做一个对比,看哪一个比较好。
CV.ml介绍
集成了许多优秀的机器学习算法,主要有:
- cv2.ml.svm---------------------支持向量机
- cv2.ml.knn---------------------K.近邻
- cv2.ml.bayesian----------------正态贝叶斯分类器
- cv2.ml.em----------------------期望最大化
- cv2.ml.boost.tree--------------boost分类器
- cv2.ml.tree--------------------决策树分类器
- cv2.ml.ann.mlp-----------------感知器神经网络分类器
- cv2.ml.cnn---------------------卷积神经网络
- cv2.ml.random.trees-----------------随机树分类器
- cv2.ml.extremely.randomized.trees---随机森林分类器
- cv2.ml.gradient.boosting.trees------梯度boost分类器
具体的功能我就不一一介绍了,大家有兴趣的可以参看上面的官方文档,具体说说我用到的cv2.ml.ANN_MLP
cv2.ml.ANN_MLP
最好的学习一个新功能的方式就是去使用它,话不多说,直接上程序
1. 创建训练模型
这个模型是训练通过摄像头获取到的照片,来判断应该前进的方向,算是智能驾驶的一部分。
import cv2
model = cv2.ml.ANN_MLP_create()#建立模型
model.setActivationFunction(cv2.ml.ANN_MLP_SIGMOID_SYM)#设置激活函数为SIGMOID,其实就这一个可以选
model.setLayerSizes(np.int32([38400,32,4]))#设置层数,输入38400层(像素320*240),输出层4(上下左右四个方向),以及中间层32
model.setTermCriteria(( cv2.TERM_CRITERIA_COUNT | cv2.TERM_CRITERIA_EPS, 500, 0.0001 ))#设置终止条件
model.setTrainMethod(cv2.ml.ANN_MLP_BACKPROP)#设置训练方式为反向传播
model.setBackpropWeightScale(0.001) #设置反向传播中的一些参数
model.setBackpropMomentumScale(0.0) #设置反向传播中的一些参数
2.开始训练模型
载入数据
训练要有数据,数据是我之前拍好的,拍的时候获取两个参数,一是拍到的照片,二是拍照片的时候的前进方向,以此来进行训练,将拍好的照片以npz格式保存,然后进行读取。
import numpy as np
import glob
# load training data
image_array = np.zeros((1, 38400))
label_array = np.zeros((1, 4), 'float')
training_data = glob.glob('training_data/*.npz')
#开始一个个读取
for single_npz in training_data:
with np.load(single_npz) as data:
train_temp = data['train']#拍到的照片
train_labels_temp = data['train_labels']#照片对应的标签
image_array = np.vstack((image_array, train_temp))
label_array = np.vstack((label_array, train_labels_temp))
X = image_array[1:, :]
Y = label_array[1:, :]
数据分类
将数据按照一定的比例分为测试集和训练集,比例大概为2:8
from sklearn.model_selection import train_test_split
train, test, train_labels, test_labels = train_test_split(X, y, test_size=0.2)
开始训练
records = []
#建立一个列表,进行照片和标签的一一对应
for x in range(0, len(train)):
records.append(record(train[x], train_labels[x]))
#训练两个epoch
EPOCHS = 2
for e in range(0, EPOCHS):
print("Epoch %d:" % e)
for t, c in records:
#开始训练,cv2.ml.ROW_SAMPLE代表每一行是一个样本
num_iter = model.train(t, cv2.ml.ROW_SAMPLE, c)
3. 保存训练结果
# train data
ret_0, resp_0 = model.predict(train)
prediction_0 = resp_0.argmax(-1)
true_labels_0 = train_labels.argmax(-1)
train_rate = np.mean(prediction_0 == true_labels_0)
print('Train accuracy: ', "{0:.2f}%".format(train_rate * 100))
# test data
ret_1, resp_1 = model.predict(test)
prediction_1 = resp_1.argmax(-1)
true_labels_1 = test_labels.argmax(-1)
test_rate = np.mean(prediction_1 == true_labels_1)
print('Test accuracy: ', "{0:.2f}%".format(test_rate * 100))
# save model
model.save('mlp_xml/mlp.xml')
4. 模型的识别应用
#构建模型网络,一会进行调用
class NeuralNetwork(object):
def __init__(self, file_path):
self.model = cv2.ml.ANN_MLP_load(file_path)#载入训练好的模型
#预测拍的照片给出标签值
def predict(self, samples):
ret, resp = self.model.predict(samples)
return resp.argmax(-1)
下面进行调用,因为调用需要照片,因此我把它放在了另一个接收照片的类里面,照片的传递也可以写一篇教程,这次先不说
class VideoStream(object):
def __init__(self):
self.ct_socket = socket.socket()
self.ct_socket.bind(('0.0.0.0',collect_id))
self.ct_socket.listen(5)
self.connection = self.ct_socket.accept()[0].makefile('rb')
# 模型实例化,载入训练好的文件
self.model = NeuralNetwork('mlp_xml/mlp.xml')
def handle(self):
global prediction
stream_bytes = b' '
try:
while True:
stream_bytes += self.connection.read(1024)
first = stream_bytes.find(b'\xff\xd8')
last = stream_bytes.find(b'\xff\xd9')
if first != -1 and last != -1:
jpg = stream_bytes[first:last+2]
stream_bytes = stream_bytes[last+2:]
image = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8), cv2.IMREAD_UNCHANGED)
print('接受照片中')
prediction = self.model.predict(image)
print(prediction)#这个输出的值就是检测过的标签,然后通过判断这个标签是什么,就可以执行下面的操作了。