Je travaille actuellement sur un système d'intrusion basé sur la vidéosurveillance. Afin de mener à bien cette tâche, je prends un instantané de l'arrière-plan de ma scène (supposons que c'est totalement propre, sans personnes ni objets en mouvement). Ensuite, je compare l'image que je reçois de la caméra vidéo (statique) et cherche les différences. Je dois être en mesure de vérifier toutes les différences , pas seulement la forme humaine ou autre, de sorte que je ne peux pas extraire de caractéristiques spécifiques.
Typiquement, j'ai:
J'utilise OpenCV, donc pour comparer, je fais essentiellement:
cv::Mat bg_frame;
cv::Mat cam_frame;
cv::Mat motion;
cv::absdiff(bg_frame, cam_frame, motion);
cv::threshold(motion, motion, 80, 255, cv::THRESH_BINARY);
cv::erode(motion, motion, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3,3)));
Voici le résultat:
Comme vous pouvez le constater, le bras est dénudé (je suppose, à cause du conflit de couleur différentielle) et ce n’est malheureusement pas ce que je veux.
J'ai pensé à ajouter l'utilisation de cv::Canny()
afin de détecter les arêtes et de remplir la partie manquante du bras, mais malheureusement (encore une fois), cela ne résout le problème que dans peu de situations, voire la plupart d'entre elles.
Existe-t-il un algorithme ou une technique que je pourrais utiliser pour obtenir un rapport de différence exact?
PS: Désolé pour les images. En raison de mon nouvel abonnement, je n'ai pas assez de réputation.
[~ # ~] modifier [~ # ~] J'utilise une image en niveaux de gris ici, mais je suis ouvert à toute solution.
Un problème dans votre code est cv::threshold
Qui n'utilise que des images à 1 canal. Trouver la "différence" de pixel entre deux images en niveaux de gris seulement conduit souvent à des résultats non intuitifs.
Puisque les images que vous avez fournies sont un peu traduites ou que l'appareil photo n'était pas immobile, j'ai manipulé votre image d'arrière-plan pour en ajouter un au premier plan:
image de fond:
image de premier plan:
code:
cv::Mat diffImage;
cv::absdiff(backgroundImage, currentImage, diffImage);
cv::Mat foregroundMask = cv::Mat::zeros(diffImage.rows, diffImage.cols, CV_8UC1);
float threshold = 30.0f;
float dist;
for(int j=0; j<diffImage.rows; ++j)
for(int i=0; i<diffImage.cols; ++i)
{
cv::Vec3b pix = diffImage.at<cv::Vec3b>(j,i);
dist = (pix[0]*pix[0] + pix[1]*pix[1] + pix[2]*pix[2]);
dist = sqrt(dist);
if(dist>threshold)
{
foregroundMask.at<unsigned char>(j,i) = 255;
}
}
donnant ce résultat:
avec cette image de différence:
en général, il est difficile de calculer une segmentation complète avant/arrière-plan à partir d’interprétations différentielles en pixels.
Vous devrez probablement ajouter des éléments de post-traitement pour obtenir une véritable segmentation, à partir de votre masque de premier plan. Pas sûr s'il existe encore des solutions universelles stables.
Comme Berak l'a mentionné, en pratique, il ne suffira pas d'utiliser une seule image d'arrière-plan. Vous devrez donc calculer/gérer votre image d'arrière-plan au fil du temps. Il existe de nombreux articles sur ce sujet et il n’ya pas encore de solution universelle stable.
voici quelques tests supplémentaires. J'ai converti en HSV
espace colorimétrique: cv::cvtColor(backgroundImage, HSVbackgroundImagebg, CV_BGR2HSV); cv::cvtColor(currentImage, HSV_currentImage, CV_BGR2HSV);
et effectué les mêmes opérations dans cet espace, ce qui a conduit à ce résultat:
après avoir ajouté du bruit à l'entrée:
J'obtiens ce résultat:
alors peut-être que le seuil est un peu trop élevé. Je vous encourage tout de même à regarder l'espace colorimétrique HSV également, mais vous devrez peut-être réinterpréter "l'image de différence" et redimensionner chaque canal pour combiner leurs valeurs de différence.
J'utilise Python, voici mon résultat:
Le code:
# 2017.12.22 15:48:03 CST
# 2017.12.22 16:00:14 CST
import cv2
import numpy as np
img1 = cv2.imread("img1.png")
img2 = cv2.imread("img2.png")
diff = cv2.absdiff(img1, img2))
mask = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
th = 1
imask = mask>th
canvas = np.zeros_like(img2, np.uint8)
canvas[imask] = img2[imask]
cv2.imwrite("result.png", canvas)
Mise à jour, voici le code C++:
//! 2017.12.22 17:05:18 CST
//! 2017.12.22 17:22:32 CST
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main() {
Mat img1 = imread("img3_1.png");
Mat img2 = imread("img3_2.png");
// calc the difference
Mat diff;
absdiff(img1, img2, diff);
// Get the mask if difference greater than th
int th = 10; // 0
Mat mask(img1.size(), CV_8UC1);
for(int j=0; j<diff.rows; ++j) {
for(int i=0; i<diff.cols; ++i){
cv::Vec3b pix = diff.at<cv::Vec3b>(j,i);
int val = (pix[0] + pix[1] + pix[2]);
if(val>th){
mask.at<unsigned char>(j,i) = 255;
}
}
}
// get the foreground
Mat res;
bitwise_and(img2, img2, res, mask);
// display
imshow("res", res);
waitKey();
return 0;
}
Réponses similaires:
C'est un problème de vision par ordinateur classique bien connu appelé soustraction d'arrière-plan . Il existe de nombreuses approches qui peuvent être utilisées pour résoudre ce problème, la plupart d'entre elles sont déjà implémentées, je pense donc que vous devriez d'abord examiner plusieurs algorithmes existants. Voici l'implémentation opensource de la plupart d'entre eux: https://github.com/andrewssobral/bgslibrary (J'ai personnellement trouvé SUBSENSE donnant les meilleurs résultats, mais sa lenteur est mortelle)