C++基于opencv的视频质量检测--图像抖动检测
文章目录
- 0.引言
- 1. 原始代码分析
- 2. 优化方案
- 3. 优化后的代码
- 4. 代码详细解读
0.引言
视频质量图像抖动检测已在C++基于opencv4的视频质量检测中有所介绍,本文将详细介绍其优化版本。
1. 原始代码分析
首先,我们来看图像抖动检测的原始代码:
#include <algorithm>
#include <cmath>
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <vector>namespace {
constexpr int kMinHessian = 400;
constexpr int kMinKeypoints = 20;
constexpr double kScaleWidth = 800.0;
constexpr double kInvalidReturn = -1.0;
constexpr double kDefaultMinDist = 100.0;
constexpr int kRoiYOffset = 5;
constexpr int kRoiHeightFactor = 3;
constexpr int kGoodMatchesCount = 50;
constexpr int kFilterThresholdFactor = 4;
constexpr int kFilterEndFactor = 3;
} // namespace/*** @brief 检测图像抖动的函数。* @param [in] srcImg 待检测的图像* @param [in] refImg 参考图像* @param [out] offsetX 待检测图像相对于参考图像在x轴上的偏移量* @param [out] offsetY 待检测图像相对于参考图像在y轴上的偏移量* @return 返回函数执行的状态* @retval 0 表示成功* @retval -1 表示失败*/
int JitterDetect(const cv::Mat& srcImg, const cv::Mat& refImg, double& offsetX, double& offsetY) {if (srcImg.empty() || refImg.empty()) {return -1;}cv::Mat img = srcImg.clone();cv::Mat imgRef = refImg.clone();cv::Rect roi(0, img.rows / kRoiYOffset, img.cols, img.rows * kRoiHeightFactor / kRoiYOffset);img = img(roi);imgRef = imgRef(roi);double scale = 1.0;cv::Mat imgScaled, imgRefScaled;if (img.cols > kScaleWidth) {scale = kScaleWidth / static_cast<double>(img.cols);cv::resize(img, imgScaled, cv::Size(static_cast<int>(kScaleWidth), static_cast<int>(scale * img.rows)));cv::resize(imgRef, imgRefScaled, cv::Size(static_cast<int>(kScaleWidth), static_cast<int>(scale * img.rows)));} else {imgScaled = img;imgRefScaled = imgRef;}cv::Ptr<cv::xfeatures2d::SURF> detector = cv::xfeatures2d::SURF::create(kMinHessian);std::vector<cv::KeyPoint> keypoints1, keypoints2;detector->detect(imgScaled, keypoints1);detector->detect(imgRefScaled, keypoints2);if (keypoints1.size() < kMinKeypoints || keypoints2.size() < kMinKeypoints) {return -1;}cv::Ptr<cv::xfeatures2d::SURF> extractor = cv::xfeatures2d::SURF::create();cv::Mat descriptors1, descriptors2;extractor->compute(imgScaled, keypoints1, descriptors1);extractor->compute(imgRefScaled, keypoints2, descriptors2);cv::FlannBasedMatcher matcher;std::vector<cv::DMatch> matches;matcher.match(descriptors1, descriptors2, matches);double minDist = kDefaultMinDist;for (const auto& match : matches) {if (match.distance < minDist) {minDist = match.distance;}}std::vector<cv::DMatch> goodMatches;std::vector<float> distances(matches.size());for (size_t i = 0; i < matches.size(); ++i) {distances[i] = matches[i].distance;}std::sort(distances.begin(), distances.end());double distFlag =(matches.size() < kGoodMatchesCount) ? distances[matches.size() - 1] : distances[kGoodMatchesCount - 1];for (size_t i = 0, cntK = 0; i < matches.size() && cntK < kGoodMatchesCount; ++i) {if (matches[i].distance <= distFlag) {goodMatches.push_back(matches[i]);++cntK;}}std::vector<float> moveX(goodMatches.size()), moveY(goodMatches.size());for (size_t i = 0; i < goodMatches.size(); ++i) {moveX[i] = std::abs(keypoints1[goodMatches[i].queryIdx].pt.x - keypoints2[goodMatches[i].trainIdx].pt.x);moveY[i] = std::abs(keypoints1[goodMatches[i].queryIdx].pt.y - keypoints2[goodMatches[i].trainIdx].pt.y);}std::sort(moveX.begin(), moveX.end());std::sort(moveY.begin(), moveY.end());for (size_t p = goodMatches.size() / kFilterThresholdFactor;p < goodMatches.size() * kFilterEndFactor / kFilterThresholdFactor; ++p) {offsetX += moveX[p];offsetY += moveY[p];}offsetX /= (goodMatches.size() / 2);offsetY /= (goodMatches.size() / 2);if (scale != 1.0) {offsetX *= scale;offsetY *= scale;}return 0;
}
以下是原始代码的核心步骤:
- 图像预处理:对输入图像和参考图像进行ROI裁剪和缩放。
- 特征检测与描述子计算:使用SURF算法检测关键点并计算描述子。
- 特征匹配:使用FLANN匹配器匹配描述子。
- 匹配点筛选:根据距离筛选好的匹配点。
- 偏移量计算:手动计算匹配点之间的位移,得到图像的偏移量。
2. 优化方案
我们对代码进行如下优化:
- 移除不必要的图像拷贝:避免使用
clone()
,直接在ROI裁剪后的图像上进行操作,减少内存占用和拷贝时间。 - 合并特征检测和描述子计算:使用
detectAndCompute
方法,将特征检测和描述子计算合并,提高效率。 - 简化特征匹配流程:通过排序匹配结果,直接选择距离最小的前N个匹配点,简化了匹配筛选过程。
- 采用鲁棒的变换估计方法:使用
cv::estimateAffinePartial2D
函数结合RANSAC算法,更准确地估计图像间的平移和旋转,增强鲁棒性。 - 直接提取平移量:从估计的仿射变换矩阵中直接获取偏移量,避免手动计算的复杂性。
3. 优化后的代码
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>namespace {
constexpr int kMinHessian = 400;
constexpr int kMinKeypoints = 20;
constexpr double kScaleWidth = 800.0;
constexpr int kRoiYOffset = 5;
constexpr int kRoiHeightFactor = 3;
} // namespace/*** @brief 检测图像抖动的函数。* @param [in] srcImg 待检测的图像* @param [in] refImg 参考图像* @param [out] offsetX 待检测图像相对于参考图像在x轴上的偏移量* @param [out] offsetY 待检测图像相对于参考图像在y轴上的偏移量* @return 返回函数执行的状态* @retval 0 表示成功* @retval -1 表示失败*/
int JitterDetect(const cv::Mat& srcImg, const cv::Mat& refImg, double& offsetX, double& offsetY) {if (srcImg.empty() || refImg.empty()) {return -1;}// 定义ROI区域int roiY = srcImg.rows / kRoiYOffset;int roiHeight = srcImg.rows * kRoiHeightFactor / kRoiYOffset;cv::Rect roi(0, roiY, srcImg.cols, roiHeight);// 裁剪图像到ROI区域cv::Mat img = srcImg(roi);cv::Mat imgRef = refImg(roi);// 如果图像宽度大于设定值,则缩放图像double scale = 1.0;if (img.cols > kScaleWidth) {scale = kScaleWidth / static_cast<double>(img.cols);cv::resize(img, img, cv::Size(), scale, scale);cv::resize(imgRef, imgRef, cv::Size(), scale, scale);}// 初始化特征检测器和描述子提取器cv::Ptr<cv::xfeatures2d::SURF> detector = cv::xfeatures2d::SURF::create(kMinHessian);// 检测并计算关键点和描述子std::vector<cv::KeyPoint> keypoints1, keypoints2;cv::Mat descriptors1, descriptors2;detector->detectAndCompute(img, cv::noArray(), keypoints1, descriptors1);detector->detectAndCompute(imgRef, cv::noArray(), keypoints2, descriptors2);if (keypoints1.size() < kMinKeypoints || keypoints2.size() < kMinKeypoints) {return -1;}// 使用FLANN匹配器匹配描述子cv::FlannBasedMatcher matcher;std::vector<cv::DMatch> matches;matcher.match(descriptors1, descriptors2, matches);if (matches.empty()) {return -1;}// 按距离排序匹配结果std::sort(matches.begin(), matches.end(), [](const cv::DMatch& a, const cv::DMatch& b) {return a.distance < b.distance;});// 选择前N个好的匹配const int numGoodMatches = std::min(50, static_cast<int>(matches.size()));std::vector<cv::DMatch> goodMatches(matches.begin(), matches.begin() + numGoodMatches);// 提取匹配的关键点坐标std::vector<cv::Point2f> points1, points2;for (const auto& match : goodMatches) {points1.push_back(keypoints1[match.queryIdx].pt);points2.push_back(keypoints2[match.trainIdx].pt);}// 使用RANSAC估计仿射变换矩阵cv::Mat inliers;cv::Mat affine = cv::estimateAffinePartial2D(points1, points2, inliers, cv::RANSAC);if (affine.empty()) {return -1;}// 提取平移量offsetX = affine.at<double>(0, 2);offsetY = affine.at<double>(1, 2);// 根据缩放比例调整偏移量if (scale != 1.0) {offsetX /= scale;offsetY /= scale;}return 0;
}
4. 代码详细解读
流程说明:
- 开始:函数
JitterDetect
开始执行。 - 检查输入图像是否为空:如果输入图像为空,返回错误。
- 定义ROI区域:根据预设的偏移量和比例,定义感兴趣区域(ROI)。
- 裁剪图像到ROI:将输入图像和参考图像裁剪到指定的ROI区域。
- 检查图像是否需要缩放:如果图像宽度大于设定的最大宽度,进行缩放。
- 检测并计算关键点和描述子:使用SURF算法检测关键点并计算描述子。
- 检查关键点数量是否足够:如果关键点数量不足,返回错误。
- 匹配描述子:使用FLANN匹配器匹配两个图像的描述子。
- 检查匹配结果是否为空:如果没有找到匹配,返回错误。
- 排序匹配结果:将匹配结果按距离从小到大排序。
- 选择好的匹配点:选择距离最小的前N个匹配点。
- 提取匹配的关键点坐标:从好的匹配中提取对应的关键点坐标。
- 估计仿射变换矩阵:使用RANSAC算法估计图像间的仿射变换。
- 检查仿射变换矩阵是否有效:如果估计失败,返回错误。
- 提取平移量:从仿射变换矩阵中获取偏移量。
- 调整偏移量:根据图像缩放比例,调整偏移量。
- 返回成功状态:函数执行成功,返回0。
相关文章:

C++基于opencv的视频质量检测--图像抖动检测
文章目录 0.引言1. 原始代码分析2. 优化方案3. 优化后的代码4. 代码详细解读 0.引言 视频质量图像抖动检测已在C基于opencv4的视频质量检测中有所介绍,本文将详细介绍其优化版本。 1. 原始代码分析 首先,我们来看图像抖动检测的原始代码: …...

Cuda By Example - 11 (Texture Memory 2-D)
跟1D一样,2D的代码也没有运行过。旧的方法看看就好。 声明二维Texture texture<float, 2> texConstSrc; texture<float, 2> texIn; texture<float, 2> texOut; 访问二维Texture 使用2D的Texture的便利性体现在blend_kernel函数里。不再需要通…...

Go匿名结构体使用场景
1. 定义 在 Go 语言中,匿名结构体(Anonymous Struct)是一种没有显式命名的结构体类型。你可以直接在代码中定义并使用匿名结构体,而不需要为其定义一个单独的类型名称。匿名结构体通常用于临时数据结构或一次性使用的场景。 匿名…...

Vue 发布十年了!你知道我这十年是怎么过的吗?
2014 年 2 月 3 日,Vue 在 Hacker News 上首次亮相。十年后的今天,Vue 已经成为使用最广泛的前端框架之一,拥有了一个非常丰富的生态系统。本文来梳理一下 Vue.js 十年以来的重要里程碑! 尤雨溪,无疑是 Vue.js 背后的灵…...

Unity 6 来袭
这里写自定义目录标题 1.提升渲染性能1.1 降低CPU开销 Lower CPU overhead1.2.减少内存带宽1.3.高档低分辨率帧2.多人游戏创作3.扩大多平台覆盖范围3.1.增进Android平台开发4.使用Runtime AI解锁各种可能性4.1.Unity Muse4.2.Unity Sentis5.实现更具吸引力的视觉效果5.1.自适应…...

SpringMVC课时1
一:SpringMVC Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet,负责表述层(控制层)实现简化。 由于 Spring MVC 本身就是 Spring 框架的一部分,和 Spring 框架是无缝集成。 二:SSM的主要作用 三:SpringMVC的原理架构图 …...

【小白学机器学习30】样本统计的核心参数:均值/期望,方差,标准差,标准值。
目录 1 为什么我们要搞出来这么多指标/参数? 1.1 描述统计学为啥要搞出来这么多复杂的参数?什么平均值等 1.2 所以,需要用少数几个关键数据代表1群数据 1.2.1 平均值 1.2.2 平均值的问题:方差 2 代表性的数据1:…...

flink1.17.2安装和使用
版本:flink1.17.2 单机模式 配置 # 为了在别处连接flink-web rest.bind-address: 0.0.0.0命令 # 启动集群 bin/start-cluster.sh # 关闭集群 bin/stop-cluster.sh使用 使用浏览器连接 ip:8081 使用flink-web...

C向C++入门-- C语言填坑
1.c参考文档 我们在学习c中需要查找参照信息到是从这些文档中得到。 https://legacy.cplusplus.com/reference/ 标准只更新到C11,但是以头⽂件形式呈现,内容⽐较易看好懂。 https://zh.cppreference.com/w/cpp https://en.cppreference.com/w/ 后两…...

扫雷游戏(C语言详解)
扫雷游戏(C语言详解) 放在最前面的1、前言(扫雷游戏的简介)2、扫雷游戏的规则(简易版)3、代码实现(3.1)提醒一下:( i ) 提醒1:( ii ) 提醒2: &…...

信刻全自动光盘摆渡系统
随着各种数据传输、储存技术、信息技术的快速发展,保护信息安全是重中之重。各安全领域行业对跨网数据交互需求日益迫切。针对于业务需要与保密规范相关要求,涉及重要秘密信息,需做到安全的物理隔离,并且保证跨网数据高效安全传输…...

计算机网络的数据链路层
计算机网络的数据链路层 数据链路层是OSI参考模型中的第二层,它位于物理层之上,网络层之下。数据链路层的主要功能是在物理层提供的服务的基础上向网络层提供服务,其最基本的服务是将源自网络层来的数据可靠地传输到相邻节点的目标机网络层。…...

从0开始搭建一个生产级SpringBoot2.0.X项目(三)SpringBoot接口统一返回和全局异常处理
前言 最近有个想法想整理一个内容比较完整springboot项目初始化Demo。 SpringBoot接口统一返回和全局异常处理,使用ControllerAdvice ExceptionHandler 的组合来实现。 一、pom文件新增依赖 <dependency><groupId>com.alibaba</groupId><ar…...

Mybatis-plus-扩展功能
Mybatis-plus-扩展功能 一:代码生成器 AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。 功能的演示:…...

【AI辅助】AWS Toolkit+AmazonQ
#偶然看到网上某up主用的这个AI工具,感觉还挺实用的,推荐大家~我们不可阻挡AI的攻势,但是成为利用它的人,也是反侵占的方式呢# AWS toolkit Amazon Q 安装 VScode--Extensions--搜索工具--安装 安装后,工具栏会多出对…...

云手机简述(概况,使用场景,自己部署云手机)
背景 最近经常会看到云手机的相关广告,手痒难耐,了解一下。 我的主要需求: Android 已 root,能够做一些自动化等高级功能。能够通过 远程adb 控制手机。能够尽量的少花钱,最好是能够提供动态创建删除手机的方式&…...

Java已死,大模型才是未来?
作者:不惑_ 引言 在数字技术的浪潮中,编程语言始终扮演着至关重要的角色。Java,自1995年诞生以来,便以其跨平台的特性和丰富的生态系统,成为了全球范围内开发者们最为青睐的编程语言之一 然而,随着技术的…...

NCCL安装(Ubuntu等)
目录 一、NCCL的定义二、安装NCCL的原因1、加速多GPU通信2、支持流行的深度学习框架3、提高计算效率4、易于使用和集成5、可扩展性 三、NCCL安装方法1、下载安装包2、更新APT数据库3、使用APT安装libnccl2包,另外,如果需要使用NCCL编译应用程序ÿ…...

加载视频显示 - python 实现
#-*-coding:utf-8-*- # date:2021-03-21 # Author: DataBall - Xian # Function: 加载视频并显示import cv2 if __name__ "__main__":#加载视频cap cv2.VideoCapture(./video/1.mp4)while True:ret, img cap.read()# 获取相机图像if ret True:# 如果 ret 返回值为…...

数据结构模拟题[五]
数据结构试卷(五) 一、选择题 (20 分) 1.数据的最小单位是( )。 (A) 数据项 (B) 数据类型 (C) 数据元素 (D) 数据变量 2.设一组初始记录关键字序列为 (50 ,40, 95,20…...

IDEA切换窗口快捷键失效
问题描述: 在idea中,如果切换窗口的快捷键(Alt Tab)失效了,可以通过清除缓存的方式修复...

QT中使用图表之QChart绘制X轴为日期时间轴的折线图
显然X轴是日期时间轴的话,那么我们使用的轴类就得是QDateTimeAxis QChart中日期时间轴的精度是毫秒 因此图表里面的数据的x值需要是一个毫秒数,才能显示出来 --------------------------------------------------------------------------------------…...

【传知代码】短期电力负荷(论文复现)
🍑个人主页:Jupiter. 🚀 所属专栏:传知代码 欢迎大家点赞收藏评论😊 目录 备注前言介绍问题背景复现:一. 多维特征提取的提取框架:二. 论文中进行性能测试的MultiTag2Vec-STLF模型:三…...

ubuntu20.04 加固方案-设置重复登录失败后锁定时间限制
一、编辑PAM配置文件 打开终端。 使用文本编辑器(如vim)编辑/etc/pam.d/common-auth文件。 sudo vim /etc/pam.d/common-auth 二、添加配置参数 在打开的配置文件中,添加或修改以下参数: auth required pam_tally2.so deny5 un…...

【综合算法学习】(第十三篇)
目录 解数独(hard) 题目解析 讲解算法原理 编写代码 单词搜索(medium) 题目解析 解析算法原理 编写代码 解数独(hard) 题目解析 1.题目链接:. - 力扣(LeetCode)…...

Web3 Key Talking #4|Sui有何不同?及其发展路线图
活动时间: 2024 年 10 月 31 日(周四)20:00–21:00(UTC8) 会议链接: 腾讯会议 会议 ID :429–339–777 主持:Sanzhisanzhichazi1 嘉宾:uvdwangtxxl,Sui …...

Axios 请求超时设置无效的问题及解决方案
文章目录 Axios 请求超时设置无效的问题及解决方案1. 引言2. 理解 Axios 的超时机制2.1 Axios 超时的工作原理2.2 超时错误的处理 3. Axios 请求超时设置无效的常见原因3.1 配置错误或遗漏3.2 超时发生在建立连接之前3.3 使用了不支持的传输协议3.4 代理服务器或中间件干扰3.5 …...

数据结构+算法
一、数据结构 1、线性结构 数组: 访问:O(1)访问特定位置的元素;插入:O(n)最坏的情况发生在插入发生在数组的首部并需要移动所有元素时;删除:O(n)最坏的情况发生在删除数组的开头发生并需要移动第一元素后…...

利用ExcelJS封装一个excel表格的导出
ExcelJS 操作和写入Excel 文件。 直接上代码,js部分: exportFn.js import ExcelJS from exceljs; import { saveAs } from file-saver;export function exportExcleUtils(tHeader, filterVal, listData, fileName) {//设置工作簿属性const workbook ne…...

AI 原生时代,更要上云:百度智能云云原生创新实践
本文整理自百度云智峰会 2024 —— 云原生论坛的同名演讲。 我今天分享的主题,是谈谈在云计算和 AI 技术快速发展和深入落地的背景下,百度智能云在云原生的基础设施产品和技术层面做的一些创新实践。 毋庸置疑,过去十几年云计算和 AI 技术是…...