題目中變換系數熵編碼指的是對量化後的變換系數進行熵編碼,這部分操作位於視頻編碼系統中量化模塊後,熵編碼模塊之前。這裏的變換系數是量化後的變換系數,爲了敘述方便本文後面仍稱其爲變換系數。
量化後的變換系數進行熵編碼包括兩部分:一是對量化後變換系數掃描;而是對非零的變換系數位置和值進行熵編碼。
變換系數掃描
變換系數掃描就是將二維變換系數變成一維變換系數。掃描時儘量使幅值相近的數排列在一起。
H.265/HEVC對變換系數的掃描是基於4x4塊的,所以對於大於4x4的TB要先將其分爲若干個4x4的子塊,子塊內部和子塊間按同樣方式進行掃描。
H.265/HEVC使用反向掃描方式,掃描順序總是從右下角開始到左上角的DC係數。這一順序和H.264/AVC掃描順序正好相反。並且H.265/HEVC通過了三種掃描方式:對角掃描、水平掃描和垂直掃描。
以8x8TB的掃描爲例,首先將其劃分爲4個4x4的子塊,然後進行掃描。下面分別是8x8TB按對角、水平和垂直方式掃描的示意圖。
每個4x4子塊掃描後得到的16個連續的係數稱爲係數組(Coefficient Group,CG)。
採用不同預測模式的TB其係數分佈往往具有一定規律,如水平預測塊變換後能量往往集中在前幾列,使用垂直掃描方式效率更高。H.265/HEVC採用模式依賴的係數掃描方式(Mode-Dependent Cofficient Scan,MDCS),規定了預測模式和掃描方式的對應關係因此掃描方式不需要語法元素顯示錶示。
幀內4x4或8x8亮度TB:如果模式號爲6~14採用垂直掃描方式;如果模式號爲22~30採用水平掃描方式;其他方向都採用對角掃描方式。
其他TB:如果是幀內亮度16x16或32x32TB,或是幀間亮度TB,無論什麼尺寸,是否爲對稱劃分,一律使用對角掃描方式。
對於色度TB其掃描方式和對應的亮度TB一致。
HM中相關定義如下:
//掃描類型定義
/// coefficient scanning type used in ACS
enum COEFF_SCAN_TYPE
{
SCAN_DIAG = 0, ///< up-right diagonal scan
SCAN_HOR = 1, ///< horizontal first scan
SCAN_VER = 2, ///< vertical first scan
SCAN_NUMBER_OF_TYPES = 3
};
掃描模式選擇如下:
UInt TComDataCU::getCoefScanIdx(const UInt uiAbsPartIdx, const UInt uiWidth, const UInt uiHeight, const ComponentID compID) const
{
//------------------------------------------------
//this mechanism is available for intra only
//<!幀間預測塊都採用對角掃描
if (!isIntra(uiAbsPartIdx))
{
return SCAN_DIAG;
}
//------------------------------------------------
//check that MDCS can be used for this TU
const ChromaFormat format = getPic()->getChromaFormat();
const UInt maximumWidth = MDCS_MAXIMUM_WIDTH >> getComponentScaleX(compID, format);
const UInt maximumHeight = MDCS_MAXIMUM_HEIGHT >> getComponentScaleY(compID, format);
//<!大於8x8的亮度塊或大於4x4的色度塊採用對角掃描
if ((uiWidth > maximumWidth) || (uiHeight > maximumHeight))
{
return SCAN_DIAG;
}
//------------------------------------------------
//otherwise, select the appropriate mode
UInt uiDirMode = getIntraDir(toChannelType(compID), uiAbsPartIdx);
if (uiDirMode==DM_CHROMA_IDX)
{
const TComSPS *sps=getSlice()->getSPS();
const UInt partsPerMinCU = 1<<(2*(sps->getMaxTotalCUDepth() - sps->getLog2DiffMaxMinCodingBlockSize()));
uiDirMode = getIntraDir(CHANNEL_TYPE_LUMA, getChromasCorrespondingPULumaIdx(uiAbsPartIdx, getPic()->getChromaFormat(), partsPerMinCU));
}
if (isChroma(compID) && (format == CHROMA_422))
{
uiDirMode = g_chroma422IntraAngleMappingTable[uiDirMode];
}
//------------------
//<!模式號爲22~30採用水平掃描方式
if (abs((Int)uiDirMode - VER_IDX) <= MDCS_ANGLE_LIMIT)
{
return SCAN_HOR;
}//<!模式號爲6~14採用垂直掃描方式
else if (abs((Int)uiDirMode - HOR_IDX) <= MDCS_ANGLE_LIMIT)
{
return SCAN_VER;
}
else
{
return SCAN_DIAG;
}
}
變換系數熵編碼
一個TB經過上面掃描後得到一個一維的變換系數數組,由於變換系數經過量化後大部分值都爲0如果直接按順序對每個係數進行熵編碼效率不高。H.265/HEVC中採用的是對每個非零係數的位置和幅值進行CABAC編碼。
符號位隱藏
符號位隱藏(Sign Data Hiding,SDH)技術是用來隱藏每個CG最後一個非零比特符號位的技術。是否使用由PPS中相應標誌位決定。
SDH技術:首先計算CG內所有非零係數絕對值之和,若和爲偶數則CG內最後一個非零係數被判定爲“+”;若和爲奇數則CG內最後一個非零係數被判定爲“-”;使用SDH後解碼端可以直接判斷CG中最後一個非零係數的符號,編碼端可以省略它的語法元素coeff_sign_flag的熵編碼。
當SDH得到的符號和CG內最後一個非零係數符號不一致時就需要調整CG中某個非零係數,將它的值加1或減1,以使其和真實符號一致。所以使用SDH技術可能會帶來變換系數的失真,但符號位在熵編碼時使用的是旁路編碼模式開銷較大,使用SDH技術節約的碼率大於一個變換系數失真帶來的影響。
注:視頻編碼中除了量化會引入失真,SDH也會。
感興趣的請關注微信公衆號Video Coding