一、前言

  最近学习牟新刚编著《基于FPGA的数字图像处理原理及应用》的第六章直方图操作,由于需要将捕获的图像转换为灰度图像,因此在之前代码的基础上加入了RGB图像转灰度图像的算法实现。

                                                                             2020-02-29 10:38:40

二、RGB图像转灰度图像算法原理

  将彩色图像转换为灰度图像的方法有两种,一个是令RGB三个分量的数值相等。输出后便可以得到灰度图像,另一种是转换为YCbCr格式,将Y分量提取出来,YCbCr格式中的Y分量表示的是图

像的亮度和浓度,所以只输出Y分量,得到图像就是灰度图像。

  YCbCr是通过有序的三元组来表示的,三元由Y(Luminance)、Cb(Chrominace-Blue)和Cr(Chrominace-Red)组成,其中Y表示颜色的明亮度和浓度,而Cb和Cr则分别表示颜色的蓝色浓度

偏移量和红色浓度偏移量。人的肉眼对由YCbCr色彩空间编码的视频中Y分量更敏感,而Cb和Cr的微小变换不会引起视觉上的不同。根据该原理,通过对Cb和Cr进行子采样来减小图像的数据量。使得

图像对存储需求和传输带宽的要求大大降低,从而达到完成图像压缩的同时,也保证了视觉上几乎没有损失的效果,进而使得图像的传输速度更快、存储更加方便。

  官方给的RGB888转YCrCb的算法公式:

  Y = 0.299R + 0.587G + 0.114B                             

  Cb = 0.568(B-Y) + 128 = -0.172R -0.339G + 0.511B + 128

  Cr = 0.713(R -Y) + 128 = 0.511R - 0.428G - 0.083B + 128

  扩大256倍 →

  Y = ((77*R + 150*G + 29*B)>>8)

   Cb = ((-43*B - 85*G + 128*B)>>8) + 128

  Cr = ((128*R - 107*G - 21*B)>>8) + 128

三、代码实现

  代码分为三部分,包括视频码流生成image_src.v、视频捕获video_cap.v、彩色图像转灰度图像RGB2YCbCr.v及顶层文件rgb2gray.v;同时为了仿真及测试结果分析提供相应的matlab文件及用于

Modelsim仿真的rgb2gray.do文件。

  (1)频码流生成image_src.v,生成640*512的24Bit RGB图像数据流;

 /*
***********************************************************************************************************
** Input file: None
** Component name: image_src.v
** Author: zhengXiaoliang
** Company: WHUT
** Description: to simulate dvd stream
***********************************************************************************************************
*/ `timescale 1ps/1ps `define SEEK_SET
`define SEEK_CUR
`define SEEK_END module image_src(
reset_l, //全局复位
clk, //同步时钟
src_sel, //数据源通道选择
test_vsync, //场同步输出
test_dvalid, //像素有效输出
test_data, //像素数据输出
clk_out //像素时钟输出
); parameter iw = ; //默认视频宽度
parameter ih = ; //默认视频高度
parameter dw = ; //默认像素数据位宽 parameter h_total = ; //行总数
parameter v_total = ; //垂直总数 parameter sync_b = ; //场前肩
parameter sync_e = ; //场同步脉冲
parameter vld_b = ; //场后肩 //port decleared
input reset_l,clk;
input [:] src_sel; //to select the input file
output test_vsync, test_dvalid,clk_out;
output [dw-:] test_data; //variable decleared
reg [dw-:] test_data_reg;
reg test_vsync_temp;
reg test_dvalid_tmp;
reg [:] test_dvalid_r; reg [:] h_cnt;
reg [:] v_cnt; integer fp_r;
integer cnt = ; //输出像素时钟
assign clk_out = clk; //output the dv clk //输出像素数据
assign test_data = test_data_reg; //test data output //当行同步有效时,从文件读取像素数据输出到数据线上
always@(posedge clk or posedge test_vsync_temp)begin
if(((~(test_vsync_temp))) == 'b0) //场同步清零文件指针
cnt <= ; //clear file pointer when a new frame comes
else begin
if(test_dvalid_tmp == 'b1)begin //行同步有效,说明当前时钟数据有效
case(src_sel) //选择不同的数据源
'b0000: fp_r = $fopen("E:/Modelsim/rgb2gray/sim/rgb_image.txt","r");
'b0001: fp_r = $fopen("txt_source/test_scr1.txt","r");
'b0010: fp_r = $fopen("txt_source/test_scr2.txt","r");
'b0011: fp_r = $fopen("txt_source/test_scr3.txt","r");
'b0100: fp_r = $fopen("txt_source/test_scr4.txt","r");
'b0101: fp_r = $fopen("txt_source/test_scr5.txt","r");
'b0110: fp_r = $fopen("txt_source/test_scr6.txt","r");
'b0111: fp_r = $fopen("txt_source/test_scr7.txt","r");
'b1000: fp_r = $fopen("txt_source/test_scr8.txt","r");
'b1001: fp_r = $fopen("txt_source/test_scr9.txt","r");
'b1010: fp_r = $fopen("txt_source/test_scr10.txt","r");
'b1011: fp_r = $fopen("txt_source/test_scr11.txt","r");
'b1100: fp_r = $fopen("txt_source/test_scr12.txt","r");
'b1101: fp_r = $fopen("txt_source/test_scr13.txt","r");
'b1110: fp_r = $fopen("txt_source/test_scr14.txt","r");
'b1111: fp_r = $fopen("txt_source/test_scr15.txt","r");
default: fp_r = $fopen("txt_source/test_src3.txt","r");
endcase $fseek(fp_r,cnt,); //查找当前需要读取的文件位置
$fscanf(fp_r,"%02x\n",test_data_reg); //将数据按指定格式读入test_data_reg寄存器 cnt <= cnt + ; //移动文件指针到下一个数据
$fclose(fp_r); //关闭文件
$display("h_cnt = %d,v_cnt = %d, pixdata = %d",h_cnt,v_cnt,test_data_reg); //for debug use
end
end
end //水平计数器,每来一个时钟就+1,加到h_total置零重新计数
always@(posedge clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
h_cnt <= # {{'b0}};
else begin
if(h_cnt == ((h_total -)))
h_cnt <= # {{'b0}};
else
h_cnt <= # h_cnt + 'b00000000001;
end
end //垂直计数器:水平计数器计满后+1,计满后清零
always@(posedge clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
v_cnt <= # {{'b0}};
else begin
if(h_cnt == ((h_total - )))begin
if(v_cnt == ((v_total - )))
v_cnt <= # {{'b0}};
else
v_cnt <= # v_cnt + 'b00000000001;
end
end
end //场同步信号生成
always@(posedge clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
test_vsync_temp <= # 'b1;
else begin
if(v_cnt >= sync_b & v_cnt <= sync_e)
test_vsync_temp <= # 'b1;
else
test_vsync_temp <= # 'b0;
end
end assign test_vsync = (~test_vsync_temp); //水平同步信号生成
always@(posedge clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
test_dvalid_tmp <= # 'b0;
else begin
if(v_cnt >= vld_b & v_cnt < ((vld_b + ih)))begin
if(h_cnt == 'b0000000000)
test_dvalid_tmp <= # 'b1;
else if(h_cnt == iw)
test_dvalid_tmp <= # 'b0;
end
else
test_dvalid_tmp <= # 'b0;
end
end //水平同步信号输出
assign test_dvalid = test_dvalid_tmp; always@(posedge clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
test_dvalid_r <= # 'b00;
else
test_dvalid_r <= # ({test_dvalid_r[],test_dvalid_tmp});
end endmodule

  (2)视频捕获video_cap.v,捕获RGB图像数据,并输出RGB888格式的24bit数据码流;

 //2020-02-17
//Huang.Wei
`timescale 1ps/1ps module video_cap(
reset_l, //异步复位信号
DVD, //输入视频流
DVSYN, //输入场同步信号
DHSYN, //输入行同步
DVCLK, //输入DV时钟
cap_dat, //输出RGB通道像素流,24位
cap_dvalid, //输出数据有效
cap_vsync, //输出场同步
cap_clk, //本地逻辑时钟
img_en,
cmd_rdy, //命令行准备好,代表可以读取
cmd_rdat, //命令行数据输出
cmd_rdreq //命令行读取请求
); parameter TRIG_VALUE = ; //读触发值,也即行消隐时间
parameter IW = ; //图像宽度
parameter IH = ; //图像高度 parameter DW_DVD = ; //输入像素宽度
parameter DVD_CHN = ; //输入像素通道: RGB 3通道
parameter DW_LOCAL = ; //本地捕获的数据宽度24位
parameter DW_CMD = ; //命令行数据宽度
parameter VSYNC_WIDTH = ; //9 //场同步宽度,9个时钟 parameter CMD_FIFO_DEPTH = ; //行缓存位宽
parameter CMD_FIFO_DW_DEPTH = ;
parameter IMG_FIFO_DEPTH = ; //异步fifo深度,选512
parameter IMG_FIFO_DW_DEPTH = ; //Port Declared
input reset_l;
input [DW_DVD-:] DVD;
input DVSYN;
input DHSYN;
input DVCLK; output reg [DW_LOCAL-:] cap_dat;
output reg cap_dvalid;
output cap_vsync;
input cap_clk;
output img_en; output reg cmd_rdy;
output [DW_CMD-:] cmd_rdat;
input cmd_rdreq; //首先完成数据位宽转换
wire pixel_clk;
reg [:] vc_reset;
reg dv_enable;
reg [:] count_lines;
reg cmd_en;
reg cmd_wrreq;
reg cmd_wrreq_r;
reg rst_cmd_fifo;
wire [DW_CMD-:] cmd_din;
reg [DW_CMD-:] cmd_dat; assign pixel_clk = DVCLK; always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
begin
vc_reset <= 'b00;
dv_enable <= 'b0;
end
else
begin
dv_enable <= # 'b1;
if((~(DVSYN)) == 'b1 & dv_enable == 1'b1)
vc_reset <= # ({vc_reset[],'b1});
end
end reg [DW_DVD-:] vd_r[:DVD_CHN-];
reg [DVD_CHN*DW_DVD-:] data_merge; reg vsync;
reg [DVD_CHN:] hsync_r;
reg mux;
reg mux_r; //缓存场同步和行同步信号
always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
begin
vsync <= 'b0;
hsync_r <= {DVD_CHN+{'b0}};
end
else
begin
vsync <= # DVSYN;
hsync_r <= # {hsync_r[DVD_CHN-:],DHSYN};
end
end //像素通道计算,指示当前像素属于RGB那个通道
reg [DVD_CHN:] pixel_cnt; always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
begin
pixel_cnt <= {DVD_CHN+{'b1}};
end
else
begin
if(hsync_r[] == 'b0)
pixel_cnt <= # {DVD_CHN+{'b1}};
else
if(pixel_cnt == DVD_CHN -)
pixel_cnt <= # {DVD_CHN+{'b0}};
else
pixel_cnt <= # pixel_cnt + 'b1;
end
end integer i;
integer j; //缓存输入DV,获得3个RGB通道值 always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l)))=='b1)
for(i=;i<DVD_CHN;i=i+)
vd_r[i] <= {DW_DVD{'b0}};
else
begin
vd_r[] <= # DVD;
for(j=;j<DVD_CHN;j=j+)
vd_r[j] <= vd_r[j-];
end
end //RGB 合并有效信号
wire mux_valid; always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
mux <= 'b0;
else begin
if(hsync_r[DVD_CHN-] == 'b0)
mux <= # 'b1;
else
if(mux_valid == 'b1)
mux <= # 'b1;
else
mux <= # 'b0;
end
end always@(posedge pixel_clk)
mux_r <= mux; wire [DVD_CHN*DW_DVD-:] dvd_temp;
wire mux_1st; assign mux_1st = (~hsync_r[DVD_CHN]) & (hsync_r[DVD_CHN-]); //一个颜色通道
generate
if(DVD_CHN == )
begin: xhdl1
assign mux_valid = hsync_r[];
assign dvd_temp = vd_r[];
end
endgenerate //两个颜色通道
generate
if(DVD_CHN == )
begin: xhdl2
assign mux_valid = mux_1st | (pixel_cnt == DVD_CHN - );
assign dvd_temp = {vd_r[],vd_r[]};
end
endgenerate //三个颜色通道,将三路RBG数据合并到dvd_temp信号中
generate
if(DVD_CHN == )
begin: xhdl3
assign mux_valid = mux_1st | (pixel_cnt == );
assign dvd_temp = {vd_r[],vd_r[],vd_r[]};
end
endgenerate //四个颜色通道
generate
if(DVD_CHN == )
begin: xhdl4
assign mux_valid = mux_1st | (pixel_cnt == );
assign dvd_temp = {vd_r[],vd_r[],vd_r[],vd_r[]};
end
endgenerate //将合并后的数据存入寄存器
always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
data_merge <= {DVD_CHN*DW_DVD{'b0}};
else
begin
if(hsync_r[DVD_CHN] == 'b1 & mux == 1'b1)
data_merge <= # dvd_temp;
end
end //将合并后的数据打入异步fifo
wire [DW_DVD*DVD_CHN-:] fifo_din;
wire [DW_DVD*DVD_CHN-:] fifo_dout; wire [IMG_FIFO_DW_DEPTH-:] rdusedw;
reg [:] trig_cnt;
wire fifo_empty;
reg fifo_wrreq;
reg fifo_wrreq_r;
//wire fifo_wrreq; //assign fifo_wrreq = mux & hsync_r[DVD_CHN]; reg fifo_rdreq;
reg fifo_rdreq_r1;
reg rst_fifo; //实例化异步fifo
cross_clock_fifo img_fifo(
.data(fifo_din),
.rdclk(cap_clk),
.rdreq(fifo_rdreq),
.wrclk(pixel_clk),
.wrreq(fifo_wrreq),
.q(fifo_dout),
.rdempty(fifo_empty),
.rdusedw(rdusedw),
.aclr(rst_fifo)
); /*
defparam img_fifo.DW = DW_DVD*DVD_CHN;
defparam img_fifo.DEPTH = IMG_FIFO_DEPTH;
defparam img_fifo.DW_DEPTH = IMG_FIFO_DW_DEPTH;
*/ assign fifo_din = data_merge; //RGB合并时写入fifo
always@(posedge pixel_clk or negedge reset_l)begin
if(reset_l == 'b0)begin
fifo_wrreq <= # 'b0;
fifo_wrreq_r <= # 'b0;
end
else begin
fifo_wrreq <= hsync_r[DVD_CHN] & mux_r;
fifo_wrreq_r <= fifo_wrreq;
end
end //fifo中数据大于触发值时开始读,读完一行停止
always@(posedge cap_clk or negedge reset_l)begin
if(reset_l == 'b0)
fifo_rdreq <= # 'b0;
else
begin
if((rdusedw >= TRIG_VALUE) & (fifo_empty == 'b0))
fifo_rdreq <= # 'b1;
else if(trig_cnt == (IW - ))
fifo_rdreq <= # 'b0;
end
end //读计数
always@(posedge cap_clk or negedge reset_l)begin
if(reset_l == 'b0)
trig_cnt <= # {{'b0}};
else
begin
if(fifo_rdreq == 'b0)
trig_cnt <= # {{'b0}};
else
if(trig_cnt == (IW - ))
trig_cnt <= # {{'b0}};
else
trig_cnt <= # trig_cnt + 'b0000000001;
end
end wire [DW_LOCAL-:] img_din; assign img_din = ((cmd_en == 'b0)) ? fifo_dout[DW_LOCAL-1:0] : {DW_LOCAL{1'b0}}; assign cmd_din = ((cmd_en == 'b1)) ? fifo_dout[DW_CMD-1:0] : {DW_CMD{1'b0}}; //生成场同步信号、数据有效信号及像素数据输出
reg vsync_async;
reg vsync_async_r1;
reg [VSYNC_WIDTH:] vsync_async_r;
reg cap_vsync_tmp; always@(posedge cap_clk or negedge reset_l)begin
if(reset_l == 'b0)
begin
vsync_async <= # 'b0;
vsync_async_r1 <= # 'b0;
vsync_async_r <= {VSYNC_WIDTH+{'b0}};
cap_vsync_tmp <= # 'b0;
end
else
begin
vsync_async <= # (~vsync);
vsync_async_r1 <= # vsync_async;
vsync_async_r <= {vsync_async_r[VSYNC_WIDTH-:], vsync_async_r1};
if(vsync_async_r[] == 'b1 & vsync_async_r[0] == 1'b0)
cap_vsync_tmp <= # 'b1;
else if(vsync_async_r[VSYNC_WIDTH] == 'b0 & vsync_async_r[0] == 1'b0)
cap_vsync_tmp <= # 'b0;
end
end assign cap_vsync = cap_vsync_tmp; always@(posedge cap_clk or negedge reset_l)begin
if(reset_l=='b0)
begin
cap_dat <= # {DW_LOCAL{'b0}};
fifo_rdreq_r1 <= # 'b0;
cap_dvalid <= # 'b0;
cmd_dat <= # {DW_CMD{'b0}};
cmd_wrreq <= # 'b0;
cmd_wrreq_r <= # 'b0;
end
else
begin
cap_dat <= # img_din;
fifo_rdreq_r1 <= # fifo_rdreq;
cap_dvalid <= # fifo_rdreq_r1 & (~(cmd_en));
cmd_dat <= # cmd_din;
cmd_wrreq <= # fifo_rdreq_r1 & cmd_en;
cmd_wrreq_r <= cmd_wrreq;
end
end //frame count and img_en signal
reg [:] fr_cnt;
reg img_out_en; always@(posedge cap_clk)begin
if(vc_reset[] == 'b0)
begin
img_out_en <= 'b0;
fr_cnt <= {{'b0}};
end
else
begin
if(vsync_async_r1 == 'b0 & vsync_async == 1'b1)
begin
fr_cnt <= fr_cnt + 'b01;
if(fr_cnt == 'b11)
img_out_en <= 'b1;
end
end
end assign img_en = img_out_en; //行计数,确定cmd数据到来时刻
always@(posedge cap_clk)begin
if(cap_vsync_tmp == 'b1)
begin
count_lines <= {{'b0}};
cmd_en <= 'b0;
cmd_rdy <= 'b0;
end
begin
if(fifo_rdreq_r1 == 'b1 & fifo_rdreq == 1'b0)
count_lines <= # count_lines + 'h1;
if(count_lines == (IH - ))
rst_cmd_fifo <= 'b1;
else
rst_cmd_fifo <= 'b0;
if(count_lines >= IH)
cmd_en <= # 'b1;
if(cmd_wrreq_r == 'b1 & cmd_wrreq == 1'b0)
cmd_rdy <= 'b1;
if(cmd_wrreq_r == 'b1 & cmd_wrreq == 1'b0)
rst_fifo <= 'b1;
else
rst_fifo <= 'b0;
end
end //Instance a line buffer to store the cmd line
line_buffer_new
cmd_buf(
.aclr(rst_cmd_fifo),
.clock(cap_clk),
.data(cmd_dat),
.rdreq(cmd_rdreq),
.wrreq(cmd_wrreq),
.empty(),
.full(),
.q(cmd_rdat),
.usedw()
); /*
defparam cmd_buf.DW = DW_CMD;
defparam cmd_buf.DEPTH = CMD_FIFO_DEPTH;
defparam cmd_buf.DW_DEPTH = CMD_FIFO_DW_DEPTH;
defparam cmd_buf.IW = IW;
*/
endmodule

  (3)彩色图像转灰度图像RGB2YCbCr.v,实现RGB888到YCbCr图像格式的转换,并输出三个通道的数据;

 //==================================================================================================//
//FileName: RGB2YCrCb.v
/*
官方给的RGB888 to YCbCr的计算公式:
Y = 0.299R + 0.587G + 0.114B
Cb = 0.568(B - Y) + 128 = -0.172R - 0.339G + 0.511B + 128
Cr = 0.713(R -Y) + 128 = 0.511R - 0.428G - 0.083B + 128 => Y = ((77*R + 150*G + 29*B)>>8);
Cb = ((-43*R - 85*G + 128*B)>>8) + 128;
Cr = ((128*R - 107*G - 21*B)>>8) + 128;
*/
//Date: 2020-02-28
//==================================================================================================//
`timescale 1ps/1ps module RGB2YCrCb(
RESET, //异步复位信号 RGB_CLK, //输入像素时钟
RGB_VSYNC, //输入场同步信号
RGB_DVALID, //输入数据有信号
RGB_DAT, //输入RGB通道像素流,24位 YCbCr_CLK, //输出像素时钟
YCbCr_VSYNC, //输出场同步信号
YCbCr_DVALID, //输出数据有效信号
Y_DAT, //输出Y分量
Cb_DAT, //输出Cb分量
Cr_DAT //输出Cr分量
); parameter RGB_DW = ; //输入像素宽度
parameter YCbCr_DW = ; //输出像素宽度 //Port Declared
input RESET;
input RGB_CLK;
input RGB_VSYNC;
input RGB_DVALID;
input [RGB_DW-:]RGB_DAT; output YCbCr_CLK;
output YCbCr_VSYNC;
output YCbCr_DVALID;
output reg [YCbCr_DW-:] Y_DAT;
output reg [YCbCr_DW-:] Cb_DAT;
output reg [YCbCr_DW-:] Cr_DAT; reg [*YCbCr_DW-:] RGB_R1,RGB_R2,RGB_R3;
reg [*YCbCr_DW-:] RGB_G1,RGB_G2,RGB_G3;
reg [*YCbCr_DW-:] RGB_B1,RGB_B2,RGB_B3; reg [*YCbCr_DW-:] IMG_Y,IMG_Cb,IMG_Cr; reg [:] VSYNC_R;
reg [:] DVALID_R; //Step1: Consume 1Clk
always@(posedge RGB_CLK or negedge RESET)begin
if(!RESET)begin
RGB_R1 <= {*YCbCr_DW{'b0}};
RGB_R2 <= {*YCbCr_DW{'b0}};
RGB_R3 <= {*YCbCr_DW{'b0}};
RGB_G1 <= {*YCbCr_DW{'b0}};
RGB_G2 <= {*YCbCr_DW{'b0}};
RGB_G3 <= {*YCbCr_DW{'b0}};
RGB_B1 <= {*YCbCr_DW{'b0}};
RGB_B2 <= {*YCbCr_DW{'b0}};
RGB_B3 <= {*YCbCr_DW{'b0}};
end
else begin
RGB_R1 <= RGB_DAT[:] * 'd77;
RGB_G1 <= RGB_DAT[:] * 'd150;
RGB_B1 <= RGB_DAT[:] * 'd29;
RGB_R2 <= RGB_DAT[:] * 'd43;
RGB_G2 <= RGB_DAT[:] * 'd85;
RGB_B2 <= RGB_DAT[:] * 'd128;
RGB_R3 <= RGB_DAT[:] * 'd128;
RGB_G3 <= RGB_DAT[:] * 'd107;
RGB_B3 <= RGB_DAT[:] * 'd21;
end
end //Step2: Consume 1Clk
always@(posedge RGB_CLK or negedge RESET)begin
if(!RESET)begin
IMG_Y <= {*YCbCr_DW{'b0}};
IMG_Cr <= {*YCbCr_DW{'b0}};
IMG_Cb <= {*YCbCr_DW{'b0}};
end
else begin
IMG_Y <= RGB_R1 + RGB_G1 + RGB_B1;
IMG_Cb <= RGB_B2 - RGB_R2 - RGB_G2 + 'd32768;
IMG_Cr <= RGB_R3 - RGB_G3 - RGB_B3 + 'd32768;
end
end //Step3: Consume 1Clk
always@(posedge RGB_CLK or negedge RESET)begin
if(!RESET)begin
Y_DAT <= {YCbCr_DW{'b0}};
Cb_DAT <= {YCbCr_DW{'b0}};
Cr_DAT <= {YCbCr_DW{'b0}};
end
else begin
Y_DAT <= IMG_Y[:];
Cr_DAT <= IMG_Cr[:];
Cb_DAT <= IMG_Cb[:];
end
end assign YCbCr_CLK = RGB_CLK; always@(posedge RGB_CLK or negedge RESET)begin
if(!RESET)begin
VSYNC_R <= 'd0;
DVALID_R <= 'd0;
end
else begin
VSYNC_R <= {VSYNC_R[:],RGB_VSYNC};
DVALID_R <= {DVALID_R[:],RGB_DVALID};
end
end assign YCbCr_DVALID = DVALID_R[];
assign YCbCr_VSYNC = VSYNC_R[]; endmodule

  (4)顶层文件 rgb2gray.v;

 //===============================================================================================//
//FileName: rgb2gray.v
//Date:2020-02-28
//===============================================================================================// `timescale 1ps/1ps module rgb2gray(
RSTn, //全局复位
CLOCK, //系统时钟 IMG_CLK, //像素时钟
IMG_DVD, //像素值
IMG_DVSYN, //输入场信号
IMG_DHSYN, //输入数据有效信号 GRAY_CLK, //输出灰度图像时钟
GRAY_VSYNC, //输出灰度图像场信号
GRAY_DVALID, //输出灰度图像数据有效信号
Y_DAT, //输出图像数据Y分量
Cb_DAT, //输出图像数据Cb分量
Cr_DAT //输出图像数据Cr分量 );
/*image parameter*/
parameter iw = ; //image width
parameter ih = ; //image height
parameter trig_value = ; // /*data width*/
parameter dvd_dw = ; //image source data width
parameter dvd_chn = ; //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr
parameter local_dw = dvd_dw * dvd_chn; //local algorithem process data width
parameter cmd_dw = dvd_dw * dvd_chn; //local algorithem process data width //Port Declared
input RSTn;
input CLOCK;
input IMG_CLK;
input [dvd_dw-:] IMG_DVD;
input IMG_DVSYN;
input IMG_DHSYN; output GRAY_CLK;
output GRAY_VSYNC;
output GRAY_DVALID;
output [dvd_dw-:] Y_DAT;
output [dvd_dw-:] Cb_DAT;
output [dvd_dw-:] Cr_DAT; //Variable Declared
wire [local_dw-:] RGB_DAT;
wire RGB_DVALID;
wire RGB_VSYNC; video_cap u1(
.reset_l(RSTn), //异步复位信号
.DVD(IMG_DVD), //输入视频流
.DVSYN(IMG_DVSYN), //输入场同步信号
.DHSYN(IMG_DHSYN), //输入行同步
.DVCLK(IMG_CLK), //输入DV时钟
.cap_dat(RGB_DAT), //输出RGB通道像素流,24位
.cap_dvalid(RGB_DVALID), //输出数据有效
.cap_vsync(RGB_VSYNC), //输出场同步
.cap_clk(CLOCK), //本地逻辑时钟
.img_en(),
.cmd_rdy(), //命令行准备好,代表可以读取
.cmd_rdat(), //命令行数据输出
.cmd_rdreq() //命令行读取请求
); defparam u1.DW_DVD = dvd_dw;
defparam u1.DW_LOCAL = local_dw;
defparam u1.DW_CMD = cmd_dw;
defparam u1.DVD_CHN = dvd_chn;
defparam u1.TRIG_VALUE = trig_value;
defparam u1.IW = iw;
defparam u1.IH = ih; RGB2YCrCb u2(
.RESET(RSTn), //异步复位信号 .RGB_CLK(CLOCK), //输入像素时钟
.RGB_VSYNC(RGB_VSYNC), //输入场同步信号
.RGB_DVALID(RGB_DVALID), //输入数据有信号
.RGB_DAT(RGB_DAT), //输入RGB通道像素流,24位 .YCbCr_CLK(GRAY_CLK), //输出像素时钟
.YCbCr_VSYNC(GRAY_VSYNC), //输出场同步信号
.YCbCr_DVALID(GRAY_DVALID), //输出数据有效信号
.Y_DAT(Y_DAT), //输出Y分量
.Cb_DAT(Cb_DAT), //输出Cb分量
.Cr_DAT(Cr_DAT) //输出Cr分量
); defparam u2.RGB_DW = local_dw;
defparam u2.YCbCr_DW = dvd_dw; endmodule

  (5)Maltab文件用显示仿真结果;

 clc;
clear; %% 数据获取
RGBImg = imread('lena_512x512.jpg'); %rgb原始图像
RGBImg = imresize(RGBImg,[ ]); GRAYImg = rgb2gray(RGBImg); %Matlab变换灰度图像 fid = fopen('gray_image_Y.txt','r'); %FPGA转换灰度图像
data = fscanf(fid,'%2x');
data = uint8(data);
gray_data = reshape(data,,);
gray_data = gray_data'; %% 画图显示
figure();
subplot(,,);
imshow(RGBImg);
title('lena原始图像'); subplot(,,);
imshow(GRAYImg);
title('Matlab变换灰度图像'); subplot(,,);
imshow(gray_data);
title('FPGA变换灰度图像');

  (6)用于Modelsim测试的Testbench文件rgb2gray_tb.v;

 `timescale 1ps/1ps

 module rgb2gray_tb;

     /*image para*/
parameter iw = ; //image width
parameter ih = ; //image height
parameter trig_value = ; // /*video parameter*/
parameter h_total = ;
parameter v_total = ;
parameter sync_b = ;
parameter sync_e = ;
parameter vld_b = ; parameter clk_freq = ; /*data width*/
parameter dvd_dw = ; //image source data width
parameter dvd_chn = ; //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr
parameter local_dw = dvd_dw * dvd_chn; //local algorithem process data width
parameter cmd_dw = dvd_dw * dvd_chn; //local algorithem process data width /*test module enable*/
parameter cap_en = ; /*signal group*/
reg clk = 'b0;
reg reset_l;
reg [:] src_sel; /*input dv group*/
wire dv_clk;
wire dvsyn;
wire dhsyn;
wire [dvd_dw-:] dvd; /*dvd source data generated for simulation*/
image_src //#(iw*dvd_chn, ih+1, dvd_dw, h_total, v_total, sync_b, sync_e, vld_b)
u1(
.clk(clk),
.reset_l(reset_l),
.src_sel(src_sel),
.test_data(dvd),
.test_dvalid(dhsyn),
.test_vsync(dvsyn),
.clk_out(dv_clk)
); defparam u1.iw = iw*dvd_chn;
defparam u1.ih = ih + ;
defparam u1.dw = dvd_dw;
defparam u1.h_total = h_total;
defparam u1.v_total = v_total;
defparam u1.sync_b = sync_b;
defparam u1.sync_e = sync_e;
defparam u1.vld_b = vld_b; /*local clk: also clk of all local modules*/
reg cap_clk = 'b0; /*output data*/
wire GRAY_CLK;
wire GRAY_VSYNC;
wire GRAY_DVALID;
wire [dvd_dw-:] Y_DAT;
wire [dvd_dw-:] Cb_DAT;
wire [dvd_dw-:] Cr_DAT; /*video capture: capture image src and transfer it into local timing*/ rgb2gray u2(
.RSTn(reset_l),
.CLOCK(cap_clk), .IMG_CLK(dv_clk),
.IMG_DVD(dvd),
.IMG_DVSYN(dvsyn),
.IMG_DHSYN(dhsyn), .GRAY_CLK(GRAY_CLK),
.GRAY_VSYNC(GRAY_VSYNC),
.GRAY_DVALID(GRAY_DVALID),
.Y_DAT(Y_DAT),
.Cb_DAT(Cb_DAT),
.Cr_DAT(Cr_DAT)
); initial
begin: init
reset_l <= 'b1;
src_sel <= 'b0000;
#(); //reset the system
reset_l <= 'b0;
#();
reset_l <= 'b1;
end //dv_clk generate
always@(reset_l or clk)begin
if((~(reset_l)) == 'b1)
clk <= 'b0;
else
begin
if(clk_freq == ) //48MHz
clk <= # (~(clk)); else if(clk_freq == 51.84) //51.84MHz
clk <= # (~(clk)); else if(clk_freq == ) //72MHz
clk <= # (~(clk));
end
end //cap_clk generate: 25MHz
always@(reset_l or cap_clk)begin
if((~(reset_l)) == 'b1)
cap_clk <= 'b0;
else
cap_clk <= # (~(cap_clk));
end generate
if(cap_en != ) begin :capture_operation
integer fid1, fid2, fid3, cnt_cap=; always@(posedge GRAY_CLK or posedge GRAY_VSYNC)begin
if(((~(GRAY_VSYNC))) == 'b0)
cnt_cap = ;
else
begin
if(GRAY_DVALID == 'b1)
begin
//Y
fid1 = $fopen("E:/Modelsim/rgb2gray/sim/gray_image_Y.txt","r+");
$fseek(fid1,cnt_cap,);
$fdisplay(fid1,"%02x\n",Y_DAT);
$fclose(fid1); //Cb
fid2 = $fopen("E:/Modelsim/rgb2gray/sim/gray_image_Cb.txt","r+");
$fseek(fid2,cnt_cap,);
$fdisplay(fid2,"%02x\n",Cb_DAT);
$fclose(fid2); //Cr
fid3 = $fopen("E:/Modelsim/rgb2gray/sim/gray_image_Cr.txt","r+");
$fseek(fid3,cnt_cap,);
$fdisplay(fid3,"%02x\n",Cr_DAT);
$fclose(fid3); cnt_cap<=cnt_cap+;
end
end
end
end
endgenerate endmodule

  (7) 用于Modelsim仿真的.do文件rgb2gray.do。

 #切换至工程目录
cd E:/Modelsim/rgb2gray/sim #打开工程
project open E:/Modelsim/rgb2gray/sim/rgb2gray #添加指定设计文件
project addfile E:/Modelsim/rgb2gray/src/cross_clock_fifo.v
project addfile E:/Modelsim/rgb2gray/src/image_src.v
project addfile E:/Modelsim/rgb2gray/src/line_buffer_new.v
project addfile E:/Modelsim/rgb2gray/src/rgb2gray.v
project addfile E:/Modelsim/rgb2gray/src/RGB2YCbCr.v
project addfile E:/Modelsim/rgb2gray/src/video_cap.v
project addfile E:/Modelsim/rgb2gray/sim/rgb2gray_tb.v #编译工程内所有文件
project compileall #仿真work库下面的rgb2gray_tb实例,同时调用altera_lib库,不进行任何优化
vsim -t 1ps -novopt -L altera_lib work.rgb2gray_tb #添加输入信号
add wave -divider RGBImg
add wave -radix binary -position insertpoint sim:/rgb2gray_tb/dv_clk
add wave -radix binary -position insertpoint sim:/rgb2gray_tb/dvsyn
add wave -radix binary -position insertpoint sim:/rgb2gray_tb/dhsyn
add wave -radix hex -position insertpoint sim:/rgb2gray_tb/dvd #添加输出信号
add wave -divider GRAYImg
add wave -radix binary -position insertpoint sim:/rgb2gray_tb/GRAY_CLK
add wave -radix binary -position insertpoint sim:/rgb2gray_tb/GRAY_VSYNC
add wave -radix binary -position insertpoint sim:/rgb2gray_tb/GRAY_DVALID
add wave -radix hex -position insertpoint sim:/rgb2gray_tb/Y_DAT
add wave -radix hex -position insertpoint sim:/rgb2gray_tb/Cb_DAT
add wave -radix hex -position insertpoint sim:/rgb2gray_tb/Cr_DAT #复位
restart #取消警告
set StdArithNoWarnings #开始
run 17ms

四、仿真结果

  如下图所示,整个转换消耗3个时钟,因此相应的行/场信号延迟3个时钟,保持时钟的同步性。

  如下图所示,将FPGA运算处理结果与Matlab自带rgb2gray函数处理结果对比如下。

  

基于FPGA的RGB图像转灰度图像算法实现的更多相关文章

  1. 基于FPGA的肤色识别算法实现

    大家好,给大家介绍一下,这是基于FPGA的肤色识别算法实现. 我们今天这篇文章有两个内容一是实现基于FPGA的彩色图片转灰度实现,然后在这个基础上实现基于FPGA的肤色检测算法实现. 将彩色图像转化为 ...

  2. 基于FPGA的HDTV视频图像灰度直方图统计算法设计

    随着HDTV的普及,以LCD-TV为主的高清数字电视逐渐进入蓬勃发展时期.与传统CRT电视不同的是,这些高清数字电视需要较复杂的视频处理电路来驱动,比如:模数转换(A/D Converter).去隔行 ...

  3. 基于FPGA的线阵CCD图像测量系统研究——笔记

    本文是对基于FPGA的线阵CCD图像测量系统研究(作者:高尚)的阅读笔记 第一章绪论 1. 读读看 读了前面的摘要依然没有看懂作者要做什么.接着往下读....终于看到了一个字眼“基于机器视觉的图像测量 ...

  4. 基于FPGA的图像开发平台 其他摄像头附件说明(OV5642 OV9655)

    基于FPGA的图像开发平台 其他摄像头附件说明 FPGA_VIP_V101 编者 奇迹再现 个人博客 http://www.cnblogs.com/ccjt/ 联系邮箱 Shenyae86@163.c ...

  5. 基于FPGA的Uart接收图像数据至VGA显示

    系统框图 前面我们设计了基于FPGA的静态图片显示,接下来我们来做做基于FPGA的动态图片显示,本实验内容为:由PC端上位机软件通过串口发送一幅图像数据至FPGA,FPGA内部将图像数据存储,最后扫描 ...

  6. 基于Xilinx FPGA的视频图像采集系统

    本篇要分享的是基于Xilinx FPGA的视频图像采集系统,使用摄像头采集图像数据,并没有用到SDRAM/DDR.这个工程使用的是OV7670 30w像素摄像头,用双口RAM做存储,显示窗口为320x ...

  7. 基于FPGA的线阵CCD实时图像采集系统

    基于FPGA的线阵CCD实时图像采集系统 2015年微型机与应用第13期 作者:章金敏,张 菁,陈梦苇2016/2/8 20:52:00 关键词: 实时采集 电荷耦合器件 现场可编程逻辑器件 信号处理 ...

  8. 基于FPGA的OLED真彩色动态图像显示的实现

    源:基于FPGA的OLED真彩色动态图像显示的实现 作为第3代显示器,有机电致发光器件(Organic Light Emitting Diode,OLED)由于其主动发光.响应快.高亮度.全视角.直流 ...

  9. 基于FPGA的RGB565_YCbCr_Gray算法实现

    前面我们讲了基于FPGA用VGA显示一副静态图片,那么接下来我们就接着前面的工程来实现我们图像处理的基础算法里最简单的一个那就是彩色图像转灰度的实现. 将彩色图像转化为灰度的方法有两种,一个是令RGB ...

随机推荐

  1. 吴裕雄--天生自然TensorFlow高层封装:Keras-TensorFlow API

    # 1. 模型定义. import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist_ ...

  2. 一个帖子csrf的例子

    服务端 <?php $conn=mysqli_connect('localhost','root','root','csrf'); $data=$_POST; $user=$_POST['use ...

  3. 学习ECC及Openssl下ECC生成密钥的部分源代码心得

    一.ECC的简介 椭圆曲线算法可以看作是定义在特殊集合下数的运算,满足一定的规则.椭圆曲线在如下两个域中定义:Fp域和F2m域. Fp域,素数域,p为素数: F2m域:特征为2的有限域,称之为二元域或 ...

  4. Exception in thread "main" java.lang.AbstractMethodError

    参考https://stackoverflow.com/questions/15758151/class-conflict-when-starting-up-java-project-classmet ...

  5. 现有.NET 开源框架浅析

    自己一直在关注框架,也喜欢捣鼓一些框架,最近我们自己一直是在用OSGI.NET 做插件式模块开发,感兴趣的可以到 http://www.iopenworks.com/   官网了解,在进行OSGI.N ...

  6. Ubuntu中Unable to acquire the dpkg frontend lock解决方案

    根据百度总结三种方式:第三种解决了我的问题 1. ps -e|grep apt-get 结果:6965 ? 00:00:01 apt-get 执行:sudo kill 6965 #强制解锁,会删除文件 ...

  7. 正则表达式grep学习(一)

    文本处理三剑客grep       文本过滤sed       流过滤awk       格式处理 正则表达式就是一些特殊字符组成的模式,赋予了他特定的含义 在大多数程序里,正则表达式都被置于两个正斜 ...

  8. python调用存储过程失败返回1787错误

    (1787, 'When @@GLOBAL.ENFORCE_GTID_CONSISTENCY = 1, the statements CREATE TEMPORARY TABLE and DROP T ...

  9. 将java list转换为js的数组

    var data = new Array();<%ArrayList list = new ArrayList();list.add(0);list.add(1);if(list!=null){ ...

  10. Zabbix 监控sqlserver

    转:Zabbix 监控sqlserver 一:Zabbix监控sqlserver 方法一: 1.思路整理 1.在zabbix server上安装Freetds.unixODBC.unixODBC-de ...