CV-extrait différences entre deux images
je travaille actuellement sur un système d'intrusion basé sur la vidéosurveillance. Afin d'accomplir cette tâche, je prendre un instantané de l'arrière-plan de ma scène (supposons qu'elle est totalement propre, pas de personnes ou d'objets en mouvement). Ensuite, je compare le cadre que j'obtiens de la caméra vidéo (statique) et je cherche les différences. Je dois être en mesure de vérifier tout différences, non seulement la forme humaine ou autre, donc je ne peux pas l'extraction caractéristique spécifique.
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 voir, le bras est dépouillé (en raison d'un conflit de couleur différentielle je suppose) et ce n'est malheureusement pas ce que je veux.
I pensé à ajouter l'utilisation de cv::Canny()
afin de détecter les bords et remplir la partie manquante du bras, mais malheureusement (encore une fois), elle ne résout le problème en quelques pas de la plupart d'entre eux.
y a-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 l'image en niveaux de gris ici, mais je suis ouvert à toute solution.
2 réponses
un problème dans votre code est cv::threshold
qui n'utilise que des images sur 1 canal. Trouver la "différence" entre deux images dans le sens des pixels en échelle de gris seulement conduit souvent à des résultats peu intuitifs.
puisque vos images fournies sont un peu traduites ou que la caméra n'était pas stationnaire, j'ai manipulé votre image de fond pour ajouter un peu de premier plan:
image d'arrière-plan:
image au 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 avant-plan/arrière-plan complète à partir de l'interprétation des différences en pixels.
vous aurez probablement à ajouter postprocessing stuff pour obtenir une véritable segmentation, où vous commencez à partir de votre masque de premier plan. Je ne sais pas s'il existe encore des solutions universelles stables.
comme berak l'a mentionné, dans la pratique, il ne sera pas suffisant d'utiliser une seule image de fond, vous devrez donc calculer/gérer votre image de fond au fil du temps. Il ya beaucoup de documents couvrant ce sujet et afaik aucune solution universelle stable encore.
voici un peu plus de tests. J'ai converti en HSV
l'espace de couleur: cv::cvtColor(backgroundImage, HSVbackgroundImagebg, CV_BGR2HSV); cv::cvtColor(currentImage, HSV_currentImage, CV_BGR2HSV);
et effectué les mêmes opérations dans cet espace, conduisant à ce résultat:
après avoir ajouté un peu de bruit à l'entrée:
j'ai ce résultat:
alors peut-être que le seuil est un peu trop haut. Je vous encourage toujours à jeter un oeil à L'espace de couleur HSV trop, mais vous pourriez avoir à réinterpréter l ' "image de différence" et de revenir chaque canal pour combiner leurs valeurs de différence.
j'utilise Python, c'est 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: