HEVC码率控制(二):从compressGOP()到compressSlice()
2016-12-16 22:26
337 查看
compressGOP()函数要点:
根据iGOPid逐次递增,对一个GOP内的picture依次编码根据picture level对当前picture进行码率分配
实际中一个Pic就是一个Slice,因此关于slice的循环只执行一次(默认)
根据已经编过的picture,来得到当前picture的lambda
得到当前picture的lambda后,对该picture下每一个CTU进行码率分配
一个picture编完,码流写好了,根据实际编码所用的比特,更新对应level下的lambda参数
compressGOP() { ... for(Int iGOPid=0;iGOPid<m_iGopSize;iGOPid++) //GOP内picture循环 { m_pcSliceEncoder->initEncSlice(); //当前Slice编码参数QP、lambda的默认设置,会在码率控制中进一步被修改 ... if(m_pcCfg->getUseRateCtrl()) { m_pcRateCtrl->initRCPic(frameLevel);//根据frame level进行比特分配 ... lambda=m_pcRateCtrl->getRCPic()->estimatePicLambda();//确定当前picture的lambda sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda,.. );//QP由lambda计算 ... } for(UInt nextCtuTsAddr = 0; nextCtuTsAddr < numberOfCtusInFrame; ) //这个循环只执行了一次 { ... m_pcSliceEncoder->compressSlice(); ... } ... //编完一帧,开始熵编码,写码流等等 ... if(m_pcCfg->getUseRateCtrl()) { ... updataAfterPicture(); //编完一帧,更新当前picture level对应的lambda参数值(alpha和beta) ... } }//iGOPid loop }
estimatePicLambda()函数中,在得到lambda之后,对一个picture内所有LCU进行比特分配:
Double TEncRCPic::estimatePicLambda( ) { Double alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha; Double beta = m_encRCSeq->getPicPara( m_frameLevel ).m_beta; //alpha和beta从数组中对应level处取,这两个值在编完前一个相同level的帧后,被设置 Double bpp = (Double)m_targetBits/(Double)m_numberOfPixel; Double estLambda; if (eSliceType == I_SLICE) { estLambda = calculateLambdaIntra(alpha, beta, pow(m_totalCostIntra/(Double)m_numberOfPixel, BETA1), bpp); } else { estLambda = alpha * pow( bpp, beta ); } Double lastLevelLambda = -1.0; Double lastPicLambda = -1.0; Double lastValidLambda = -1.0; //对estLambda进行Clip操作 m_estPicLambda = estLambda; //TEncRCPic::m_estPicLambda Double totalWeight = 0.0; // initial BU bit allocation weight for ( Int i=0; i<m_numberOfLCU; i++ ) { Double alphaLCU, betaLCU; if ( m_encRCSeq->getUseLCUSeparateModel() ) { alphaLCU = m_encRCSeq->getLCUPara( m_frameLevel, i ).m_alpha; betaLCU = m_encRCSeq->getLCUPara( m_frameLevel, i ).m_beta; } else { alphaLCU = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha; betaLCU = m_encRCSeq->getPicPara( m_frameLevel ).m_beta; } m_LCUs[i].m_bitWeight = m_LCUs[i].m_numberOfPixel * pow( estLambda/alphaLCU, 1.0/betaLCU ); if ( m_LCUs[i].m_bitWeight < 0.01 ) { m_LCUs[i].m_bitWeight = 0.01; } totalWeight += m_LCUs[i].m_bitWeight; } for ( Int i=0; i<m_numberOfLCU; i++ ) { Double BUTargetBits = m_targetBits * m_LCUs[i].m_bitWeight / totalWeight; m_LCUs[i].m_bitWeight = BUTargetBits; } return estLambda; }
m_bitWeight即使每一个CTU对应的比特分配权重
跟新picture level 的alpha和beta函数updateAfterPicture():
void TEncRCPic::updateAfterPicture() { //计算inputLambda //计算calLambda alpha += m_encRCSeq->getAlphaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * alpha; Double lnbpp = log( picActualBpp ); lnbpp = Clip3( -5.0, -0.1, lnbpp ); //按照码率控制算法,跟新alpha和beta TRCParameter rcPara; rcPara.m_alpha = alpha; rcPara.m_beta = beta; m_encRCSeq->setPicPara( m_frameLevel, rcPara ); //将更新后的alpha和beta保存在数组中 }
compressSlice()函数
按照CTU的先后顺序循环遍历编码编完一个CTU之后,要更新参数
void TEncSlice::compressSlice() { ... xDetermineStartAndBoundingCtuTsAddr ( startCtuTsAddr, boundingCtuTsAddr, pcPic ); ... for( UInt ctuTsAddr = startCtuTsAddr; ctuTsAddr < boundingCtuTsAddr; ++ctuTsAddr ) //对一个Slice内所有CTU开始循环编码 { ... if ( m_pcCfg->getUseRateCtrl() ) { Int estQP = pcSlice->getSliceQp(); Double estLambda = -1.0; Double bpp = -1.0; 8f8d //根据LCU的m_bitWeight这个成员计算当前LCU的比特,再除以LCU内Pixel总数,得到bpp。而m_bitWeight这个成员是得到Pic的lambda之后被赋值。 estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP); ... m_pcRdCost->setLambda(estLambda, pcSlice->getSPS()->getBitDepths()); } m_pcCuEncoder->compressCtu(pCtu); ... if(m_pcCfg->getUseRateCtrl()) { ... updateAfterCTU(); ... } //将CTU的编码结果累加到Picture的统计量之中 m_uiPicTotalBits+=pCtu->getTotalBits(); m_dPicRdcost+=PCtu->getTotalCost(); m_uiPicDist+=PCtu->getTotalDistortion; } }
相关文章推荐
- Linux根文件系统分析之init和busybox
- Linux下的C语言编程——买鸡问题
- Docker之Linux UnionFS
- B/S架构简介
- 远程创建用户的脚本
- windows下的托盘编程(Shell_NotifyIcon)
- 如何参与Linux内核社区开发
- shell 脚步获取当前的函数名
- 春哥nginx
- 利用Jekyll Now 在github上创建自己的个人网站
- WPS for Linux,系统缺失字体
- Linux下Tomcat的启动、关闭、杀死进程
- nginx配置
- Docker之Linux Cgroups
- Docker之Linux Namespace
- archlinux更新错误
- archlinux pacman 常用选项
- 使用Spark批量上传图片到HBase中并同时使用OpenCV提取SIFT特征值
- 解决Nginx: [error] open() "/usr/local/Nginx/logs/Nginx.pid
- nginx 启动,停止和重新加载配置