基于FPGA的彩色图转灰度图算法

一、算法基础

对于彩色转灰度,有一个很著名的心理学公式:Gray = R0.299 + G0.587 + B*0.114;
来自于RGB888 to YCbCr的算法公式,我们可以直接把算法移植到FPGA上,但是我们都知道FPGA无法进行浮点运算,所以我们采取将整个式子右端先都扩大256倍,然后再右移8位,这样就得到了FPGA擅长的乘法运算和加法运算了。
基于FPGA的彩色图转灰度图算法_第1张图片

如果是RGB565,需要先将RGB565转化成RGB888,避免精度损失较大:
24bit RGB888 -> 16bit RGB565 的转换(只取高位)

24ibt RGB888 {R7 R6 R5 R4 R3 R2 R1 R0} {G7 G6 G5 G4 G3 G2 G1 G0} {B7 B6 B5 B4 B3 B2 B1 B0}

16bit RGB656 {R7 R6 R5 R4 R3} {G7 G6 G5 G4 G3 G2} {B7 B6 B5 B4 B3}

同样也可以恢复回去。

16bit RGB565 -> 24bit RGB888 的转换(高位补低位)

16bit RGB656 {R4 R3 R2 R1 R0} {G5 G4 G3 G2 G1 G0} {B4 B3 B2 B1 B0}

24ibt RGB888 {R4 R3 R2 R1 R0 R2 R1 R0} {G5 G4 G3 G2 G1 G0 G1 G0} {B4 B3 B2 B1 B0 B2 B1 B0}

使用移位算法来避免浮点运算,并保证算法的精度;
 习惯上使用16位精度,2的16次幂是65536,所以这样计算系数:

 0.299 * 65536 = 19595.264 ≈ 19595;

 0.587 * 65536 + (0.264) = 38469.632 + 0.264 = 38469.896 ≈ 38469;

 0.114 * 65536 + (0.896) = 7471.104 + 0.896 = 7472。

可能很多人看见了,我所使用的舍入方式不是四舍五入。四舍五入会有较大的误差,应该将以前的计算结果的误差一起计算进去,舍入方式是去尾法:

写成表达式是:Gray = (R19595 + G38469 + B*7472) >> 16。

2至20位精度的系数:

  Gray = (R*1 + G*2 + B*1) >> 2

  Gray = (R*2 + G*5 + B*1) >> 3

  Gray = (R*4 + G*10 + B*2) >> 4

  Gray = (R*9 + G*19 + B*4) >> 5

  Gray = (R*19 + G*37 + B*8) >> 6

  Gray = (R*38 + G*75 + B*15) >> 7

  Gray = (R*76 + G*150 + B*30) >> 8

  Gray = (R*153 + G*300 + B*59) >> 9

  Gray = (R*306 + G*601 + B*117) >> 10

  Gray = (R*612 + G*1202 + B*234) >> 11

  Gray = (R*1224 + G*2405 + B*467) >> 12

  Gray = (R*2449 + G*4809 + B*934) >> 13

  Gray = (R*4898 + G*9618 + B*1868) >> 14

  Gray = (R*9797 + G*19235 + B*3736) >> 15

  Gray = (R*19595 + G*38469 + B*7472) >> 16

  Gray = (R*39190 + G*76939 + B*14943) >> 17

  Gray = (R*78381 + G*153878 + B*29885) >> 18

  Gray = (R*156762 + G*307757 + B*59769) >> 19

  Gray = (R*313524 + G*615514 + B*119538) >> 20

仔细观察上面的表格,这些精度实际上是一样的:3与4、7与8、10与11、13与14、19与20。

所以16位运算下最好的计算公式是使用7位精度,比先前那个系数缩放100倍的精度高,而且速度快:Gray = (R38 + G75 + B*15) >> 7。

其实最有意思的还是那个2位精度的,完全可以移位优化:Gray = (R + (WORD)G<<1 + B) >> 2。

二、代码实现

1.FPGA算法实现

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2018/10/22 16:52:47
// Design Name: 
// Module Name: RGB2Gray
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
//     Gray = R*0.299 + G*0.587 + B*0.114
//     Gray = (R*76 + G*150 + B*30) >> 8    使用8位精度来进行运算
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module RGB2Gray
#(
                       parameter  Pixel_Width = 24
)
(
                        input                         I_clk,
                        input                         I_reset_p,
                        input                         I_pixel_data_valid,
                        input  [Pixel_Width-1:0]      I_pixel_data_RGB,//RGB 888 [23-16,15-8,7-0]
                        output reg                    O_pixel_data_valid,
                        output [7:0]                  O_pixel_data_Gray

);

reg  [14:0] R_mult;// 7bit * 8 bit
reg  [15:0] G_mult;//8bit * 8bit
reg  [14:0] B_mult;//5bit * 8bit
reg  [16:0] gray_temp;
reg         pixel_data_valid_d1; 

always@(posedge I_clk)
   begin
      if(I_reset_p)
         begin
            R_mult <= 'h0;
            G_mult <= 'h0;
            B_mult <= 'h0;            
         end
      else
         begin
            R_mult <= I_pixel_data_RGB[Pixel_Width-1-:8] * 76;
            G_mult <= I_pixel_data_RGB[15:8] * 150;
            B_mult <= I_pixel_data_RGB[7:0] * 30;            
         end
   end

always@(posedge I_clk)
   begin
      if(I_reset_p)
         gray_temp <=  'h0;
      else
         gray_temp <= R_mult + G_mult + B_mult;
   end
always@(posedge I_clk)
   begin
      pixel_data_valid_d1 <= I_pixel_data_valid;
      O_pixel_data_valid <= pixel_data_valid_d1;
   end

assign    O_pixel_data_Gray = (gray_temp[16])? 8'hff : gray_temp[15:8];// >> 8 bit
   
endmodule

如果是RGB565,需要先转化为RGB888:,代码如下:

wire [7:0]  RGB_R;
wire [7:0]  RGB_G;
wire [7:0]  RGB_B;

assign  RGB_R = {I_pixel_data_RGB[15:11],I_pixel_data_RGB[13:11]};
assign  RGB_G = {I_pixel_data_RGB[10:5],I_pixel_data_RGB[6:5]};
assign  RGB_B = {I_pixel_data_RGB[4:0],I_pixel_data_RGB[2:0]};

always@(posedge I_clk)
   begin
      if(I_reset_p)
         begin
            R_mult <= 'h0;
            G_mult <= 'h0;
            B_mult <= 'h0;            
         end
      else
         begin
            R_mult <= RGB_R * 76;
            G_mult <= RGB_G * 150;
            B_mult <= RGB_B * 30;            
         end
   end

2.FPGA仿真

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2018/10/22 17:52:24
// Design Name: 
// Module Name: RGB2Gray_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module RGB2Gray_tb(

    );
parameter  Pixel_Width = 24;

reg                           I_clk;
reg                           I_reset_p;
reg                           I_pixel_data_valid;
//reg    [Pixel_Width-1:0]      I_pixel_data_RGB;//RGB 888 [23-16,15-8,7-0]
wire                          O_pixel_data_valid;
wire    [7:0]                 O_pixel_data_Gray;    

reg     [16:0]                addra;
wire    [23:0]                douta;
reg                           start;
integer                      number_file;

RGB2Gray  RGB2Gray_inst(
       .I_clk(I_clk),
       .I_reset_p(I_reset_p),
       .I_pixel_data_valid(I_pixel_data_valid),
       .I_pixel_data_RGB(douta),
       .O_pixel_data_valid(O_pixel_data_valid),
       .O_pixel_data_Gray(O_pixel_data_Gray)

);

image_ram image_ram_inst(
        .clka(I_clk),    // input wire clka
        .ena(1'b1),      // input wire ena
        .wea(1'b0),      // input wire [0 : 0] wea
        .addra(addra),  // input wire [16 : 0] addra
        .dina(),    // input wire [23 : 0] dina
        .douta(douta)  // output wire [23 : 0] douta
        );

initial begin
   I_clk = 0;
   I_reset_p = 1;
   start  = 0;
   #10;
   I_reset_p = 0;
   #20;
   @(posedge I_clk)  start  = 1;
   
end

always #5 I_clk <= ~I_clk;



always@(posedge I_clk)
   begin
      if(I_reset_p)
         addra <= 17'd0;
      else if(start)
         if(addra < 17'd128000)
            addra <= addra + 1;        
   end

always@(posedge I_clk)
   begin
      if(I_reset_p)
         I_pixel_data_valid <= 1'b0;
      else if(start && addra < 17'd128000 )
         I_pixel_data_valid <= 1'b1;
      else
         I_pixel_data_valid <= 1'b0;
   end

//always@(posedge I_clk)
//   begin
//      if(addra == 17'd128000)
//         begin
//            $display("the image RGB TO GRAY is done!!!\n");
//            $display("the cost time is %t",$time);
//            $stop;
//         end
//   end

initial begin
   number_file = $fopen("RGB2Gray.txt","w");
 
   repeat(129000) @(posedge I_clk)
     begin
     if(O_pixel_data_valid)
       begin
          $fwrite(number_file,"%d\n",O_pixel_data_Gray);//注意是$fwrite 而不是$write 
       end
     end
     
     #10

          $display("the image RGB TO GRAY is done!!!\n");
          $display("the cost time is %t",$time);
          $fclose(number_file);//只有$fclose 才可以写进去数据或更改原有数据 

end

//always@(posedge I_clk)
//   begin
//      if(O_pixel_data_valid)
//         begin
//            $fwrite(number_file,"%d\n",O_pixel_data_Gray);//注意是$fwrite 而不是$write 
//         end
//   end    
    
endmodule

3.Matalb生成Xilinx RAM 的coe文件

clear all;
close all;

src = imread('lena640x200.bmp');
gray = rgb2gray(src);

r = src(:,:,1);
g = src(:,:,2);
b = src(:,:,3);
imshow(src);

[m,n,k] = size(src);
N = m*n;           %m行,n列
data_lenth = 8;    %数据位宽

data_r = reshape(r',1,N); %数字矩阵改为1行N列
data_g = reshape(g',1,N);
data_b = reshape(b',1,N);
data_gray = reshape(gray',1,N);

fin = fopen('lena.coe','wt');
fprintf(fin,'MEMORY_INITIALIZATION_RADIX=16;\n');
fprintf(fin,'MEMORY_INITIALIZATION_VECTOR=\n')
for i = 1:N-1
    fprintf(fin,'%x%x%x,\n',data_r(i),data_g(i),data_b(i));
end
fprintf(fin,'%x%x%x;',data_r(N),data_g(N),data_b(N));%最后一行是;结束
fclose(fin);

f_gray = fopen('lena_matlab_gray.txt','wt');
for i = 1:N
   fprintf(f_gray,'%x ',data_gray(i));
end
fclose(f_gray);

4.FPGA算法数据和Matlabch处理数据对比

clear all;
close all;

src_rgb = imread('lena640x200.bmp');
subplot(2,2,1),imshow(src_rgb), title('image-RGB')

gray = rgb2gray(src_rgb);
matlab_gray = rgb2gray(src_rgb);
[m,n] = size(matlab_gray);
subplot(2,2,2),imshow(matlab_gray), title('image-matlab-gray')

RGB2Gray_FPGA = load('RGB2Gray.txt');%verilog 产生的灰度图
RGB2Gray_FPGA_M = reshape(RGB2Gray_FPGA,n,m);%reshape 是一列一列的排列数据的,从左到右
RGB2Gray_FPGA_M = uint8(RGB2Gray_FPGA_M');
subplot(2,2,3),imshow(RGB2Gray_FPGA_M), title('image-RGB2Gray-FPGA')

diff = matlab_gray - RGB2Gray_FPGA_M;
subplot(2,2,4),imshow(diff), title('diff')

index =find(diff);
[n_index,a] = size(index);
fprintf('the diff pixel num is %d\n',n_index);

diff_sub = diff;
N = m*n;
for i = 1:n_index
    if(diff_sub(index(i)) == 1)
        diff_sub(index(i)) = 0;
    end
end
index_sub = find(diff_sub);
[n_index_sub,b] = size(index_sub);
fprintf('the diff_sub pixel num is %d\n',n_index_sub);

5.仿真结果

基于FPGA的彩色图转灰度图算法_第2张图片

你可能感兴趣的:(FPGA,图像处理)