YUV444 转 YUV422 的算法原理与 Verilog 实现
1. 格式差异
-
YUV444:每个像素独立存储 Y、U、V 分量(24bpp,顺序通常为
Y[23:16], U[15:8], V[7:0]
)。 -
YUV422:水平相邻的两个像素共享 U、V 分量(16bpp,以
YUYV
格式为例,数据排列为Y0, U0, Y1, V0
)。
2. 转换算法
核心是 色度分量下采样。常用两种方法:
-
简单丢弃法(低复杂度)
丢弃偶数像素的 U、V,保留奇数像素的 UV 作为共用值:text
-
Pixel0 (Y0, U0, V0) → 输出 {Y0, U0} Pixel1 (Y1, U1, V1) → 输出 {Y1, V0} // 共享 Pixel0 的 V0
-
平均值法(质量更优)
计算两个像素 UV 的平均值:text
-
U_avg = (U0 + U1) >> 1 V_avg = (V0 + V1) >> 1 输出序列:{Y0, U_avg}, {Y1, V_avg}
Verilog 实现(简单丢弃法)
设计要点
-
输入:每周期 24-bit YUV444 数据(
data_in
)。 -
输出:每周期 16-bit YUV422 数据(
data_out
)。 -
时序:用
flip
标志区分奇数/偶数像素,寄存奇数像素的V
分量。
verilog
module yuv444_to_yuv422 ( input wire clk, // 时钟 input wire reset, // 复位 input wire [23:0] data_in, // 输入数据 (Y[23:16], U[15:8], V[7:0]) input wire valid_in, // 输入有效标志 output reg [15:0] data_out, // 输出数据 (YUV422, 16-bit) output reg valid_out // 输出有效标志 ); reg flip; // 像素位置标志 (0: 奇数像素, 1: 偶数像素) reg [7:0] reg_V; // 寄存奇数像素的 V 分量 always @(posedge clk) begin if (reset) begin flip <= 0; reg_V <= 0; data_out <= 0; valid_out <= 0; end else begin valid_out <= valid_in; // 输出有效与输入同步 if (valid_in) begin if (flip == 0) begin // 奇数像素: 输出 {Y, U}, 寄存 V data_out <= {data_in[23:16], data_in[15:8]}; // {Y0, U0} reg_V <= data_in[7:0]; // 保存 V0 flip <= 1; // 下一周期为偶数像素 end else begin // 偶数像素: 输出 {Y, 寄存的 V} (共享前一个像素的 V) data_out <= {data_in[23:16], reg_V}; // {Y1, V0} flip <= 0; // 下一周期为奇数像素 end end end end endmodule
时序说明
-
周期1(奇数像素):
输入{Y0, U0, V0}
→ 输出{Y0, U0}
,寄存V0
。 -
周期2(偶数像素):
输入{Y1, U1, V1}
→ 输出{Y1, V0}
(丢弃U1/V1
)。 -
输出流格式:
{Y0, U0}
,{Y1, V0}
,{Y2, U2}
,{Y3, V2}
, ...
Verilog 实现(平均值法)
设计要点
-
需缓存第一个像素的 YUV。
-
在第二个像素到来时计算 UV 平均值。
verilog
module yuv444_to_yuv422_avg ( input wire clk, input wire reset, input wire [23:0] data_in, input wire valid_in, output reg [15:0] data_out, output reg valid_out ); reg state; // 状态 (0: 等待第一个像素, 1: 等待第二个像素) reg [23:0] reg_pixel0; // 缓存第一个像素 (Y0, U0, V0) always @(posedge clk) begin if (reset) begin state <= 0; reg_pixel0 <= 0; data_out <= 0; valid_out <= 0; end else begin valid_out <= 0; // 默认输出无效 if (valid_in) begin if (state == 0) begin // 状态0: 缓存第一个像素,不输出 reg_pixel0 <= data_in; state <= 1; end else begin // 状态1: 计算平均值并输出两个16-bit数据 reg [7:0] U_avg = (reg_pixel0[15:8] + data_in[15:8]) >> 1; reg [7:0] V_avg = (reg_pixel0[7:0] + data_in[7:0]) >> 1; // 输出第一个16-bit: {Y0, U_avg} data_out <= {reg_pixel0[23:16], U_avg}; valid_out <= 1; // 标记有效 // 下一周期输出第二个16-bit: {Y1, V_avg} reg_pixel0 <= {data_in[23:16], 8'd0, V_avg}; // 暂存 Y1 和 V_avg state <= 0; // 下一周期返回状态0 end end end end endmodule
时序说明
-
周期1(状态0):
缓存 Pixel0{Y0, U0, V0}
,无输出。 -
周期2(状态1):
输入 Pixel1{Y1, U1, V1}
:-
计算
U_avg = (U0 + U1)/2
,V_avg = (V0 + V1)/2
。 -
输出
{Y0, U_avg}
。 -
缓存
Y1
和V_avg
,下一周期输出。
-
-
周期3(状态0):
输出{Y1, V_avg}
,并准备缓存新像素。
注意:平均值法输出延迟较大(首像素需2周期),但色度信息保留更完整。
选择建议
-
简单丢弃法:适合对实时性要求高、资源受限的场景。
-
平均值法:适合对图像质量要求高的场景(如视频处理)。
可根据实际需求调整实现,例如支持其他YUV422子格式(如UYVY
)。