1

我正在处理以下代码

#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

4 回答 4

2

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

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

在OpenCV 教程中有一个计算它的例子。

基于该代码,您可以拥有

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()像你已经做的那样预过滤以减少噪音。blur@fatih_k 建议的方法不是高斯滤波器;它是一个盒子过滤器,虽然速度更快可能会引入伪影。

于 2013-06-21T13:54:00.753 回答
1

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

为了减轻噪声的影响,您可以使用方法blur()GaussianBlur()对每一帧使用简单的框或高斯滤波器来去除微小的细节。

然后,作为相似性标准,您可以取两帧的差值,然后将所得差分矩阵的绝对值与abs相加,您可以将所有元素相加,并计算该和与第一帧的总像素和的比率. 如果这个比率超过某个阈值,比如说 0.05,那么您可以推断出图像帧有很大的不同。

于 2013-06-21T10:33:54.697 回答
0

让我们看一下 OpenCV 文档中关于 cv::waitKey 返回值的内容:

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

所以......循环是无限的,并且每两个比较的图像打印一次“检测到的变化”,直到程序终止。

于 2013-06-21T10:07:35.013 回答
0

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

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),上面的代码返回零。对于 1D 图像,项 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;
于 2021-04-26T06:55:42.640 回答