FPGA实现图像灰度转换(1):RGB分量转Gray

  Gray灰度图像:即我们常说的黑白图像,由黑到白的灰阶为 0- 255(8bit)。

  本博客整理一下 RGB 分量实现 Gray 灰度效果的实验,这个实验非常的简单,简单到看到代码就感觉非常无语......

一、RGB分量转Gray灰度的原理

  RGB格式即一个像素由R、G、B三基色构成,例如 RGB565 格式的像素排列为R[4:0]、G[5:0]、B[4:0],RGB三个分量的数值不同,最后合成的像素颜色则不同。

  RGB分量转Gray灰度即只挑取 R 或 G 或 B 的 1 个分量,剩下的 2 个分量丢弃,其位置由挑取的分量来替代。

 

二、MATLAB

  此次实验选择了一张 RGB 分量明显的图片,先从 MATLAB 软件中查看效果如何。代码如下所示:

clc;
clear all;
RGB = imread('flower.bmp'); %读取图像

R_gray = RGB(:,:,1);        %提取R分量后的灰度图
G_gray = RGB(:,:,2);        %提取G分量后的灰度图
B_gray = RGB(:,:,3);        %提取B分量后的灰度图

subplot(2,2,1);imshow(RGB);   title('原图');
subplot(2,2,2);imshow(R_gray);title('R分量灰度图');
subplot(2,2,3);imshow(G_gray);title('G分量灰度图');
subplot(2,2,4);imshow(B_gray);title('B分量灰度图');

  运行效果如下所示:

  可以看出虽然都是灰度图,但不同分量获得的效果是不同的。

 

三、FPGA中的实现

1、模块划分

  本实验基于串口传输,在前面的博客中整理过。

  图像处理的模块添加在哪里好呢?一开始我是添加到串口模块到 SDRAM 缓存模块之间的,但是因为这次实验是基于图片的,加在这个位置后 SDRAM 里缓存的是一张处理之后的图片,如果要进行效果切换则必须重新编译下载工程,串口再重新发送图片数据,非常的麻烦。经过思考,我决定不动SDRAM之前的模块,让SDRAM缓存原图,再把图像处理模块添加在 TFT 控制器模块之后,并且引入按键,通过按键切换显示效果。这样原先的TFT控制器模块就相当于一个中转站,图像真正的传输到管脚是在图像处理之后。图像处理模块则命名为 ISP_top,专门进行各种图像处理,最终的信号连接到 FPGA 的 TFT屏管脚。本工程的框架图如下所示:

image

   这篇博客实际算是图像处理的第一篇博客,之前的都是预备知识,所以展示的内容全一点,后续的图像处理工程也都是基于此架构实现,不会再详细说明。

2、完整代码

  过于简单,所以直接贴代码吧。

(1)ISP_top

//**************************************************************************
// *** 名称 : ISP_top.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2020年3月
// *** 描述 : 图像处理模块的顶层文件
//**************************************************************************

module ISP_top
//========================< 参数 >==========================================
#(
parameter H_DISP            = 12'd480               ,   //图像宽度
parameter V_DISP            = 12'd272                   //图像高度
)
//========================< 端口 >==========================================
(
input   wire                clk                     ,   //时钟
input   wire                rst_n                   ,   //复位
//RGB -----------------------------------------------
input   wire                RGB_hsync               ,   //RGB行同步
input   wire                RGB_vsync               ,   //RGB场同步
input   wire    [15:0]      RGB_data                ,   //RGB数据
input   wire                RGB_de                  ,   //RGB数据使能
//key -----------------------------------------------
input   wire    [ 1:0]      key_vld                 ,   //消抖后的按键值
//DISP ----------------------------------------------
output  wire                DISP_hsync              ,   //最终显示的行同步
output  wire                DISP_vsync              ,   //最终显示的场同步
output  wire    [15:0]      DISP_data               ,   //最终显示的数据
output  wire                DISP_de                     //最终显示的数据使能
);
//========================< 连线 >==========================================
wire    [15:0]              R_Gray                  ;   //R分量灰度数据
wire    [15:0]              G_Gray                  ;   //G分量灰度数据
wire    [15:0]              B_Gray                  ;   //B分量灰度数据
//==========================================================================
//==                        RGB分量
//==========================================================================
RGB_Gray u_RGB_Gray
(
    //RGB -------------------------------------------
    .RGB_data               (RGB_data               ),  //原始RGB图像
    //Gray ------------------------------------------
    .R_Gray                 (R_Gray                 ),  //R分量灰度数据
    .G_Gray                 (G_Gray                 ),  //G分量灰度数据
    .B_Gray                 (B_Gray                 )   //B分量灰度数据
);
//==========================================================================
//==                        按键选择不同图像效果
//==========================================================================
display u_display
(
    .clk                    (clk                    ),  //时钟
    .rst_n                  (rst_n                  ),  //复位
    //RGB -------------------------------------------
    .RGB_hsync              (RGB_hsync              ),  //RGB行同步
    .RGB_vsync              (RGB_vsync              ),  //RGB场同步
    .RGB_data               (RGB_data               ),  //RGB数据
    .RGB_de                 (RGB_de                 ),  //RGB数据使能
    //Gray ------------------------------------------
    .R_Gray                 (R_Gray                 ),  //R分量灰度数据
    .G_Gray                 (G_Gray                 ),  //G分量灰度数据
    .B_Gray                 (B_Gray                 ),  //B分量灰度数据
    //key -------------------------------------------
    .key_vld                (key_vld                ),  //消抖后的按键值
    //DISP ------------------------------------------
    .DISP_hsync             (DISP_hsync             ),  //最终显示的行同步
    .DISP_data              (DISP_data              ),  //最终显示的场同步
    .DISP_de                (DISP_de                ),  //最终显示的数据 
    .DISP_vsync             (DISP_vsync             )   //最终显示的数据使能
);



endmodule

(2)RGB_Gray

 1 //**************************************************************************
 2 // *** 名称 : RGB_Gray.v
 3 // *** 作者 : xianyu_FPGA
 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
 5 // *** 日期 : 2020年3月
 6 // *** 描述 : RGB分量转Gray灰度图
 7 //**************************************************************************
 8 
 9 module RGB_Gray
10 //========================< 端口 >==========================================
11 (
12 input   wire    [15:0]      RGB_data                , //原始图像数据
13 output  wire    [15:0]      red                     , //R分量灰度图
14 output  wire    [15:0]      green                   , //G分量灰度图
15 output  wire    [15:0]      blue                      //B分量灰度图
16 );
17 //==========================================================================
18 //==                        代码
19 //==========================================================================
20 assign red   = {RGB_data[15:11],RGB_data[15:11],1'b0,RGB_data[15:11]}; 
21 assign green = {RGB_data[10:6],RGB_data[10:5],RGB_data[10:6]};        
22 assign blue  = {RGB_data[4:0],RGB_data[4:0],1'b0,RGB_data[4:0]};      
23 
24 
25 
26 endmodule

(3)display

//**************************************************************************
// *** 名称 : display.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2020年3月
// *** 描述 : 按键切换不同的图像效果
//**************************************************************************

module display
//========================< 端口 >==========================================
(
input   wire                clk                     ,   //时钟
input   wire                rst_n                   ,   //复位
//RGB -----------------------------------------------
input   wire                RGB_hsync               ,   //RGB行同步
input   wire                RGB_vsync               ,   //RGB场同步
input   wire    [15:0]      RGB_data                ,   //RGB数据
input   wire                RGB_de                  ,   //RGB数据使能
//RGB_gray ------------------------------------------
input   wire    [15:0]      R_Gray                  ,   //R分量灰度数据
input   wire    [15:0]      G_Gray                  ,   //G分量灰度数据
input   wire    [15:0]      B_Gray                  ,   //B分量灰度数据
//key -----------------------------------------------
input   wire    [ 1:0]      key_vld                 ,   //消抖后的按键值
//DISP ----------------------------------------------
output  reg                 DISP_hsync              ,   //最终显示的行同步
output  reg                 DISP_vsync              ,   //最终显示的场同步
output  reg     [15:0]      DISP_data               ,   //最终显示的数据 
output  reg                 DISP_de                     //最终显示的数据使能
);
//========================< 信号 >==========================================
reg     [ 3:0]              state                   ;   //状态机
//========================< 参数 >==========================================
localparam IDLE             = 4'b0001               ;   //IDLE状态
localparam R                = 4'b0010               ;   //R分量状态
localparam G                = 4'b0100               ;   //G分量状态
localparam B                = 4'b1000               ;   //B分量状态
//==========================================================================
//==    按键切换不同显示效果
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        state <= IDLE;
    else begin
        case(state)
            //--------------------------------------------------- 原图
            IDLE:   begin
                        if(key_vld[0])
                            state <= R;
                        else if(key_vld[1])
                            state <= IDLE;
                    end
            //--------------------------------------------------- R分量灰度图
            R:      begin
                        if(key_vld[0])
                            state <= G;
                        else if(key_vld[1])
                            state <= IDLE;
                    end
            //--------------------------------------------------- G分量灰度图
            G:      begin
                        if(key_vld[0])
                            state <= B;
                        else if(key_vld[1])
                            state <= IDLE;
                    end
            //--------------------------------------------------- B分量灰度图
            B:      begin
                        if(key_vld[0])
                            state <= R;
                        else if(key_vld[1])
                            state <= IDLE;
                    end
            //--------------------------------------------------- 默认输出
            default:state <= IDLE;
        endcase
    end
end
//==========================================================================
//==    不同状态对应不同的图像输出
//==========================================================================
always @(*) begin
    case(state)
        //--------------------------------------------------- 原图
        IDLE:   begin
                    DISP_hsync = RGB_hsync;
                    DISP_vsync = RGB_vsync;
                    DISP_data  = RGB_data;
                    DISP_de    = RGB_de;
                end
        //--------------------------------------------------- R分量灰度图
        R:      begin
                    DISP_hsync = RGB_hsync;
                    DISP_vsync = RGB_vsync;
                    DISP_data  = R_Gray;
                    DISP_de    = RGB_de;
                 end
        //--------------------------------------------------- G分量灰度图
        G:      begin
                    DISP_hsync = RGB_hsync;
                    DISP_vsync = RGB_vsync;
                    DISP_data  = G_Gray;
                    DISP_de    = RGB_de;
                end
        //--------------------------------------------------- B分量灰度图
        B:      begin
                    DISP_hsync = RGB_hsync;
                    DISP_vsync = RGB_vsync;
                    DISP_data  = B_Gray;
                    DISP_de    = RGB_de;
                end
        //--------------------------------------------------- 默认输出
        default:begin
                    DISP_hsync = RGB_hsync;
                    DISP_vsync = RGB_vsync;
                    DISP_data  = RGB_data;
                    DISP_de    = RGB_de; 
                end
    endcase
end



endmodule

上面说过,这个图像处理非常简单,看到代码就觉得非常无语......连时钟和复位都没有用到,如果不是为了实现按键切换的效果,其实可以直接在顶层assign一下就行了。但是本次实验是后续一系列实验的开篇之作,所以模块什么的都尽可能划科学点,后面就省事了。

 

四、上板验证

  原图:

  R分量灰度图:

  G分量灰度图:

  B分量灰度图:

  实验效果和 MATLAB 中呈现的一样,此次实验成功。

  实验可以通过按键模块 key_select 切换显示效果,切换顺序为:原图、R分量灰度图、G分量灰度图、B分量灰度图。视频演示如下:

 

参考资料:

    [1]小梅哥FPGA图像处理教程

    [2]OpenS Lee:FPGA开源工作室(公众号)

 

posted @ 2020-03-04 09:40  咸鱼IC  阅读(5291)  评论(0)    收藏  举报