转:JM8.6的解码端去方块滤波代码详述
作者:张戟(E-mail:bearriezj@gmail.com)
这部分在标准的8.7 Deblocking filter process有详细的描述,技术白皮书中也有专门讲去方块滤波的部分,因此在资料方面应该是不成问题。去方块滤波的代码主要集中在JM8.6 Decoder的loopFilter.c文件中,入口函数为DeblockPicture(),此函数在image.c的exit_picture()中被调用,也就是说这个去方块滤波模块是在解码完一幅图像后被调用的。这里,我不得不说,H.264中的去方块滤波做的太好了,朋友们可以自己尝试一下,在exit_picture()中注释掉DeblockPicture(),你会发现解码的PSNR会下降那么多。另外,我这边还要提醒做误码掩盖的朋友们,不知道你们是否注意过DeblockPicture()是在误码掩盖前被调用的,也就是说此时去方块滤波对丢失块的边界并没有使用滤波,所以在误码掩盖后再进行一次去方块滤波你可能会有不一样的收获,当然,我相信你不会傻乎乎的直接在误码掩盖后直接再次调用DeblockPicture()就完事了。
虽然这部分的代码不是很长,但我并不认为就很好理解,我下面只是把我的注释代码公布了下(本来想写详细过程的,但写了一半觉得不好,全部删除了),如果有误,请大家指正。
#include <stdlib.h>
#include <string.h>
#include "global.h"
#include "image.h"
#include "mb_access.h"
#include "loopfilter.h"
extern const byte QP_SCALE_CR[52] ;
extern StorablePicture *dec_picture;
byte mixedModeEdgeFlag, fieldModeFilteringFlag;
/*********************************************************************************************************/
#define IClip( Min, Max, Val) (((Val)<(Min))? (Min):(((Val)>(Max))? (Max):(Val)))
// NOTE: to change the tables below for instance when the QP doubling is changed from 6 to 8 values
// send an e-mail to Peter.List@t-systems.com to get a little programm that calculates them automatically
///////////////////////////////
//标准204页的表8-16或毕书141页的表6.23
byte ALPHA_TABLE[52] = {0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,4,4,5,6, 7,8,9,10,12,13,15,17, 20,22,25,28,32,36,40,45, 50,56,63,71,80,90,101,113, 127,144,162,182,203,226,255,255} ;
byte BETA_TABLE[52] = {0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,2,2,2,3, 3,3,3, 4, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 11,11,12,12,13,13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18} ;
///////////////////////////////
//这边对应标准206页的表8-17或毕书144页的表6.24
//但是稍微有点不同,二维数组中的52指量化参数,5指Bs强度从0-4
//从表中知道,Bs=3和4时,他们的CLIP_TAB是相同的
byte CLIP_TAB[52][5] =
{
{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0},{ 0, 0, 0, 1, 1},{ 0, 0, 0, 1, 1},{ 0, 0, 0, 1, 1},{ 0, 0, 0, 1, 1},{ 0, 0, 1, 1, 1},{ 0, 0, 1, 1, 1},{ 0, 1, 1, 1, 1},
{ 0, 1, 1, 1, 1},{ 0, 1, 1, 1, 1},{ 0, 1, 1, 1, 1},{ 0, 1, 1, 2, 2},{ 0, 1, 1, 2, 2},{ 0, 1, 1, 2, 2},{ 0, 1, 1, 2, 2},{ 0, 1, 2, 3, 3},
{ 0, 1, 2, 3, 3},{ 0, 2, 2, 3, 3},{ 0, 2, 2, 4, 4},{ 0, 2, 3, 4, 4},{ 0, 2, 3, 4, 4},{ 0, 3, 3, 5, 5},{ 0, 3, 4, 6, 6},{ 0, 3, 4, 6, 6},
{ 0, 4, 5, 7, 7},{ 0, 4, 5, 8, 8},{ 0, 4, 6, 9, 9},{ 0, 5, 7,10,10},{ 0, 6, 8,11,11},{ 0, 6, 8,13,13},{ 0, 7,10,14,14},{ 0, 8,11,16,16},
{ 0, 9,12,18,18},{ 0,10,13,20,20},{ 0,11,15,23,23},{ 0,13,17,25,25}
} ;
void GetStrength(byte Strength[16],struct img_par *img,int MbQAddr,int dir,int edge, int mvlimit,StorablePicture *p);
void EdgeLoop(byte** Img, byte Strength[16],struct img_par *img, int MbQAddr, int AlphaC0Offset, int BetaOffset, int dir, int edge, int width, int yuv);
void DeblockMb(ImageParameters *img, StorablePicture *p, int MbQAddr, int flag);
void CheckNeighbors(int mb_nr);
void searchErrorMB( int *errorMB, int downMB, int rightMB );
/*!
*****************************************************************************************
* /brief
* Filter all macroblocks in order of increasing macroblock address.
*****************************************************************************************
*/
//deblock会减轻block效应,但同时会导致画面模糊,不过这样看起来会舒服很多,
//在某些方面来说确实是"提高"质量吧。
void DeblockPicture(ImageParameters *img, StorablePicture *p, int flag, int *errorMB)
{
unsigned int i;
unsigned int currLen = 0;
int downMB, rightMB;
if( flag )
{
for (i=0; i<p->PicSizeInMbs; i++)//循环遍历图像内的所有宏块
{
DeblockMb( img, p, i, flag );
}
}
else
{
while( errorMB[currLen] != errorMBInit )
currLen++;
i = 0;
while( errorMB[i] != errorMBInit && i < currLen )
{
downMB = errorMB[i] + p->PicWidthInMbs;
rightMB = errorMB[i] + 1;
searchErrorMB( errorMB, downMB, rightMB );
i++;
}
i = 0;
while( errorMB[i] != errorMBInit )
{
DeblockMb( img, p, errorMB[i], flag );
i++;
}
}
}
/*!
*****************************************************************************************
* /brief
* Deblocking filter for one macroblock.
*****************************************************************************************
*/
void DeblockMb(ImageParameters *img, StorablePicture *p, int MbQAddr, int flag)
{
int EdgeCondition;
int dir,edge;
byte Strength[16];
int mb_x, mb_y;
int filterLeftMbEdgeFlag;
int filterTopMbEdgeFlag;
int fieldModeMbFlag;
int mvlimit=4;
int i, StrengthSum;
Macroblock *MbQ;
byte **imgY = p->imgY;
byte ***imgUV = p->imgUV;
img->DeblockCall = 1;//滤波的标志位
get_mb_pos (MbQAddr, &mb_x, &mb_y);//MbQAddr是0~nOfMB的序号
//mb_x和mb_y表示当前宏块的左上顶点的整像素的横纵坐标
filterLeftMbEdgeFlag = (mb_x != 0);
filterTopMbEdgeFlag = (mb_y != 0);
//为什么要判断filterLeftMbEdgeFlag和filterTopMbEdgeFlag
//因为对于一幅图像的第一行和第一列是没有上宏块和左宏块的
MbQ = &(img->mb_data[MbQAddr]) ; // current Mb
if (p->MbaffFrameFlag && mb_y==16 && MbQ->mb_field)
filterTopMbEdgeFlag = 0;
/*
标准中对于变量fieldModeMbFlag的说明
- If any of the following conditions is true, fieldModeMbFlag is set equal to 1.
- field_pic_flag is equal to 1
- MbaffFrameFlag is equal to 1 and the macroblock CurrMbAddr is a field macroblock
- Otherwise, fieldModeMbFlag is set equal to 0.
*/
fieldModeMbFlag = (p->structure!=FRAME) || (p->MbaffFrameFlag && MbQ->mb_field);
/*
NOTE - A vertical difference of 4 in units of quarter luma frame samples
is a difference of 2 in units of quarter luma field samples
对于场,标准规定mvlimit = 2
*/
if (fieldModeMbFlag)
mvlimit = 2;
//else
//mvlimit = 4;
// return, if filter is disabled
//MbQ->LFDisableIdc指定了在块的边界是否要用滤波,同时指明哪个块的边界不用块的滤波
if (MbQ->LFDisableIdc==1) {
img->DeblockCall = 0;
return;
}
/*
当LFDisableIdc没有出现在SliceHeader中,LFDisableIdc的值推断为0
LFDisableIdc的值范围在0-2之间
However, for purposes of determining which edges are to be filtered
when disable_deblocking_filter_idc is equal to 2,
macroblocks in different slices are considered not available
during specified steps of the operation of the deblocking filter process.
*/
if (MbQ->LFDisableIdc==2)
{
// don't filter at slice boundaries
//标准上说的很明白,当LFDisableIdc等于2时,来自不同Slice的宏块视为不可用
//在程序中判断一个宏块的ABCD是否可用,就是判断是否在同一个Slice中
filterLeftMbEdgeFlag = MbQ->mbAvailA;
filterTopMbEdgeFlag = MbQ->mbAvailB;
}
img->current_mb_nr = MbQAddr;
CheckAvailabilityOfNeighbors();
/*
if( flag )
CheckAvailabilityOfNeighbors();//检测周围A,B,C,D四个宏块的可用度
else
CheckNeighbors(MbQAddr);
*/
for( dir=0 ; dir<2 ; dir++ ) // vertical edges, than horicontal edges
{
EdgeCondition = (dir && filterTopMbEdgeFlag) || (!dir && filterLeftMbEdgeFlag); // can not filter beyond picture boundaries
for( edge=0 ; edge<4 ; edge++ ) // first 4 vertical strips of 16 pel
{ // then 4 horicontal
//现在对于这个判断条件应该很清楚了
//对于edge=1,2,3当然不用关心EdgeCondition
//但是当edge=0时,就要考虑左宏块或上宏块是否可用
if( edge || EdgeCondition )
{
GetStrength(Strength,img,MbQAddr,dir,edge, mvlimit, p); // Strength for 4 blks in 1 stripe
StrengthSum = Strength[0];
for (i = 1; i < 16; i++) StrengthSum += Strength[i];
if( StrengthSum ) // only if one of the 16 Strength bytes is != 0
{
EdgeLoop( imgY, Strength, img, MbQAddr, MbQ->LFAlphaC0Offset, MbQ->LFBetaOffset, dir, edge, p->size_x, 0) ;
//!(edge & 1)表明只有当edge=0,2时成立
//调用EdgeLoop的时候有edge/2操作,转换到0和1
if( (imgUV != NULL) && !(edge & 1) )
{
EdgeLoop( imgUV[0], Strength, img, MbQAddr, MbQ->LFAlphaC0Offset, MbQ->LFBetaOffset, dir, edge/2, p->size_x_cr, 1 ) ;
EdgeLoop( imgUV[1], Strength, img, MbQAddr, MbQ->LFAlphaC0Offset, MbQ->LFBetaOffset, dir, edge/2, p->size_x_cr, 1 ) ;
}
}
//this is the extra horizontal edge between a frame macroblock pair and a field above it
if (dir && !edge && !MbQ->mb_field && mixedModeEdgeFlag)
{
img->DeblockCall = 2;
GetStrength(Strength,img,MbQAddr,dir,4, mvlimit, p); // Strength for 4 blks in 1 stripe
if( *((int*)Strength) ) // only if one of the 4 Strength bytes is != 0
{
EdgeLoop( imgY, Strength, img, MbQAddr, MbQ->LFAlphaC0Offset, MbQ->LFBetaOffset, dir, 4, p->size_x, 0) ;
if( (imgUV != NULL) && !(edge & 1) )
{
EdgeLoop( imgUV[0], Strength, img, MbQAddr, MbQ->LFAlphaC0Offset, MbQ->LFBetaOffset, dir, 4, p->size_x_cr, 1 ) ;
EdgeLoop( imgUV[1], Strength, img, MbQAddr, MbQ->LFAlphaC0Offset, MbQ->LFBetaOffset, dir, 4, p->size_x_cr, 1 ) ;
}
}
img->DeblockCall = 1;
}
}//if( edge || EdgeCondition )
}//for( edge=0 ; edge<4 ; edge++ )
}//for( dir=0 ; dir<2 ; dir++ )
img->DeblockCall = 0;
}
/*!
*********************************************************************************************
* /brief
* returns a buffer of 16 Strength values for one stripe in a mb (for different Frame types)
*********************************************************************************************
*/
///////////////////////////////////////////////////////////////////////////////////////
//这一块在程序并没有真正用到
int ININT_STRENGTH[4] = {0x04040404, 0x03030303, 0x03030303, 0x03030303} ;
byte BLK_NUM[2][4][4] = {{{0,4,8,12},{1,5,9,13},{2,6,10,14},{3,7,11,15}},{{0,1,2,3},{4,5,6,7},{8,9,10,11},{12,13,14,15}}} ;
byte BLK_4_TO_8[16] = {0,0,1,1,0,0,1,1,2,2,3,3,2,2,3,3} ;
#define ANY_INTRA (MbP->mb_type==I4MB||MbP->mb_type==I16MB||MbP->mb_type==IPCM||MbQ->mb_type==I4MB||MbQ->mb_type==I16MB||MbQ->mb_type==IPCM)
///////////////////////////////////////////////////////////////////////////////////////
//输出Bs,处理以4x4块为基本单位
//GetStrength(Strength,img,MbQAddr,dir,edge, mvlimit, p);
//dir=0表示现在处理的是垂直方向的边界,dir=1表示现在处理的是水平方向的边界
//对照标准8.7.2.1,为什么选用P和Q来命名宏块,查看标准200页的图8-11
//Q表示当前块,P表示左方或上方的邻近块
//这边的edge就是指毕书142页图6.65中的a,b,c等边界,不过这边对应的是0,1,2
void GetStrength(byte Strength[16],struct img_par *img,int MbQAddr,int dir,int edge, int mvlimit, StorablePicture *p)
{
int blkP, blkQ, idx;
int blk_x, blk_x2, blk_y, blk_y2 ;
int ***list0_mv = p->mv[LIST_0];
int ***list1_mv = p->mv[LIST_1];
int **list0_refIdxArr = p->ref_idx[LIST_0];
int **list1_refIdxArr = p->ref_idx[LIST_1];
int64 **list0_refPicIdArr = p->ref_pic_id[LIST_0];
int64 **list1_refPicIdArr = p->ref_pic_id[LIST_1];
int xQ, xP, yQ, yP;
int mb_x, mb_y;
int condition1, condition2, condition3;
Macroblock *MbQ;
Macroblock *MbP;
PixelPos pixP;
MbQ = &(img->mb_data[MbQAddr]);//CurrentMB
//去方块滤波的基本单位是4x4块,这样的理解是错误的
//这边的idx=0~15指一个宏块的正像素范围,这样xQ和yQ就可以很好解释
for( idx=0 ; idx<16 ; idx++ )
{
//dir=0表示现在处理的是垂直方向的边界,dir=1表示现在处理的是水平方向的边界
/*
当dir=0时
xQ=edge<<2,因为edge是0-4范围,即这边扩展到整像素范围
yQ=idx=0-15,就是一个宏块的垂直整像素范围
当dir=1时
xQ=dix=0-15,就是一个宏块的水平整像素范围
yQ=edge < 4 ? edge << 2 : 1,当edge>=4时,yQ=1,注意,这边是整像素单位的
*/
xQ = dir ? idx : edge << 2;
yQ = dir ? (edge < 4 ? edge << 2 : 1) : idx;
getNeighbour(MbQAddr, xQ - (1 - dir), yQ - dir, 1, &pixP);//getNeighbour(int curr_mb_nr, int xN, int yN, int luma, PixelPos *pix)
xP = pixP.x;//pixP点在当前宏块中整像素横坐标
yP = pixP.y;//pixP点在当前宏块中整像素纵坐标
MbP = &(img->mb_data[pixP.mb_addr]);//对于dir=0,取左边邻近块,对于dir=1,取上边邻近块
//in different macroblock pairs 完全不合,如果没有宏块对,mb_field肯定都是0
/*
当然如果按照标准的定义应该这样
if(img->MbaffFrameFlag && MbQ->mb_field != MbP->mb_field)
mixedModeEdgeFlag = 1
else
mixedModeEdgeFlag = 0
*/
//当相邻两个块来自同一个场,那么mixedModeEdgeFlag=0
//当相邻两个块来自不同的场,那么mixedModeEdgeFlag=1
mixedModeEdgeFlag = MbQ->mb_field != MbP->mb_field;//标准上我已经标注
blkQ = ((yQ>>2)<<2) + (xQ>>2);//((yQ>>2)<<2)可能取值0,4,8,12 (xQ>>2)可能取值0,1,2,3
//即blkQ表示当前4x4块在整个宏块中的序号
blkP = ((yP>>2)<<2) + (xP>>2);
//毕书上描述有误
//当边界两边一个或两个块为帧内预测并且边界为宏块边界,Bs=4
//当边界两边一个或两个块为帧内预测,Bs=3
if ((p->slice_type==SP_SLICE)||(p->slice_type==SI_SLICE) )
{
condition1 = (!p->MbaffFrameFlag && (p->structure==FRAME));
condition2 = (p->MbaffFrameFlag && !MbP->mb_field && !MbQ->mb_field);
condition3 = ((p->MbaffFrameFlag || (p->structure != FRAME)) && !dir);//即dir=0垂直
//这边的dir=0倒是标准上指定 verticalEdgeFlag is equal to 1
Strength[idx] = (edge == 0 && (( condition1 || condition2 ) || condition3 )) ? 4 : 3;
//edge=0时即表明是宏块边界
}
else
{
// Start with Strength=3. or Strength=4 for Mb-edge
//edge==0对应the block edge is also a macroblock edge
Strength[idx] = (edge == 0 && (((!p->MbaffFrameFlag && (p->structure==FRAME)) ||
(p->MbaffFrameFlag && !MbP->mb_field && !MbQ->mb_field)) ||
((p->MbaffFrameFlag || (p->structure!=FRAME)) && !dir))) ? 4 : 3;
//说明P和Q都不是帧内编码
//也就是说,只有当相邻块都不是帧内预测的时候,程序才有可能执行下面,即bs才会=0,1,2
if( !(MbP->mb_type==I4MB || MbP->mb_type==I16MB || MbP->mb_type==IPCM)
&& !(MbQ->mb_type==I4MB || MbQ->mb_type==I16MB || MbQ->mb_type==IPCM) )
{
//这个判断条件标准上有
/*
- the luma block containing sample p0 or the luma block containing sample q0
contains non-zero transform coefficient levels
*/
//亮度或色度包含非零变换系数等级
//边界两边一个图像块为残差编码
//cbp的每个bit用来表示8x8块中是否含有变换系数
//cbp_blk的每个比特用来表示4x4块中是否含有变换系数
if( ((MbQ->cbp_blk & (1 << blkQ )) != 0) || ((MbP->cbp_blk & (1 << blkP)) != 0) )
Strength[idx] = 2;//blkQ代表当前处理的4x4块的序号
else
//bs=1的大概条件
//边界两边图像块运动矢量之差不小于1个亮度图像点的距离
//边界两边图像块运动补偿的参考帧不同
{ // if no coefs, but vector difference >= 1 set Strength=1
// if this is a mixed mode edge then one set of reference pictures will be frame and the
// other will be field
if (mixedModeEdgeFlag)
{
(Strength[idx] = 1);
}
else//mixedModeEdgeFlag=0,严格按照标准
{
//注意:这边的mb_x和mb_y得到的是当前宏块的以宏块为单位的坐标
get_mb_block_pos (MbQAddr, &mb_x, &mb_y);
blk_y = (mb_y<<2) + (blkQ >> 2);//当前4x4块在整个图像中的坐标
blk_x = (mb_x<<2) + (blkQ & 3);
blk_y2 = pixP.pos_y >> 2;//相邻块在整个图像中的坐标
blk_x2 = pixP.pos_x >> 2;
// if( (img->type == B_SLICE) )
{
int64 ref_p0,ref_p1,ref_q0,ref_q1;
ref_p0 = list0_refIdxArr[blk_x][blk_y]<0 ? -1 : list0_refPicIdArr[blk_x][blk_y];
ref_q0 = list0_refIdxArr[blk_x2][blk_y2]<0 ? -1 : list0_refPicIdArr[blk_x2][blk_y2];
ref_p1 = list1_refIdxArr[blk_x][blk_y]<0 ? -1 : list1_refPicIdArr[blk_x][blk_y];
ref_q1 = list1_refIdxArr[blk_x2][blk_y2]<0 ? -1 : list1_refPicIdArr[blk_x2][blk_y2];
if ( ((ref_p0==ref_q0) && (ref_p1==ref_q1)) ||
((ref_p0==ref_q1) && (ref_p1==ref_q0)))//为什么这么判断,注意标准202页的Note部分
{
Strength[idx]=0;
// L0 and L1 reference pictures of p0 are different; q0 as well
if (ref_p0 != ref_p1)
{
// compare MV for the same reference picture
/*
NOTE - A vertical difference of 4 in units of quarter luma frame samples
is a difference of 2 in units of quarter luma field samples
这样也就明白了为什么mvlimit=2或4的原因
*/
if (ref_p0==ref_q0) //这边为什么是4,因为相当于1个整象素点
{
Strength[idx] = (abs( list0_mv[blk_x][blk_y][0] - list0_mv[blk_x2][blk_y2][0]) >= 4) |
(abs( list0_mv[blk_x][blk_y][1] - list0_mv[blk_x2][blk_y2][1]) >= mvlimit) |
(abs( list1_mv[blk_x][blk_y][0] - list1_mv[blk_x2][blk_y2][0]) >= 4) |
(abs( list1_mv[blk_x][blk_y][1] - list1_mv[blk_x2][blk_y2][1]) >= mvlimit);
}
else
{
Strength[idx] = (abs( list0_mv[blk_x][blk_y][0] - list1_mv[blk_x2][blk_y2][0]) >= 4) |
(abs( list0_mv[blk_x][blk_y][1] - list1_mv[blk_x2][blk_y2][1]) >= mvlimit) |
(abs( list1_mv[blk_x][blk_y][0] - list0_mv[blk_x2][blk_y2][0]) >= 4) |
(abs( list1_mv[blk_x][blk_y][1] - list0_mv[blk_x2][blk_y2][1]) >= mvlimit);
}
}
else //if (ref_p0 == ref_p1)
{ // L0 and L1 reference pictures of p0 are the same; q0 as well
Strength[idx] = ((abs( list0_mv[blk_x][blk_y][0] - list0_mv[blk_x2][blk_y2][0]) >= 4) |
(abs( list0_mv[blk_x][blk_y][1] - list0_mv[blk_x2][blk_y2][1]) >= mvlimit ) |
(abs( list1_mv[blk_x][blk_y][0] - list1_mv[blk_x2][blk_y2][0]) >= 4) |
(abs( list1_mv[blk_x][blk_y][1] - list1_mv[blk_x2][blk_y2][1]) >= mvlimit))
&&
((abs( list0_mv[blk_x][blk_y][0] - list1_mv[blk_x2][blk_y2][0]) >= 4) |
(abs( list0_mv[blk_x][blk_y][1] - list1_mv[blk_x2][blk_y2][1]) >= mvlimit) |
(abs( list1_mv[blk_x][blk_y][0] - list0_mv[blk_x2][blk_y2][0]) >= 4) |
(abs( list1_mv[blk_x][blk_y][1] - list0_mv[blk_x2][blk_y2][1]) >= mvlimit));
}
}
else
//if(((ref_p0==ref_q0)&&(ref_p1==ref_q1)) || ((ref_p0==ref_q1)&&(ref_p1==ref_q0)))
{
Strength[idx] = 1;
}
}//Empty
/* else
{ // P slice
int64 ref_p0,ref_q0;
ref_p0 = list0_refIdxArr[blk_x][blk_y]<0 ? -1 : list0_refPicIdArr[blk_x][blk_y];
ref_q0 = list0_refIdxArr[blk_x2][blk_y2]<0 ? -1 : list0_refPicIdArr[blk_x2][blk_y2];
Strength[idx] = (ref_p0 != ref_q0 ) |
(abs( list0_mv[blk_x][blk_y][0] - list0_mv[blk_x2][blk_y2][0]) >= 4 ) |
(abs( list0_mv[blk_x][blk_y][1] - list0_mv[blk_x2][blk_y2][1]) >= mvlimit );
} */
}//if (mixedModeEdgeFlag)
}//这边是判断是否含非零系数
}//判断P和Q是否是帧内宏块
}//if ((p->slice_type==SP_SLICE)||(p->slice_type==SI_SLICE) )
}//for( idx=0 ; idx<16 ; idx++ )
}
/*
chroma_qp_index_offset指色度分量的量化参数是根据亮度分量的量化参数计算出来的
此句法元素用以指明计算时用到的参数
色度量化参数=亮度量化参数+chroma_qp_index_offset
*/
#define CQPOF(qp) (Clip3(0, 51, qp + active_pps->chroma_qp_index_offset))
/*!
*****************************************************************************************
* /brief
* Filters one edge of 16 (luma) or 8 (chroma) pel
*****************************************************************************************
*/
//核心滤波过程
//EdgeLoop( imgUV[0], Strength, img, MbQAddr, MbQ->LFAlphaC0Offset, MbQ->LFBetaOffset, dir, edge/2, p->size_x_cr, 1 ) ;
//最后的yuv用来判断当前滤波是亮度还是色度
void EdgeLoop(byte** Img, byte Strength[16],struct img_par *img, int MbQAddr, int AlphaC0Offset, int BetaOffset,
int dir, int edge, int width, int yuv)
{
int pel, ap = 0, aq = 0, Strng ;
int incP, incQ;
int C0, c0, Delta, dif, AbsDelta ;
int L2 = 0, L1, L0, R0, R1, R2 = 0, RL0, L3, R3 ;
int Alpha = 0, Beta = 0 ;
byte* ClipTab = NULL;
int small_gap;
int indexA, indexB;
int PelNum;
int StrengthIdx;
byte *SrcPtrP, *SrcPtrQ;
int QP;
int xP, xQ, yP, yQ;
Macroblock *MbQ, *MbP;
PixelPos pixP, pixQ;
PelNum = yuv ? 8 : 16;//亮度和色度宽
//注意这边对于dir=0和1不同的扫描方式
for( pel=0 ; pel<PelNum ; pel++ )
{
xQ = dir ? pel : edge << 2;
yQ = dir ? (edge < 4 ? edge << 2 : 1) : pel;
//xQ和yQ类似GetStrength函数中的两个变量
getNeighbour(MbQAddr, xQ, yQ, 1-yuv, &pixQ);//冗余代码
getNeighbour(MbQAddr, xQ - (1 - dir), yQ - dir, 1-yuv, &pixP);
//对于dir=0,取左边邻近块,对于dir=1,取上边邻近块
xP = pixP.x;
yP = pixP.y;
//所以到这边xQ,yQ,xP和yP得到的值都是指当前像素点相对于本宏块内坐上点的整像素坐标
MbQ = &(img->mb_data[MbQAddr]);
MbP = &(img->mb_data[pixP.mb_addr]);
fieldModeFilteringFlag = MbQ->mb_field || MbP->mb_field;
StrengthIdx = yuv ? ((MbQ->mb_field && !MbP->mb_field) ? pel<<1 : ((pel>>1)<<2)+(pel%2)) : pel ;
//当处理亮度的时候,即yuv=0,StrengthIdx=pel=0-15
//当处理色度的时候,即yuv=1,StrengthIdx=(MbQ->mb_field && !MbP->mb_field) ? pel<<1 : ((pel>>1)<<2)+(pel%2)
//令idx=((pel>>1)<<2)+(pel%2)=(pel/2)*4+(pel%2)
/*
pel=0,idx=0
pel=1,idx=1
pel=2,idx=4
pel=3,idx=5
pel=4,idx=8
pel=5,idx=9
pel=6,idx=12
pel=7,idx=13
色度块边界滤波的Bs值不另外计算,而是从相应亮度块边界的Bs值复制而来
*/
if (pixP.available || (MbQ->LFDisableIdc== 0))
{
incQ = dir ? ((fieldModeFilteringFlag && !MbQ->mb_field) ? 2 * width : width) : 1;
incP = dir ? ((fieldModeFilteringFlag && !MbP->mb_field) ? 2 * width : width) : 1;
/*
如果dir=0,即处理垂直边界
incQ=incP=1
如果dir=1,即处理水平边界
由于我们的情况MbQ->mb_field=MbP->mb_field=0,所以incQ=incP=width
这边的width,如QCIF,对于亮度就是176,对于色度就是88
*/
//pos_x和pos_y表示所求点在整幅图像中的整像素横纵坐标
SrcPtrQ = &(Img[pixQ.pos_y][pixQ.pos_x]);//Q和P的象素值
SrcPtrP = &(Img[pixP.pos_y][pixP.pos_x]);
// Average QP of the two blocks
//这边的QP用的是相邻两块的平均值,且在标准203页的公式8-461已经指出
QP = yuv ? (QP_SCALE_CR[CQPOF(MbP->qp)] + QP_SCALE_CR[CQPOF(MbQ->qp)] + 1) >> 1 : (MbP->qp + MbQ->qp + 1) >> 1;
//这边就是毕书141页上的QP+OffsetA和QP+OffsetB
indexA = IClip(0, MAX_QP, QP + AlphaC0Offset);
indexB = IClip(0, MAX_QP, QP + BetaOffset);
//Alpha和Beta都是通过查表得到
Alpha=ALPHA_TABLE[indexA];
Beta=BETA_TABLE[indexB];
ClipTab=CLIP_TAB[indexA];//限幅变量由indexA决定查表得到
/*
L0,L1,L2,L3表示P块中处理边界的同一行或同一列的4个像素点的像素值
R0,R1,R2,R3表示Q块中处理边界的同一行或同一列的4个像素点的像素值
当dir=0时,排列方式
L3 L2 L1 L0 R0 R1 R2 R3
当dir=1时,排列方式
L3
L2
L1
L0
R0
R1
R2
R3
*/
L0 = SrcPtrP[0] ;//相当于*(SrcPtrP)
R0 = SrcPtrQ[0] ;//相当于*(SrcPtrQ)
L1 = SrcPtrP[-incP] ;//相当于*(SrcPtrP-incP)
R1 = SrcPtrQ[ incQ] ;
L2 = SrcPtrP[-incP*2] ;//相当于*(SrcPtrP-incP*2)
R2 = SrcPtrQ[ incQ*2] ;
L3 = SrcPtrP[-incP*3] ;//相当于*(SrcPtrP-incP*3)
R3 = SrcPtrQ[ incQ*3] ;
//也就是说非零强度才会被滤波
if( (Strng = Strength[StrengthIdx]) )//只要Strng非0,这边就是true
{
AbsDelta = abs( Delta = R0 - L0 ) ;
//这边就是毕书140页上说的,为了区别真假边界所需要满足的3个条件
if( AbsDelta < Alpha )
{
C0 = ClipTab[ Strng ] ;//通过Bs确定最后限幅值
if( ((abs( R0 - R1) - Beta ) & (abs(L0 - L1) - Beta )) < 0 )
{
if( !yuv)//只对亮度
//这边对应毕书142页的公式6.62和6.63,用来决定亮度点的滤波范围
//当两个条件都成立的时候,说明边界变化强度不大,滤波强度的设定值相对于实际
//滤波来说偏大
{
aq = (abs( R0 - R2) - Beta ) < 0 ;
ap = (abs( L0 - L2) - Beta ) < 0 ;
//即aq和ap都等于1
}
RL0 = L0 + R0 ;
//特殊超强滤波
//只是简单的套公式了,但真正为什么要这么做,原理就不清楚了
if(Strng == 4 ) // INTRA strong filtering
{
if( yuv) // Chroma 色度
{
SrcPtrQ[0] = ((R1 << 1) + R0 + L1 + 2) >> 2;
SrcPtrP[0] = ((L1 << 1) + L0 + R1 + 2) >> 2;
}
else // Luma 亮度
{
//这部分要看标准206页,毕书没有讲的很详细
small_gap = (AbsDelta < ((Alpha >> 2) + 2));
aq &= small_gap;
ap &= small_gap;
SrcPtrQ[0] = aq ? ( L1 + ((R1 + RL0) << 1) + R2 + 4) >> 3 : ((R1 << 1) + R0 + L1 + 2) >> 2 ;
SrcPtrP[0] = ap ? ( R1 + ((L1 + RL0) << 1) + L2 + 4) >> 3 : ((L1 << 1) + L0 + R1 + 2) >> 2 ;
SrcPtrQ[ incQ] = aq ? ( R2 + R0 + R1 + L0 + 2) >> 2 : R1;
SrcPtrP[-incP] = ap ? ( L2 + L1 + L0 + R0 + 2) >> 2 : L1;
SrcPtrQ[ incQ*2] = aq ? (((R3 + R2) <<1) + R2 + R1 + RL0 + 4) >> 3 : R2;
SrcPtrP[-incP*2] = ap ? (((L3 + L2) <<1) + L2 + L1 + RL0 + 4) >> 3 : L2;
}
}
//普通滤波
else//Strng=0,1,2,3 // normal filtering
{
c0 = yuv? (C0+1):(C0 + ap + aq) ;
//dif的公式即毕书143页的6.66
dif = IClip( -c0, c0, ( (Delta << 2) + (L1 - R1) + 4) >> 3 );
SrcPtrP[0] = IClip(0, 255, L0 + dif) ;//公式6.64
SrcPtrQ[0] = IClip(0, 255, R0 - dif) ;//公式6.65
if( !yuv )
{
//公式6.69
if( ap )
SrcPtrP[-incP] += IClip( -C0, C0, ( L2 + ((RL0 + 1) >> 1) - (L1<<1)) >> 1 ) ;
if( aq )
SrcPtrQ[ incQ] += IClip( -C0, C0, ( R2 + ((RL0 + 1) >> 1) - (R1<<1)) >> 1 ) ;
} ;
} ;//if(Strng == 4 )
} ;
} ;//if( AbsDelta < Alpha )
} ;//if( (Strng = Strength[StrengthIdx]) )
} ;//if (pixP.available || (MbQ->LFDisableIdc== 0))
}//for( pel=0 ; pel<PelNum ; pel++ )
}