HSV色彩空間相對于RGB色彩空間更適合做顏色追蹤,分割顏色等。那么RGB色彩空間要怎么轉變為HSV色彩空間呢。
轉換公式如下:

需要注意的是在OpenCV中為了顯示HSV色彩空間的內容會將H的值除以2,S的值和V的值是要乘以255。
在FPGA中實現RGB轉HSV的話,上面公式中第一步除以255的歸一化是可以不做的。因為在H和S的計算中255可以約掉的,如下所示。而在V的值需要乘以255來顯示,所以也可以不除以255.

如R,G,B為169 152 133的值計算過程如下:

定義如下

在計算中需要使用除法器:

因為8bit的整數,8bit小數的有符號數,所以數據位寬為17bit。
仿真結果如下:

可以看到ref和dut之間有誤差,這個是因為計算過程中采用了8bit的定點小數造成的,如果小數位寬擴大,那么可以減少誤差。
仿真用的計分板:

提供SpinalHDL的源碼:如果需要Verilog源碼可以私聊
import spinal.core._ import spinal.lib._ class Rgb2hsv extends Component { val io = new Bundle { val dataIn = slave(FrameInterface(24)) val dataOut = master(FrameInterface(24)) } val R, G, B = UInt(8 bits) val RMax = Reg(Bool()) init False val GMax = Reg(Bool()) init False val BMax = Reg(Bool()) init False val RMaxDelay = Delay(RMax, 12, init = False) val GMaxDelay = Delay(GMax, 12, init = False) val BMaxDelay = Delay(BMax, 12, init = False) val CMax = Reg(UInt(8 bits)) val CMin = Reg(UInt(8 bits)) val derta = UInt(8 bits) val calcH = new Area { val dertaEq0 = Delay(derta === 0, 12) val G_B = RegNext(((U"1'b0" @@ G).asSInt - (U"1'b0" @@ B).asSInt) @@ S"8'd0") val B_R = RegNext(((U"1'b0" @@ B).asSInt - (U"1'b0" @@ R).asSInt) @@ S"8'd0") val R_G = RegNext(((U"1'b0" @@ R).asSInt - (U"1'b0" @@ G).asSInt) @@ S"8'd0") val div1 = new Div(17, 8) val div2 = new Div(17, 8) val div3 = new Div(17, 8) val G_B_derta = Bits(17 bits) val B_R_derta = Bits(17 bits) val R_G_derta = Bits(17 bits) val G_B_derta_add = Reg(SInt(17 bits)) val B_R_derta_add = Reg(SInt(17 bits)) val R_G_derta_add = Reg(SInt(17 bits)) val G_B_derta_add_mul = Reg(SInt(16 bits)) val B_R_derta_add_mul = Reg(SInt(16 bits)) val R_G_derta_add_mul = Reg(SInt(16 bits)) val H_D = Reg(UInt(9 bits)) val H = Reg(UInt(8 bits)) div1.driverFrom(G_B.asBits, (U"1'b0" @@ derta @@ U"8'b0").asBits, RMax, G_B_derta) div2.driverFrom(B_R.asBits, (U"1'b0" @@ derta @@ U"8'b0").asBits, GMax, B_R_derta) div3.driverFrom(R_G.asBits, (U"1'b0" @@ derta @@ U"8'b0").asBits, BMax, R_G_derta) G_B_derta_add := G_B_derta.asSInt B_R_derta_add := B_R_derta.asSInt + 2 @@ S"8'b0" R_G_derta_add := R_G_derta.asSInt + 4 @@ S"8'b0" val mul60 = AFix.S(8 exp, -8 exp) val afixG_B_derta_add = AFix.S(8 exp, -8 exp) val afixB_R_derta_add = AFix.S(8 exp, -8 exp) val afixR_G_derta_add = AFix.S(8 exp, -8 exp) afixG_B_derta_add := G_B_derta_add afixB_R_derta_add := B_R_derta_add afixR_G_derta_add := R_G_derta_add mul60 := S"9'd60" @@ S"8'd0" G_B_derta_add_mul := (mul60 * afixG_B_derta_add).roundHalfUp(0).asSInt().resized B_R_derta_add_mul := (mul60 * afixB_R_derta_add).roundHalfUp(0).asSInt().resized R_G_derta_add_mul := (mul60 * afixR_G_derta_add).roundHalfUp(0).asSInt().resized // val H = SInt(9 bits) (R, G, B) := io.dataIn.data when(R >= G && R >= B) { CMax := R RMax := True GMax := False BMax := False } elsewhen (G >= R && G >= B) { CMax := G RMax := False GMax := True BMax := False } otherwise { CMax := B RMax := False GMax := False BMax := True } when(R <= G && R <= B) { CMin := R } elsewhen (G <= R && G <= B) { CMin := G } otherwise { CMin := B } derta := CMax - CMin when(dertaEq0) { H_D := 0 } elsewhen (RMaxDelay) { when(G_B_derta_add_mul.sign) { H_D := (G_B_derta_add_mul +^ 360).asUInt.resized } otherwise { H_D := G_B_derta_add_mul(8 downto 0).asUInt.resized } } elsewhen (GMaxDelay) { when(B_R_derta_add_mul.sign) { H_D := (B_R_derta_add_mul + 360).asUInt.resized } otherwise { H_D := B_R_derta_add_mul(8 downto 0).asUInt.resized } } elsewhen (BMaxDelay) { when(R_G_derta_add_mul.sign) { H_D := (R_G_derta_add_mul + 360).asUInt.resized } otherwise { H_D := R_G_derta_add_mul(8 downto 0).asUInt.resized } } H := (H_D(8 downto 1) + H_D(0).asUInt) } val calcS = new Area { val S_Div = Bits(17 bits) val div = new Div(17, 8) div.driverFrom((U"1'd0" @@ derta @@ U"8'd0").asBits, (U"1'd0" @@ CMax @@ U"8'd0").asBits, RegNext(io.dataIn.valid), S_Div) val mul255 = AFix.S(8 exp, -8 exp) val afixS = AFix.S(8 exp, -8 exp) afixS := S_Div.asSInt mul255 := S"9'd255" @@ S"8'd0" val afix_mul = RegNext(afixS * mul255) val afix_mul_r = RegNext(RegNext(afix_mul.roundHalfUp(0)).asUInt()) val S = Reg(UInt(8 bits)) val CMaxEq0 = Delay(CMax === 0, 13) when(CMaxEq0){ S := 0 } otherwise{ S := afix_mul_r(7 downto 0) } } val calcV = new Area { val V = Delay(CMax, 14) } io.dataOut.valid := Delay(io.dataIn.valid, 15, init = False) io.dataOut.data := (calcH.H @@ calcS.S @@ calcV.V).asBits } object Rgb2hsv extends App { SpinalVerilog(new Rgb2hsv) }
審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
FPGA
+關注
關注
1662文章
22480瀏覽量
638471 -
圖像處理
+關注
關注
29文章
1344瀏覽量
59653 -
RGB
+關注
關注
4文章
835瀏覽量
62187 -
HSV
+關注
關注
0文章
10瀏覽量
2840
原文標題:FPGA圖像處理--RGB轉HSV
文章出處:【微信號:FPGA開源工坊,微信公眾號:FPGA開源工坊】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
熱點推薦
FPGA工程師:如何在FPGA中實現狀態機?
安全高效的狀態機設計對于任何使用FPGA的工程師而言都是一項重要技能。選擇Moore狀態機、Mealy狀態機還是混合機取決于整個系統的需求。無論選擇哪種類型的狀態機,充分掌握實現方案所需的工具和技巧,將確保您實現最佳解決方案。本
發表于 03-29 15:02
?1.4w次閱讀
如何在FPGA中實現過零檢測器?
大家好!我想知道如何在FPGA中使用VHDL實現過零檢測器。所以我想實現一個數字常數小數鑒別器。 firt部分提供了雙極性信號,但我想知道如何在
發表于 01-29 08:16
什么是CVSD?其算法分析如何在FPGA中實現?
的不足,同時也方便在現場可編程門陣列(FPGA)中增加一些其他相關的應用功能,因此在FPGA中實現CVSD語音編譯碼調制功能的前景將是非常廣
發表于 08-07 07:04
如何在FPGA中實現實時時鐘或時間和日期計數器
嗨,我是Xilinx FPGA的新手。我該如何在FPGA中實現實時時鐘或時間和日期計數器?Xilinx是否為Artix 7提供任何RTC核心
發表于 05-22 12:41
如何在低端FPGA中實現DPA的功能?
在FPGA中,動態相位調整(DPA)主要是實現LVDS接口接收時對時鐘和數據通道的相位補償,以達到正確接收的目的。那么該如何在低端FPGA
發表于 04-08 06:47
請問在FPGA上怎么實現從RGB轉換到YCbCr?
本文推導出一種適合在FPGA上實現從RGB到YCbCr。顏色空間變換的新算法,采用單片FPGA完成電路設計,利用FPGA內嵌DSP核
發表于 04-29 06:57
低成本FPGA中實現動態相位調整方案
在FPGA中,動態相位調整(DPA)主要是實現LVDS接口接收時對時鐘和數據通道的相位補償,以達到正確接收的目的。ALTERA的高端FPGA,如STRATIX(r) 系列
萊迪思FPGA助力玩視科技(HDCVT)實現SDI轉HDMI解決方案
近日宣布深圳玩視科技有限公司(HDCVT)采用萊迪思FPGA器件提供的豐富高速SERDES資源和靈活的I/O接口,實現雙通道3G SDI轉HDMI/VGA/RGB橋接。
發表于 01-04 12:00
?2275次閱讀
如何利用OpenCV進行顏色分類 rgb和hsv的區別
如果光源不穩定,光照變化較大(存在陰影或者亮斑),則利用HSV通道檢測就比RGB檢測高效得多。光照變化較大時,對RGB三個色道的參數影響都很大,在實際調參過程中會顯得非常麻煩,而且效果不理想。
發表于 08-07 09:52
?2422次閱讀
如何在FPGA中實現狀態機
在FPGA(現場可編程門陣列)中實現狀態機是一種常見的做法,用于控制復雜的數字系統行為。狀態機能夠根據當前的輸入和系統狀態,決定下一步的動作和新的狀態。這里,我們將詳細探討如何在
如何在FPGA中實現RGB轉HSV
評論