H.264视频编解码的FPGA源码分析(二)帧内预测1

目录

前述文章链接在此~~
H.264视频编解码的FPGA源码分析(一)输入数据分析

帧内预测算法原理

基于论文的普通介绍

  1. 目的:提高I帧压缩性能
  2. 核心思想:利用图像内部特别是图像平滑部分相邻像素间的相关性来降低编码码率,也就是通过减少空间冗余来实现压缩
  3. 使用当前编码的宏块上方以及左方的宏块,计算当前宏块的预测值,再将预测值与实际的像素值相减得到预测残差,编码的时候只对预测残差进行编码(有点像残差神经网络的思想嘿嘿嘿)
  4. 4种帧内预测模式:4×4亮度块预测(intra_4×4),16×16亮度块预测(intra_16×16),8×8色度块预测(intra_chroma),PCM预测方式(I_PCM)

硬件实现

亮度块与色度块的划分

我们来复习一下,上一篇文章中我们已经分析出,信号mc_cur_umc_cur_v是连接8×8色度块以及帧内预测模块,连接方式为:
H.264视频编解码的FPGA源码分析(二)帧内预测1

mb_cb mc_cur_u
mb_cr mc_cur_v

然后来看这个数据去了哪里

genvar j; 
generate 
  for(j=0;j<64; j=j+1) begin:j_n
  	always @( * ) begin
  		cur_u[j] = mb_cb[(j+1)*8-1:j*8];
  		cur_v[j] = mb_cr[(j+1)*8-1:j*8];  
    end
  end
endgenerate

这里的cur_ucur_v是寄存器数组,定义如下:

reg [`BIT_DEPTH-1:0] 		cur_u[0:63];
reg [`BIT_DEPTH-1:0] 		cur_v[0:63];

然后这个数据~~

always @(*) begin
	case (chroma_num)
	3'b000:ori_uv={cur_u[0],  cur_u[1],  cur_u[2],  cur_u[3],
				   cur_u[8],  cur_u[9],  cur_u[10], cur_u[11],
				   cur_u[16], cur_u[17], cur_u[18], cur_u[19],
				   cur_u[24], cur_u[25], cur_u[26], cur_u[27] };
	3'b001:ori_uv={cur_u[4],  cur_u[5],  cur_u[6],  cur_u[7],
				   cur_u[12], cur_u[13], cur_u[14], cur_u[15],
				   cur_u[20], cur_u[21], cur_u[22], cur_u[23],
				   cur_u[28], cur_u[29], cur_u[30], cur_u[31] };
	3'b010:ori_uv={cur_u[32], cur_u[33], cur_u[34], cur_u[35],
				   cur_u[40], cur_u[41], cur_u[42], cur_u[43],
				   cur_u[48], cur_u[49], cur_u[50], cur_u[51],
				   cur_u[56], cur_u[57], cur_u[58], cur_u[59] };
	3'b011:ori_uv={cur_u[36], cur_u[37], cur_u[38], cur_u[39],
				   cur_u[44], cur_u[45], cur_u[46], cur_u[47],
				   cur_u[52], cur_u[53], cur_u[54], cur_u[55],
				   cur_u[60], cur_u[61], cur_u[62], cur_u[63] };	
	3'b100:ori_uv={cur_v[0],  cur_v[1],  cur_v[2],  cur_v[3],
				   cur_v[8],  cur_v[9],  cur_v[10], cur_v[11],
				   cur_v[16], cur_v[17], cur_v[18], cur_v[19],
				   cur_v[24], cur_v[25], cur_v[26], cur_v[27] };
	3'b101:ori_uv={cur_v[4],  cur_v[5],  cur_v[6],  cur_v[7],
				   cur_v[12], cur_v[13], cur_v[14], cur_v[15],
				   cur_v[20], cur_v[21], cur_v[22], cur_v[23],
				   cur_v[28], cur_v[29], cur_v[30], cur_v[31] };
	3'b110:ori_uv={cur_v[32], cur_v[33], cur_v[34], cur_v[35],
				   cur_v[40], cur_v[41], cur_v[42], cur_v[43],
				   cur_v[48], cur_v[49], cur_v[50], cur_v[51],
				   cur_v[56], cur_v[57], cur_v[58], cur_v[59] };
	3'b111:ori_uv={cur_v[36], cur_v[37], cur_v[38], cur_v[39],
				   cur_v[44], cur_v[45], cur_v[46], cur_v[47],
				   cur_v[52], cur_v[53], cur_v[54], cur_v[55],
				   cur_v[60], cur_v[61], cur_v[62], cur_v[63] };
	endcase	
end

这种整齐的代码看起来简直不要太爽
可以看到case的状态变量是chroma_num,这个变量是来自模块intra_16x16_chroma_top

intra_16x16_chroma_top u_intra_16x16_chroma_top(
				.clk				( clk				),    
				.rst_n				( rst_n				),    
				.mb_x_total			( mb_x_total		),    
				.mb_x				( mb_x				),    
				.mb_y				( mb_y				),   
				
				.pred_start_i    	( i16x16_chroma_start ),
				.pred_done_o        ( i16x16_chroma_done  ),
				.i16x16_rec_start_i ( i16x16_rec_start  ),
				.i16x16_rec_done_o  ( i16x16_rec_done   ),
				.chroma_rec_start_i ( chroma_rec_start  ),
				.chroma_rec_done_o  ( chroma_rec_done   ),
				.luma_16x16_cost_o  ( i16x16_cost       ),
				.chroma_8x8_cost_o	( chroma_cost   	),			      
				.luma_16x16_sel_o   ( i16x16_num        ),
				.chroma_8x8_sel_o   ( chroma_num        ),

篇幅所限我这里只截取模块实例化的一部分代码
也就是说,模块接收来自intra_16x16_chroma的指示信号,然后根据这个信号的值来对ori_uv这个变量赋值,变量定义为:

reg [`BIT_DEPTH*16-1:0] 	ori_uv;

是一个8*16=128bit的寄存器变量,这里case了8种情况,没有default但是因为已经包含了所有可能的情况,所以不会出现锁存器。chroma_num主要是指示要把哪些数据赋值给ori_uv。但这里我第一次看的时候没注意,其实cur_vcur_u是不同的

{cur_u[{addr_uv, 2'b00}],cur_v[{addr_uv, 2'b00}],cur_u[{addr_uv, 2'b01}],cur_v[{addr_uv, 2'b01}],
cur_u[{addr_uv, 2'b10}],cur_v[{addr_uv, 2'b10}],cur_u[{addr_uv, 2'b11}],cur_v[{addr_uv, 2'b11}]} <= pdata_i;

也就是说cur_vcur_u是交替赋值的。

这里我个人其实没有理解是怎么赋值的,氮素~我们可以通过仿真来直观的看一下这个赋值的过程。把从pdata开始到ori_uv的代码单独抠出来仿真一下,试试看!let’s go!
H.264视频编解码的FPGA源码分析(二)帧内预测1
开始计数,pdata一次取出64bit,第一个数是525956565d575355
对应文件中的数
H.264视频编解码的FPGA源码分析(二)帧内预测1
可以看到一次取两行数据
H.264视频编解码的FPGA源码分析(二)帧内预测1
可以看到当pvalid_i变为高之后,addr_p开始计数,然后下一个时钟周期,cur_y开始赋值,一个周期赋值64bit数据。与此同时,addr_y(5bit)和addr_uv(4bit)同步开始计数。

H.264视频编解码的FPGA源码分析(二)帧内预测1
addr_p(6bit)的最高位变成1,下一个周期cur_u开始赋值,采用pdata_i隔一个赋一个,只取了32bit 的数据。
H.264视频编解码的FPGA源码分析(二)帧内预测1H.264视频编解码的FPGA源码分析(二)帧内预测1H.264视频编解码的FPGA源码分析(二)帧内预测1
一个16×16的亮度宏块,和两个8×8的色度宏块
实在是看不出来是什么东西。。。
那么为什么要这么划分呢?这个就要涉及到YUV的编码格式:YUV中的Y代表亮度(luma),CbCr代表色度(chroma)。常见的有三种格式:YUV444,YUV422,YUV420。第一个数字4代表4个图像像素中,每个都有亮度值,第二个数字代表U,表示四个图形像素中,Cb只占两个像素,第三个数字代表V。具体的可以参考下图
H.264视频编解码的FPGA源码分析(二)帧内预测1
所以这里的赋值方法大概就是类似于倒数第一种or倒数第三种?

4×4亮度预测模块

可以看到由intra_4×4模块发出一个指示信号i4x4_num

intra_4x4_top u_intra_4x4_top(
				.clk				( clk				), 
				.rst_n				( rst_n				), 
				.mb_x_total			( mb_x_total		), 
				.mb_x				( mb_x				), 
				.mb_y				( mb_y				), 
				
				.start_i           	( i4x4_start        ),
				.done_o            	( i4x4_done         ),
				.lambda_i          	( lambda            ),
				.luma_4x4_cost_o   	( i4x4_cost         ),
				                  	
				.i4x4_num_o        	( i4x4_num          ),

这个信号用于指示cur_y数据的分配

always @(*) begin
	case (i4x4_num)
		4'b0000:ori4x4 ={ cur_y[0],   cur_y[1],   cur_y[2],   cur_y[3],
						  cur_y[16],  cur_y[17],  cur_y[18],  cur_y[19],
						  cur_y[32],  cur_y[33],  cur_y[34],  cur_y[35],
						  cur_y[48],  cur_y[49],  cur_y[50],  cur_y[51] };
		4'b0001:ori4x4 ={ cur_y[4],   cur_y[5],   cur_y[6],   cur_y[7],
						  cur_y[20],  cur_y[21],  cur_y[22],  cur_y[23],
						  cur_y[36],  cur_y[37],  cur_y[38],  cur_y[39],
						  cur_y[52],  cur_y[53],  cur_y[54],  cur_y[55] };
		4'b0010:ori4x4 ={ cur_y[64],  cur_y[65],  cur_y[66],  cur_y[67],
						  cur_y[80],  cur_y[81],  cur_y[82],  cur_y[83],
						  cur_y[96],  cur_y[97],  cur_y[98],  cur_y[99],
						  cur_y[112], cur_y[113], cur_y[114], cur_y[115] };
		4'b0011:ori4x4 ={ cur_y[68],  cur_y[69],  cur_y[70],  cur_y[71],
						  cur_y[84],  cur_y[85],  cur_y[86],  cur_y[87],
						  cur_y[100], cur_y[101], cur_y[102], cur_y[103],
						  cur_y[116], cur_y[117], cur_y[118], cur_y[119] };
		4'b0100:ori4x4 ={ cur_y[8],   cur_y[9],   cur_y[10],  cur_y[11],
						  cur_y[24],  cur_y[25],  cur_y[26],  cur_y[27],
						  cur_y[40],  cur_y[41],  cur_y[42],  cur_y[43],
						  cur_y[56],  cur_y[57],  cur_y[58],  cur_y[59] };
		4'b0101:ori4x4 ={ cur_y[12],  cur_y[13],  cur_y[14],  cur_y[15],
						  cur_y[28],  cur_y[29],  cur_y[30],  cur_y[31],
						  cur_y[44],  cur_y[45],  cur_y[46],  cur_y[47],
						  cur_y[60],  cur_y[61],  cur_y[62],  cur_y[63] };
		4'b0110:ori4x4 ={ cur_y[72],  cur_y[73],  cur_y[74],  cur_y[75],
						  cur_y[88],  cur_y[89],  cur_y[90],  cur_y[91],
						  cur_y[104], cur_y[105], cur_y[106], cur_y[107],
						  cur_y[120], cur_y[121], cur_y[122], cur_y[123] };
		4'b0111:ori4x4 ={ cur_y[76],  cur_y[77],  cur_y[78],  cur_y[79],
						  cur_y[92],  cur_y[93],  cur_y[94],  cur_y[95],
						  cur_y[108], cur_y[109], cur_y[110], cur_y[111],
						  cur_y[124], cur_y[125], cur_y[126], cur_y[127] };
		4'b1000:ori4x4 ={ cur_y[128], cur_y[129], cur_y[130], cur_y[131],
						  cur_y[144], cur_y[145], cur_y[146], cur_y[147],
						  cur_y[160], cur_y[161], cur_y[162], cur_y[163],
						  cur_y[176], cur_y[177], cur_y[178], cur_y[179] };
		4'b1001:ori4x4 ={ cur_y[132], cur_y[133], cur_y[134], cur_y[135],
						  cur_y[148], cur_y[149], cur_y[150], cur_y[151],
						  cur_y[164], cur_y[165], cur_y[166], cur_y[167],
						  cur_y[180], cur_y[181], cur_y[182], cur_y[183] };
		4'b1010:ori4x4 ={ cur_y[192], cur_y[193], cur_y[194], cur_y[195],
						  cur_y[208], cur_y[209], cur_y[210], cur_y[211],
						  cur_y[224], cur_y[225], cur_y[226], cur_y[227],
						  cur_y[240], cur_y[241], cur_y[242], cur_y[243] };
		4'b1011:ori4x4 ={ cur_y[196], cur_y[197], cur_y[198], cur_y[199],
						  cur_y[212], cur_y[213], cur_y[214], cur_y[215],
						  cur_y[228], cur_y[229], cur_y[230], cur_y[231],
						  cur_y[244], cur_y[245], cur_y[246], cur_y[247] };									     
		4'b1100:ori4x4 ={ cur_y[136], cur_y[137], cur_y[138], cur_y[139],
						  cur_y[152], cur_y[153], cur_y[154], cur_y[155],
						  cur_y[168], cur_y[169], cur_y[170], cur_y[171],
						  cur_y[184], cur_y[185], cur_y[186], cur_y[187] };
		4'b1101:ori4x4 ={ cur_y[140], cur_y[141], cur_y[142], cur_y[143],
						  cur_y[156], cur_y[157], cur_y[158], cur_y[159],
						  cur_y[172], cur_y[173], cur_y[174], cur_y[175],
						  cur_y[188], cur_y[189], cur_y[190], cur_y[191] };
		4'b1110:ori4x4 ={ cur_y[200], cur_y[201], cur_y[202], cur_y[203],
						  cur_y[216], cur_y[217], cur_y[218], cur_y[219],
						  cur_y[232], cur_y[233], cur_y[234], cur_y[235],
						  cur_y[248], cur_y[249], cur_y[250], cur_y[251] };
		4'b1111:ori4x4 ={ cur_y[204], cur_y[205], cur_y[206], cur_y[207],
						  cur_y[220], cur_y[221], cur_y[222], cur_y[223],
						  cur_y[236], cur_y[237], cur_y[238], cur_y[239],
						  cur_y[252], cur_y[253], cur_y[254], cur_y[255] };
	endcase
end

总共是16种情况

assign ori00_4x4 = ori4x4[127:120];
assign ori01_4x4 = ori4x4[119:112];
assign ori02_4x4 = ori4x4[111:104];
assign ori03_4x4 = ori4x4[103: 96];
assign ori10_4x4 = ori4x4[ 95: 88];
assign ori11_4x4 = ori4x4[ 87: 80];
assign ori12_4x4 = ori4x4[ 79: 72];
assign ori13_4x4 = ori4x4[ 71: 64];
assign ori20_4x4 = ori4x4[ 63: 56];
assign ori21_4x4 = ori4x4[ 55: 48];
assign ori22_4x4 = ori4x4[ 47: 40];
assign ori23_4x4 = ori4x4[ 39: 32];
assign ori30_4x4 = ori4x4[ 31: 24];
assign ori31_4x4 = ori4x4[ 23: 16];
assign ori32_4x4 = ori4x4[ 15:  8];
assign ori33_4x4 = ori4x4[  7:  0];
intra_4x4_top u_intra_4x4_top(
				.clk				( clk				), 
				.rst_n				( rst_n				), 
				.mb_x_total			( mb_x_total		), 
				.mb_x				( mb_x				), 
				.mb_y				( mb_y				), 
				
				.start_i           	( i4x4_start        ),
				.done_o            	( i4x4_done         ),
				.lambda_i          	( lambda            ),
				.luma_4x4_cost_o   	( i4x4_cost         ),
				                  	
				.i4x4_num_o        	( i4x4_num          ),
				.i4x4_end_o        	( i4x4_end          ),
				.i4x4_pred_mode_i  	( i4x4_pred_mode    ),
				.i4x4_min_mode_o   	( i4x4_min_mode     ),
				.i4x4_min_val_o    	( i4x4_min_val      ),
				.i4x4_min_num_o	  	( i4x4_min_num	    ),
				                  	
				.tq_en_o           	( tq_i4x4_en_o      ),
				.tq_mod_o          	( tq_i4x4_mod_o     ),
				.tq_num_o          	( tq_i4x4_num_o     ),
				.tq_end_o          	( tq_i4x4_end_o     ),
				.tq_min_o          	( tq_i4x4_min_o     ),				
				
				.ori00 ( ori00_4x4 ), .ori01 ( ori01_4x4 ), .ori02 ( ori02_4x4 ), .ori03 ( ori03_4x4 ),
				.ori10 ( ori10_4x4 ), .ori11 ( ori11_4x4 ), .ori12 ( ori12_4x4 ), .ori13 ( ori13_4x4 ),
				.ori20 ( ori20_4x4 ), .ori21 ( ori21_4x4 ), .ori22 ( ori22_4x4 ), .ori23 ( ori23_4x4 ),
				.ori30 ( ori30_4x4 ), .ori31 ( ori31_4x4 ), .ori32 ( ori32_4x4 ), .ori33 ( ori33_4x4 ),

下面我们跳到这个intra_4×4模块
对应的定义

// Original Pixel Input
input  [`BIT_DEPTH-1:0]		ori00, ori01, ori02, ori03,
                            ori10, ori11, ori12, ori13,
                            ori20, ori21, ori22, ori23,
                            ori30, ori31, ori32, ori33;
// Reference Pixel Input
input  [`BIT_DEPTH-1:0]		ref00tl,                            
							ref00t,  ref01t,  ref02t,  ref03t,  
							ref00l,  ref01l,  ref02l,  ref03l,  
							ref00tr, ref01tr, ref02tr, ref03tr;
// Predicted Pixel Output
output  [`BIT_DEPTH-1:0]	pre00, pre01, pre02, pre03,
							pre10, pre11, pre12, pre13,
							pre20, pre21, pre22, pre23,
							pre30, pre31, pre32, pre33;
// Residual Data Output
output  [`BIT_DEPTH:0]		res00, res01, res02, res03,
							res10, res11, res12, res13,
							res20, res21, res22, res23,
							res30, res31, res32, res33;

好我们来分析一下。
cur_y是一个8bit位宽,深度为256的寄存器数组,但是在4×4的帧内预测模块中,是以一个44子块作为单元来实现的。于是就将其划分为44的子块,其中行是连续的,但是行与行之间的数据是不连续的。然后转化为串行数据,传递给4×4的帧内预测模块进行处理。
这里还涉及到了一个参考信号,看一下这个信号是什么。

定义

// Ref Pixels for Intra_4x4
wire [`BIT_DEPTH-1:0] ref4x4_00tl,                                      
					  ref4x4_00t,  ref4x4_01t,  ref4x4_02t,  ref4x4_03t,
					  ref4x4_00l,  ref4x4_01l,  ref4x4_02l,  ref4x4_03l,
					  ref4x4_00tr, ref4x4_01tr, ref4x4_02tr, ref4x4_03tr;

可以看到是从模块intra_ref产生。【这里我们下次介绍】

如何产生预测像素与残差像素?

进入这个模块:

intra_4x4_pe u_intra_4x4_pe (
				.clk  		( clk   			),
				.rst_n		( rst_n 			),
				.curr_mode	( i4x4_curr_mode  	),
				.blk_avail	( blk_avail 		),
				
				.pixel_ori00( ori00 ), .pixel_ori01( ori01 ), .pixel_ori02( ori02 ), .pixel_ori03( ori03 ),
				.pixel_ori10( ori10 ), .pixel_ori11( ori11 ), .pixel_ori12( ori12 ), .pixel_ori13( ori13 ),
				.pixel_ori20( ori20 ), .pixel_ori21( ori21 ), .pixel_ori22( ori22 ), .pixel_ori23( ori23 ),
				.pixel_ori30( ori30 ), .pixel_ori31( ori31 ), .pixel_ori32( ori32 ), .pixel_ori33( ori33 ),

输出的残差信号是pixel_res00_o,这个信号是打一拍之后的赋值,打一拍之前的赋值关系是:

assign pixel_res00 = {1'b0,pixel_ori00} - {1'b0,pixel_pred00[`BIT_DEPTH-1:0]};
assign pixel_res01 = {1'b0,pixel_ori01} - {1'b0,pixel_pred01[`BIT_DEPTH-1:0]};
assign pixel_res02 = {1'b0,pixel_ori02} - {1'b0,pixel_pred02[`BIT_DEPTH-1:0]};
assign pixel_res03 = {1'b0,pixel_ori03} - {1'b0,pixel_pred03[`BIT_DEPTH-1:0]};
assign pixel_res10 = {1'b0,pixel_ori10} - {1'b0,pixel_pred10[`BIT_DEPTH-1:0]};
assign pixel_res11 = {1'b0,pixel_ori11} - {1'b0,pixel_pred11[`BIT_DEPTH-1:0]};
assign pixel_res12 = {1'b0,pixel_ori12} - {1'b0,pixel_pred12[`BIT_DEPTH-1:0]};
assign pixel_res13 = {1'b0,pixel_ori13} - {1'b0,pixel_pred13[`BIT_DEPTH-1:0]};
assign pixel_res20 = {1'b0,pixel_ori20} - {1'b0,pixel_pred20[`BIT_DEPTH-1:0]};
assign pixel_res21 = {1'b0,pixel_ori21} - {1'b0,pixel_pred21[`BIT_DEPTH-1:0]};
assign pixel_res22 = {1'b0,pixel_ori22} - {1'b0,pixel_pred22[`BIT_DEPTH-1:0]};
assign pixel_res23 = {1'b0,pixel_ori23} - {1'b0,pixel_pred23[`BIT_DEPTH-1:0]};
assign pixel_res30 = {1'b0,pixel_ori30} - {1'b0,pixel_pred30[`BIT_DEPTH-1:0]};
assign pixel_res31 = {1'b0,pixel_ori31} - {1'b0,pixel_pred31[`BIT_DEPTH-1:0]};
assign pixel_res32 = {1'b0,pixel_ori32} - {1'b0,pixel_pred32[`BIT_DEPTH-1:0]};
assign pixel_res33 = {1'b0,pixel_ori33} - {1'b0,pixel_pred33[`BIT_DEPTH-1:0]};

其中pixel_ori00就是输入的原始信号,那个pixel_pred00是什么呢?
可以看到,这个变量是通过一个case来赋值的:

always @(*)begin
	case(curr_mode)
		//0
		INTRA4x4_V   :begin
			pixel_pred00=ref00_t; pixel_pred01=ref01_t; pixel_pred02=ref02_t; pixel_pred03=ref03_t;
			pixel_pred10=ref00_t; pixel_pred11=ref01_t; pixel_pred12=ref02_t; pixel_pred13=ref03_t;
			pixel_pred20=ref00_t; pixel_pred21=ref01_t; pixel_pred22=ref02_t; pixel_pred23=ref03_t;
			pixel_pred30=ref00_t; pixel_pred31=ref01_t; pixel_pred32=ref02_t; pixel_pred33=ref03_t;

控制状态的变量为curr_mode,总共有9中情况,加上一个默认情况(全0)。这样就很清晰了,对应的就是9种预测模式:
H.264视频编解码的FPGA源码分析(二)帧内预测1

垂直模式INTRA4x4_V

pixel_pred00=ref00_t; pixel_pred01=ref01_t; pixel_pred02=ref02_t; pixel_pred03=ref03_t;
pixel_pred10=ref00_t; pixel_pred11=ref01_t; pixel_pred12=ref02_t; pixel_pred13=ref03_t;
pixel_pred20=ref00_t; pixel_pred21=ref01_t; pixel_pred22=ref02_t; pixel_pred23=ref03_t;
pixel_pred30=ref00_t; pixel_pred31=ref01_t; pixel_pred32=ref02_t; pixel_pred33=ref03_t;

H.264视频编解码的FPGA源码分析(二)帧内预测1
ABCD分别对应ref00_t/ref01_t/ref02_t/ref03_t

水平模式INTRA4x4_H

pixel_pred00=ref00_l; pixel_pred01=ref00_l; pixel_pred02=ref00_l; pixel_pred03=ref00_l;
pixel_pred10=ref01_l; pixel_pred11=ref01_l; pixel_pred12=ref01_l; pixel_pred13=ref01_l;
pixel_pred20=ref02_l; pixel_pred21=ref02_l; pixel_pred22=ref02_l; pixel_pred23=ref02_l;
pixel_pred30=ref03_l; pixel_pred31=ref03_l; pixel_pred32=ref03_l; pixel_pred33=ref03_l;

H.264视频编解码的FPGA源码分析(二)帧内预测1
IJKL分别对应ref00_l/ref01_l/ref02_l/ref03_l

直流模式INTRA4x4_DC

	if(blk_avail[3]&&blk_avail[2])begin
		pixel_pred00=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred01=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred02=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred03=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred10=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred11=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred12=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred13=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred20=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred21=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred22=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred23=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred30=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred31=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred32=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
		pixel_pred33=(ref00_t+ref01_t+ref02_t+ref03_t+ref00_l+ref01_l+ref02_l+ref03_l+3'd4)>>3;
	end
	else if(blk_avail[3])begin
		pixel_pred00=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred01=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred02=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred03=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred10=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred11=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred12=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred13=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred20=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred21=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred22=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred23=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred30=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred31=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred32=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
		pixel_pred33=(ref00_l+ref01_l+ref02_l+ref03_l+2'd2)>>2;
	end
	else if(blk_avail[1])begin
		pixel_pred00=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred01=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred02=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred03=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred10=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred11=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred12=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred13=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred20=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred21=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred22=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred23=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred30=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred31=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred32=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
		pixel_pred33=(ref00_t+ref01_t+ref02_t+ref03_t+2'd2)>>2;
	end
	else begin
		pixel_pred00=8'd128;
		pixel_pred01=8'd128;
		pixel_pred02=8'd128;
		pixel_pred03=8'd128;
		pixel_pred10=8'd128;
		pixel_pred11=8'd128;
		pixel_pred12=8'd128;
		pixel_pred13=8'd128;
		pixel_pred20=8'd128;
		pixel_pred21=8'd128;
		pixel_pred22=8'd128;
		pixel_pred23=8'd128;
		pixel_pred30=8'd128;
		pixel_pred31=8'd128;
		pixel_pred32=8'd128;
		pixel_pred33=8'd128;
	end
end

看起来很长哈哈哈哈。
H.264视频编解码的FPGA源码分析(二)帧内预测1
这个指示信号intra_4x4_ctrl来源于intra_4x4_ctrl模块,官方注释是4x4 block top, left, top_left, top_right available info.是四个信号的组合

assign 	blk_avail 		= {blk_lf_avail,blk_tl_avail,blk_tp_avail,blk_tr_avail};

我觉得可以理解为,如果前两位为1,就取水平和垂直两个方向的均值,如果只有最高位为1,就取水平方向的均值,如果只有次高位为1,就取垂直方向的均值。
呼呼~先码到这里,剩下的写在后面的文章里罗
加油鸭!ヾ(◍°∇°◍)ノ゙

上一篇:c#-确定ARGB范围之间的像素颜色


下一篇:C#位图:“参数无效”,大小不是2的幂