Mac上Clion平台Opencv3.2物体跟踪学习

OpenCV3.2相比于以前的图像跟踪的算法有了很多改动,以前的教学代码很多已经不再适用,本着初学者的思路重新整理一下图像跟踪的一些知识。OpenCV中提供了6中API工具,分别是BOOSTING,MIL,KCF,TLD,MEDIANFLOW,GOTURN,接下来简单介绍一下相关的算法。
一、首先回顾一下基础的一些知识
1.视频的来源有两种,一种为摄像头直接传入的,一种为保存好的视频文件。
(1)摄像头直接传入的
VideoCapture video(0);//代表摄像头打开,将视频流存储在video对象中。
(2)读取保存好的视频文件
VideoCapture video(“d:\road.avi”);//传入视频的路径即可,原理一样
2.一些概念
(1)密集光流:计算视频中每个像素的运动矢量。
(2)稀疏光流:跟踪图像的几个特征点的位置,例如KLT特征跟踪器。
(3)卡尔曼滤波:基于先前的运动信息来预测运动对象下一帧位置的信号处理算法。
(4)MeanShift算法和CamShift定位:密度函数的最大值的算法,也可以用于跟踪。
(5)单个对象跟踪器:在此类跟踪器中,第一帧使用矩形标记,以指示需要跟踪的对象的位置,然后使用跟踪算法在后面的帧中进行跟踪,通常和对象检测器搭配使用。
(6)多目标跟踪算法:在具有一个快速目标探测器的情况下,检测每一帧中的多个目标,根据运行轨迹,识别一个帧中的哪个矩形框对应下一个帧中的哪个矩形框。
二、跟踪器介绍
(1)先介绍一下跟踪器怎么使用:

Ptr tracker = TrackerTLD::create(); //先选择跟踪器的类型,TrackerTLD,TrackerKCF等等,并且实例化一个对象。、 
 VideoCapture video(”shipinlujing.mp4”); //读取存储在电脑中的视频文件 
 if(!video.isOpened()){cout<”读取视频失败!”<< endl ;}//检查视频是否读取成功 
 Mat frame; 
 video.read(frame);读取第一帧视频 
 Rect2d box(250,25,85,320);定义矩形框 
 tracker->init(frame,box);使用第一帧图像和矩形框来初始化跟踪器


(2)跟踪的目标是在当前帧中找到一个对象,由于我们之前已经跟踪对象到了当前帧,我们已经知道它是如何运动的,也就是我们知道对象的运动模型,知道对象在之前帧中的位置和速度,有速度,有运动方向,我们就可以预测到对象在当前帧中的位置,在运动模型预测到的位置再利用对象的外观模型来进行小邻域范围内的搜索,这样就准确的预测到对象的位置。
运动模型预测了对象的大概位置,外观模型给予外观提供更加准确的估计。
如果对象很简单,在视频流中外观变化不大,背景变化不大,就可以使用简单的模板作为外观模型,然后来寻找那个模板。但是一般现实场景变化很多,所以现代的一些跟踪器,外观模型是在线方式训练的分类器。
分类器的工作是将图像的矩形区域分类为对象或背景。 分类器接受图像补丁作为输入,并返回0和1之间的分数,以指示图像补丁包含对象的概率。 当绝对确定图像补丁是背景时,分数为0,当绝对确定补丁是对象时,分数为1。
在机器学习中,我们使用“在线”这个词来指代在运行时在运行中训练的算法。 离线分类器可能需要数千个示例来训练分类器,但是在运行时通常使用非常少的示例训练在线分类器。
分类器通过馈送正(对象)和负(背景)示例来训练。 如果你想建立一个分类器来检测猫,你训练它与成千上万包含猫的图像和数千不包含猫的图像。 这样分类器学习区分什么是猫和什么不是猫。 你可以在这里了解有关图像分类的更多信息。 在建立在线分类器时,我们没有成千上万的正和负的例子。

(3)让我们来看看不同的跟踪算法如何处理这个在线训练问题。
BOOSTING跟踪器
此跟踪器基于AdaBoost的在线版本 - 基于HAAR级联的面部检测器在内部使用的算法。 这个分类器需要在运行时用对象的正和负例子训练。 由用户提供的初始边界框(或由另一个对象检测算法)作为对象的正例,并且边界框外部的许多图像补片被当作背景。 给定新帧,对先前位置的邻域中的每个像素运行分类器,并记录分类器的得分。 对象的新位置是得分最大的位置。 所以现在我们有一个更积极的例子为分类器。 随着更多的帧进入,分类器用该附加数据更新。

优点:无。 这个算法是十年前并且可以运用,但我找不到一个很好的理由使用它,特别是当其他高级跟踪器(MIL,KCF)基于类似的原理可用。

缺点:跟踪性能平庸。

MIL跟踪器
此跟踪器在概念上类似于上述的BOOSTING跟踪器。 最大的区别在于,代替仅考虑对象的当前位置作为积极示例,它在当前位置周围的小邻域中查找以生成若干潜在的正例子。 你可能认为这是一个坏主意,因为在大多数这些“积极”的例子中,对象不是中心。

在MIL中,你没有指定正和负例子,但是有正和负“包”。 正包中的图像集合并不都是积极的例子。 相反,只有一个图像在积极的包里需要一个积极的例子。在我们的示例中,正包包含以对象的当前位置为中心的补丁,以及在其周围的小邻域中的补丁。 即使被跟踪对象的当前位置不准确,当来自当前位置的邻域的样本被放入正包中时,很有可能这个包包含至少一个图像,其中对象被良好地置于居中。

优点:性能相当不错。 它不像BOOSTING跟踪器那样漂移,它在部分遮挡下合理地工作。如果你使用OpenCV 3.0,这可能是你可用的最好的跟踪。 但是如果你使用更高版本,考虑KCF。

缺点:跟踪失败报告不可靠。不能从完全闭塞恢复。

KCF跟踪器
KFC代表内核化相关滤波器。 这个跟踪器建立在前两个跟踪器提出的想法。 该跟踪器利用了这样的事实,即在MIL跟踪器中使用的多个正样本具有大的重叠区域。 这种重叠的数据导致一些良好的数学特性,利用这个跟踪器,使跟踪更快,同时更准确。

优点:准确度和速度都比MIL更好,它报告跟踪失败比BOOSTING和MIL更好。 如果您使用OpenCV 3.1和更高版本,我建议对于大多数应用程序使用这个。

缺点:不能从完全闭塞恢复。 未在OpenCV 3.0中实现。

Bug警告:在OpenCV 3.1(仅限Python)中有一个错误,因为返回了不正确的边界框。 参见错误报告。

TLD跟踪器
TLD代表跟踪,学习和检测。 顾名思义,该跟踪器将长期跟踪任务分解为三个组件 - (短期)跟踪,学习和检测。 从作者的论文,“跟踪器跟踪对象从一帧到帧。 检测器定位到目前为止观察到的所有外观,并在必要时校正跟踪器。 学习估计检测器的错误并更新它以避免未来的这些错误。“这个跟踪器的输出往往会跳一下。 例如,如果你正在跟踪行人,并且场景中还有其他行人,则该跟踪器有时可以临时跟踪与您要跟踪的行人不同的行人。 在积极的一面,这条轨道似乎在更大的规模,运动和遮挡下跟踪物体。 如果你有一个视频序列,其中的对象隐藏在另一个对象后面,这个跟踪器可能是一个不错的选择。

优点:在多个帧的遮挡下工作最好。 此外,跟踪最佳的规模变化。

缺点:很多误报,使它几乎不可用。

MEDIANFLOW跟踪器
在内部,该跟踪器在时间上向前和向后方向上跟踪对象,并且测量这两个轨迹之间的差异。 最小化该ForwardBackward错误使它们能够可靠地检测跟踪失败并在视频序列中选择可靠的轨迹。

在我的测试中,我发现这个跟踪器在运动是可预测和小规模的时候效果最好。 与其他跟踪器不同,即使跟踪明显失败,该跟踪器知道跟踪失败的时间。

优点:优秀的跟踪失败报告。 当运动是可预测的并且没有遮挡时工作得很好。

缺点:在大规模运动下失败。

GOTURN跟踪器
在跟踪器类中的所有跟踪算法中,这是基于卷积神经网络(CNN)的唯一一种。 它也是唯一一个使用离线训练模型,因为它比其他跟踪器更快。 从OpenCV文档,我们知道它“对视点变化,照明变化和变形是强大的”。 但它不能很好地处理遮挡。
CMakeLists.txt

cmake_minimum_required(VERSION 3.8)
project(untitled2)
find_package(OpenCV)
set(CMAKE_CXX_STANDARD 11)
include_directories(${OpenCV_INCLUDE_DIRS})
set(SOURCE_FILES main.cpp)
add_executable(untitled2 ${SOURCE_FILES})
target_link_libraries(untitled2 ${OpenCV_LIBS})

.cpp文件

#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/tracking.hpp>
#include <opencv2/video.hpp>
#include <opencv2/core/utility.hpp>
#include <cstring>

using namespace std;
using namespace cv;

int main(int argc,char** argv) {
    Mat frame;
    Ptr<TrackerTLD> tracker = TrackerTLD::create();
    VideoCapture video(0);
    if(!video.isOpened())
    {
        cerr<<"cannot read video!"<<endl;
        return -1;
    }
    video.read(frame);
    Rect2d box(270,120,180,260);
    tracker->init(frame,box);
    while(video.read(frame))
    {
        tracker->update(frame,box);
        rectangle(frame,box,Scalar(255,0,0),2,1);
        imshow("Tracking",frame);
        int k = waitKey(1);
        if(k == 27)
        {
            break;
        }
    }
}