x264_frame_init_lowres函数中会根据原图像生成一个宽高都为原图一半的子图像,保存在frame->lowres中,首先我们看下其生成的源代码:
void x264_frame_init_lowres( x264_t *h, x264_frame_t *frame )
{
pixel *src = frame->plane[0];
int i_stride = frame->i_stride[0]; //等于frame->i_width[0] + 64
int i_height = frame->i_lines[0]; //等于图像的height
int i_width = frame->i_width[0]; //图像的width,会被x264扩展为16的倍数
// 1.duplicate last row and column so that their interpolation doesn't have to be special-cased
for( int y = 0; y < i_height; y++ ) //将每行最后一个数值拷贝,放到它后面
src[i_width+y*i_stride] = src[i_width-1+y*i_stride];
//将最后一行数据拷贝,然后放到它后面
memcpy( src+i_stride*i_height, src+i_stride*(i_height-1), (i_width+1) * sizeof(pixel) );
//2.用4像素内插法将生成四分之一分辨率的子图像,保存在frame->lowres里
h->mc.frame_init_lowres_core( src, frame->lowres[0], frame->lowres[1], frame->lowres[2], frame->lowres[3],
i_stride, frame->i_stride_lowres, frame->i_width_lowres, frame->i_lines_lowres );
//3.对子图像上下左右边界进行填充
x264_frame_expand_border_lowres( frame );
memset( frame->i_cost_est, -1, sizeof(frame->i_cost_est) );
for( int y = 0; y < h->param.i_bframe + 2; y++ )
for( int x = 0; x < h->param.i_bframe + 2; x++ )
frame->i_row_satds[y][x][0] = -1;
for( int y = 0; y <= !!h->param.i_bframe; y++ )
for( int x = 0; x <= h->param.i_bframe; x++ )
frame->lowres_mvs[y][x][0][0] = 0x7FFF;
}
主要完成几件事:
1.对src(frame->plane[0])中的图像数据进行补边,代码如下:
// duplicate last row and column so that their interpolation doesn't have to be special-cased
for( int y = 0; y < i_height; y++ ) //将每行最后一个数值拷贝,放到它后面
src[i_width+y*i_stride] = src[i_width-1+y*i_stride];
//将最后一行数据拷贝,然后放到它后面
memcpy( src+i_stride*i_height, src+i_stride*(i_height-1), (i_width+1) * sizeof(pixel) );
内存分布图如下:
copyplane内存图.png
2.调用h->mc.frame_init_lowres_core函数,用内插法生成半像素图像,保存在frame->lowres中,lowers是什么呢,我们可以看下x264_frame结构体中的形式:
typedef struct x264_frame
{
//省略
//Orig:原点; H:水平; V:垂直 HV:对角
pixel *lowres[4]; /* half-size copy of input frame: Orig, H, V, HV */
//省略
} x264_frame_t;
由源码可知,lowres是指向4块内存区域的指针数组,这4块内存指针分别为Orig:原点; H:水平; V:垂直 HV:对角,到底是什么意思呢,我们先看h->mc.frame_init_lowres_core函数,它实际上是frame_init_lowres_core函数,源码如下:
//半像素内插,实际上就是利用4个像素平均生成一个像素
static void frame_init_lowres_core( pixel *src0, pixel *dst0, pixel *dsth, pixel *dstv, pixel *dstc,
intptr_t src_stride, intptr_t dst_stride, int width, int height )
{
for( int y = 0; y < height; y++ )
{
pixel *src1 = src0+src_stride;
pixel *src2 = src1+src_stride;
for( int x = 0; x<width; x++ )
{
// slower than naive bilinear, but matches asm
//以x==0为例
//dst0[0] = ((src0[0]+src1[0])/2 + (src0[1]+src1[1])/2) / 2
//dsth[0] = ((src0[1]+src1[1])/2 + (src0[2]+src1[2])/2) / 2
//dstv[0] = ((src1[0]+src2[0])/2 + (src1[1]+src2[1])/2) / 2
//dstc[0] = ((src0[1]+src2[1])/2 + (src1[2]+src2[2])/2) / 2
#define FILTER(a,b,c,d) ((((a+b+1)>>1)+((c+d+1)>>1)+1)>>1)
dst0[x] = FILTER(src0[2*x ], src1[2*x ], src0[2*x+1], src1[2*x+1]);
dsth[x] = FILTER(src0[2*x+1], src1[2*x+1], src0[2*x+2], src1[2*x+2]);
dstv[x] = FILTER(src1[2*x ], src2[2*x ], src1[2*x+1], src2[2*x+1]);
dstc[x] = FILTER(src1[2*x+1], src2[2*x+1], src1[2*x+2], src2[2*x+2]);
#undef FILTER
}
src0 += src_stride*2;
dst0 += dst_stride;
dsth += dst_stride;
dstv += dst_stride;
dstc += dst_stride;
}
}
如源码,dst0,dst1,dst2,dst3对应着frame->lowres[0], frame->lowres[1], frame->lowres[2], frame->lowres[3],生成的方法如注释所言,这里再画个图辅助:
半像素内插.png
如图,每个dst等于它邻近的4个src像素平均值,看图中4个dst的位置,应该可以知道上面所说的原点,水平,垂直和对角是什么意思了吧...
3.调用 x264_frame_expand_border_lowres函数对子图像进行填充,将在下一章分析。
网友评论