简单处理——RGB转YCbCr转Gray
简单处理——RGB转YCbCr转Gray
一、RGB简介
一辐RGB图像是一个MxNx3的彩色像素数组,每个彩色像素都是一个三值组,这三个值分别代表这个颜色的红、绿、蓝三个分量的值,整体颜色等于这三个分量值的堆叠。一般来说,生活中常用的RGB表达格式是RGB888,即三个分量都以8bit的分量来显示。
另一个比较重要的概念是索引图像,如果我们想知道一辐RGB888格式图片上某个像素点的值,我们很大概率上会得到一个类似于(255,12,255)的三值组,索引图像类似于调色盘,它告诉我们这个三值组对应的什么颜色。RGB可以理解为调色盘的一种,还存在着其他的调色盘,调色盘之间的转换,就叫彩色空间转换。
二、YCbCr彩色空间
YCbCr彩色空间广泛用于数字视频中,在这种格式中,Y分量表示亮度信息,Cb和Cr作为色差分量存储彩色信息。工具箱中从RGB转换为YCbCr所用到的变换是:
到这个地方其实我们可以考虑一些问题:做变换是需要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.公式变形和精度判断
这里遗留一个问题:如何平衡你这个移位位宽呢?就是如果这个移位位数越大,精度越高,占用数据位宽也就越大,这里其实是有一个平衡的问题;
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分量的纹理信息比较丰富。
下图是通过我们搭建的在线仿真平台进行验证得到的结果,经验证我们的RGB_to_Y算法无误。
五、上板验证
参考咸鱼FPGA的设计,我们可以实现用按键控制显示屏显示串口发送图像的各个分量,现在的思考是如何将处理前和处理后的图像数据分别锁存在sdram中间。
后来发现sdram其实是会锁存住写入到其中的数据的,除非你再往那个地址中写入数据,不然地址中的数据不变,不过这应该是有一个期限的,掉电经过一段时间之后应该就会复原。
继续留一个值得思考的问题:实现的功能是当按下按键时,切换sdram输出的图像,实验发现有时候按下按键之后会有失灵的现象发生。思考按下按键的逻辑:首先VGA驱动模块是无时不刻不在进行一个时序的产生与数据的读取打包的,只不过中间被ISP模块截胡,比如原本的模式是显示RGB原始图片,我按下按键之后,display模块将显示模式更改到Y分量模式,那么你的pre数据和post数据定然是合不到一块的;
为什么会有失灵的情况?初步推测可能是按键去抖有一定的问题?明天重新写下去抖模块的逻辑试试。
参考资料:咸鱼FPGA