我目前正在研究基于视频监控的入侵系统。为了完成此任务,我拍摄了场景背景的快照(假设它完全干净,没有人或移动的物体)。然后,我比较从(静态)摄像机获得的帧并寻找差异。我必须能够检查任何差异,不仅仅是人体形状或其他什么,所以我无法提取特定的特征。
通常,我有:
我正在使用 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:抱歉图片。由于我是新订阅的,我没有足够的声誉。
编辑 我在这里使用灰度图像,但我愿意接受任何解决方案。
代码中的一个问题是
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;
}
}
给出这个结果:
这张差异图像:
一般来说,很难从像素差异解释中计算出完整的前景/背景分割。
您可能需要添加后处理内容才能获得真正的分割,从前景蒙版开始。不确定是否有稳定的通用解决方案。
正如 berak 提到的,实际上,使用单个背景图像是不够的,因此您必须随着时间的推移计算/管理您的背景图像。有很多论文涉及这个主题,但目前还没有稳定的通用解决方案。
这里还有一些测试。我转换为
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;
}
类似答案:
获得两幅图像之间精确像素差异的另一种技术是使用论文图像质量评估:从错误可见性到结构相似性中首次介绍的结构相似性指数(SSIM)。该方法可用于确定两个图像是否相同和/或由于微小的图像差异而显示差异。 SSIM 已在 scikit-image 库 中实现,用于图像处理,如
skimage.metrics.structural_similarity()
structural_similarity()
函数返回score
和差异图像diff
。 score
表示两个输入图像之间的平均结构相似性指数,可以落在 [-1,1]
范围内,值越接近表示相似性越高。但由于您只对两个图像的不同之处感兴趣,因此 diff
图像才是我们将关注的焦点。具体来说,diff
图像包含实际图像差异,较暗区域具有更大的视差。较大的视差区域以黑色突出显示,而较小的差异区域以灰色突出显示。
使用这两个输入图像
我们得到这个结果
比较两幅图像后的SSIM分数表明它们非常相似
图像相似度:95.8648%
from skimage.metrics import structural_similarity
import cv2
# Load images as grayscale
image1 = cv2.imread('1.png', 0)
image2 = cv2.imread('2.png', 0)
# Compute SSIM between the two images
(score, diff) = structural_similarity(image1, image2, full=True)
# The diff image contains the actual image differences between the two images
# and is represented as a floating point data type in the range [0,1]
# so we must convert the array to 8-bit unsigned integers in the range
# [0,255] image1 we can use it with OpenCV
diff = (diff * 255).astype("uint8")
print("Image Similarity: {:.4f}%".format(score * 100))
cv2.imshow('diff', diff)
cv2.waitKey()