【opencv】教程代码 —videoio(2)将两个视频的每一帧逐一读取并计算其PSNR 和MSSIM...

本教程开始介绍的源代码将对每一帧执行PSNR测量,并且只对PSNR低于输入值的帧进行SSIM测量。为了可视化的目的,我们在OpenCV窗口中展示两幅图像,并将PSNR和MSSIM值打印到控制台。期望看到如下内容:

16ab19844294d9c88f0cbdb1598fc49e.png

video-input-psnr-ssim.cpp  将两个视频的每一帧逐一读取并计算其峰值信号噪声比(PSNR) 和 结构相似性指标(MSSIM)

4abe1f8f4cde8991df5f902cc8ecfb37.png

#include <iostream> // 标准输入输出流
#include <string>   // 字符串操作库
#include <iomanip>  // 输入输出流格式控制
#include <sstream>  // 字符串与数字转换


#include <opencv2/core.hpp>     // OpenCV基础结构 (cv::Mat, Scalar)
#include <opencv2/imgproc.hpp>  // 高斯模糊处理
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>  // OpenCV窗口输入输出


using namespace std;
using namespace cv;


double getPSNR(const Mat& I1, const Mat& I2); // 声明计算PSNR值的函数
Scalar getMSSIM(const Mat& I1, const Mat& I2); // 声明计算MSSIM值的函数


static void help() // 帮助文本输出函数
{
    cout
        << "------------------------------------------------------------------------------" << endl
        << "This program shows how to read a video file with OpenCV. In addition, it "
        << "tests the similarity of two input videos first with PSNR, and for the frames "
        << "below a PSNR trigger value, also with MSSIM."                                   << endl
        << "Usage:"                                                                         << endl
        << "./video-input-psnr-ssim <referenceVideo> <useCaseTestVideo> <PSNR_Trigger_Value> <Wait_Between_Frames> " << endl
        << "--------------------------------------------------------------------------"     << endl
        << endl;
}


int main(int argc, char *argv[]) // 主函数
{
    help(); // 显示帮助文本


    if (argc != 5) // 检查输入参数数量
    {
        cout << "Not enough parameters" << endl;
        return -1;
    }


    stringstream conv; // 创建字符串流


    const string sourceReference = argv[1], sourceCompareWith = argv[2]; // 引用视频和待比较视频路径
    int psnrTriggerValue, delay; // PSNR阈值和帧间延迟
    conv << argv[3] << endl << argv[4]; // 将参数放入字符串流
    conv >> psnrTriggerValue >> delay; // 从字符串流提取参数值


    int frameNum = -1; // 帧计数器


    VideoCapture 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"; // 参考视窗名称


    // 创建窗口
    namedWindow(WIN_RF, WINDOW_AUTOSIZE);
    namedWindow(WIN_UT, WINDOW_AUTOSIZE);
    moveWindow(WIN_RF, 400, 0);         
    moveWindow(WIN_UT, refS.width, 0);         


    // 输出参考帧分辨率和视频帧数
    cout << "Reference frame resolution: Width=" << refS.width << "  Height=" << refS.height
         << " of nr#: " << captRefrnc.get(CAP_PROP_FRAME_COUNT) << endl;


    // 输出PSNR阈值信息
    cout << "PSNR trigger value " << setiosflags(ios::fixed) << setprecision(3)
         << psnrTriggerValue << endl;


    Mat frameReference, frameUnderTest; // 创建存储参考帧和待测帧的Mat对象
    double psnrV; // PSNR值
    Scalar mssimV; // MSSIM值


    for(;;) // 无限循环,用于显示视频帧并处理
    {
        captRefrnc >> frameReference; // 读取参考帧
        captUndTst >> frameUnderTest; // 读取待测帧


        if (frameReference.empty() || frameUnderTest.empty()) // 如果读取为空,则表明视频结束
        {
            cout << " < < <  Game over!  > > > ";
            break;
        }


        ++frameNum; // 增加帧数
        cout << "Frame: " << frameNum << "# ";


        // 计算PSNR值
        psnrV = getPSNR(frameReference,frameUnderTest);
        cout << setiosflags(ios::fixed) << setprecision(3) << psnrV << "dB";


        // 如果PSNR值低于阈值,并且非零,则计算MSSIM值
        if (psnrV < psnrTriggerValue && psnrV)
        {
            mssimV = getMSSIM(frameReference, frameUnderTest);


            // 输出MSSIM的RGB通道值
            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);


        // 等待按键,如果按下ESC键,则退出循环
        char c = (char)waitKey(delay);
        if (c == 27) break;
    }


    return 0; // 返回0,表明程序正常退出
}


// 计算PSNR值的函数
double getPSNR(const Mat& I1, const Mat& I2)
{
    Mat s1;
    absdiff(I1, I2, s1);       // 计算I1和I2的绝对差值 |I1 - I2|
    s1.convertTo(s1, CV_32F);  // 将结果转换为32位浮点数,因为不能在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()); // 计算均方误差MSE
        double psnr = 10.0 * log10((255 * 255) / mse); // 根据MSE计算PSNR值
        return psnr;
    }
}


// 计算MSSIM值的函数
Scalar getMSSIM(const Mat& i1, const Mat& i2)
{
    const double C1 = 6.5025, C2 = 58.5225; // 定义常数C1和C2
    /***************************** 初始化 **********************************/
    int d = CV_32F;


    Mat I1, I2;
    i1.convertTo(I1, d);          // 将图像转换为32位浮点数进行计算
    i2.convertTo(I2, d);


    Mat I2_2 = I2.mul(I2);        // 计算I2的平方
    Mat I1_2 = I1.mul(I1);        // 计算I1的平方
    Mat I1_I2 = I1.mul(I2);       // 计算I1和I2的乘积


    /**************************** 结束初始化 *******************************/


    Mat mu1, mu2;                 // 预先计算
    GaussianBlur(I1, mu1, Size(11, 11), 1.5); // 计算均值mu1
    GaussianBlur(I2, mu2, Size(11, 11), 1.5); // 计算均值mu2


    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
    sigma1_2 -= mu1_2;


    GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5); // 计算标准差sigma2_2
    sigma2_2 -= mu2_2;


    GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5); // 计算协方差sigma12
    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); // 计算ssim_map的平均值
    return mssim;
}

此代码是一个用于比较两个视频文件的相似性的C++程序,它使用OpenCV库来读取和处理视频帧。首先,程序通过计算峰值信噪比(PSNR)来比较每对视频帧。如果PSNR值低于某个阈值,程序额外使用结构相似性指数(MSSIM)进行比较。结果随着视频播放实时显示,并通过命令行参数控制一些基本设置,如PSNR阈值和帧间等待时间。程序还能够在窗口中实时显示参考视频和待测视频的帧。

6215dbd56266f022a934acb7c3ca7417.png

613bd52d8f773321c88cc73e38dfa2c1.png

4d6a88e6c5b6857d32a11394ad234794.png

 cout << setiosflags(ios::fixed) << setprecision(3) << psnrV << "dB";

15e4568dfa8e933d3152ad222fc2d10d.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/511523.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JeeSite Vue3:前端开发控制实现基于身份角色的权限验证

随着技术的飞速发展&#xff0c;前端开发技术日新月异。在这个背景下&#xff0c;JeeSite Vue3 作为一个基于 Vue3、Vite、Ant-Design-Vue、TypeScript 和 Vue Vben Admin 的前端框架&#xff0c;引起了广泛关注。它凭借其先进的技术栈和丰富的功能模块&#xff0c;为初学者和团…

IP代理检测:判断IP质量优劣要注意什么?

做跨境电商的用户们往往对IP代理这个词都不会感到陌生&#xff0c;那么如何去评判IP的优劣势以及再选择IP时需要注意什么呢&#xff1f; 首先要知道的是IP代理检测是确保网络安全、提高网络访问效率以及满足特定需求的重要步骤。在判断IP代理质量优劣时&#xff0c;有几个关键…

使用阿里云试用Elasticsearch学习:1.1 基础入门——入门实践

阿里云试用一个月&#xff1a;https://help.aliyun.com/search/?kelastic&sceneall&page1 官网试用十五天&#xff1a;https://www.elastic.co/cn/cloud/cloud-trial-overview Elasticsearch中文文档&#xff1a;https://www.elastic.co/guide/cn/elasticsearch/guide…

剑指Offer题目笔记24(集合的组合、排序)

面试题79&#xff1a; 问题&#xff1a; ​ 输入一个不含重复数字的数据集合&#xff0c;找出它的所有子集。 解决方案&#xff1a; ​ 使用回溯法。子集就是从一个集合中选出若干元素。如果集合中包含n个元素&#xff0c;那么生成子集可以分为n步&#xff0c;每一步从集合中…

数据可视化:智慧农业发展的催化剂

数据可视化在智慧农业中发挥着不可替代的作用。随着科技的不断进步&#xff0c;农业领域也在不断探索创新&#xff0c;以提高生产效率、优化资源利用&#xff0c;从而实现可持续发展。而数据可视化技术的应用&#xff0c;则成为了实现智慧农业目标的重要途径。下面我就从可视化…

ABAP OOALV标题设置

ABAP OOALV标题设置 OOALV默认标题是SAP&#xff0c;需要我们自己创建GUI 标题 创建GUI 标题&#xff0c;写好要展示的描述 添加截图中的代码即可。 下面的ALV 报表标题修改的位置在以下代码区域。 这时候通过查询layout&#xff08;wa_layout TYPE lvc_s_layo&#xff0…

mini2440移植lvgl(v8.2)

目录 概述 1 下载源码 1.1 登录LVGL git地址 1.2 LVGL linux平台上的库文件介绍 1.3 下载代码 1.3.1 下载lvgl 1.3.2 下载lv_drivers 1.3.3 下载lv_port_linux_frame_buffer 2 配置编译环境 2.1 创建工程目录 2.2 完善工程目录下的文件 2.2.1 构建工程文件 2.2.2 匹…

Oracle常用sql命令(新手)

1、备份单张表 创建复制表结构 create table employeesbak as select * from cims.employees 如果只复制表结构&#xff0c;只需要在结尾加上 where 10 插入数据 insert into employeesbak select * from cims.employees 删除一条数据 delete from…

水泥设备如何实现物联网远程监控?

水泥设备如何实现物联网远程监控&#xff1f; 在当今的工业4.0时代&#xff0c;水泥行业正在经历一场深度的技术革新&#xff0c;其中构建智慧工厂并采用物联网远程监控解决方案成为了提升生产效率、保障产品质量、实现节能减排的关键路径。该方案通过集成先进的信息技术、物联…

list使用与模拟实现

目录 list使用 reverse sort unique splice list模拟实现 类与成员函数声明 节点类型的定义 非const迭代器的实现 list成员函数 构造函数 尾插 头插 头删 尾删 任意位置插入 任意位置删除 清空数据 析构函数 拷贝构造函数 赋值重载函数 const迭代器的设计 …

【PostgreSQL】用pgAdmin轻松管理PostgreSQL

pgAdmin 是一个功能强大的开源Web界面工具&#xff0c;专为管理和维护PostgreSQL数据库而设计。它提供了一个直观的图形界面&#xff0c;使得用户能够轻松地执行复杂的数据库操作&#xff0c;如查询、更新、导入/导出数据以及管理数据库对象等。pgAdmin 支持几乎所有的PostgreS…

EasyExcel 模板导出excel、合并单元格及单元格样式设置。 Freemarker导出word 合并单元格

xls文件&#xff1a; 后端代码&#xff1a; InputStream filePath this.getClass().getClassLoader().getResourceAsStream(templateFile);// 根据模板文件生成目标文件ExcelWriter excelWriter EasyExcel.write(orgInfo.getFilename()).excelType(ExcelTypeEnum.XLS).withTe…

redis 数据库的安装及使用方法

目录 一 关系数据库与非关系型数据库 &#xff08;一&#xff09;关系型数据库 1&#xff0c;关系型数据库是什么 2&#xff0c;主流的关系型数据库有哪些 3&#xff0c;关系型数据库注意事项 &#xff08;二&#xff09;非关系型数据库 1&#xff0c;非关系型数据库是…

37.HarmonyOS鸿蒙系统 App(ArkUI) 创建第一个应用程序hello world

HarmonyOS App(ArkUI) 创建第一个应用程序helloworld 线性布局 1.鸿蒙应用程序开发app_hap开发环境搭建 3.DevEco Studio安装鸿蒙手机app本地模拟器 打开DevEco Studio,点击文件-》新建 双击打开index.ets 复制如下代码&#xff1a; import FaultLogger from ohos.faultL…

鸿蒙OS元服务开发说明:【WebGL网页图形库开发接口】

一、场景介绍 WebGL主要帮助开发者在前端开发中完成图形图像的相关处理&#xff0c;比如绘制彩色图形等。目前该功能仅支持使用兼容JS的类Web开发范式开发。 二、接口说明 表1 WebGL主要接口列表 鸿蒙OS开发更多内容↓点击HarmonyOS与OpenHarmony技术鸿蒙技术文档开发知识更…

elment UI el-date-picker 月份组件选定后提交后台页面显示正常,提交后台字段变成时区格式

需求&#xff1a;要实现一个日期的月份选择<el-date-picker :typeformData.dateType :value-formatdateFormat v-modelformData.leaveFactoryDateplaceholder选择月份></el-date-picker>错误示例&#xff1a;将日期显示类型(type)dateType或将日期绑定值的格式(val…

Java SpringBoot中优雅地判断一个对象是否为空

在Java中&#xff0c;可以使用以下方法优雅地判断一个对象是否为空&#xff1a; 使用Objects.isNull()方法判断对象是否为空&#xff1a; import java.util.Objects;if (Objects.isNull(obj)) {// obj为空的处理逻辑 }使用Optional类优雅地处理可能为空的对象&#xff1a; impo…

为何网易游戏会选择引入OceanBase数据库

本文作者&#xff1a;田维繁&#xff0c;网易游戏关系型数据库小组负责人 作为中国游戏开发领域的佼佼者&#xff0c;网易游戏始终站在网络游戏自主研发的前沿。其产品及周边产品线丰富多样&#xff0c;因此&#xff0c;为满足各种业务场景的需求&#xff0c;需要多种不同的数据…

XRDP登录ubuntu桌面闪退问题

修改 /etc/xrdp/startwm.sh unset DBUS_SESSION_BUS_ADDRESS unset XDG_RUNTIME_DIR . $HOME/.profile

ensp华为AC+AP上线配置

AR1配置&#xff1a; <Huawei>system-view # 进入系统视图<Huawei>sysname R1 # 设备重命名[R1]dhcp enable # 开启DHCP功能[R1]interface GigabitEthernet0/0/0 # 进入接口 [R1-GigabitEthernet0/0/0]ip address 192.168.0.1 23 # 配置接口地址 [R1-GigabitE…