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)
}
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !