昨天才更新了兩篇博客,今天又要更新了,並不是我垃圾產,只不過這些在上個月就已經寫好了,只是因為比賽忙,一直騰不出時間整理出來發表而已,但是做完一件事情總感覺不寫一博文總結一下就少點什么,所以之后的一段時間里我會把我這學期學到的一些東西陸續整理出來發表,給自己一個總結交代。
將彩色圖像轉化為灰度的方法有兩種,一個是令RGB三個分量的數值相等,輸出后便可以得到灰度圖像,另一種是轉化為YCbCr格式,將Y分量提取出來,YCbCr格式中的Y分量表示的是圖像的亮度和濃度所以只輸出Y分量,得到的圖像就是灰度圖像了。我在這里選擇第二種方法實現。
YCBCr是通過有序的三元組來表示的,三元由Y(Luminance)、Cb(Chrominance-Blue)和Cr(Chrominance-Red)組成,其中Y表示顏色的明亮度和濃度,而Cb和Cr則分別表示顏色的藍色濃度偏移量和紅色濃度偏移量。人的肉眼對由YCbCr色彩空間編碼的視頻中的Y分量更敏感,而Cb和Cr的微小變化不會引起視覺上的不同,根據該原理,通過對Cb和Cr進行子采樣來減小圖像的數據量,使得圖像對存儲需求和傳輸帶寬的要求大大降低,從而達到在完成圖像壓縮的同時也保證了視覺上幾乎沒有損失的效果,進而使得圖像的傳輸速度更快,存儲更加方便。我們要的到灰度圖像,首先要將采集到的彩色圖像轉化為YCbCr。
我們配置攝像頭采集到的數據是RGB565的格式,官方給出的轉化公式是RGB888->YCbCr,所以先需要將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}
1 //-------------------------------------------- 2 //RGB565 to RGB 888 3 wire [7:0] cmos_R0; 4 wire [7:0] cmos_G0; 5 wire [7:0] cmos_B0; 6 7 assign cmos_R0 = {cmos_R, cmos_R[4:2]}; 8 assign cmos_G0 = {cmos_G, cmos_G[5:4]}; 9 assign cmos_B0 = {cmos_B, cmos_B[4:2]};
采用高位補低位的方法直接轉化即可。
這是官方給的RGB888 to YCbCr的算法公式,我們可以直接把算法移植到FPGA上,但是我們都知道FPGA無法進行浮點運算,所以我們采取將整個式子右端先都擴大256倍,然后再右移8位,這樣就得到了FPGA擅長的乘法運算和加法運算了。
這個計算式子看起來是十分簡單的,但是要是直接用Verilog直接寫出來,那么只能說,這個人的代碼寫的一塌糊塗,所以這里就引出FPGA中流水線的設計思想。
在這里我們選擇加3級流水線,就第一個Y分量而言,先計算括號中得乘法運算,消耗一個時鍾,然后將括號中的數據求和,消耗一個時鍾,這里為了計算方便,將128也擴大256倍,放到括號中,最終結果除以256就行了也就是右移8位,在FPGA中我們只需要舍棄低8位取高8位就行。具體代碼如下

1 //-------------------------------------------- 2 /*Refer to <OV7725 Camera Module Software Applicaton Note> page 5 3 Y = (77 *R + 150*G + 29 *B)>>8 4 Cb = (-43*R - 85 *G + 128*B)>>8 + 128 5 Cr = (128*R - 107*G - 21 *B)>>8 + 128 6 ---> 7 Y = (77 *R + 150*G + 29 *B)>>8 8 Cb = (-43*R - 85 *G + 128*B + 32768)>>8 9 Cr = (128*R - 107*G - 21 *B + 32768)>>8*/ 10 //-------------------------------------------- 11 //RGB888 to YCrCb 12 //step1 conmuse 1clk 13 reg [15:0] cmos_R1, cmos_R2, cmos_R3; 14 reg [15:0] cmos_G1, cmos_G2, cmos_G3; 15 reg [15:0] cmos_B1, cmos_B2, cmos_B3; 16 always @(posedge clk or negedge rst_n) 17 begin 18 if(!rst_n)begin 19 cmos_R1 <= 16'd0; 20 cmos_G1 <= 16'd0; 21 cmos_B1 <= 16'd0; 22 cmos_R2 <= 16'd0; 23 cmos_G2 <= 16'd0; 24 cmos_B2 <= 16'd0; 25 cmos_R3 <= 16'd0; 26 cmos_G3 <= 16'd0; 27 cmos_B3 <= 16'd0; 28 end 29 else begin 30 cmos_R1 <= cmos_R0 * 8'd77; 31 cmos_G1 <= cmos_G0 * 8'd150; 32 cmos_B1 <= cmos_B0 * 8'd29; 33 cmos_R2 <= cmos_R0 * 8'd43; 34 cmos_G2 <= cmos_G0 * 8'd85; 35 cmos_B2 <= cmos_B0 * 8'd128; 36 cmos_R3 <= cmos_R0 * 8'd128; 37 cmos_G3 <= cmos_G0 * 8'd107; 38 cmos_B3 <= cmos_B0 * 8'd21; 39 end 40 end 41 42 //----------------------------------------------- 43 //step2 consume 1clk 44 reg [15:0] img_Y0; 45 reg [15:0] img_Cb0; 46 reg [15:0] img_Cr0; 47 48 always @(posedge clk or negedge rst_n) 49 begin 50 if(!rst_n)begin 51 img_Y0 <= 16'd0; 52 img_Cb0 <= 16'd0; 53 img_Cr0 <= 16'd0; 54 end 55 else begin 56 img_Y0 <= cmos_R1 + cmos_G1 + cmos_B1; 57 img_Cb0 <= cmos_B2 - cmos_R2 - cmos_G2 + 16'd32768; 58 img_Cr0 <= cmos_R3 - cmos_G3 - cmos_B3 + 16'd32768; 59 end 60 61 end 62 //------------------------------------------- 63 //step3 conmuse 1clk 64 reg [7:0] img_Y1; 65 reg [7:0] img_Cb1; 66 reg [7:0] img_Cr1; 67 68 always @(posedge clk or negedge rst_n) 69 begin 70 if(!rst_n)begin 71 img_Y1 <= 8'd0; 72 img_Cb1 <= 8'd0; 73 img_Cr1 <= 8'd0; 74 end 75 else begin 76 img_Y1 <= img_Y0 [15:8]; 77 img_Cb1 <= img_Cb0 [15:8]; 78 img_Cr1 <= img_Cr0 [15:8]; 79 end 80 81 end
對於流水線的理論詳細解釋,請看我另一篇博文:http://www.cnblogs.com/ninghechuan/p/6970750.html
將RGB565—>YCbCr成功后,提取出Y的值輸出,就可以得到灰度色彩的圖像了。
將采集到的RGB565的像素數據,輸入到算法處理模塊進行操作,由RGB565——>YCbCr——Gray官方給出的公式來算,先將RGB565拆分開R G B三個分量,使用如上公式計算的到Y Cb Cr是三個分量。
RGB轉YCbCr算法的仿真過程,從圖中可以看出,加了流水線后的運算過程,每一級運算相差一個時鍾,然而每一級都在進行新的運算,我們加了3級流水線,這樣運算速度可以提升3倍。
將行信號、場信號和像素數據使能信號進行延時,消耗多少周期便延時幾個周期,保持時鍾的同步性。
最后將Y分量的數據輸出,進行位拼接,16位的RGB565像素R、G、B分量分別對應的取Y分量的高位,最后的輸出顯示出來就是灰度圖像了。
1 assign lcd_data = {per_img_Y1 [7:3],per_img_Y1 [7:2],per_img_Y1 [7:3]};//Gray
這里還是采用高位補低位的方法。
最終下載到FPGA開發板上,顯示如下:
圖上為灰度顯示,圖下為轉化前的原圖,可以看出,灰度最終顯示的是將彩色圖像的顏色過濾掉,這樣減小了圖像的體積,濾掉多余的影響因素,為后面的濾波,邊緣檢測等圖像處理做出關鍵性的基礎。
轉載請注明出處:NingHeChuan(寧河川)
個人微信訂閱號:開源FPGANingHeChuan
如果你想及時收到個人撰寫的博文推送,可以掃描左邊二維碼(或者長按識別二維碼)關注個人微信訂閱號
知乎ID:NingHeChuan
微博ID:NingHeChuan