FPGA ISP 算法模块实现(lsc)

发布时间:2026/10/8 8:13:51
FPGA ISP 算法模块实现(lsc) 目录一、介绍二、增益表格生成三、双线性插值算法四、LSC verilog代码一、介绍由于镜头对不同区域光线的折射率并不均匀中心区域的透光率通常高于边缘区域因此画面四角容易出现亮度下降luma shading有时还会伴随色彩偏移color shading。镜头阴影矫正Lens Shading Correction正是为了消除这类成像缺陷而设计的。校正方式1、预先拍一张均匀光照的灰卡分别生成R、GGrGb共用、B的张增益表格。2、将增益表格存储在ISP内部运行时通过像素坐标查找增益值再将增益值乘以像素值作品为输出。luma shading 会导致图像中间亮边角暗如下图所示二、增益表格生成由于硬件存储资源有限生成增益表格时会将图像划分为网格仅存储格点上的增益数据使用时再通过插值法计算得到每个像素的增益。增益表格生成步骤1、图像网格划分以将图像划分为16*16的网格为例R、Gr、Gb、B四通道数据单独处理每个网格包含sideX列sideY行像素。sideXfloor图像宽度/16sideYfloor图像高度/16。2、提取格点亮度图像被划分为16*16的网格则有17*17的格点。以每个格点为中心向四周取sideX*sideY个像素当触碰到图像上下左右边界时仅取到边界上的像素。以ideX*sideY区域内像素的均值作为该格点的亮度。3、计算 LSC 亮度增益以最中心3*3格个点的平局亮度作为参考值每个格点的增益为参考值/格点亮度。具体的MATLAB代码参考ISP-镜头阴影校正LSC-CSDN博客文章浏览阅读3.2w次点赞30次收藏239次。本文介绍了镜头阴影校正Lens Shading Correction的基本概念及其在图像处理中的应用。包括lumashading和colorshading的形成原因及影响并提供了一种通过计算增益并采用双线性插值的方法来校正镜头阴影的算法。https://blog.csdn.net/xiaoyouck/article/details/77206505?sharetypeblogshareId77206505sharereferAPPsharesourceqq_42744011https://blog.csdn.net/xiaoyouck/article/details/77206505?sharetypeblogshareId77206505sharereferAPPsharesourceqq_42744011https://blog.csdn.net/xiaoyouck/article/details/77206505?sharetypeblogshareId77206505sharereferAPPsharesourceqq_42744011https://blog.csdn.net/xiaoyouck/article/details/77206505?sharetypeblogshareId77206505sharereferAPPsharesourceqq_42744011三、双线性插值算法双线性插值的目的是在已知以下四个格点的坐标及其增益值时Q11坐标为x1y1增益值为 fQ11Q21坐标为x2y1增益值为 fQ21Q12坐标为x1y2增益值为 fQ12Q22坐标为x2y2增益值为 fQ22计算P的增益值。具体计算过程如下1、用x方向的单线性插值计算R1和R2的增益值2、用y方向的单线性插值计算P的增益值对每个网格都建立如上坐标系令 x10x2sideXy10y2sideY其中 x、y 为像素在网格内的相对坐标则最终可以推导出四、LSC verilog代码module lsc #(parameter DATA_WIDTH12,GAIN_WIDTH16,GAIN_ADDR_WIDTH9,sideX64,sideY64,IMG_WIDTH960,IMG_HEIGHT540,mesh_num_X31,mesh_num_Y18) ( input wire clk, input wire rst_n, input wire [DATA_WIDTH-1:0] din, input wire din_vld, input wire [GAIN_WIDTH-1:0] gain1,gain2,gain3,gain4, output reg [DATA_WIDTH-1:0] dout, output reg dout_vld, output wire [GAIN_ADDR_WIDTH-1:0] gain1_addr,gain2_addr,gain3_addr,gain4_addr ); /* 处理图像尺寸1080*1920 划分网格数17*30 增益存储格式16bit定点数据2bit整数位14bit小数位 每个通道使用两个FPGA的Dual port ROM IP 存储LSC增益表格ROM的读取延时为1个clk gain1,gain2,gain3,gain4分别表示左上右上左下右下格点对应的增益值以左上格点为原点。 增益值按x轴正方形存储 */ localparam ONE 512d1; localparam ZERO 512d0; localparam FF 64hFFFF_FFFF_FFFF_FFFF; localparam IMG_WIDTH_CNT_WIDTH $clog2(IMG_WIDTH); localparam IMG_HEIGHT_CNT_WIDTH $clog2(IMG_HEIGHT); localparam sideX_CNT_WIDTH $clog2(sideX); localparam sideY_CNT_WIDTH $clog2(sideY); localparam meshX_WIDTH $clog2(mesh_num_X); localparam meshY_WIDTH $clog2(mesh_num_Y); /* cntWcntH 记录像素在图像上的绝对坐标 cntXcntY 记录像素在网格内的相对坐标 meshXmeshY 记录像素所在网格的左上格点坐标 */ reg [IMG_WIDTH_CNT_WIDTH-1:0] cntW; wire cntW_start,cntW_end; reg [IMG_HEIGHT_CNT_WIDTH-1:0] cntH; wire cntH_start,cntH_end; reg [sideX_CNT_WIDTH-1:0] cntX; wire cntX_start,cntX_end; reg [sideY_CNT_WIDTH-1:0] cntY; wire cntY_start,cntY_end; reg [meshX_WIDTH-1:0] meshX; wire meshX_start,meshX_end; reg [meshY_WIDTH-1:0] meshY; wire meshY_start,meshY_end; assign cntW_start din_vld; assign cntW_end cntW IMG_WIDTH-1; always (posedge clk) begin if(!rst_n) cntWZERO[IMG_WIDTH_CNT_WIDTH-1:0]; else if (cntW_start) if(cntW_end) cntWZERO[IMG_WIDTH_CNT_WIDTH-1:0]; else cntWcntW1b1; end assign cntH_start cntW_end cntW_start; assign cntH_end cntH IMG_HEIGHT-1; always (posedge clk) begin if(!rst_n) cntHZERO[IMG_HEIGHT_CNT_WIDTH-1:0]; else if (cntH_start) if(cntH_end) cntHZERO[IMG_HEIGHT_CNT_WIDTH-1:0]; else cntHcntH1b1; end assign cntX_start din_vld; assign cntX_end cntX sideX-1; always (posedge clk) begin if(!rst_n) cntXZERO[sideX_CNT_WIDTH-1:0]; else if (cntH_start) //最右侧网格像素个数不足sideX cntXZERO[sideX_CNT_WIDTH-1:0]; else if (cntX_start) if(cntX_end) cntXZERO[sideX_CNT_WIDTH-1:0]; else cntXcntX1b1; end assign cntY_start cntH_start; assign cntY_end cntY sideY-1; always (posedge clk) begin if(!rst_n) cntYZERO[sideY_CNT_WIDTH-1:0]; else if (cntH_start cntH_end) //最下方网格像素个数不足sideY cntYZERO[sideY_CNT_WIDTH-1:0]; else if (cntY_start) if(cntY_end) cntYZERO[sideY_CNT_WIDTH-1:0]; else cntYcntY1b1; end assign meshX_start cntX_start cntX_end; always (posedge clk) begin if(!rst_n) meshXZERO[meshX_WIDTH-1:0]; else if (cntH_start) meshXZERO[meshX_WIDTH-1:0]; else if (meshX_start) meshXmeshX1b1; end assign meshY_start cntY_start cntY_end; always (posedge clk) begin if(!rst_n) meshYZERO[meshY_WIDTH-1:0]; else if (cntH_start cntH_end) meshYZERO[meshY_WIDTH-1:0]; else if (meshY_start) meshYmeshY1b1; end //四格点增益地址计算 assign gain1_addr meshX meshY*mesh_num_X; assign gain2_addr meshX meshY*mesh_num_X1; assign gain3_addr meshX (meshY1)*mesh_num_X; assign gain4_addr meshX (meshY1)*mesh_num_X1; //像素插值增益计算 wire [GAIN_WIDTH:0] add_temp1,add_temp2; wire [GAIN_WIDTH1:0] add_temp3; wire [GAIN_WIDTHsideX_CNT_WIDTH:0] mul_temp1; wire [GAIN_WIDTHsideY_CNT_WIDTH:0] mul_temp2; wire [GAIN_WIDTHsideX_CNT_WIDTHsideY_CNT_WIDTH1:0] mul_temp3; wire [GAIN_WIDTH2:0] gain_temp; reg [GAIN_WIDTH-1:0] gain; assign add_temp1 {1b0,gain2}-{1b0,gain1}; assign add_temp2 {1b0,gain4}-{1b0,gain3}; assign add_temp3 {add_temp2[GAIN_WIDTH],add_temp2}-{add_temp1[GAIN_WIDTH],add_temp1}; assign mul_temp1 {{sideX_CNT_WIDTH{add_temp1[GAIN_WIDTH]}},add_temp1}*cntX; assign mul_temp2 {{sideY_CNT_WIDTH{add_temp2[GAIN_WIDTH]}},add_temp2}*cntY; assign mul_temp3 {{(sideX_CNT_WIDTHsideY_CNT_WIDTH){add_temp3[GAIN_WIDTH1]}},add_temp3}*cntX*cntY; assign gain_temp gain1 {{2{mul_temp1[GAIN_WIDTH]}},mul_temp1[GAIN_WIDTHsideX_CNT_WIDTH:sideX_CNT_WIDTH]} {{2{mul_temp2[GAIN_WIDTH]}},mul_temp2[GAIN_WIDTHsideY_CNT_WIDTH:sideY_CNT_WIDTH]} {{mul_temp3[GAIN_WIDTH1]},mul_temp3[GAIN_WIDTHsideX_CNT_WIDTHsideY_CNT_WIDTH1:sideX_CNT_WIDTHsideY_CNT_WIDTH]}; always (posedge clk) begin if(!rst_n) gainZERO[GAIN_WIDTH-1:0]; else if (din_vld) gaingain_temp[GAIN_WIDTH2] ? 16h4000 : |gain_temp[GAIN_WIDTH1:GAIN_WIDTH] ? 16hFFFF : gain_temp[0:GAIN_WIDTH]; /* gain_temp[GAIN_WIDTH2] 1 gain_temp为负值下溢出让增益为1输出原值 gain_temp[GAIN_WIDTH2] 0 gain_temp[GAIN_WIDTH1:GAIN_WIDTH] 不全为0 上溢出 取最大值 */ end //lsc 输出 // 输入数据与插值增益同步 reg [DATA_WIDTH-1:0] din_reg ; reg din_vld_reg ; always (posedge clk) begin if(!rst_n) din_regZERO[DATA_WIDTH-1:0]; else if (din_vld) din_reg din; end always (posedge clk) begin if(!rst_n) din_vld_reg1b0; else din_vld_reg din_vld; end //输出计算 wire [DATA_WIDTHGAIN_WIDTH-1:0] dout_temp; assign dout_temp din_reg * gain;//插值增益出现负值时输出原像素值 always (posedge clk) begin if(!rst_n) doutZERO[DATA_WIDTH-1:0]; else if (din_vld_reg) dout ~(|dout_temp[DATA_WIDTHGAIN_WIDTH-1:DATA_WIDTHGAIN_WIDTH-2]) ? FF[DATA_WIDTH-1:0] : dout_temp[14:DATA_WIDTH]; //结果上溢出时取最大值 end always (posedge clk) begin if(!rst_n) dout_vld1b0; else dout_vld din_vld_reg; end endmodule