检测2张图像之间的差异

时间:2013-06-21 09:39:50

标签: c++ image opencv image-processing artificial-intelligence

请查看以下代码

#include <iostream>
#include <opencv2/core/core.hpp>
#include <string>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/background_segm.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat current,currentGrey,next,abs;
    VideoCapture cam1,cam2;

    std:: vector<vector<Point>>contours;
    vector<vector<Point>>contoursPoly(contours.size());

    cam1.open(0);
    cam2.open(0);

    namedWindow("Normal");
    namedWindow("Difference");

    if(!cam1.isOpened())
    {
        cout << "Cam not found" << endl;
        return -1;
    }



    while(true)
    {
        //Take the input
        cam1 >> current;
        currentGrey = current;
        cam2 >> next;

        //Convert to grey
        cvtColor(currentGrey,currentGrey,CV_RGB2GRAY);
        cvtColor(next,next,CV_RGB2GRAY);



        //Reduce Noise
        cv::GaussianBlur(currentGrey,currentGrey,Size(0,0),4);
        cv::GaussianBlur(next,next,Size(0,0),4);

        imshow("Normal",currentGrey);

        //Get the absolute difference
        absdiff(currentGrey,next,abs);
        imshow("Difference",abs);


       for(int i=0;i<abs.rows;i++)
        {
            for(int j=0;j<abs.cols;j++)
            {
                if(abs.at<int>(j,i)>0)
                {
                    cout << "Change Detected" << endl;

                    j = abs.cols+1;
                    i = abs.rows+1;
                }

            }
        }


        if(waitKey(30)>=0)
        {
            break;
        }
    }

}

在这里,我想要做的是每当检测到图像之间的差异时打印一条消息。以下部分是技术

for(int i=0;i<abs.rows;i++)
            {
                for(int j=0;j<abs.cols;j++)
                {
                    if(abs.at<int>(j,i)>0)
                    {
                        cout << "Change Detected" << endl;

                        j = abs.cols+1;
                        i = abs.rows+1;
                    }

                }
            }

不幸的是,它不是在检测到差异时打印消息,而是始终打印消息。为什么是这样?请帮忙。

4 个答案:

答案 0 :(得分:2)

你应该计算两帧之间的均方误差。

MSE = sum((frame1-frame2)^2 ) / no. of pixels

有一个在an OpenCV tutorial计算它的例子。

根据您可以拥有的代码

double getMSE(const Mat& I1, const Mat& I2)
{
    Mat s1;
    absdiff(I1, I2, s1);       // |I1 - I2|
    s1.convertTo(s1, CV_32F);  // cannot make a square on 8 bits
    s1 = s1.mul(s1);           // |I1 - I2|^2

    Scalar s = sum(s1);         // sum elements per channel

    double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels

    if( sse <= 1e-10) // for small values return zero
        return 0;
    else
    {
        double  mse =sse /(double)(I1.channels() * I1.total());
        return mse;
        // Instead of returning MSE, the tutorial code returned PSNR (below).
        //double psnr = 10.0*log10((255*255)/mse);
        //return psnr;
    }
}

您可以在代码中使用它:

   if(getMSE(currentGrey,next) > some_threshold)
        cout << "Change Detected" << endl;

由您来决定MSE的大小,您认为这些大小与图像相同。 此外,您应该使用GaussianBlur()预过滤以减少噪音,就像您已经做过的那样。 @fatih_k建议的blur方法不是高斯滤波器;它是一个盒式过滤器,虽然速度更快但可能会引入伪影。

答案 1 :(得分:1)

图像差异有一些技巧。由于噪音任何2帧可能不相同。

为了减轻噪音的影响,您可以对每一帧使用方法blur()GaussianBlur(),以便可以使用简单的框或高斯滤镜删除细微的细节。

然后,作为相似性标准,您可以获取两个帧的差异,并且在将得到的差异矩阵的绝对值与abs取得之后,您可以对所有元素求和并计算此总和的比率第一帧的总像素和。如果这个比例超过某个阈值,让我们说0.05,那么你可以推断图像帧是完全不同的。

答案 2 :(得分:0)

让我们来看看OpenCV文档中有关cv :: waitKey返回值的内容:

  

如果在指定时间过去之前未按任何键,则返回按下的键的代码或 -1。

所以...循环是无限的&#34;变化检测&#34;每两张图像打印一次,直到程序终止。

答案 3 :(得分:0)

上面描述的函数 getMSE() 可以稍微调整一下,以更好地覆盖无符号整数 8 数据类型。每次结果为负时,无符号整数 8 数据类型的差异将产生 0。通过先将矩阵转换为 double 数据类型,然后计算均方误差,可以避免这个问题。

double getMSE(Mat& I1, Mat& I2)
{
    Mat s1;
    // save the I! and I2 type before converting to float
    int im1type = I1.type();
    int im2type = I2.type();
    // convert to float to avoid producing zero for negative numbers
    I1.convertTo(I1, CV_32F);
    I2.convertTo(I2, CV_32F);
    absdiff(I1, I2, s1);       // |I1 - I2|
    s1.convertTo(s1, CV_32F);  // cannot make a square on 8 bits
    s1 = s1.mul(s1);           // |I1 - I2|^2

    Scalar s = sum(s1);         // sum elements per channel

    double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels

    if( sse <= 1e-10) // for small values return zero
        return 0;
    else
    {
        double  mse =sse /(double)(I1.channels() * I1.total());
        return mse;
        // Instead of returning MSE, the tutorial code returned PSNR (below).
        //double psnr = 10.0*log10((255*255)/mse);
        //return psnr;
    }
     // return I1 and I2 to their initial types
    I1.convertTo(I1, im1type);
    I2.convertTo(I2, im2type);

}

对于小的 mse 值(在 1e-10 下),上面的代码返回零。对于一维图像,项 s.val1 和 s.val[2] 为零。

如果你也想检查一维图像输入(它基本上支持3通道图像),使用以下代码进行测试(使用随机无符号数):

Mat I1(12, 12, CV_8UC1), I2(12, 12, CV_8UC1);
double low = 0;
double high = 255;

cv::randu(I1, Scalar(low), Scalar(high));
cv::randu(I2, Scalar(low), Scalar(high));
double mse = getMSE(I1, I2);
cout << mse << endl;

如果要检查 3D 图像输入,请使用以下代码进行测试(使用随机无符号数):

Mat I1(12, 12, CV_8UC3), I2(12, 12, CV_8UC3);
double low = 0;
double high = 255;

cv::randu(I1, Scalar(low), Scalar(high));
cv::randu(I2, Scalar(low), Scalar(high));
double mse = getMSE(I1, I2);
cout << mse << endl;