视频分析目标跟踪

举报
Gere 发表于 2022/08/07 19:29:02 2022/08/07
【摘要】 opencv (opencv_contrib) 实现目标跟踪前提需要安装 opencv 和 opencv_contrib。请参考文章:linux 下 opencv_contrib 源代码编译方法为什么需要跟踪?通常跟踪比检测快单目标检测速度可能为 100+ ms,而单目标跟踪的速度为 10+ ms。目标跟踪可以基于前一帧检测的位置,运动方向,速度预测下一帧的位置。并围绕预期位置进行小范围搜索...

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
  1. 运行后,需要框选目标。
  2. 框选目标后,按 SPACE 或 ENTER 键确定
  3. 如果需要框选多个目标,重复步骤 1, 2.
  4. 选定目标后,按 ESC 进行目标跟踪。

GOTURN 运行需要安装 goturn 模型。

下载模块可以参考:GOTURN 模型

需要把模型放于与 multiTracker 同一级目录。

跟踪算法测试结果

0. BOOSTING

BOOSTING

1. MIL

MIL

2. KCF

KCF

3. TLD

TLD

4. MEDIANFLOW

MEDIANFLOW

5. GOTURN

GOTURN

6. MOSSE

MOSSE

7. CSRT

CSRT

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。