HEVC部分源碼剖析

HEVC(High Efficiency Video Coding) 是2013年提出的最新的視頻編碼標準,它的核心是將視頻中的每一幀圖像分割成 Coding Tree Unit(CTU) 个初,然后對每個CTU確定最佳的分割深度怎披,關于具體的算法可以參考:

在了解了一些HEVC內部的術語如CTU, CU, TU...后疲牵,我們來試著利用HEVC的源代碼輸出CTU的分割信息。

代碼的下載和配置

首先疏尿,需要下載源代碼,源代碼是使用SVN托管的易桃,因此需要用SVN下載褥琐,我們可以使用TortoiseSVN進行下載。安裝好TortoiseSVN后晤郑,打開SVN browser踩衩,在地址欄輸入

https://hevc.hhi.fraunhofer.de/svn/svn_HEVCSoftware/

就可以看到源代碼的目錄結構,然后選擇export贩汉,就可以將源代碼保存到本地。注意這個目錄下面有很多個版本锚赤,我們這里選擇/trunk/目錄下的主版本導出匹舞。

導出到本地之后,進入/build/文件夾线脚,里面就是編譯所需的文件赐稽,支持不同的平臺:

compilation

我使用的是VS2015編譯,打開這個工程之后浑侥,可以看到所有的項目姊舵,對應的源代碼在/source/文件夾下:

HEVC projects

選擇F7或者Build Solution就可以進行編譯,生成exe文件寓落,但是如果你直接去運行的話會發(fā)現程序輸出一段說明文字之后就結束了括丁,這是因為我們需要先進行配置,指定輸入的視頻和其他的一些參數伶选。

配置文件在/cfg/目錄下史飞,其中最主要的兩個配置文件是bitstream.cfgencoder_intra_main.cfg,我們把這兩個文件復制到一個自定義工作目錄下仰税,比如E:\HM\trunk\workspace构资,接下來在Visual Studio中,右擊解決方案中“TAppEncoder”->“設為啟動項目”
再右擊“TAppEncoder”->”屬性”->”配置屬性”->”調試” 陨簇,在工作目錄欄指定工作目錄路徑E:\HM\trunk\workspace吐绵,在命令參數欄中填寫-c encoder_intra_main.cfg -c bitstream.cfg,如圖:

config

對于encoder_intra_main.cfg河绽,我們需要進行配置的參數就只有:

#======== Quantization =============
QP                            : 32          # Quantization parameter(0-51)

這個值從0到51都可以己单。而在bitstream.cfg中,我們需要配置以下的參數:

bitstream.cfg

InputFile后面寫上輸入的.YUV視頻文件的絕對路徑葵姥,FramesToBeEncoded是指你想編碼多少幀荷鼠,至于FrameRate, SourceWidth, SourceHeight這些信息都在視頻文件名中,比如我這里使用的BasketballDrill_832x480_50.yuv榔幸,文件名中就能看出寬度允乐、高度和幀率矮嫉。

接下來就可以運行了,運行結束后牍疏,工作目錄下會出現兩個文件:str.bin和rec.yuv蠢笋,其中rec.yuv是編碼過程中重建的yuv圖像,str.bin則是壓縮后的碼流鳞陨。

YUV文件

首先提供一些YUV視頻資源的下載地址:

對于HEVC的測試輸入昨寞,一般都是采用YUV文件,推薦安裝一個YUV播放器:YUView厦滤,可以看到每一幀的圖片援岩。

YUV文件就是由一幀一幀的圖像構成的,在之前提到的配置文件中的參數FramesToBeEncoded中掏导,你指定的編碼多少幀享怀,HEVC就會取前面的多少幀進行編碼。如果想提取出YUV文件中的每一幀趟咆,可以使用FFmpeg

ffmpeg -video_size 832x480 -r 50 -pixel_format yuv420p -i BasketballDrill_832x480_50.yuv output-%d.png

當然添瓷,使用的時候這個命令中的視頻大小、幀率值纱、像素格式鳞贷、視頻名稱都需要根據具體的視頻修改。其中像素格式(-pixel_format)這個參數可能很多人不知道該選什么虐唠,在命令行中輸入:

ffmpeg -pix_fmts

可以看到FFmpeg支持的格式搀愧,這里使用的yuv420p是一般采用的格式,420代表的是 YCbCr color space with 4:2:0 sampling.

This separates a color representation into three components called Y, Cb, and Cr. The Y component is also called luma, and represents brightness. The two chroma components Cb and Cr represent the extent to which the color deviates from gray toward blue and red, respectively.

關于color space疆偿,可以參考:
colorspace – FFmpeg

如果不想了解這么多妈橄,只想直接知道自己所使用的YUV應該對應哪種 pixel format,方法是使用YUView打開YUV文件翁脆,在右側的properties中有一個YUV Format眷蚓,然后用這個format,去對應:

Most commonly used formats pixel_format
8-bit 4:2:0 yuv420p
8-bit 4:2:2 yuv422p
8-bit 4:4:4 yuv444p
10-bit 4:2:0 yuv420p10le
10-bit 4:2:2 yuv422p10le

參考自:Chroma Subsampling – FFmpeg

CTU分割信息的輸出

這一部分是在Encoder部分完成的反番,所以我們主要看TAppEncoder這個項目沙热。在TEncGOP.cpp中,有一個precompressSlice()和一個compressSlice()函數罢缸,前者可以不用管篙贸,而后者則計算出了每個ctu的最佳分割深度,轉到compressSlice()函數的定義枫疆,來到了TEncSlice.cpp爵川,其中對ctu進行最佳深度計算的關鍵函數是compressCtu()這個函數。

compressCtu()這個函數內部息楔,當xCompressCU()這個函數運行完成之后寝贡,最佳的分割深度就已經得到了扒披,此時,如果想要輸出當前ctu最佳的分割圃泡,可以在xCompressCU()這個函數后面碟案,加上下面的語句:

  xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 DEBUG_STRING_PASS_INTO(sDebug) );
  //============== add code from here ===============
  TComDataCU* DepthCU = m_ppcBestCU[0];
  UInt tempDepth;
  ofstream outfile("PartitionInfo.txt", ios::in | ios::app);
  for (UInt iPartitionNum = 0; iPartitionNum < DepthCU->getTotalNumPart(); iPartitionNum++)
  {
      if (iPartitionNum % 16 == 0) {
          outfile << endl;
      }
      tempDepth = DepthCU->getDepth(g_auiRasterToZscan[iPartitionNum]);
      outfile << " " << tempDepth;
  }
  outfile << endl;
  // =============code added end here===============

最佳的分割保存在m_ppcBestCU[0]里面,使用getDepth()[i]就可以得到颇蜡。

接下來价说,如果我們不僅想輸出所有的ctu分割信息,還想輸出當前是第幾個ctu风秤,這時候就要去找哪個變量記錄了當前ctu的編號鳖目,我們可以回到compressCtu()函數開始的地方,看到這個函數接收了一個參數TComDataCU* pCtu缤弦,而我們可以通過pCtu->getCtuRsAddr()這個語句得到當前CTU的編號疑苔,所以,可以在xCompressCU()之前甸鸟,加上下面的語句:

//============== add code from here ===============
ofstream outfile("PartitionInfo.txt", ios::in | ios::app);
UInt temp_ctu_addr = pCtu->getCtuRsAddr();
outfile << "ctu:" << temp_ctu_addr << endl;
outfile.close();
// =============code added end here===============
xCompressCU(m_ppcBestCU[0], m_ppcTempCU[0], 0 DEBUG_STRING_PASS_INTO(sDebug));

除了輸出當前ctu的編號,我們還想要輸出當前編碼到了第幾幀(frame)兵迅,這時仍然可以通過Debug的方式找到存儲這一信息的變量抢韭。最終發(fā)現當前編碼的frame數量存儲在變量m_iFrameRcvd中,而這一變量是在TAppEncTop.cpp文件中恍箭。因此刻恭,如果要輸出當前是第幾幀,可以在TAppEncTop.cpp文件中找到

m_iFrameRcvd++;

這一語句扯夭,并在后面加入:

ofstream outfile("PartitionInfo.txt", ios::in | ios::app);
outfile<< "frame:" << m_iFrameRcvd << endl;

這樣鳍贾,我們就可以輸出每個frame對應的每個ctu的分割信息了,像這樣:

PartitionInfo

然后我們可以使用Matlab畫出每一幀的PU分割信息交洗,參考:


最后補充一下骑科,HEVC在幀內預測的時候,對于每個depth构拳,都要向下分成4個depth遞歸尋找最優(yōu)分割咆爽,因此我們還可以把每個depth對應的RD-cost打印出來。Depth0會分成4個Depth1置森,Depth1會分成4個Depth2,Depth2分成4個Depth3,Depth3向下判斷是8x8還是4x4的PU

打印出每種分割的RD-cost的好處就是愧膀,當我們訓練好了神經網絡模型旅东,用模型生成CU分割深度信息的時候,我們可以用RD-cost來和HEVC編碼器比較RD-cost變化了多少行贪,而神經網絡模型得到的分割方法的RD-cost的計算就可以用之前打印出來的每個depth的RD-cost

xCompressCU()函數內部漾稀,有兩個變量:m_ppcBestCUm_ppcTempCU,想要得到當前深度的RD-cost可以通過調用它們的getTotalCost()方法围橡,不過m_ppcBestCU只存放了最佳的分割深度翁授,所以不能得到全面的RD-cost晾咪,因此只能調用m_ppcTempCUgetTotalCost()方法谍倦。但m_ppcTempCUxCompressCU()函數內部并沒有存放或者被賦值RD-cost信息昼蛀,我們只能到xCheckRDCostIntra()函數內部來獲取。

xCheckRDCostIntra()函數內部仇哆,當前所在的深度存放在變量uiDepth中讹剔。我們可以在

rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );

之后延欠,添加如下代碼沈跨,輸出每一層深度的RD-cost:

  rpcTempCU->getTotalBits() = m_pcEntropyCoder->getNumberOfWrittenBits();
  rpcTempCU->getTotalBins() = ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
  rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );
  // ==============added code to print depth and RDcost==========
  ofstream outfile("rdcost.txt", ios::in | ios::app);
  double temp_rdcost;
  temp_rdcost = rpcTempCU->getTotalCost();
  outfile << "depth:" << uiDepth << endl;
  outfile << temp_rdcost << endl;
  outfile.close();
  // ===============code added end here=====================
  xCheckDQP( rpcTempCU );

同樣饿凛,我們還是需要輸出是在哪一幀,以及哪個CTU笤喳。輸出之后,在進行一些處理蒙畴,就能方便地計算自己的模型的CU劃分對應的RD-cost了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末碑隆,一起剝皮案震驚了整個濱河市上煤,隨后出現的幾起案子劫狠,更是在濱河造成了極大的恐慌独泞,老刑警劉巖苔埋,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荞膘,死亡現場離奇詭異羽资,居然都是意外死亡瓮栗,警方通過查閱死者的電腦和手機瞄勾,發(fā)現死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來进陡,“玉大人,你說我怎么就攤上這事趾疚∮Ю” “怎么了糙麦?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵辛孵,是天一觀的道長赡磅。 經常有香客問我魄缚,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任冶匹,我火速辦了婚禮习劫,結果婚禮上嚼隘,老公的妹妹穿的比我還像新娘诽里。我一直安慰自己谤狡,他們只是感情好桩皿,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布拒贱。 她就那樣靜靜地躺著佛嬉,像睡著了一般暖呕。 火紅的嫁衣襯著肌膚如雪斜做。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天湾揽,我揣著相機與錄音瓤逼,去河邊找鬼。 笑死库物,一個胖子當著我的面吹牛霸旗,可吹牛的內容都是我干的。 我是一名探鬼主播戚揭,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼诱告,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了民晒?” 一聲冷哼從身側響起精居,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎潜必,沒想到半個月后靴姿,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡磁滚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年空猜,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡辈毯,死狀恐怖坝疼,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情谆沃,我是刑警寧澤钝凶,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站唁影,受9級特大地震影響耕陷,放射性物質發(fā)生泄漏。R本人自食惡果不足惜据沈,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一哟沫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锌介,春花似錦嗜诀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至崔慧,卻和暖如春拂蝎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惶室。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工温自, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人皇钞。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓悼泌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鹅士。 傳聞我的和親對象是個殘疾皇子券躁,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內容

  • **之前沒接觸過惩坑,今天剛剛開始掉盅,寫博客就是為了鼓勵自己堅持學習!源碼下載:https://hevc.hhi.fra...
    Persistently閱讀 2,513評論 0 2
  • 在保證視頻圖像質量的前提下以舒,HEVC通過增加一定的計算復雜度趾痘,可以實現碼流在H.264/AVC的基礎上降低50%。...
    加劉景長閱讀 7,865評論 0 6
  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom閱讀 2,696評論 0 3
  • 版本記錄 前言 FFMPEG是一個多媒體轉碼蔓钟、音視頻流化傳輸的一個第三方開源框架永票,并且這個第三方庫是基于C語言的。...
    刀客傳奇閱讀 9,735評論 3 54
  • 編碼方式 在iOS中編碼方式有兩種硬編碼: 在iOS8.0之后,使用原生框架VideoToolBox&AudioT...
    三月望龍閱讀 1,158評論 2 4