• 【数字图像处理】RGB 转灰度图


    常见的数字图像格式有 RGB, RGBA, YCbCr 等,RGB/RGBA 格式适合存储,而 YCbCr 格式适合图像处理。在数字图像处理中,通常需要将 RGB 格式的图像变换为灰度图,再进行后续的处理,例如边缘检测、锐化等。本文主要介绍数字图像 RGB 转灰度图的基本原理,并记录在紫光同创 PGL22G FPGA 平台的布署与实现过程。

    目录

    1. RGB 转灰度图原理

    2. FPGA 布署与实现

    2.1 功能与指标定义

    2.2 模块设计

    2.3 上板调试


    1. RGB 转灰度图原理

            在研究生物视觉的过程中,人们发现人眼视网膜中包含三种视锥细胞,这三种视锥细胞的光谱吸收峰值不同,对应光的波长分别处于 450 nm, 530nm 和 570 nm 附近,对应光的颜色分别为蓝、绿、红。

            考虑人眼对不同颜色光的感知程度不同,对红、绿、蓝 3 个颜色分量分别设定一个权重,颜色分量值与对应权重相乘再累加,就得到灰度值。

            目前关于颜色权重的选取,仍存在一些争议,但总的偏差不大。常用的 RGB 转灰度图的公式如下:

    Gray = 0.299 \times R + 0.587 \times G + 0.114 \times B

    使用 Matlab 进行验证。

    1. clc, clear
    2. % 读取图像
    3. im = imread('./loopy.png');
    4. im = im2double(im);
    5. % RGB转灰度图
    6. im_r = im(:,:,1);
    7. im_g = im(:,:,2);
    8. im_b = im(:,:,3);
    9. im_new = 0.299*im_r +0.587*im_g +0.114*im_b;
    10. % 查看图像
    11. subplot(121)
    12. imshow(im)
    13. title('原图像')
    14. subplot(122)
    15. imshow(im_new)
    16. title('处理后图像')

    2. FPGA 布署与实现

    2.1 功能与指标定义

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

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

    (2)RGB 转灰度图处理,使用 FPGA 内部的乘法器,实现颜色分量的加权;

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

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

    2.2 模块设计

            主要的设计模块功能划分如下:


    top_uart 模块

    · uart_rx_slice 模块,UART 串口接收驱动模块

    · uart_rx_parse 模块,UART 串口数据解析模块,从上位机接收 RGB 8bit 图像

    top_vidin 模块

    · vidin_pipeline 模块,缓存两行图像(使用乒乓操作),并将数据提交到 ddr3 用户数据调度模块

    · mult_9x9_uaub IP,乘法器 IP,输入为 2 个 9bit 无符号数,输出为 18bit 无符号数

    merge_out 模块

    · dvi_timing_gen 模块,根据定义的分辨率,产生 HDMI 视频控制时序

    · dvi_ddr_rd 模块,根据 HDMI 控制信号,提交读指令到 ddr3 用户数据调度模块

    · dvi_encoder 模块,HDMI 输出编码(8b10b 编码)与输出驱动

    · dvi_pll IP,PLL IP,将参考时钟倍频,产生需要的像素时钟信号

    其中,乘法器的输入为 2 个 9bit 无符号数,RGB 颜色分量为 8bit,颜色分量系数需要扩大 512 倍再取整,由

    0.299 \times 512 = 153.088 

    0.587 \times 512 = 300.544

    0.114 \times 512 = 58.368

    得 RGB 乘法器相乘系数分别取:153,301 和 58。

    FPGA 工程资源使用率如下:

    4925a8030c3d4f7b814f139460e721f2.png

    2.3 上板调试

    使用 PyQt5 和 OpenCV 库,编写上位机程序,实现以下功能:

    (1)使用 OpenCV 库加载图像,并截取左上角 512 × 384 的区域;

    (2)通过串口发送给 PGL22G 开发板。

    1. # -*- Coding: UTF-8 -*-
    2. import cv2
    3. import sys
    4. import struct
    5. from PyQt5 import Qt, QtGui, QtCore, QtWidgets, QtSerialPort
    6. class mainWindow(Qt.QWidget):
    7. def __init__(self, com_port, parent=None):
    8. super(mainWindow, self).__init__(parent)
    9. #self.setGeometry(720, 300, 512, 384)
    10. self.setFixedSize(530, 384)
    11. self.setWindowTitle("PGL OpenCV Tool")
    12. # 创建标签与按钮
    13. self.img_widget = QtWidgets.QLabel()
    14. self.btn1 = QtWidgets.QPushButton("打开")
    15. self.btn1.clicked.connect(self.getfile)
    16. self.btn2 = QtWidgets.QPushButton("关闭")
    17. self.btn2.clicked.connect(self.close)
    18. # 创建布局
    19. centralLayout = QtWidgets.QVBoxLayout()
    20. centralLayout.addWidget(self.img_widget)
    21. bottomLayout = QtWidgets.QHBoxLayout()
    22. bottomLayout.addWidget(self.btn1)
    23. bottomLayout.addWidget(self.btn2)
    24. centralLayout.addLayout(bottomLayout)
    25. self.setLayout(centralLayout)
    26. # 串口对象
    27. self.COM = QtSerialPort.QSerialPort()
    28. self.COM.setPortName(com_port)
    29. self.COM.setBaudRate(256000)
    30. self.open_status = False
    31. self.row_cnt = 0
    32. self.img = None
    33. self.timer = QtCore.QTimer()
    34. self.timer.timeout.connect(self.sendImage)
    35. self.startup()
    36. def startup(self):
    37. """Write code here to run once"""
    38. for com_port in QtSerialPort.QSerialPortInfo.availablePorts():
    39. print(com_port.portName())
    40. # Try open serial port
    41. if not self.COM.open(QtSerialPort.QSerialPort.ReadWrite):
    42. self.open_status = False
    43. print("Open Serial Port failed.")
    44. else:
    45. self.open_status = True
    46. def getfile(self):
    47. """获取图像路径"""
    48. fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file',
    49. 'C:\\Users\\Administrator\\Pictures', "Image files(*.jpg *.png)")
    50. self.clipImage(fname[0])
    51. self.updateImage()
    52. self.sendImage()
    53. def clipImage(self, fname):
    54. """读取并裁剪图片至512x384大小"""
    55. if fname:
    56. img = cv2.imread(fname, cv2.IMREAD_COLOR)
    57. img_roi = img[:384,:512,:]
    58. print(img_roi.shape)
    59. cv2.imwrite('./img_roi.png', img_roi)
    60. def updateImage(self):
    61. """显示裁剪后的图像"""
    62. self.img_widget.setPixmap(QtGui.QPixmap('./temp/img_roi.png'))
    63. self.img = cv2.imread('./img_roi.png')
    64. self.timer.start(100)
    65. def sendImage(self):
    66. """通过串口发送图片"""
    67. pattern = ">4H{:d}B".format(512*3)
    68. if self.open_status:
    69. if self.row_cnt == 384:
    70. self.row_cnt = 0
    71. self.timer.stop()
    72. else:
    73. args1 = [self.row_cnt, self.img.shape[0], 0, 0]
    74. args2 = [rgb for rgb in self.img[self.row_cnt,:].reshape(-1)]
    75. send_data = struct.pack(pattern, *(args1+args2))
    76. self.row_cnt += 1
    77. self.COM.write(send_data)
    78. def main():
    79. app = QtWidgets.QApplication(sys.argv)
    80. window = mainWindow('COM21')
    81. window.show()
    82. sys.exit(app.exec_())
    83. if __name__ == "__main__":
    84. main()

     297055f22e75458a83586a9a3a6e2d98.png

            在电脑端发送图像,就可以看到 FPGA 处理结果了 ~

  • 相关阅读:
    简单工厂模式 和 工厂方法 和 抽象工厂的区别
    自动监测站主要设备介绍(​​​​​​​雨水情遥测终端机)
    Day 05 python学习笔记
    深入理解函数式编程(上)
    scala之偏函数学习
    C#/.NET/.NET Core优秀项目和框架2024年2月简报
    每隔一段时间自动敲键盘的的vbs脚本
    Python学习从0开始——项目一day02数据库连接
    [MySQL]事务ACID详解
    计算机起源(二)
  • 原文地址:https://blog.csdn.net/sxyang2018/article/details/134354185