【MATLAB】目标检测初探
文章目录
- 0 前言
- 1 目标检测概述
- 2 算法实践
- 2.1 YOLO v2
- 2.2 YOLO v3
- 3 项目实践
- 3.1 项目背景和数据集
- 3.2 实践结果
- 3.3 算法对比
- 4 工具箱与数据标注
- 5 总结
0 前言
之前因为项目原因,做了一个基于YOLOv5实现目标检测的程序,是基于Python做的,直接使用YOLOv5开源库,只需要导入数据训练再运行特定文件即可,简单的操作得到优秀的结果。因此当时没有仔细研究代码,更没有深究目标检测相关理论。最近学习了一些MATLAB中目标检测相关的内容,于是想结合官方文档和demo重新实现之前那个项目,作为一个入门参考。
1 目标检测概述
所谓目标检测(Object Detection),其核心任务是从图像或视频中识别并定位感兴趣的对象,这不仅要求高准确率的分类,还需要精确的位置预测。相比于图像分类(Classification)只需要知道图片中是什么(what),目标检测既要知道“what”,也要知道“where”。
目标检测经过二十多年的发展,从最初基于传统机器学习先提取特征,再进行图像分类,到现在使用卷积神经网络直接“端到端”,使特征提取自动化,同时也提高了目标检测的准确率。
目前基于深度学习实现目标检测主要有两类做法,一种称为两段式目标检测,即先通过一个网络筛选出感兴趣的区域(ROI),或者说存在目标可能性较大的区域,这一步叫做region proposal,然后再根据这些筛选出的region,判断其属于哪种类别,即图像分类,两段式的代表算法有R-CNN,SPPNet,Fast R-CNN,Faster R-CNN等;另一种方法叫做一段式目标检测,即将上述两个步骤合并到一起进行,没有单独的region proposal的步骤,两段式最具代表性的算法就是YOLO系列算法,目前已经发展出多个版本,从YOLO v1到YOLO v8(来自不同的研究团队),比较知名的还是YOLO v5.
所谓YOLO,即You Only Look Once,顾名思义,必然是一段式的
从研究情况来看,YOLO系列算法更受欢迎,各个YOLO系列算法也有各自的开源仓库(当然,都是Python的),目前MATLAB中已经支持YOLO v2,YOLO v3,YOLO v4,但没有和python开源仓库那种比较完善的,因此想基于帮助文档进行代码实践,对已有函数进行再次封装,提高其易用性的情况下测试一下我之前一个项目的数据集,并比较各算法优劣。
参考链接
2 算法实践
2.1 YOLO v2
首先来看MATLAB自带的一个示例,在命令行输入help help
,打开帮助文档,直接搜索YOLO,找到这个链接:
点开之后可以得到一个训练检测车辆目标的demo:
上述demo链接直达
依次运行该代码,可以发现其实核心主要有三个步骤:准备数据,导入模型,设置训练参数,这也是训练核心函数trainYOLOv2ObjectDetector
的必要参数。
trainYOLOv2ObjectDetector详解
接下来依次实现。其带注解代码如下所示
clear,clc
% 导入训练数据
data = load('vehicleTrainingData.mat');
trainingData = data.vehicleTrainingData;
% 对数据进行处理——相对路径转换成绝对路径
dataDir = fullfile(toolboxdir('vision'),'visiondata');
trainingData.imageFilename = fullfile(dataDir,trainingData.imageFilename);
% 对数据进行处理——打乱数据,便于训练
rng(0); % 固定随机种子,保证运行结果可复现
shuffledIdx = randperm(height(trainingData));
trainingData = trainingData(shuffledIdx,:);
% 构建数据结构——datastore
imds = imageDatastore(trainingData.imageFilename); % 图片数据存储
blds = boxLabelDatastore(trainingData(:,2:end)); % 标注数据存储
ds = combine(imds, blds); % 将两个结合起来
% 导入网络
net = load('yolov2VehicleDetector.mat');
lgraph = net.lgraph;
% 设置训练选项
options = trainingOptions('sgdm',... % 算法选择
'InitialLearnRate',0.001,... % 初始学习率
'Verbose',true,... % 是否输出训练信息
'VerboseFrequency',30,... % 输出训练信息的频率
'MiniBatchSize',16,... % 批量大小
'MaxEpochs',30,... % 最大迭代次数
'Shuffle','never',... % 训练前是否打乱数据
'CheckpointPath',"./yolov2-checkpoint"); % 检查点保存所在位置
% 开始训练
[detector,info] = trainYOLOv2ObjectDetector(ds,lgraph,options);
% 显示训练过程损失变化曲线
figure
plot(info.TrainingLoss)
grid on
xlabel('Number of Iterations')
ylabel('Training Loss for Each Iteration')
% 运行推理,进行检测
img = imread('detectcars.png');
[bboxes,scores] = detect(detector,img);
if(~isempty(bboxes)) % 如果检测到目标,就将标注框加上
img = insertObjectAnnotation(img,'rectangle',bboxes,scores);
end
figure
imshow(img)
运行该代码,训练时间约3min(CPU),最终得到示例图片的检测结果:
从结果上来看,效果还是挺不错的,识别准确度较高,而且LOSS曲线下降也非常快速。
2.2 YOLO v3
和YOLO v2相比,YOLO v3的基本步骤也差不多,也是上面提到的关键三步:准备数据,导入模型,设置训练参数,其中,准备数据基本是一致的,二者的数据结构基本差不多。只是在模型选择和训练参数设置上有差异。此处不再赘述,可以直接参考官方文档
需要注意的是,实测发现YOLO v3不支持较低版本的MATLAB,如2019b,至少是2020a及以上的才行。
3 项目实践
3.1 项目背景和数据集
在工厂生产瓶装水的流水线中,最后将瓶装水打包为一箱的这一步也是通过流水线完成的,但是在实际运行中可能会出现一个问题,那就是有倒下的水瓶,目前针对该问题的解决方式是依靠整理水瓶的最后一步如果有倒下的水瓶,机器就会卡住并报警,通知员工来将倒下的水瓶给扶起来,等待正常之后继续运行。这样就耽误了生产时间,生产效率大大下降。如何提高生产效率,避免出现打包机遇到倒瓶自动暂停的现象,有一个解决思路就是在流水线的上游设置一个倒瓶检测装置,当检测到倒瓶时,发出报警,达到提前警示,节约时间的效果,从而提高生产效率。
为了节约成本,肯定是不能随便修改生产线的,所以最佳的方式是用视觉传感器,即通过摄像头检测有没有倒瓶,有就发出报警,通知员工来调整。因此这个问题本质上是一个目标检测的问题。
实践过程中,首先需要固定摄像头,运行流水线,并采集有倒瓶的照片,并进行标注,再基于标注完成的数据集进行训练,得到训练好的模型再部署到设备当中,完成算法开发。
数据集一共有236张照片,照片基于工业相机拍摄,像素较高,是1624×1240,也存在几张照片中不含有待检测目标,即负样本。数据集如下图所示。
当时为了满足YOLO v5对于数据集结构的要求,因此也将该数据集整理成YOLOv5的格式:
YOLO Data
|-- test_data
| |-- 0.bmp
| |-- 1.bmp
| |-- ...
`-- train_data
|-- bottle.yaml
|-- images
| |-- train
| | |-- IMG_1.bmp
| | |-- IMG_10.bmp
| | |-- ...
| `-- val
| |-- IMG_101.bmp
| |-- IMG_103.bmp
| |-- ...
`-- labels
|-- classes.txt
|-- train
| |-- IMG_1.txt
| |-- IMG_10.txt
|-- val
| |-- IMG_101.txt
| |-- IMG_103.txt
| |-- ...
3.2 实践结果
参考上述YOLO v2的demo,只需要更换数据集以及检测样例即可,代码如下所示。
clear,clc
% 图片数据
filename = mfilename('fullpath'); % 返回当前脚本文件所在路径,直接运行节不可行
imgdir = fullfile(pwd, "./YOLOData/train_data/images/train");
imglist = dir(fullfile(imgdir, "*.bmp"));
lbldir = fullfile(pwd, "./YOLOData/train_data/labels/train");
lbllist = dir(fullfile(lbldir, "*.txt"));
len = length(imglist); % 数据个数
img = cell(len,1);
label = cell(len,1);
k = 1;
for i=1:len
img_tmp = fullfile(imglist(i,:).folder, imglist(i,:).name);
info = imfinfo(img_tmp);
width1 = info.Width; height1 = info.Height;
label_i = fullfile(lbllist(i,:).folder, lbllist(i,:).name);
a = load(label_i);
if isempty(a)
continue;
end
a = a(:,2:end);
a(:,[1,3]) = round(a(:,[1,3])*width1);
a(:,[2,4]) = round(a(:,[2,4])*height1);
a(:,1) = round(a(:,1) - a(:,3)/2); % 中心点转换为左上角点
a(:,2) = round(a(:,2) - a(:,4)/2);
img{k} = img_tmp;
label{k} = a;
k = k + 1;
end
img = img(1:k-1,:); % 用圆括号索引,得到的还是cell
label = label(1:k-1,:);
trainingData = table(img, label, 'VariableNames',{'Image','Label'});
% 对数据进行处理——打乱数据,便于训练
rng(0); % 固定随机种子,保证运行结果可复现
shuffledIdx = randperm(height(trainingData));
trainingData = trainingData(shuffledIdx,:);
% 构建数据结构——datastore
imds = imageDatastore(trainingData.Image); % 图片数据存储
blds = boxLabelDatastore(trainingData(:,2:end)); % 标注数据存储
ds = combine(imds, blds); % 将两个结合起来
% 导入网络
net = load('yolov2VehicleDetector.mat');
lgraph = net.lgraph; % 这个网络来自预训练好的模型,和训练数据关联不大
% 设置训练选项
options = trainingOptions('sgdm',... % 算法选择
'InitialLearnRate',0.001,... % 初始学习率
'Verbose',true,... % 是否输出训练信息
'VerboseFrequency',30,... % 输出训练信息的频率
'MiniBatchSize',16,... % 批量大小
'MaxEpochs',30,... % 最大迭代次数
'Shuffle','never',... % 训练前是否打乱数据
'CheckpointPath',"./yolov2-checkpoint"); % 检查点保存所在位置
% 开始训练
[detector,info] = trainYOLOv2ObjectDetector(ds,lgraph,options);
% 显示训练过程损失变化曲线
figure
plot(info.TrainingLoss)
grid on
xlabel('Number of Iterations')
ylabel('Training Loss for Each Iteration')
%% 运行推理,进行检测
testfilename = "783.bmp";
testfiledir = 'F:\MATLAB\YOLOData\test_data';
img1 = imread(fullfile(testfiledir, testfilename));
[bboxes,scores] = detect(detector,img1);
if(~isempty(bboxes)) % 如果检测到目标,就将标注框加上
img1 = insertObjectAnnotation(img1,'rectangle',bboxes,scores);
end
figure
imshow(img1)
由于这个图片数量更多,size也更大,所以训练时间更长一点,一共是10min(CPU),检测结果如下图所示,效果还算可以,但是置信度不高。
3.3 算法对比
YOLOv2和YOLOv3在相对简单的数据集(大小适中,目标清晰)性能差别不明显,效果都比较好,从给出的demo就能看出,但是在相对复杂的数据集中表现一般。
另外,和YOLO v5相比,其性能差别也比较明显,如下图所示是YOLO v5识别的效果
4 工具箱与数据标注
以上是直接利用现成的标注好的数据,如果是未标注的数据,也可以利用MATLAB自带的工具箱进行标注,且既可以标注图片,也可以标注视频。使用方式比较简单,直接在APP页面找到对应功能按钮打开即可。
而且对于视频标注,还可以利用其内置算法实现全自动或半自动标注,非常快捷,具体可以参考这个视频。
不过需要尤为注意的是,标注文件数据的格式问题。之前使用YOLOv5,用labelimg进行标注,得到的标注文件是五列:
[类别编号 中心点x坐标 中心点y坐标 目标宽度 目标长度]
,且体现的数值是归一化之后的,即除以图片的长或宽。但是在MATLAB中的标注是[类别编号 左上角x坐标 左上角y坐标 目标宽度 目标长度]
,因此二者差了一半长/宽,一定注意,否则会产生报错。
5 总结
本文从理论出发,介绍了目标检测的几种类别以及其各自的算法理念,并基于MATLAB平台实践了当下热门的YOLO算法,并总结出如何将python下的数据集转换成MATLAB下需要的数据集。为了检验其模型的性能,分别训练并测试了不同的数据集,发现YOLOv2版本的模型检测准确度等性能指标在复杂数据集面前偏低,参考官方的介绍,重新构建深度学习网络,可以在一定程度上提高模型的性能。