verilog实现rgb2gray

前言

项目算法需求,需要将RGB彩色图像转换为灰度图像,算法原理是很简单的,但是对于刚接触FPGA的宝宝来说,进行时序的设计和调试还是不那么容易的,为了省事儿,就按照上一篇中值滤波(http://www.cnblogs.com/happyamyhope/p/5577898.html)的结构进行设计。开始的开始,只能根据已经做好的设计照葫芦画瓢,否则调试还是很繁琐的,主要是因为目前还是掌握不了时序设计的精髓和思路,慢慢来吧。

实验步骤:

1.实验原理介绍;

2.编写各模块的代码;

3.调试仿真,并与matlab中rgb2gray函数的结果进行比较;

实验过程:

1.实验原理介绍;

matlab中rgb2gray函数的原理还是比较简单的,最后输出的灰度图像是RGB三种颜色通道的加权和;

rgb2gray converts RGB values to grayscale values by forming a weighted sum of the R, G, and B components:

0.2989 * R + 0.5870 * G + 0.1140 * B

上面就是MATLAB中rgb2gray函数的算法原理;

本模块输入的是8bits的三通道彩色图像数据,输出的也是8bits的数据,至于整体算法模块的小数,有待到时候进行一下整合,改变数据的位数。

为了不涉及到小数,我们将数据整体左移16位即乘以65536,根据matlab中的算法原理进行设计:

gray <= 19589 * red + 38469 * green + 7471 * blue;
//gray <= 19595 * red + 38469 * green + 7472 * blue;

查看了一些资料,比如: http://www.cnblogs.com/diewcs/archive/2010/10/03/1841744.html

2.编写个模块的代码;

先将计数器控制模块和rgb2gray进行调试仿真,再将两个模块综合到一起进行调试仿真,得到最终的结果。

主要是计数器控制模块,如何正确得到正确时序的地址数据。

1)计数器控制模块:

module counter_ctrl(

  CLK,
  RSTn,
  iCall,
  iNxt_pix,
  oAddr,
  oDone

    );

input CLK;
input RSTn;
input iCall;
input iNxt_pix;
output [17:0] oAddr;
output oDone;

reg [17:0] imk;
reg isDone ;

reg start_sig_d;

wire start_sig_rising_vld;

always @ (posedge CLK or negedge RSTn)   //Asynchronous reset
  if ( !RSTn )
     start_sig_d <= 0;
  else
     start_sig_d <= iCall;

assign start_sig_rising_vld = iCall & (~start_sig_d);

always @ ( posedge CLK or negedge RSTn )
  if ( !RSTn ) begin
    imk <= 18‘d0;
     isDone <= 1‘b0;
  end
  else if ( start_sig_rising_vld )
     begin
        imk <= 18‘b1;
        isDone <= 1‘b1;
    end
  else if ( iNxt_pix )  // & ( imk != 166222 )
       begin
          imk <= imk + 1‘b1;
        isDone <= 1‘b1;
        end
     else isDone <= 1‘b0;    

assign oAddr = imk;
assign oDone = isDone;

endmodule

模块通过计数控制得到将要读取图像数据的地址,需要注意的是何时开始获取数据初始地址,何时开始地址开始进行加一计数。

testbench模块:

module ctrl_tb;

    // Inputs
    reg CLK;
    reg RSTn;
    reg iCall;

    // Outputs
    wire [17:0] oAddr;
    wire oDone;

    // Instantiate the Unit Under Test (UUT)
    counter_ctrl uut (
        .CLK(CLK),
        .RSTn(RSTn),
        .iCall(iCall),
        .oAddr(oAddr),
        .oDone(oDone)
    );

    initial begin
        // Initialize Inputs
        CLK = 0;
        RSTn = 0;
        iCall = 0;

        // Wait 100 ns for global reset to finish
        #100;
        RSTn = 1;
        iCall = 1;

        // Add stimulus here

    end

 always #10 CLK = ~CLK;

 always @ ( posedge CLK or RSTn )
   if ( oDone )
      $display("%d\n", oAddr );

 always @ ( posedge CLK or RSTn )
   if ( oAddr == 18‘d200 )
      begin
        iCall <= 0;
        $stop;
      end

endmodule

2)rgb2gray算法模块;

// matlab algorithm:gray = 0.2989 * R + 0.5870 * G + 0.1140 * B;
// move left by 16bits: gray = 19589 * R + 38469 * G + 7471 * B;
module rgb2gray(

  CLK,
  RSTn,
  iCall,
  iRed,
  iGreen,
  iBlue,
  oGray,
  oDone

    );

input CLK;
input RSTn;
input iCall;
//input [23:0] iRGB;
input [7:0] iRed;
input [7:0] iGreen;
input [7:0] iBlue;

output [7:0] oGray;
output oDone;

reg [7:0] red;
reg [7:0] green;
reg [7:0] blue;

reg [23:0] gray;
reg [2:0] i;
reg isDone;

/********************************************************************************/ 

reg get_pix_vld;

always @ ( posedge CLK or negedge RSTn )
    if (!RSTn)
           get_pix_vld <= 1‘b0;
     else if ( iCall )
            get_pix_vld <= 1‘b1;
     else if ( i==3‘d2 )
            get_pix_vld <= 1‘b0;

always @ ( posedge CLK or negedge RSTn )
  if ( !RSTn ) begin
    red   <= 8‘d0;
    green <= 8‘d0;
     blue  <= 8‘d0;
  end
  else if ( iCall ) begin
    red   <= iRed  ;
    green <= iGreen;
     blue  <= iBlue ;
 end                                       

always @ ( posedge CLK or negedge RSTn )
  if ( !RSTn ) begin
    i <= 3‘d0;
    gray <= 24‘d0;
     isDone <= 1‘b0;
  end
  else if ( get_pix_vld )
    case ( i )
       0: begin
            //gray <= 19595 * iRGB[23:16] + 38469 * iRGB[15:8] + 7472 * iRGB[7:0];
            gray <= 19589 * red + 38469 * green + 7471 * blue;
           //gray <= 19595 * red + 38469 * green + 7472 * blue;
          i <= i + 1‘b1;
            end
        1: begin isDone <= 1‘b1; i <= i + 1‘b1; end
        2: begin isDone <= 1‘b0; i <= 3‘d0; end
    endcase

assign oGray = ( gray >> 16 ) ;
assign oDone = isDone ;      

endmodule

将输入的三色通道图像数据转换为灰度图像。

3)创建IP ROM核,将彩色图象数据分通道储存在ROM中,具体操作步骤请参考

http://www.cnblogs.com/happyamyhope/p/5498745.html

图像大小是383*434,共R、G、B三个通道。

4)顶层模块:

将低层各个模块联系起来,得到rgb2gray的整体模块。

module rgb_gray(
  CLK,
  RSTn,
  Start,
  Gray,
  Done
    );

input CLK;
input RSTn;
input Start;

output [7:0] Gray;
output Done;

/*****************************************************************************/

wire [17:0]  addra ;
wire [7:0 ]  red   ;
wire [7:0 ]  green ;
wire [7:0 ]  blue  ;

imLr imLr_inst(
  .clka  ( CLK   ),
  .addra ( addra ),
  .douta ( red )
);

imLg imLg_inst(
  .clka  ( CLK   ),
  .addra ( addra ),
  .douta ( green )
);

imLb imLb_inst(
  .clka  ( CLK   ),
  .addra ( addra ),
  .douta ( blue  )
);

/*****************************************************************************/

wire done_ctrl;
wire done_gray;    

counter_ctrl counter_ctrl_inst( 

  .CLK     ( CLK       ),
  .RSTn    ( RSTn      ),
  .iCall   ( Start     ),
  .iNxt_pix( done_gray ),
  .oAddr   ( addra     ),
  .oDone   ( done_ctrl )
    );

wire [7:0] gray;

rgb2gray rgb2gray_inst(

  .CLK    ( CLK        ) ,
  .RSTn   ( RSTn       ) ,
  .iCall  ( done_ctrl  ) ,
  .iRed   ( red        ) ,
  .iGreen ( green      ) ,
  .iBlue  ( blue       ) ,
  .oGray  ( gray       ) ,
  .oDone  ( done_gray  )
    );

/*****************************************************************************/

assign Gray = gray;
assign Done = done_gray;

endmodule

顶层模块需要注意不同模块之间数据的连接,以数据线的形式相连,故要使用wire类型。

5)testbench模块:

module rgb_gray_tb;

    // Inputs
    reg CLK;
    reg RSTn;
    reg Start;
   reg [17:0] pix_cnt;

    // Outputs
    wire [7:0] Gray;
    wire Done ;

   integer fout ;

    // Instantiate the Unit Under Test (UUT)
    rgb_gray rgb_gray_inst (
        .CLK(CLK),
        .RSTn(RSTn),
        .Start(Start),
        .Gray(Gray),
        .Done(Done)
    );

    initial begin
        // Initialize Inputs
        CLK = 0;
        RSTn = 1;
        Start = 0;
        pix_cnt = 0;

        fout = $fopen( "rgb2gray_re.txt" );

        // Wait 100 ns for global reset to finish
        #100;
        RSTn = 0;
        Start = 1;
      pix_cnt = 0;

        // Add stimulus here
        #100;   // To start the system
        // Add stimulus here
        RSTn = 1;
        pix_cnt = 1;

    end

always #10 CLK = ~CLK;

always @ ( posedge CLK )
  if ( Done )
    pix_cnt <= pix_cnt + 1‘b1;

always @ ( posedge CLK )
  if ( pix_cnt == 18‘d166223 ) begin
    Start <= 0;
     $display("Image rgb2gray Completed!\n");
     $display("The all time is %d \n",$time);
     $stop;
  end

always @ ( posedge CLK )
  if ( Done ) begin
     $fwrite(fout, "%d", Gray, "\n");
     $display("%d,   %d ",pix_cnt, Gray);
  end

endmodule

结果图片:

        

前一张图片,在最后两个像素的时候代码出现了警告,不知道是什么原因,不过最后的结果没有错误。

后一张图片可以看出前几个像素点得到的算法结果。

3.调试仿真,并与matlab中rgb2gray函数的结果进行比较:

matlab的比较代码:

% code to create image data from rgb2gray txt file
clc;
clear all;
close all;

I_rgb = imread(‘imL.png‘);
subplot(2, 2, 1), imshow(I_rgb), title(‘imL-rgb‘)

I_gray = rgb2gray(I_rgb);
[m, n] = size(I_gray);
subplot(2, 2, 3), imshow(I_gray), title(‘imL-rgb2gray-mfunc‘)

rgb2gray_v_load = load(‘.\rgb2gray_re.txt‘); % verilog 产生的rgb2gray数据
rgb2gray_v = reshape(rgb2gray_v_load, n, m);
rgb2gray_v = uint8(rgb2gray_v‘);

diff = I_gray - rgb2gray_v;
subplot(2, 2, 4), imshow(rgb2gray_v), title(‘rgb2gray-verilog‘);

最后的数据结果显示,verilog的算法结果与matlab函数的处理结果相比较,差值在1个像素之内。

显示结果:

可以看出,最后的结果相差无几,Good!

实验结论:

因为有中值滤波的基础,基于此,实现rgb2gray的算法设计还是比较简单的,不过也还是弄得有点久,最重要的是时序的设计,在调试仿真的过程中让宝宝比较烦恼的就是时序问题,是在是搞不懂FPGA的时序到底是怎么回事儿,就算明白了时序的问题还会有一些该有的延时或者该考虑的时序没有考虑到,还有,就算考虑到了,可是编写代码还是硬伤。

加油吧,少年!

做自己该做的事情,做自己喜欢做的事情,安静做一枚有思想的技术媛。

时间: 2024-08-28 13:45:40

verilog实现rgb2gray的相关文章

基于UVM的verilog验证

Abstract 本文介绍UVM框架,并以crc7为例进行UVM的验证,最后指出常见的UVM验证开发有哪些坑,以及怎么避免. Introduction 本例使用环境:ModelSim 10.2c,UVM-1.1d,Quartus II 13.1(64 bit),器件库MAX V 1. UVM介绍 对UVM结构熟悉的读者可跳过本节. 叫UVM“框架”可能并不确切(只是便于理解,可类比软件界的“框架”).UVM全称为通用验证方法论.在硬件开发过程中,验证是十分重要的环节.可以说,左手开发,右手验证.

&lt;Verilog干啥的&gt;

Verilog是一种硬件描述语言.它是一种描述型工具, 就像素描笔一样,画了一个太阳 .通过这个画,其实我是想告诉大家: 今天太热了! 每次提到Verilog怎么学,总有很多人告诉你, 它是描述型语言,语法简单,你应该去学数字电路设计. 什么叫数字电路 ?  本质肯定是电路, 那电路是干啥用的,肯定是为了实现一个功能的吧. 怎么才能叫实现功能 ?  看下图 : 给电路一定的输入信号,经过我的模块Black_Box,我送出你想要的输出信号.这就实现了功能. (1)  好了,怎么正确的描述这个电路的

VHDL:信号、端口以及和Verilog的区别

1.信号 信号是描述硬件系统的基本数据对象,它的性质类似于连接线.信号可以作为设计实 体中并行语句模块间的信息交流通道.      信号作为一种数值容器,不但可以容纳当前值,也可以保持历史值(这决定于语句的表达方式).这一属性与触发器的记忆功能有很好的对应关系,只是不必注明信号上数据流动的方向.信号定义的语句格式与变量相似,信号定义也可以设置初始值,定义格式是:   SIGNAL 信号名: 数据类型 := 初始值 :      同样,信号初始值的设置也不是必需的,而且初始值仅在 VHDL 的行为

Verilog HDL程序设计——基本要素

Verilog基本上熟悉了,继续整理一下Verilog的学习笔记吧.前面记载了Verilog的结构,写Verilog的结构有了,但是该怎么写呢?在写之前就得了解一下Verilog的一些基本要素了,也就是Verilog是怎么一点一点写出来的. 一.标识符与注释 前面已经说到,模块名的定义要符合标识符的定义,那么什么是标识符呢?它的语法是什么呢? ①标识符是赋给对象的唯一名称,通过标识符可以提及相应的对象,Verilog语法将对转义标识符中的字符逐个处理. ②标识符可以是字母.数字.下划线和美元符$

Verilog读写文件

在通过编写Verilog代码实现ram功能时,需要自己先计算寄存器的位数和深度再编写代码. 而如果需要在编写的ram中预置值的话,就需要使用Verilog语言编写程序读写文件,来将相应的数据赋给寄存器. 这里给出Verilog实现ram的代码: module dpram( rclk, raddr, dout, wclk, we, waddr, din ); parameter aw = 16; //address widtth parameter dw = 12; //data address

Verilog 基础回顾 (一)

Verilog 大小写敏感, 且所有关键字都是小写 1  寄存器 register = storage,是数据存储单元的抽象,可视为能够存储数值的变量 (variable that can hold value) 关键字 reg; 缺省值 x; 2  网络连接 net = connection, 表示寄存器之间的连接,只能采用连续赋值 (must be driven continuously) 关键字 wire; 缺省值 z; 例 1)  D 触发器 (同步复位) module dff(clk,

3_8译码器Verilog HDL语言的简单实现

最近在学Verilog HDL语言,觉得learn in doing是比较好的学习方式,所以我们来直接分析分析代码好了. 先来一波代码: 1 module q_decode_38(data_in,data_out); 2 3 input[2:0] data_in; //端口声明 4 output[7:0] data_out; 5 reg[7:0] data_out; 6 7 always@(data_in) 8 begin 9 case(data_in) 10 3'd0:data_out = 8

永远的流水灯(Verilog)

1. 为了更好地学习FPGA和深入理解Verilog语法,首先从最简单的流水灯做起.虽然简单,但是也包含了不少知识.通过这次实验项目,可以了解开发软件的使用及Verilog的编程方法,熟悉模块化设计的方法. 2. 该项目主要实现的功能为: (1)10位的流水灯 (2)中间两个led灯每隔100ms闪烁一次 (3)两边的led灯每隔100ms流动一下,从中间向两边流水. 3.  具体实现如下 (1)首先定义一个时间计数寄存器counter,每当达到预定的100ms时,计数寄存器就清零,否则的话寄存

基于Verilog HDL整数乘法器设计与仿真验证

基于Verilog HDL整数乘法器设计与仿真验证 1.预备知识 整数分为短整数,中整数,长整数,本文只涉及到短整数.短整数:占用一个字节空间,8位,其中最高位为符号位(最高位为1表示为负数,最高位为0表示为正数),取值范围为-127~127. 负数的表示方法为正值的求反又加1.例如: 8’b0000_0100; //表示值:4,正值求反为:8’b1111_1011:再加1表示为:8’b1111_1100,这样便得到了-4的表示方法为:8’b1111_1100. 同理,负值变成正值的方法为:负值