CV - 提取两个图像之间的差异

时间:2014-11-20 09:12:30

标签: opencv difference motion-detection

我目前正致力于基于视频监控的入侵系统。为了完成这项任务,我拍摄了我场景背景的快照(假设它完全干净,没有人或移动物体)。然后,我比较从(静态)摄像机获得的帧,并寻找差异。我必须能够检查任何差异,不仅仅是人体形状或其他什么,所以我无法进行特定的特征提取。

通常,我有:

http://postimg.org/image/dxtcp4u8h/

我正在使用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)));

结果如下:

http://postimg.org/image/3kz0o62id/

正如你所看到的那样,手臂被剥离了(由于我猜的颜色差异冲突),这可能不是我想要的。

我考虑添加使用cv::Canny()以检测边缘并填充手臂的缺失部分,但遗憾的是(再一次),它只能在少数情况下解决问题而不是大多数问题。

我是否可以使用任何算法或技术来获取准确的差异报告?

PS:对不起图片。由于我的新订阅,我没有足够的声誉。

修改 我在这里使用灰度图像,但我对任何解决方案都持开放态度。

4 个答案:

答案 0 :(得分:51)

代码中的一个问题是cv::threshold,它只使用1个频道图像。仅以灰度查找两个图像之间的像素“差异”通常会导致不直观的结果。

由于您提供的图像有点翻译或相机不静止,我操纵您的背景图像以添加一些前景:

背景图片:

enter image description here

前景图片:

enter image description here

代码:

    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;
            }
        }

给出这个结果:

enter image description here

使用此差异图像:

enter image description here

一般来说,很难根据逐像素差异解释来计算完整的前景/背景分割。

您可能需要添加后处理内容以获得真正的分段,从前景掩码开始。尚不确定是否有任何稳定的通用解决方案。

正如berak所提到的,在实践中使用单个背景图像是不够的,因此您将不得不计算/管理您的背景图像。有很多论文涉及这个主题,但还没有稳定的通用解决方案。

这里还有一些测试。我转换为HSV颜色空间:cv::cvtColor(backgroundImage, HSVbackgroundImagebg, CV_BGR2HSV); cv::cvtColor(currentImage, HSV_currentImage, CV_BGR2HSV);并在此空间中执行相同的操作,从而产生以下结果:

enter image description here

在输入中添加一些噪音之后:

enter image description here

我得到了这个结果:

enter image description here

所以也许阈值有点太高了。我仍然鼓励您查看HSV色彩空间,但您可能需要重新解释“差异图像”并重新缩放每个通道以组合它们的差异值。

答案 1 :(得分:16)

我使用Python,这是我的结果:

enter image description here

代码:

# 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;
}

类似的答案:

  1. CV - Extract differences between two images

  2. While finding a difference between 2 pictures OpenCV difference is bigger than it is supposed to be

答案 2 :(得分:2)

获得两个图像之间精确像素差异的另一种技术是使用在论文Image Quality Assessment: From Error Visibility to Structural Similarity中首次引入的结构相似指数(SSIM)。该方法可用于确定由于微小的图像差异而导致两个图像是否相同和/或展示差异。 scikit-image library中已将SSIM实现为skimage.measure.compare_ssim()

compare_ssim()函数返回一个score和一个差异图像diffscore代表两个输入图像之间的平均结构相似性指标,并且可以落在范围[-1,1]之间,其值更接近一个代表更高相似性的值。但是,由于您只对两个图像的不同之处感兴趣,因此我们将重点关注diff图像。具体地,diff图像包含实际图像差异,其中较暗的区域具有更大的视差。较大的差异区域以黑色突出显示,而较小的差异则以灰色突出显示。


使用这两个输入图像

我们得到这个结果

  

图片相似度:0.9587009832317672

比较两张图片后的SSIM得分显示它们非常相似

from skimage.metrics import structural_similarity as ssim

import cv2

image1 = cv2.imread('1.png')
image2 = cv2.imread('2.png')

# Convert images to grayscale
image1_gray = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
image2_gray = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)

# Compute SSIM between two images
(score, diff) = ssim(image1_gray, image2_gray, full=True)
print("Image similarity:", score)

# 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")

cv2.imshow('diff', diff)
cv2.waitKey()

已通过Python v3.7.4和skimage v0.17测试。 compare_ssim似乎已在skimage v0.18中删除。

答案 3 :(得分:0)

这是众所周知的经典计算机视觉问题,称为背景减法。有很多方法可以用来解决此问题,其中大多数方法已经实现,因此,我认为您应该首先了解多种现有算法,以下是其中大多数方法的开源实现:https://github.com/andrewssobral/bgslibrary(I亲自发现SUBSENSE效果最好,但速度却很慢)