web-dev-qa-db-fra.com

C++ OpenCV: suivi des personnes en mouvement dans la rue

J'essaie de faire en sorte que le suivi des personnes en mouvement fonctionne avec OpenCV en C++, avec une caméra qui regarde une rue et les gens qui y circulent. pour un exemple de vidéo que j'ai tourné et que j'utilise, voir ici: http://akos.maroy.hu/~akos/eszesp/MVI_0778.MOV

J'ai lu sur ce sujet et j'ai essayé un certain nombre de choses, notamment:

  • détection d'arrière-plan et création de contours
  • essayer de détecter les blobs (points-clés pour les blobs)
  • en utilisant un détecteur de personnes pour chaque image avec un descripteur HOG

mais aucun de ceux-ci fournissent un bon résultat. pour mon exemple de code, voir ci-dessous. pour la sortie du code basé sur la vidéo ci-dessus, voir: http://akos.maroy.hu/~akos/eszesp/ize.avi . les contours détectés sur le fond sont en rouge, les rectangles de délimitation des contours sont en vert et les résultats du détecteur de personnes HOG sont en bleu.

les problèmes spécifiques que j'ai sont:

la détection d’arrière-plan, puis la recherche de contours semblent fonctionner correctement, bien qu’il existe des faux positifs. mais le principal inconvénient est que très souvent, une seule personne est «découpée» en plusieurs contours. Existe-t-il un moyen simple de les joindre, peut-être en supposant une taille de personne «idéale» ou un autre moyen?

quant au détecteur de personnes HOG, dans mon cas, il identifie très rarement les personnes réelles sur l'image. Que pourrais-je faire de mal là-bas?

tous les pointeurs, idées bienvenues!

et ainsi, le code que j'utilise jusqu'ici, qui est une gloire de divers échantillons que j'ai trouvés ici et là:

#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;
}
10
Ákos Maróy

En fait, c'est un sujet très large. Il existe de nombreux articles scientifiques qui tentent de s’attaquer à ce problème. Vous devriez lire quelque chose avant.

En bref: la détection du fond et des contours est la technique la plus simple. OpenCV a de très jolies implémentations, également optimisées pour le gpu. Pour affiner les blobs de premier plan/d’arrière-plan, vous pouvez utiliser une opération morphologique, essayer de fermer les trous dans les blobs et d’obtenir de meilleurs résultats. Mais ne vous attendez pas à des résultats parfaits. La soustraction d’arrière-plan est une opération difficile. Vous pouvez passer des heures à ajuster avec précision les paramètres d’un jeu de données donné, puis essayez votre code dans le monde réel et .. rien ne fonctionne. Lumières, ombres, changements d'arrière-plan avec des objets non-intéressés .. juste pour mentionner quelques problèmes.

Donc .. non, il n’existe pas de technique simple et standard pour traiter le problème dit de "fragmentation de blob" ou de "scission-fusion" (une personne est parfois divisée en plusieurs blobs, parfois plus de personnes sont fusionnées en un seul blob). Encore une fois, il est plein d'articles scientifiques sur cet argument. Mais il existe des techniques pour gérer le suivi des observations incomplètes ou de fouillis. L’un des plus faciles est d’essayer de déduire l’état réel du système à partir d’une observation incomplète avec le filtre de Kalman. Opencv a une implémentation de Nice à ce sujet. Encore une fois, si vous effectuez une recherche sur le "suivi du filtre de Kalman" ou sur "l'association de données GNN", vous en trouverez beaucoup.

Si vous souhaitez utiliser certaines informations géométriques, telles que l'estimation de la hauteur d'une personne, etc., vous pouvez le faire, mais vous avez besoin des paramètres d'étalonnage de la caméra. Cela implique qu’ils soient disponibles (les paramètres de Microsoft kinect de la caméra iphone standard sont disponibles) ou qu’ils sont calculés via un processus d’étalonnage de la caméra. Cela signifie télécharger une image d'échiquier, l'imprimer sur un papier et en prendre une photo. Ensuite, OpenCV a toutes les méthodes pour effectuer l’étalonnage. Après cela, vous devez estimer le plan de sol, puis utiliser des méthodes simples de rendu projet/non projet pour aller de 2d à 3D coordonnées en avant et en arrière, et estimer le cadre de sélection 2D d'une personne standard 3D.

Des approches modernes sur l'observation d'extraits de "suivi de piétons" avec un détecteur. La soustraction d'arrière-plan peut donner une carte où essayer de détecter pour ne pas rechercher sur l'image du trou, mais la détection de blob est inutile dans ce cas. Dans OpenCV, les implémentations les plus utilisées dans ce cas sont le détecteur Haar Adaboost et le détecteur HOG. Le détecteur HOG semble donner de meilleurs résultats dans certains cas. Le classificateur déjà mis en œuvre dans OpenCV inclut un détecteur de visage pour Haar et des personnes détectées pour HOG. Vous trouverez des exemples dans les exemples cpp et python du référentiel OpenCV.

Si les détections standard échouent (votre vidéo a une taille différente ou vous devez détecter un autre objet que les piétons), vous devez entraîner votre propre détecteur. Cela signifie que vous devez collecter des images de l'objet que vous souhaitez détecter (échantillons positifs) et des images avec quelque chose d'autre (échantillons négatifs) et former vos propres classificateurs avec des techniques d'apprentissage automatique telles que SVN. encore une fois, google est votre ami :)

Bonne chance!

10
nkint

Je créerais un traqueur humain comme ceci:

  1. Tout d'abord, nous devons initialiser les objets. Comment? Détection d'objet. Utilisez HOG ou le classifieur en cascade avec le modèle approprié (c’est-à-dire haarcascade_fullbody.xml) (ou utilisez-les tous ensemble).

  2. Ensuite, nous devons suivre ces pixels trouvés dans les boîtes englobantes. Comment? Faire correspondre les modèles précédents ! Idée: accumulez plus d'un dans un vector<cv::Mat> et utilisez le mean template pour la corrélation.

Plus d'idées:

  • Combinez les résultats: utilisez le détecteur comme modèle d'observation le plus fiable et, s'il échoue, passez au modèle de correspondance.

  • Utilisez la modélisation d’arrière-plan pour classer les faux positifs (le lien entre les PF est excellent).

Essayez également blobtrack_sample.cpp qui se trouve dans le dossier exemple d’opencv si vous souhaitez un suivi basé sur les contours.

3
William

Avez-vous vu le Reading People Tracker. C'était un projet de recherche mais open source et assez efficace. Voir ici

Ce n’est probablement pas encore à la pointe de la technologie, mais la source est disponible et elle est très bien structurée.

3
David Elliman

Il vous manque le composant "modèle de mouvement" du suivi. Le filtre de Kalman/particules devrait aider. Je préfère Kalman.

1
Bhanu Challa