学生休息室

分享技术和现实生活的例子,学生可以如何使用MATLAB和Simulink在他们的日常项目#学生成功金宝app

深度Chimpact:深度估计的野生动物保护- MATLAB基准代码

正如我们在上一篇博文的MATLAB基准代码深度影响:野生动物保护挑战的深度估计

来自MathWorks工程开发组的James Drummond尝试了这个挑战,并编写了这个初始代码。在这里,他将谈谈他用来解决这个问题的光流+ CNN方法。他还将抛出一些提示和技巧之间,你可以如何改善这个代码和提高你的分数。

请查看下面的链接,以注册挑战,获得免费的MATLAB许可证,并访问讨论论坛以获得支持。金宝app

的数据

这项挑战的目的是在单目跟踪摄像机镜头中估计距离动物的距离。这些视频都是彩色和灰度的夜视和包含一系列不同的动物作为主题。

数据集由3500多个视频(将近200GB的数据)组成,分为训练集和测试集。这些存储在一个公共S3桶中。使用说明可以在竞赛中找到Data选项卡

每个视频都有一个独特的4个字母的名字,或者是。mp4或者。avi格式。提供标签("train_labels.csv“&”test_labels.csv),作为每个视频中特定时间戳到动物重心的距离。为了帮助加快数据处理速度,还提供了每个文件的下采样版本。

此外,您还提供“train_metadata.csv”“test_metadata.csv”文件。这些文件包括文件名等信息,关于摄像机位置的细节,以及在每个时间戳模型生成的动物边界框的估计。

有关数据集的详细信息,请查看问题描述在竞赛网页上。

MATLAB入门

我提供了一个MATLAB中的基本示例代码,作为开发的起点。在这段代码中,我使用光流对视频帧进行预处理,并使用预先训练过的CNN将这些图像输入基本的回归模型。然后,我将使用这个模型对测试数据进行深度估计,并以挑战所需的格式保存一个CSV文件。您也可以从以下网站下载此MATLAB入门代码这GitHub回购.这可以作为基本代码,在这里您可以开始分析数据,并致力于开发更有效、优化和准确的模型。此外,我还为下一步的调查提供了一些提示和建议。

所以,让我们开始这个挑战吧!

加载标签和元数据

第一步是加载关于将要使用的数据集的信息。从文件中访问变量值train_metadata.csvtrain_labels.csv,我将它们导入MATLAB使用再保险adtable功能:

标签= readtable(“train_labels.csv”);(标签)

元数据= readtable(“train_metadata.csv”);(元数据)

访问和处理视频文件

在MATLAB中的数据存储是一种方便的方法,用于处理和表示数据集合,这些数据集合太大,一次无法装入内存。它是用于读取单个文件或文件或数据集合的对象。数据存储充当具有相同结构和格式的数据的存储库。要了解更多关于不同数据存储的信息,请查看下面的文档:

  1. 开始使用数据存储
  2. “应用程序文件格式”选择“数据存储”
  3. 用于深度学习的数据存储

在这个博客中,我将使用imageDatastore加载S3桶中的视频。每个视频都是使用readVideo helper函数概述在下面的部分。在继续下一节之前,我将数据存储保存在tempdir或当前文件夹中的mat文件中。如果MAT文件已经存在,则从MAT文件加载数据存储,而不重新评估它们。

在这里,我使用向下采样的视频来节省带宽和处理时间。每个视频被压缩到每秒一帧,这就足够满足我的需要了。要访问完整的视频,需要替换S3桶上文件的URL。

tempimds = fullfile (tempdir imds.mat);if exist(tempds,'file') load(tempds,'imds') else imds = imageDatastore('s3://drivendata-competition-depth- estimate -public/ train_videos_downsampling /',…“ReadFcn”,@(文件名)readVideo(文件名、元数据“TrainingData”),…FileExtensions,{‘mp4’,‘.avi});保存(tempimds, imd);end files = imds.Files;

提示(可选)为了减少处理时间,您可以选择使用子集imageDatastore的初始调查:

rng (0);%Seed随机数生成器的可重复性idx = randperm(numel(files));imd = imds.subset (idx (1:10 0));%随机测试子集

提取视频帧和光流

一旦我有了imageDatastore,我就可以通过定义一个自定义读取函数来提取视频帧readVideo代码位于该文件的末尾),它首先使用VideoReader对象。由于下采样的视频每秒只有一帧,所以可以简单地对视频进行索引以提取所需的帧。

光流

光流是图像中物体视速度的分布。通过估计视频帧间的光流,可以测量视频中物体的速度。

对于每一个标记的帧,我计算光流与前一帧/秒相比。如果我们假设动物是在一个基本静止的背景下运动,光流会突出显示它们所在的位置,并为它们的运动提供一些背景信息。为了提高信噪比,使用所提供的包围盒估计生成感兴趣区域的二值掩码。这是用来代替简单的裁剪图像,以保留任何空间上下文。

有关使用的技术的更多资料,可浏览以下连结:

为了帮助后面的处理,每个图像都用它来自的视频和时间戳来命名。这里是视频aany.mp4的帧0的输出示例:

这清楚地显示了猴子的位置和大小相对于图像的其余部分,但蒙版避免了背景噪声。

为了生成完整的数据集,我使用下面的命令阅读所有的视频。在这里,我使用并行计算工具箱来加快处理速度,因为每个视频都可以独立读取。

洛桑国际管理发展学院。再保险adall("UseParallel", true);

神经网络的设计

在这个例子中,我将使用预先训练过的网络ResNet-18进行迁移学习。然而,在你可以进步到实际的学习之前,网络需要适应我们的需求。特别是,需要替换输入和输出层。ResNet-18以224x224x3的图像输入,输出1000个类别的图像分类。然而,我将输入480×640图像并输出一个单一的回归距离估计。

在MATLAB中,这些变化可以通过两种方式实现,或通过编程或图形化使用深度网络设计师app

两种方法都是从安装ResNet-18网络的深度学习工具箱模型扩展浏览器

方法1:使用深度网络设计器

这个应用程序可以在应用程序ribbon中找到,或者通过运行命令:

deepNetworkDesigner

从启动页面,导入预先训练的ResNet-18网络:

在输入端,我替换了224x224x3imageInputLayer使用我自己的480×640 imageInputLayer。为了连接到网络的其余部分,图像将需要使用resize3dLayer图层OutputSize为480x640x3。

接下来在输出端,我删除了最后的几个层,并用我自己的层替换它们。特别地,我用final替换了fulllyconnectedlayer, softmaxLayer和classificationLayerfullyConnectedLayer只给出一个输出值——距离估计值——它将输入输出regressionLayer

在进行这些更改之后,您可以将此网络导出到您的工作空间,以便在培训中使用。网络被导出为一个名为“lgraph_1”的LayerGraph。

方法2:以编程方式创建网络

或者,您可以通过编程方式应用这些更改。安装完ResNet-18 Add-on后,可以导入经过培训的网络。

lgraph_1 = layerGraph (resnet18);

在输入端,我替换了224x224x3imageInputLayer-命名为' data ' -与我自己的480×640 imageInputLayer。为了连接到网络的其余部分,我使用resize3dLayer图层OutputSize为480x640x3。

inputLayers = [imageInputLayer([480 640],'Name','imageinput'),…resize3dLayer (OutputSize, 480 640 3, '名称',' resize3D-output-size '))

lgraph_1 = replaceLayer (lgraph_1‘数据’,inputLayers);

接下来在输出端,我删除了最后的几个层,并用我自己的层替换它们。特别是,fulllyconnectedlayer ' fc1000 ', softmaxLayer ' prob '和classificationLayer ' ClassificationLayer_predictions '与最终fullyConnectedLayer只给出一个输出值——距离估计值——它将输入输出regressionLayer

lgraph_1 = removeLayers(lgraph_1,{'fc1000','prob','ClassificationLayer_predictions'}) outputLayers = [fullconnectedlayer (1,'Name','fc'),regressionLayer('Name','regressionoutput')]

现在我将输出层添加到层图的末尾。然后我用图层图连接' pool5 '和' fc '。

lgraph_1 = addLayers (lgraph_1 outputLayers);lgraph_1 = connectLayers (lgraph_1 pool5, fc);

更多关于层图和上面使用的对象函数的信息可以在这里找到:layerGraph

分析网络

然后您可以运行网络分析仪来确认我们的新图层是否合适。结果应该显示一个480×640输入被缩放到480x640x3,通过预先训练的网络运行,然后作为一个单一的回归值输出。

analyzeNetwork (lgraph_1)

准备训练数据

现在预处理已经完成,网络也设计好了,下一步就是准备训练所需的数据。

首先,我创建我们的数据集。这包括一个CombinedDatastore,由一个imageDatastore为输入图像和arrayDatastore他们的标签。的generateLabelDS helper函数是必要的,以确保图像和标签正确匹配。从combinedDatastore读取,'fullDataset',返回图像和对应的标签。

成像= imageDatastore(“TrainingData”);labelDS = generateLabelDS(成像、标签);fullDataset = combine(imageDS, labelDS);

在这里,我通过将数据划分为训练和验证分区来准备训练数据。我将80%的数据分配给训练分区,20%分配给验证分区。

n_images =元素个数(imageDS.Files);n_training =圆(0.8 * n_images);idx = randperm (n_images);trainingIdx = idx (1: n_training);validationIdx = idx (n_training + 1:结束);trainingDS =子集(fullDataset trainingIdx);validationDS =子集(fullDataset validationIdx);

指定培训选项

作为下一步,我将使用trainingOptions功能:

  • 设置迷你批量大小8。
  • 设置初始学习率为0.01。
  • 每个纪元都洗牌数据。
  • 每个epoch验证一次网络。
  • 在绘图中显示训练进度并抑制冗长的输出。
miniBatchSize = 8;validationFrequency =地板(n_training / miniBatchSize);options = trainingOptions('sgdm',…MiniBatchSize, MiniBatchSize,……“MaxEpochs”,50岁,…“InitialLearnRate”,0.01,…“洗牌”、“every-epoch’,……ValidationData, validationDS,……ValidationFrequency, ValidationFrequency,……“阴谋”、“训练进步”,… 'Verbose',true, ... 'ExecutionEnvironment',"auto");

培训网络

我现在用trainNetwork函数,并将输出保存到文件。注意,根据使用的计算类型(GPU、CPU),这个函数可能需要很长时间运行。

(网络,信息)= trainNetwork (trainingDS、lgraph_1选项);保存(trainedNetwork.mat,净)

使用一个单独的Titan Xp GPU,对完整的数据集进行50个纪元的训练花费了近5个小时。但是,由于后半段的表现趋于稳定,所以可以提前结束训练。同样,最初的调查是在数据的子集上进行的,因此训练要快得多。

准备测试数据

就像我处理训练数据一样,现在我将在imageDatastore中读取测试文件,并提取必要帧的光流。

test_metadata = readtable(“test_metadata.csv”);imd = imageDatastore (s3: / / drivendata-competition-depth-estimation-public / test_videos_downsampled /”,…“ReadFcn”,@(文件名)readVideo(文件名、test_metadata TestData),…FileExtensions,{‘mp4’,‘.avi});imds.readall(“UseParallel”,真正的);

使用测试数据进行深度估计

使用上面训练过的网络,我现在对测试集进行预测。为了做到这一点,我使用预测训练网络的方法。预测方法接收480×640图像并返回深度估计。通过循环测试集中的所有图像,我生成了一个结果表。在这里,我创建了一个1333(测试帧的数量)行表,其中包括video_id、时间和距离。

%初始化输出表结果= table('Size',[height(test_metadata) 3],'VariableTypes',{'string','string','single'},…VariableNames,{‘video_id’,‘时间’,‘距离’});For I = 1:height(test_metadata) id = test_metadata.video_id{I};t = test_metadata.time(我);num2str(t) = [id(1:4)]“中将”);%查找对应的图像名称file = fullfile('TestData',filename);如果isfile(file) %没有边框的帧将没有输入图像I = imread(file);预测=预测(净,我,“ExecutionEnvironment”,“汽车”);Else预测= 0; end results.video_id(i) = id; results.time(i) = t; results.distance(i) = prediction; end head(results)

保存提交文件

上面生成的结果表与提交所需的格式匹配,所以您现在需要做的就是将其保存为CSV文件。这是你要提交的挑战文件。

writetable(结果,“Submission.csv”);

辅助函数

视频预处理-光流

这个辅助函数提取并预处理下采样视频中的标记帧。然后将它们保存到提供的文件夹中——'/ TrainingData在这个例子中。

这是我们计算必要帧的光流的地方,然后使用边界框应用一个二进制掩码。

函数输出=readVideo(filename, metadata, folder) %Load video vr = VideoReader(filename);H = vr.Height;W = vr.Width;[~, name] = fileparts(文件名);idx =包含(metadata.video_id、名称);videoMetadata = rmmissing(元数据(idx:));%忽略没有边界框的帧n_Frames = height(videoMetadata);%预分配输出图像数组输出= 0 (480,640,n_Frames);for i = 1:n_Frames opticFlow = opticflowlk('噪声阈值',0.009);%定义光流t = videometdata .time(i); %Extract timestamp try if t == 0 %If first frame compare with second f1 = vr.read(1); f2 = vr.read(2); else %Otherwise take current frame (t+1) and previous (t) f1 = vr.read(t); f2 = vr.read(t+1); end catch continue %Ignore videos where timings do not match with frames end %Convert to grayscale f1Gray = im2gray(f1); f2Gray = im2gray(f2); %Calculate optical flow estimateFlow(opticFlow,f1Gray); flow = estimateFlow(opticFlow,f2Gray); %Extract corners of bounding box x1 = videoMetadata.x1(i); x2 = videoMetadata.x2(i); y1 = videoMetadata.y1(i); y2 = videoMetadata.y2(i); %Apply mask for bounding box mask = poly2mask([x1 x2 x2 x1]*W,[y1 y1 y2 y2]*H,H,W); maskedFlow = bsxfun(@times, flow.Magnitude, cast(mask, 'like', flow.Magnitude)); maskedFlow = imresize(maskedFlow,'OutputSize',[480 640]); file = fullfile(folder, [name num2str(t) '.png']); %Generate file name %Save image to file if isfolder(folder) imwrite(maskedFlow,file) else mkdir(folder) imwrite(maskedFlow,file) end output(:,:,i) = maskedFlow; end end

为响应创建arrayDatastore

这个函数接受一个图像数据存储和一个标签表。然后构建一个arrayDatastore的标签,确保与图像文件的对应。

函数labelDS= generateLabelDS(imds,labels) files = imds. files;n_files =长度(文件);dataLabels = 0 (n_files, 1);For I = 1:n_files [~,id] = fileparts(files{I});视频= id (1:4);时间= str2double (id(5:结束);Idx = (contains(labels.video_id,video)) & (labels.video_id)时间= =);dataLabels (i) = labels.distance (idx);end labelDS = arrayDatastore(dataLabels);结束

改进的下一步

在这个例子中,我调查了许多可以用来提高分数的设计选择:

光流

  • 算法MATLAB提供了4种计算方法光流.基于视觉外观,我选择了Lucas-Kanade方法。
  • 参数—噪声阈值是信号和背景噪声之间的权衡。
  • 感兴趣的区域-我选择应用一个二进制掩码来突出感兴趣的区域,但可能有一个更好的方法。此外,在这个例子中,我忽略了没有边框的帧。相反,您可以尝试包括整个光流图像。

训练数据分区

  • 比例-改变你的训练和验证数据的比例可以帮助控制性能和过拟合。
  • 方法—在本例中,图像是随机分割的。但是,您也可以根据视频或相机网站进行分割。请注意,测试数据来自不同的站点,而不是训练集中的站点。

转移学习

  • 转移学习-我从一个训练有素的人开始神经网络并根据比赛数据进行额外的训练——这个过程叫做转移学习在这里,我们允许在再训练过程中改变预训练模型的所有层(所有权重都是未冻结的)。然而,这意味着我们可能会失去模型现有的一些知识。相反,迁移学习的第一步通常是在新数据上只训练预训练模型的最后一层,保持所有之前的层不变。一旦新的层被训练,早期的层就可以更好地进行微调,而不会失去模型的预训练。这也可以通过冻结初始层的重量来实现。
  • 冻结的重量—通过设置初始层的学习速率为0,可以防止权值被后续学习更新。可以找到更多的信息在这里
  • Pre-trained网络- MATLAB提供了许多选项pretrained网络让你去调查。
  • 培训方案-调整训练参数对最终结果有很大影响。例如:学习率,小批量,动量,梯度阈值,损失函数

此代码可从这GitHub回购.我们提供这些代码作为起点,并很高兴看到您能提出什么创新。好运!

|
  • 打印
  • 发送电子邮件

评论

要留下评论,请点击在这里登录到您的MathWorks帐户或创建一个新帐户。