C++ OpenCV:跟踪街上移动的人

Posted

技术标签:

【中文标题】C++ OpenCV:跟踪街上移动的人【英文标题】:C++ OpenCV: tracking moving people on the street 【发布时间】:2013-08-09 02:49:14 【问题描述】:

我正在尝试使用 C++ 中的 OpenCV 让移动的人跟踪工作,用摄像头看着街道,人们在街上走动。有关我拍摄并正在使用的示例视频,请参见此处:http://akos.maroy.hu/~akos/eszesp/MVI_0778.MOV

我阅读了这个主题,并尝试了很多方法,包括:

背景检测和创建轮廓 尝试检测 blob(blob 的关键点) 对带有 HOGDescriptor 的每一帧使用人员检测器

但这些都没有提供好的结果。对于我的示例代码,请参见下文。基于上述视频的代码输出见:http://akos.maroy.hu/~akos/eszesp/ize.avi。在背景中检测到的轮廓为红色,轮廓的边界矩形为绿色,HOG 人检测结果为蓝色。

我遇到的具体问题是:

背景检测然后寻找轮廓似乎工作正常,尽管有一些误报。但主要缺点是很多时候一个人被“切割”成多个轮廓。有没有一种简单的方法可以将这些“连接”在一起,可能是通过假定的“理想”人尺寸或其他方式?

至于 HOG 人物检测器,在我的情况下,它很少能识别图像上的真实人物。我在那里做错了什么?

所有指针,欢迎提出想法!

因此,到目前为止,我使用的代码是我在这里和那里找到的各种示例的自定义粘贴的荣耀:

#include<opencv2/opencv.hpp>
#include<iostream>
#include<vector>

int main(int argc, char *argv[])

    if (argc < 3) 
        std::cerr << "Usage: " << argv[0] << " in.file out.file" << std::endl;
        return -1;
    

    cv::Mat frame;
    cv::Mat back;
    cv::Mat fore;
    std::cerr << "opening " << argv[1] << std::endl;
    cv::VideoCapture cap(argv[1]);
    cv::BackgroundSubtractorMOG2 bg;
    //bg.nmixtures = 3;
    //bg.bShadowDetection = false;

    cv::VideoWriter output;
    //int ex = static_cast<int>(cap.get(CV_CAP_PROP_FOURCC));
    int ex = CV_FOURCC('P','I','M','1');
    cv::Size size = cv::Size((int) cap.get(CV_CAP_PROP_FRAME_WIDTH),
                             (int) cap.get(CV_CAP_PROP_FRAME_HEIGHT));
    std::cerr << "saving to " << argv[2] << std::endl;
    output.open(argv[2], ex, cap.get(CV_CAP_PROP_FPS), size, true);

    std::vector<std::vector<cv::Point> > contours;

    cv::namedWindow("Frame");
    cv::namedWindow("Fore");
    cv::namedWindow("Background");


    cv::SimpleBlobDetector::Params params;
    params.minThreshold = 40;
    params.maxThreshold = 60;
    params.thresholdStep = 5;
    params.minArea = 100; 
    params.minConvexity = 0.3;
    params.minInertiaRatio = 0.01;
    params.maxArea = 8000;
    params.maxConvexity = 10;
    params.filterByColor = false;
    params.filterByCircularity = false;


    cv::SimpleBlobDetector blobDtor(params);
    blobDtor.create("SimpleBlob");

    std::vector<std::vector<cv::Point> >    blobContours;
    std::vector<cv::KeyPoint>               keyPoints;
    cv::Mat                                 out;

    cv::HOGDescriptor hog;
    hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector());


    for(;;)
    
        cap >> frame;

        bg.operator ()(frame, fore);

        bg.getBackgroundImage(back);
        cv::erode(fore, fore, cv::Mat());
        cv::dilate(fore, fore, cv::Mat());

        blobDtor.detect(fore, keyPoints, cv::Mat());

        //cv::imshow("Fore", fore);

        cv::findContours(fore, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
        cv::drawContours(frame, contours, -1, cv::Scalar(0,0,255), 2);

        std::vector<std::vector<cv::Point> >::const_iterator it = contours.begin();
        std::vector<std::vector<cv::Point> >::const_iterator end = contours.end();
        while (it != end) 
            cv::Rect bounds = cv::boundingRect(*it);
            cv::rectangle(frame, bounds, cv::Scalar(0,255,0), 2);

            ++it;
        

        cv::drawKeypoints(fore, keyPoints, out, CV_RGB(0,255,0), cv::DrawMatchesFlags::DEFAULT);
        cv::imshow("Fore", out);


        std::vector<cv::Rect> found, found_filtered;
        hog.detectMultiScale(frame, found, 0, cv::Size(8,8), cv::Size(32,32), 1.05, 2);
        for (int i = 0; i < found.size(); ++i) 
            cv::Rect r = found[i];
            int j = 0;
            for (; j < found.size(); ++j) 
                if (j != i && (r & found[j]) == r) 
                    break;
                
            
            if (j == found.size()) 
                found_filtered.push_back(r);
            
        

        for (int i = 0; i < found_filtered.size(); ++i) 
            cv::Rect r = found_filtered[i];
            cv::rectangle(frame, r.tl(), r.br(), cv::Scalar(255,0,0), 3);
        


        output << frame;

        cv::resize(frame, frame, cv::Size(1280, 720));
        cv::imshow("Frame", frame);

        cv::resize(back, back, cv::Size(1280, 720));
        cv::imshow("Background", back);



        if(cv::waitKey(30) >= 0) break;
    
    return 0;

【问题讨论】:

【参考方案1】:

您缺少跟踪的“运动模型”组件。卡尔曼/粒子滤波器应该有所帮助。我更喜欢卡尔曼。

【讨论】:

【参考方案2】:

我会像这样创建一个人类跟踪器:

    首先,我们必须初始化对象。如何?物体检测。使用 HOG 或具有适当模型的级联分类器(即 haarcascade_fullbody.xml)(或一起使用它们)。

    然后,我们必须跟踪在边界框内找到的那些像素。如何? Match past templates!思路:将多个累加到vector&lt;cv::Mat&gt;中,并使用mean template进行关联。

更多想法:

结合结果:使用检测器作为最可靠的观察模型,如果失败则切换到模板匹配。

使用背景建模来过滤误报(FP 与背景的相关性非常好)。

此外,如果您想要基于轮廓的跟踪,请尝试在 opencv 示例文件夹中找到的 blobtrack_sample.cpp。

【讨论】:

【参考方案3】:

其实,这是一个非常广泛的话题。有很多科学论文试图解决这个问题。你应该先阅读一些东西。

简单地说: 背景检测和轮廓是最简单的技术。 OpenCV 有非常好的实现,也针对 gpu 进行了优化。为了优化前景/背景斑点,您可以使用一些morphological operation, 尝试关闭斑点中的孔并获得更好的结果。但不要指望完美的结果。背景减法是一项困难的操作,您可以花费数小时来微调给定数据集的参数,然后在现实世界中尝试您的代码......没有任何效果。灯光、阴影、背景随不感兴趣的对象而变化.. 仅提及一些问题。

所以.. 不,没有一种简单而标准的技术来处理所谓的“blob 碎片”或“split-merge”问题(有时一个人被分成更多的 blob,有时更多的人被合并成一个斑点)。同样,它充满了关于这个论点的科学论文。但是有一些技术可以处理跟踪不完整或杂乱的观察。最简单的方法之一是在使用卡尔曼滤波器进行一些不完整观察的情况下尝试推断系统的真实状态。 Opencv 对此有一个很好的实现。同样,如果您搜索“卡尔曼滤波器跟踪”或“GNN 数据关联”,您会发现很多。

如果您想使用一些几何信息,例如估计人的身高等,您可以这样做,但您需要相机的校准参数。这意味着让它们可用(标准 iphone 相机的微软 kinect 具有可用的参数)或通过相机校准过程计算它们。这意味着下载棋盘图像,将其打印在纸上,然后拍一些照片。然后,OpenCV 拥有进行校准的所有方法。之后,您需要估计地平面,然后使用一些简单的渲染投影/取消投影方法从 2d 坐标到 3d 坐标来回,并估计 3d 标准人的 2d 边界框。

“行人跟踪”的现代方法使用一些检测器提取观察结果。背景减法可以给出一个地图来尝试检测而不是在孔图像上搜索,但在这种情况下,斑点检测是无用的。在 OpenCV 中,这种情况下使用较多的实现是 Haar Adaboost 检测器和 HOG 检测器。在某些情况下,HOG 检测器似乎给出了更好的结果。 OpenCV 中已经实现的分类器包括用于 Haar 的人脸检测器和用于 HOG 的人检测器。您可以在 OpenCV 存储库的 cpp 和 python 示例中找到示例。

如果标准检测失败(您的视频大小不同,或者您必须检测除行人之外的其他物体).. 您必须训练自己的检测器。这意味着收集一些您想要检测的对象图像(正样本),以及一些带有其他东西的图​​像(负样本),并使用机器学习技术(如 SVN)训练您自己的分类器。再次,谷歌是你的朋友:)

祝你好运!

【讨论】:

【参考方案4】:

您看过阅读人员追踪器吗?这是一个研究项目,但它是开源的并且非常有效。见here

它现在可能不是最先进的,但源代码是可用的并且结构很好。

【讨论】:

以上是关于C++ OpenCV:跟踪街上移动的人的主要内容,如果未能解决你的问题,请参考以下文章

如何使openCV背景减法KNN算法持续更长时间,跟踪一个没有移动的前景对象

opencv怎么计算物体移动速度 求具体解决方法,最好能有源代码参考一下,

OpenCV中MeanShift算法视频移动对象分析

干货 | OpenCV中KLT光流跟踪原理详解与代码演示

OpenCV C++,找到描述物体形状的方程

使用Python,OpenCV追踪对象的轨迹,来确定其移动方向