TA的每日心情 | 开心 2019-11-4 13:48 |
---|
签到天数: 14 天 连续签到: 1 天 [LV.3]偶尔看看II
|
本文主要内容是实现图像的边缘检测功能
mif文件的制作
受资源限制,将图片像素定为 160 * 120,将图片数据制成 mif 文件,对 rom ip 核进行初始化。mif文件的制作方法网上有好多办法,因此就不再叙述了,重点说mif文件的格式。
1、mif文件的格式为:
- WIDTH=16 ; //数据位宽
- DEPTH=19200 ; // rom 深度即图片像素点的个数
- ADDRESS_RADIX=UNS ; //地址数据格式
- DATA_RADIX=BIN ; //数据格式
- CONTENT
- BEGIN
- 0:1010110011010000 ; // 地址 :数据 ;注意格式要和上面定义的保持统一
- 1:1010110011010000 ;
- 2:1010010010110000 ;
- ......
- 19198:1110011011111001 ;
- 19199:1110011011011000 ;
- END;
复制代码
灰度处理
任何颜色都由红、绿、蓝三原色组成,假如原来某点的颜色为( R,G,B )那么,我们可以通过下面几种方法,将其转换为灰度:
- 浮点算法:Gray=0.299R+0.587G+0.114B
- 平均值法:Gray=(R+G+B)/3;
- 仅取单色(如绿色):Gray=G;
将计算出来的Gray值同时赋值给 RGB 三个通道即RGB为(Gray,Gray,Gray),此时显示的就是灰度图。通过观察调色板就能看明了。 通过观察可知,当RGB三个通道的值相同时即为灰色,Gray的值越大,颜色越接近白色,反之越接近黑色(这是我自己的理解,不严谨错误之处请大神指正)。
这是在线调色板网址,可以进去自己研究一下。http://tool.chinaz.com/tools/selectcolor.aspx
此次我采用是浮点算法来实现灰度图的,我的图片数据是RGB565 格式 ,
难点: 如何进行浮点运算。
思路:先将数据放大,然后再缩小。
例如:
Gray=0.299R+0.587G+0.114B转化为 Gray=(77R+150G+29B)>>8 即可,这里有一个技巧,若 a 为 16 位即 a [15:0],那么 a>>8 与 a [15:8]是一样的。
核心代码如下:
均值滤波
均值滤波的原理
http://blog.csdn.net/hhygcy/article/details/4325304 (此处引用 hhygcy 的文章)
难点:如何生成 3*3 的像素阵列。
我们可以利用 ip 核生成移位寄存器 ,方法与 ip 核 生成 rom 一样,详情见目录 2 因此不再赘述 。
仿真波形如下 row_1 , row_2 , row_3 是指图像的第一、二、三行的数据,Per_href 是行有效信号(受VGA时序的启发,从 rom 中读取数据时设计了行有效和场有效的控制信号,事半功倍,有了利于仿真查错和数据的控制)。从 3 开始就出现了3*3 的像素阵列,这时候就可以求取周围 8 个像素点的平均值,进行均值滤波。
下面这个图是我自己画的 FPGA 如何将矩阵数据处理成并行的像素点,可以结合下面的代码好好理解,这也是精华所在。
正方形红框框起来的是第一个完整的 3*3 矩阵,长方形红框框起来的是并行的像素点,在此基础上就可以求得平均值,进行均值滤波。
从下图也能看到 3*3 矩阵从左往右滑动。
第一个3*3 阵列。
0 1 2 -- > p11 p12 p13
3 4 5 -- > p21 p22 p23
6 7 8 -- > p31 p32 p33
核心代码如下:
sobel 边缘检测
边缘检测的原理
该算子包含两组 3x3 的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。A代表原始图像的 3*3 像素阵列,Gx及Gy分别代表经横向及纵向边缘检测的图像,其公式如下:
图像的每一个像素的横向及纵向梯度近似值可用以下的公式结合,来计算梯度的大小。
如果梯度G大于某一阀值则认为该点(x,y)为边缘点。
-------------------------------------------------------------------------------------------------------------------
用的是 边缘检测算法。
难点:(1)掌握了 3*3 像素阵列,Gx 与 Gy 就很好计算了 (注意问题:为了避免计算过程中出现负值,所以将正负值分开单独计算,具体见代码)
(2)G的计算需要开平方,如何进行开平方运算
Quartus 提供了开平方 ip 核,因此我们直接调用就好了 。
代码:
- reg [8:0] p_x_data ,p_y_data ; // x 和 y 的正值之和
- reg [8:0] n_x_data ,n_y_data ; // x 和 y 的负值之和
- reg [8:0] gx_data ,gy_data ; //最终结果
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- p_x_data <=0;
- n_x_data <=0;
- gx_data <=0;
- end
- else if(per_href_ff1==1) begin
- p_x_data <= p_13 + (p_23<<1) + p_33 ;
- n_x_data <= p_11 + (p_12<<1 )+ p_13 ;
- gx_data <= (p_x_data >=n_x_data)? p_x_data - n_x_data : n_x_data - p_x_data ;
- end
- else begin
- p_x_data<=0;
- n_x_data<=0;
- gx_data <=0;
- end
- end
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- p_y_data <=0;
- n_y_data <=0;
- gy_data <=0;
- end
- else if(per_href_ff1==1) begin
- p_y_data <= p_11 + (p_12<<1) + p_13 ;
- n_y_data <= p_31 + (p_32<<1) + p_33 ;
- gy_data <= (p_y_data >=n_y_data)? p_y_data - n_y_data : n_y_data - p_y_data ;
- end
- else begin
- p_y_data <=0;
- n_y_data <=0;
- gy_data <=0;
- end
- end
- //求平方和,调用ip核开平方
- reg [16:0] gxy; // Gx 与 Gy 的平方和
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- gxy<=0;
- end
- else begin
- gxy<= gy_data* gy_data + gx_data* gx_data ;
- end
- end
- wire [8:0] squart_out ;
- altsquart u1_altsquart ( //例化开平方的ip核
- .radical (gxy),
- .q (squart_out), //输出的结果
- .remainder()
- );
- //与阈值进行比较
- reg [15:0] post_y_data_r;
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- post_y_data_r<=16'h00;
- end
- else if(squart_out>=threshold)
- post_y_data_r<=16'h00 ;
- else
- post_y_data_r<=16'hffff ;
-
- end
复制代码
图片的显示
本来是想用 VGA 来显示图片的,由于条件的限制没能实现,最终只能将处理完的数据输出保存在 .txt 文件中,然后借助好友写的网页进行显示。
难点:(1) 如何将数据流输出保存到 .txt 文件中。
(2) 网页的使用及注意事项
在testbench里加入下面所示代码即可将图片数据保存到 .txt 文本
代码如下:
- integer w_file;
- initial
- w_file = $fopen("data_out_3.txt"); //保存数据的文件名
- always @(posedge clk or negedge rst_n)
- begin
- if(flag_write==1&&post_href==1)//根据自己的需求定义
- $fdisplay(w_file,"%b",post_y_data);
- end
复制代码
网页的界面如下,将参数设置好以后就可以显示图片。
下载链接
aggregrate.zip
(908 Bytes, 下载次数: 10)
|
|