HEVC码率控制浅析——HM代码阅读之四
继续分析第一篇提到的compressSlice中对LCU的RC参数初始化:
#if RATE_CONTROL_LAMBDA_DOMAIN
Double oldLambda = m_pcRdCost->getLambda();
if ( m_pcCfg->getUseRateCtrl() )
{
Int estQP = pcSlice->getSliceQp();
Double estLambda = -1.0;
Double bpp = -1.0; #if M0036_RC_IMPROVEMENT
if ( ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE && m_pcCfg->getForceIntraQP() ) || !m_pcCfg->getLCULevelRC() )
#else
if ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE || !m_pcCfg->getLCULevelRC() )
#endif
{ //!< 如果当前slice为I slice或者不进行LCU level RC,则LCU直接使用当前slice的QP
estQP = pcSlice->getSliceQp();
}
else
{
#if RATE_CONTROL_INTRA
bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType());
if ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE)
{
estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP);
}
else
{
estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );
estQP = m_pcRateCtrl->getRCPic()->getLCUEstQP ( estLambda, pcSlice->getSliceQp() );
}
#else
bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp();
estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );
estQP = m_pcRateCtrl->getRCPic()->getLCUEstQP ( estLambda, pcSlice->getSliceQp() );
#endif estQP = Clip3( -pcSlice->getSPS()->getQpBDOffsetY(), MAX_QP, estQP ); m_pcRdCost->setLambda(estLambda);
#if M0036_RC_IMPROVEMENT
#if RDOQ_CHROMA_LAMBDA
// set lambda for RDOQ
Double weight=m_pcRdCost->getChromaWeight();
m_pcTrQuant->setLambda( estLambda, estLambda / weight );
#else
m_pcTrQuant->setLambda( estLambda );
#endif
#endif
} m_pcRateCtrl->setRCQP( estQP );
pcCU->getSlice()->setSliceQpBase( estQP ); //!< 设置编码时使用的QP值
}
#endif
#if RATE_CONTROL_INTRA
Double TEncRCPic::getLCUTargetBpp(SliceType eSliceType)
#else
Double TEncRCPic::getLCUTargetBpp() //!< LCU level bit allocation
#endif
{
Int LCUIdx = getLCUCoded(); //!< 未编码LCU数
Double bpp = -1.0;
Int avgBits = 0;
#if !M0036_RC_IMPROVEMENT
Double totalMAD = -1.0;
Double MAD = -1.0;
#endif #if RATE_CONTROL_INTRA
if (eSliceType == I_SLICE){
Int noOfLCUsLeft = m_numberOfLCU - LCUIdx + 1;
Int bitrateWindow = min(4,noOfLCUsLeft);
Double MAD = getLCU(LCUIdx).m_costIntra; if (m_remainingCostIntra > 0.1 )
{
Double weightedBitsLeft = (m_bitsLeft*bitrateWindow+(m_bitsLeft-getLCU(LCUIdx).m_targetBitsLeft)*noOfLCUsLeft)/(Double)bitrateWindow;
avgBits = Int( MAD*weightedBitsLeft/m_remainingCostIntra );
}
else
{
avgBits = Int( m_bitsLeft / m_LCULeft );
}
m_remainingCostIntra -= MAD;
}
else
{
#endif
#if M0036_RC_IMPROVEMENT
Double totalWeight = 0;
for ( Int i=LCUIdx; i<m_numberOfLCU; i++ )
{
totalWeight += m_LCUs[i].m_bitWeight;
}
Int realInfluenceLCU = min( g_RCLCUSmoothWindowSize, getLCULeft() );
avgBits = (Int)( m_LCUs[LCUIdx].m_bitWeight - ( totalWeight - m_bitsLeft ) / realInfluenceLCU + 0.5 );
#else
if ( m_lastPicture == NULL ) //!< 如果前一帧图像为空,则直接求平均比特数
{
avgBits = Int( m_bitsLeft / m_LCULeft );
}
else //!< 利用前一帧保存下来的MAD求出当前LCU对应的权重,注意:此处的MAD已经进行过K0103中公式的两个处理了。
{
MAD = m_lastPicture->getLCU(LCUIdx).m_MAD;
totalMAD = m_lastPicture->getTotalMAD();
for ( Int i=0; i<LCUIdx; i++ )
{
totalMAD -= m_lastPicture->getLCU(i).m_MAD;
} if ( totalMAD > 0.1 )
{
avgBits = Int( m_bitsLeft * MAD / totalMAD );
}
else
{
avgBits = Int( m_bitsLeft / m_LCULeft );
}
}
#endif
#if RATE_CONTROL_INTRA
}
#endif if ( avgBits < 1 )
{
avgBits = 1;
} bpp = ( Double )avgBits/( Double )m_LCUs[ LCUIdx ].m_numberOfPixel;
m_LCUs[ LCUIdx ].m_targetBits = avgBits; return bpp;
} Double TEncRCPic::getLCUEstLambda( Double bpp )
{
Int LCUIdx = getLCUCoded();
Double alpha;
Double beta;
if ( m_encRCSeq->getUseLCUSeparateModel() ) //!< enable LCU level RC
{
alpha = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_alpha;
beta = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_beta;
}
else //!< 只进行picture level 的 RC,故alpha,beta使用的是picture level的值
{
alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;
beta = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;
} Double estLambda = alpha * pow( bpp, beta );
//for Lambda clip, picture level clip
Double clipPicLambda = m_estPicLambda; //for Lambda clip, LCU level clip
Double clipNeighbourLambda = -1.0;
for ( int i=LCUIdx - 1; i>=0; i-- )
{
if ( m_LCUs[i].m_lambda > 0 )
{
clipNeighbourLambda = m_LCUs[i].m_lambda;
break;
}
}
//!< 在K0103的section 3.2中进行了定义
if ( clipNeighbourLambda > 0.0 )
{
estLambda = Clip3( clipNeighbourLambda * pow( 2.0, -1.0/3.0 ), clipNeighbourLambda * pow( 2.0, 1.0/3.0 ), estLambda );
} if ( clipPicLambda > 0.0 )
{
estLambda = Clip3( clipPicLambda * pow( 2.0, -2.0/3.0 ), clipPicLambda * pow( 2.0, 2.0/3.0 ), estLambda );
}
else
{
estLambda = Clip3( 10.0, 1000.0, estLambda );
} if ( estLambda < 0.1 )
{
estLambda = 0.1;
} return estLambda;
} Int TEncRCPic::getLCUEstQP( Double lambda, Int clipPicQP )
{
Int LCUIdx = getLCUCoded();
Int estQP = Int( 4.2005 * log( lambda ) + 13.7122 + 0.5 ); //for Lambda clip, LCU level clip
Int clipNeighbourQP = g_RCInvalidQPValue;
for ( int i=LCUIdx - 1; i>=0; i-- )
{
if ( (getLCU(i)).m_QP > g_RCInvalidQPValue )
{
clipNeighbourQP = getLCU(i).m_QP;
break;
}
} //!< 在K0103的section 3.2中进行了定义
if ( clipNeighbourQP > g_RCInvalidQPValue )
{
estQP = Clip3( clipNeighbourQP - 1, clipNeighbourQP + 1, estQP );
} estQP = Clip3( clipPicQP - 2, clipPicQP + 2, estQP ); return estQP;
}
至此,RC的初始化过程分析完毕,从之前几篇也能发现,其实看懂代码并不难,只要把提案看明白了,对整个流程熟悉,再把HM的框架搞清楚了,基本上就是照着提案找对应代码的事情了。但是,应该指出的是,这里仅仅只是浅显分析代码的含义,有关算法的深层内涵,比如为什么要这么做,这个值为什么要设置成这样等问题,需要在了解代码的基础上,进一步地仔细研究才行。
HEVC码率控制浅析——HM代码阅读之四的更多相关文章
- HEVC码率控制浅析——HM代码阅读之一
HM的码率控制提案主要参考如下三篇:K0103,M0036,M0257.本文及后续文章将基于HM12.0进行讨论,且首先仅讨论K0103对应的代码,之后再陆续补充M0036,M0257对应的代码分析, ...
- HEVC码率控制浅析——HM代码阅读之二
上一篇文章主要讨论了RC的总体框架,本文开始分析具体的代码实现细节.分析的顺序按照总体框架来,即初始化-->更新. (1)m_cRateCtrl.init() #if M0036_RC_IMPR ...
- 最近调试HEVC中码率控制, 发现HM里面一个重大bug
最近调试HEVC中码率控制, 发现里面一个重大bug! 码率控制中有这么一个函数: Int TEncRCGOP::xEstGOPTargetBits( TEncRCSeq* encRCSeq, Int ...
- MediaInfo代码阅读
MediaInfo是一个用来分析媒体文件的开源工具. 支持的文件非常全面,基本上支持所有的媒体文件. 最近是在做HEVC开发,所以比较关注MediaInfo中关于HEVC的分析与处理. 从Meid ...
- FFMpeg的码率控制
mediaxyz是一位研究ffmpeg有三年的高人了,这几天一直在折腾ffmpeg中的x264,就是不知道该如何控制码率,主要是参数太多,也不知道该如何设置,在google上search了一下,这方面 ...
- [转载] FFMpeg的码率控制
mediaxyz是一位研究ffmpeg有三年的高人了,这几天一直在折腾ffmpeg中的x264,就是不知道该如何控制码率,主要是参数太多,也不知道该如何设置,在google上search了一下,这方面 ...
- [置顶] Linux协议栈代码阅读笔记(一)
Linux协议栈代码阅读笔记(一) (基于linux-2.6.21.7) (一)用户态通过诸如下面的C库函数访问协议栈服务 int socket(int domain, int type, int p ...
- Bleve代码阅读(二)——Index Mapping
引言 Bleve是Golang实现的一个全文检索库,类似Lucene之于Java.在这里通过阅读其代码,来学习如何使用及定制检索功能.也是为了通过阅读代码,学习在具体环境下Golang的一些使用方式. ...
- py-faster-rcnn代码阅读1-train_net.py & train.py
# train_net.py#!/usr/bin/env python # -------------------------------------------------------- # Fas ...
随机推荐
- poj 1363 Rails in PopPush City &&【求堆栈中合法出栈顺序次数】
问题如下: 问题 B: Rails 时间限制: Sec 内存限制: MB 提交: 解决: [提交][状态][讨论版] 题目描述 There is a famous railway station in ...
- Week2(9月19日):增加一个CodeFirst的例子来说明
Part I:提问 =========================== 1.上堂课中我们使用了()数据库,它是()可部署的,只需要将相应的()文件添加到应用程序的()文件夹,就可以使用了,该数据 ...
- Mysql 启动失败 报错 1067
Mysql装好后,重启电脑第二次发现服务无法启动.提示如下: ------------------------ MySQL 服务无法启动. 系统出错. 发生系统错误 1067. 进程意外终止. --- ...
- 怎样删除在Github中创建的项目
像我这种刚開始学习的人总会不可避免的创建了一些測试性的项目.随后自然就是要删除了.那么该怎样删除呢? 你此刻可能处于这个界面: 也可能处于这个界面: 假设是第一个界面直接点击右側的"Sett ...
- 二维码的妙用:通过Zxing实现wifi账号password分享功能
二维码是搭载信息的一种载体,通过二维码能够传递名片.网址.商品信息等,本文讲到二维码的第二种妙用:通过二维码实现wifi账号和password分享. 关于二维码的基础知识,请訪问:二维码的生成细节和原 ...
- javascript每日一练(六)——事件一
一.event对象 var oEvent = ev || event;//获取事件对象 oEvent.clientX oEvent.clientY//获取鼠标坐标 oEvent.cancelBubbl ...
- SQLite数据库框架ORMLite与GreenDao的简单比较
笔记摘要:最近准备使用数据库做个缓存,以前因为项目中的实时性要求比较高,所以在整体的框架中就没有加缓存,有些地方只 是简单的将对象保存到了Preference中,所以并没有对数据库方面有所研究,既然准 ...
- 京香julia_百度百科
京香julia_百度百科 京香julia
- Linux less命令
less 工具也是对文件或其它输出进行分页显示的工具,应该说是linux正统查看文件内容的工具,功能极其强大.less 的用法比起 more 更加的有弹性.在 more 的时候,我们并没有办法向前面翻 ...
- iOS经常使用加密方式(MD5,AES,BASE64)与网络数据安全
演示样例项目下载地址 https://github.com/cerastes/Encryption 1MD5 创建MD5类 #import <Foundation/Foundation.h&g ...