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…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
