您的位置:首页 > 产品设计 > UI/UE

OpenCV学习笔记18 OpenCV高级GUI和多媒体模块 HIGHGUI MODULE (二)

2014-07-23 15:10 537 查看

4.2 OpenCV视频输入和相似性度量

视频有两种获得形式:摄像头传来的实时视频流 和 读取存储介质中的视频文件。在OpenCV中,视频捕获的所有函数都集成在了 VideoCaptureC++ 类里面了,它本身底层是依赖FFmpeg开源库。视频是由连续的图像组成的,每一幅图像称为一帧(frame)。
首先,定义一个 VideoCapture类的对象来打开和获取视频流,具体可以通过它的构造函数或open成员函数实现。如果参数是 路径名+文件名 组成的字符串,则打开指定的视频文件。如果参数是一个int型数,则将对象绑定到一台摄像机上,该int型数代表了摄像机的设备号。如果只连接了一个摄像机,则参数为 0 即可。
调用析构函数时,会自动关闭视频。如果希望提前关闭,则调用 release 成员函数。
const string sourceReference = argv[1],sourceCompareWith = argv[2];

VideoCapture captRefrnc(sourceReference);
// 或者
VideoCapture captUndTst;
captUndTst.open(sourceCompareWith);
用 isOpened 函数检查视频是否成功打开了。

if ( !captRefrnc.isOpened())
{
cout  << "Could not open reference " << sourceReference << endl;
return -1;
}
因为视频中的每一帧都是一副图像,只需要将 VideoCapture 对象里的每一帧提取出来,放到Mat对象存储即可进行进一步处理了。有两种基本的提取方法:调用
read 成员函数或者使用重载的操作符 >> 。这两种方法实际上包含了读取视频帧(grab)和解码(retrieve)两个步骤,也可以调用grab
retrieve 成员函数显示地进行这两项操作从而替代 read函数。

Mat frameReference, frameUnderTest;
captRefrnc >> frameReference;
captUndTst.read(frameUnderTest);
如果视频无法捕获,上面的操作就会返回孔的 Mat 对象。可以用下面的代码检查。
if( frameReference.empty()  || frameUnderTest.empty())
{
// 退出程序
}
一个视频里,除了一帧帧的内容以外,还包含了许多其他的和视频有关的信息。但是这些视频属性信息在一些情况下都是以较短的字符串表示的(不多于4个字节),因此有一个通用的成员函数get用于获取这些视频属性,返回一个 double 型(8个字节)的值。它唯一的参数是想要获取的属性信息的ID,具体参见API文档。这里获取视频的分辨率和帧数信息。
Size refS = Size((int) captRefrnc.get(CV_CAP_PROP_FRAME_WIDTH),
(int) captRefrnc.get(CV_CAP_PROP_FRAME_HEIGHT)),

cout << "参考帧的分辨率: 宽度=" << refS.width << "  高度=" << refS.height
<< " of nr#: " <<
<< endl;
当需要设置视频的属性信息的值时,可以调用set 成员函数进行设置。函数的第一个参数是属性信息的ID,第二个参数是要设置的 double 型属性值。返回 true 表示设定成功,返回 false 表示设定失败。
captRefrnc.set(CV_CAP_PROP_POS_MSEC, 1.2);  // 跳转到视频1.2秒的位置
captRefrnc.set(CV_CAP_PROP_POS_FRAMES, 10); // 跳转到视频的第10帧
// 然后重新调用read来得到你刚刚设置的那一帧


图像相似性的比较算法:PSNR SSIM

比较两个视频的细微差异时,需要逐帧比较。比较常用的算法是 PSNR (Peak signal-to-noise ratio,峰值信噪比),这是使用
最小均方误差
(MSE) 来判断差异的最简单的方法。假设有两幅图像I1和I2,它们的行列数分别是i,j,有c个通道。

MSE = 1/(c*i*j)∑(I1-I2)*(I1-I2)
PSNR公式为:
PSNR = 10 * log10(MAXI*MAXI / MSE)
每个像素的每个通道占用一个字节,值域 [0, 255],这里MAXI*MAXI是一个像素的最大有效值,也就是255。当两幅图像相同时,MSE值为0。这样会导致PSNR公式中除以0。所以需要单独处理这种情况。由于像素的动态范围很广,所以使用对数变换来缩小范围。PSNR算法的C++代码如下。
double getPSNR(const Mat& I1, const Mat& I2)
{
Mat s1;
absdiff(I1, I2, s1);       // |I1 - I2|
s1.convertTo(s1, CV_32F);  // 不能在8位矩阵上做平方运算
s1 = s1.mul(s1);           // |I1 - I2|^2

Scalar s = sum(s1);         // 叠加每个通道的元素

double sse = s.val[0] + s.val[1] + s.val[2]; // 叠加所有通道

if( sse <= 1e-10) // 如果值太小就直接等于0
return 0;
else
{
double  mse =sse /(double)(I1.channels() * I1.total());
double psnr = 10.0*log10((255*255)/mse);
return psnr;
}
}
在使用例程提供的视频进行比较时,这个值大约在30到50之间,数字越大表明视频间的差异越小。PSNR算法简单,易于检查相似性,计算速度快。但是其呈现的差异值和人类眼睛的主管感受不成比例。因此,结构相似性(structural similarity)算法旨在纠正这个问题。

请参考下面关于SSIM的文章:”Z. Wang, A. C. Bovik, H. R. Sheikh and E. P. Simoncelli, “Image quality assessment: From error visibility to structural similarity,” IEEE Transactions on Image Processing, vol. 13, no. 4, pp. 600-612, Apr. 2004.文章下载下来看了一下,具有学习价值,看完文章再看代码就会非常好理解了。
Scalar getMSSIM( const Mat& i1, const Mat& i2)
{
const double C1 = 6.5025, C2 = 58.5225;
/***************************** INITS **********************************/
int d     = CV_32F;

Mat I1, I2;
i1.convertTo(I1, d);           // 不能在单字节像素上进行计算,范围不够。
i2.convertTo(I2, d);

Mat I2_2   = I2.mul(I2);        // I2^2
Mat I1_2   = I1.mul(I1);        // I1^2
Mat I1_I2  = I1.mul(I2);        // I1 * I2

/***********************初步计算 ******************************/

Mat mu1, mu2;   //
GaussianBlur(I1, mu1, Size(11, 11), 1.5);
GaussianBlur(I2, mu2, Size(11, 11), 1.5);

Mat mu1_2   =   mu1.mul(mu1);
Mat mu2_2   =   mu2.mul(mu2);
Mat mu1_mu2 =   mu1.mul(mu2);

Mat sigma1_2, sigma2_2, sigma12;

GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
sigma1_2 -= mu1_2;

GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
sigma2_2 -= mu2_2;

GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
sigma12 -= mu1_mu2;

///////////////////////////////// 公式 ////////////////////////////////
Mat t1, t2, t3;

t1 = 2 * mu1_mu2 + C1;
t2 = 2 * sigma12 + C2;
t3 = t1.mul(t2);              // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))

t1 = mu1_2 + mu2_2 + C1;
t2 = sigma1_2 + sigma2_2 + C2;
t1 = t1.mul(t2);               // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))

Mat ssim_map;
divide(t3, t1, ssim_map);      // ssim_map =  t3./t1;

Scalar mssim = mean( ssim_map ); // mssim = ssim_map的平均值
return mssim;
}
该代码会对图像中的每个通道都返回一个相似指数,以 Scalar 类型表示,取值范围在0到1之间,为1时表示两幅图像完全相同。尽管该方法产生的数据更优秀,但是高斯模糊耗时比较长,所以在实时环境(每秒24帧)中,更多采用PSNR算法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐