您的位置:首页 > 理论基础

计算机视觉之目标跟踪——论文Learning to Track at 100 FPS with Deep Regression Networks

2017-03-13 18:01 746 查看
论文解读
模型
输入

输出

模型结构

训练
motion model

训练数据

实现
说明

源码解读
srchelper文件夹
bounding_boxcpp

image_proccpp

srctrain文件夹
example_generatorcpp

tracker_trainercpp

traincpp

问题

Learning to Track at 100 FPS with Deep Regression Networks,

David Held, Sebastian Thrun, Silvio Savarese,

European Conference on Computer Vision (ECCV), 2016 (In press)

论文解读

本文采用深度学习回归模型GOTURN(Generic Object Tracking Using Regression Networks)解决单目标跟踪问题。单目标跟踪问题的难点在于物体的平移、旋转、大小变化、视角变化、明暗变化、变形以及遮挡等情况。

作者一直在强调的是本模型可以离线训练,这样可以从大量的训练数据中学到数据的分布;而且在使用的时候由于只有正向的一个推理过程,在GPU上的速度可以达到100fps(其他基于神经网络的跟踪器速度在0.8fps-15fps之间,性能最好的神经网络跟踪器的速递为1fps。);另外大量的训练数据也使得模型的泛化性能比较好,可以跟踪其他没有见过的物体。需要补充一点,速度的提升的原因除了离线训练还有模型本身的原因,本文提出的模型是回归模型,网络只需正向跑一次,而其他深度跟踪模型本身是分类模型,需要对多个候选patches进行打分,以得分最高者作为目标。

模型:



输入:

GOTURN是按帧处理视频的,根据上一张图中目标的位置来判断当前图片的目标位置,本质上还是在处理图片。假设上一张图片目标以c=(cx,cy)为中心,宽w高h,那么以c为中心宽k1w高k1h的截图作为神经网络的一个输入。并在当前图片中同样以c为中心截宽k2w高k2h的图,我们称这个截图为搜索区域,是神经网络的另一个输入。

那么问题来了,不同图片上的目标的BBox大小形状不一,导致截图不一样,但是神经网络的输入要求固定大小,怎么办呢?对截图进行reshape处理,缩放到一样大小。

为什么要在当前图片中以c为中心截宽k2w高k2h截图?是因为作者简化了问题,认为当物体运动速度不是特别快时是会出现在这个区域中的。那么问题来了,如果运动速度过快怎么办?作者只是说可以增大搜索区域的范围,但是这样的话超参k就是动态的了,需要先判断物体运动速度,然后再选择k;或者结合其他的跟踪方法,这一点作者留在了未来工作中。这个搜索区域对于遮挡情况也处理不了,这一点不是很理解,希望大家指教。

输出:

跟踪目标在搜索区域中的相对位置,左上角及右下角的位置(x1,y1,x2,y2)。注意作者设置的坐标范围不是在[0,1]之间,而是乘以扩展因子变换到[0,10]之间,不知道为什么?

模型结构:

五层卷积层:CaffeNet结构的前五层

三层4096全连接层:使用ReLU非线性函数,层之间加上dropout层

训练

损失函数为L1距离。

motion model

作者针对视频中物体的运动情况进行统计,发现物体由上一帧运动到下一帧的过程是有规律的。假设上一张图片的BBox的中心点为c,当前图片BBox的中心点为c′x,我们为两者建立以下模型:

c′x=cx+w⋅Δx(1)

c′y=cy+h⋅Δy(2)

其中w和h分别为上一张图片的宽和高。Δx和Δy两个随机变量,作者发现这两个随机变量服从均值为0的Laplace分布。

对BBox的大小变化建立以下模型:

w′=w⋅γw(3)

h′=h⋅γh(4)

其中w′和h′是当前BBox的宽和高。γw和γh是两个随机变量,可以用均值为1的Laplace分布建模。

训练数据

训练数据既有视频,还由图像。使用大量图片数据进行训练可以增加模型的泛化能力,追踪更多种类的目标。

那对于一张标明目标的图片,怎么产生两张图片呢?通过查看作者的实现发现,深度回归网络的两个输入是一样的,都是对这幅图片的填充完全截图。而对于用random crop生成的人造数据,是对图片的BBox进行变换,然后再根据新BBox在图片上进行截图,得到人造当前截图。

对于视频的上一图片和当前图片,除了前面介绍的真实截图输入,我们也对当前图片进行random crop生成数据(上一张图片的截图保持不变)。具体来说,根据Laplace分布随机采样位移Δx、Δy和大小变化γw、γh,然后对当前图片的BBox进行变化,得到新的BBox,再根据新的BBox在当前图片上进行完全填充截图。???一头黑线啊,怎么可以在当前图片的BBox上进行变换呢?不是应该在上一张图片的BBox上进行变化,然后再在当前图片上截图吗?

实现

作者在Github上上传了实现源码https://github.com/davheld/GOTURN。在测试集上跑了一下,跟踪速度非常快,但是会出现目标错误、跟踪范围过大的情况,而且目标错误出现了多次。

这里简单地阅读一下源码以便后面可以对代码做些修改。

说明

首先对解读源码过程中用到的词语做一下说明:

截图、完全截图、填充完全截图、填充完全变换截图:在截图的时候,可能会出现截图范围超过图片的情况。这时我们把在图片内部的部分称为截图;整个截图(包括超出部分)称为完全截图;超出部分用黑色填充称为填充完全截图;对目标BBox做变换后再截图称为填充完全变换截图。

源码解读

src/helper文件夹

bounding_box.cpp:

全局变量:

kContextFactor = 2
:为目标填充多少上下文,BBox大小的相对值

kScaleFactor = 10
:基于神经网络的输出值范围,缩放BBox的坐标

use_coordinates_output
:决定在初始化BoundingBox时传入的浮点数向量代表的坐标含义。为true时,神经网络输出(x1, y1, x2, y2);为false时,输出(center_x, center_y, width, height)

类BoundingBox

成员变量:

x1_, y1_, x2_, y2_
:BBox的坐标

scale_factor_
:输入神经网络前缩放BBox的坐标

成员函数

double compute_output_width() const
:计算截图的宽,为kContextFactor * bbox_width。

参数:无

返回

output_weight
:截图的宽

double edge_spacing_x() const
:当截图的左边界超过图片的左边界时,超出的长度。如果不超出,则返回0。

void Recenter(const BoundingBox& search_location,

const double edge_spacing_x, const double edge_spacing_y,

BoundingBox* bbox_gt_recentered) const
:计算BBox相对搜索区域(填充完全变换截图)的位置。

void Scale(const cv::Mat& image, BoundingBox* bbox_scaled) const
:根据图片的大小规范化BBox,即不再以像素点长度作为坐标值而是以和图片之间的比例。先缩放BBox的坐标使其位于[0,1]之间,然后再乘以scale_factor_,使其到[0,scale_factor_]之间。

参数

image


bbox_scaled
:就是调用此函数的BoundingBox对象,现在保存的坐标值是在[0, scale_factor_]之间,scale_factor_在创建BoundingBox对象时初始化为kScaleFactor。

返回:无

void Shift(const cv::Mat& image,

const double lambda_scale_frac,

const double lambda_shift_frac,

const double min_scale, const double max_scale,

const bool shift_motion_model,

BoundingBox* bbox_rand) const
:根据Laplace分布得到BBox附近的一个BBox保存在bbox_rand中

image_proc.cpp:

函数:

void CropPadImage(const BoundingBox& bbox_tight, const cv::Mat& image, cv::Mat* pad_image)
:在BBox周围截图。

参数

bbox_tight
:BBox

image
:整幅图片

pad_image
:保存截图

void CropPadImage(const BoundingBox& bbox_tight, const cv::Mat& image, cv::Mat* pad_image,

BoundingBox* pad_image_location, double* edge_spacing_x, double* edge_spacing_y)
:基于BBox截图,并增加一些padding。根据BBox进行截图后,因为考虑到完全截图可能会超过图片,所以截图和完全截图不一定一样大小,作者又恢复了完整截图,只不过对于超过图片的部分用黑色进行填充。

参数

bbox_tight
:BBox

image
:图片

pad_image
:保存被黑色填充的target_pad完全截图

pad_image_location
:保存截图的BBox

edge_spacing_x
:截图的左边界超出图片的时候,超出的长度;不超过的时候为0

edge_spacing_y
:截图的上边界超出图片的时候,超出的长度;不超出的时候为0

返回

void ComputeCropPadImageLocation(const BoundingBox& bbox_tight, const cv::Mat& image, BoundingBox* pad_image_location)
:计算截图的位置,以BBox的中心为中心,大小为(output_weight, output_height)。如果截图超过图片,那么只考虑图片内的截图。

参数

bbox_tight
:BBox,图片中目标的BBox

image
:整张图片

pad_image_location
:保存截图的BBox

返回

src/train文件夹

example_generator.cpp:

类ExampleGenerator:处理两张图片,生成训练数据,包括真实样本和生成样本

成员变量:

lambda_shift_
:BBox的平移随机变量服从的分布的参数

lambda_scale_
:BBox的缩放随机变量服从的分布的参数

min_scale_


max_scale_
:限制BBox放大、缩小比例

image_curr_
:当前的训练图片

bbox_curr_gt_
:当前图片中的目标的BBox

bbox_prev_gt_
:上一张图片中的目标的BBox

target_pad_
:根据上一张图片的目标截取的用黑色填充的完全截图

video_index_


frame_index_
:当前样本生成自哪一个视频和哪一个帧,这两个参数只在保存图片的额时候才会用到

成员函数

构造函数
ExampleGenerator(const double lambda_shift,

const double lambda_scale,

const double min_scale,

const double max_scale)


参数

lambda_shift
:paramater of Laplace distribution of shift

lambda_scale
:paramater of Laplace distribution of rescale

min_scale


max_scale
:constraint on change of size

返回:无

void Reset(const BoundingBox& bbox_prev,

const BoundingBox& bbox_curr,

const cv::Mat& image_prev,

const cv::Mat& image_curr)
:根据输入的参数设置ExampleGenerator对象对应的成员变量值

参数

bbox_prev
:上一张图片的BBox

bbox_curr
:当前图片的BBox

image_prev
:上一张图片

image_curr
:当前图片

返回:无

void get_default_bb_params(BBParams* default_params) const
:把ExampleGenerator对象的lambda参数和scale参数保存至defaut_params。

void MakeTrainingExampleBBShift(const bool visualize_example,

const BBParams& bbparams,

cv::Mat* rand_search_region,

cv::Mat* target_pad,

BoundingBox* bbox_gt_scaled) const
:生成人造数据。对当前BBox进行平移和缩放(Laplace分布),然后对变换BBox在当前图片上进行填充完全截图(称为搜索区域),并计算新BBox此时相对搜索区域的位置得到新的bbox_gt_recentered,对这个BBox的坐标缩放到[0, kScaleFactor]之间。

参数

visualize_example
:是否可视化

bbparams
:获取ExampleGenerator对象的变换参数

rand_search_region
:填充完全转换截图

target_pad
:填充的完全截图

bbox_gt_scaled
:BBox相对于搜索区域的位置(经过规范化的,坐标取值在[0, kScaleFactor]之间)

返回:无

void MakeTrainingExampleBBShift(cv::Mat* image_rand_focus,

cv::Mat* target_pad,

BoundingBox* bbox_gt_scaled) const
:平移和缩放

参数

image_rand_focus
:保存填充完全转换截图

target_pad
:保存上一张图片的目标BBox的完全填充截图

bbox_gt_scaled
:保存BBox相对于搜索区域的位置(经过规范化的,坐标取值在[0, kScaleFactor]之间)

返回:无

void MakeTrainingExamples(const int num_examples,

std::vector<cv::Mat>* images,

std::vector<cv::Mat>* targets,

std::vector<BoundingBox>* bboxes_gt_scaled)
: 根据当前图片生成num_examples个人造数据。根上一张图片的截图保持不变,对当前图片的目标进行变换并截图:[targets,images]就是神经网络的输入。

参数

num_examples
:合成样本数

images
:保存得到的填充完全变换截图的向量

targets
:保存填充完全截图的向量

bboxes_gt_scaled
:保存BBox在搜索区域(填充完全变换截图)中的相对位置,坐标在[0, kScaleFactor]之间

返回:无

void MakeTrueExample(cv::Mat* curr_search_region,

cv::Mat* target_pad,

BoundingBox* bbox_gt_scaled) const
:根据当前图片和上一图片得到的截图(真实数据)

参数

curr_search_region
:当前图片目标的填充完全截图

target_pad
:上一图片目标的填充完全截图

bbox_gt_scaled
:ground truth

返回

tracker_trainer.cpp:

变量:

const int kBatchSize = 50
:batch大小

const int kGeneratedExamplesPerImage = 10
:对每张图片生成多少个人工样本

类TrackerTrainer:

成员变量

std::vector<cv::Mat>  image_batch_
:当前训练batch的数据。这个是当前图片的截图

std::vector<cv::Mat>  targets_batch_
:上一张图片的截图

std::vector<BoundingBox> bboxes_gt_scaled_batch_
:输出的ground truth

ExampleGenerator* example_generator_
:用来生成真实训练数据以及人工数据

RegressorTrainBase* regressor_train_
:神经网络

int num_batches_
:已经训练的batch总数

成员函数:

void Train(const cv::Mat& image_prev, const cv::Mat& image_curr,

const BoundingBox& bbox_prev, const BoundingBox& bbox_curr)
:根据提供的当前图片、上一图片以及对应的BBox参数得到一个真实样本以及kGeneratedExamplesPerImage个人工样本。用这些样本去补TrackerTrainer的batch数据。如果这些样本数加上TrackerTrainer的数据超过了kBatchSize,那么填满batch后剩下的数据留作下一个batch用,并且由于TrackerTrainer的数据达到一个batch开始训练,然后清空batch,并把由当前图片得到的数据中没有使用的部分再添加到空batch中;如果没有达到kBatchSize个数据,那么添加到batch中去等待再一个数据。

参数:

image_prev


image_curr


bbox_prev


bbox_curr


返回:

virtual void MakeTrainingExamples(std::vector<cv::Mat>* images,

std::vector<cv::Mat>* targets,

std::vector<BoundingBox>* bboxes_gt_scaled)
:根据传递给train函数的参数设置成员变量example_generator_,然后调用example_generator_的MakeTrueExample()函数以及MakeTrainingExamples()得到真实样本以及生成样本

参数

images
:保存根据当前图片目标生成的填充完全截图以及填充完全变换截图

targets
:保存上一图片的目标填充完全截图

bboxes_gt_scaled
:保存ground truth

返回

virtual void ProcessBtch()
:根据TrackerTrainer的batch数据训练网络

参数:

返回:

train.cpp

变量

kNumBatches = 500000
:batch的数目

函数:

void train_image(const LoaderImagenetDet& image_loader,

const std::vector<std::vector<Annotation> >& images,

TrackerTrainer* tracker_trainer)
:任意选择一个图片,并任意选择它的一个Annotation,通过image_loader加载响应的图片数据和BBox进行训练

参数

image_loader
:用于加载图片数据和BBox

images
:所有图片的所有Annotation

tracker_trainer
:训练器

返回

void train_video(const std::vector<Video>& videos, TrackerTrainer* tracker_trainer)
:任意选择一个视频,然后选择该视频中任意一个Annotation,以其对应的帧作为上一张图片,以下一个Annotation对一个的帧作为当前图片,加载两个图片和两个BBox进行训练

参数:

videos
:所有视频

tracker_trainer
:训练器

返回

main函数
:需要数据文件参数、网络文件参数、以及目标BBox变换所需要的四个参数,用于加载图片数据集和视频数据集;创建神经网络;创建ExampleGenerator来生成训练样本。定义TrackerTrainer对象,然后随机训练一张图片,随机训练一个视频。

问题

怎么保证神经网络的输入是一样大小的?不同目标的BBox的大小不一样,所以截图也不一样;在人工数据中,对BBox进行的变换是不定的,对应的截图大小也不一样。

在Regressor::Estimate()函数中会对数据reshape到神经网络需要的大小。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐