请查看以下代码
#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;
}
}
}
不幸的是,它不是在检测到差异时打印消息,而是始终打印消息。为什么是这样?请帮忙。
答案 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;