FPGA彩色RGB图像转YCbCr
图像由COMS摄像头的感光阵列传感器采集,转化为电压信号然后进行信号处理输出为常见的3 个基本色彩模型RGB,CMYK和YUV中的一种。 ## RGB图像格式简介
RGB色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。RGB 颜色称为加成色,因为您通过将 R、G 和 B 添加在一起(即所有光线反射回眼睛)可产生白色。加成色用于照明光、电视和计算机显示器。例如,显示器通过红色、绿色和蓝色荧光粉发射光线产生颜色。绝大多数可视光谱都可表示为红、绿、蓝 (RGB) 三色光在不同比例和强度上的混合。这些颜色若发生重叠,则产生青、洋红和黄。如果RGB三色都用8bit来表示,8bit的数值范围为0~255,一个颜色有256种亮度,三个颜色叠加就是256^3,所以表示的颜色范围是很宽的,也叫作全真彩RGB888。图像的色彩丰富,但同时图像的多占的内存也大大增加,RGB888一个像素点就占用3个字节,假若一副1024x1024的图像它的大小就要3M的内存。除了RGB888还有其他的格式RGB565,RGB555,人对颜色的辨别度没有那么敏感,减小颜色的亮度级,基本不会影响人眼的看到的效果,这也是图像压缩的一种方式。
YCbCr图像格式简介
YCbCr或Y'CbCr有的时候会被写作:YCBCR或是Y'CBCR,是色彩空间的一种,通常会用于影片中的影像连续处理,或是数字摄影系统中。Y'为颜色的亮度(luma)成分、而CB和CR则为蓝色和红色的浓度偏移量成份。Y'和Y是不同的,而Y就是所谓的流明(luminance),表示光的浓度且为非线性,使用伽马修正(gamma correction)编码处理。YCbCr 则是在世界数字组织视频标准研制过程中作为ITU - R BT.601 建议的一部分,其实是YUV经过缩放和偏移的翻版。其中Y与YUV 中的Y含义一致,Cb,Cr 同样都指色彩,只是在表示方法上不同而已。在YUV 家族中,YCbCr 是在计算机系统中应用最多的成员,其应用领域很广泛,JPEG、MPEG均采用此格式。一般人们所讲的YUV大多是指YCbCr。YCbCr 有许多取样格式,如4∶4∶4,4∶2∶2,4∶1∶1 和4∶2∶0。
对于图像处理我们要做的就是要提取Y分量(又叫做灰度值),它的效果相当于把彩色图像转化为黑白图像,彩色图像中一个像素中有RGB三种颜色分量,对图像处理时不好处理,一般是把RGB图像转为YCbCr图像类型,对Y分量进行处理,后期在恢复彩色图像,这样对三个量的处理就变为一个量了,值具体想要了解可以百度查找其他资料。
RGB转YCbCr
在MATLAB中有写好的函数可以直接实现RGB转换为YCbCr,但是直接调用函数我们无法了解他是如何转化的,其实原理就是简单的变换,下面我们自己手写实现一个转换函数,下面给出转换的公式。
Y = 0.257 ∗ R + 0.564 ∗ G + 0.098 ∗ B + 16 C b = − 0.148 ∗ R − 0.291 ∗ G + 0.439 ∗ B + 128 C r = 0.439 ∗ R − 0.368 ∗ G − 0.071 ∗ B + 128 \\Y = 0.257*R+0.564*G+0.098*B+16 \\Cb = -0.148*R-0.291*G+0.439*B+128 \\Cr = 0.439*R-0.368*G-0.071*B+128 Y=0.257∗R+0.564∗G+0.098∗B+16Cb=−0.148∗R−0.291∗G+0.439∗B+128Cr=0.439∗R−0.368∗G−0.071∗B+128
MTALAB代码
%将一副RGB图像转为灰度图像
%RGB888 to YCbCr
% Y = 0.257*R+0.564*G+0.098*B+16
% Cb = -0.148*R-0.291*G+0.439*B+128
% Cr = 0.439*R-0.368*G-0.071*B+128
clc;
clear all;
close all;
%读取图片
RGB_data = imread ('lena.jpg');
%分解出RGB三种颜色分量
R_data = RGB_data(:,:,1);
G_data = RGB_data(:,:,2);
B_data = RGB_data(:,:,3);
% subplot(121);
imshow(RGB_data);
%size返回图像的ROW(行长度),COl(列长度),DIM(维度)
[ROW,COL,DIM] = size(RGB_data);
%生成三个同样大小的矩阵单元
Y_data = zeros(ROW,COL);
Cb_data = zeros(ROW,COL);
Cr_data = zeros(ROW,COL);
%计算Y_data,Cb_data,Cr_data的值
%Y_data为图像的亮度(灰度),Cb为蓝色分量,Cr为红色分量
for r = 1:ROW
for c = 1:COL
Y_data(r,c) = 0.257*R_data(r,c) + 0.564*G_data(r,c) + 0.098*B_data(r,c) +16;
Cb_data(r,c)= -0.148*R_data(r,c) - 0.291*G_data(r,c) +0.439*B_data(r,c) +128;
Cr_data(r,c) = 0.439*R_data(r,c) - 0.368*G_data(r,c) -0.071*B_data(r,c) +128;
end
end
%填充颜色信息
Gray_data(:,:,1) = Y_data;
Gray_data(:,:,2) = Y_data;
Gray_data(:,:,3) = Y_data;
Gray_data = uint8(Gray_data);
% subplot(122);
figure;
imshow(Gray_data);
FPGA实现的主要代码
//RGBtoYCbCr
module rgb2ycbcr(
input clk,
input rst_n,
//图像处理前的数据接口
input pre_frame_vsync, //场同步信号
input pre_frame_hsync, //行同步信号
input pre_frame_de, //数据使能信号
input [4:0] img_red,
input [5:0] img_green,
input [4:0] img_blue,
//图像处理后的数据接口
output post_frame_vsync,
output post_frame_hsync,
output post_frame_de,
// output [7:0] img_y,
// output [7:0] img_cb,
// output [7:0] img_cr,
output [15:0] ycbcr
);
//reg define
reg [15:0] rgb_r_m0, rgb_r_m1, rgb_r_m2;
reg [15:0] rgb_g_m0, rgb_g_m1, rgb_g_m2;
reg [15:0] rgb_b_m0, rgb_b_m1, rgb_b_m2;
reg [15:0] img_y0 ;
reg [15:0] img_cb0;
reg [15:0] img_cr0;
reg [15:0] ycbcr1 ;
reg [ 7:0] img_y1 ;
reg [ 7:0] img_cb1;
reg [ 7:0] img_cr1;
reg [ 2:0] pre_frame_vsync_d;
reg [ 2:0] pre_frame_hsync_d;
reg [ 2:0] pre_frame_de_d ;
//wire define
wire [ 7:0] rgb888_r;
wire [ 7:0] rgb888_g;
wire [ 7:0] rgb888_b;
// mian code
//RGB565 to RGB888
assign rgb888_r = {img_red,img_red[4:2]};
assign rgb888_g = {img_green,img_green[5:4]};
assign rgb888_b = {img_blue,img_blue[4:2]};
//同步数据接口信号
assign post_frame_vsync = pre_frame_vsync_d[2];
assign post_frame_hsync = pre_frame_hsync_d[2];
assign post_frame_de = pre_frame_de_d[2];
assign ycbcr = post_frame_hsync ? ycbcr1 : 16'd0;
assign img_y = post_frame_hsync ? img_y1 : 8'd0;
assign img_cb = post_frame_hsync ? img_cb1: 8'd0;
assign img_cr = post_frame_hsync ? img_cr1: 8'd0;
//--------------------------------------------
//RGB 888 to YCbCr
/********************************************************
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
Y = (77 *R + 150*G + 29 *B )>>8
Cb = (-43*R - 85 *G + 128*B + 32768)>>8
Cr = (128*R - 107*G - 21 *B + 32768)>>8
*********************************************************/
//step1 pipeline mult
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rgb_r_m0 <= 16'd0;
rgb_r_m1 <= 16'd0;
rgb_r_m2 <= 16'd0;
rgb_g_m0 <= 16'd0;
rgb_g_m1 <= 16'd0;
rgb_g_m2 <= 16'd0;
rgb_b_m0 <= 16'd0;
rgb_b_m1 <= 16'd0;
rgb_b_m2 <= 16'd0;
end
else begin
rgb_r_m0 <= rgb888_r * 8'd77;
rgb_r_m1 <= rgb888_r * 8'd43;
rgb_r_m2 <= rgb888_r << 3'd7;
rgb_g_m0 <= rgb888_g * 8'd150;
rgb_g_m1 <= rgb888_g * 8'd85;
rgb_g_m2 <= rgb888_g * 8'd107;
rgb_b_m0 <= rgb888_b * 8'd29;
rgb_b_m1 <= rgb888_b << 3'd7;
rgb_b_m2 <= rgb888_b * 8'd21;
end
end
//step2 pipeline add
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
img_y0 <= 16'd0;
img_cb0 <= 16'd0;
img_cr0 <= 16'd0;
end
else begin
img_y0 <= rgb_r_m0 + rgb_g_m0 + rgb_b_m0;
img_cb0 <= rgb_b_m1 - rgb_r_m1 - rgb_g_m1 + 16'd32768;
img_cr0 <= rgb_r_m2 - rgb_g_m2 - rgb_b_m2 + 16'd32768;
end
end
//step pipeline div
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
img_y1 <= 8'd0;
img_cb1 <= 8'd0;
img_cr1 <= 8'd0;
ycbcr1 <= 16'd0;
end
else begin
img_y1 <= img_y0[15:8];
img_cb1 <= img_cb0[15:8];
img_cr1 <= img_cr0[15:8];
ycbcr1 <= {img_y0[15:11],img_y0[15:10],img_y0[15:11]};
end
end
//延时3拍以同步数据信号
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
pre_frame_vsync_d <= 3'd0;
pre_frame_hsync_d <= 3'd0;
pre_frame_de_d <= 3'd0;
end
else begin
pre_frame_vsync_d <= {pre_frame_vsync_d[1:0],pre_frame_vsync};
pre_frame_hsync_d <= {pre_frame_hsync_d[1:0],pre_frame_hsync};
pre_frame_de_d <= {pre_frame_de_d[1:0],pre_frame_de};
end
end
endmodule
FPGA代码采用的方法的是采用流水线型设计,这样可以加快处理的速度,但是消耗的资源也会更多。第一步,先运算RGB的乘法运算,由于FPGA很难做小数的乘法,所以现将小数乘以256x然后在右移8位就完成了小数乘法的运算。第二步,做三个量的加法运算。第三步,缓存一拍输出。这样算起来从数据输入到输出延时了三个时钟,实现如下图。
本文引用了其他文章资料,如有侵权,联系本人会做出修改。版本1.0,2019.9.28 作者小飞。