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

图像翻转是常见的数字图像处理方式,分为水平翻转和垂直翻转。本文主要介绍 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.mzph.cn/news/610186.shtml

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

相关文章

Amazing OpenAI API:把非 OpenAI 模型都按 OpenAI API 调用

分享一个有趣的小工具&#xff0c;10MB 身材的小工具&#xff0c;能够将各种不同的模型 API 转换为开箱即用的 OpenAI API 格式。 让许多依赖 OpenAI API 的软件能够借助开发者能够接触到的&#xff0c;非 OpenAI 的 API 私有部署和使用起来。 写在前面 这个小工具软件写于两…

ChatGPT知名开源项目有哪些

ChatGPT-Next-Web&#xff1a;基于ChatGPT API的私有化部署网页聊天系统 主要功能&#xff1a; 只需在 1 分钟内即可在 Vercel 上一键免费部署&#xff0c;支持私有服务器快速部署&#xff0c;支持使用私有域名支持ChatGPT3.5、4等常见模型Linux/Windows/MacOS 上的紧凑型客户…

Ribbon学习思维导图

参考资料 1、OpenFeign与Ribbon源码分析总结与面试题 2、万字剖析OpenFeign整合Ribbon实现负载均衡的原理 3、扒一扒Nacos、OpenFeign、Ribbon、loadbalancer组件协调工作的原理 4、OpenFeign原来是这么基于Ribbon来实现负载均衡的

第18集《佛法修学概要》

戊五、结示法要 请大家打开讲义第四十四页。我们讲因果同时&#xff0c;借缘显现。 从因果转变的角度&#xff0c;佛教是说“罪从心起将心忏&#xff0c;心若灭时罪亦亡。”那么我们要知道&#xff0c;业是怎么来的&#xff1f;怎么会有业&#xff1f;为什么苹果掉下来&#x…

DEATHNOTE: 1

首先通过kali使用nmap进行主机发现 发现IP地址为192.168.75.129的主机 发现其22端口和80端口开放 对其进行详细扫描发现其操作系统是Linux 4.15 - 5.6 访问192.168.75.129&#xff1a;80会重定向到另一个页面 修改一下kali的/ect/hosts的添加一句192.168.75.129 deathnote.v…

【Vue3】2-8 : 条件渲染与列表渲染及注意点

本书目录&#xff1a;点击进入 一、条件渲染 - v-if 表达式 1.1 真值与假值 1.2 v-if &#xff0c;v-else-if &#xff0c;v-else 1.3 实战&#xff1a;isShow1 为真值时 显示 bbbbb &#xff1e; 代码 &#xff1e; 效果 二、列表渲染 - v-for 2.1 渲染 - 数组 &…

Spring基于注解的AOP控制事务

首先在.xml中开启sprong对注解事务的支持 applicationContext.xml <tx:annotation-driven transaction-manager"transactionManager"/> 然后再Service中加上注解 service Service Transactional(readOnlytrue,propagation Propagation.SUPPORTS) public cl…

Vue-11、Vue计算属性

Vue计算属性是Vue实例的属性&#xff0c;用来根据已有的数据进行计算得到新的数据。计算属性的值会根据它的依赖缓存起来&#xff0c;在依赖没有发生改变时直接返回缓存的值&#xff0c;提高了性能。 计算属性的定义方式为在Vue实例中使用computed关键字&#xff0c;并将计算属…

(Matlab)基于CNN-GRU的多输入分类(卷积神经网络-门控循环单元网络)

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、部分程序&#xff1a; 四、完整代码数据分享&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平台编译…

雍禾医疗启动“毛发森林”公益计划 为地球种植“发际线”

不久前&#xff0c;由雍禾植发、新华网及中国绿化基金会三方共同发起的 “毛发森林地球生发计划”在内蒙古自治区阿拉善盟额济纳旗揭牌启动,此计划将陆续在西部严重沙化地区植下十万棵梭梭树改善荒漠化地区环境。 据悉,早在2023年3月,雍禾植发就主动与新华网一起启动“让美好生…

Rabbitmq 消息可靠性保证

1、简介 消息的可靠性投递就是要保证消息投递过程中每一个环节都要成功&#xff0c;本文详细介绍两个环节的消息可靠性传递方式&#xff1a;1&#xff09;、消息传递到交换机的 confirm 模式&#xff1b;2&#xff09;、消息传递到队列的 Return 模式。 消息从 producer 到 ex…

Msa全球最新研究:多系统萎缩特效药全球最新进展?

多系统萎缩&#xff0c;是一种以神经系统为主的遗传性疾病&#xff0c;典型症状表现为运动障碍、自主神经功能障碍和认知障碍等。对于这种疾病&#xff0c;西医目前尚未有明确的根治办法&#xff0c;大多数医生只能通过药物缓解患者的症状&#xff0c;但无法彻底治愈。 然而&a…

第9章 正则表达式

学习目标 熟悉正则表达式,能够说出正则表达式的概念和作用 掌握正则表达式的创建,能够使用两种方式创建正则表达式 掌握正则表达式的使用,能够使用正则表达式进行字符串匹配 掌握正则表达式中元字符的使用,能够根据需求选择合适的元字符 掌握正则表达式中模式修饰符的使用,…

CDN加速之HTTPS配置

记录一下HTTPS配置的免费证书配置 2张图搞定 最后补充说明&#xff1a; 由于CDN采用的Tengine服务基于Nginx&#xff0c;因此开启HTTPS安全加速功能的加速域名&#xff0c;只支持上传Nginx能读取的PEM格式的证书。如果证书不是PEM格式&#xff0c;需转换成PEM格式。转换方法&a…

C#,字符串匹配(模式搜索)KMP算法的源代码与数据可视化

D.E.Knuth J.H.Morris 一、KMP算法 KMP 算法&#xff08;Knuth-Morris-Pratt 算法&#xff09;是其中一个著名的、传统的字符串匹配算法&#xff0c;效率比较高。 KMP算法由D.E.Knuth&#xff0c;J.H.Morris和V.R.Pratt在 Brute-Force算法的基础上提出的模式匹配的改进算法。…

江山易改本性难移之ZYNQ SDK QSPI固化bug及其解决方法

之前在Vivado2018.3通过QSPI方式固化程序时出现问题&#xff0c;显示flash擦除成功&#xff0c;但最后总是不能写入到flash中。 查资料发现从VIVADO 2017.3版本开始&#xff0c;Xilinx官方为了使Zynq-7000和Zynq UltraScale 实现流程相同&#xff0c;在QSPI FLASH使用上做了变化…

基于JAVA+SpringBoot的高校学术报告系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 智慧高校学术报告系统…

【SpringCloud】之入门级及nacos的集成使用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringCloud开发之入门级及nacos》。&#x1f3…

设计模式面试

C 面向对象设计 封装&#xff1a;隐藏内部实现继承&#xff1a;复用现有代码多态&#xff1a;改写对象行为 设计模式关键在于分解和抽象; 设计模式的主要目的是易于变化 面向对象设计原则–比设计模式更加重要 违背了设计原则&#xff0c;设计模式是错误的。 依赖倒置原则…

你们做外贸主要的获客渠道有哪些?

昨天跟一个同行朋友聊天&#xff0c;他原本主打产品是做动力类的&#xff0c;这两年竞争太大&#xff0c;订单也减少了很多。为了求发展&#xff0c;就拓品了&#xff0c;而拓展的新品刚好是我们这一块&#xff0c;而且非常迅速地找到场地把生产线弄了起来&#xff0c;还不断扩…