【opencv/videoio module】(一)Video Input with OpenCV and similarity measurement
目录
- 说在前面
- SourceCode
- 读取视频流
- 图像相似度
- Code
- Result
说在前面
- opencv版本:4.0.1
- 操作系统:win10
- vs版本:2017
- 官方文档:Video Input with OpenCV and similarity measurement
- 其他说明:自学,记录,demo
SourceCode
读取视频流
- 所有的视频操作需要类VideoCapture(基于开源库FFmpeg)
- 视频由一系列图像组成,每一张图称为一帧。在视频文件中一个重要属性为帧率,用于度量帧与帧之间的时间间隔
- 我们使用 cv::VideoCapture::VideoCapture 或者 cv::VideoCapture::open 来初始化。参数有两种:
- 一个整数,代表摄像头设备,值取决于操作系统(一般是0/1/2,递增)
- 文件名(路径)
const string sourceReference = "test1.avi",sourceCompareWith = "test2.avi";VideoCapture captRefrnc(sourceReference); // or VideoCapture captUndTst; captUndTst.open(sourceCompareWith);
- 我们可以使用cv::VideoCapture::isOpened来判断是否成功打开视频流
if ( !captRefrnc.isOpened()){cout << "Could not open reference " << sourceReference << endl;return -1;}
- 视频流会在析构函数调用时自动关闭,但也可使用cv::VideoCapture::release 手动关闭
captRefrnc.release();
- 由于每一帧都是简单的图像,我们可以使用Mat来操作
- 使用重载的操作符>>
- 使用cv::VideoCapture::read
Mat frameReference, frameUnderTest; captRefrnc >> frameReference;//当前视频文件还未读取完的时候 captUndTst.read(frameUnderTest);
- 判断是否到视频文件末(上面读取的时候若已经无帧可读,则Mat为空)
if( frameReference.empty() || frameUnderTest.empty()) {// exit the program }
- 获取VideoCapture的属性,支持的属性下面的表格(未显示摄像头相关,全部见 ALL)
Size refS = Size((int) captRefrnc.get(CAP_PROP_FRAME_WIDTH),(int) captRefrnc.get(CAP_PROP_FRAME_HEIGHT)),cout << "Reference frame resolution: Width=" << refS.width << " Height=" << refS.height << " of nr#: " << captRefrnc.get(CAP_PROP_FRAME_COUNT) << endl;
- 设置VideoCapture的属性
captRefrnc.set(CAP_PROP_POS_MSEC, 1.2); // go to the 1.2 second in the video captRefrnc.set(CAP_PROP_POS_FRAMES, 10); // go to the 10th frame of the video // now a read operation would read the frame at the set position
名称 | 意义 |
---|---|
CAP_PROP_POS_MSEC | 当前帧所在视频流的位置(单位:毫秒) |
CAP_PROP_POS_FRAMES | 下一帧是第几帧(从0开始) |
CAP_PROP_POS_AVI_RATIO | 视频文件相对位置(0:靠近视频头;1:靠近视频尾) |
CAP_PROP_FRAME_WIDTH | 帧宽度(像素) |
CAP_PROP_FRAME_HEIGHT | 帧高度(像素) |
CAP_PROP_FPS | 帧率(frame per second) |
CAP_PROP_FOURCC | 四个字符的编码类型 |
CAP_PROP_FRAME_COUNT | 视频文件帧总数 |
CAP_PROP_FORMAT | VideoCapture::retrieve()返回的Mat格式 |
图像相似度
在进行视频处理的时候有时某些操作的结果是肉眼不可见的,所以我们需要逐帧进行对比(操作前后的同一帧图像)
最简单的计算方式为均方差(mean squad error)
MSE=1c∗i∗j∑(I1−I2)2MSE = \frac{1}{c*i*j} \sum{(I_1-I_2)^2}MSE=c∗i∗j1∑(I1−I2)2
其中I1、I2I_1、I_2I1、I2为 iii 行 jjj 列 ccc 通道的图像,后半部分为两个图像所有对应像素的对应通道的差值平方和的求和
PSNR=10⋅log10(MAXI2MSE)PSNR = 10 \cdot \log_{10} \left( \frac{MAX_I^2}{MSE} \right)PSNR=10⋅log10(MSEMAXI2)
上面的为PSNR公式,其中MAXIMAX_IMAXI为通道的最大值(例如CV_8U就是255);PSNR值越小,表明差异越大double getPSNR(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 bitss1 = s1.mul(s1); // |I1 - I2|^2Scalar s = sum(s1); // sum elements per channeldouble sse = s.val[0] + s.val[1] + s.val[2]; // sum channelsif( sse <= 1e-10) // for small values return zeroreturn 0;else{double mse =sse /(double)(I1.channels() * I1.total());double psnr = 10.0*log10((255*255)/mse);return psnr;} }
但是这种计算方式在实际应用中可能与人眼的感觉结果不一致(啥意思?)
另一种计算方式,structural similarity
用了高斯模糊,咱先不管这部分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); // cannot calculate on one byte large valuesi2.convertTo(I2, d);Mat I2_2 = I2.mul(I2); // I2^2Mat I1_2 = I1.mul(I1); // I1^2Mat I1_I2 = I1.mul(I2); // I1 * I2/***********************PRELIMINARY COMPUTING ******************************/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 = average of ssim mapreturn mssim; }
Code
#include <iostream> // for standard I/O
#include <string> // for strings
#include <iomanip> // for controlling float print precision
#include <sstream> // string to number conversion#include <opencv2/core.hpp> // Basic OpenCV structures (cv::Mat, Scalar)
#include <opencv2/imgproc.hpp> // Gaussian Blur
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp> // OpenCV window I/Ousing namespace std;
using namespace cv;double getPSNR(const Mat& I1, const Mat& I2);
Scalar getMSSIM(const Mat& I1, const Mat& I2);int main()
{stringstream conv;const string sourceReference = "Megamind.avi", sourceCompareWith = "Megamind_bugy.avi";int psnrTriggerValue=35, delay=10;int frameNum = -1; // Frame counterVideoCapture captRefrnc(sourceReference), captUndTst(sourceCompareWith);if (!captRefrnc.isOpened()){cout << "Could not open reference " << sourceReference << endl;return -1;}if (!captUndTst.isOpened()){cout << "Could not open case test " << sourceCompareWith << endl;return -1;}Size refS = Size((int)captRefrnc.get(CAP_PROP_FRAME_WIDTH),(int)captRefrnc.get(CAP_PROP_FRAME_HEIGHT)),uTSi = Size((int)captUndTst.get(CAP_PROP_FRAME_WIDTH),(int)captUndTst.get(CAP_PROP_FRAME_HEIGHT));if (refS != uTSi){cout << "Inputs have different size!!! Closing." << endl;return -1;}const char* WIN_UT = "Under Test";const char* WIN_RF = "Reference";// WindowsnamedWindow(WIN_RF, WINDOW_AUTOSIZE);namedWindow(WIN_UT, WINDOW_AUTOSIZE);moveWindow(WIN_RF, 400, 0); //750, 2 (bernat =0)moveWindow(WIN_UT, refS.width, 0); //1500, 2cout << "Reference frame resolution: Width=" << refS.width << " Height=" << refS.height<< " of nr#: " << captRefrnc.get(CAP_PROP_FRAME_COUNT) << endl;cout << "PSNR trigger value " << setiosflags(ios::fixed) << setprecision(3)<< psnrTriggerValue << endl;Mat frameReference, frameUnderTest;double psnrV;Scalar mssimV;for (;;) //Show the image captured in the window and repeat{captRefrnc >> frameReference;captUndTst >> frameUnderTest;if (frameReference.empty() || frameUnderTest.empty()){cout << " < < < Game over! > > > ";break;}++frameNum;cout << "Frame: " << frameNum << "# ";psnrV = getPSNR(frameReference, frameUnderTest);cout << setiosflags(ios::fixed) << setprecision(3) << psnrV << "dB";if (psnrV < psnrTriggerValue && psnrV){mssimV = getMSSIM(frameReference, frameUnderTest);cout << " MSSIM: "<< " R " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[2] * 100 << "%"<< " G " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[1] * 100 << "%"<< " B " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[0] * 100 << "%";}cout << endl;imshow(WIN_RF, frameReference);imshow(WIN_UT, frameUnderTest);char c = (char)waitKey(delay);if (c == 27) break;}return 0;
}double getPSNR(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 bitss1 = s1.mul(s1); // |I1 - I2|^2Scalar s = sum(s1); // sum elements per channeldouble sse = s.val[0] + s.val[1] + s.val[2]; // sum channelsif (sse <= 1e-10) // for small values return zeroreturn 0;else{double mse = sse / (double)(I1.channels() * I1.total());double psnr = 10.0 * log10((255 * 255) / mse);return psnr;}
}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); // cannot calculate on one byte large valuesi2.convertTo(I2, d);Mat I2_2 = I2.mul(I2); // I2^2Mat I1_2 = I1.mul(I1); // I1^2Mat I1_I2 = I1.mul(I2); // I1 * I2/*************************** END INITS **********************************/Mat mu1, mu2; // PRELIMINARY COMPUTINGGaussianBlur(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 = average of ssim mapreturn mssim;
}
Result
- 测试视频为官方的那个,一个原视频、一个压缩后的视频
END-2019.7.5┗( T﹏T )┛
【opencv/videoio module】(一)Video Input with OpenCV and similarity measurement相关推荐
- Video Input with OpenCV and similarity measurement(使用opencv测量两个视频的相似度)
Video Input with OpenCV and similarity measurement(使用opencv测量两个视频的相似度) 参考示例程序及视频文件下载: 一.先决条件 1.两种检查相 ...
- load opencv videoio gstreamer453 64d dll failed 打开摄像头慢
环境:win10+opencv4.5.3+opencv_contrib-4.5.3+vs2019 状态:禁用笔记本自带摄像头,使用webcam 问题 配置好opencv和contrib库后,通过ope ...
- opencv videoio无法读取rstp_使用一行Python代码从图像读取文本
作者 | Dario Radečić 编译 | VK 来源 | Towards DataScience 处理图像不是一项简单的任务.对你来说,作为一个人,很容易看着某样东西然后马上知道你在看什么.但电 ...
- opencv 脸部识别_实时面部识别如何与OpenCV一起使用?
opencv 脸部识别 An eazy code from eazy ciphers 易密码的易密码 Are you keen to learn about the implementation of ...
- OpenCV入门(十七)快速学会OpenCV 16 视频处理
OpenCV入门(十七)快速学会OpenCV 16 视频处理 1.构造VideoCapture对象 2.构造VideoWriter对象 3.视频操作基础 3.1 读取视频帧 3.2 播放视频文件 3. ...
- opencv resize_树莓派监控摄像头python+picamera或openCV
1.在raspi-config中使能摄像头 打开树莓派终端,输入sudo raspi-config 完成后重启树莓派 2.检查摄像头运行情况 vcgencmd get_camera raspistil ...
- python卸载opencv包_Ubuntu下安装与卸载opencv模块
opencv安装 因工程需要,想在python中调用opencv import cv2 现在记录一下如何在Linux系统(ubutun)下安装该模块: 在自己的电脑上安装成功,现记录一下安装过程: s ...
- cv2.error: OpenCV(4.1.0) C:\projects\opencv-python\opencv\modules\highgui\src\window.cpp:352: error:
运行环境:python 3.6.0 初学 opencv,看着程序跟书本都一样啊,突然报了以下错误 Traceback (most recent call last):File "E:/Pyt ...
- OpenCV开发笔记(一):OpenCV介绍、编译
若该文为原创文章,未经允许不得转载 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/100072151 各位读者,知识无穷而人力有穷, ...
最新文章
- 谷歌“亲儿子”竟然成为了NLP革新者
- Spring配置问题——元素 quot;context:component-scanquot; 的前缀 quot;contextquot; 未绑定...
- zabbix监控防火墙和交换机
- pandas库Series使用和ix、loc、iloc基础用法
- 【阿里妈妈营销科学系列】第三篇:受众沟通和品牌认知评价
- 开源开放 | 熵简科技 AI Lab 开源金融领域中文预训练语言模型 FinBERT
- MySQL流浪记(四)—— DDL和DML区别与介绍
- 漫画:下辈子你还当程序员么?
- windows下MongoDB数据库的安装
- jsp mysql论坛_体育论坛ssm,mysql)
- HTML Text Editor
- Unity显示(内嵌)网页- UniWebView的使用教程
- html设置div大小位置不变,div大小 div固定大小设置
- 84.常用的返回QuerySet对象的方法使用详解:select_related, prefetch_related
- 推荐5个设计素材网站
- 使用OAS Validator帮助你规范OpenAPI Spec文档
- android12.0(S) 从SD卡导入vCard文件到通讯录 号码带“-“ 如何把横线去除
- 创建虚拟机、安装centos6,centos7系统,图形化界面
- Telnet 1521端口连接失败问题,经过四天的努力终于解决!
- nth_element详解