HSV色彩空間相對(duì)于RGB色彩空間更適合做顏色追蹤,分割顏色等。那么RGB色彩空間要怎么轉(zhuǎn)變?yōu)镠SV色彩空間呢。
轉(zhuǎn)換公式如下:
需要注意的是在OpenCV中為了顯示HSV色彩空間的內(nèi)容會(huì)將H的值除以2,S的值和V的值是要乘以255。
在FPGA中實(shí)現(xiàn)RGB轉(zhuǎn)HSV的話(huà),上面公式中第一步除以255的歸一化是可以不做的。因?yàn)樵贖和S的計(jì)算中255可以約掉的,如下所示。而在V的值需要乘以255來(lái)顯示,所以也可以不除以255.
如R,G,B為169 152 133的值計(jì)算過(guò)程如下:
定義如下
在計(jì)算中需要使用除法器:
因?yàn)?bit的整數(shù),8bit小數(shù)的有符號(hào)數(shù),所以數(shù)據(jù)位寬為17bit。
仿真結(jié)果如下:
可以看到ref和dut之間有誤差,這個(gè)是因?yàn)橛?jì)算過(guò)程中采用了8bit的定點(diǎn)小數(shù)造成的,如果小數(shù)位寬擴(kuò)大,那么可以減少誤差。
仿真用的計(jì)分板:
提供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
+關(guān)注
關(guān)注
1625文章
21622瀏覽量
601241 -
圖像處理
+關(guān)注
關(guān)注
27文章
1275瀏覽量
56577 -
RGB
+關(guān)注
關(guān)注
4文章
797瀏覽量
58338 -
HSV
+關(guān)注
關(guān)注
0文章
10瀏覽量
2594
原文標(biāo)題:FPGA圖像處理--RGB轉(zhuǎn)HSV
文章出處:【微信號(hào):FPGA開(kāi)源工坊,微信公眾號(hào):FPGA開(kāi)源工坊】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論