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

Val*_*qué 30 opencv motion-detection difference

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

通常,我有:

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)));
Run Code Online (Sandbox Code Playgroud)

结果如下:

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

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

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

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

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

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

Mic*_*cka 51

您的代码中的一个问题是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;
            }
        }
Run Code Online (Sandbox Code Playgroud)

给出这个结果:

在此输入图像描述

有了这个差异图像:

在此输入图像描述

通常,很难从像素方面的差异解释计算完整的前景/背景分割.

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

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

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

在此输入图像描述

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

在此输入图像描述

我得到这个结果:

在此输入图像描述

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


Kin*_*t 金 18

我使用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)
Run Code Online (Sandbox Code Playgroud)

更新,这是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;
}
Run Code Online (Sandbox Code Playgroud)

类似的答案:

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

  2. 虽然找到2张图片之间的差异,但OpenCV差异大于预期

  • 众所周知,Python是快速原型设计的不错选择。尽管C/C++/Python等不同语言的细节有所不同,但算法(或主要思想)是相同的。当然,这是一个玩具代码,只是为这个问题编写的,指定了两个图像。但是,如果进行更多的测试和跟踪,例如“使用变形操作对滑动减法进行阈值设置”,则可以扩展视频。请注意,没有两个图像是相同的,因为存在白噪声。 (2认同)

nat*_*ncy 6

另一种获得两幅图像之间精确像素差异的技术是使用结构相似性指数 (SSIM),首先在论文图像质量评估:从错误可见性到结构相似性中引入。此方法可用于确定两个图像是否相同和/或由于微小的图像差异而展示差异。SSIM 已经在scikit-image 库中实现,用于图像处理skimage.measure.compare_ssim()

compare_ssim()函数返回一个score和一个差异图像,diff。的score表示两个输入图像之间的平均结构相似性索引与范围之间可以落下[-1,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()
Run Code Online (Sandbox Code Playgroud)

使用 Python v3.7.4 和 skimage v0.17 进行测试。compare_ssim似乎在 skimage v0.18 中被删除了。