一、算法介绍
Demosaic 之后,每个像素点已经拥有了 R、G、B 三个通道的数值。然而,由于人眼对红、绿、蓝三色的感知响应曲线,与 CMOS 传感器实际的光电转换特性存在天然差异,此时得到的 RGB 数据并不能直接等同于我们期望的"标准色彩"。
CCM(Color Correction Matrix,色彩校正矩阵)的作用,正是通过线性变换,将传感器捕获的 RGB 数值映射至标准色彩空间(如 sRGB),从而补偿感知曲线与感光曲线之间的偏差,使最终呈现的色彩更贴近人眼的真实观感。
CCM 本质上是一个 3×3 的线性变换矩阵,其核心运算为矩阵乘法。即,由一个 3×3 的色彩校正矩阵与图形按 R、G、B 三个通道扁平化后的3×N的矩阵(N 为像素总数)相乘。经过校正后的R、G、B如下:
输出R = ccm[0][0]×输入R + ccm[0][1]×输入G + ccm[0][2]×输入B
输出G = ccm[1][0]×输入R + ccm[1][1]×输入G + ccm[1][2]×输入B
输出B = ccm[2][0]×输入R + ccm[2][1]×输入G + ccm[2][2]×输入B
二、verilog代码
module ccm # (parameter DATAWIDTH=12) (
input wire clk,
input wire rst_n,
//进过去马赛克处理后,每个像素的R/G/B数据并行输出
input wire [DATAWIDTH-1:0] Rdin,
input wire [DATAWIDTH-1:0] Gdin,
input wire [DATAWIDTH-1:0] Bdin,
input wire din_vld,
output reg [DATAWIDTH-1:0] Rdout,
output reg [DATAWIDTH-1:0] Gdout,
output reg [DATAWIDTH-1:0] Bdout,
output reg dout_vld
);
localparam ONE=512'd1;
localparam ZERO=512'd0;
localparam FF=64'hFFFF_FFFF_FFFF_FFFF;
/*
ccm矩阵:
0.4376 -0.0678 0.0439
-0.2061 0.3193 -0.25
0.0327 -0.0059 0.4561
将ccm矩阵定点化为1bit符号位+12bit小数位的数据
*/
parameter ccm00=13'b0_0110_1110_1100,ccm01=13'b1_1110_1111_1100,ccm02=13'b0_0000_1011_0100,
ccm10=13'b1_1100_1011_0100,ccm11=13'b0_0101_0001_1100,ccm12=13'b1_1100_0000_0000,
ccm20=13'b0_0000_1000_0110,ccm21=13'b1_1111_1110_1000,ccm22=13'b0_0111_0100_1100;
reg [DATAWIDTH+12:0] Rmul0,Rmul1,Rmul2,
Gmul0,Gmul1,Gmul2,
Bmul0,Bmul1,Bmul2;
reg mul_vld;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
Rmul0 <= ZERO[DATAWIDTH+12:0];
Rmul1 <= ZERO[DATAWIDTH+12:0];
Rmul2 <= ZERO[DATAWIDTH+12:0];
Gmul0 <= ZERO[DATAWIDTH+12:0];
Gmul1 <= ZERO[DATAWIDTH+12:0];
Gmul2 <= ZERO[DATAWIDTH+12:0];
Bmul0 <= ZERO[DATAWIDTH+12:0];
Bmul1 <= ZERO[DATAWIDTH+12:0];
Bmul2 <= ZERO[DATAWIDTH+12:0];
end
else if (din_vld) begin
Rmul0 <= ccm00*Rdin;
Rmul1 <= ccm01*Gdin;
Rmul2 <= ccm02*Bdin;
Gmul0 <= ccm10*Rdin;
Gmul1 <= ccm11*Gdin;
Gmul2 <= ccm12*Bdin;
Bmul0 <= ccm20*Rdin;
Bmul1 <= ccm21*Gdin;
Bmul2 <= ccm22*Bdin;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
mul_vld <= 1'b0;
else
mul_vld <= din_vld;
end
wire [DATAWIDTH+14:0] Rdout_temp,Gdout_temp,Bdout_temp;
assign Rdout_temp = Rmul0 + Rmul1 + Rmul2;
assign Gdout_temp = Gmul0 + Gmul1 + Gmul2;
assign Bdout_temp = Bmul0 + Bmul1 + Bmul2;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
Rdout <= ZERO[DATAWIDTH-1:0];
Gdout <= ZERO[DATAWIDTH-1:0];
Bdout <= ZERO[DATAWIDTH-1:0];
end
else if (mul_vld) begin
//结果做防溢出处理
Rdout <= Rdout_temp[DATAWIDTH+14] ? ZERO[DATAWIDTH-1:0] : Rdout_temp[DATAWIDTH+13] ? FF[DATAWIDTH-1:0] : Rdout_temp[DATAWIDTH+12:0];
Gdout <= Gdout_temp[DATAWIDTH+14] ? ZERO[DATAWIDTH-1:0] : Gdout_temp[DATAWIDTH+13] ? FF[DATAWIDTH-1:0] : Gdout_temp[DATAWIDTH+12:0];
Bdout <= Bdout_temp[DATAWIDTH+14] ? ZERO[DATAWIDTH-1:0] : Bdout_temp[DATAWIDTH+13] ? FF[DATAWIDTH-1:0] : Bdout_temp[DATAWIDTH+12:0];
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
dout_vld <= 1'b0;
else
dout_vld <= mul_vld;
end
endmodule

