14图像边缘检测的sobel_ctrl控制模块
一设计功能
计算得到的dxy,再通过和阈值比较大小,输出po_sum作为VGA的输入,在显示器器上显示图像的轮廓。
二设计思路
根据前一篇博客对sobel算法的介绍,先通过FIFO的双流水线操作采集到三行三列的九个数,再得到dx 和dy,再求dx 和dy的绝对值和给Dxy,最后把Dxy 和阈值比较大小得到输出po_sum。
(一)双FIFO的流水线操做
根据FIFO的时序图:写的读写控制代码如下
注意在实际过程中加一个wr_cnt来计算数据的个数。一般先设置一个FIFOIP核,再自己写一个控制程序。根据上面的读写时序,控制程序如下 module fifo_ctrl( input wire clk, input wire rst_n ); reg wr_en; reg [8:0] wr_cnt; reg [7:0] wr_data; wire full,empty; reg rd_en; reg [1:0] empty_dly; reg read_start; reg [8:0] rd_cnt; wire [7:0] rd_data; always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin wr_en <= 1'b0; end else if (wr_cnt == 'd0) begin wr_en <= 1'b1; end else if (wr_cnt== 'd257) begin wr_en <= 1'b0; end end always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset wr_cnt <= 'd0; end else if (wr_cnt != 'd257) begin wr_cnt <= wr_cnt + 1'b1; end end always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset wr_data <='d0; end else if (wr_en == 1'b1) begin wr_data <= wr_data + 1'b1; end else begin wr_data <= 'd0; end end always @(posedge clk) begin empty_dly <= {empty_dly[0],empty};//shift reg empty_dly == 2'b10 negedge end always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset read_start <= 1'b0; end else if (empty_dly == 2'b10) begin read_start <= 1'b1; end else begin read_start <= 1'b0; end end always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset rd_en <= 1'b0; end else if(rd_cnt == 256)begin rd_en <= 1'b0; end else if (read_start == 1'b1) begin rd_en <= 1'b1; end end always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset rd_cnt <= 'd0; end else if (rd_en == 1'b1) begin rd_cnt <= rd_cnt + 1'b1; end end asfifo_w256x8_r256x8 fifo_inst ( .wr_clk(clk), // input wr_clk .rd_clk(clk), // input rd_clk .din(wr_data), // input [7 : 0] din .wr_en(wr_en), // input wr_en .rd_en(rd_en), // input rd_en .dout(rd_data), // output [7 : 0] dout .full(full), // output full .empty(empty) // output empty ); endmodule |
1. sobel_ctrl控制模块的关键点一:双FIFO流水线操作
有了上面一个FIFO的读写控制代码基础,现在来实现怎么设计实现双FIFO的流水线操作
/根据时序图写代码时,一定得懂设计思路 //FIFO1的写使能怎么产生,写数据来源于什么 //FIFO2的写使能怎么产生,写数据来源于什么 //读信号怎么产生,读出的数据给谁,读信号有效才加 来回答上面的问题: 第一部分:写使能信号的产生 FIFO1的写使能信号:在0行时,来一个pi_flag 就拉高。对于2~84行的数据,利用wr_en1_pre1 和wr_en1_pre2进行打一拍操作,在2~84行,只要有pi_flag 就拉高wr_en1_pre2。 FIFO2的写使能怎么产生:在1~84行且来一个pi_flag 就拉高wr_en2 第二部分:写数据的产生 FIFO1:在第0行,fifo1的数据来源于串口接收端。对于2~84行的数据,FIFO1的数据来源于FIFO2的读出端数据。 FIFO2:在1~84行,FIFO2的数据来源于串口的数据(来一个pi_flag) 第三部分:读信号和读出的数据输出给谁 对于FIFO1和FIFO2的读出端数据dout1和dout2.对于2~85行的数据,在读信号的控制下,读出后直接在加标志信号flag_add控制下,将FIFO1 和FIFO2的读出端数据和串口接收端的数据加起来输出给PO_SUM 读信号rd_en,在大于第二行,且来一个pi_flag 就拉高rd_en 根据上面双FIFO流水线设计思路的要点,编写代码如下: parameter VALUE = 10 ; //阈值 always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin wr_en1 <= 1'b0; end else if (cnt_row == 'd0) begin wr_en1 <= pi_flag; end else begin wr_en1 <= wr_en1_pre1; end end always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset wr_en1_pre2 <= 1'b0; end else if (cnt_row >=2 && cnt_row <=198 && pi_flag == 1'b1) begin wr_en1_pre2 <= 1'b1; end else begin wr_en1_pre2 <= 1'b0; end end always @(posedge clk) begin wr_en1_pre1 <= wr_en1_pre2; end always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset rd_en <= 1'b0; end else if (cnt_row >=2 && cnt_row <=199 && pi_flag == 1'b1) begin rd_en <= 1'b1; end else begin rd_en <= 1'b0; end end always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset data_in1 <= 'd0; end else if (cnt_row == 'd0) begin data_in1 <= pi_data; end else begin//if (cnt_row >=2 && cnt_row <=84) data_in1 <= dout2; end end always @(posedge clk or negedge rst_n ) begin if (rst_n == 1'b0) begin // reset wr_en2 <= 1'b0; end else if (cnt_row >=1 && cnt_row <=198 && pi_flag == 1'b1) begin wr_en2 <= 1'b1; end else begin wr_en2 <= 1'b0; end end always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset data_in2 <= 'd0; end else if ( cnt_row >=1 && cnt_row <=198) begin data_in2 <= pi_data; end end always @(posedge clk) begin add_flag <= flag_shift; end |
2. sobel_ctrl控制模块的关键点二:怎么采集三行三列的9个数
关键是怎么采集三行三列的9个数,我的想法是,比如第零行用FIFO1存储,然后再加三个寄存器,打拍操作。每来一个串口接收标志信号就送FIFO1的数据到如寄存器1中,然后是寄存器2,再是寄存器3。
怎么采集三行三列的9个数
//注意打拍的寄存器越往后是越早的数据 //如pi_data是最新的数据,dout1_tt是前两个数据 reg flag_shift; always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin // reset flag_shift <= 1'b0; end else begin flag_shift <= rd_en; end end //打拍操作,三个寄存器在标志信号下打一拍存一个数 always@(posedge clk)begin if(flag_shift == 1'b1)begin {dout1_tt,dout1_t} <= {dout1_t,dout1}; {dout2_tt,dout2_t} <= {dout2_t,dout2}; {rx_data_tt,rx_data_t}<={rx_data_t,pi_data}; end end |
3. sobel_ctrl控制模块的关键点三:怎么得到横向的DX和纵向的DY?
always @(posedge clk) begin add_flag <= flag_shift; end //对这三行三列的数进行加减乘运算 //((dout2-dout2_tt)<<1)实现乘2操作 reg [7:0]dx; always@(posedge clk or negedge rst_n) if(rst_n==0) dx<=0; else if(add_flag) dx<=(dout1-dout1_tt)+((dout2-dout2_tt)<<1)+(pi_data-rx_data_tt); //对这三行三列的数进行加减乘运算 reg [7:0]dy; always@(posedge clk or negedge rst_n) if(rst_n==0) dy<=0; else if(add_flag) dy<=(dout1_tt-rx_data_tt)+((dout1_t-rx_data_t)<<1)+(dout1-pi_data); |
4. sobel_ctrl控制模块的关键点四:怎么得到横向的DX和纵向的DY的绝对值和?
利用原码反码,补码的关系。即最高位为符号位,1表示负数,绝对值为取反加一。0表示正数,绝对值等于本身。值得注意的是进行绝对值前,利用flag_d_pre,flag_d, flag_abs延迟三个时钟周期,打了三拍。再求绝对值的和,又利用flag_dxy打了一拍。
//对dx和dy进行绝对值求和 //补码原码反码的关系 //flag_d_pre reg flag_d_pre; always@(posedge clk or negedge rst_n) if(rst_n==0) flag_d_pre<=0; else if(cnt_row>=2 && cnt_col>=2 && pi_flag) flag_d_pre<=1; else flag_d_pre<=0; //flag_d; reg flag_d; always@(posedge clk or negedge rst_n) if(rst_n==0) flag_d<=0; else flag_d<=flag_d_pre; //falg_abs reg flag_abs; always@(posedge clk or negedge rst_n) if(rst_n==0) flag_abs<=0; else flag_abs<=flag_d; //abs_dx reg [7:0]abs_dx; reg [7:0]abs_dy; always@(posedge clk or negedge rst_n) if(!rst_n) abs_dx<=0; else if(flag_abs & dx[7]==1'b1) abs_dx<=(~dx)+1; else if(flag_abs & dx[7]==1'b0) abs_dx<=dx; //abs_dy always@(posedge clk or negedge rst_n) if(!rst_n) abs_dy<=0; else if(flag_abs & dy[7]==1'b1) abs_dy<=(~dy)+1; else if(flag_abs & dy[7]==1'b0) abs_dy<=dy; //取了绝对值进行打拍 reg flag_dxy; always@(posedge clk or negedge rst_n) if(rst_n==0) flag_dxy<=0; else flag_dxy<=flag_abs; reg [7:0]dxy; always@(posedge clk or negedge rst_n) if(rst_n==0) dxy<=0; else if(flag_dxy) dxy<=abs_dy+abs_dx; |
5. sobel_ctrl控制模块的关键点五:怎么阈值比较得到边缘,大于阈值白色显示,否则黑色?
//判断相对于阈值大小输出不同的图像 reg flag_rgb; always@(posedge clk or negedge rst_n) if(rst_n==0) flag_rgb<=0; else flag_rgb<=flag_dxy; //rgb always@(posedge clk or negedge rst_n) if(!rst_n) po_sum<=0; else if(flag_rgb & dxy>=VALUE) po_sum<=8'hff; else if(flag_rgb & dxy<=VALUE) po_sum<=8'h00; always@(posedge clk or negedge rst_n) if(rst_n==0) po_flag<=0; else po_flag<=flag_rgb; |
三,仿真验证
到这里,终于把图像边缘检测的算法关键点讲清楚了,而我的仿真是先利用80X4的数据,即用sublime的重复语句产生一个0~320的数,然后再为仿真的输入,进行波形的验证。
在sublime中利用敲出{:08b},然后再选中这个代码,再右键选择重复代码,之后在设置起始位和停止位及步长,,敲回车即可。
如0~320,1
RAM一般最大为9K,即1024X9个数据。
刚开始我写的仿真程序包含了串口发送有点复杂,所以在这里提高vga_ram模块的仿真,两者很相似,只需要改哈端口就行,设计思想相同。把上面产生的数据保存在一个txt文件里,在仿真里用mem读取这个文本,再用一个task函数把这数据赋值给我们功能模块vga_ram的输入pi_data,如程序所写。b第二个注意点,如要产生300X3的数据,不用写一个矩阵的仿真程序,直接并转串,用一个900X1的数据表示,结果是一样,还很方便。
`timescale 1ns / 1ps module tb_vga_ram; // Inputs reg sclk; reg rst_n; reg pi_flag; reg [7:0]pi_data; reg [7:0] mem[899:0]; // Outputs wire hsync ; wire vsync ; wire [7:0]vga_rgb; vga_ram inst_vga_ram ( .clk (sclk), .rst_n (rst_n), .pi_flag (pi_flag), .pi_data (pi_data), .hsync (hsync), .vsync (vsync), .vga_rgb (vga_rgb) ); // Instantiate the Unit Under Test (UUT) initial begin // Initialize Inputs sclk = 0; rst_n = 0; pi_flag=0; pi_data=0; // Wait 100 ns for global reset to finish #100; rst_n =1; // Add stimulus here end initial begin $readmemb("./data.txt",mem); end always #10 sclk = ~sclk; always #100 pi_flag = ~pi_flag; initial begin #200; rx_byte(); end task rx_byte; integer i; integer j; begin for(j=0;j<900;j=j+1)begin for (i=0;i<900;i=i+1)begin pi_data=mem[i]; #199; end end end endtask endmodule |
波形如下:
14图像边缘检测的sobel_ctrl控制模块的更多相关文章
- CSharpGL(24)用ComputeShader实现一个简单的图像边缘检测功能
CSharpGL(24)用ComputeShader实现一个简单的图像边缘检测功能 效果图 这是红宝书里的例子,在这个例子中,下述功能全部登场,因此这个例子可作为使用Compute Shader的典型 ...
- 图像边缘检测--OpenCV之cvCanny函数
图像边缘检测--OpenCV之cvCanny函数 分类: C/C++ void cvCanny( const CvArr* image, CvArr* edges, double threshold1 ...
- Python实现图像边缘检测算法
title: "Python实现图像边缘检测算法" date: 2018-06-12T17:06:53+08:00 tags: ["图形学"] categori ...
- 图像边缘检测——几种图像边缘检测算子的学习及python 实现
本文学习利用python学习边缘检测的滤波器,首先读入的图片代码如下: import cv2 from pylab import * saber = cv2.imread("construc ...
- OpenCV3入门(八)图像边缘检测
1.边缘检测基础 图像的边缘是图像的基本特征,边缘点是灰度阶跃变化的像素点,即灰度值的导数较大或极大的地方,边缘检测是图像识别的第一步.用图像的一阶微分和二阶微分来增强图像的灰度跳变,而边缘也就是灰度 ...
- 图像边缘检测——Sobel算子
边缘是图像最基本的特征,其在计算机视觉.图像分析等应用中起着重要的作用,这是因为图像的边缘包含了用于识别的有用信息,是图像分析和模式识别的主要特征提取手段. 1.何为“图像边缘”? 在图像中,“边缘” ...
- 数字集成电路设计-8-一个简单sobel图像边缘检测加速器的设计,实现,仿真与综合
引言 图像视频处理等多媒体领域是FPGA应用的最主要的方面之一,边缘检测是图像处理和计算机视觉中的基本问题,所以也是最常用的,随着数据量的不断增加以及对实时性的要求,一般软件已经不能满足实际需要,这时 ...
- python计算机视觉2:图像边缘检测
我是一名初学者,如果你发现文中有错误,请留言告诉我,谢谢 如果需要检测到图像里面的边缘,首先我们需要知道边缘处具有什么特征. 对于一幅灰度图像来说,边缘两边的灰度值肯定不相同,这样我们才能分辨出哪里是 ...
- 基于 FPGA 的图像边缘检测
本文主要内容是实现图像的边缘检测功能 目录 mif文件的制作 调用 ip 核生成rom以及在 questasim 仿真注意问题 灰度处理 均值滤波:重点是3*3 像素阵列的生成 sobel边缘检测 图 ...
随机推荐
- Nacos中服务删除不了,怎么办?
前两天遇到了一个问题,Nacos 中的永久服务删除不了,折腾了一番,最后还是顺利解决了.以下是原因分析和解决方案,建议先收藏,以备不时之需. 临时实例和持久化实例是 Nacos 1.0.0 中新增了一 ...
- 《PHP程序员面试笔试真题解析》——新书上线
你好,是我--琉忆.很高兴可以跟你分享我的新书. 很高兴,在出版了PHP程序员面试笔试宝典后迎来了我的第二本书出版--<PHP程序员面试笔试真题解析>. 如果你是一个热爱PHP的程序员,刚 ...
- php使用CURL实现GET和POST方式请求
CURL请求,支持GET和POST两种方式,默认为GET方式,如果传第二个参数则为POST方式请求,设置了超时时间,避免程序卡死. /** 使用curl方式实现get或post请求 @param $u ...
- Android SugarORM(4)
Android Sugar ORM(4) Android Sugar ORM 数据库迁移 据官网描述, Sugar ORM的设计灵感来自与Rails(没用过, 咱也不知道是啥, 以后也许会学到吧)迁移 ...
- Android SugarORM(2)
Android Sugar ORM (2) Android Sugar ORM 实体 1. 创建一个实体类 Sugar ORM在创建一个实体的时候, 仅需要使这个实体类继承于SugarRecord即可 ...
- Java邮件发送中的setRecipient方法使用
一.方法setRecipient(Message.RecipientType type, Address address),是用于设置邮件的接收者. 1.有两个参数,第一个参数是接收者的类型,第二 ...
- 从数据源支持、支持方式等角度深入了解Smartbi与Tableau
对数据分析来讲,数据源支持是基本功.让数据分析工具与数据保持一个通道,建立会话.用数据分析应用服务器与我们需要分析的业务数据进行连接,拿到需要的数据进行分析.Smartbi.Tableau系统给我们提 ...
- 从这3个方面考虑BI工具,选型一选一个准
BI工具在很多场合都能听到,那么BI工具有什么功能呢?能给企业带来什么?好用的BI工具长什么样?今天跟着小编走近BI工具,一探究竟! 首先要了解BI工具的定义,什么是BI工具.BI工具是指利用现代数据 ...
- 报表软件测评来啦!Smartbi电子表格使用感受
最近因为工作需求,需要用到一些报表工具软件,看到Smartbi比较方便,可以直接在excel中进行配置,所以安装体验了一下. 下载 Smartbi有多个版本,我主要是在excel中使用,所以下载了一个 ...
- strtok()出现segment fault的错误
在写一个简易的 shell 时,需要将命令行的命令通过空格分割成一个个字符串参数,这里我使用了 strtok() 函数,然后遇到了 segment fault 的错误. 出现问题的代码如下: 终于寻找 ...