对于图像处理的知识,个人仅限于研究生时上过的数字图像处理课程,而对于FPGA的数字图像处理,一直仅限于略(yan)有(gao)耳(shou)闻(di)。国庆躺尸看了点儿简单的FPGA图像处理,发现在FPGA图像处理中矩阵提取是个有趣的东东,本文仅限个人DIY,是否具有工程价值不做论述,权当一乐。
无处不在的line buffer
闲暇逛知乎,发现在FPGA图像处理中,凡事牵涉到矩阵运算的算法里面基本都有line buffer的身影,而line buffer往往采用Xilinx的Ram-based Shift Register或者Altrea的Shift Register来实现:
嗯,简单明了,似乎没什么难的,随后翻了翻两个厂商的IP,发现有点儿别扭:
1、我把数据从IP的din灌入进去了,但输出数据什么时候有效没告诉我啊,虽然仿真时可以看到数据有效时的状态但设计里你还要我手动去控制判断什么时候是有效输出是不是有点儿过分了?……
2、当年老师也讲过在进行矩阵处理时对于边缘像素提取矩阵时往往需要填充像素。软件里感觉还好但FPGA里实时地你要我补像素貌似有点儿麻烦哎?……
也许个人太懒了吧,这种事情做起来都不复杂,但要在RTL里做这些我还是有点儿拒绝的。另辟蹊径,借助SpinalHDL里方便的电路描述符方式,魔改实现一个IP:
1、串行输入像素,带Valid指示,输入前无需用户进行边缘像素填充。
2、输出nxn矩阵用于做图像处理,输出数据有效带Valid标签。
对于3x3矩阵,用户调用的RTL接口列表将会如下所示(起名有点儿随意?):
module bufWindow ( input dataIn_valid, input [15:0] dataIn_payload, output dataOut_valid, output [15:0] dataOut_payload_0_0, output [15:0] dataOut_payload_0_1, output [15:0] dataOut_payload_0_2, output [15:0] dataOut_payload_1_0, output [15:0] dataOut_payload_1_1, output [15:0] dataOut_payload_1_2, output [15:0] dataOut_payload_2_0, output [15:0] dataOut_payload_2_1, output [15:0] dataOut_payload_2_2, input clk, input reset);endmodule
line buffer实现
首先要实现的是行缓冲器,说白了就是一连串移位寄存器,这里使用了SpinalHDL里提供的Histroy工具,它的定义是:
天然的实现行缓冲器的有效工具啊!!!几行代码就可以实现一个多抽头行缓冲器:
val sram=History(io.dataIn.payload,cfg.lineDepth*cfg.lineNum+1,init=U(0,cfg.dataWidth bits))for(index 0 io.dataOut.payload(index):=sram.vec((index+1)*cfg.lineDepth)}
这里有一点需要注意的是History最后一个元素是延迟了length-1拍结果,故这里length为缓冲的像素数+1。 同时为了实现对边缘像素的处理,避免填补像素,在对外给输出Valid信号时需数据在到达缓存像素个数中间时给出有效标志(在矩阵处理时待处理像素都是在矩阵中间),同样需要借助一个缓冲器缓冲Valid标签:
val validRam=History(io.dataIn.valid,(cfg.lineNum+1)*cfg.lineDepth/2+1,init=False)
至此行缓冲器的整体结构便有了(几行代码的事情)。
bufWindow实现
有了line buffer的实现,bufWindow的实现就水到渠成了,line buffer按序输出了n行待处理的元素,接下来在bufWIndow里通过寄存器缓存实现矩阵窗口及数据有效标志位处理即可。 针对矩阵窗口的实现,由于line buffer输出已缓存的一排排数据,故在bufWidnow里只需再缓存nx(n-1)个元素即可,不多说,移位寄存(也可调整元素顺序,使得可算法描述里一一对应)~ 而数据有效位的处理,则需对line buffer输出延迟(n+1)/2-1拍即可。val windowValid=History(lineBuffers.io.dataOut.valid,(cfg.lineNum+1)/2,init=False)io.dataOut.valid:=windowValid.vec.last
实现结果
对于行宽为3,行数为3,提取3x3矩阵仿真结果为(按行输入元素从左到右依次填充1,2,3……25) :buffer window 10 0 00 1 20 4 5buffer window 20 0 01 2 34 5 6buffer window 30 0 12 3 45 6 7buffer window 40 1 23 4 56 7 8buffer window 51 2 34 5 67 8 9buffer window 62 3 45 6 78 9 0buffer window 73 4 56 7 89 0 0buffer window 84 5 67 8 90 0 0buffer window 95 6 78 9 00 0 0
对于行宽为5,行数为5,提取5x5矩阵仿真结果为(按行输入元素从左到右依次填充1,2,3……25) :
buffer window 10 0 0 0 00 0 0 0 00 0 1 2 30 0 6 7 80 0 11 12 13buffer window 20 0 0 0 00 0 0 0 00 1 2 3 40 6 7 8 90 11 12 13 14buffer window 30 0 0 0 00 0 0 0 01 2 3 4 56 7 8 9 1011 12 13 14 15buffer window 40 0 0 0 00 0 0 0 12 3 4 5 67 8 9 10 1112 13 14 15 16buffer window 50 0 0 0 00 0 0 1 23 4 5 6 78 9 10 11 1213 14 15 16 17buffer window 60 0 0 0 00 0 1 2 34 5 6 7 89 10 11 12 1314 15 16 17 18buffer window 70 0 0 0 00 1 2 3 45 6 7 8 910 11 12 13 1415 16 17 18 19buffer window 80 0 0 0 01 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 20buffer window 90 0 0 0 12 3 4 5 67 8 9 10 1112 13 14 15 1617 18 19 20 21buffer window 100 0 0 1 23 4 5 6 78 9 10 11 1213 14 15 16 1718 19 20 21 22buffer window 110 0 1 2 34 5 6 7 89 10 11 12 1314 15 16 17 1819 20 21 22 23buffer window 120 1 2 3 45 6 7 8 910 11 12 13 1415 16 17 18 1920 21 22 23 24buffer window 131 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 25buffer window 142 3 4 5 67 8 9 10 1112 13 14 15 1617 18 19 20 2122 23 24 25 0buffer window 153 4 5 6 78 9 10 11 1213 14 15 16 1718 19 20 21 2223 24 25 0 0buffer window 164 5 6 7 89 10 11 12 1314 15 16 17 1819 20 21 22 2324 25 0 0 0buffer window 175 6 7 8 910 11 12 13 1415 16 17 18 1920 21 22 23 2425 0 0 0 0buffer window 186 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 250 0 0 0 0buffer window 197 8 9 10 1112 13 14 15 1617 18 19 20 2122 23 24 25 00 0 0 0 0buffer window 208 9 10 11 1213 14 15 16 1718 19 20 21 2223 24 25 0 00 0 0 0 0buffer window 219 10 11 12 1314 15 16 17 1819 20 21 22 2324 25 0 0 00 0 0 0 0buffer window 2210 11 12 13 1415 16 17 18 1920 21 22 23 2425 0 0 0 00 0 0 0 0buffer window 2311 12 13 14 1516 17 18 19 2021 22 23 24 250 0 0 0 00 0 0 0 0buffer window 2412 13 14 15 1617 18 19 20 2122 23 24 25 00 0 0 0 00 0 0 0 0buffer window 2513 14 15 16 1718 19 20 21 2223 24 25 0 00 0 0 0 00 0 0 0 0
可以看到,bufWindow能够为每个元素提取相应的矩阵,不过在处理边缘像素时填充的元素不一定全为0,部分为其它边缘像素值,若在工程中这点可接收那么在处理时将减少很多工作量了~
当然只是DIY,这里矩阵大小n只支持奇数。
国庆假期要结束了,又要开启搬砖生涯了。想要源代码或者生成的RTL代码的小伙伴可私信我 。