简单处理——RGB转YCbCr转Gray

简单处理——RGB转YCbCr转Gray

一、RGB简介

​ 一辐RGB图像是一个MxNx3的彩色像素数组,每个彩色像素都是一个三值组,这三个值分别代表这个颜色的红、绿、蓝三个分量的值,整体颜色等于这三个分量值的堆叠。一般来说,生活中常用的RGB表达格式是RGB888,即三个分量都以8bit的分量来显示。

​ 另一个比较重要的概念是索引图像,如果我们想知道一辐RGB888格式图片上某个像素点的值,我们很大概率上会得到一个类似于(255,12,255)的三值组,索引图像类似于调色盘,它告诉我们这个三值组对应的什么颜色。RGB可以理解为调色盘的一种,还存在着其他的调色盘,调色盘之间的转换,就叫彩色空间转换。

二、YCbCr彩色空间

​ YCbCr彩色空间广泛用于数字视频中,在这种格式中,Y分量表示亮度信息,Cb和Cr作为色差分量存储彩色信息。工具箱中从RGB转换为YCbCr所用到的变换是:

2.1

​ 到这个地方其实我们可以考虑一些问题:做变换是需要RGB888格式的,但是在上一节前置基础中我们给到FPGA的是RGB565格式的数据,所以第一步我们需要的是RGB565转RGB888;第二个问题是FPGA无法进行浮点数的直接运算,所以第二步我们需要找到FPGA内部进行浮点运算的方法以及确定精度;第三个问题是,这个里面的运算操作不仅仅只有一种,在遇到多种运算符时,就还有第三步——流水线操作和时钟的对齐

三、Verilog程序设计

1.RGB565转RGB888

​ 原理是低位补0或者继续补充原通道的低位

assign rgb888 = {rgb[15:11],3'b0,rgb[10:5],3'b0,rgb[4:0],3'b0};
或者
assign rgb888 = {rgb[15:11],rgb[13:11]rgb[10:5],rgb[6:5],rgb[4:0],rgb[2:0]};

2.公式变形和精度判断

2.2

这里遗留一个问题:如何平衡你这个移位位宽呢?就是如果这个移位位数越大,精度越高,占用数据位宽也就越大,这里其实是有一个平衡的问题;

3.流水线操作和时钟线对齐

assign {R0,G0,B0} = rgb888;
// 先把乘法集中在一起 一拍
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        {R1,G1,B1} <= {3{16'b0}};  //这里时因为最开始等式两边同时乘以256的缘故
        {R2,G2,B2} <= {3{16'b0}};
        {R3,G3,B3} <= {3{16'b0}};
    end
    else begin
        {R1,G1,B1} <= {R0*77.G0*150,B0*29};
        {R2,G2,B2} <= {R0*43.G0*85,B0*128};
        {R3,G3,B3} <= {R0*128.G0*107,B0*21};
    end
end
//再做加减法 二拍
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;  //这里时因为最开始等式两边同时乘以256的缘故
        Cb1 <= B2 - R2 + G2 + 32768;
        Cr1 <= R3 - G3 - B3 + 32768;
    end
end
//最后做移位 三拍
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 >> 8;
        Cb2 <= Cb1 >> 8;
        Cr2 <= Cr1 >> 8;
    end
end
//实际上转换为灰度图使用的是亮度分量Y
assign gray = {Y2[7:3],Y2[7:2],Y2[7:3]};

//信号打拍对齐,缺三拍就设置一个位宽3的信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        rgb_vsync <= 3'd0;
        rgb_hsync <= 3'd0;
        rgb_de <= 3'd0;
    end
    else begin
        rgb_vsync <= {rgb_vsync[1:0],vsync}
        rgb_hsync <= {rgb_hsync[1:0],hsync}
        rgb_de <= {rgb_de[1:0],de}
    end
end

assign gray_vsync = rgb_vsync[2];
assign gray_hsync = rgb_hsync[2];
assign gray_de = rgb_de[2];

四、仿真验证

下图是通过MATLAB运算得到的RGB各分量以及Y分量效果图,可以看出G分量和Y分量的纹理信息比较丰富。

2.3

下图是通过我们搭建的在线仿真平台进行验证得到的结果,经验证我们的RGB_to_Y算法无误。

2.4

五、上板验证

​ 参考咸鱼FPGA的设计,我们可以实现用按键控制显示屏显示串口发送图像的各个分量,现在的思考是如何将处理前和处理后的图像数据分别锁存在sdram中间。

​ 后来发现sdram其实是会锁存住写入到其中的数据的,除非你再往那个地址中写入数据,不然地址中的数据不变,不过这应该是有一个期限的,掉电经过一段时间之后应该就会复原。

2.5

2.6

​ 继续留一个值得思考的问题:实现的功能是当按下按键时,切换sdram输出的图像,实验发现有时候按下按键之后会有失灵的现象发生。思考按下按键的逻辑:首先VGA驱动模块是无时不刻不在进行一个时序的产生与数据的读取打包的,只不过中间被ISP模块截胡,比如原本的模式是显示RGB原始图片,我按下按键之后,display模块将显示模式更改到Y分量模式,那么你的pre数据和post数据定然是合不到一块的;

​ 为什么会有失灵的情况?初步推测可能是按键去抖有一定的问题?明天重新写下去抖模块的逻辑试试。

参考资料:咸鱼FPGA

posted @ 2024-04-06 22:08  齐迩  阅读(67)  评论(0编辑  收藏  举报