【数字图像处理】水平翻转、垂直翻转

图像翻转是常见的数字图像处理方式,分为水平翻转和垂直翻转。本文主要介绍 FPGA 实现图像翻转的基本思路,以及使用紫光同创 PGL22G 开发板实现数字图像水平翻转、垂直翻转的过程。

目录

1 水平翻转与垂直翻转

2 FPGA 布署与实现

2.1 功能与指标定义

2.2 逻辑设计

2.3 上板调试


1 水平翻转与垂直翻转

        在数字图像处理中,图像翻转是指将图像进行水平或者垂直方向的翻转,使其呈现不同的效果,分为水平翻转和垂直翻转。

        水平翻转是将图像沿着水平轴线进行翻转,将左半部分和右半部分进行交换。垂直翻转则是将图像沿着垂直轴线进行翻转,将上部分和下部分进行交换。

        使用 FPGA 实现水平翻转时,由于每次向 DDR3 写入一行数据,因此可以借助写 DDR3 之前的 Dual-port RAM,将一行数据进行翻转,实现水平翻转的功能。FPGA 逻辑设计的大致思路是:写 Dual-port RAM 时,写地址从 0 增加;读 Dual-port RAM 写 DDR3 时,读地址从最大值逐步减小到 0 结束。 

        对于垂直翻转功能,则需要借助 DDR3 来实现。像素数据第 1 行写到第 N 行的位置,第 2 行写到第 N-1 行的位置,以此类推。FPGA 逻辑设计的大致思路是:每一行数据写入 DDR3 时,用图像高度减去原来的行地址,作为新的行地址。

2 FPGA 布署与实现

2.1 功能与指标定义

        使用紫光同创 FPGA 平台实现图像翻转功能,FPGA 需要实现的功能与指标如下:

(1)与电脑的串口通信,用于接收上位机下发的图像数据,波特率为 256000 Bd/s;

(2)水平翻转与图像翻转:借助 Dual-port RAM 与 DDR3,分别实现水平翻转与垂直翻转功能;

(3)DDR3 读写控制,将处理前后的图像数据分别写入 DDR3 的不同区域,实现图像的拼接;

(4)HDMI 输出,输出一路 HDMI 信号源,用于将拼接后的图像显示在外接显示器上,分辨率为 1024×768。

2.2 逻辑设计

        图像翻转工程主要的设计模块层次与功能说明如下:

模块名称功能说明
top_uartuart_rx_slice串口接收驱动模块
uart_rx_parse串口数据解析模块,从上位机接收 8bit 原始图像
top_vidinvidin_pipelinepipeline 单元模块,缓存两行图像数据,并将数据提交到 ddr3 数据调度模块
merge_outdvi_timing_genHDMI 视频时序产生模块
dvi_ddr_rd根据 HDMI 控制信号,提交读指令到 ddr3 数据调度模块
dvi_encoderHDMI 输出编码(8b10b 编码)与输出驱动模块

其中,vidin_pipeline 模块实现图像翻转功能,代码如下:

`timescale 1 ns/ 1 ps`include "../ddr_scheduler/ddr_parameter.vh"module vidin_pipeline (// System levelsys_rst,sys_clk,ddr_init_done,flip_lr,flip_ud,// pipeline input portspipeline_in_info,pipeline_in_data,pipeline_in_wren,pipeline_in_end,// pipeline output portspipeline_out_info,pipeline_out_data,pipeline_out_vld,pipeline_out_end,// User defined ports for ddr_schedulerddr_wr_baseaddr,ddr_wr_addr,ddr_wr_priority,ddr_wr_burstsize,ddr_wr_req,ddr_wr_ack,ddr_wr_end,ddr_wr_rden,ddr_wr_q,ddr_wr_mask
);// IO direction/register definitions
input                     sys_rst;
input                     sys_clk;
input                     ddr_init_done;
input                     flip_lr;
input                     flip_ud;input  [31:0]             pipeline_in_info;
input  [31:0]             pipeline_in_data;
input                     pipeline_in_wren;
input                     pipeline_in_end;output [31:0]             pipeline_out_info;
output [31:0]             pipeline_out_data;
output                    pipeline_out_vld;
output                    pipeline_out_end;input  [`DDR_A_W-1:0]     ddr_wr_baseaddr;
output [`DDR_A_W-1:0]     ddr_wr_addr;
output                    ddr_wr_priority;
output [`DDR_BURST_W-1:0] ddr_wr_burstsize;
output                    ddr_wr_req;
input                     ddr_wr_ack;
input                     ddr_wr_end;
input                     ddr_wr_rden;
output [`DDR_D_W-1:0]     ddr_wr_q;
output [`DDR_D_W/8-1:0]   ddr_wr_mask;reg    [31:0]             pipeline_out_info;
reg    [31:0]             pipeline_out_data;
reg                       pipeline_out_vld;
reg                       pipeline_out_end;// internal signal declarations
reg    [`DDR_CMD_W-1:0]   ddr_cmd_data;
reg                       ddr_cmd_vld;reg    [9:0]              blk_mem_waddr;
reg    [31:0]             blk_mem_wdata;
reg                       blk_mem_wren;
reg    [9:0]              blk_mem_raddr;
wire   [31:0]             blk_mem_rdata;
reg                       blk_mem_rden;
reg                       blk_mem_rd_busy;
reg                       blk_mem_rd_end;
reg                       blk_mem_rd_vld;// line_buffer_inst: Dual-port ram for line pixel data buffer
blk_mem_1024x32b line_buffer_inst (.wr_data         (blk_mem_wdata   ), // input 32-bit.wr_addr         (blk_mem_waddr   ), // input 10-bit.wr_en           (blk_mem_wren    ), // input 1-bit.wr_clk          (sys_clk         ), // input 1-bit.wr_rst          (sys_rst         ), // input 1-bit.rd_addr         (blk_mem_raddr   ), // input 10-bit.rd_data         (blk_mem_rdata   ), // output 32-bit.rd_clk          (sys_clk         ), // input 1-bit.rd_rst          (sys_rst         )  // input 1-bit
);
// End of line_buffer_inst instantiationalways @(posedge sys_rst or posedge sys_clk) beginif (sys_rst) beginblk_mem_waddr <= {10{1'b0}};blk_mem_wdata <= {32{1'b0}};blk_mem_wren  <= 1'b0;endelse beginblk_mem_wdata <= pipeline_in_data;blk_mem_wren  <= pipeline_in_wren;// Use ping-pong storage hereif (pipeline_in_end) blk_mem_waddr <= {~blk_mem_waddr[9], {9{1'b0}}};else if (pipeline_in_wren) blk_mem_waddr <= {blk_mem_waddr[9], blk_mem_waddr[0+:9]+1'b1};end
endalways @(posedge sys_rst or posedge sys_clk) beginif (sys_rst) beginblk_mem_raddr   <= {10{1'b0}};blk_mem_rden    <= 1'b0;blk_mem_rd_busy <= 1'b0;blk_mem_rd_end  <= 1'b0;blk_mem_rd_vld  <= 1'b0;endelse beginif (~blk_mem_rd_busy && pipeline_in_end) beginblk_mem_rd_busy <= 1'b1;if (flip_lr == 1'b0) blk_mem_raddr <= {blk_mem_raddr[9], {9{1'b0}}};elseblk_mem_raddr <= {blk_mem_raddr[9], {9{1'b1}}};endelse if (blk_mem_rd_busy) begin// Use ping-pong storage hereif (flip_lr == 1'b0) beginif (& blk_mem_raddr[0+:9]) blk_mem_raddr <= {~blk_mem_raddr[9], {9{1'b0}}};else blk_mem_raddr <= {blk_mem_raddr[9], blk_mem_raddr[0+:9]+1'b1};endelse beginif (blk_mem_raddr[0+:9] == 0)blk_mem_raddr <= {~blk_mem_raddr[9], {9{1'b1}}};elseblk_mem_raddr <= {blk_mem_raddr[9], blk_mem_raddr[0+:9]-1'b1};end// Pull down read busy flagif (flip_lr == 1'b0) beginif (& blk_mem_raddr[0+:9]) blk_mem_rd_busy <= 1'b0;endelse beginif (blk_mem_raddr[0+:9] == 0)blk_mem_rd_busy <= 1'b0;endendblk_mem_rden <= blk_mem_rd_busy;blk_mem_rd_vld <= blk_mem_rden;if (blk_mem_rd_busy) beginif (flip_lr == 1'b0) beginif (& blk_mem_raddr[0+:9]) blk_mem_rd_end <= 1'b1;else blk_mem_rd_end <= 1'b0;endelse beginif (blk_mem_raddr[0+:9] == 0)blk_mem_rd_end <= 1'b1;else blk_mem_rd_end <= 1'b0;endendelseblk_mem_rd_end <= 1'b0;end
endalways @(posedge sys_rst or posedge sys_clk) beginif (sys_rst) beginpipeline_out_info <= {32{1'b0}};pipeline_out_data <= {32{1'b0}};pipeline_out_vld  <= 1'b0;pipeline_out_end  <= 1'b0;endelse beginif (pipeline_in_end) pipeline_out_info <= pipeline_in_info;pipeline_out_data <= blk_mem_rdata;pipeline_out_vld  <= blk_mem_rd_vld;pipeline_out_end  <= blk_mem_rd_end;end
end/
always @(posedge sys_rst or posedge sys_clk) beginif (sys_rst) beginddr_cmd_data <= {`DDR_CMD_W{1'b0}};ddr_cmd_vld <= 1'b0;endelse beginif (pipeline_in_end) beginddr_cmd_data[32+:`DDR_BURST_W] <= 8'h7F; // used fixed size here, 512 /4 -1 = 127if (flip_ud == 1'b0)ddr_cmd_data[0+:28] <= {pipeline_in_info[0+:16], 12'd0};elseddr_cmd_data[0+:28] <= {16'd383-pipeline_in_info[0+:16], 12'd0};endif (blk_mem_rd_end) ddr_cmd_vld <= 1'b1;else ddr_cmd_vld <= 1'b0;end
end// vid_ddr_wr_inst: ddr write control module
vid_ddr_wr vid_ddr_wr_inst (.sys_rst          (sys_rst           ), // input 1-bit.sys_clk          (sys_clk           ), // input 1-bit.ddr_init_done    (ddr_init_done     ), // input 1-bit.vid_cmd_data     (ddr_cmd_data      ), // input 40-bit.vid_cmd_vld      (ddr_cmd_vld       ), // input 1-bit.vid_img_data     (blk_mem_rdata     ), // input 32-bit.vid_img_data_vld (blk_mem_rd_vld    ), // input 1-bit.ddr_wr_baseaddr  (ddr_wr_baseaddr   ), // input 27-bit.ddr_wr_addr      (ddr_wr_addr       ), // output 27-bit.ddr_wr_priority  (ddr_wr_priority   ), // output 1-bit.ddr_wr_burstsize (ddr_wr_burstsize  ), // output 8-bit.ddr_wr_req       (ddr_wr_req        ), // output 1-bit.ddr_wr_ack       (ddr_wr_ack        ), // input 1-bit.ddr_wr_end       (ddr_wr_end        ), // input 1-bit.ddr_wr_rden      (ddr_wr_rden       ), // input 1-bit.ddr_wr_q         (ddr_wr_q          ), // output 128-bit.ddr_wr_mask      (ddr_wr_mask       )  // output 16-bit
);
// End of vid_ddr_wr_inst instantiationendmodule

2.3 上板调试

        使用 PyQt5 和 OpenCV 库编写上位机程序,通过串口发送原始图像数据,以及水平翻转、垂直翻转参数。

# -*- Coding: UTF-8 -*-
import cv2
import sys
import struct
import numpy as np
from PyQt5 import Qt, QtGui, QtCore, QtWidgets, QtSerialPortclass mainWindow(Qt.QWidget):def __init__(self, com_port, parent=None):super(mainWindow, self).__init__(parent)self.setFixedSize(530, 384)self.setWindowTitle("PGL OpenCV Tool")self.flip_horizontal = Falseself.flip_vertical = False# 创建标签与按钮self.img_widget = QtWidgets.QLabel()self.btn1 = QtWidgets.QPushButton("打开")self.btn1.clicked.connect(self.getfile)self.btn2 = QtWidgets.QPushButton("关闭")self.btn2.clicked.connect(self.close)self.btn3 = QtWidgets.QPushButton("水平翻转")self.btn3.clicked.connect(self.flip_lr)self.btn4 = QtWidgets.QPushButton("垂直翻转")self.btn4.clicked.connect(self.flip_ud)# 创建布局centralLayout = QtWidgets.QVBoxLayout()centralLayout.addWidget(self.img_widget)bottomLayout = QtWidgets.QHBoxLayout()bottomLayout.addWidget(self.btn1)bottomLayout.addWidget(self.btn2)bottomLayout.addWidget(self.btn3)bottomLayout.addWidget(self.btn4)centralLayout.addLayout(bottomLayout)self.setLayout(centralLayout)# 串口对象self.COM = QtSerialPort.QSerialPort()self.COM.setPortName(com_port)self.COM.setBaudRate(256000)self.open_status = Falseself.row_cnt = 0self.img = Noneself.timer = QtCore.QTimer()self.timer.timeout.connect(self.sendImage)self.startup()def startup(self):"""Write code here to run once"""for com_port in QtSerialPort.QSerialPortInfo.availablePorts():print(com_port.portName())# Try open serial portif not self.COM.open(QtSerialPort.QSerialPort.ReadWrite):self.open_status = Falseprint("Open Serial Port failed.")else:self.open_status = Truedef flip_lr(self):"""水平翻转回调函数"""if self.flip_horizontal == False:self.flip_horizontal = Trueself.btn3.setStyleSheet("QPushButton{color:rgb(128,128,255)}")else:self.flip_horizontal = Falseself.btn3.setStyleSheet("QPushButton{color:rgb(0,0,0)}")def flip_ud(self):"""垂直翻转回调函数"""if self.flip_vertical == False:self.flip_vertical = Trueself.btn4.setStyleSheet("QPushButton{color:rgb(128,128,255)}")else:self.flip_vertical = Falseself.btn4.setStyleSheet("QPushButton{color:rgb(0,0,0)}")def getfile(self):"""获取图像路径"""fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file','C:\\Users\\Administrator\\Pictures', "Image files(*.jpg *.png)")self.clipImage(fname[0])self.updateImage()self.sendImage()def clipImage(self, fname):"""读取并裁剪图片至512x384大小"""if fname:img = cv2.imread(fname, cv2.IMREAD_COLOR)img_roi = img[:384,:512,:]print(img_roi.shape)cv2.imwrite('./img_roi.png', img_roi)def updateImage(self):"""显示裁剪后的图像"""self.img_widget.setPixmap(QtGui.QPixmap('./img_roi.png'))self.img = cv2.imread('./img_roi.png')if self.open_status:self.timer.start(100)def sendImage(self):"""通过串口发送图片"""pattern = ">2BH{:d}B".format(512*3)# 获取图像翻转信息flip_flag = 0x00if self.flip_horizontal:flip_flag = flip_flag + 0x10if self.flip_vertical:flip_flag = flip_flag + 0x01# 发送图像数据if self.open_status:if self.row_cnt == 384+3:self.row_cnt = 0self.timer.stop()else:args1 = [0x55, flip_flag, self.row_cnt]args2 = [rgb for rgb in self.img[(self.row_cnt % 384),:].reshape(-1)]send_data = struct.pack(pattern, *(args1+args2))self.row_cnt += 1self.COM.write(send_data)def closeEvent(self, event):super().closeEvent(event)#self.slider_window.close()# 定时器停止self.timer.stop()if self.open_status:self.COM.close() # 关闭串口def main():app = QtWidgets.QApplication(sys.argv)window = mainWindow('COM21')window.show()#for win in (window, window.slider_window):#   win.show()sys.exit(app.exec_())if __name__ == "__main__":main()

        连接 HDMI 线和串口线,选择与发送图像,就可以看到 FPGA 的处理效果了。以下是水平翻转效果。

以下是垂直翻转效果。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java学习笔记-day05-响应式编程初探-自定义实现Reactive Streams规范

最近在学响应式编程&#xff0c;这里先记录下&#xff0c;响应式编程的一些基础内容 1.名词解释 Reactive Streams、Reactor、WebFlux以及响应式编程之间存在密切的关系&#xff0c;它们共同构成了在Java生态系统中处理异步和响应式编程的一系列工具和框架。 Reactive Streams…

线性代数——(期末突击)矩阵(下)-习题篇(初等变换求逆矩阵、矩阵乘法、求矩阵方程、求线性方程组、解齐次线性方程组)

目录 初等变换求逆矩阵 矩阵乘法 求矩阵方程 求线性方程组 解齐次线性方程组 带有未知数的方程组 初等变换求逆矩阵 如果,则A可逆&#xff0c;且 例题&#xff1a; &#xff0c;求A的逆矩阵。 矩阵乘法 求AB&#xff0c;BA. 矩阵之间的乘法是行乘以列&#xff0c;以这…

2024年全国教资笔试报名流程(建议电脑报名),看看有啥新要求?

一.报名、考试时间节点 1.笔试报名时间: 2024年1月12日-15日 2.笔试考试时间:2024年3月9日 3.笔试成绩查询时间:2024年4月15日 4.面试报名时间:2024年4月15日 5.面试考试时间:2024年5月18日 6.面试成绩查询时间:2024年6月14日 二.笔试报名流程: 登陆→考生注册 →填报个…

git打tag以及拉取tag

场景&#xff1a;某次git代码发布后定版记录&#xff0c;将发版所在的commit时候代码打上tag记录&#xff0c;方便后期切换到对应tag代码位置。 查看所有tag名 git tag// 1.1.0 // 1.0.0查看tag和描述 git tag -l -n//1.0.0 云监管一期项目完结 //1.1.0 …

Android BUG 之 Error: Activity class {} does not exist

项目场景&#xff1a; 更换包名&#xff0c;运行报错 问题描述 原因分析&#xff1a; 在替换包名的时候要确认&#xff0c;配置文件跟build中的保持一致&#xff0c;在更换后还要将旧包的缓存数据清理掉 解决方案&#xff1a; 1 替换后删除 app 下的build 文件夹 2 Rebuild Pr…

AI论文润色平台一览,让你的论文更加流畅易懂!

之前其实比较犹豫&#xff0c;是否应该整理一篇关于可辅助论文写作的在线平台的文章。因为论文这个事情&#xff0c;更重要的是要有个人的思考&#xff0c;要亲自动手写&#xff0c;这涉及到诚信的问题。然而&#xff0c;通过AI直接生成的论文可能很难通过查重和AI检测&#xf…

STM32F103C8T6(HAL库函数 - 内部Flash操作)

简介 STM32F103C8T6 内部Flash 为 64KB&#xff0c;本次将对他多余空间进行读写。 介绍 数据手册下载 STM32F103x8/STM32F103xB 数据手册 包含Flash Memory Page分布 STM32F设备命名 设备容量类型 中容量类型 内部空间介绍 64 KBytes大小Flash Memory 从 0x0800 0000 ~…

理解接雨水算法

一、IDEA注释显示图片 在做题时&#xff0c;需要对照这图片&#xff0c;才能更好的梳理思路。 首先&#xff0c;注释里添加<img/>标签 之后&#xff0c;将鼠标光标放置在需要以阅读模式预览注释的地方&#xff0c;然后按快捷键CtrlAltQ即可 二、接雨水算法 先看接雨水…

apt和apt-get的区别

文章目录 环境问题背景区别进度条显示可更新包的数量upgrade 对比apt-get 过时了吗使用apt还是apt-get总结参考 环境 RHEL 9.3Docker Community 24.0.7Ubuntu Docker image jammy 22.04lunar 23.04 Ubuntu 22.04 问题 apt 和 apt-get 有一些相似之处。比如&#xff0c;如果想…

Vue-9、Vue事件修饰符

1、prevent 阻止默认事件 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>事件修饰符</title><!--引入vue--><script type"text/javascript" src"https://cdn.jsdeliv…

Native Crash回溯栈

获取调用栈四种方案&#xff1a;Android Native Crash 收集 1、使用系统的<unwind.h>库 可以获取到出错文件与函数名。只不过需要自己解析函数符号&#xff0c;同时经常会捕获到系统错误&#xff0c;需要手动过滤。 2、libcorkscrew 在4.1.1以上&#xff0c;5.0以下&…

微信小程序+前后端开发学习材料

目录结构 全局文件 1.app.json 文件 用来对微信小程序进行全局配置&#xff0c;决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。文件内容为一个 JSON 对象。 1.1 page用于指定小程序由哪些页面组成&#xff0c;每一项都对应一个页面的 路径&#xff08;含文…

2024年第九届机器学习技术国际会议(ICMLT 2024) 即将召开

2024年第九届机器学习技术国际会议&#xff08;ICMLT 2024&#xff09;将于2024年5月24-26日在挪威奥斯陆举行。ICMLT 2024旨在讨论机器学习技术领域的最新研究技术现状和前沿趋势&#xff0c;为来自世界各地的科学家、工程师、实业家、学者和其他专业人士提供一个互动和交流的…

SCT2A27STER:5.5V-100V Vin,4A峰值限流,高效异步降压DCDC转换器,集成200mA LDO

特性&#xff1a; • 5.5V-100V 输入电压范围 • 最大输出电压&#xff1a;30V • 2A 连续输出电流 • 4A峰值电流限制 • 1.2V 1% 反馈电压 • 集成500mΩ 高侧功率 MOSFETs • 可选5V或者3.3V,输出一路200mA LDO • 25uA静态电流&#xff0c;VBIAS连接到高于6V的辅助电源 •…

Ubuntu18.04 安装 qt 5.15.2

一.安装qt 1.下载 在线安装包 使用国内镜像源在线安装QT(2023.3.25更新)_qt国内镜像-CSDN博客 2.安装 &#xff08;1&#xff09;QT库安装&#xff1a; 注意&#xff1a;我安装时 勾选 Qt Design studio 会导致报错&#xff0c;直接不勾选。 注意&#xff1a;Qtcreator 无…

Orchestrator源码解读2-故障失败发现

目录 目录 前言 核心流程函数调用路径 GetReplicationAnalysis 故障类型和对应的处理函数 拓扑结构警告类型 与MHA相比 前言 Orchestrator另外一个重要的功能是监控集群&#xff0c;发现故障。根据从复制拓扑本身获得的信息&#xff0c;它可以识别各种故障场景。Orchest…

Deno 1.22 发布

目录 更新默认的类型检查模式 移除Deno.emit()Deno.formatDiagnostics()和Deno.applySourceMap() API 默认启用Deno命名空间 --no-config标识 Navigator.userAgent 更新 Deno.resolveDns() API 引入新的Response.json()静态方法 在 LSP 默认启用 Linting 对测试运行程…

7 集中式日志和分布式跟踪

文章目录 日志聚合模式日志集中化的简单解决方案使用日志并输出分布式跟踪Spring Cloud Sleuth实现分布式跟踪 小结 前面的文章&#xff1a; 1、 1 一个测试驱动的Spring Boot应用程序开发 2、 2 使用React构造前端应用 3、 3 试驱动的Spring Boot应用程序开发数据层示例 4、…

北京大学漏洞报送证书

获取来源&#xff1a;edusrc&#xff08;教育漏洞报告平台&#xff09; url&#xff1a;教育漏洞报告平台(EDUSRC) 兑换价格&#xff1a;30金币 获取条件&#xff1a;北京大学任意中危或以上级别漏洞

机器学习系列--R语言随机森林进行生存分析(2)

随机森林&#xff08;Breiman 2001a&#xff09;&#xff08;RF&#xff09;是一种非参数统计方法&#xff0c;需要没有关于响应的协变关系的分布假设。RF是一种强大的、非线性的技术&#xff0c;通过拟合一组树来稳定预测精度模型估计。随机生存森林&#xff08;RSF&#xff0…