《H.264/AVC視訊編解碼技術詳解》視訊教程已經在“CSDN學院”上線,視訊中詳述了H.264的背景、標準協議和實現,並通過一個實戰工程的形式對H.264的標準進行解析和實現,歡迎觀看!

“紙上得來終覺淺,絕知此事要躬行”,只有自己按照標準文件以程式碼的形式操作一遍,才能對視訊壓縮編碼標準的思想和方法有足夠深刻的理解和體會!

GitHub程式碼地址:點選這裡

一、去塊濾波的基本概念

1.1 去塊濾波的作用

去塊濾波器(Deblocking Filter)是視訊編解碼器中的重要組成部分,其核心作用在於消除編碼過程中產生的影象塊效應。影象中的塊效應主要因為以巨集塊為基本單元的編碼結構而產生。在編碼中,每個巨集塊的子塊都會按照既定分割方式進行預測、變換和量化編碼,在這個過程中可能導致塊效應的因素主要有以下幾種:

  1. 由於變換和量化編碼的運算精度誤差導致邊界出現不連續;
  2. 由於位元速率設定較低,量化強度較大,或者相鄰巨集塊的量化引數不一致導致重建影象的細節部分產生差異;
  3. 由於運動補償時的參考塊位置與當前塊位置關係不一致導致重建畫素的內容實際上缺乏相關性。

為了解決該問題,在H.264中定義了名為”Deblocking Filter”的畫素域的影象濾波演算法,以降低塊與巨集塊邊界的畫素不一致性,提升整體視覺主觀體驗。

1.2 去塊濾波器的定義

在H.264的標準文件中,去塊濾波器定義在8.7節中。在H.264的以下profile中,去塊濾波是必要的組成部分:

  • Baseline, Constrained Baseline, Main, Extended, High, High 10, High 4:2:2, High 4:4:4 Predictive;

在以下profile中,去塊濾波器推薦而不強制使用:
- High 10 Intra, High 4:2:2 Intra, High 4:4:4 Intra, CAVLC 4:4:4 iNTRA;

在H.264幀解碼的過程中,去塊濾波器在該幀所有巨集塊的解碼畫素資料重建完成之後進行。在執行中,該過程按照巨集塊地址的順序,對所有的NxN分割模式的巨集塊的畫素塊分割邊界分別進行,其中不包括整個影象的邊界以及被標誌值disable_deblocking_filter_idc所禁用的邊界。

二、 去塊濾波的執行過程

去塊濾波對於每一個巨集塊的亮度和色度分量分別進行。對於巨集塊的每一個分量,首先進行垂直方向的濾波,然後進行水平方向的濾波。垂直方向的濾波從左向右進行,水平方向的濾波從上向下進行。在水平和垂直兩方向上,待濾波的塊邊沿分為兩類:半巨集塊和1/4巨集塊的邊沿。標準文件中的插圖表示如下:

在上圖中,實線表示半巨集塊的邊沿,虛線表示1/4巨集塊的邊沿。對於亮度/色度以及不同的引數設定,去塊濾波操作的邊沿不同。對於亮度分量,根據transform_size_8x8_flag的值判斷:

  • 如果transform_size_8x8_flag為0,即採用4×4尺寸變換,對實線和虛線的邊沿進行濾波;
  • 如果transform_size_8x8_flag為1,即採用8×8尺寸變換,只對實線的塊邊沿進行濾波;

對於色度分量,只考慮4:2:0格式,只對半巨集塊邊沿即實線部分進行濾波。

巨集塊去塊濾波的過程主要分為如下幾個步驟:

  1. 確定當前巨集塊的鄰域有效性;
  2. 獲取幾個關鍵的標識引數:
    • fieldMbInFrameFlag:只考慮幀編碼,該值應為0;
    • filterInternalEdgesFlag:如果當前slice中的disable_deblocking_filter_idc的值設為0,則該flag為1;反之為0;
    • filterLeftMbEdgeFlag:如果當前slice中的disable_deblocking_filter_idc設為1,或disable_deblocking_filter_idc設為2而且該巨集塊不可得,或巨集塊為影象的左側邊沿巨集塊,則該flag設為0;反之設為1;
    • filterTopMbEdgeFlag:如果當前slice中的disable_deblocking_filter_idc設為1,或disable_deblocking_filter_idc設為2而且該巨集塊不可得,或巨集塊為影象的上方邊沿巨集塊,則該flag設為0;反之設為1;
  3. 根據上述關鍵標識引數執行濾波過程。

2.1 左巨集塊邊沿濾波

若標識位filterLeftMbEdgeFlag設為1,根據協議文件中8.7.1節的方法執行巨集塊亮度分量左側邊沿的濾波。其中chromaEdgeFlag為0,verticalEdgeFlag為1,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = (0, k)的k取[0,15]。

2.2 上巨集塊邊沿濾波

若標識位filterTopMbEdgeFlag的值為1,巨集塊亮度分量的上水平邊沿進行濾波過程。由於當前設定下MbaffFrameFlag始終為0,因此根據協議文件中8.7.1節的方法執行濾波。相關引數為chromaEdgeFlag為0,verticalEdgeFlag為1,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = (k,0)的k取[0,15]。

2.3 內部塊邊沿濾波

當標誌位filterInternalEdgesFlag為1時,執行巨集塊內部塊邊沿的濾波。內部塊邊沿的濾波分為水平和垂直兩種。

2.3.1 內部垂直塊邊沿濾波

內部垂直邊沿濾波的步驟為:

  1. 如果transform_size_8x8_flag為0,根據協議文件中8.7.1節的方法執行濾波。相關引數為chromaEdgeFlag為0,verticalEdgeFlag為1,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = (4,k)的k取[0,15]。該步驟即為第一和第二列塊相鄰邊沿的濾波
  2. 根據協議文件中8.7.1節的方法執行濾波,相關引數為chromaEdgeFlag為0,verticalEdgeFlag為1,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = (8,k)的k取[0,15]。該步驟即為第二和第三列塊相鄰邊沿的濾波
  3. 如果transform_size_8x8_flag為0,根據協議文件中8.7.1節的方法執行濾波。相關引數為chromaEdgeFlag為0,verticalEdgeFlag為1,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = (12,k)的k取[0,15]。該步驟即為第三和第四列塊相鄰邊沿的濾波

2.3.2 內部水平塊邊沿濾波

內部水平邊沿濾波的步驟為:

  1. 如果transform_size_8x8_flag為0,根據協議文件中8.7.1節的方法執行濾波。相關引數為chromaEdgeFlag為0,verticalEdgeFlag為0,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = (k,4)的k取[0,15]。該步驟即為第一和第二行塊相鄰邊沿的濾波
  2. 根據協議文件中8.7.1節的方法執行濾波,相關引數為chromaEdgeFlag為0,verticalEdgeFlag為0,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = (k,8)的k取[0,15]。該步驟即為第二和第三行塊相鄰邊沿的濾波
  3. 如果transform_size_8x8_flag為0,根據協議文件中8.7.1節的方法執行濾波。相關引數為chromaEdgeFlag為0,verticalEdgeFlag為1,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = (k,12)的k取[0,15]。該步驟即為第三和第四行塊相鄰邊沿的濾波

2.4 色度分量的濾波

在影象為彩色影象的情況下,色度部分同樣需要進行濾波操作。對於色度分量的濾波,其思路類似於亮度分量。

2.4.1 色度左巨集塊邊沿濾波

如果filterLeftMbEdgeFlag為1時,根據協議文件中8.7.1節的方法執行巨集塊色度分量左側邊沿的濾波。chromaEdgeFlag為1,verticalEdgeFlag為1,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = (0, k)的k取[0,7]。

2.4.2 色度上巨集塊邊沿濾波

如果filterTopMbEdgeFlag為1,根據協議文件中8.7.1節的方法執行巨集塊色度分量上方邊沿的濾波。。chromaEdgeFlag為1,verticalEdgeFlag為0,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = ( k,0)的k取[0,7]。

2.4.3 色度分量內部塊邊沿濾波

色度分量的內部塊濾波也分為水平和垂直兩個方向,且只有在transform_size_8x8_flag為0時執行。

對於水平方向,根據協議文件中8.7.1節的方法執行濾波,相關引數為chromaEdgeFlag為1,verticalEdgeFlag為0,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = (k,4)的k取[0,8]。

對於垂直方向,根據協議文件中8.7.1節的方法執行濾波,相關引數為chromaEdgeFlag為1,verticalEdgeFlag為1,fieldModeInFrameFilteringFlag等於fieldMbInFrameFlag(幀編碼模式中始終0),(xEk, yEk) = (4,k)的k取[0,8]。

三、塊/巨集塊邊沿濾波方法

濾波每一個塊邊沿的方法定義於標準協議文件的8.7.1節。所需要的步驟包括:

  1. 確定相關引數:
    • chromaEdgeFlag:表明當前資料屬於亮度或色度;
    • iCbCr:色度分量索引;
    • verticalEdgeFlag:水平、垂直方向標誌位;
    • fieldModeInFrameFilteringFlag:隔行模式標誌位;
    • nE:一個邊沿包含的畫素數量,亮度為16,色度為8;
  2. 獲取原重建塊的待濾波畫素;
  3. 根據原畫素進行濾波;
  4. 修改重建畫素值。

    3.1 獲取原重建塊的待濾波畫素

    對一個塊邊沿上的某一個畫素位置進行濾波時,需要獲取該畫素位置相鄰的8個原畫素值作為參考。標準文件中的插圖如下:

    上圖中的p0和q0代表塊邊界某個位置左右的相鄰畫素,根據濾波方向的不同,可能是左右或上下相鄰的畫素。以水平塊邊沿濾波為例,採用邊界垂直方向的8個相鄰畫素,如下圖所示:

    Deblocking_edge

    上圖中,紅色畫素P0和Q0表示邊界兩側的相鄰畫素,其餘藍色畫素為濾波所需要的其他相鄰畫素。

    3.2 邊界畫素濾波

    在獲取到邊界的參考畫素後,下一步執行的操作是參考8個畫素值進行濾波。濾波過程的具體方法定義在標準的8.7.2節。實現一個邊界的濾波需要三大步驟:

    • 計算邊界濾波強度;
    • 判斷濾波條件;
    • 濾波邊界畫素;

3.2.1 計算邊界濾波強度

邊界濾波強度是邊界畫素濾波時的一個重要的引數,決定了畫素點的濾波採取怎樣的演算法。實現方法定義在標準的8.7.2.1節。邊界濾波強度可能的取值有0、1、2、3、4共5個值。其中:

  • 0:表示該邊界不需要濾波;
  • 1、2:主要用於幀間編碼影象的濾波;
  • 3、4:主要用於幀內編碼影象的濾波;

本節中主要以幀內編碼為例說明邊界濾波強度的計算方法,即濾波強度取3、4的條件。

  • 當濾波邊界為巨集塊邊沿,並且邊界相鄰畫素p0和q0都屬於幀編碼巨集塊,並且任意一個畫素的所屬巨集塊為幀內編碼或所屬的幀型別為SI或SP時,邊界濾波強度為4;
  • 當濾波邊界為子塊邊界,並且邊界相鄰畫素p0和q0都屬於幀編碼巨集塊,並且任意一個畫素的所屬巨集塊為幀內編碼或所屬的幀型別為SI或SP時,邊界濾波強度為3;
  • 在幀內編碼模式中,同時作為巨集塊邊沿和影象邊沿的畫素,濾波強度為0,不進行濾波。

3.2.2 判斷濾波條件

判斷濾波條件的方法定義在8.7.2.2節,所需資料包括邊界兩側的4個畫素值p0/p1/q0/q1,顏色空間分量標誌位chromaEdgeFlag,邊界濾波強度bS,所屬巨集塊的量化引數QPp/QPq,以及在視訊頭部讀取到的兩個語法元素filterOffsetA/filterOffsetB。

首先計算邊界相鄰畫素所屬巨集塊量化引數QP的平均值:

qPav =(qPp +qPq +1)>>1;

計算查表所需要的兩個索引值:

indexA = Clip3(0, 51, qPav + filterOffsetA);
indexB = Clip3(0, 51, qPav + filterOffsetB);

根據計算得到的indexA和indexB,通過在表8-16中查詢對應的alpha和beta值:

根據上述計算得到的資訊,可以獲得濾波判決條件:

filterSamplesFlag=(bS!=0 && Abs(p0 −q0 )<α && Abs(p1 −p0 )<β && Abs(q1 −q0 )<β);

3.2.3 濾波邊界畫素

針對邊界濾波強度不同的畫素點,採用的方法也相應不同。濾波過程修改的畫素值包括邊界兩邊的相鄰畫素以及分別向兩個方向擴充套件兩個的畫素值,即根據邊界左右對稱的6個相鄰畫素。

對於濾波強度為4的畫素點(即非作為影象邊緣的巨集塊邊界),採用如下方法進行濾波:

如果濾波亮度分量,且滿足ap <β && Abs(p0 −q0 )<((α>>2)+2)時,邊界左側或上方的三個濾波後像素的計算方法為:

  • p′0 =(p2 +2*p1 +2*p0 +2*q0 +q1 +4)>>3
  • p′1 =(p2 +p1 +p0 +q0 +2)>>2
  • p′2 =(2*p3 +3*p2 +p1 +p0 +q0 +4)>>3

右側或下方的三個濾波後像素的計算方法為:

  • q′0 =(p1 +2*p0 +2*q0 +2*q1 +q2 +4)>>3
  • q′1 =(p0 +q0 +q1 +q2 +2)>>2
  • q′2 =(2*q3 +3*q2 +q1 +q0 +p0 +4)>>3

如果濾波色度分量,或者不滿足ap <β && Abs(p0 −q0 )<((α>>2)+2)時,邊界左側或上方的三個濾波後像素的計算方法為:

  • p′0 =(2*p1 +p0 +q1 +2)>>2
  • p′1 = p1
  • p′2 = p2

右側或下方的三個濾波後像素的計算方法為:

  • q′0 =(2*q1 +q0 +p1 +2)>>2
  • q′1 = q1
  • q′2 = q2

對於濾波強度為3的畫素點(即內部塊邊沿),採用的濾波方法略複雜:

首先依據上面獲得的indexA和邊界濾波強度查表獲得C0值,該值由標準文件中的表8-17獲得。

獲取參考畫素的二級差分值:

ap =Abs(p2 −p0 )
aq =Abs(q2 −q0 )

計算閾值變數tc:

亮度:tC =tC0 +((ap <β)?1:0)+((aq <β)?1:0)
色度:tC =tC0 +1

計算邊界相鄰畫素:

Δ =Clip3(−tC,tC,((((q0p0 )<<2)+(p1q1 )+4)>>3)) p′0 = Clip1( p0 + Δ )
q′0 = Clip1( q0 − Δ )

對亮度分量,p1/q1的推導方式為:

p′1 =p1 +Clip3(−tC0,tC0,(p2 +((p0 +q0 +1)>>1)−(p1 <<1)) >> 1)
q′1 =q1 +Clip3(−tC0,tC0,(q2 +((p0 +q0 +1)>>1)−(q1 <<1)) >> 1)

其它,p2/q2以及色度分量的p1/q1/p2/q2畫素維持原值不變。