视频分析目标跟踪
opencv (opencv_contrib) 实现目标跟踪
前提
需要安装 opencv 和 opencv_contrib。
请参考文章:linux 下 opencv_contrib 源代码编译方法
为什么需要跟踪?
通常跟踪比检测快
单目标检测速度可能为 100+ ms,而单目标跟踪的速度为 10+ ms。
目标跟踪可以基于前一帧检测的位置,运动方向,速度预测下一帧的位置。并围绕预期位置进行小范围搜索以准确定位物体。
比无依赖直接进行检测会快很多。
一个高效的系统应该通常会在 n 帧的第一帧进行使用检测算法进行目标检测,此后 n - 1 帧使用跟踪算法。
为什么不基于第一次检测结果的基础上,后续全部使用跟踪算法?
因为跟踪在遇到遮挡,运动方向和速度高速变化以致于跟踪算法跟不上时,可能会导致跟踪失败。
而且跟踪在一定程度上会有误差参数,误差一直积累下去时,会导致跟踪偏离原始目标。
所以需要检测算法对目标进行修正,一定程度上避免跟踪误差的积累。
当检测失败时,跟踪可以提供帮助
检测在目标被遮挡时,会检测失败。
而一个好的跟踪算法,还能依靠前面帧的位置,方向,速度信息,预测当前的目标。
一定程度上解决一些遮挡问题、
目标跟踪
opencv (opencv_contrib 模块) 目前支持 8 种跟踪算法。
| 跟踪算法 | opencv 支持的最低版本 | 说明 | 
|---|---|---|
| Boosting | opencv 3.0.0 | 比较古老的目标跟踪算法,速度慢 | 
| MIL | opencv 3.0.0 | 速度快,但是失败率较高 | 
| KCF | opencv 3.1.0 | 比 Boosting 和 MIL 快,但是遮挡情况下效果差 | 
| TLD | opencv 3.0.0 | (TLD 目前有 3 种版本算法。TLD 1.0,TLD 2.0,TLD 3.0。opencv 的 TLD 跟踪效果较差,怀疑是基于 TLD 1.0 算法实现的) | 
| MedianFlow | opencv 3.0.0 | 跟踪效果较好,但对于快速移动/跳动的物体容易失败。 | 
| GOTURN | opencv 3.2.0 | 基于深度学习的目标检测器。需要额外的 GOTURN 模型,可以自行训练目标跟踪模型。 | 
| MOSSE | opencv 3.4.1 | 速度比其它算法快,但是准确率比 CSRT 和 KCF 低。 | 
| CSRT | opencv 3.4.2 | 准确率比 KCF 稍高,但速度不如 KCF。 | 
建议:
- CSRT:如果可以接受较慢速度下,追求高准确度。
- KCF:速度要求稍高,准确度要求不高。
- MOSSE:只要求高速,不要求准确度。
- GOTURN:可以自行训练模型,提高识别效果。
opencv 目标跟踪
参考代码如下:
#include <opencv2/opencv.hpp>
#include <opencv2/tracking.hpp>
#define TEST_MODE_VIDEO         0
#define TEST_MODE_CAMERA        1
#define ENABLE_TEST_MODE        TEST_MODE_VIDEO
#define ENABLE_SHOW_FPS         1
// #define TEST_VIDEO_FILE         "videos/run.mp4"
enum EnumTrackerType {
    E_TRACKER_TYPE_BOOSTING,
    E_TRACKER_TYPE_MIL,
    E_TRACKER_TYPE_KCF,
    E_TRACKER_TYPE_TLD,
    E_TRACKER_TYPE_MEDIANFLOW,
    E_TRACKER_TYPE_GOTURN,
    E_TRACKER_TYPE_MOSSE,
    E_TRACKER_TYPE_CSRT,
    E_TRACKER_TYPE_MAX
};
#define ENUM_TRACKER_TYPE(type)       E_TRACKER_TYPE_##type
#define TEXT_TRACKER_TYPE(type)       #type
#define VEC_TRACKER_TYPE(vec, type)   do {  \
                                          vec[ENUM_TRACKER_TYPE(type)] = TEXT_TRACKER_TYPE(type); \
                                      } while(0)
std::vector<std::string> trackerTypes(E_TRACKER_TYPE_MAX);
int initTrackerTypes(std::vector<std::string> & vec) {
    // vec.reserve(E_TRACKER_TYPE_MAX);
    VEC_TRACKER_TYPE(vec, BOOSTING);
    VEC_TRACKER_TYPE(vec, MIL);
    VEC_TRACKER_TYPE(vec, KCF);
    VEC_TRACKER_TYPE(vec, TLD);
    VEC_TRACKER_TYPE(vec, MEDIANFLOW);
    VEC_TRACKER_TYPE(vec, GOTURN);
    VEC_TRACKER_TYPE(vec, MOSSE);
    VEC_TRACKER_TYPE(vec, CSRT);
    return vec.size();
}
cv::Ptr<cv::Tracker> createTrackerByName(EnumTrackerType eTrackerType) {
    switch( eTrackerType ) {
        case ENUM_TRACKER_TYPE(BOOSTING):   return cv::TrackerBoosting::create();
        case ENUM_TRACKER_TYPE(MIL):        return cv::TrackerMIL::create();
        case ENUM_TRACKER_TYPE(KCF):        return cv::TrackerKCF::create();
        case ENUM_TRACKER_TYPE(TLD):        return cv::TrackerTLD::create();
        case ENUM_TRACKER_TYPE(MEDIANFLOW): return cv::TrackerMedianFlow::create();
        case ENUM_TRACKER_TYPE(GOTURN):     return cv::TrackerGOTURN::create();
        case ENUM_TRACKER_TYPE(MOSSE):      return cv::TrackerMOSSE::create();
        case ENUM_TRACKER_TYPE(CSRT):       return cv::TrackerCSRT::create();
        default:
            std::cout << "Incorrect tracker index : " << eTrackerType << std::endl;
            for (int i = 0, len = trackerTypes.size(); i < len; ++i) {
                printf(" [ %d ] %s\n", i, trackerTypes[i].c_str());
            }
            break;
    }
    cv::Ptr<cv::Tracker> tracker;
    return tracker;
}
void getRandomColors(std::vector<cv::Scalar> &colors, int numColors) {
    cv::RNG rng(0);
    for(int i = 0; i < numColors; ++i) {
        colors.push_back(cv::Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255)));
    }
}
int main(int argc, char * argv[]) {
    std::string videoPath = TEST_VIDEO_FILE;
    int trackerTypeMax = initTrackerTypes(trackerTypes);
    int trackerTypeIndex = 0;
    if (argc >= 2) {
        int index = argv[1][0] - '0';
        if (index >= 0 && index < trackerTypeMax) {
            trackerTypeIndex = index;
        }
    }
    if (argc >= 3) {
        videoPath = argv[2];
    }
    std::string trackerType = trackerTypes[trackerTypeIndex];
    std::vector<cv::Rect> bboxes;
    cv::Mat frame;
    std::cout << "Default tracking algoritm is " << trackerType << std::endl;
    std::cout << "Available tracking algorithms are:" << std::endl;
    for (int i = 0; i < trackerTypeMax; ++i) {
        printf(" [ %d ] %s\n", i, trackerTypes[i].c_str());
    }
#if (ENABLE_TEST_MODE == TEST_MODE_VIDEO)
    cv::VideoCapture cap(videoPath);
#elif (ENABLE_TEST_MODE == TEST_MODE_CAMERA)
    cv::VideoCapture cap(0);
#endif
    if(!cap.isOpened()) {
        std::cout << "Error opening video file " << videoPath << std::endl;
        return -1;
    }
    cap.read(frame);
    bool showCrosshair = true, fromCenter = false;
    std::cout << "\n==========================================================\n";
    std::cout << "OpenCV says press c to cancel objects selection process" << std::endl;
    std::cout << "It doesn't work. Press Escape to exit selection process" << std::endl;
    std::cout << "\n==========================================================\n";
    cv::selectROIs("MultiTracker", frame, bboxes, showCrosshair, fromCenter);
    if (bboxes.size() < 1) {
        return 0;
    }
    std::vector<cv::Scalar> colors;
    getRandomColors(colors, bboxes.size());
    cv::Ptr<cv::MultiTracker> multiTracker = cv::MultiTracker::create();
    for(int i = 0; i < bboxes.size(); ++i) {
        multiTracker->add(createTrackerByName((EnumTrackerType)trackerTypeIndex), frame, cv::Rect2d(bboxes[i]));
    }
    std::cout << "\n==========================================================\n";
    std::cout << "Started tracking, press ESC to quit." << std::endl;
#if ENABLE_SHOW_FPS
    char strText[30];
    sprintf(strText, "[ %s ] FPS: ", trackerType.c_str());
    const int len = strlen(strText);
#endif
    while(cap.isOpened()) {
        cap.read(frame);
        if (frame.empty()) {
            break;
        }
    #if ENABLE_SHOW_FPS
        double timer = (double) cv::getTickCount();
    #endif
        multiTracker->update(frame);
    #if ENABLE_SHOW_FPS
        float fps = cv::getTickFrequency() / ((double) cv::getTickCount() - timer);
    #endif
        for(unsigned i = 0; i < multiTracker->getObjects().size(); ++i) {
            cv::rectangle(frame, multiTracker->getObjects()[i], colors[i], 2, 1);
        }
    #if ENABLE_SHOW_FPS
        sprintf(strText + len, "%0.2f", fps);
        cv::putText(frame, strText, cv::Point(100,50), cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 0, 250), 2);
    #endif
        cv::imshow("MultiTracker", frame);
        if (cv::waitKey(1) == 27) {
            break;
        }
    }
    return 0;
}
使用方式:
# 编译
g++ multiTracker.cpp `pkg-config --libs --cflags opencv` -o multiTracker
# 运行
# 第一个参数:使用哪一种跟踪算法
#     0 - BOOSTING
#     1 - MIL
#     2 - KCF
#     3 - TLD
#     4 - MEDIANFLOW
#     5 - GOTURN (需要下载 goturn 模型)
#     6 - MOSSE
#     7 - CSRT
# 第二个参数:测试视频的路径
./multiTracker 0 ./video.mp4
- 运行后,需要框选目标。
- 框选目标后,按 SPACE 或 ENTER 键确定
- 如果需要框选多个目标,重复步骤 1, 2.
- 选定目标后,按 ESC 进行目标跟踪。
GOTURN 运行需要安装 goturn 模型。
下载模块可以参考:GOTURN 模型
需要把模型放于与 multiTracker 同一级目录。
跟踪算法测试结果
0. BOOSTING

1. MIL

2. KCF

3. TLD

4. MEDIANFLOW

5. GOTURN

6. MOSSE

7. CSRT

- 点赞
- 收藏
- 关注作者
 
             
           
评论(0)