FPGA实现图像灰度转换(2):RGB转YCbCr转Gray

  本篇博客整理一下 RGB565 转 RGB888,再转YCbCr444的算法,最后取 YCbCr 的 Y 分量即可实现 Gray 灰度效果。

一、YCbCr介绍

  “YCbCr或Y'CbCr有的时候会被写作:YCBCR或是Y'CBCR,是色彩空间的一种,通常会用于影片中的影像连续处理,或是数字摄影系统中。Y'为颜色的亮度(luma)成分、而CB和CR则为蓝色和红色的浓度偏移量成份。Y'和Y是不同的,而Y就是所谓的亮度(luminance),表示光的浓度且为非线性,使用伽马修正(gamma correction)编码处理。

  正如几何上用坐标空间来描述坐标集合,色彩空间用数学方式来描述颜色集合。常见的3 个基本色彩模型是RGB,CMYKYUV。YCbCr 则是在世界数字组织视频标准研制过程中作为ITU - R BT.601 建议的一部分,其实是YUV经过缩放和偏移的翻版。其中Y与YUV 中的Y含义一致,Cb,Cr 同样都指色彩,只是在表示方法上不同而已。在YUV 家族中,YCbCr 是在计算机系统中应用最多的成员,其应用领域很广泛,JPEGMPEG均采用此格式。一般人们所讲的YUV大多是指YCbCr。YCbCr 有许多取样格式,如4∶4∶4,4∶2∶2,4∶1∶1 和4∶2∶0。
  YCbCr其中Y是指亮度分量,Cb指蓝色色度分量,而Cr指红色色度分量。人的肉眼对视频的Y分量更敏感,因此在通过对色度分量进行子采样来减少色度分量后,肉眼将察觉不到的图像质量的变化。主要的子采样格式有 YCbCr 4:2:0、YCbCr 4:2:2 和 YCbCr 4:4:4。
  YCbCr 4:4:4 即 YUV三个信道的抽样率相同,因此在生成的图像里,每个象素的三个分量信息完整(每个分量通常8比特),经过8比特量化之后,未经压缩的每个像素占用3个字节。“

——百度百科《YCbCr》

  RGB888 转 YCbCr 的公式如下所示:

 

 

二、MATLAB实现

  首先还是在 MATLAB 中实现,由于 MATLAB 中本来就是 RGB888 的格式,因此不需要 RGB565 转 RGB888 的操作。MATLAB代码如下所示:

 1 %--------------------------------------------------------------------------
 2 %                       RGB转YCbCr取Y值转灰度图
 3 %--------------------------------------------------------------------------
 4 clc;
 5 clear all;
 6 RGB = imread('flower.bmp'); %读取图像
 7 
 8 R = RGB(:,:,1);             %R分量
 9 G = RGB(:,:,2);             %G分量
10 B = RGB(:,:,3);             %B分量
11 
12 [ROW,COL,N] = size(RGB);    %获得图像尺寸[高度,长度,维度]
13 for r = 1:ROW 
14     for c = 1:COL
15         Y(r,c)  =  0.299*R(r,c) + 0.587*G(r,c) + 0.114*B(r,c);
16         Cb(r,c) = -0.172*R(r,c) - 0.339*G(r,c) + 0.511*B(r,c) + 128;
17         Cr(r,c) =  0.511*R(r,c) - 0.428*G(r,c) - 0.083*B(r,c) + 128;
18     end
19 end 
20 
21 subplot(2,2,1);imshow(R);title('R分量灰度图');
22 subplot(2,2,2);imshow(G);title('G分量灰度图');
23 subplot(2,2,3);imshow(B);title('B分量灰度图');
24 subplot(2,2,4);imshow(Y);title('Y分量灰度图');

  点击运行,得到如下效果:

  由结果可以看到,相比 RGB 分量转灰度图来说,YCbCr 取 Y 分量的灰度图更具有层次感,和原图片更为接近。

 

 

三、FPGA实现

1、RGB565 转 RGB888

  本次实验的输入像素是 RGB565 的,因此需要先转换为 RGB888。前面的博客介绍过 RGB332 转 RGB565,原理是一样的,即低位补 0 或继续补充原通道的低位。代码如下所示:

//RGB565 转 RGB888
assign R0 = {RGB_data[15:11],RGB_data[13:11]}; //R8
assign G0 = {RGB_data[10: 5],RGB_data[ 6: 5]}; //G8
assign B0 = {RGB_data[ 4: 0],RGB_data[ 2: 0]}; //B8

2、公式变形

  RGB888 转 YCbCr 的原公式如下所示:

  如果直接对着这个公式进行 Verilog 代码编写是不行的,因为 FPGA 无法进行浮点数的运算,先将公式变一下形,变形过程如下所示:

  第一次变形:由于 FPGA 无法进行浮点运算,因此将包含乘法的部分扩大256倍,然后再右移 8 位,右移8位即在二进制中即除以256的意思。

  第二次变形:将 128 也扩大 256 倍后移到括号里,最后一起右移 8 位。

3、流水线设计

  所谓流水线设计,即数据流由原先的一条线运算转变为多条线同时运算,最终再汇合在一起。这样能充分利用 FPGA 并行的特点,扩大了面积但是提高了速度。

  (1)计算乘法,第一个时钟先将所有的乘法运算集中到一个 always 块中计算。

//clk 1
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        {R1,G1,B1} <= {16'd0, 16'd0, 16'd0};
        {R2,G2,B2} <= {16'd0, 16'd0, 16'd0};
        {R3,G3,B3} <= {16'd0, 16'd0, 16'd0};
    end
    else begin
        {R1,G1,B1} <= { {R0 * 16'd77},  {G0 * 16'd150}, {B0 * 16'd29 } };
        {R2,G2,B2} <= { {R0 * 16'd43},  {G0 * 16'd85},  {B0 * 16'd128} };
        {R3,G3,B3} <= { {R0 * 16'd128}, {G0 * 16'd107}, {B0 * 16'd21 } };
    end
end

  (2)计算加减法,第二个时钟将所有的加减法运算集中到一个 always 块中计算。

//clk 2
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        Y1  <= 16'd0;
        Cb1 <= 16'd0;
        Cr1 <= 16'd0;
    end
    else begin
        Y1  <= R1 + G1 + B1;
        Cb1 <= B2 - R2 - G2 + 16'd32768; //128扩大256倍
        Cr1 <= R3 - G3 - B3 + 16'd32768; //128扩大256倍
    end
end

  (3)右移8位,第三个时钟将所有的移位运行集中到一个 always 块中计算,这样便得到了 YCbCr 的不同分量值。

//clk 3,除以256即右移8位,即取高8位
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        Y2  <= 8'd0;
        Cb2 <= 8'd0;
        Cr2 <= 8'd0;
    end
    else begin
        Y2  <= Y1[15:8];  
        Cb2 <= Cb1[15:8];
        Cr2 <= Cr1[15:8];
    end
end

4、Y分量赋值

  得到 YCbCr 的三个分量后,取 Y 分量赋值给我们的 RGB565 通道即可。

assign gray_data = {Y2[7:3],Y2[7:2],Y2[7:3]}; //只取Y分量给RGB565格式

5、打拍计算

  在图像处理时,我们是有数据的同步信号的,有的是数据使能信号,有的还有行同步信号和帧同步信号。经过图像处理后的数据延迟了拍数,这些同步信号也要相应的打拍,否则最终的图像显示会出问题。

  本次设计我们共耗费了 3 拍,因此同步信号也要相应的延迟 3 拍。

//==========================================================================
//==    信号同步
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        RGB_de_r    <= 3'b0;
        RGB_hsync_r <= 3'b0;
        RGB_vsync_r <= 3'b0;
    end
    else begin  
        RGB_de_r    <= {RGB_de_r[1:0],    RGB_de};
        RGB_hsync_r <= {RGB_hsync_r[1:0], RGB_hsync};
        RGB_vsync_r <= {RGB_vsync_r[1:0], RGB_vsync};
    end
end

assign gray_de    = RGB_de_r[2];
assign gray_hsync = RGB_hsync_r[2];
assign gray_vsync = RGB_vsync_r[2];

 

四、上板验证

  最终效果如下所示:

  和上面 MATLAB 的情况一样,YCbCr取Y分量的灰度图更有层次感。

  实验视频如下所示,本次设计和上一篇博客一样,利用了 key_select 按键选择模块,使用按键对图像进行效果切换。共5种显示效果,分别为原图、R分量灰度图、G分量灰度图、B分量灰度图、YCbCr的Y分量灰度图。

 

五、后记

  很多图像算法都是基于 YCbCr 的 Y 分量来进行的,因此后面就用这个版本的工程做蓝本了。

 

参考资料:

    [1]CrazyBingo 图像处理教程

    [2]NingHechuan 图像处理教程

    [3]小梅哥FPGA教程

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

posted @ 2020-03-04 13:33  咸鱼IC  阅读(6793)  评论(0编辑  收藏  举报