FPGA + 图像处理(三)生成3x3像素矩阵
前言
生成NxN的像素矩阵是对图像进行各类滤波操作的基本前提,本文介绍一种通过bram生成3x3矩阵的方法。
程序
生成bram核
因为本文介绍的是基于bram生成的3x3像素矩阵,所以要先生成两个bram核,用于缓存前两行图像数据
在 IP catalog中选择Block Memory Generator
配置如下
注意这里选择simple dual port RAM,即伪双端口,一个端口只能写,一个端口只能读
端口A用于写入数据,注意数据的位宽要与图像位深相同,彩色通常为24位,灰度图为8位,数据深度为一行像素的长度,operating选择写优先,enable port type选择始终使能
端口B用于读取数据,这里要注意下面的primitives output register要勾选上,勾选该选项后,数据的输出会延迟一个时钟周期,用于对齐数据。
HDMI时序生成模块
这里也用到了HDMI时序生成模块,具体作用和前面文章讲的一样,一是可以做到通过同步信号简化对图像数据的管理,二是可以让测试的数据处理模块更方便的适配用HDMI显示的图像处理工程。
具体代码如下
module hdmi_tim_gen(input clk ,input rst_n ,input [23:0] data_in ,output hdmi_hs , //行同步信号output hdmi_vs , //场同步信号output hdmi_de , //数据使能output [23:0] hdmi_data , //RGB888颜色数据output reg data_req );//1280*720 分辨率时序参数
parameter H_SYNC = 11'd40; //行同步
parameter H_BACK = 11'd220; //行显示后沿
parameter H_DISP = 11'd1280; //行有效数据
parameter H_FRONT = 11'd110; //行显示前沿
parameter H_TOTAL = 11'd1650; //行扫描周期parameter V_SYNC = 11'd5; //场同步
parameter V_BACK = 11'd20; //场显示后沿
parameter V_DISP = 11'd720; //场有效数据
parameter V_FRONT = 11'd5; //场显示前沿
parameter V_TOTAL = 11'd750; //场扫描周期//reg define
reg [11:0] cnt_h;
reg [11:0] cnt_v;reg [10:0] pixel_xpos;
reg [10:0] pixel_ypos;assign hdmi_de = data_req;
assign hdmi_hs = ( cnt_h < H_SYNC ) ? 1'b0 : 1'b1; //行同步信号赋值
assign hdmi_vs = ( cnt_v < V_SYNC ) ? 1'b0 : 1'b1; //场同步信号赋值//RGB888数据输出
assign hdmi_data = hdmi_de ? data_in : 24'd0;//请求像素点颜色数据输入
always @(posedge clk or negedge rst_n) beginif(!rst_n)data_req <= 1'b0;else if(((cnt_h >= H_SYNC + H_BACK - 2'd2) && (cnt_h < H_SYNC + H_BACK + H_DISP - 2'd2))&& ((cnt_v >= V_SYNC + V_BACK) && (cnt_v < V_SYNC + V_BACK+V_DISP)))data_req <= 1'b1;elsedata_req <= 1'b0;
end//像素点x坐标
always@ (posedge clk or negedge rst_n) beginif(!rst_n)pixel_xpos <= 11'd0;else if(data_req)pixel_xpos <= cnt_h + 2'd2 - H_SYNC - H_BACK ;else pixel_xpos <= 11'd0;
end//像素点y坐标
always@ (posedge clk or negedge rst_n) beginif(!rst_n)pixel_ypos <= 11'd0;else if((cnt_v >= (V_SYNC + V_BACK)) && (cnt_v < (V_SYNC + V_BACK + V_DISP)))pixel_ypos <= cnt_v + 1'b1 - (V_SYNC + V_BACK) ;else pixel_ypos <= 11'd0;
end//行计数器对像素时钟计数
always @(posedge clk or negedge rst_n) beginif (!rst_n)cnt_h <= 11'd0;else beginif(cnt_h < H_TOTAL - 1'b1)cnt_h <= cnt_h + 1'b1;else cnt_h <= 11'd0;end
end//场计数器对行计数
always @(posedge clk or negedge rst_n) beginif (!rst_n)cnt_v <= 11'd0;else if(cnt_h == H_TOTAL - 1'b1) beginif(cnt_v < V_TOTAL - 1'b1)cnt_v <= cnt_v + 1'b1;else cnt_v <= 11'd0;end
endendmodule
生成3x3像素矩阵的顶层模块
module kernel_3x3_gen
(input clk, input rst_n, //准备要进行处理的图像数据input vs_i,input de_i,input [23:0] data_i,//矩阵化后的图像数据和控制信号output vs_o,output de_o,output reg [23:0] mat11, output reg [23:0] mat12,output reg [23:0] mat13,output reg [23:0] mat21, output reg [23:0] mat22, output reg [23:0] mat23,output reg [23:0] mat31, output reg [23:0] mat32, output reg [23:0] mat33
);//wire define
wire [23:0] row1_data; //第一行数据
wire [23:0] row2_data; //第二行数据
wire de_i_en ;//reg define
reg [23:0] row3_data; //第三行数据,即当前正在接受的数据
reg [23:0] row3_data_d0;
reg [23:0] row3_data_d1;
reg [23:0] row2_data_d0;
reg [1:0] vs_i_d;
reg [1:0] de_i_d;assign de_i_en = de_i_d[0] ;
assign vs_o = vs_i_d[1];
assign de_o = de_i_d[1] ;//当前数据放在第3行
always@(posedge clk or negedge rst_n) beginif(!rst_n)row3_data <= 0;else begin if(de_i)row3_data <= data_i ;elserow3_data <= row3_data ;end
end//用于存储列数据的RAM
line_shift u_line_shift
(.clk (clk),.de_i (de_i),.data_i (data_i), //当前行的数据.data1_o (row2_data), //前一行的数据.data2_o (row1_data) //前前一行的数据
);//将同步信号延迟两拍,用于同步化处理
always@(posedge clk or negedge rst_n) beginif(!rst_n) begin vs_i_d <= 0;de_i_d <= 0;endelse begin vs_i_d <= { vs_i_d[0], vs_i };de_i_d <= { de_i_d[0], de_i };end
endalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrow3_data_d1 <= 0;row3_data_d0 <= 0;row2_data_d0 <= 0;endelse beginrow3_data_d0 <= row3_data;row3_data_d1 <= row3_data_d0;row2_data_d0 <= row2_data;end
end//在同步处理后的控制信号下,输出图像矩阵
always@(posedge clk or negedge rst_n) beginif(!rst_n) begin {mat11, mat12, mat13} <= 0;{mat21, mat22, mat23} <= 0;{mat31, mat32, mat33} <= 0;endelse if(de_i_en) begin {mat11, mat12, mat13} <= {mat12, mat13, row1_data};{mat21, mat22, mat23} <= {mat22, mat23, row2_data_d0};{mat31, mat32, mat33} <= {mat32, mat33, row3_data_d1};endelse begin {mat11, mat12, mat13} <= 0;{mat21, mat22, mat23} <= 0;{mat31, mat32, mat33} <= 0;end
endendmodule
行移位模块
module line_shift(input clk,input de_i,input [23:0] data_i, //当前行的数据output [23:0] data1_o, //前一行的数据output [23:0] data2_o //前前一行的数据
);//reg define
reg de_i_d0;
reg de_i_d1;
reg de_i_d2;
reg [10:0] ram_rd_addr;
reg [10:0] ram_rd_addr_d0;
reg [10:0] ram_rd_addr_d1;
reg [23:0] data_i_d0;
reg [23:0] data_i_d1;
reg [23:0] data_i_d2;
reg [23:0] data1_o_d0;//在数据到来时,RAM的读地址累加
always@(posedge clk)beginif(de_i)ram_rd_addr <= ram_rd_addr + 1 ; elseram_rd_addr <= 0 ;
end//将数据使能延迟两拍
always@(posedge clk) beginde_i_d0 <= de_i;de_i_d1 <= de_i_d0;de_i_d2 <= de_i_d1;
end//将RAM地址延迟2拍
always@(posedge clk ) beginram_rd_addr_d0 <= ram_rd_addr;ram_rd_addr_d1 <= ram_rd_addr_d0;
end//输入数据延迟3拍送入RAM
always@(posedge clk)begindata_i_d0 <= data_i;data_i_d1 <= data_i_d0;data_i_d2 <= data_i_d1;
end//用于存储前一行图像的RAM
blk_mem_gen_0 u_ram_1024x8_0(.clka (clk),.wea (de_i_d2),.addra (ram_rd_addr_d1), //在延迟的第三个时钟周期,当前行的数据写入RAM0.dina (data_i_d2),.clkb (clk),.addrb (ram_rd_addr), .doutb (data1_o) //延迟一个时钟周期,输出RAM0中前一行图像的数据
);//寄存前一行图像的数据
always@(posedge clk)begindata1_o_d0 <= data1_o;
end//用于存储前前一行图像的RAM
blk_mem_gen_0 u_ram_1024x8_1(.clka (clk),.wea (de_i_d1),.addra (ram_rd_addr_d0),.dina (data1_o_d0), //在延迟的第二个时钟周期,将前一行图像的数据写入RAM1.clkb (clk),.addrb (ram_rd_addr),.doutb (data2_o) //延迟一个时钟周期,输出RAM1中前前一行图像的数据
);endmodule
仿真模块
`timescale 1ns/1nsmodule pic_tb();reg clk,rst_n ;reg [23:0] data_in ;
wire hdmi_hs,hdmi_vs,hdmi_de ;
wire [23:0] hdmi_data ;
wire data_req ;reg vs_i,de_i ;
wire vs_o,de_o ;
wire [23:0] mat11, mat12, mat13 ;
wire [23:0] mat21, mat22, mat23 ;
wire [23:0] mat31, mat32, mat33 ;
//延迟1clk,与data同步,hdmi时序中,data比de延迟了一个时钟周期
always @(posedge clk)beginvs_i <= hdmi_vs;de_i <= hdmi_de;
endinitial beginclk = 1;rst_n = 0;#20 rst_n = 1;
end
always #10 clk = ~clk;reg [23:0] img[0:1280*720-1];
reg [31:0] addr;
initial begin$readmemh("D:/pic/img2txt.txt",img);
endalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginaddr <= 0 ;data_in <= 0 ;endelse if(data_req) begindata_in <= img[addr];addr <= addr + 1;if(addr == (1280*720-1))addr <= 0;end
endhdmi_tim_gen u_hdmi_tim_gen(.clk (clk), .rst_n (rst_n),//input.data_in (data_in),//output.hdmi_hs (hdmi_hs),.hdmi_vs (hdmi_vs),.hdmi_de (hdmi_de),.hdmi_data (hdmi_data),.data_req (data_req)
);kernel_3x3_gen u_kernel_3x3_gen(.clk (clk), .rst_n (rst_n),//预处理灰度数据.vs_i (vs_i),.de_i (de_i), .data_i (hdmi_data),//输出3x3矩阵.vs_o (vs_o),.de_o (de_o),.mat11 (mat11), .mat12 (mat12), .mat13 (mat13),.mat21 (mat21), .mat22 (mat22), .mat23 (mat23),.mat31 (mat31), .mat32 (mat32), .mat33 (mat33)
);endmodule
整体架构
仿真结果
截取部分数据结果
mat31、mat32、mat33是第一行数据(最先输入的那一行),mat11、mat12、mat13是第三行数据(最后输入的那一行),可以看见数据的移位满足像素矩阵的要求。
相关文章:

FPGA + 图像处理(三)生成3x3像素矩阵
前言 生成NxN的像素矩阵是对图像进行各类滤波操作的基本前提,本文介绍一种通过bram生成3x3矩阵的方法。 程序 生成bram核 因为本文介绍的是基于bram生成的3x3像素矩阵,所以要先生成两个bram核,用于缓存前两行图像数据 在 IP catalog中选…...

Redis安装说明2
Redis安装说明 1.3.2.指定配置启动 如果要让Redis以后台方式启动,则必须修改Redis配置文件,就在我们之前解压的redis安装包下(/usr/local/src/redis-6.2.6),名字叫redis.conf: 我们先将这个配置文件备份一…...
ArcGIS10.8保姆式安装教程
ArcGIS 10.8是一款非常强大的地理信息系统软件,用于创建、管理、分析和可视化地理数据。以下是ArcGIS 10.8的详细安装教程: 确保系统满足安装要求 在开始安装之前,请确保您的计算机满足以下系统要求: 操作系统:Windo…...
设计原则、设计模式、设计模式项目实战
设计原则 封装、继承、多态、抽象分别可以解决哪些编程问题 封装:也叫做信息隐藏或数据保护访问。数据 通过暴露有限的访问接口,授权外部仅能通过类提供接口访问,对内的类private私有化属性,通过封装简化操作,让用户更…...
【Redis】解决List类型的消息可靠性问题
前言 平时做后端开发时,如果需要用到消息队列功能,但公司的IT环境又没有提供专业的队列软件(RabitMQ/Kafka…),那么在简单且要求不高的场景下,可以使用 Redis 的List数据类型来做消息队列。 但List类型有…...

挑战30天C++基本入门(DAY8--树)[part 3](速通哦~)
#上一章我们把搜索二叉树的知识给传授完毕,如果认真的看下去并且手打了几遍,基本上内部的逻辑还是可以理解的,那我们现在就截至继续学习树的一些重要知识啦~~ 树高怎么求呀?如果用上一次学的层次遍历来求树高,有点小题…...

在虚拟机尝试一次用启动盘重装系统
在虚拟机尝试一次用启动盘重装系统 没有自己重装过系统,也不敢对自己的笔记本下手,用虚拟机重装玩玩试试。 先设置成u盘启动 从boot中选择相应的创建的硬盘即可(刚刚突然发现图片不能上传了,经过乱七八糟的尝试后,开一…...

力扣347. 前 K 个高频元素
思路:记录元素出现的次数用map; 要维护前k个元素,不至于把所有元素都排序再取前k个,而是新建一个堆,用小根堆存放前k个最大的数。 为什么是小根堆?因为堆每次出数据时只出堆顶,每次把当前最小的…...

SCP 从Linux快速下载文件到Windows本地
需求:通过mobaxterm将大文件拖动到windows本地速度太慢。 环境:本地是Windows,安装了Git。 操作:进入文件夹内,鼠标右键,点击Git Bash here,然后输入命令即可。这样的话,其实自己本…...

plasmo内容UI组件层级过高导致页面展示错乱
我使用plasmo写了一个行内样式的UI组件,但是放到页面上之后,会和下拉组件出现层级错乱,看了一下样式,吓我一跳:层级竟然设置的如此之高 所以就需要将层级设置低一点: #plasmo-shadow-container {z-index: …...

《QT实用小工具·十一》Echart图表JS交互之仪表盘
1、概述 源码放在文章末尾 该项目为Echart图表JS交互之炫酷的仪表盘,可以用鼠标实时改变仪表盘的读数。 下面为demo演示: 该项目部分代码如下: #include "widget.h" #include "ui_widget.h" #include "qurl.h&q…...
深入浅出理解ArrayBuffer对象TypedArray和DataView视图
目录 举例理解 1. ArrayBuffer对象 2. TypedArray 3. DataView 总结 具体讲解 1. ArrayBuffer对象 2. TypedArray 3. DataView 注意事项 举例理解 先举个简单的例子理解ArrayBuffer对象TypedArray和DataView视图的概念和之间的关系 1. ArrayBuffer对象 想象一个场景…...

人工智能 - 服务于谁?
人工智能服务于谁? 人工智能服务于生存,其最终就是服务于战争(热战、技术战、经济战) 反正就是为了活着而战的决策。 既然人工智能所有结果,来自大数据的分挖掘(分析)也就是数据的应用&#x…...

软考高级架构师:嵌入式系统的内核架构
作者:明明如月学长, CSDN 博客专家,大厂高级 Java 工程师,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《Effective Java》独家解析》专栏作者。 热门文章推荐&am…...

分布式锁实战
4、分布式锁 4.1 、基本原理和实现方式对比 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行&#x…...

【VMware Workstation】启动虚拟机报错“此主机支持 AMD-V,但 AMD-V 处于禁用状态”
问题出现步骤: 打开虚拟机: 然后报错: “此主机支持 AMD-V,但 AMD-V 处于禁用状态。 如果已在 BIOS/固件设置中禁用 AMD-V,或主机自更改此设置后从未重新启动,则 AMD-V 可能被禁用。 (1) 确认 BIOS/固件设…...

非关系型数据库(缓存数据库)redis的基础认知与安装
目录 一.关系型数据库和非关系型数据库 关系型数据库 非关系型数据库 关系数据库与非关系型数据库的区别 ①非关系数据 关系型数据库 非关系型数据库产生背景 数据存储流向 非关系型数据库 关系数据库 二.redis的简介 1.概念 2.Redis 具有以下几个优点: 3.Redi…...
Go语言如何处理文件
1.文件的重要性 文件不过是硬盘中的数据,看起来好像没什么了不起,但实际上,文件能够让程序员管理配置、存储程序的状态乃至从底层操作系统中读取数据。 UNIX型操作系统的一个重要特征是,将一切都视为文件。这意味着在操作系统看来,从键盘到打印机的所有东西都可像文件那样…...
Java基础知识总结(42)
(1)Java关键字的相关知识进行了复习 考试过程中“main”是主方法名,而不是Java关键字 (2)类型转换 当一个算术表达式中包含多个基本类型的值时,整个算术表达式的数据类型将发生自动提升,所有的b…...

C++ | Leetcode C++题解之第6题Z字形变换
题目: 题解: class Solution { public:string convert(string s, int numRows) {int n s.length(), r numRows;if (r 1 || r > n) {return s;}string ans;int t r * 2 - 2;for (int i 0; i < r; i) { // 枚举矩阵的行for (int j 0; j i &l…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...

篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...