1. 程式人生 > >HEVC學習(三) —— 幀內預測系列之一

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格式的,這種格式的可以通過層級定位來定