【问题标题】:CV - Extract differences between two imagesCV - 提取两个图像之间的差异
【发布时间】:2015-01-18 01:58:55
【问题描述】:

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

通常,我有:

我使用的是 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:对不起图片。由于我是新订阅的,我没有足够的声誉。

编辑 我在这里使用灰度图像,但我愿意接受任何解决方案。

【问题讨论】:

  • 也许可以试试not to reinvent the wheel
  • 看看,已经内置了什么(如果效果更好的话),是的。
  • 你转换成灰度了吗?如果您不想使用 openCV 类:尝试计算每个通道的差异并将它们组合,尝试 HSV 图像的差异。如果您想使用现有技术,请尝试 ViBe。请记住,即使是光照变化也会是“任何差异,不仅仅是人体形状或其他任何差异”,这对于大多数背景减除方法来说可能是一个问题。
  • 一般来说,随着时间的推移构建一个背景模型,使用许多图像,将击败任何一次性方法。另外,Vibe
  • 好吧,这里的问题不是建立背景模型,而是更好地找到“差异”(这在灰度图像中很难)。 @ValentinTrinqué 您能否添加原始单张图片,我已经实现了您的代码的多通道版本,但我无权访问原始图片。试图裁剪你的双重图像,但它们看起来有点翻译......

标签: image opencv image-processing computer-vision difference


【解决方案1】:

您的代码中的一个问题是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 颜色空间,但您可能必须重新解释“差异图像”并重新缩放每个通道以组合它们的差异值。

【讨论】:

  • @Gizmo 抱歉,我目前不在该领域工作。不错的背景建模方法是 ViBe、高斯混合或简单帧差异 ((currentFrame - lastFrame) & (nextFrame - currentFrame)),但在文献中可能有更新更好的最先进方法。
【解决方案2】:

我使用 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;
}

类似的答案:

  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

【讨论】:

  • 上面的 Python 代码是一个很好的解决方案。如果它是一个安全应用程序,那么这只是我的意见,但我猜你将无法拍摄一张快照然后将其用作参考。照明条件和移动的物体(即使是一小部分)可能会破坏您的图像。作为一个建议,最好每隔一段时间(比如 1 秒)从视频源中连续拍摄快照,然后比较最后三个或四个以寻找差异。所以如果只有三个帧是相同的,那么第四个肯定有一些额外的东西。
  • 众所周知,Python 是快速原型设计的不错选择。尽管细节与 C/C++/Python 等不同语言不同,但算法(或主要思想)是相同的。当然这是一个玩具代码,只是为这个问题写的,指定为两个图像。但是,如果进行更多的测试和跟踪,例如threshold the sliding-subtractions with morph-ops,则可以扩展视频。请注意,没有两张图像完全相同,因为存在白噪声。
【解决方案3】:

这是著名的经典计算机视觉问题,称为背景减法。有很多方法可以解决这个问题,大部分已经实现了,所以我认为你应该先看看现有的多种算法,这里是其中大部分的开源实现:https://github.com/andrewssobral/bgslibrary(我个人发现SUBSENSE 提供最佳结果,但速度非常慢)

【讨论】:

    【解决方案4】:

    获得两个图像之间精确像素差异的另一种技术是使用论文Image Quality Assessment: From Error Visibility to Structural Similarity中首次介绍的结构相似性指数(SSIM)。此方法可用于确定两个图像是否相同和/或由于微小的图像差异而显示差异。 SSIM 已经在scikit-image library 中实现,用于图像处理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 中删除。

    【讨论】:

    • 您能否更新您的答案,因为在 skimage v0.18 中删除了 compare_ssim 函数
    • @user889030 感谢更新。你知道它是被永久删除了,还是被移到了其他模块?
    • 谢谢,我检查过他们已将其移至另一个模块
    • @nathancy 你是个传奇!
    • @JosephAdam 请参阅 detect and visualize differences between two images 以获取最新版本
    猜你喜欢
    • 2011-08-09
    • 2021-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-10
    相关资源
    最近更新 更多