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) begin
	if(!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;
	else
		data_req <= 1'b0;
end

//像素点x坐标
always@ (posedge clk or negedge rst_n) begin
    if(!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) begin
    if(!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) begin
    if (!rst_n)
        cnt_h <= 11'd0;
    else begin
        if(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) begin
    if (!rst_n)
        cnt_v <= 11'd0;
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)
            cnt_v <= cnt_v + 1'b1;
        else 
            cnt_v <= 11'd0;
    end
end

endmodule

生成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) begin
	if(!rst_n)
		row3_data <= 0;
	else begin		
		if(de_i)
			row3_data <= data_i ;
		else
			row3_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) begin
	if(!rst_n) begin		
		vs_i_d <= 0;
		de_i_d <= 0;
	end
	else begin		
		vs_i_d  <= { vs_i_d[0], vs_i };
		de_i_d  <= { de_i_d[0], de_i };
	end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        row3_data_d1 <= 0;
        row3_data_d0 <= 0;
        row2_data_d0 <= 0;
    end
    else begin
        row3_data_d0 <= row3_data;
        row3_data_d1 <= row3_data_d0;
        row2_data_d0 <= row2_data;
    end
end

//在同步处理后的控制信号下,输出图像矩阵
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin		
		{mat11, mat12, mat13} <= 0;
		{mat21, mat22, mat23} <= 0;
		{mat31, mat32, mat33} <= 0;
	end
	else 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};
	end
	else begin		
		{mat11, mat12, mat13} <= 0;
		{mat21, mat22, mat23} <= 0;
		{mat31, mat32, mat33} <= 0;
	end
end

endmodule

行移位模块

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)begin
	if(de_i)
		ram_rd_addr <= ram_rd_addr + 1 ;	
	else
		ram_rd_addr <= 0 ;
end

//将数据使能延迟两拍
always@(posedge clk) begin
	de_i_d0 <= de_i;
	de_i_d1 <= de_i_d0;
	de_i_d2 <= de_i_d1;
end

//将RAM地址延迟2拍
always@(posedge clk ) begin
	ram_rd_addr_d0 <= ram_rd_addr;
	ram_rd_addr_d1 <= ram_rd_addr_d0;
end

//输入数据延迟3拍送入RAM
always@(posedge clk)begin
	data_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)begin
	data1_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/1ns

module 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)begin
    vs_i <= hdmi_vs;
    de_i <= hdmi_de;
end

initial begin
	clk = 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);
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        addr <= 0		;
        data_in <= 0	;
    end
    else if(data_req) begin
        data_in	 <= img[addr];
        addr	 <= addr + 1;
        if(addr == (1280*720-1))
			addr <= 0;
    end
end

hdmi_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是第三行数据(最后输入的那一行),可以看见数据的移位满足像素矩阵的要求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/523182.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【VTKExamples::Meshes】第九期 TestWindowedSincPolyDataFilter

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享VTK样例TestWindowedSincPolyDataFilter,并解析接口vtkWindowedSincPolyDataFilter,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! …

yolo训练数据集时怎么改变数据集的下载和存放路径

项目场景&#xff1a; 在yolov8训练官方给的或者公开的或者自己的数据集的时候&#xff0c;给数据集写个配置文件也就是.yaml文件肯定是必不可少的&#xff0c;但有时根据ultralytics文件夹里的别人博客提示根本解决不了问题。 问题描述 在模型训练时自己明明将数据集放到了u…

计算机毕业设计java 基于Android的拼图游戏app

当今社会&#xff0c;随着电子信息技术的发展&#xff0c;电子游戏也成为人们日常生活的一部分。这种娱乐方式结合了日新月异的技术&#xff0c;在游戏软件中结合了多种复杂技术。拼图游戏流行在各种电子产品上&#xff0c;从计算机&#xff0c;掌上游戏机到如今的手机&#xf…

C语言基础语法-教案20(预处理-条件编译)

最近给大家争取到一个 深夜福利 保证你在深夜手机刷到 嘎嘎香~ 那就是 官方授权 大流量卡 缺点&#xff1a;月租太便宜 185GB~ 100分钟通话时长~ 长期套餐~ 畅想自由的气息 流量自由的同时还拥有超长通话&#xff0c;而且免费领取。 名额有限&#xff0c;咱们废话不…

测试基础|为啥大多数功能测试会觉得测试平台不好用?自动化测试的几点思考

一、接口自动化到底要验证什么 个人觉得做什么事情前&#xff0c;应该想下做的动机和想要达成的目的&#xff0c;这样会减少很多不必要的弯路。 1. 自动化的原因 测试界普遍认为应该加自动化用于提高测试效率和保障&#xff1b; 测试kpi任务&#xff1b; 应对需要频繁执行…

mysql 数据库的MHA高可用

目录 一、MHA概述&#xff1a; 1.认识MHA&#xff1a; 2.MHA 的组成&#xff1a; 3.MHA 的特点&#xff1a; 4.MHA 工作原理&#xff1a; 5.数据流向&#xff1a; 6.数据同步方式&#xff1a; 7. mysql 的高可用 &#xff1a; 二. MySQL MHA 的搭建: 1. 修改 Master、…

vulhub中Struts2-016 远程代码执行漏洞复现

影响版本: 2.0.0 - 2.3.15 漏洞复现 在struts2中&#xff0c;DefaultActionMapper类支持以"action:"、"redirect:"、"redirectAction:"作为导航或是重定向前缀&#xff0c;但是这些前缀后面同时可以跟OGNL表达式&#xff0c;由于struts2没有对这…

蓝桥杯每日一题:杨辉三角形(组合计数)

下面的图形是著名的杨辉三角形&#xff1a; 如果我们按从上到下、从左到右的顺序把所有数排成一列&#xff0c;可以得到如下数列&#xff1a; 1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, ... 给定一个正整数 N&#xff0c;请你输出数列中第一次出现 N是在第几个数&#x…

Vue - 你能说说Vue3和Vue2相比,改进了哪些地方吗

难度级别:中级及以上 提问概率:85% Vue2终将面临停止维护,不过幸好Vue3做到了很好的向后兼容,可以使前端开发人员能够更平滑的过渡。在前端面试中,Vue3的相关知识也会越来越多,那么Vue3与Vue2相比,都做到了哪些改进呢? 从开发阶段讲…

VS Code远程连接服务器运行python程序

之前一直用pycharm连接服务器跑程序&#xff0c;pycharm需要本地和远程都存一份代码&#xff0c;然后把本地的更新同步到服务器上来实现代码修改&#xff0c;后来实习的时候发现企业里面都用VS Code&#xff0c;不得不说&#xff0c;VS Code真的很方便&#xff0c;直接连服务器…

Leetcode 56. 合并区间

心路历程&#xff1a; 这道题看起来很简单&#xff0c;但实际上操作起来很多细节&#xff0c;第一反应是朋友圈问题&#xff0c;于是想到了并查集去做&#xff0c;顺便复习了一下并查集。但是这道题用并查集的话只能98% A&#xff0c;无法全部通过。 这道题其实是考察数组重复…

如何使用 FastApi

安装 fastapi fastapi 是一个用于构建高性能 Web 应用的 Python 框架&#xff0c;它提供了简洁、高效的 API 开发体验。 pip install fastapi 安装 uvicorn uvicorn 是一个用于运行 FastAPI 应用的服务器&#xff0c;它可以将你的 FastAPI 代码部署到生产环境中。 pip inst…

MySQL基础【语句执行顺序】

一个SQL语句它的执行顺序对于我们思考题意有着很重要的关系 题意就是&#xff1a;找出哪些只逛超市不买单的人&#xff08;买单0元也算哦&#xff0c;可能是使用的是代金券吧&#xff09; 看到此题关键找出两个数据 参观过的人 和 买单的人 他们的差就是白嫖的人&#xff08;支…

【简单讲解想如何安装MXNet】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

C语言解决汉诺塔问题

背景 首先带大家了解一下汉诺塔问题 汉诺塔是一个典型的函数递归问题&#xff0c;汉诺塔描述了这样的场景&#xff0c;有三个柱子&#xff0c;A,B,C&#xff0c;A柱为起始柱&#xff0c;在A柱上面有若干大小不同的盘子&#xff0c;最下面的最大&#xff0c;最上面的最小&#x…

【教程】VOC数据集制作

语义分割任务中VOC数据集的制作&#xff0c;任务中只有一种标签&#xff1a;gas 文章目录 1、由黑白图像识别为txt标签2、txt转json3、数据集转VOC格式 1、由黑白图像识别为txt标签 由于使用CycleGAN网络进行风格迁移学习&#xff0c;生成了大量伪标签图像&#xff0c;因此需…

Python基于深度学习的屋内烟雾检测系统的研究与实现,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

实现Hello Qt 程序

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;QT❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、使用 "按钮" 实现 1、纯代码方式实现 2、可视化操作实现 &#xff08;1&#xff09…

计算机网络基础(二)

之前我们讲到了计算机网络的分类&#xff0c;现在我们继续讲解&#xff1a; 一.按网络的线路结构进行分类 1.星型 如上图&#xff0c;星型型拓扑结构是目前局域网普遍采用的一种拓扑结构。 特点&#xff1a; 星型拓扑结构是用一个节点作为中心节点&#xff0c;其他节点直接与…

常见的线程安全类

线程安全&#xff01;线程安全&#xff01;&#xff01;线程安全&#xff01;&#xff01;&#xff01; 鼠鼠我最近被线程安全这个词弄得好烦啊&#xff0c;那既然如此就来写一篇常见的线程安全类防止以后鼠鼠我的大脑又宕机了忘记了....... 这里我们讨论的线程安全的是指&am…