目前用到的国产红外探测器普遍均匀度较差、且存在较多坏点,为了不影响最终的成像质量一般都会对探测器输出的图像先进行均匀性矫正和坏点去除。

基本原理

坏点去除原理很简单就是用周围的像素值来代替坏点的像素值。首先需要判断一张图像中坏点的位置,用待标定红外相机拍摄不同温度的黑体图像,坏点对温度的响应明显区别于正常的像素点,将这些点坐标标记出来。坏点替换时一般根据实际情况用3*3或者5*5或者更大的开窗的像素平均值来替换中心坏点的值。在实际操作时用均值替换的效果不如用周围像素的中值替换,因为很多红外探测器坏点喜欢集中出现,一个坏点周围可能还有坏点,用中值替换可以减少周围坏点对替换后效果的影响。

FPGA实现
坏点替换算法和中值滤波或者均值滤波算法很相似,坏点算法仅对标记为坏点的开窗计算均值或中值进行替换,而中值滤波或是均值滤波则对整幅图都计算中值或均值进行替换。所以实现坏点算法的过程和滤波算法极为相似
首先建立3*3开窗的寄存器,将图像视频流用移位寄存器ip核缓存两行,这样三行图像就实现了并行输出了,和当前行输出一起缓存三次就形成了3*3开窗。注意这里缓存行的长度包含了行空闲,如果一行长度超出了移位寄存器(shift ram ip)的最大长度就用两个寄存器。FPGA的图像处理实现基本都建立在开窗基础之上。
f373089bc0c64eab86c70028a4d3abef~noop.image?_iz=58558&from=article.jpg

将坏点标记和对应的图像像素一起缓存,中间的那行的中间像素若标记为1则代表当前开窗为坏点开窗,进行中值或均值替换,若不为1则不进行替换。
1.下面是以中值滤波为基础的坏点替换算法:
开窗建立顶层:
  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2022/07/19 14:08:30
  7. // Design Name:
  8. // Module Name: mid_wave
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module mid_wave(
  22.    input I_clk,
  23.    input I_reset,
  24.    input I_clk_test,
  25.    input I_frame_valid,
  26.    input I_line_valid,
  27.    input[31:0] I_par_Q,
  28.    input[15:0] I_video_in,
  29.    output wire O_frame_valid,
  30.    output wire O_line_valid,
  31.    output wire[15:0] O_video_out,
  32.    output O_hblank,
  33.    output O_vblank
  34.     );
  35.    
  36. reg [18:0] S_temp0_r0;
  37. reg [18:0] S_temp0_r1;
  38. reg [18:0] S_temp0_r2;
  39. reg [18:0] S_temp1_r0;
  40. reg [18:0] S_temp1_r1;
  41. reg [18:0] S_temp1_r2;
  42. reg [18:0] S_temp2_r0;
  43. reg [18:0] S_temp2_r1;
  44. reg [18:0] S_temp2_r2;
  45. wire[18:0] S_fifo0;
  46. wire[18:0] S_fifo1;
  47. wire[18:0] S_fifo0_1;
  48. wire[18:0] S_fifo1_1;
  49. wire[18:0] S_video_in;
  50. wire[15:0] S_video_out;
  51. wire S_blind_sig;
  52. assign S_video_in = {I_frame_valid,I_line_valid,S_blind_sig,I_video_in};
  53. assign O_hblank = (~O_line_valid) & O_frame_valid;
  54. assign O_vblank = ~O_frame_valid;
  55. assign S_blind_sig = I_par_Q[28];
  56. c_shift_ram_0 c_shift_ram_0_0(
  57.     .D(S_video_in),
  58.     .CLK(I_clk),
  59.     .Q(S_fifo0_1)
  60.   );
  61.   
  62. c_shift_ram_0 c_shift_ram_0_1(
  63.     .D(S_fifo0_1),
  64.     .CLK(I_clk),
  65.     .Q(S_fifo0)
  66.   );
  67.   
  68. c_shift_ram_0 c_shift_ram_0_2(
  69.     .D(S_fifo0),
  70.     .CLK(I_clk),
  71.     .Q(S_fifo1_1)
  72.   );
  73.   
  74. c_shift_ram_0 c_shift_ram_0_3(
  75.     .D(S_fifo1_1),
  76.     .CLK(I_clk),
  77.     .Q(S_fifo1)
  78.   );
  79.   
  80. mid_data mid_data_i(
  81.     //System Interfaces
  82.     .sclk(I_clk)                 ,
  83.     .rst_n(I_reset)           ,
  84.     .mat_row1(S_video_in)     ,
  85.     .mat_row2(S_fifo0)        ,
  86.     .mat_row3(S_fifo1)        ,
  87.     .O_frame(O_frame_valid)   ,
  88.     .O_line(O_line_valid)     ,
  89.     .O_data(O_video_out)         
  90. );
  91. ///
  92. endmodule
I_par_Q为坏点标记因为是matlab计算的浮点数所以是32位也可以只用1位来表示坏点。将坏点标记和行同步帧同步一起加到数据的高3位,再一起进行缓存便于最后输出。这里是640*512 16位红外图像,每一行加上行空闲一共有700多个像素时钟周期超出移位寄存器ip最大设置,所以每一行用了2个移位寄存器ip。
中值计算
  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2022/07/19 14:24:10
  7. // Design Name:
  8. // Module Name: mid_data
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module mid_data(
  22.     //System Interfaces
  23.     input                   sclk            ,
  24.     input                   rst_n           ,
  25.     //Communication Interfaces
  26.     input           [18:0]  mat_row1        ,
  27.     input           [18:0]  mat_row2        ,
  28.     input           [18:0]  mat_row3        ,
  29.     output          reg     O_frame         ,
  30.     output            reg        O_line            ,
  31.     output  reg     [15:0]  O_data         
  32. );
  33. //========================================================================================\
  34. //**************Define Parameter and  Internal Signals**********************************
  35. //========================================================================================/
  36. reg                 [18:0]  mat_row1_1      ;
  37. reg                 [18:0]  mat_row2_1      ;
  38. reg                 [18:0]  mat_row3_1      ;
  39. reg                 [18:0]  mat_row1_2      ;
  40. reg                 [18:0]  mat_row2_2      ;
  41. reg                 [18:0]  mat_row3_2      ;
  42.                      
  43. reg                 [18:0]  max_h1          ;
  44. reg                 [18:0]  mid_h1          ;  
  45. reg                 [18:0]  min_h1          ;
  46. reg                 [18:0]  max_h2          ;
  47. reg                 [18:0]  mid_h2          ;  
  48. reg                 [18:0]  min_h2          ;  
  49. reg                 [18:0]  max_h3          ;
  50. reg                 [18:0]  mid_h3          ;  
  51. reg                 [18:0]  min_h3          ;
  52. reg                 [18:0]  min_max         ;
  53. reg                 [18:0]  mid_mid         ;
  54. reg                 [18:0]  max_min         ;      
  55.   
  56. //========================================================================================\
  57. //**************     Main      Code        **********************************
  58. //========================================================================================/
  59. always @(posedge sclk)
  60.     begin
  61.         mat_row1_1          <=          mat_row1;
  62.         mat_row2_1          <=          mat_row2;
  63.         mat_row3_1          <=          mat_row3;
  64.         mat_row1_2          <=          mat_row1_1;
  65.         mat_row2_2          <=          mat_row2_1;
  66.         mat_row3_2          <=          mat_row3_1;
  67.     end
  68.    
  69.    
  70. always @(posedge sclk or negedge rst_n)
  71. begin
  72.     if(rst_n == 1'b0)
  73.         max_h1              <=          18'd0;        
  74.     else if(mat_row1[15:0] >= mat_row1_1[15:0] && mat_row1[15:0] >= mat_row1_2[15:0])
  75.         max_h1              <=          mat_row1;
  76.     else if(mat_row1_1[15:0] >= mat_row1[15:0] && mat_row1_1[15:0] >= mat_row1_2[15:0])
  77.         max_h1              <=          mat_row1_1;
  78.     else
  79.         max_h1              <=          mat_row1_2;
  80. end
  81. always @(posedge sclk or negedge rst_n)
  82. begin
  83.     if(rst_n == 1'b0)
  84.         mid_h1              <=          18'd0;        
  85.     else if((mat_row1[15:0] >= mat_row1_1[15:0] && mat_row1_1[15:0] >= mat_row1_2[15:0]) || (mat_row1_2[15:0] >= mat_row1_1[15:0] && mat_row1_1[15:0] >= mat_row1[15:0]))
  86.         mid_h1              <=          mat_row1_1;
  87.     else if((mat_row1_1[15:0] >= mat_row1[15:0] && mat_row1[15:0] >= mat_row1_2[15:0]) || (mat_row1_2[15:0] >= mat_row1[15:0] && mat_row1[15:0] >= mat_row1_1[15:0]))
  88.         mid_h1              <=          mat_row1;
  89.     else
  90.         mid_h1              <=          mat_row1_2;
  91. end
  92.          
  93. always @(posedge sclk or negedge rst_n)
  94. begin
  95.     if(rst_n == 1'b0)
  96.         min_h1              <=          18'd0;        
  97.     else if(mat_row1[15:0] <= mat_row1_1[15:0] && mat_row1[15:0] <= mat_row1_2[15:0])
  98.         min_h1              <=          mat_row1;
  99.     else if(mat_row1_1[15:0] <= mat_row1[15:0] && mat_row1_1[15:0] <= mat_row1_2[15:0])
  100.         min_h1              <=          mat_row1_1;
  101.     else
  102.         min_h1              <=          mat_row1_2;  
  103. end
  104. always @(posedge sclk or negedge rst_n)
  105. begin
  106.     if(rst_n == 1'b0)
  107.         max_h2              <=          18'd0;        
  108.     else if(mat_row2[15:0] >= mat_row2_1[15:0] && mat_row2[15:0] >= mat_row2_2[15:0])
  109.         max_h2              <=          mat_row2;
  110.     else if(mat_row2_1[15:0] >= mat_row2[15:0] && mat_row2_1[15:0] >= mat_row2_2[15:0])
  111.         max_h2              <=          mat_row2_1;
  112.     else
  113.         max_h2              <=          mat_row2_2;
  114. end
  115. always @(posedge sclk or negedge rst_n)
  116. begin
  117.     if(rst_n == 1'b0)
  118.         mid_h2              <=          18'd0;        
  119.     else if((mat_row2[15:0] >= mat_row2_1[15:0] && mat_row2_1[15:0] >= mat_row2_2[15:0]) || (mat_row2_2[15:0] >= mat_row2_1[15:0] && mat_row2_1[15:0] >= mat_row2[15:0]))
  120.         mid_h2              <=          mat_row2_1;
  121.     else if((mat_row2_1[15:0] >= mat_row2[15:0] && mat_row2[15:0] >= mat_row2_2[15:0]) || (mat_row2_2[15:0] >= mat_row2[15:0] && mat_row2[15:0] >= mat_row2_1[15:0]))
  122.         mid_h2              <=          mat_row2;
  123.     else
  124.         mid_h2              <=          mat_row2_2;
  125. end
  126.          
  127. always @(posedge sclk or negedge rst_n)
  128. begin
  129.     if(rst_n == 1'b0)
  130.         min_h2              <=          18'd0;        
  131.     else if(mat_row2[15:0] <= mat_row2_1[15:0] && mat_row2[15:0] <= mat_row2_2[15:0])
  132.         min_h2              <=          mat_row2;
  133.     else if(mat_row2_1[15:0] <= mat_row2[15:0] && mat_row2_1[15:0] <= mat_row2_2[15:0])
  134.         min_h2              <=          mat_row2_1;
  135.     else
  136.         min_h2              <=          mat_row2_2;  
  137. end
  138. always @(posedge sclk or negedge rst_n)
  139. begin
  140.     if(rst_n == 1'b0)
  141.         max_h3              <=          18'd0;        
  142.     else if(mat_row3[15:0] >= mat_row3_1[15:0] && mat_row3[15:0] >= mat_row3_2[15:0])
  143.         max_h3              <=          mat_row3;
  144.     else if(mat_row3_1[15:0] >= mat_row3[15:0] && mat_row3_1[15:0] >= mat_row3_2[15:0])
  145.         max_h3              <=          mat_row3_1;
  146.     else
  147.         max_h3              <=          mat_row3_2;
  148. end
  149. always @(posedge sclk or negedge rst_n)
  150. begin
  151.     if(rst_n == 1'b0)
  152.         mid_h3              <=          18'd0;        
  153.     else if((mat_row3[15:0] >= mat_row3_1[15:0] && mat_row3_1[15:0] >= mat_row3_2[15:0]) || (mat_row3_2[15:0] >= mat_row3_1[15:0] && mat_row3_1[15:0] >= mat_row3[15:0]))
  154.         mid_h3              <=          mat_row3_1;
  155.     else if((mat_row3_1[15:0] >= mat_row3[15:0] && mat_row3[15:0] >= mat_row3_2[15:0]) || (mat_row3_2[15:0] >= mat_row3[15:0] && mat_row3[15:0] >= mat_row3_1[15:0]))
  156.         mid_h3              <=          mat_row3;
  157.     else
  158.         mid_h3              <=          mat_row3_2;
  159. end
  160.          
  161. always @(posedge sclk or negedge rst_n)
  162. begin
  163.     if(rst_n == 1'b0)
  164.         min_h3              <=          18'd0;        
  165.     else if(mat_row3[15:0] <= mat_row3_1[15:0] && mat_row3[15:0] <= mat_row3_2[15:0])
  166.         min_h3              <=          mat_row3;
  167.     else if(mat_row3_1[15:0] <= mat_row3[15:0] && mat_row3_1[15:0] <= mat_row3_2[15:0])
  168.         min_h3              <=          mat_row3_1;
  169.     else
  170.         min_h3              <=          mat_row3_2;
  171. end
  172. always @(posedge sclk or negedge rst_n)
  173. begin
  174.     if(rst_n == 1'b0)
  175.         min_max             <=          18'd0;
  176.     else if(max_h1[15:0] <= max_h2[15:0] && max_h1[15:0] <= max_h3[15:0])
  177.         min_max             <=          max_h1;
  178.     else if(max_h2[15:0] <= max_h1[15:0] && max_h2[15:0] <= max_h3[15:0])
  179.         min_max             <=          max_h2;
  180.     else
  181.         min_max             <=          max_h3;
  182. end
  183.          
  184.    
  185. always @(posedge sclk or negedge rst_n)
  186. begin
  187.     if(rst_n == 1'b0)
  188.         mid_mid             <=          18'd0;
  189.     else if((mid_h1[15:0] >= mid_h2[15:0] && mid_h2[15:0] >= mid_h3[15:0]) || (mid_h3[15:0] >= mid_h2[15:0] && mid_h2[15:0] >= mid_h1[15:0]))
  190.         mid_mid             <=          mid_h2;
  191.     else if((mid_h2[15:0] >= mid_h1[15:0] && mid_h1[15:0] >= mid_h3[15:0]) || (mid_h3[15:0] >= mid_h1[15:0] && mid_h1[15:0] >= mid_h2[15:0]))
  192.         mid_mid             <=          mid_h1;
  193.     else
  194.         mid_mid             <=          mid_h3;
  195. end
  196. always @(posedge sclk or negedge rst_n)
  197. begin
  198.     if(rst_n == 1'b0)
  199.         max_min             <=          18'd0;
  200.     else if(min_h1[15:0] <= min_h2[15:0] && min_h1[15:0] <= min_h3[15:0])
  201.         max_min             <=          min_h1;
  202.     else if(min_h2[15:0] <= min_h1[15:0] && min_h2[15:0] <= min_h3[15:0])
  203.         max_min             <=          min_h2;
  204.     else
  205.         max_min             <=          min_h3;
  206. end
  207.            
  208. always @(posedge sclk or negedge rst_n)
  209. begin
  210.     if(rst_n == 1'b0)
  211.       begin
  212.         O_frame             <=          1'd0;
  213.         O_line                <=            1'd0;
  214.         O_data              <=          16'd0;
  215.       end
  216.     else if((mid_mid[15:0] >= min_max[15:0] && min_max[15:0] >= max_min[15:0]) || (max_min[15:0] >= min_max[15:0] && min_max[15:0] >= mid_mid[15:0]))
  217.       begin
  218.         O_frame             <=          mat_row2_1[18];
  219.         O_line                <=            mat_row2_1[17];
  220.         if(mat_row2_1[16] == 1)
  221.             O_data              <=          min_max[15:0];
  222.         else
  223.             O_data              <=          mat_row2_1[15:0];
  224.       end
  225.     else if((min_max[15:0] >= mid_mid[15:0] && mid_mid[15:0] >= max_min[15:0]) || (max_min[15:0] >= mid_mid[15:0] && mid_mid[15:0] >= min_max[15:0]))
  226.       begin
  227.         O_frame             <=          mat_row2_1[18];
  228.         O_line                <=            mat_row2_1[17];
  229.         if(mat_row2_1[16] == 1)
  230.             O_data              <=          mid_mid[15:0];
  231.         else
  232.             O_data              <=          mat_row2_1[15:0];
  233.       end
  234.     else
  235.       begin
  236.         O_frame             <=          mat_row2_1[18];
  237.         O_line              <=          mat_row2_1[17];
  238.         if(mat_row2_1[16] == 1)
  239.             O_data              <=          max_min[15:0];
  240.         else
  241.             O_data              <=          mat_row2_1[15:0];
  242.       end
  243. end
  244.          
  245.         
  246. endmodule
以上两个模块构成了完整的坏点替换算法,可以用带有坏点的模拟图像输入至模块顶层即可输出替换后的结果,I_frame_valid为输入帧同步高有效,I_line_valid为输入行同步高有效,I_video_in为像素数据,I_clk为像素同步时钟输入这几个信号就可以得到输出结果。可以防止试试。
2.基于均值滤波的坏点替换模块
  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2022/01/06 10:48:23
  7. // Design Name:
  8. // Module Name: blind_point
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //参考均值滤波法,使用两个深度为1280的19位fifo级联,延时前两行,每行输出端使用两个寄存器暂存前两个图像数据,形成3*3寄存器矩阵
  14. //对3*3寄存器取均值后直接输出,可将行有效帧有效和盲元标志并入像素数据高三位,直接输入,计算结果直接输出不必做状态机,不必管输入的帧有效和行有效
  15. // Dependencies:
  16. //
  17. // Revision:
  18. // Revision 0.01 - File Created
  19. // Additional Comments:
  20. //
  21. //
  22. module blind_point(
  23.    input I_clk,
  24.    input I_reset,
  25.    input I_clk_test,
  26.    input I_frame_valid,
  27.    input I_line_valid,
  28.    input[15:0] I_video_in,
  29.    input[31:0] I_par_Q,
  30.    output wire O_frame_valid,
  31.    output wire O_line_valid,
  32.    output wire[15:0] O_video_out,
  33.    output O_hblank,
  34.    output O_vblank
  35.     );
  36.    
  37. reg [18:0] S_temp0_r0;
  38. reg [18:0] S_temp0_r1;
  39. reg [18:0] S_temp0_r2;
  40. reg [18:0] S_temp1_r0;
  41. reg [18:0] S_temp1_r1;
  42. reg [18:0] S_temp1_r2;
  43. reg [18:0] S_temp2_r0;
  44. reg [18:0] S_temp2_r1;
  45. reg [18:0] S_temp2_r2;
  46. wire S_blind_sig;
  47. wire[18:0] S_fifo0;
  48. wire[18:0] S_fifo1;
  49. wire [18:0] S_fifo0_1;
  50. wire [18:0] S_fifo1_1;
  51. wire [18:0] S_add;
  52. wire [18:0] S_video_in;
  53. wire [15:0] S_result;
  54. wire S_frame_valid, S_line_valid;
  55. wire [15:0] S_video_out;
  56. assign S_blind_sig = I_par_Q[28];
  57. assign S_video_in = {I_frame_valid,I_line_valid,S_blind_sig,I_video_in};
  58. assign O_hblank = (~O_line_valid) & O_frame_valid;
  59. assign O_vblank = ~O_frame_valid;
  60. c_shift_ram_0 c_shift_ram_0_0(
  61.     .D(S_video_in),
  62.     .CLK(I_clk),
  63.     .Q(S_fifo0_1)
  64.   );
  65.   
  66. c_shift_ram_0 c_shift_ram_0_1(
  67.     .D(S_fifo0_1),
  68.     .CLK(I_clk),
  69.     .Q(S_fifo0)
  70.   );
  71.   
  72. c_shift_ram_0 c_shift_ram_0_2(
  73.     .D(S_fifo0),
  74.     .CLK(I_clk),
  75.     .Q(S_fifo1_1)
  76.   );
  77.   
  78. c_shift_ram_0 c_shift_ram_0_3(
  79.     .D(S_fifo1_1),
  80.     .CLK(I_clk),
  81.     .Q(S_fifo1)
  82.   );
  83. ///
  84. always @(posedge I_clk or negedge I_reset)
  85. begin
  86. if(!I_reset)
  87.   begin
  88.    S_temp0_r2 <= 0;
  89.    S_temp0_r1 <= 0;
  90.    S_temp0_r0 <= 0;
  91.   end
  92. else
  93.   begin
  94.    S_temp0_r2 <= S_video_in;
  95.    S_temp0_r1 <= S_temp0_r2;
  96.    S_temp0_r0 <= S_temp0_r1;
  97.   end
  98. end
  99. always @(posedge I_clk or negedge I_reset)
  100. begin
  101. if(!I_reset)
  102.   begin
  103.    S_temp1_r2 <= 0;
  104.    S_temp1_r1 <= 0;
  105.    S_temp1_r0 <= 0;
  106.   end
  107. else
  108.   begin
  109.    S_temp1_r2 <= S_fifo0;
  110.    S_temp1_r1 <= S_temp1_r2;
  111.    S_temp1_r0 <= S_temp1_r1;
  112.   end
  113. end
  114. always @(posedge I_clk or negedge I_reset)
  115. begin
  116. if(!I_reset)
  117.   begin
  118.    S_temp2_r2 <= 0;
  119.    S_temp2_r1 <= 0;
  120.    S_temp2_r0 <= 0;
  121.   end
  122. else
  123.   begin
  124.    S_temp2_r2 <= S_fifo1;
  125.    S_temp2_r1 <= S_temp2_r2;
  126.    S_temp2_r0 <= S_temp2_r1;
  127.   end
  128. end
  129. assign S_add = (S_temp0_r2[15:0] + S_temp0_r1[15:0] + S_temp0_r0[15:0])
  130.                 + (S_temp1_r2[15:0] + S_temp1_r0[15:0])
  131.                 + (S_temp2_r2[15:0] + S_temp2_r1[15:0] + S_temp2_r0[15:0]);
  132. assign S_result =  S_add/8 ;              
  133. assign O_video_out = (S_temp1_r1[16]) ? S_result : S_temp1_r1[15:0];
  134. assign O_frame_valid = S_temp1_r1[18];
  135. assign O_line_valid = S_temp1_r1[17];
  136. endmodule
这个是用均值进行替换的3*3开窗坏点替换,可以直接输入图像试试。

来源:沃爱单片机