简介
DGain(Digital Gain)模块用于控制图像的整体亮度,分为Sensor端和ISP端。
通常在CMOS Sensor摄像头中会有模拟增益Sensor AGC(Analog Gain Control)和数字增益Sensor DGC(Digital Gain Control)的配置,由对应的相关寄存器进行配置,如下图的OV2686, AMP就是Sensor端的模拟增益,digital gain就是Sensor端的数字增益。
模拟增益是指CMOS Sensor在经过光电转换后是一个非常小的信号,为了便于后续ADC转换成数字信号,会通过一个放大器来放大信号,这个放大器的增益就是模拟增益。
而Sensor端的数字增益是在经过ADC转换后成为数字信号后,对数字信号直接乘以一个增益。有些摄像头只有模拟增益,没有数字增益。
对于模拟增益和数字增益的区别可以参考:
analog_gain(模拟增益) 与digital_gain(数字增益)的区别与联系_模拟增益和数字增益-优快云博客
简单来讲在Sensor端使用使用模拟增益不会放大 ADC 的电路噪声,ADC 的量化噪声,以及 ADC 以后的噪声。所以模拟增益在获得信号增大好处的同时,相比同样的数字增益,系统最终的 Noise 要小,这样 SNR 就更大。
但是在不得不用到数字增益的时候,我们一般也不会使用Sensor端的数字增益,而是在ISP中自己添加一个数字增益。因为ADC出来的数字信号是带有很多噪声的,如果在Sensor端直接使用数字增益输出的RAW图噪声将会非常影响图像质量。
一般的设计是关闭Sensor端数字增益,输出的RAW图经过DPC坏点校正、BLC黑电平校正、以及BNR(Bayer域降噪)后再进行ISP端的数字增益,这样相比在Sensor端的数字增益,ISP端的数字增益输出的图像质量更好。
硬件实现
硬件实现比较简单,对于增益可能是一个浮点数,因此需要对其进行定点量化,这方面参考往期博客:
在本设计中,还在输入添加了一个偏移量。因此公式变为如下:
硬件中对每个输入像素都进行以上变换即可。
需要注意以下几点:
1、乘法结果位宽和加法结果位宽,由于涉及两个无符号数相乘,乘法结果位宽直接定义成两个输入信号位宽之和即可,加法在此基础上再扩展一位。
2、对于乘法的处理,一般FPGA中会将“*”综合成FPGA器件中的DSP资源,如果不想这样实现,则可以使用乘法IP核来计算。
3、对于结果的钳位处理
Verilog源码如下:
/*************************************************************************
> File Name: isp_dgain.v
************************************************************************/
`timescale 1 ns / 1 ps
/*
* ISP - Digital Gain
* O = I * gain + offset
*/
module isp_dgain
#(
parameter BITS = 8,
parameter WIDTH = 1280,
parameter HEIGHT = 960
)
(
input pclk,
input rst_n,
input [7:0] gain, //4.4, 定点为4,小数部分占4位,整数部分占四位
input [BITS-1:0] offset, //偏移
input in_href,
input in_vsync,
input [BITS-1:0] in_raw,
output out_href,
output out_vsync,
output [BITS-1:0] out_raw
);
reg [BITS-1+8:0] data_0; //扩展8位用于暂存乘法中间结果
always @ (posedge pclk or negedge rst_n) begin
if (!rst_n) begin
data_0 <= 0;
end
else begin
data_0 <= in_raw * gain; //*在FPGA中会自动被综合成乘法组合逻辑或者DSP
end
end
reg [BITS-1+9:0] data_1; //再扩展1位用于暂存加法中间结果
always @ (posedge pclk or negedge rst_n) begin
if (!rst_n) begin
data_1 <= 0;
end
else begin
data_1 <= data_0 + {offset, {4'd0}}; //注意是定点数加法
end
end
reg [BITS-1:0] data_2; //对结果进行钳位
always @ (posedge pclk or negedge rst_n) begin
if (!rst_n) begin
data_2 <= 0;
end
else begin
//判断增益后整数部分是否大于位宽最大值,是则输出能表示最大值,否则取整数部分
data_2 <= data_1[BITS-1+9:4] > {BITS{1'b1}} ? {BITS{1'b1}} : data_1[BITS-1+4:4];
end
end
localparam DLY_CLK = 3; //乘法使用一个时钟周期,加法使用一个时钟周期,钳位使用一个时钟周期
reg [DLY_CLK-1:0] href_dly;
reg [DLY_CLK-1:0] vsync_dly;
always @ (posedge pclk or negedge rst_n) begin
if (!rst_n) begin
href_dly <= 0;
vsync_dly <= 0;
end
else begin
href_dly <= {href_dly[DLY_CLK-2:0], in_href};
vsync_dly <= {vsync_dly[DLY_CLK-2:0], in_vsync};
end
end
assign out_href = href_dly[DLY_CLK-1];
assign out_vsync = vsync_dly[DLY_CLK-1];
assign out_raw = out_href ? data_2 : {BITS{1'b0}};
endmodule
参考
开源ISP项目