1、具體代碼位置
角度預測的代碼路徑:TLibCommon\TComPrediction.cpp xPredIntraAng()
2、代碼流程
const Bool bIsModeVer = (dirMode >= 18);
const Int intraPredAngleMode = (bIsModeVer) ? (Int)dirMode - VER_IDX : -((Int)dirMode - HOR_IDX);
const Int absAngMode = abs(intraPredAngleMode);
const Int signAng = intraPredAngleMode < 0 ? -1 : 1;
const Bool edgeFilter = bEnableEdgeFilters && isLuma(channelType) && (width <= MAXIMUM_INTRA_FILTERED_WIDTH) && (height <= MAXIMUM_INTRA_FILTERED_HEIGHT);
// Set bitshifts and scale the angle parameter to block size
static const Int angTable[9] = { 0, 2, 5, 9, 13, 17, 21, 26, 32 };
static const Int invAngTable[9] = { 0, 4096, 1638, 910, 630, 482, 390, 315, 256 }; // (256 * 32) / Angle
Int invAngle = invAngTable[absAngMode];
Int absAng = angTable[absAngMode];
Int intraPredAngle = signAng * absAng;
結合之前的角度圖 和 角度表。 上面一段代碼的功能就是 根據模式來 取 intraPredAngle 角度。
首先以 18 爲分界 分水平 和垂直, 大於等於18 垂直, 小於水平。
水平和 垂直 分類的角度中 又可以以10 爲分界 分爲 水平向上 向下, 垂直又可以以26爲分界分爲垂直向左 向右
水平的2-17 從10 往兩邊角度增加相同的即 2 5 9 13 17 21 26 32。 垂直的18-34 從26 往兩邊角度增加相同的即 2 5 9 13 17 21 26 32。
所以只要定義這個 就可以把所有模式的角度給計算出來。
intraPredAngleMode = (bIsModeVer) ? (Int)dirMode - VER_IDX : -((Int)dirMode - HOR_IDX);
這個就是計算距離 10 和 26 的差值,absAngMode 這個是距離的絕對值, signAng這個就是符合 是向上 向下 向左 還是向右。
const Int refMainOffsetPreScale = (bIsModeVer ? height : width) - 1;
const Int refMainOffset = height - 1;
for (Int x = 0; x < width + 1; x++)
{
refAbove[x + refMainOffset] = pSrc[x - srcStride - 1];
}
for (Int y = 0; y < height + 1; y++)
{
refLeft[y + refMainOffset] = pSrc[(y - 1)*srcStride - 1];
}
refMain = (bIsModeVer ? refAbove : refLeft) + refMainOffset;
refSide = (bIsModeVer ? refLeft : refAbove) + refMainOffset;
// Extend the Main reference to the left.
Int invAngleSum = 128; // rounding for (shift by 8)
for (Int k = -1; k > (refMainOffsetPreScale + 1)*intraPredAngle >> 5; k--)
{
invAngleSum += invAngle;
refMain[k] = refSide[invAngleSum >> 8];
}
inAngle 的作用, 相當於 是一個算法定點化的一個過程。
這邊實際就是之前說過的映射的過程,refMain 主參考邊的數組,對於垂直類的模式,主參考邊是refAbove ,水平類的就是refLeft。
主參考邊的數組確定之後,剩下的一個就是副參考 refSide。
最後的循環就是要將refSide 映射到 refMain。 也就是下面這個公式,就是將refSide一一映射到refMain。
Pel *pDsty = pDst;
for (Int y = 0, deltaPos = intraPredAngle; y < height; y++, deltaPos += intraPredAngle, pDsty += dstStride)
{
const Int deltaInt = deltaPos >> 5;
const Int deltaFract = deltaPos & (32 - 1);
if (deltaFract)
{
// Do linear filtering
const Pel *pRM = refMain + deltaInt + 1;
Int lastRefMainPel = *pRM++;
for (Int x = 0; x < width; pRM++, x++)
{
Int thisRefMainPel = *pRM;
pDsty[x + 0] = (Pel)(((32 - deltaFract)*lastRefMainPel + deltaFract * thisRefMainPel + 16) >> 5);
lastRefMainPel = thisRefMainPel;
}
}
else
{
// Just copy the integer samples
for (Int x = 0; x < width; x++)
{
pDsty[x] = refMain[x + deltaInt + 1];
}
}
}
上面這個就是參考像素獲取的代碼。 最外層的循環 是 y = 0 到 height -1。deltaPos每次都是增加intraPredAngle,pDsty 也是增加dstStride。
算出deltaInt 這個用來確定位置,另外一個則是權重。
refMain 指針移動deltaInt + 1 個字節 pRM指向這個位置。 然後先把這個位置的值取出來賦值給lastRefMainPel,pRM 向前移動一位。
然後在內層循環裏面。塊中行掃描。 thisRefMainPel 取 pRM 當前指向的值。 預測值 就取lastRefMainPel和thisRefMainPel 的權重。
然後將thisRefMainPel 賦值給lastRefMainPel, pRM 向前移動一位 thisRefMainPel 取pRM。 這樣循環。
index的變化 簡化就是外面循環加intra_pred_angle,算delta_int,裏面的循環delta_int的基礎上面取累積。
for (int j = 0; j < iHeight; j++) {
delta_pos = delta_pos + intra_pred_angle;
delta_int = delta_pos >> 5;
for (int i = 0; i < iWidth; i++) {
int ref_main_index = i + delta_int;
int ref_side_index = ref_main_index + 1;
saveDistanceMatri(distanMatri, i, j, ref_main_index, ref_side_index);
}
}
if (!bIsModeVer)
{
for (Int y = 0; y < height; y++)
{
for (Int x = 0; x < width; x++)
{
pTrueDst[x*dstStrideTrue] = pDst[x];
}
pTrueDst++;
pDst += dstStride;
}
}
x方向和y方向之間的關係
垂直方向是按行掃描過去 然後得到的結果一個個存放在 pDsty中。
一行都是相差1的。
水平方向的 是需要按列是相差1的。 但是現在 按照行做完,pDsty 中卻是行相差1, 所有應該行列轉置一下