RGB转Gray算法实现

主要介绍3种方法:

方法1:求平均法

原则上的灰度,就是让R=G=B,那顾名思义,可以直接求平均,如下:

gray = (R+G+B)/3

这里有除法,这里将3改为256,这里公式变为gray =(R+G+B)*85>>8。图像显示时用(gray,gray,gray)替代(R,G,B)即可。

(注:为什么将256替换3?在FPGA实现中,除法一般使用移位进行替换,适用于2的指数次方)。

不过这样会导致图像质量不好,这是必然的,因为也许图像的分量不均匀,再者肉眼对色彩的敏感程度也是不一样的。

方法2:典型灰度转换公式

对于彩色转灰度,有一个很著名的心理学公式:

gray = R*0.299 + G*0.587 + B*0.114

在此0.299+0.587+0.144=1,刚好是满偏,这是通过不同的敏感度以及经验总结出来的公式,一般可以直接用这个。而实际应用时,希望避免低速的浮点运算,所以需要整数算法。注意到系数都是小数点后3位,我们可以将它们缩放1000倍来实现整数运算算法,如下

gray = (R*299 + G*587 + B*114) / 1000

但是除法就是不爽,为了能在后续实现移位,将1000扩展到1024,得到式子如下:

gray = (R* 306 + G*601 + B*117) / 1024=(R* 306 + G*601 + B*117) >>10

适当的还可以在精简,压缩到8位以内,现在变成这样子:

gray = (R*75 + G*147 + B*36) >>8

方法3:查找表方法

到目前为止,整数算法已经很快了,但是完美是没有极限的,其实是可以更快的,观察原始式子

gray = R*0.299 + G*0.587 + B*0.114

每一通道数据乘以一个常数,这三个变量可以提前算好,保存在ROM,这样就只是查找表的时间了。同样为了避免浮点,将式子变为

gray = (R*75 + G*147 + B*36) >>8

方法1实现:
该方法实现起来比较简单,这里乘法也通过移位的方式进行计算,具体实现见下面代码:

1  module RGB2Gray(
2       clk,
3       rst_n,
4       rgb_inen,
5       red,
6       green,
7       blue,
8       gray,
9       gray_outen
10  );
11      input clk;         //时钟
12      input rst_n;       //异步复位
13      input rgb_inen;    //rgb输入有效标识
14      input [7:0]red;    //R输入
15      input [7:0]green;  //G输入
16      input [7:0]blue;   //B输入
17      output [7:0]gray;  //GRAY输出
18      output reg gray_outen; //gray输出有效标识
19      
20  //求平均法GRAY = (R+B+G)/3=((R+B+G)*85)>>8
21      wire [9:0]sum;
22      reg [15:0]gray_r;
23      
24      assign sum = red + green + blue;
25      
26      always@(posedge clk or negedge rst_n)
27      begin
28          if(!rst_n)
29              gray_r <= 16'd0;
30          else if(rgb_inen)
31              gray_r <= (sum<<6) + (sum<<4) + (sum<<2) + sum;
32          else
33              gray_r <= 16'd0;
34      end
35      
36      assign gray = gray_r[15:8];
37      
38      always@(posedge clk or negedge rst_n)
39      begin
40          if(!rst_n)
41              gray_outen <= 1'b0;
42          else if(rgb_inen)
43              gray_outen <= 1'b1;
44          else
45              gray_outen <= 1'b0;
46      end
47  
48  endmodule 

方法2实现:

1   module RGB2Gray(
2       clk,
3       rst_n,
4       rgb_inen,
5       red,
6       green,
7       blue,
8       gray,
9       gray_outen
10  );
11      input clk;         //时钟
12      input rst_n;       //异步复位
13      input rgb_inen;    //rgb输入有效标识
14      input [7:0]red;    //R输入
15      input [7:0]green;  //G输入
16      input [7:0]blue;   //B输入
17      output [7:0]gray;  //GRAY输出
18      output reg gray_outen; //gray输出有效标识
19      
20  //典型灰度转换公式Gray = R*0.299+G*0.587+B*0.114=(R*75 + G*147 + B*36) >>8
21      wire [15:0]w_R;
22      wire [15:0]w_G;
23      wire [15:0]w_B;
24      reg [17:0]sum;
25
26      assign w_R = {red,6'b000000} + {red,3'b000} + {red,1'b0} + red;
27      assign w_G = {green,7'b0000000} + {green,4'b0000} + {green,1'b0} + green;
28      assign w_B = {blue,5'b00000} + {blue,2'b00};
29
30      always@(posedge clk or negedge rst_n)
31      begin
32          if(!rst_n)
33              sum <= 18'd0;
34          else if(rgb_inen)
35              sum <= w_R + w_G + w_B;
36          else
37              sum <= 18'd0;
38      end
39      
40      assign gray = sum[15:8];
41
42      always@(posedge clk or negedge rst_n)
43      begin
44          if(!rst_n)
45              gray_outen <= 1'b0;
46          else if(rgb_inen)
47              gray_outen <= 1'b1;
48          else
49              gray_outen <= 1'b0;
50      end
51  
52  endmodule

该方法在实现过程中遇到一个错误,通过仿真方式很容易的发现并进行了改正,发生错误的地方是在计算w_Rw_Gw_B的地方,错误代码如下:

assign w_R = red<<6 + red<<3 + red<<1 + red;
assign w_G = green<<7 + green<<4 + green<<1 + green;
assign w_B = blue<<5 + blue<<2;

在等式左边计算过程中由于redgreenblue是位宽8bit的数,在移位后数据高位部分就丢失掉了,比如,green<<7得到的是{green[0],7'b0000000},并非我期望的green乘以27次方。后来改为如下正确的代码后就可以得到正确的结果了。

assign w_R = {red,6'b000000} + {red,3'b000} + {red,1'b0} + red;
assign w_G = {green,7'b0000000} + {green,4'b0000} + {green,1'b0} + green;
assign w_B = {blue,5'b00000} + {blue,2'b00};

 方法3实现:

该方法主要的优势是速度块,但占用的存储器会更多,因为需要将RGB乘以系数之后的数值存储在ROM中,然后通过读取ROM方式来得到计算之后的数值。这里使用全quartusII软件添加3ROMIP核,分别对R*75G*147B*360≤R≤2550≤G≤2550≤B≤255)建立3mif文件,然后在ROM IP核中分别添加mif文件进行初始化。具体代码如下:代码中ROM_RROM_GROM_B分别存储着R*75G*147B*360≤R≤2550≤G≤2550≤B≤255256个数值。

1   module RGB2Gray(
2       clk,
3       rst_n,
4       rgb_inen,
5       red,
6       green,
7       blue,
8       gray,
9       gray_outen
10  );
11      input clk;         //时钟
12      input rst_n;       //异步复位
13      input rgb_inen;    //rgb输入有效标识
14      input [7:0]red;    //R输入
15      input [7:0]green;  //G输入
16      input [7:0]blue;   //B输入
17      output [7:0]gray;  //GRAY输出
18      output reg gray_outen; //gray输出有效标识
19      
20  //查找表方式,可以省去乘法运算Gray =(R*75 + G*147 + B*36) >>8,将3个分量乘以系数后的数值存储在ROM中
21  wire [14:0]w_R;
22  wire [15:0]w_G;
23  wire [13:0]w_B;
24  
25  reg [17:0]sum;
26  reg [1:0]r_gray_outen;
27  
28  ROM_R ROM_R(
29      .address(red),
30      .clock(clk),
31      .rden(rgb_inen),
32      .q(w_R)
33  );
34      
35  ROM_G ROM_G(
36      .address(green),
37      .clock(clk),
38      .rden(rgb_inen),
39      .q(w_G)
40  );
41  
42  ROM_B ROM_B(
43      .address(blue),
44      .clock(clk),
45      .rden(rgb_inen),
46      .q(w_B)
47  );
48  
49  always@(posedge clk)
50      {gray_outen,r_gray_outen} <= {r_gray_outen,rgb_inen};
51  
52  always@(posedge clk or negedge rst_n)
53  begin
54      if(!rst_n)
55          sum <= 18'd0;
56      else if(r_gray_outen[1])
57          sum <= w_R + w_G + w_B;
58      else
59          sum <= 18'd0;
60  end
61  
62  assign gray = sum[15:8];
63  
64  endmodule 

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值