FPGA图像处理之三行缓存

文章目录

  • 一、前言
  • 二、FPGA实现三行缓存的架构
  • 三、Verilog代码实现
  • 四、仿真验证
  • 五、输入图像数据进行仿真验证


一、前言

  在 FPGA 做图像处理时,行缓存是一个非常重要的一个步骤,因为图像输入还有输出都是一行一行进行的,即处理完一行后再处理下一行。行缓存可以存储当前行和前一行的数据以及多行的数据,使得在处理当前行时能够方便地访问周围像素。许多图像处理的算法都需要几行的图像数据进行处理,因此行缓存是非常重要的,本文实现三行缓存,多行缓存的思想也是一致的。

二、FPGA实现三行缓存的架构

  由于图像数据一般都是从上到下从左到右一个一个输入进来,因此我们优先考虑使用FIFO,先进先出。按照一般想法,我们只需要三个FIFO,每个FIFO存储一行数据即可实现三行缓存,这里可以节省资源只使用两个FIFO实现,具体实现框框架如下:

在这里插入图片描述

  开始时,图像数据的第0行写入到FIFO1中,图像数据的第1行写入到FIFO2中。

在这里插入图片描述
  当第2行数据到来时写入到FIFO2中,同时输出写入的数据作为第二行;同时读出FIFO2中的数据写入到FIFO1中,并输出作为第一行,同时读出FIFO1中的数据输出作为第0行。

在这里插入图片描述
  同理,当第三行数据来临时写入到FIFO2中,同时输出写入数据作为第二行,再同时读出FIFO2中的数据写入到FIFO1中并输出作为第一行,同时读出FIFO2中的数据输出作为第0行。后面的行以此类推。

三、Verilog代码实现

  先看输入接口,输入为像素数据和有效信号,输出为三行数据以及有效信号。

 	input                                               sys_clk ,
    input                                               sys_rst ,
    input           [23:0]                              i_img_data  ,
    input                                               i_img_data_valid    ,
    output          [23:0]                              o_img_data_1line  ,
    output          [23:0]                              o_img_data_2line  ,
    output          [23:0]                              o_img_data_3line  ,
    output                                              o_img_data_valid    

  再次例化两个FIFO,位宽就为一个像素位宽,深度为一行中最多的像素数量。

img_line_buffer_fifo u0_img_line_buffer_fifo (
  .clk  (sys_clk        ),  // input wire clk
  .srst (sys_rst        ),  // input wire srst
  .din  (fifo1_wr_data  ),  // input wire [23 : 0] din
  .wr_en(fifo1_wr_en    ),  // input wire wr_en
  .rd_en(rd_en          ),  // input wire rd_en
  .dout (fifo1_q        ),  // output wire [23 : 0] dout
  .full (),                 // output wire full
  .empty()                  // output wire empty
);

img_line_buffer_fifo u1_img_line_buffer_fifo (
  .clk  (sys_clk        ),  // input wire clk
  .srst (sys_rst        ),  // input wire srst
  .din  (fifo2_wr_data  ),  // input wire [23 : 0] din
  .wr_en(fifo2_wr_en    ),  // input wire wr_en
  .rd_en(rd_en          ),  // input wire rd_en
  .dout (fifo2_q        ),  // output wire [23 : 0] dout
  .full (),                 // output wire full
  .empty()                  // output wire empty
);

  然后根据架构图编写出剩下的代码,编写仿真代码。

四、仿真验证

  仿真我们先设置图像宽度为50*50,这样仿真可以跑快一点,然后写入数据流为每次都是0-49循环。就像一幅图像的第0行数据是0-49,第1行的数据也是0-49,每一行的数据都是0-49。按照想法,我们每次输出的数据就是前三行的像素,也就是3行的 0-49数据,仿真代码如下:

`timescale 1ns / 1ps

module tb_img_3line_buffer();

reg                                                 sys_clk ;
reg                                                 sys_rst ;
reg                                                 i_img_data_valid    ;
reg             [23:0]                              i_img_data  ;
reg             [12:0]                              cnt ;
wire            [23:0]                              o_img_data_1line    ;
wire            [23:0]                              o_img_data_2line    ;
wire            [23:0]                              o_img_data_3line    ;
wire                                                o_img_data_valid    ;

initial begin
    sys_clk =0;
    sys_rst = 1;
    i_img_data_valid = 0;
    i_img_data = 'd0;
    #200;
    sys_rst = 0;
end

always #5 sys_clk = ~sys_clk;

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        i_img_data_valid <= 1'b0;
    else if(cnt == 49)
        i_img_data_valid <= 1'b0;
    else
        i_img_data_valid <= 1'b1;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        cnt <= 'd0;
    else if(cnt == 49)
        cnt <= 'd0;
    else if(i_img_data_valid == 1'b1)
        cnt <= cnt + 1'b1;
    else
        cnt <= cnt;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        i_img_data <= 'd0;
    else if(i_img_data == 49)
        i_img_data <= 'd0;
    else if(i_img_data_valid == 1'b1)
        i_img_data <= i_img_data + 1'b1;
    else
         i_img_data <= i_img_data;
end

img_3line_buffer#(
    .IMG_WIDTH         ( 50 ),
    .IMG_HEIGHT        ( 50 )
)u_img_3line_buffer(
    .sys_clk           ( sys_clk           ),
    .sys_rst           ( sys_rst           ),
    .i_img_data        ( i_img_data        ),
    .i_img_data_valid  ( i_img_data_valid  ),
    .o_img_data_1line  ( o_img_data_1line  ),
    .o_img_data_2line  ( o_img_data_2line  ),
    .o_img_data_3line  ( o_img_data_3line  ),
    .o_img_data_valid  ( o_img_data_valid  )
);

endmodule

  运行仿真

在这里插入图片描述
  可以看到写入的每一行数据都是0-49,写入两行后,开始输出数据。

在这里插入图片描述
  我们可以看到输出的三行数据都是0-49的数据。符合预期。我们修改一下仿真代码,写入2500个数据,对应50*50的图像大小,数据为0-2499,这样第0行的数据就是0-49,第1行的数据就是50-99,第2行的数据就是100-149,第3行的数据就是150-199。输出的数据就应该是(0,50,100),(1,51,101)以此类推,仿真代码如下:

`timescale 1ns / 1ps

module tb_img_3line_buffer();

reg                                                 sys_clk ;
reg                                                 sys_rst ;
reg                                                 i_img_data_valid    ;
reg             [23:0]                              i_img_data  ;
reg             [12:0]                              cnt ;
wire            [23:0]                              o_img_data_1line    ;
wire            [23:0]                              o_img_data_2line    ;
wire            [23:0]                              o_img_data_3line    ;
wire                                                o_img_data_valid    ;

initial begin
    sys_clk =0;
    sys_rst = 1;
    i_img_data_valid = 0;
    i_img_data = 'd0;
    #200;
    sys_rst = 0;
end

always #5 sys_clk = ~sys_clk;

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        i_img_data_valid <= 1'b0;
    else if(cnt == 2499)
        i_img_data_valid <= 1'b0;
    else
        i_img_data_valid <= 1'b1;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        cnt <= 'd0;
    else if(cnt == 2499)
        cnt <= 'd0;
    else if(i_img_data_valid == 1'b1)
        cnt <= cnt + 1'b1;
    else
        cnt <= cnt;
end

always @(posedge sys_clk) begin
    if(sys_rst == 1'b1)
        i_img_data <= 'd0;
    else if(i_img_data == 2499)
        i_img_data <= 'd0;
    else if(i_img_data_valid == 1'b1)
        i_img_data <= i_img_data + 1'b1;
    else
         i_img_data <= i_img_data;
end

img_3line_buffer#(
    .IMG_WIDTH         ( 50 ),
    .IMG_HEIGHT        ( 50 )
)u_img_3line_buffer(
    .sys_clk           ( sys_clk           ),
    .sys_rst           ( sys_rst           ),
    .i_img_data        ( i_img_data        ),
    .i_img_data_valid  ( i_img_data_valid  ),
    .o_img_data_1line  ( o_img_data_1line  ),
    .o_img_data_2line  ( o_img_data_2line  ),
    .o_img_data_3line  ( o_img_data_3line  ),
    .o_img_data_valid  ( o_img_data_valid  )
);

endmodule

  运行仿真

在这里插入图片描述
在这里插入图片描述
  验证完成和预期一致,后续一些图像处理算法需要用到这个行缓存。

五、输入图像数据进行仿真验证

  现在我们在仿真中输入一张图片,然后通过三行缓存输出,每次只取出第一行的数据写入到新的图片中:

在这里插入图片描述
  可以看出输出的图像和输入图像一模一样,文件大小也是一模一样,因此三行缓存是没问题的。

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

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

相关文章

实现uniapp天地图边界范围覆盖

前言&#xff1a; 在uniapp中&#xff0c;难免会遇到使用地图展示的功能&#xff0c;但是百度谷歌这些收费的显然对于大部分开源节流的开发者是不愿意接受的&#xff0c;所以天地图则是最佳选择。 此篇文章&#xff0c;详细的实现地图展示功能&#xff0c;并且可以自定义容器宽…

Python画笔案例-086 turtle 多线程绘画

1、turtle 多线程绘画 通过 python 的turtle 库 多线程绘画,如下图: 2、实现代码 turtle 库 多线程绘画,以下为实现代码: """多线程绘画.py """ from random import random,randint from turtle import Turtle,Screen from threading

SpringDataRedis快速入门

SpringDataRedis 什么是SpringDataRedis SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis SpringDataRedis中提供了RedsiTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类…

redis基础—主从同步原理与配置以及哨兵模式

一&#xff1a;redis的主从同步原理 1.slave节点发送同步请求到master节点 2.slave节点通过master节点的认证开始进行同步 3.认证结束后&#xff0c;master节点开启bgsave进程 4.master节点会开启bgsave进程发送内存快照rbd到slave节点&#xff0c;在此过程中是异步操作&…

【原创】java+springboot+mysql在线文件管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

Docker学习笔记(2)- Docker的安装

1. Docker的基本组成 镜像&#xff08;image&#xff09;&#xff1a;Docker镜像就像是一个模板&#xff0c;可以通过这个模板来创建容器服务。通过一个镜像可以创建多个容器。最终服务运行或者项目运行就是在容器中。容器&#xff08;container&#xff09;&#xff1a;Docker…

Spring6梳理14——依赖注入之P命名空间

以上笔记来源&#xff1a; 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09;https://www.bilibili.com/video/BV1kR4y1b7Qc 目录 ①搭建模块 ②引入配置文件 ③创建bean-dip.xml文件 ④创建课程类文件 ⑤创建学生…

基于SpringBoot+Vue+uniapp微信小程序的校园反诈骗微信小程序的详细设计和实现(源码+lw+部署文档+讲解等)

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

day-69 使二进制数组全部等于 1 的最少操作次数 II

思路 与3191. 使二进制数组全部等于 1 的最少操作次数 I思路类似&#xff0c;区别在于该题每次将下标i开始一直到数组末尾所有元素反转&#xff0c;所以我们用一个变量可以统计翻转次数 解题过程 从左向右遍历数组的过程中&#xff0c;有两种情况需要进行翻转&#xff1a;1.当…

【Linux】内存文件系统的I/O、重定向

文章目录 1. 系统中的文件2. 回顾C中的文件接口3. 文件类的系统调用3.1 open3.2 文件描述符 4. IO的基本过程5.重定向5.1 引入重定向5.2 系统中的重定向接口 6. 缓冲区问题7. 简单版shell的实现 1. 系统中的文件 在学习完Linux权限后&#xff0c;我们清楚的知道&#xff1a;文…

【JVM】内存模型

文章目录 内存模型的基本概念案例 程序计数器栈Java虚拟机栈局部变量表栈帧中局部变量表的实际状态栈帧中存放的数据有哪些 操作数栈帧数据 本地方法栈 堆堆空间是如何进行管理的? 方法区静态变量存储 直接内存直接内存的作用 内存模型的基本概念 在前面的学习中,我们知道了字…

论文笔记:Pre-training to Match for Unified Low-shot Relation Extraction

论文来源&#xff1a;ACL 2022 论文地址&#xff1a;https://aclanthology.org/2022.acl-long.397.pdf 论文代码&#xff1a;https://github.com/fc-liu/MCMN &#xff08;笔记不易&#xff0c;请勿恶意转载抄袭&#xff01;&#xff01;&#xff01;&#xff09; 目录 A…

从头预训练一只迷你 LLaMA 3_llama3 预训练预处理

我将向你展示如何使用 LLama 3.1&#xff08;一个本地运行的模型&#xff09;来执行GraphRAG操作&#xff0c;总共就50号代码。。。 首先&#xff0c;什么是GraphRAG&#xff1f;GraphRAG是一种通过考虑实体和文档之间的关系来执行检索增强生成的方式&#xff0c;关键概念是节…

Elasticsearch学习笔记(七)安装并配置Metricbeat

Metricbeat 是一个轻量级的开源数据采集器&#xff0c;专门用于收集操作系统和服务的指标&#xff08;metrics&#xff09;。它是 Elastic Stack&#xff08;即 ELK Stack&#xff09;的一部分&#xff0c;通常用于监控系统性能、收集应用程序和服务器的性能指标&#xff0c;并…

【大模型】AI视频课程制作工具开发

1. 需求信息 1.1 需求背景 讲师们在制作视频的过程中&#xff0c;发现录制课程比较麻烦&#xff0c;要保证环境安静&#xff0c;保证录制过程不出错&#xff0c;很容易反复重复录制&#xff0c;为了解决重复录制的工作量&#xff0c;想通过 ai 课程制作工具&#xff0c;来解决…

字节跳动青训营——入营考核解答(持续更新中~~~)

考核内容&#xff1a; 在指定的题库中自主选择不少于 15 道算法题并完成解题&#xff0c;其中题目难度分配如下&#xff1a; 简单题不少于 10 道中等题不少于 4 道困难题不少于 1 道 解答代码 5.简单四则运算 &#xff08;中&#xff09; 代码实现&#xff1a; import ja…

TON(六)——fift算法,注释的改写

系列文章目录 TON&#xff08;五&#xff09; TON&#xff08;四&#xff09; TON&#xff08;三&#xff09; TON&#xff08;二&#xff09; TON&#xff08;一&#xff09; 前言 fift是一门十分强大的栈编程语言&#xff0c;&#xff0c;在TON中它是由c编译而成的语言…

WordPress官方发布“新”插件“SCF”(安全自定义字段)

安全自定义字段 (SCF) 为您提供了处理数据所需的所有工具&#xff0c;从而将 WordPress 网站转变为成熟的内容管理系统。 使用 SCF 插件可以完全控制您的 WordPress 编辑屏幕、自定义字段数据等。 按需添加字段—SCF字段生成器允许您快速轻松地将字段添加到 WP 编辑屏幕&…

第一个servlet程序

文章目录 在原有工程上建立模块前端配置前后端映射关系添加外部依赖库后端代码启动配置 在原有工程上建立模块 添加web框架 前端 应用结构 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>第一…

什么是SYN flood,如何处理

在数字化时代&#xff0c;随着互联网的普及和技术的飞速发展&#xff0c;网络安全问题变得日益严峻。Flood攻击&#xff0c;作为一种典型的网络攻击手段&#xff0c;对个人和企业的信息安全构成了重大威胁。通过深入了解Flood攻击的概念、特点、影响及解决方案&#xff0c;我们…