本帖最后由 IvanDMido 于 2022-11-25 18:42 编辑
基于行空板的物体分类项目(水果识别)
前言
应两位小伙伴要求,将原“智能快递柜机器人”项目中的物体分类功能摘出,作一分享。
项目目标
用人工智能中的物体分类技术,对三种不同的水果(苹果、香蕉、西瓜)进行识别分类。
项目器材
行空板,USB摄像头
项目流程1、采集拍取要识别的三类水果的图片集 2、利用已有的一个基础物体分类模型,结合采集的图片数据集,训练一个包含这三类图片的新模型 3、借助生成的模型,预测某一类水果
项目实践
STEP0硬件连接
将USB摄像头连上行空板。
STEP1摄像头采集图像
一、功能描述
在这个任务中,将采集苹果、香蕉、西瓜、以及白色背景四种类型的图像,并存储到当前路径下“dataset_object_classification”目录中的“01Apple”、“02Banana”、“03Watermelon”、“04Others”四个子目录内,这些目录自动生成。
二、编写程序
- '''拍照,采集图像作为数据集;
- 运行程序,终端输入1,表示采集"01Apple"数据集,待摄像头的画面启动后,按住按键a进行持续拍照,终端出现“image saved”字样,表示100张图片已采集完成”
- 之后重复上述操作,可采集02、03、04类数据集'''
- import cv2 # 导入cv库
- import os
-
- os.system('mkdir -p dataset_object_classification/01Apple') # 创建文件夹以保存采集的图像
- os.system('mkdir -p dataset_object_classification/02Banana') # 创建文件夹以保存采集的图像
- os.system('mkdir -p dataset_object_classification/03Watermelon') # 创建文件夹以保存采集的图像
- os.system('mkdir -p dataset_object_classification/04Others') # 创建文件夹以保存采集的图像
-
-
- number = input("请输入数据集文件夹编号:")
- if number == "1":
- location = 'dataset_object_classification/01Apple/'
- elif number == "2":
- location = 'dataset_object_classification/02Banana/'
- elif number == "3":
- location = 'dataset_object_classification/03Watermelon/'
- elif number == "4":
- location = 'dataset_object_classification/04Others/'
- print(location)
-
- def get_photo(): # 定义拍照函数(上传参数--图片保存地址)
- cap = cv2.VideoCapture(0) # 创建一个 VideoCapture 对象, 构建视频抓捕器, 0表示需要启动的摄像头
- cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) # Set the camera buffer to 1, to decrease the latency. # 设置1帧的缓冲,减少延迟避免卡顿
- cv2.namedWindow('window',cv2.WND_PROP_FULLSCREEN) # Set the windows to be full screen. # 构建一个窗口,名称为window,属性为可以全屏
- cv2.setWindowProperty('window', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) # Set the windows to be full screen. # 设置窗口全屏
- count = 0
-
- while (cap.isOpened()): # 循环读取每一帧
- ret, frame = cap.read() # 从摄像头读取图片 , ret存储布尔值,frame存储图像
- #frame = cv2.flip(frame, 1, dst=None) # 镜像
- cv2.imshow("window", frame) # 窗口显示,显示名为 window
- # 保持画面的持续。
- key = cv2.waitKey(1) # 每帧数据延时1ms,延时不能为 0,否则读取的结果会是静态帧
- if key & 0xFF == ord('b'): # 按b键退出
- break
- elif key & 0xFF == ord('a'): # 通过a键保存图片,并退出。
- if count < 100: # 100张图片,可自行修改数量
- global location
- cv2.imwrite(location+ str(count) + ".jpg", frame) # 将图像保存为图片
- count = count + 1
- # print(count)
- else:
- print("image saved")
- cap.release() # 释放摄像头
- cv2.destroyAllWindows() # 关闭所有窗口
-
- get_photo() # 调用拍照函数,同时传入要保存的图片名称
复制代码
三、运行程序 A:运行程序,在终端输入1(表示将采集的图像存储到“01Apple”目录),随后将摄像头对准苹果的图片,待画面启动后,按住按键a进行持续拍照,终端出现“image saved”字样后,100张图片即采集完成”,再按下按键b退出摄像头画面。这里,我们采集苹果的图片集。 操作过程视频:
B:之后用同样的方式,分别在终端输入2,3,4在“02Banana”、“03Watermelon”、“04Others”目录中拍摄并存储香蕉、西瓜、以及白色背景(用白色背景模拟其他物体)三种图片集。 结果视频:
STEP2用神经网络训练物体模型
一、功能描述 将采集的四组图片数据集用神经网络训练出物体模型。
二、编写程序 - '''训练模型,将采集的图片数据集用神经网络训练出物体模型;
- 运行程序,待计算机自动训练,时间约1-5分钟不等,完成后,终端会显示“模型保存完成!”字样,并在当前文件目录下会自动生成“object_classification_model.h5”模型文件。'''
- # 导入tensorflow深度学习框架
- import tensorflow as tf
- # 导入keras深度学习库
- from tensorflow import keras
- # 导入数据处理库
- import numpy as np
-
-
- # 数据预处理
- # 将图片处理成预训练模型可以输入的格式
- # 定义数据集路径
- train_dir = 'dataset_object_classification'
-
- # ImageDataGenerator为Keras的图像生成器,用于预处理图像数据
- # mobilenet_v2.preprocess_input为mobilenet_v2的输入图像处理方法,用于将图像处理成适合mobilenet_v2模型的格式
- datagen = keras.preprocessing.image.ImageDataGenerator(preprocessing_function=keras.applications.mobilenet_v2.preprocess_input)
-
- # 图像生成器的flow_from_directory方法,负责生成批量数据,供模型训练
- train_batches = datagen.flow_from_directory(
- directory=train_dir, # directory:目标文件夹路径,在目标文件夹中,包含多个子文件夹,每个子文件夹都是一类,包含一类图像数据
- shuffle=True, # shuffle:是否打乱数据,设置为True,表示打乱每个子文件夹的数据处理顺序,否则按照子文件夹中图片名称的字母顺序处理数据
- target_size=(96,96), # target_size:目标尺寸,所有图像将被重置为目标尺寸
- batch_size=10) # batch_size:数据批次大小,表示一批数据中包含的数据量,预处理时,会按批次处理数据
-
- # train_batches返回的参数包括输入数据和标签数据
-
- # 冻结预训练模型
-
- # 基础模型用的是mobilenet_v2,输入图像尺寸为 224*224
- base_model = keras.models.load_model("mobilenet_v2_96.h5",compile=False) # 导入预训练模型
- base_model.trainable = False # 冻结预训练模型
-
- # 这里的模型不含输出层,只有输入层和隐藏层
- # 冻结后,权重等参数不再改变
- # 冻结后的模型相当于只含输入层的特征提取器
-
-
- # 创建神经网络
-
- # 实例化一个神经网络模型
- model = keras.Sequential(name="object_classification_model")
-
- # 创建输入层
- model.add(base_model) # 定义输入层为预训练模型,输入尺寸即为 224*224
- # 创建隐藏层
- model.add(keras.layers.Dense(100, activation='relu')) # 创建一层隐藏层,包括100个神经元
- # 创建输出层
- model.add(keras.layers.Dense(4, activation='softmax')) # 定义输出层为4分类(种类数量)
-
- # # 查看模型结构
- # print("模型结构:")
- # model.summary()
-
-
- # 训练模型
-
- # 设置参数
- model.compile(
- optimizer=keras.optimizers.Adam(0.0001),
- loss='categorical_crossentropy',
- metrics=['accuracy']
- )
-
- # 训练模型
- print("开始训练:")
- model.fit(train_batches, epochs=5) # 设置5轮训练
- print("训练完成!")
-
- # 保存模型
- model.save("object_classification_model.h5")
- print("模型保存完成!")
复制代码
三、运行程序 Tips:如果出现没有库的一些报错,如tensorflow等,可通过pip install的方式自行安装。
A:运行程序,等待行空板自动训练模型(时间约1-5分钟不等,图片越多时间越久)。 训练过程视频
B:训练完成后,会在终端显示“模型保存完成”,并在程序路径下生成对应的模型文件。
STEP3用神经网络预测单张图片一、功能描述 用“dataset_object_classification/03Watermelon/2.jpg”图片进行预测,检验模型准确率。
二、编写程序 - '''用“dataset_object_classification/03Watermelon/2.jpg”图片进行预测,检验模型准确率;
- 运行程序,待计算机自动导入模型进行预测,随后会在终端显示该图片在四个类别下的不同分类概率,以及取概率最高的类别作为最终结果
- PS:本程序非必须'''
-
- # 导入tensorflow深度学习框架
- import tensorflow as tf
- # 导入keras深度学习库
- from tensorflow import keras
- # 导入数据处理库
- import numpy as np
-
-
- # 导入物体分类模型
- print("导入模型中...")
- model_name = "object_classification_model.h5"
- model = tf.keras.models.load_model(model_name)
- print("{}模型导入完成".format(model_name))
-
-
- # 输出预测结果
- img = keras.preprocessing.image.load_img("dataset_object_classification/01Apple/2.jpg", target_size=(96, 96))
- img2 = keras.preprocessing.image.load_img("dataset_object_classification/02Banana/2.jpg", target_size=(96, 96))
- img3 =keras.preprocessing.image.load_img("dataset_object_classification/03Watermelon/2.jpg", target_size=(96, 96))
- img4 =keras.preprocessing.image.load_img("dataset_object_classification/04Others/2.jpg", target_size=(96, 96))
- # 图像预处理
- img_array = keras.preprocessing.image.img_to_array(img3) # 将图像数据转换为数组 # 用img3进行预测,可自行修改
- img_array = keras.applications.mobilenet_v2.preprocess_input(img_array) # 将数组处理成适合mobilenet_v2的输入格式
- img_array = tf.expand_dims(img_array, 0) # 在图像数组前面增加一维,即将 (224,224,3) 的图像矩阵变成 (1,224,224,3)
-
- # 定义分类结果的名称,按照数据集中的文件夹顺序,神经网络的输出依次为01-04
- class_names = ["01Apple","02Banana","03Watermelon","04Others"]
-
- # 预测
- print("预测结果为:")
- predictions = model.predict(img_array) # 预测图像(整体概率)
- print(predictions)
- result = class_names[predictions.argmax()] # 预测结果(概率最高者)
- print(result)
复制代码
三、运行程序
A:运行程序,等待行空板加载模型并进行预测 预测过程视频
结果图示
STEP4用神经网络预测视频流
一、功能描述
实时显示图像的预测结果。
二、编写程序
- '''在Pyboard屏幕上显示视频流画面并实时预测结果'''
-
- # 导入tensorflow深度学习框架
- import tensorflow as tf
- # 导入keras深度学习库
- from tensorflow import keras
- # 导入OpenCV计算机视觉库
- import cv2
- # 导入数据处理库
- import numpy as np
-
-
- # 导入物体分类模型
- print("导入模型中...")
- model_name = "object_classification_model.h5"
- model = tf.keras.models.load_model(model_name)
- print("{}模型导入完成".format(model_name))
-
- # 图像预处理
- def preprocess_img(frame):
- img = tf.image.resize(frame, (96, 96)) # 重置尺寸
- img_array = keras.preprocessing.image.img_to_array(img) # 将图像转为数组
- img_array = tf.expand_dims(img_array, 0) # 将图像数组增加一维,匹配模型输入
- img_array = img_array/255. # 归一化
- return img_array # 返回预处理后的图像数据
-
-
-
- # 绘制分类结果
- def add_data(frame,predictions):
- res = frame # 获取一帧图像
- box_startpoint = (30,10) # 定义坐标
- class_names = ["01Apple","02Banana","03Watermelon","04Others"] # 定义分类结果
-
- # # 在屏幕左上方,绘制各个分类结果的置信度直方图
- # for idx,pred in enumerate(predictions.numpy()[0]):
- # # 绘制文字
- # res = cv2.putText(res,
- # str(class_names[idx]),
- # (box_startpoint[0]-20, box_startpoint[1]+idx*40+10), #+20
- # cv2.FONT_HERSHEY_SIMPLEX, 0.5,
- # (0, 0, 255),
- # 2,
- # cv2.LINE_4)
- # # 绘制直方图
- # res = cv2.rectangle(res,
- # (box_startpoint[0]+70,box_startpoint[1]+idx*40),
- # (int(box_startpoint[0]+100*np.around(predictions.numpy(),2)[0][idx])+70,box_startpoint[1]+idx*40+15),
- # (0,0,255),
- # -1)
- # 绘制直方图
- res = cv2.rectangle(res,
- (0,0),
- (160,35),
- (255,255,255),
- -1)
- # 在屏幕下方,绘制预测结果"predicted result :"+
- res = cv2.putText(res,str(class_names[predictions.numpy().argmax()]),
- (0,25), # coor
- cv2.FONT_HERSHEY_SIMPLEX, 1,
- (255, 0, 0),
- 2,
- cv2.LINE_4)
-
-
- res = str(class_names[predictions.numpy().argmax()])
- return res
-
- # 初始化屏幕
- window_name = 'frame'
- cv2.namedWindow(window_name, cv2.WND_PROP_FULLSCREEN)
- cv2.setWindowProperty(window_name, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
- # whole_frame = np.zeros((480,800,3), dtype="uint8")
-
- # 初始化摄像头
- while True:
- cap = cv2.VideoCapture(0) # 获取摄像头图像
- cap.set(cv2.CAP_PROP_FRAME_WIDTH, 240)
- cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 320)
- cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) # 设置内部缓冲存储器中的帧数量
- if cap is not None: # 如果没有连接摄像头,就等待
- break
-
- while True:
- # 逐帧捕获摄像头的图像
- ret, frame = cap.read() # ret为True或False,表示有没有读到图像,frame表示当前截取一帧的图像
- img_array = preprocess_img(frame) # 对一帧图像做预处理
- predictions = model.predict(img_array) # 模型预测(整体概率)
- predictions = tf.nn.softmax(predictions) # 输出预测结果
- new_frame = add_data(frame,predictions) # 对每一帧图像绘制分类结果
- # frame=cv2ImgAddText(frame,new_frame, (10, 30),(0, 255, 0), 30)
- print(new_frame)
-
- # 显示图像
- cv2.imshow('frame',frame)
-
- # 按下熊猫键盘“A”键,退出程序
- if cv2.waitKey(1) & 0xFF == ord('b'):
- break
-
- #清空显示
- cap.release()
- cv2.destroyAllWindows()
复制代码
三、运行程序
过程视频
小结
由于时间有限,对于程序的解释较为简单,但关于物体分类的总体操作思路不变,即“采集图像--训练模型--预测图像”,这也是很多人工智能项目普遍的操作流程。最后,希望本文能给你带来一些收获和启发,如果对相关的技术感兴趣,也欢迎作进一步研究并将你的成果开源出来。(PS:最后的最后,也感谢一下在原“智能快递柜机器人”项目中一起研讨并提供help的小伙伴。)
|