openh264 运动估计搜索原理源码分析

运动估计搜索

运动估计搜索是视频编码中的一个重要步骤,它用于确定视频序列中两个帧之间的运动向量(MV)。这些运动向量用于预测帧之间的运动,从而减少编码所需的数据量。以下是运动估计搜索的一些关键概念和步骤:

  1. 运动估计的目的:

    • 运动估计的目的是找到最佳运动向量,使得当前帧(编码帧)与参考帧之间的差异最小。
  2. 运动向量:

    • 运动向量是一个二维向量,表示在参考帧中找到与编码帧中某个区域最匹配的区域的位置偏移。
  3. 搜索方法:

    • 有多种运动估计搜索算法,包括:
      • 全搜索(Full Search):检查参考帧中的每个可能位置。
      • 对分搜索(Logarithmic Search):从较大的搜索范围开始,逐渐缩小到更精细的搜索。
      • 交叉钻石搜索(Cross-Diamond Search):结合了对分搜索和钻石搜索的优点。
      • 钻石搜索(Diamond Search):在对分搜索的基础上,使用钻石形状的搜索路径。
      • 新钻石搜索(New Diamond Search):钻石搜索的改进版本,通常在搜索精度和速度上有所提升。
  4. 搜索过程:

    • 运动估计搜索通常包括以下步骤:
      • 初始化:设置初始运动向量,通常为零或前一帧的运动向量。
      • 搜索:根据选定的搜索算法,在参考帧中搜索最佳匹配区域。
      • 计算成本:对于每个候选位置,计算与编码帧的匹配成本,如Sad(绝对差值之和)或SAD+Variance。
      • 选择最佳:选择成本最低的候选位置作为最佳运动向量。
  5. 成本函数:

    • 常用的成本函数包括:
      • Sad:计算编码帧和参考帧对应区域的像素值差的绝对值之和。
      • Sse:计算编码帧和参考帧对应区域的像素值差的平方和。
      • Ssad:Sad的改进版本,考虑了像素值的梯度。
  6. 优化:

    • 为了提高运动估计的效率,可以采用多种优化策略,如:
      • 多分辨率搜索:先在较低分辨率上进行粗略搜索,然后在高分辨率上进行精细搜索。
      • 预测运动向量:使用前一帧的运动向量作为初始估计。
      • 快速算法:如快速模式决策和快速成本计算。
  7. 应用:

    • 运动估计搜索是视频编码标准(如H.264, H.265, VP9等)的核心部分,对于视频压缩效率至关重要。

运动估计搜索算法的选择和实现取决于编码器的设计目标,包括压缩效率、编码速度和硬件资源等因素。

openh264 运动估计搜索原理

函数关系框架

在这里插入图片描述

  1. 说明:
  • 虽然开启了 8x4、4x8、4x4 分块预测,但其实实际应用中使用宏开关关闭状态;
  • pfMotionSearch[1]pfMotionSearch[2]虽然分别指向了WelsMotionEstimateSearchStaticWelsMotionEstimateSearchScrolled两个函数,但实际并没使用。

核心函数

  1. WelsMotionEstimateSearch函数
  • 功能:帧间预测过程中运动估计搜索的核心函数;它通过搜索最佳匹配块来最小化编码后视频的残差,从而提高压缩效率。运动估计的精度直接影响到编码效率和最终视频质量。
  • 原理过程
  • 局部变量声明:
    • kiStrideEnc 和 kiStrideRef 分别是当前编码帧和参考帧的步长(stride),步长是图像一行像素的字节数。
  • 初始点预测:
    • WelsMotionEstimateInitialPoint 函数用于基于已有信息(如上一帧的运动矢量)预测当前宏块的运动估计初始点。
    • 如果初始点预测失败(返回值为 false),则调用 pFuncList->pfSearchMethod[pMe->uiBlockSize] 进行更精细的搜索。
  • 运动估计搜索方法:
    • pfSearchMethod 是一个函数指针数组,根据宏块的大小(pMe->uiBlockSize)选择相应的搜索算法,一般指向WelsDiamondSearch函数。
  • 结束整数像素搜索:
    • MeEndIntepelSearch 函数用于结束整数像素级别的搜索,并准备进行亚像素级别的搜索。
  • 计算SATD:
    • pfCalculateSatd 函数用于计算 SATD,这是一种衡量预测块和原始块差异的方法,用于评估运动估计的质量。
    • pSampleDealingFuncs.pfSampleSatd[pMe->uiBlockSize] 是一个根据块大小选择的 SATD 计算函数。
  • 源码
/*!* \brief  BL mb motion estimate search** \param  enc      Wels encoder context* \param  pMe          Wels me information** \return  NONE*/void WelsMotionEstimateSearch (SWelsFuncPtrList* pFuncList, SDqLayer* pCurDqLayer, SWelsME* pMe, SSlice* pSlice) {const int32_t kiStrideEnc = pCurDqLayer->iEncStride[0];const int32_t kiStrideRef = pCurDqLayer->pRefPic->iLineSize[0];//  Step 1: Initial point predictionif (!WelsMotionEstimateInitialPoint (pFuncList, pMe, pSlice, kiStrideEnc, kiStrideRef)) {pFuncList->pfSearchMethod[pMe->uiBlockSize] (pFuncList, pMe, pSlice, kiStrideEnc, kiStrideRef);MeEndIntepelSearch (pMe);}pFuncList->pfCalculateSatd (pFuncList->sSampleDealingFuncs.pfSampleSatd[pMe->uiBlockSize], pMe, kiStrideEnc,kiStrideRef);
}
  1. WelsDiamondSearch函数
  • 功能:运动搜索中整像素的钻石搜索模板实现函数
  • 原理过程
  • 局部变量声明:
    • pSad 是指向样本处理函数的指针,用于计算 SAD(Sum of Absolute Differences)。
    • pFref 和 kpEncMb 分别是指向参考宏块和当前编码宏块的指针。
    • kpMvdCost 是指向运动向量差异成本的指针。
  • 运动向量范围限制:
    • ksMvStartMin 和 ksMvStartMax 定义了运动向量的搜索范围。
  • 初始运动向量差值计算:
    • iMvDx 和 iMvDy 计算当前运动向量与预测运动向量的差值,并且将差值扩大四倍。
  • 搜索循环:
    • 使用 while 循环进行迭代搜索,iTimeThreshold 作为迭代次数的阈值。
  • 运动向量调整:
    • 每次迭代中,将差值右移两位以恢复原始的运动向量比例。
  • 运动向量范围检查:
    • 使用 CheckMvInRange 函数检查调整后的运动向量是否在有效范围内。
  • 计算 SAD 成本:
    • 调用 pSad 函数计算当前预测宏块与编码宏块之间的 SAD 成本。
  • 最佳成本选择:
    • 使用 WelsMeSadCostSelect 函数选择最佳成本,如果当前成本更优,则更新最佳成本和相关变量。
  • 更新运动向量差值:
    • 如果找到更优的成本,则更新差值以在下一次迭代中调整运动向量。
  • 更新参考宏块指针:
    • 根据选择的 X 和 Y 偏移更新参考宏块的指针。
  • 结束搜索:
    • 当找到最佳成本或达到迭代次数阈值时,结束搜索。
  • 最终运动向量计算:
    • 将最终的运动向量差值加上预测运动向量的坐标,然后右移两位以得到整数像素级别的运动向量。
  • 更新运动估计信息:
    • 更新 pMe 结构中的运动向量、SAD 成本、SATD 成本和参考宏块指针。
  • 源码
void WelsDiamondSearch (SWelsFuncPtrList* pFuncList, SWelsME* pMe, SSlice* pSlice,const int32_t kiStrideEnc,  const int32_t kiStrideRef) {PSample4SadCostFunc      pSad          =  pFuncList->sSampleDealingFuncs.pfSample4Sad[pMe->uiBlockSize];uint8_t* pFref = pMe->pRefMb;uint8_t* const kpEncMb = pMe->pEncMb;const uint16_t* kpMvdCost = pMe->pMvdCost;const SMVUnitXY ksMvStartMin    = pSlice->sMvStartMin;const SMVUnitXY ksMvStartMax    = pSlice->sMvStartMax;int32_t iMvDx = ((pMe->sMv.iMvX) * (1 << 2)) - pMe->sMvp.iMvX;int32_t iMvDy = ((pMe->sMv.iMvY) * (1 << 2)) - pMe->sMvp.iMvY;uint8_t* pRefMb = pFref;int32_t iBestCost = (pMe->uiSadCost);int32_t iTimeThreshold = ITERATIVE_TIMES;ENFORCE_STACK_ALIGN_1D (int32_t, iSadCosts, 4, 16)while (iTimeThreshold--) {pMe->sMv.iMvX = (iMvDx + pMe->sMvp.iMvX) >> 2;pMe->sMv.iMvY = (iMvDy + pMe->sMvp.iMvY) >> 2;if (!CheckMvInRange (pMe->sMv, ksMvStartMin, ksMvStartMax))continue;pSad (kpEncMb, kiStrideEnc, pRefMb, kiStrideRef, &iSadCosts[0]);int32_t iX, iY;const bool kbIsBestCostWorse = WelsMeSadCostSelect (iSadCosts, kpMvdCost, &iBestCost, iMvDx, iMvDy, &iX, &iY);if (kbIsBestCostWorse)break;iMvDx -= (iX * (1 << 2)) ;iMvDy -= (iY * (1 << 2)) ;pRefMb -= (iX + iY * kiStrideRef);}/* integer-pel mv */pMe->sMv.iMvX = (iMvDx + pMe->sMvp.iMvX) >> 2;pMe->sMv.iMvY = (iMvDy + pMe->sMvp.iMvY) >> 2;pMe->uiSatdCost = pMe->uiSadCost = (iBestCost);pMe->pRefMb = pRefMb;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

贪心算法——赶作业(C++)

慢慢来&#xff0c;沉稳一点。 2024年6月18日 题目描述 A同学有n份作业要做&#xff0c;每份作业有一个最后期限&#xff0c;如果在最后期限后交作业就会扣分&#xff0c;现在假设完成每份作业都需要一天。A同学想安排作业顺序&#xff0c;把扣分降到最低&#xff0c;请帮他实…

易备防勒索备份方案与成功案例

随着信息化的发展&#xff0c;数据安全的重要性愈加突出。据 Hiscox 全球网络安全统计&#xff0c;在勒索软件攻击事件当中&#xff0c;64%以上的用户是中小企业。因此&#xff0c;制定完善的灾备策略&#xff0c;是抵御网络威胁的终极方案。而在诸多数据备份方案中&#xff0c…

【Linux】进程控制1——进程创建和进程终止

1.进程创建 1.1.再谈fork 在linux中fork函数时非常重要的函数&#xff0c;它从已存在进程中创建一个新进程。新进程为子进程&#xff0c;而原进程为父进程。 #include <unistd.h> pid_t fork(void);//pid_t为整形 返回值&#xff1a;子进程中的fork()返回0&#xff…

内置类型不够用?试试Python内置类型子类化!

目录 1、经典继承法:直接子类化内置类型 🧬 1.1 了解Python内置类型 1.2 实现子类化的基础步骤 步骤1:定义子类 步骤2:添加自定义行为 步骤3:使用子类 1.3 实战:子类化列表list示例 1.4 优化:重写魔法方法实现自定义行为 2、高级技巧:元类介入定制 🪐 2.1 …

TCP/IP协议,三次握手,四次挥手,常用的协议

IP - 网际协议 IP 负责计算机之间的通信。 IP 负责在因特网上发送和接收数据包。 HTTP - 超文本传输协议 HTTP 负责 web 服务器与 web 浏览器之间的通信。 HTTP 用于从 web 客户端&#xff08;浏览器&#xff09;向 web 服务器发送请求&#xff0c;并从 web 服务器向 web …

智能门锁电池双节升压充电芯片-FP6291支持5V1A输入升压 8.4V双节电池充电

方案背景 可充电锂电池是一种环保、高效的智能锁电池类型&#xff0c;其主要优点是可以循环充电使用、容量大、使用寿命长。与一次性电池相比&#xff0c;可充电锂电池可以循环充电使用&#xff0c;减少了废弃物的产生和对环境的影响。同时&#xff0c;可充电锂电池的容量较大…

细说MCU输出互补型PWM波形的实现方法

目录 一、硬件及工程 二、建立工程 1、TIM1引脚 2、建立工程 &#xff08;1&#xff09;配置GPIO &#xff08;2&#xff09;选择时钟源和Debug模式 &#xff08;3&#xff09;配置定时器 &#xff08;4&#xff09;配置中断 &#xff08;5&#xff09;配置系统时钟 …

怎么去避免手机赚钱的骗局?

要避免手机赚钱的骗局&#xff0c;可以遵循以下一些建议&#xff1a; 1. 谨慎对待高收益承诺&#xff1a;如果一个项目承诺轻松获取高额回报&#xff0c;那么很可能存在风险。真正的高收益往往伴随着高风险&#xff0c;而且需要付出大量的努力和时间。 2. 调查了解相关项目&am…

【基因功能富集2:分析流程】非模式生物怎么注释 clusterProfiler包GO、KEGG

文章目录 概要整体流程step1 百度搜索注释物种--拉丁文名称step2 注释官网搜索--该物种对应库--编号step3 正常注释 即可 概要 不常见的物种如何进行富集分析&#xff1f;&#xff1f; 整体流程 提示&#xff1a; step1 百度搜索注释物种–拉丁文名称 提示&#xff1a;拉丁文…

Gobject tutorial 六

Instantiatable classed types Initialization and destruction 类型的实例化是通过函数g_tpye_create_instance()实现的。这个函数首先会查找与类型相关的GTypeInfo结构体&#xff0c;之后&#xff0c;查询结构体中的instance_size和 instance policy即 n_preallocs(在 2.10版…

MySQL数据库管理 二

1、数据表高级操作 &#xff08;1&#xff09;克隆表 方法一&#xff1a; create table 新表名 like 旧表名; #克隆表结构 insert into 新表名 select * from 旧表名; #克隆表数据 #此方法能保证 新表的表结构、表数据 跟旧表都是一致的 方法二&#x…

ECharts 词云案例三:2024年阅读关键词

ECharts 词云案例三&#xff1a;2024年阅读关键词 引言 在数据可视化领域&#xff0c;ECharts 以其强大的功能性和灵活性&#xff0c;成为开发者和设计师的首选工具之一。继上一篇关于 ECharts 词云图的详细介绍后&#xff0c;本文将探索词云图的进阶应用——使用蒙版来创造更…

19.面包屑导航制作

面包屑导航制作 官网&#xff1a;组件 | Element 1. 在layout下新建BreadCrumb.vue BreadCrumb.vue <template><div class"bread-text"><el-breadcrumb class"bred"separator"/"><el-breadcrumb-item v-for"item in…

家人们,我最近迷上了食家巷的方形饼

那独特的方形造型&#xff0c;超级可爱。&#x1f44f;刚出炉的方形饼&#xff0c;热气腾腾&#xff0c;散发着诱人的香气。&#x1f60b;咬一口&#xff0c;酥脆的外皮“咔滋”作响&#xff0c;里面的面饼却又十分绵软&#xff0c;口感层次超丰富&#xff01;&#x1f929;无论…

Golang | Leetcode Golang题解之第144题二叉树的前序遍历

题目&#xff1a; 题解&#xff1a; func preorderTraversal(root *TreeNode) (vals []int) {var p1, p2 *TreeNode root, nilfor p1 ! nil {p2 p1.Leftif p2 ! nil {for p2.Right ! nil && p2.Right ! p1 {p2 p2.Right}if p2.Right nil {vals append(vals, p1.V…

iview 组件里面的(任何一个月)整月日期全部选中_iview时间轴选中有历史记录日期

iview 组件里面的整月日期全部选中&#xff1a; ①&#xff1a;第一种是当前月的日期全部选中&#xff1a; 先上效果图&#xff1a;当前月分 获取到的值&#xff1a; 当前月的方法&#xff1a; // getDateStr() {// var curDate new Date();// var curMonth curDate.ge…

每日一题——Python代码实现力扣58. 最后一个单词的长度(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 代码逻辑&#xff1a; 时间复杂度&#xff1a; 空间复杂度&#xff1a; …

HTML5休闲小游戏《城堡守卫传说》源码,引流、刷广告利器

HTML5休闲小游戏《城堡守卫传说》源码&#xff0c;直接把源码上传到服务器就能使用了&#xff01; 下载链接&#xff1a;https://www.huzhan.com/code/goods467802.html

成都百洲文化传媒有限公司助力商家扬帆远航

在数字经济的浪潮中&#xff0c;电商行业如日中天&#xff0c;成都百洲文化传媒有限公司正是这一领域的佼佼者。作为一家专注于电商服务的传媒公司&#xff0c;百洲文化以其专业的服务、创新的理念和卓越的成果&#xff0c;在业内树立了良好的口碑&#xff0c;成为众多商家信赖…

划分子网和构造超网的学习

子网掩码长度&#xff1d;32位 某位&#xff1d;1&#xff1a;IP地址中的对应位为网络号和子网号 某位&#xff1d;0&#xff1a;IP地址中的对应位为主机号 从一个 IP 数据报的首部并无法判断源主机或目的主机所连接的网络是否进行了子网划分。 使用子网掩码(subnet mask)可…