ZYNQ图像处理(3)——RGB图像转灰度图像

1、RGB转YUV的基本原理

YUV(YCbCr)是另一种色彩空间,Y是指饱和度、U和V是指色度,YUV在图像处理领域有着比RGB更广泛的应用。需要灰度图时,只需要输出Y值即可,下面是转换的公式。
在这里插入图片描述

2、RGB转YUV的matlab实现

考虑到FPGA只能进行整点数的操作,因此在matlab仿真中先把数值放大256倍,然后移8位,如下图所示,并且为了防止负数产生,所以加上32768。这样子我们的数据就都是在0到255之间了,可以正常显示。
在这里插入图片描述
matlab的代码如下所示,代码比较基础,就是对上述的几个公式的复现。

clc;
clear all;

img_rgb=imread('dog.png');

%high and width
h=size(img_rgb,1);
w=size(img_rgb,2);

%rgb dog show
subplot(221);
imshow(img_rgb);
title('rgb dog');

% Relized method 2:myself Algorithm realized
% Y = ( R*77 + G*150 + B*29) >>8
% Cb = (-R*44 - G*84 + B*128) >>8
% Cr = ( R*128 - G*108 - B*20) >>8
img_rgb=double(img_rgb);
img_y=zeros(h,w);
img_u=zeros(h,w);
img_v=zeros(h,w);
for i = 1 : h
    for j = 1 : w
        img_y(i,j) = bitshift(( img_rgb(i,j,1)*77 + img_rgb(i,j,2)*150 + img_rgb(i,j,3)*29),-8);
        img_u(i,j) = bitshift((-img_rgb(i,j,1)*44 - img_rgb(i,j,2)*84 + img_rgb(i,j,3)*128 + 32678),-8);
        img_v(i,j) = bitshift(( img_rgb(i,j,1)*128 - img_rgb(i,j,2)*108 - img_rgb(i,j,3)*20 + 32678),-8);
    end
end
img_y = uint8(img_y); 
img_u = uint8(img_u); 
img_v = uint8(img_v); 

%dog show
subplot(222);
imshow(img_y);
title('gray dog');
subplot(223);
imshow(img_u);
title('u dog');
subplot(224);
imshow(img_v);
title('v dog');

最后显示的效果如下图所示,第一张是原图;第二张是Y分量图也就是灰度图;第三张是U分量的图;第四张是V分量的图。
在这里插入图片描述

3、RGB转YUV的FPGA实现

FPGA实现RGB转YUV,这边可以在ov5640显示工程的基础上进行修改,前面两节已经很清楚的讲了ov5640显示环境的搭建。为了实现rgb2yuv,需要自己编写一个转换的模块,转换的verilog代码如下图。
我这边的verilog代码是采用了流水线设计,而不是直接用组合逻辑去实现,也是为了方便打拍子。代码整个流程分为三步:第一步是将RGB分量分开并且扩大256倍;第二步是将各分量转换为YUV分量;第三步是将YUV分量向右移8位。还有比较重要的一点是,时钟使能信号、场同步信号和数据有效信号需要延时三个时钟周期来达到同步的目的。此外,最终输出是只取了Y分量来达到输出灰度图的目的。

module rgb2yuv(
    input pclk,
    input rst_n,
    input [23:0] rgb_data,
    input rgb_data_vaild,
    input rgb_vsync,
    input rgb_clk_en,

    output wire [23:0] gray_data,
    output wire gray_data_vaild,
    output wire gray_vsync,
    output wire gray_clk_en    
);

wire [7:0] rgb_r;
wire [7:0] rgb_g;
wire [7:0] rgb_b;
wire [7:0] yuv_y;
wire [7:0] yuv_u;
wire [7:0] yuv_v;

reg [15:0] rgb_r0;
reg [15:0] rgb_g0;
reg [15:0] rgb_b0;
reg [15:0] rgb_r1;
reg [15:0] rgb_g1;
reg [15:0] rgb_b1;
reg [15:0] rgb_r2;
reg [15:0] rgb_g2;
reg [15:0] rgb_b2;
reg [15:0] rgb_r3;
reg [15:0] rgb_g3;
reg [15:0] rgb_b3;
reg [15:0] yuv_y0;
reg [15:0] yuv_u0;
reg [15:0] yuv_v0;
reg [7:0] yuv_y1;
reg [7:0] yuv_u1;
reg [7:0] yuv_v1;
reg [2:0] gray_data_vaild_dy;
reg [2:0] gray_vsync_dy;
reg [2:0] gray_clk_en_dy;


assign rgb_r=rgb_data[23:16];
assign rgb_g=rgb_data[15:8];
assign rgb_b=rgb_data[7:0];
assign gray_data_vaild=gray_data_vaild_dy[2];
assign gray_vsync=gray_vsync_dy[2];
assign gray_clk_en=gray_clk_en_dy[2];
assign yuv_y=gray_data_vaild? yuv_y1:8'd0;
assign yuv_u=gray_data_vaild? yuv_u1:8'd0;
assign yuv_v=gray_data_vaild? yuv_v1:8'd0;
assign gray_data={yuv_y,yuv_y,yuv_y};

// rgb to yuv calu
// Y = ( R*77 + G*150 + B*29) >>8
// U = (-R*44 - G*84 + B*128 + 32768) >>8
// V = ( R*128 -G*108 - B*20 + 32768) >>8

//step1:multiply data
always @(posedge pclk or negedge rst_n) begin
    if(~rst_n)begin
        rgb_r0<=16'd0;
        rgb_g0<=16'd0;
        rgb_b0<=16'd0;
        rgb_r1<=16'd0;
        rgb_g1<=16'd0;
        rgb_b1<=16'd0;
        rgb_r2<=16'd0;
        rgb_g2<=16'd0;
        rgb_b2<=16'd0;
    end
    else begin
        rgb_r0<=8'd77*rgb_r;
        rgb_g0<=8'd150*rgb_g;
        rgb_b0<=8'd29*rgb_b;
        rgb_r1<=8'd44*rgb_r;
        rgb_g1<=8'd84*rgb_g;
        rgb_b1<=8'd128*rgb_b;
        rgb_r2<=8'd128*rgb_r;
        rgb_g2<=8'd108*rgb_g;
        rgb_b2<=8'd20*rgb_b;
    end
end
//step2:add data
always @(posedge pclk or negedge rst_n) begin
    if(~rst_n)begin
        yuv_y0<=16'd0;
        yuv_u0<=16'd0;
        yuv_v0<=16'd0;
    end
    else begin
        yuv_y0<=rgb_r0+rgb_g0+rgb_b0;
        yuv_u0<=16'd32768-rgb_r1-rgb_g1+rgb_b1;
        yuv_v0<=16'd32768+rgb_r2-rgb_g2-rgb_b2;
    end
end 
//step3:shift data
always @(posedge pclk or negedge rst_n) begin
    if(~rst_n)begin
        yuv_y1<=8'd0;
        yuv_u1<=8'd0;
        yuv_v1<=8'd0;
    end
    else begin
        yuv_y1<=yuv_y0[15:8];
        yuv_u1<=yuv_u0[15:8];
        yuv_v1<=yuv_v0[15:8];
    end
end

//dleay 3 tclk 
always @(posedge pclk or negedge rst_n) begin
    if(~rst_n)begin
      gray_data_vaild_dy<=3'd0;
      gray_vsync_dy<=3'd0;
      gray_clk_en_dy<=3'd0;
    end
    else begin
      gray_data_vaild_dy<={gray_data_vaild_dy[1:0],rgb_data_vaild};
      gray_vsync_dy<={gray_vsync_dy[1:0],rgb_vsync};
      gray_clk_en_dy<={gray_clk_en_dy[1:0],rgb_clk_en};
    end
end

endmodule 

我们将编写好的verilog代码添加到block design中并且连接各个信号线,如下图所示。检查无误后,生成bit流导出到sdk,sdk代码和摄像头显示代码完全相同。
在这里插入图片描述

4、灰度图显示效果

HDMI屏上正确显示了经过灰度处理的狗子!
在这里插入图片描述

5、灰度显示模块打包

为了通用性,我们将灰度模块打包成一个IP核,打包完的IP核如下图所示原图(左),打包图(右),我们在bd图中将原有模块用打包后的IP核进行替换,也可以得到正确的结果图。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

树叶~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值