FPGA + 图像处理(三)生成3x3像素矩阵

2024-04-08 07:12

本文主要是介绍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像素矩阵的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

MybatisX快速生成增删改查的方法示例

《MybatisX快速生成增删改查的方法示例》MybatisX是基于IDEA的MyBatis/MyBatis-Plus开发插件,本文主要介绍了MybatisX快速生成增删改查的方法示例,文中通过示例代... 目录1 安装2 基本功能2.1 XML跳转2.2 代码生成2.2.1 生成.xml中的sql语句头2

使用Python自动化生成PPT并结合LLM生成内容的代码解析

《使用Python自动化生成PPT并结合LLM生成内容的代码解析》PowerPoint是常用的文档工具,但手动设计和排版耗时耗力,本文将展示如何通过Python自动化提取PPT样式并生成新PPT,同时... 目录核心代码解析1. 提取 PPT 样式到 jsON关键步骤:代码片段:2. 应用 JSON 样式到

SpringBoot实现二维码生成的详细步骤与完整代码

《SpringBoot实现二维码生成的详细步骤与完整代码》如今,二维码的应用场景非常广泛,从支付到信息分享,二维码都扮演着重要角色,SpringBoot是一个非常流行的Java基于Spring框架的微... 目录一、环境搭建二、创建 Spring Boot 项目三、引入二维码生成依赖四、编写二维码生成代码五

Android与iOS设备MAC地址生成原理及Java实现详解

《Android与iOS设备MAC地址生成原理及Java实现详解》在无线网络通信中,MAC(MediaAccessControl)地址是设备的唯一网络标识符,本文主要介绍了Android与iOS设备M... 目录引言1. MAC地址基础1.1 MAC地址的组成1.2 MAC地址的分类2. android与I

PyQt5+Python-docx实现一键生成测试报告

《PyQt5+Python-docx实现一键生成测试报告》作为一名测试工程师,你是否经历过手动填写测试报告的痛苦,本文将用Python的PyQt5和python-docx库,打造一款测试报告一键生成工... 目录引言工具功能亮点工具设计思路1. 界面设计:PyQt5实现数据输入2. 文档生成:python-

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

Python如何自动生成环境依赖包requirements

《Python如何自动生成环境依赖包requirements》:本文主要介绍Python如何自动生成环境依赖包requirements问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录生成当前 python 环境 安装的所有依赖包1、命令2、常见问题只生成当前 项目 的所有依赖包1、

MySQL中动态生成SQL语句去掉所有字段的空格的操作方法

《MySQL中动态生成SQL语句去掉所有字段的空格的操作方法》在数据库管理过程中,我们常常会遇到需要对表中字段进行清洗和整理的情况,本文将详细介绍如何在MySQL中动态生成SQL语句来去掉所有字段的空... 目录在mysql中动态生成SQL语句去掉所有字段的空格准备工作原理分析动态生成SQL语句在MySQL

Java利用docx4j+Freemarker生成word文档

《Java利用docx4j+Freemarker生成word文档》这篇文章主要为大家详细介绍了Java如何利用docx4j+Freemarker生成word文档,文中的示例代码讲解详细,感兴趣的小伙伴... 目录技术方案maven依赖创建模板文件实现代码技术方案Java 1.8 + docx4j + Fr

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、