本帖最后由 FPGA课程 于 2024-10-16 18:55 编辑
软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1 色彩空间转换原理简介
RGB 、YUV 和 YCbCr 都是人为规定的色彩空间。其用途是在某些标准下以通用化的方式对彩色进行描述。本质上,彩色模型是坐标系统和子空间的描述。 RGB 是依据人眼识别的颜色定义出的空间,可表示大部分颜色。但在科学研究一般不采用RGB 颜色空间,因 为它的细节难以进行数字化的调整。它将色调,亮度,饱和度三个量放在一起表示,很难分开。它是最通用的面向 硬件的彩色模型。该模型用于彩色监视器和一些彩色视频摄像。 在 YUV 色彩空间中,每一个颜色有一个亮度信号 Y,和两个色度信号 U 和 V。亮度信号是强度的感觉,它和 色度信号断开,这样的话强度就可以在不影响颜色的情况下改变。YUV,分为三个分量,“Y”表示明亮度(Luminance 或 Luma),也就是灰度值;而“U”和“V” 表示的则是色度(Chrominance 或 Chroma),作用是描述影像色彩及饱 和度,用于指定像素的颜色。如果没有 uv 信息就会显示黑白。人眼的视觉特点是对亮度更铭感,对位置、色彩相 对来说不铭感。在视频编码系统中为了降低带宽,可以保存更多的亮度信息(luma),保存较少的色差信息(chroma)。 通常对 yuv444 ,yuv422,yuv420 的解释是后面三个数字分别对应前面三个字母。拿 yuv422 来说,y 对应 4,表 示四个图形像素中,每个都有亮度值;u 对应 2 ,表示四个图形像素中,Cb 只占用两个像素;v 对应 2 , 表示四 个图形像素中, Cr 占用两个像素。对于 yuv422 模式,这样解释是没有问题。但是对于 yuv420 解释就不对了, 不能说四个图形像素中,Cr 占用 0 个像素吧? 表示, 是 J:a:b 模式,实心黑色圆圈表示包含色度像素(Cb+Cr),空心圆圈表示不包含色度像素。对于 J:a:b 模式,主要是围绕参考块的概念定义的,这个参考块是一个 J x 2 的矩形,J 通常是 4。这样,此参考块就是宽度 有 4 个像素、高度有 2 个像素的矩形。a 表示参考块的第一行包含的色度像素样本数,b 表示在参考块的第二行 包含的色度像素样本数。 YUV 使用 RGB 的信息,但它从全彩色图像中产生一个黑白图像(Y 信号),然后提取出三个主要的颜色变成 两个额外的信号来描述颜色。把这三个信号组合回来就可以产生一个全彩色图像。 Y 通道描述明亮度信号,值的范围介于亮和暗之间。明亮度是黑白电视可以看到的信号。U (Cb) 和 V (Cr)通 道从红 (U) 和蓝 (V) 中提取亮度值来减少颜色信息量。这些值可以从新组合来决定红,绿和蓝的混合信号。
1.1YUV 与 RGB 的转换
Y = 0.299 R + 0.587 G + 0.114 BU = -0.1687 R - 0.3313 G + 0.5 B + 128 V = 0.5 R - 0.4187 G - 0.0813 B + 128R = Y + 1.402 (V-128)G = Y - 0.34414 (U-128) - 0.71414 (V-128) B = Y + 1.772 (U-128)
1.2YCbCr 与 RGB 的转换
YCbCr 是在世界数字组织视频标准研制过程中作为 ITU - R BT1601 建议的一部分,是 YUV 经过缩放和偏移 的翻版。其中 Y 与 YUV 中的 Y 含义一致, Cb , Cr 同样都指色彩, 只是在表示方法上不同而已。在 YUV 家族中, YCbCr 是在计算机系统中应用最多的成员,其应用领域很广泛,JPEG、MPEG 均采用此格式。一般人们所讲的 YUV 大多是指 YCbCr。 YCbCr 与 RGB 的相互转换: Y = 0.2568*R + 0.5041*G + 0.0979*B + 16; Cb = -0.1482*R - 0.2910*G + 0.4392*B + 128; Cr = 0.4392*R - 0.3678*G - 0.0714*B + 128; R = 1.1644*(Y- 16) + 1.5960*(Cr - 128);G = 1.1644*(Y - 16) - 0.3918*(Cb- 128) -0.8130*(Cr- 128); B = 1.1644*(Y - 16) + 2.0172*(Cb- 128);
2 框图原理图
上面框图内由摄像头采集图像送回 fpga 后经过存储发送给 HDMI。
3 代码分析
3.1matlab 代码分析- clear;clear all;clc;
- image_in = imread('lena_ 1280x720.jpg'); %获取图像
- %图像三元分类
- image_in_r = image_in(:,:,1); image_in_g = image_in(:,:,2); image_in_b = image_in(:,:,3);
- [row,col,n] = size(image_in);
- image1_in_y = uint8(zeros(row,col)); %图像数据类型,即创建一个数组,数据类型为 uint8
- image1_in_cb = uint8(zeros(row,col)); image1_in_cr = uint8(zeros(row,col));
- image2_in_y = uint8(zeros(row,col)); image2_in_cb = uint8(zeros(row,col)); image2_in_cr = uint8(zeros(row,col));
- image1_out_rgb_r = uint8(zeros(row,col)); image1_out_rgb_g = uint8(zeros(row,col)); image1_out_rgb_b = uint8(zeros(row,col));
- image2_out_rgb_r = uint8(zeros(row,col)); image2_out_rgb_g = uint8(zeros(row,col)); image2_out_rgb_b = uint8(zeros(row,col));
- %================================================================================= %rgb2ycbcr 0
- %================================================================================= % ycbcr0 = zeros(size(image_in), 'like', image_in);
- % ycbcr0(:,:,1) = 0.2568*image_in_r + 0.5041*image_in_g + 0.0979*image_in_b + 16; % ycbcr0(:,:,2) = -0.1482*image_in_r - 0.2910*image_in_g + 0.4392*image_in_b + 128; % ycbcr0(:,:,3) = 0.4392*image_in_r - 0.3678*image_in_g - 0.0714*image_in_b + 128; % image0_in_y = ycbcr0(:,:,1);
- % image0_in_cb = ycbcr0(:,:,2);
- % image0_in_cr = ycbcr0(:,:,3);
- %=================================================================================
- %================================================================================= %rgb2ycbcr 1
- %=================================================================================
- T = [ ...
- 0.2568 0.5041 0.0979;... -0.1482 -0.2910 0.4392;...
- 0.4392 -0.3678 -0.0714];%系数数组 Offset = [16; 128; 128];%转换系数
- ycbcr1 = zeros(size(image_in), 'like', image_in);%把 ycbcr1 定义为 image 类型
- for p = 1:3
- ycbcr1(:,:,p) = imlincomb( T(p,1),image_in_r,...
- T(p,2),image_in_g,... T(p,3),image_in_b,...
- Offset(p)); %进行函数运算
- end
- image1_in_y = ycbcr1(:,:,1); image1_in_cb = ycbcr1(:,:,2); image1_in_cr = ycbcr1(:,:,3);
- %=================================================================================
- %================================================================================= %rgb2ycbcr 2
- %=================================================================================
- ycbcr2 = rgb2ycbcr(image_in);
- image2_in_y = ycbcr2(:,:,1); image2_in_cb = ycbcr2(:,:,2); image2_in_cr = ycbcr2(:,:,3);
- %=================================================================================
- %================================================================================= %ycbcr2rgb 0
- %================================================================================= % image0_out_rgb_r = 1.1644*(image0_in_y - 16) + 1.5960*(image0_in_cr - 128);
- % image0_out_rgb_g = 1.1644*(image0_in_y - 16) - 0.3918*(image0_in_cb - 128) -0.8130*(image0_in_cr - 128); % image0_out_rgb_b = 1.1644*(image0_in_y - 16) + 2.0172*(image0_in_cb - 128);
- %=================================================================================
- %================================================================================= %ycbcr2rgb 1
- %=================================================================================
- T = [65.481 128.553 24.966;... -37.797 -74.203 112; ...
- 112 -93.786 -18.214];
- %1.1644 0 1.5960
- %1.1644 -0.3918 -0.8130 %1.1644 2.0172 0
- % We can rewrite the equation in terms of ycbcr which is
- % T ^-1 * (ycbcr - offset) = rgb. This is equation 9.7 in the book.
- Tinv = T^-1;%转置
- % Tinv = [0.00456621 0. 0.00625893;...
- % 0.00456621 -0.00153632 -0.00318811;...
- % 0.00456621 0.00791071 0.]
- offset = [16;128;128];
- T = 255 * Tinv;
- offset = 255 * Tinv * offset;
- % Initialize the output
- rgb1 = zeros(size(ycbcr1), 'like', ycbcr1);
- for p = 1:3
- rgb1(:,:,p) = imlincomb(T(p,1),ycbcr1(:,:,1),...
- T(p,2),ycbcr1(:,:,2),... T(p,3),ycbcr1(:,:,3),... -offset(p));
- end
- image1_out_rgb_r = rgb1(:,:,1); image1_out_rgb_g = rgb1(:,:,2); image1_out_rgb_b = rgb1(:,:,3);
- %=================================================================================
- %================================================================================= %ycbcr2rgb 2
- %=================================================================================
- rgb2 = zeros(size(ycbcr2), 'like', ycbcr2); rgb2 = ycbcr2rgb(ycbcr2);
- image2_out_rgb_r = rgb2(:,:,1); image2_out_rgb_g = rgb2(:,:,2);
- image2_out_rgb_b = rgb2(:,:,3);
- %================================================================================= % image0_out_rgb = cat(3,image0_out_rgb_r,image0_out_rgb_g,image0_out_rgb_b);
- image1_out_rgb = cat(3,image1_out_rgb_r,image1_out_rgb_g,image1_out_rgb_b);
- image2_out_rgb = cat(3,image2_out_rgb_r,image2_out_rgb_g,image2_out_rgb_b);%构造三维矩阵
- %================================================================================= %display
- %=================================================================================
- subplot(251);
- imshow(image_in), title('The original rgb image'); subplot(252);
- imshow(image1_in_y),title('The grey y image'); subplot(253);
- imshow(image1_in_cb),title('The grey cb image'); subplot(254);
- imshow(image1_in_cr),title('The grey cr image'); subplot(255);
- imshow(image1_out_rgb);title('image1 out rgb');
- %
- subplot(256);
- imshow(image_in), title('The original rgb image'); subplot(257);
- imshow(image2_in_y),title('The grey y image'); subplot(258);
- imshow(image2_in_cb),title('The grey cb image'); subplot(259);
- imshow(image2_in_cr),title('The grey cr image'); subplot(2,5,10);
- imshow(image2_out_rgb);title('image2 out rgb');图像显示
- %
复制代码
3.2Verilog 代码分析
3.2.1RGB 转 YCbCr- //按照 1.2 小节中的公式中进行公式变换:
- // ycbcr0(:,:,1) = 0.2568*image_in_r + 0.5041*image_in_g + 0.0979*image_in_b + 16; // ycbcr0(:,:,2) = -0.1482*image_in_r - 0.2910*image_in_g + 0.4392*image_in_b + 128; // ycbcr0(:,:,3) = 0.4392*image_in_r - 0.3678*image_in_g - 0.0714*image_in_b + 128; //先乘以256 然后右移 8 位。
- // ycbcr0(:,:,1) = 256*( 0.2568*image_in_r + 0.5041*image_in_g + 0.0979*image_in_b + 16 )>>8;// ycbcr0(:,:,2) = 256*(-0.1482*image_in_r - 0.2910*image_in_g + 0.4392*image_in_b + 128)>>8; // ycbcr0(:,:,3) = 256*( 0.4392*image_in_r - 0.3678*image_in_g - 0.0714*image_in_b + 128)>>8; //最终结果如下:
- // ycbcr0(:,:,1) = (66*image_in_r + 129*image_in_g + 25*image_in_b + 4096 )>>8; // ycbcr0(:,:,2) = (-38*image_in_r - 74*image_in_g + 112*image_in_b + 32768)>>8; // ycbcr0(:,:,3) = (112*image_in_r - 94*image_in_g - 18*image_in_b + 32768 )>>8; //我们将乘法运算单独在一个时钟周期中完成,代码如下:
- always@(posedge i_clk ornegedge i_rst_n) begin
- if(!i_rst_n) begin
- r_d0 <= 16'd0; g_d0 <= 16'd0; b_d0 <= 16'd0; r_d1 <= 16'd0; g_d1 <= 16'd0; b_d1 <= 16'd0; r_d2 <= 16'd0; g_d2 <= 16'd0; b_d2 <= 16'd0;
- end else begin
- r_d0 <= 66 * i_r;
- g_d0 <= 129 * i_g; b_d0 <= 25 * i_b; r_d1 <= 38 * i_r; g_d1 <= 74 * i_g; b_d1 <= 112 * i_b; r_d2 <= 112 * i_r; g_d2 <= 94 * i_g; b_d2 <= 18 * i_b;
- end end
- //完成乘法后进行加减法运算:
- always@(posedge i_clk ornegedge i_rst_n) begin
- if(!i_rst_n) begin
- y_d0 <= 16'd0; cb_d0 <= 16'd0; cr_d0 <= 16'd0;
- end else begin
- y_d0 <= r_d0 + g_d0 + b_d0 + 4096 ;cb_d0 <= b_d1 - r_d1 - g_d1 + 32768; cr_d0 <= r_d2 - g_d2 - b_d2 + 32768;
- end end
- //最后进行信号的同步: always@(posedge i_clk ) begin
- hsyn <= {hsyn[0],i_hsyn}; vsyn <= {vsyn[0],i_vsyn}; de <= {de[0],i_de};
- end
- assign o_hsyn = hsyn[1];
- assign o_vsyn = vsyn[1];
- assign o_y = y_d0 [15:8];
- assign o_cb= cb_d0[15:8];
- assign o_cr = cr_d0[15:8];
- assign o_de= de[1];
复制代码
- //按照 1.2 小节中的公式中进行公式变换:
- // image0_out_rgb_r = 1.1644*(ycbcr0(:,:,1) - 16) + 1.5960*(ycbcr0(:,:,3) - 128);
- // image0_out_rgb_g = 1.1644*(ycbcr0(:,:,1) - 16) - 0.3918*(ycbcr0(:,:,2) - 128) -0.8130*(ycbcr0(:,:,3) - 128); // image0_out_rgb_b = 1.1644*(ycbcr0(:,:,1) - 16) + 2.0172*(ycbcr0(:,:,2) - 128);
- //展开系数部分:
- // image0_out_rgb_r = 1.1644*ycbcr0(:,:,1) - 18.6304 + 1.5960*ycbcr0(:,:,3) - 204.288;
- // image0_out_rgb_g = 1.1644*ycbcr0(:,:,1) - 18.6304 - 0.3918*ycbcr0(:,:,2) + 50.1504 + -0.8130*ycbcr0(:,:,3) + 104.063;
- // image0_out_rgb_b = 1.1644*ycbcr0(:,:,1) - 18.6304 + 2.0172*ycbcr0(:,:,2) - 258.2016; //进行系数合并:
- // image0_out_rgb_r = 1.1644*ycbcr0(:,:,1) + 1.5960*ycbcr0(:,:,3) - 223;
- // image0_out_rgb_g = 1.1644*ycbcr0(:,:,1) - 0.3918*ycbcr0(:,:,2) - 0.8130*ycbcr0(:,:,3) + 136; // image0_out_rgb_b = 1.1644*ycbcr0(:,:,1) + 2.0172*ycbcr0(:,:,2) - 277;
- //先乘以256 然后右移 8 位。
- // image0_out_rgb_r = 256*(1.1644*ycbcr0(:,:,1) + 1.5960*ycbcr0(:,:,3) - 223)>>8;
- // image0_out_rgb_g = 256*(1.1644*ycbcr0(:,:,1) - 0.3918*ycbcr0(:,:,2) - 0.8130*ycbcr0(:,:,3) + 136)>>8;
- // image0_out_rgb_b = 256*(1.1644*ycbcr0(:,:,1) + 2.0172*ycbcr0(:,:,2) - 277)>>8; //最终结果如下:
- // image0_out_rgb_r = (298*ycbcr0(:,:,1) + 408*ycbcr0(:,:,3) - 57088)>>8;
- // image0_out_rgb_g = (298*ycbcr0(:,:,1) - 100*ycbcr0(:,:,2) - 208*ycbcr0(:,:,3) + 34816)>>8; // image0_out_rgb_b = (298*ycbcr0(:,:,1) + 516*ycbcr0(:,:,2) - 70912)>>8;
- //我们将乘法运算单独在一个时钟周期中完成,代码如下: always@(posedge i_clk ornegedge i_rst_n)
- begin
- if(!i_rst_n) begin
- y_d0 <= 16'd0; // cb_d0 <= 16'd0; cr_d0 <= 16'd0;
- y_d1 <= 16'd0; cb_d1 <= 16'd0; cr_d1 <= 16'd0; y_d2 <= 16'd0; cb_d2 <= 16'd0; // cr_d2 <= 16'd0;
- end else begin
- y_d0 <= 298 * i_y;
- // cb_d0 <= 0 * i_cb; cr_d0 <= 408 * i_cr;
- y_d1 <= 298 * i_y;
- cb_d1 <= 100 * i_cb; cr_d1 <= 208 * i_cr;
- y_d2 <= 298 * i_y;
- cb_d2 <= 516 * i_cb; // cr_d2 <= 0 * i_cr;
- end end
- //完成乘法后进行加减法运算:
- always@(posedge i_clk ornegedge i_rst_n) begin
- if(!i_rst_n) begin
- r_d0 <= 16'd0; g_d0 <= 16'd0; b_d0 <= 16'd0;
- end else begin
- r_d0 <= y_d0 + cr_d0 - 57088;
- g_d0 <= y_d1 - cb_d1 - cr_d1 + 34816;
- b_d0 <= y_d2 + cb_d2 - 70912;
- end end
- //最后进行信号的同步: always@(posedge i_clk ) begin
- hsyn <= {hsyn[0],i_hsyn}; vsyn <= {vsyn[0],i_vsyn};
- de <= {de[0],i_de};
- end
- assign o_hsyn = hsyn[1];
- assign o_vsyn = vsyn[1];
- assign o_r = r_d0[15:8];
- assign o_g = g_d0[15:8];
- assign o_b = b_d0[15:8];
- assign o_de= de[1];
复制代码
4 工程结构分析ov5640 摄像头采集过来是数据经 DDR3 缓存后,通过 FDMA 读出后送入了rgb2ycbcr 模块,rgb2ycbcr 输出的 数据直接送到 HDMI 模块进行驱动显示。
ov5640 摄像头采集过来是数据经 DDR3 缓存后,通过 FDMA 读出后送入了rgb2ycbcr 模块,rgb2ycbcr 输出的 数据直接送到 ycbcr2rgb 模块,最后数据送进 HDMI 模块进行驱动显示。
5 仿真及结果
5.1Matlab 实验结果第一行为 RGB 转 YCbCr ,从左至右分别是原图、Y 图、Cb 图、Cr 图、YCbCr 转 RGB 图。
5.2Modelsim 实验结果
由波形分析可得,o_y,o_cb,o_cr 有输出时,data 有数据输出。 6 搭建 Vitis-sdk 工程创建 soc_base sdk platform 和 APP 工程的过程不再重复,可以阅读 3-3-01_sdk_base_app。以下给出创建好 soc_base sdk platform 的截图和对应工程 APP 的截图。 6.1 创建 SDK Platform 工程
6.2SDK APP 工程
7 硬件连接硬件连接如图所示:
8 上板实验结果
RGB 转 YCbCr:
RGB 转 YCbCr 后转 RGB:
|