HEVC學習(三) —— 幀內預測系列之一
今天開始進入實質性內容的討論,主要是從程式碼實現的角度比較深入地研究幀內預測演算法。由於幀內預測涉及到的函式的數量相對於編解碼器複雜部分來說少,但事實上大大小小也牽涉到了十幾二十個函式(沒具體統計過,只是大概估算了下),想要一下子討論完比較困難,所以打算在接下來的若干篇文章裡逐步地儘可能詳盡地分析每一個較為重要的函式。今天所要討論的是fillReferenceSamples這個函式,它主要功能是在真正進行幀內預測之前,使用重建後的Yuv影象對當前PU的相鄰樣點進行賦值,為接下來進行的角度預測提供參考樣點值。
這個函式實際上實現的是官方當前標準(JCTVC-J1003)draft 8.4.4.2.2(Reference sample substitution process for intra sample prediction),具體內容我這裡就不重複了,有興趣的朋友可以自己下下來去看看,我先簡單把該過程複述一遍:(1)如果所有相鄰點均不可用,則參考樣點值均被賦值為DC值;(2)如果所有相鄰點均可用,則參考樣點值都會被賦值為重建Yuv影象中與其位置相同的樣點值;(3)如果不滿足上述兩個條件,則按照從左下往左上,從左上往右上的掃描順序進行遍歷,(如下圖所示),如果第一個點不可用,則使用下一個可用點對應的重建Yuv樣點值對其進行賦值;對於除第一個點外的其它鄰點,如果該點不可用,則使用它的前一個樣點值進行賦值(前一個步驟保證了前一個樣點值一定是存在的),直到遍歷完畢。
在瞭解了演算法的大致流程後,我們就可以看看程式碼是怎麼具體實現的了。我主要把我對程式碼的註釋連同程式碼一起貼出來,以供大家參考,有些地方理解上肯定還是有所欠缺的,望大家不吝賜教。
Void TComPattern::fillReferenceSamples( TComDataCU* pcCU, Pel* piRoiOrigin, Int* piAdiTemp, Bool* bNeighborFlags, Int iNumIntraNeighbor, Int iUnitSize, Int iNumUnitsInCu, Int iTotalUnits, UInt uiCuWidth, UInt uiCuHeight, UInt uiWidth, UInt uiHeight, Int iPicStride, Bool bLMmode ) { Pel* piRoiTemp; //!< piRoiOrgin指向重建Yuv影象對應於當前PU所在位置的首地址,piRoiTemp用於指向所感興趣的重建Yuv的位置,piAdiTemp Int i, j; Int iDCValue = ( 1<<( g_uiBitDepth + g_uiBitIncrement - 1) ); if (iNumIntraNeighbor == 0) // all samples are not available { // Fill border with DC value for (i=0; i<uiWidth; i++) //!< AboveLeft + Above + AboveRight { piAdiTemp[i] = iDCValue; } for (i=1; i<uiHeight; i++) //!< Left + BelowLeft { piAdiTemp[i*uiWidth] = iDCValue; } } else if (iNumIntraNeighbor == iTotalUnits) // all samples are available { // Fill top-left border with rec. samples piRoiTemp = piRoiOrigin - iPicStride - 1; //!< 左上 piAdiTemp[0] = piRoiTemp[0]; // Fill left border with rec. samples piRoiTemp = piRoiOrigin - 1; //!< 左 if (bLMmode) //!< bLMmode 預設值為false { piRoiTemp --; // move to the second left column } for (i=0; i<uiCuHeight; i++) { piAdiTemp[(1+i)*uiWidth] = piRoiTemp[0]; //!< 每個參考樣點賦值為對應位置重建Yuv樣點值 piRoiTemp += iPicStride; //!< 指向重建Yuv下一行 } // Fill below left border with rec. samples for (i=0; i<uiCuHeight; i++) //!< 左下 { piAdiTemp[(1+uiCuHeight+i)*uiWidth] = piRoiTemp[0]; //!< 每個參考樣點賦值為對應位置重建Yuv樣點值 piRoiTemp += iPicStride; } // Fill top border with rec. samples piRoiTemp = piRoiOrigin - iPicStride; //!< 重新指向重建Yuv的上方 for (i=0; i<uiCuWidth; i++) { piAdiTemp[1+i] = piRoiTemp[i]; //!< 每個參考樣點賦值為對應位置重建Yuv樣點值 } // Fill top right border with rec. samples piRoiTemp = piRoiOrigin - iPicStride + uiCuWidth; //!< 指向右上 for (i=0; i<uiCuWidth; i++) { piAdiTemp[1+uiCuWidth+i] = piRoiTemp[i]; //!< 每個參考樣點賦值為對應位置重建Yuv樣點值 } } else // reference samples are partially available { Int iNumUnits2 = iNumUnitsInCu<<1; Int iTotalSamples = iTotalUnits*iUnitSize; //!< neighboring samples的總數,iTotalUnits以4x4塊為單位,iUnitSize為塊的大小 Pel piAdiLine[5 * MAX_CU_SIZE]; Pel *piAdiLineTemp; //!<臨時儲存用於填充neighboring samples的樣點值 Bool *pbNeighborFlags; Int iNext, iCurr; Pel piRef = 0; //!< 儲存臨時樣點值 // Initialize for (i=0; i<iTotalSamples; i++) //!< 先將所有樣點值賦值為DC值 { piAdiLine[i] = iDCValue; } // Fill top-left sample piRoiTemp = piRoiOrigin - iPicStride - 1; //!< 指向重建Yuv左上角 piAdiLineTemp = piAdiLine + (iNumUnits2*iUnitSize); //!< piAdiLine的掃描順序為左下到左上,再從左到右上 pbNeighborFlags = bNeighborFlags + iNumUnits2; //!< 標記neighbor可用性的陣列同樣移動至左上角 if (*pbNeighborFlags) //!< 如果左上角可用,則左上角4個畫素點均賦值為重建Yuv左上角的樣點值 { piAdiLineTemp[0] = piRoiTemp[0]; for (i=1; i<iUnitSize; i++) { piAdiLineTemp[i] = piAdiLineTemp[0]; } } // Fill left & below-left samples piRoiTemp += iPicStride; //!< piRoiTemp指向重建Yuv的左邊界 if (bLMmode) { piRoiTemp --; // move the second left column } piAdiLineTemp--; //!< 移動指標置左邊界 pbNeighborFlags--; //!< 移動指標置左邊界 for (j=0; j<iNumUnits2; j++) //!< 從左往左下掃描 { if (*pbNeighborFlags) //!< 如果可用 { for (i=0; i<iUnitSize; i++) //!< 每個4x4塊裡的4個樣點分別被賦值為對應位置的重建Yuv的樣點值 { piAdiLineTemp[-i] = piRoiTemp[i*iPicStride]; } } piRoiTemp += iUnitSize*iPicStride; //!< 指標挪到下一個行(以4x4塊為單位,即實際上下移了4行) piAdiLineTemp -= iUnitSize; //!< 指標下移 pbNeighborFlags--; //!< 指標下移 } // Fill above & above-right samples piRoiTemp = piRoiOrigin - iPicStride; //!< piRoiTemp 指向重建Yuv的上邊界 piAdiLineTemp = piAdiLine + ((iNumUnits2+1)*iUnitSize); //!< 指向上邊界 pbNeighborFlags = bNeighborFlags + iNumUnits2 + 1; //!< 指向上邊界 for (j=0; j<iNumUnits2; j++) //!< 從左掃描至右上 { if (*pbNeighborFlags) { for (i=0; i<iUnitSize; i++) { piAdiLineTemp[i] = piRoiTemp[i]; //!< 每個4x4塊裡的4個樣點分別被賦值為對應位置的重建Yuv的樣點值 } } piRoiTemp += iUnitSize; //!< 指標右移 piAdiLineTemp += iUnitSize; //!< 指標右移 pbNeighborFlags++; //!< 指標右移 } // Pad reference samples when necessary iCurr = 0; iNext = 1; piAdiLineTemp = piAdiLine; //!< 指向左下角(縱座標最大的那個位置,即掃描起點) while (iCurr < iTotalUnits) //!< 遍歷所有neighboring samples { if (!bNeighborFlags[iCurr]) //!< 該點不可用 { if(iCurr == 0) //!< 第一個點就不可用 { while (iNext < iTotalUnits && !bNeighborFlags[iNext]) //!< 找到第1個可用點 { iNext++; } piRef = piAdiLine[iNext*iUnitSize]; //!< 儲存該可用點的樣點值 // Pad unavailable samples with new value while (iCurr < iNext) //!< 使用儲存下來的第一個可用點的樣點值賦值給在其之前被標記為不可用的點 { for (i=0; i<iUnitSize; i++) { piAdiLineTemp[i] = piRef; } piAdiLineTemp += iUnitSize; iCurr++; } } else //!< 當前點不可用且其不是第一個點,則使用該點的前一個可用點的樣點值進行賦值 { piRef = piAdiLine[iCurr*iUnitSize-1]; for (i=0; i<iUnitSize; i++) { piAdiLineTemp[i] = piRef; } piAdiLineTemp += iUnitSize; iCurr++; } } else //!< 當前點可用,繼續檢查下一點 { piAdiLineTemp += iUnitSize; iCurr++; } } // Copy processed samples piAdiLineTemp = piAdiLine + uiHeight + iUnitSize - 2; //!< piAdiLineTemp = (piAdiLine + 128) + 3,跳過之前對左上角擴充的3個畫素點 for (i=0; i<uiWidth; i++) //!< 將最終結果拷貝到左上、上、右上邊界 { piAdiTemp[i] = piAdiLineTemp[i]; } piAdiLineTemp = piAdiLine + uiHeight - 1; //!< uiHeight = uiCUHeight2 + 1 for (i=1; i<uiHeight; i++) //!< 將最終結果拷貝到左和左下邊界 { piAdiTemp[i*uiWidth] = piAdiLineTemp[-i]; //!< piAdiLineTemp下標為-i是因為賦值方向與實際儲存方向是相反的 } } }
關於一個PU的相鄰點,以及它的相鄰點的可用性如何判斷的問題,是一個細節問題,並不會影響我們對這個函式實現功能的理解,但是,為了更好地理解這一個過程,這些座標為什麼這麼算還是一個值得討論的問題,限於篇幅,本文不作討論。我會在接下來的文章中陸續對留下來的問題作更為詳盡的討論。
相關推薦
HEVC學習(三) —— 幀內預測系列之一
今天開始進入實質性內容的討論,主要是從程式碼實現的角度比較深入地研究幀內預測演算法。由於幀內預測涉及到的函式的數量相對於編解碼器複雜部分來說少,但事實上大大小小也牽涉到了十幾二十個函式(沒具體統計過,只是大概估算了下),想要一下子討論完比較困難,所以打算在接下來的若干篇文章
HEVC學習(五) —— 幀內預測系列之三
今天主要介紹幀內預測一個很重要的函式initAdiPattern,它的主要功能有三個,(1)檢測當前PU的相鄰樣點包括左上、上、右上、左、左下鄰域樣點值的可用性,或者說檢查這些點是否存在;(2)參考樣點的替換過程,主要實現的是JCTVC-J1003即draft 8.4.4.
(HEVC)幀內預測:fillReferenceSamples函式講解
將自己看到的對我幫助很大的文章轉載於此,方便下次查閱。 今天開始進入實質性內容的討論,主要是從程式碼實現的角度比較深入地研究幀內預測演算法。由於幀內預測涉及到的函式的數量相對於編解碼器複雜部分來說少,但事實上大大小小也牽涉到了十幾二十個函式(沒具體統計過,只是大概估
day12-內建模組學習(三)
我的部落格呀,從以前的預習變成了複習了,複習的東西還沒有寫完,哎 今日目錄 1.序列化模組 2.加密模組 3.包的使用 4.random模組 5.shutil模組 開始今日份總結 1.序列化模組 在學習序列化模組之前,對於網路傳輸以及檔案寫入讀取都是採用將列表,字典這些資料型
HEVC函式入門(2)——幀內編碼一個CU
這裡依然整理自http://blog.csdn.net/shaqoneal/article/details/37500715 且閱讀CU這部分主要對我而言是為了QP。另外一個方向是Tile不要迷失啦!!! 提醒我自己看http://blog.csdn.net
HEVC學習(三十五) —— 去方塊濾波之六
先看HM中定義tC、β這兩個變數的表格,與draft中的Table 8-10相對應: const UChar tctable_8x8[54] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,
爬蟲庫之BeautifulSoup學習(三)
子節點 rom lac repr 文檔 strong 爬蟲 time contents 遍歷文檔樹: 1、查找子節點 .contents tag的.content屬性可以將tag的子節點以列表的方式輸出。 print soup.body.cont
Java學習(三)JSP學習1
rtm 斷開 三大指令 保持 web應用 對比 c語言 let 新建 一、 理解JSP技術 JSP全名為Java Server Pages,中文名叫java服務器頁面,其根本是一個簡化的Servlet設計,它 是由Sun Microsystems公司倡導、許多公司參
java學習(三)類
con void pub oid 修改密碼 tro int str 用戶 類 public class Dog{ String breed; int age; String color; void barking(){ } void hungr
Qt Installer Framework的學習(三)
科技 released his 表示 star online 解壓 dem 普通 Qt Installer Framework的學習(三) Qt Installer Framework的樣例中。通常是這種:config目錄一般放了一個config.xml文件,包括的是安裝
PYTHON學習(三)之利用python進行數據分析(1)---準備工作
-- 下載 rip 安裝包 png 要求 eight code 電腦 學習一門語言就是不斷實踐,python是目前用於數據分析最流行的語言,我最近買了本書《利用python進行數據分析》(Wes McKinney著),還去圖書館借了本《Python數據分析基礎教程--N
Python學習(三) 八大排序算法的實現(下)
ram tty adjust 二叉樹 turn bre python 使用 元素 本文Python實現了插入排序、基數排序、希爾排序、冒泡排序、高速排序、直接選擇排序、堆排序、歸並排序的後面四種。 上篇:Python學習(三) 八大排序算法的實現(上)
linux4.10.8 內核移植(三)---裁剪內核
conf .cn 需要 正常 多少 分享 內核 col make 一、裁剪內核 1.1 第一次修改 現在的內核大小為2.8M左右,要裁剪到2.0M以下,畢竟給內核分區就只有2.0M。 這兩個設備我們沒有,裁剪掉。 進入make menuconfig中
RabbitMQ學習(三)訂閱/發布
cto submit actor nal chan true exec oid lsp RabbitMQ學習(三)訂閱/發布 1.RabbitMQ模型 前面所學都只用到了生產者、隊列、消費者。如上圖所示,其實生產者並不直接將信息傳輸到隊列中,在生產者和隊列
C++學習(三)入門篇——函數
image clu square src 函數接口 值類型 使用 mes 技術分享 C++函數分兩種:有返回值的和沒返回值的 1.有返回值的函數 調用函數流程 如圖,sqrt(6.25)為函數調用,
python學習(三)
操作數 sdf dfs 查找子串 索引 start val 成員 放置 第三章 使用字符串
【轉】JMeter學習(三)元件的作用域與執行順序
ces ner 處理器 規則 fig 子節點 控制器 conf 節點 1.元件的作用域 JMeter中共有8類可被執行的元件(測試計劃與線程組不屬於元件),這些元件中,取樣器是典型的不與其它元件發生交互作用的元件,邏輯控制器只對其子節點的取樣器有效,而其它元件(config
vue移動音樂app開發學習(三):輪播圖組件的開發
hub out webapp width eth reat slot utc -1 本系列文章是為了記錄學習中的知識點,便於後期自己觀看。如果有需要的同學請登錄慕課網,找到Vue 2.0 高級實戰-開發移動端音樂WebApp進行觀看,傳送門。 完成後的頁面狀態以及項目結構如
SpringMVC源代碼學習(二)FrameworkServlet內處理請求的流程
重新 tex events ... resp star 方便 沒有 isp 以下內容基於書:《看透SpringMVC-源代碼分析與實踐》基本照搬。。。用於自己查閱備忘。 先看一眼DispatcherServlet繼承樹 我們知道servlet處理方法都是通過HttpSer
selenium + python自動化測試unittest框架學習(三)webdriver對頁面其他控件操作(三)
文件的 文件路徑 內容 option selenium script web 對話 對話框 1.對話框,下拉框 (1)對話框的有兩種,一種是iframe格式的,需要switch_to_iframe()進行定位,現在大部分的對話框是div格式的,這種格式的可以通過層級定位來定