CV-извлечение различий между двумя изображениями
в настоящее время я работаю над системой вторжения, основанной на видеонаблюдении. Чтобы выполнить эту задачу, я делаю снимок фона моей сцены (предположим, что он полностью чист, нет людей или движущихся объектов). Затем я сравниваю кадр, который я получаю от (статической) видеокамеры, и ищу различия. Я должен иметь возможность проверить любой различия, а не только человеческая форма или что-то еще, поэтому я не могу извлечь конкретную функцию.
Как Правило, I есть:
Я использую OpenCV, поэтому для сравнения я в основном делаю:
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)));
вот результат:
Как вы можете видеть, рука обнажена (из-за цветового дифференциального конфликта, я думаю), и это, к сожалению, не то, что я хочу.
Я думал о добавлении использования cv::Canny()
чтобы обнаружить края и заполнить недостающую часть руки, но, к сожалению (еще раз), это только решает проблему в немногих ситуациях не большинство из них.
есть ли алгоритм или метод, который я мог бы использовать, чтобы получить точный отчет о разнице?
PS: извините за изображения. Из-за моей новой подписки у меня недостаточно репутации.
редактировать Я использую здесь изображение в оттенках серого, но я открыт для любого решения.
2 ответов
одна проблема в вашем коде cv::threshold
который использует только изображения 1 канала. Поиск пиксельной "разницы" между двумя изображениями только в оттенках серого часто приводит к неинтуитивным результатам.
поскольку ваши предоставленные изображения немного переведены или камера не была неподвижной, я манипулировал вашим фоновым изображением, чтобы добавить некоторый передний план:
фоновое изображение:
видное изображение:
код:
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;
}
}
дает такой результат:
С этой разницей изображение:
в целом трудно вычислить полную сегментацию переднего плана / фона из интерпретаций разности пикселей.
вам, вероятно, придется добавить постобработку, чтобы получить реальную сегментацию, где вы начинаете с маски переднего плана. Не уверенный есть ли еще какие-то стабильные универсальные решения.
как упоминал берак, на практике будет недостаточно использовать одно фоновое изображение, поэтому вам придется вычислять/управлять фоновым изображением с течением времени. Есть много документов, охватывающих эту тему, и afaik пока нет стабильного универсального решения.
вот еще несколько тестов. Я перешел в HSV
цветовое пространство: cv::cvtColor(backgroundImage, HSVbackgroundImagebg, CV_BGR2HSV); cv::cvtColor(currentImage, HSV_currentImage, CV_BGR2HSV);
и выполнял те же операции в этом пространстве, что и это результат:
после добавления некоторого шума на вход:
Я получаю такой результат:
так что, возможно, порог слишком высок. Я по-прежнему рекомендую вам взглянуть на цветовое пространство HSV, но вам, возможно, придется переинтерпретировать "разностное изображение" и масштабировать каждый канал, чтобы объединить их разностные значения.
Я использую Python, это мой результат:
код:
# 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)
обновить, вот 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;
}
похожие ответы: