主要介紹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_R、w_G、w_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;
在等式左邊計算過程中由於red、green、blue是位寬8bit的數,在移位後數據高位部分就丟失掉了,比如,green<<7得到的是{green[0],7'b0000000},並非我期望的green乘以2的7次方。後來改爲如下正確的代碼後就可以得到正確的結果了。
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實現:
該方法主要的優勢是速度塊,但佔用的存儲器會更多,因爲需要將R、G、B乘以係數之後的數值存儲在ROM中,然後通過讀取ROM方式來得到計算之後的數值。這裏使用全quartusII軟件添加3個ROMIP核,分別對R*75、G*147、B*36(0≤R≤255,0≤G≤255,0≤B≤255)建立3個mif文件,然後在ROM IP核中分別添加mif文件進行初始化。具體代碼如下:代碼中ROM_R、ROM_G、ROM_B分別存儲着R*75、G*147、B*36(0≤R≤255,0≤G≤255,0≤B≤255)256個數值。
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