С++ OpenCV: отслеживание перемещения людей на улице

Я пытаюсь заставить движущихся людей отслеживать работу с OpenCV на С++, с камерой, смотрящей на улицу, и люди двигаются вокруг нее. для примера видео, которое я снял, и я использую, см. здесь: http://akos.maroy.hu/~akos/eszesp/MVI_0778.MOV

Я прочитал эту тему, и я пробовал несколько вещей, в том числе:

  • обнаружение фона и создание контуров
  • попытайтесь обнаружить blobs (ключевые точки для blobs)
  • используя детектор людей для каждого кадра с помощью 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

Собственно, это очень широкая тема. Существует множество научных статей, которые пытаются атаковать эту проблему. Вы должны что-то прочитать раньше.

Коротко: Обнаружение фона и контуры - это самый простой способ. OpenCV имеет очень хорошие реализации, также оптимизированные для gpu. Для уточнения базовых/фоновых блоков вы можете использовать морфологическую операцию попытайтесь закрыть дыры в блобах и получить лучшие результаты. Но не ожидайте отличных результатов. Вычитание фона - сложная операция, вы можете потратить часы на точные параметры настройки для данного набора данных, затем попробовать свой код в реальном мире и... ничего не работает. Огни, тени, фоновые изменения с не заинтересованными объектами.. просто для некоторых проблем.

Итак, нет, нет простой и стандартной техники для обработки так называемой "фрагментации blob" или "сплит-слияния" (когда-то один человек разбивается на большее количество капель, когда-то больше людей объединяются в один клякса). Опять же, он полон научных статей по этому поводу. Но есть методы обработки слежения за неполным или беспорядочным наблюдением. Одним из самых простых является попытка вывести реальное состояние системы при некотором неполном наблюдении с помощью фильтра Калмана. У Opencv есть хорошая реализация. Опять же, если вы выполните поиск по "отслеживанию фильтра Kalman" или "ассоциации данных GNN", вы найдете много.

Если вы хотите использовать некоторую геометрическую информацию, такую ​​как оценка высоты человека и т.д., вы можете это сделать, но вам нужны параметры калибровки камеры. Это означает, что они доступны (microsoft kinect стандартной камеры iphone имеет свои параметры) или вычисляет их, хотя процесс калибровки камеры. Это означает, что вы загружаете изображение шахматной доски, печатаете его на бумаге и фотографируете. Затем в OpenCV есть все методы для калибровки. После этого вам нужно оценить наземную плоскость, а затем использовать некоторые простые методы рендеринга/непроекта для перехода от 2d к 3d координатам вперед и назад и оценить ограничивающий прямоугольник 2d для трехмерного стандартного человека.

Современные подходы к "наблюдению за пешеходами" показывают наблюдение с помощью какого-то детектора. Вычитание фона может дать карту, где можно попытаться обнаружить, чтобы не искать на изображении отверстия, но обнаружение blob бесполезно в этом случае. В OpenCV более используемыми реализациями в этом случае являются детектор Haar Adaboost и детектор HOG. В некоторых случаях датчик GOG дает лучшие результаты. Уже реализованный классификатор в OpenCV включает детектор лица для Haar, и люди обнаруживают HOG. Вы найдете примеры как в примерах cpp, так и в python в репозитории OpenCV.

Если стандартные обнаружения не срабатывают (ваше видео имеет разный размер или вам нужно обнаружить другой объект, кроме пешеходов).. вам нужно обучить свой собственный детектор. Это значит собрать некоторые изображения объекта, который вы хотите обнаружить (положительные образцы), а некоторые изображения с чем-то другим (отрицательные образцы) и подготовить свои собственные классификаторы с помощью методов машинного обучения, таких как SVN. снова, google - ваш друг:)

Удачи!

Ответ 2

Видели ли вы читателей People. Это был исследовательский проект, но с открытым исходным кодом и довольно эффективный. См. здесь

Вероятно, это не совсем современное состояние, но источник доступен и хорошо структурирован.

Ответ 3

Я бы создал человеческий трекер вроде этого:

  • Сначала мы должны инициализировать объекты. Как? Обнаружение объектов. Используйте HOG или каскадный классификатор с соответствующей моделью (т.е. Haarcascade_fullbody.xml) (или используйте их все вместе).

  • Затем мы должны ОТКЛЮЧИТЬ те пиксели, которые находятся внутри ограничивающих прямоугольников. Как? Матч прошлых шаблонов! Идея: накапливать более одного в vector<cv::Mat> и использовать mean template для корреляции.

Больше идей:

  • Объедините результаты: используйте детектор как самую надежную модель наблюдения, и если она не подходит для соответствия шаблону.

  • Использование фонового моделирования для фильтра False Positives (FP коррелируют отлично с фоном).

Кроме того, попробуйте blobtrack_sample.cpp, найденный в папке с образцом opencv, если вы хотите отслеживать на основе контуров.

Ответ 4

Вам не хватает компонента отслеживания движения. Фильтр Kalman/Particle должен помочь. Я предпочитаю Калмана.