当前位置: 首页 > news >正文

Verilog和Matlab实现RGB888互转YUV444

文章目录

  • 一、色彩空间
    • 1.1 RGB色彩空间
    • 1.2 CMYK色彩空间
    • 1.3 YUV色彩空间
  • 二、色彩空间转换公式
    • 2.1 RGB转CMYK
    • 2.2 CMYK转RGB
    • 2.3 RGB888转YUV444
    • 2.4 YUV444转RGB888
  • 三、MATLAB实现RGB888转YUV444
    • 3.1 matlab代码
    • 3.2 matlab结果
    • 3.3 保存原始图像数据和转换成yuv后的图像数据
  • 四、Verilog实现RGB888转YUV444
    • 4.1 verilog代码
    • 4.2 仿真观察
  • 五、MATLAB实现YUV444转RGB888
    • 5.1 matlab代码
    • 5.2 matlab结果
  • 六、Verilog实现YUV444转RGB888
    • 6.1 Verilog代码
    • 6.2 仿真观察


一、色彩空间

  色彩空间就是显示一幅图像所使用的特定颜色组合,不同的应用场景会使用不同的色彩空间。常见的色彩空间有RGB、CMYK、HSV、LAB以及YUV等等。

1.1 RGB色彩空间

  RGB色彩空间最常用的用途就是显示器领域,利用物理中光的三原色可以叠加成不同颜色的原理;因此一个像素由R、G、B三种颜色分量组成,在RGB色彩空间中,R、G、B三个分量的属性是独立的,每个分量数字越大,对应的颜色占比就越大。常见的RGB格式有RGB888、RGB565、RGB555等等,其中RGB888表示每种颜色分量都有256级,所以RGB888能表示256 * 256 * 256=1677w种颜色。RGB色彩空间应用十分广泛,但不适合做图像处理,因为人眼视网膜上存在两种视敏细胞:锥状细胞和杆状细胞这两种细胞对颜色和亮度的感知程度不一样(具体可以去了解以下人眼系统构成),总之就是人眼对亮度的感知大于对颜色的感知。而RGB三种分量都与亮度有关系,因此做图像处理时,改变任意分量对亮度都会产生影响,因此RGB色彩空间通常只是用来显示。

在这里插入图片描述

1.2 CMYK色彩空间

  CMYK色彩空间的使用场景是印刷、打印等领域,当光线照射到一个物体上时,物体将吸收一部分光,并将剩下的光进行反射,反射的光线就是我们所看见的物体颜色,这也是与RGB色彩空间的根本不同之处。CMYK颜色模型使用青、品红、黄、黑四个通道来表示颜色,青、品红、黄三个通道分别对应RGB的补色,K通道表示黑色墨水的量

在这里插入图片描述
  因此RGB色域更广,CMYK相较于RGB色域有限,所以存在一些RGB里的颜色在印刷时无法显示的情况,这些CMYK色域不包含的颜色在印刷时会丢失。

1.3 YUV色彩空间

  YUV是指亮度分量和色度分量都分开表示的像素格式,在 YUV空间中,每一个颜色有一个亮度信号 Y,和两个色度信号 U 和V。亮度信号是强度的感觉,它和色度信号断开,这样的话强度就可以在不影响颜色的情况下改变,是一种模拟信号,最初用于模拟电视广播。

  YCbCr 则是在世界数字组织视频标准研制过程中作为ITU - R BT.601 建议的一部分,其实是YUV经过缩放和偏移的翻版,YCbCr 是 YUV 的一种数字形式,专为数字图像和视频处理而设计。它通常用于数字视频压缩(如 JPEG、MPEG 等)。YCbCr其中Y是指亮度分量,Cb指蓝色色度分量,而Cr指红色色度分量。

  1. YUV是一种模拟信号,其色彩模型源于RGB,常用于模拟广播电视中;在某些情况下,YUV 的分量可以超出 0-255 的范围,尤其是在模拟信号中。
  2. YCbCr是一种数字信号,是YUV压缩和偏移的结果,广泛用于数字视频编码、图像压缩和传输(例如,JPEG、MPEG、H.264 等);分量的范围通常被限制在 0-255(对于 8 位图像)。在某些情况下,Cb 和 Cr 可能会在 -128 到 127 的范围内(有符号值)。

  一般人们所讲的YUV大多是指YCbCr。YCbCr 有许多采样格式,是在获取原始图像数据时采用的策略。如YUV444,YUV422,YUV420 。

  • YUV 4:4:4表示每一个 Y 分量对应一对 UV 分量,每像素占用 (Y + U + V = 8 + 8 + 8 = 24bits)

在这里插入图片描述

  • YUV 4:2:2表示每两个 Y 分量对应一对 UV 分量,每像素占用 (Y + 0.5U + 0.5V = 8 + 4 + 4 = 16bits)

在这里插入图片描述

  • YUV 4:2:0表示每四个 Y 分量对应一对 UV 分量,每像素占用 (Y + 0.25U + 0.25V = 8 + 2 + 2 = 12bits)

在这里插入图片描述

二、色彩空间转换公式

2.1 RGB转CMYK

  第一步:将RGB色彩空间中的颜色映射到CMY色彩空间中R,G,B值除以255,将范围从0…255更改为0~1

R ′ = R / 255 R^{'}=R/255 R=R/255
G ′ = G / 255 G^{'}=G/255 G=G/255
B ′ = B / 255 B^{'}=B/255 B=B/255
  第二步:计算出黑色K的量值:
K = 1 − m a x ( R ′ , G ′ , B ′ ) K=1 - max(R^{'},G^{'},B^{'}) K=1max(R,G,B)
  第三步:计算出C(青色),M(品红),Y(红色)的值:
C = ( 1 − R ′ − K ) / ( 1 − K ) C =(1 - R^{'} - K)/ (1 - K) C=1RK/(1K)
M = ( 1 − G ′ − K ) / ( 1 − K ) M =(1 - G^{'} - K)/ (1 - K) M=1GK/(1K)
Y = ( 1 − B ′ − K ) / ( 1 − K ) Y =(1 - B^{'} - K)/ (1 - K) Y=1BK/(1K)

  例如R、G、 B = 88、137、142转换成CMYK就等于C、M、Y、K=38、4、0、44(单位%)

在这里插入图片描述

2.2 CMYK转RGB

R = 255 ∗ ( 1 − C ) ∗ ( 1 − K ) R=255 * (1-C)*(1-K) R=2551C1K
G = 255 ∗ ( 1 − M ) ∗ ( 1 − K ) G=255 * (1-M)*(1-K) G=2551M1K
B = 255 ∗ ( 1 − Y ) ∗ ( 1 − K ) B=255 * (1-Y)*(1-K) B=2551Y1K

  例如C、M、Y、K=38、4、0、44(单位%)等于R、G、 B = 89、137、143

在这里插入图片描述

2.3 RGB888转YUV444

  不同的视频协议标准中,公式有所区别:

  • 标准数字电视(SDTV)约定使用BT.601中规定的色彩空间,按照BT.656中规定的方法传输数据。
  • 高清数字电视(HDTV)约定使用BT.709中规定的色彩空间,按照BT.1120中规定的方法传输数据。
  • 超高清数字电视(UHDTV)约定使用BT.2020中规定的色彩空间,按照BT.2020中规定的方法传输数据。

  不同的协议中,YCbCr也有不同的形式分为:TV range、full range。TV range 主要是广播电视采用的标准, full range主要是pc端采用的标准,所以full range 有时也叫 pc range。不同的形式,YCbCr的分量取值范围也不同:

  • TV range 的各个分量的范围为: YUV Y∈[16,235] Cb∈[16-240] Cr∈[16-240]
  • full range 的各个分量的范围均为:[0-255]

  本文采用BT.601规范中的full range形式,因此RGB转换YUV公式为:

Y = 0.299 ∗ R + 0.587 ∗ G + 0.114 ∗ B Y=0.299 *R + 0.587*G + 0.114 * B Y=0.299R+0.587G+0.114B
U = − 0.169 ∗ R − 0.331 ∗ G + 0.5 ∗ B + 128 U=-0.169 *R -0.331*G + 0.5 * B + 128 U=0.169R0.331G+0.5B+128
V = 0.5 ∗ R − 0.419 ∗ G − 0.081 ∗ B + 128 V=0.5 *R - 0.419*G -0.081 * B + 128 V=0.5R0.419G0.081B+128

2.4 YUV444转RGB888

R = Y + 1.402 ∗ V − 1.402 ∗ 128 R=Y + 1.402*V - 1.402 * 128 R=Y+1.402V1.402128
G = Y − 0.344 ∗ U − 0.714 ∗ V + 1.058 ∗ 128 G=Y -0.344*U - 0.714 * V + 1.058*128 G=Y0.344U0.714V+1.058128
B = Y + 1.772 ∗ U − 1.772 ∗ 128 B=Y + 1.772*U -1.772*128 B=Y+1.772U1.772128

三、MATLAB实现RGB888转YUV444

3.1 matlab代码

clear all; close all; clc;
% 读取 BMP 图像
bmpImage = imread('...\...\...\...\....\....\....\yuanyang.bmp');% 确保图像是 RGB 格式
if size(bmpImage, 3) ~= 3error('输入图像必须是 RGB 格式');
end% 将 RGB 图像转换为 YUV
R = double(bmpImage(:,:,1));
G = double(bmpImage(:,:,2));
B = double(bmpImage(:,:,3));Y = 0.299 * R + 0.587 * G + 0.114 * B;
U = -0.169 * R - 0.331 * G + 0.5 * B + 128; % 加上128以偏移
V = 0.5 * R - 0.419 * G - 0.081 * B + 128; % 加上128以偏移% 将 YUV 转换为 uint8 类型
Y = uint8(Y);
U = uint8(U);
V = uint8(V);% 创建一个 YUV 整体图像(将 Y、U、V 通道堆叠在一起)
YUV_image = cat(3, Y, U, V);% 显示图像
figure;% 显示原图像
subplot(2, 4, 1);
imshow(bmpImage);
title('原始图像');% 显示 R 通道
subplot(2, 4, 2);
imshow(bmpImage(:,:,1)); % 显示 R 通道
title('R 通道');% 显示 G 通道
subplot(2, 4, 3);
imshow(bmpImage(:,:,2)); % 显示 G 通道
title('G 通道');% 显示 B 通道
subplot(2, 4, 4);
imshow(bmpImage(:,:,3)); % 显示 B 通道
title('B 通道');% 显示 YUV 整体图像
subplot(2, 4, 5); % 合并多个子图用于显示 YUV 整体图像
imshow(YUV_image);
title('YUV 整体图像');% 显示 Y 通道
subplot(2, 4, 6);
imshow(Y);
title('Y 通道');% 显示 U 通道
subplot(2, 4, 7);
imshow(U);
title('U 通道');% 显示 V 通道
subplot(2, 4, 8);
imshow(V);
title('V 通道');

3.2 matlab结果

在这里插入图片描述
  这是RGB转YUV后的图片,我们还可以把每个通道单独显示出来,如下所示:

在这里插入图片描述

3.3 保存原始图像数据和转换成yuv后的图像数据

% 保存原始图像的 RGB 数据
fileID = fopen('original_image_data.dat', 'w');
for row = 1:size(bmpImage, 1)fprintf(fileID, '%s\n', sprintf('%02X %02X %02X ', bmpImage(row, :, 1), bmpImage(row, :, 2), bmpImage(row, :, 3)));
end
fclose(fileID);% 保存 YUV 图像的数据
fileID = fopen('yuv_image_data.dat', 'w');
for row = 1:size(YUV_image, 1)fprintf(fileID, '%s\n', sprintf('%02X %02X %02X ', YUV_image(row, :, 1), YUV_image(row, :, 2), YUV_image(row, :, 3)));
end
fclose(fileID);

  打开两个数据文件,原始图像RGB数据如下:

在这里插入图片描述
  原始图像RGB数值如上所示,排列方式为R、G、B,例如第一个像素点的RGB值就是 0xe1e1e3,接下来打开转换后的YUV数据文件:

在这里插入图片描述
  转换后的图像YUV数值如上所示,排列方式为Y、U、V,例如第一个像素点的YUV值就是 0xe18180,我们根据公式手动验证一下,转换后的数值是否正确:

  • Y = 0.299 * 225 + 0.587225 + 0.114227 = 225.228;四舍五入后 Y = 225 = 0xe1
  • U = -0.169 * 225 - 0.331 * 225 + 0.5 * 227 + 128 = 129 = 0x81
  • V=0.5 225 - 0.419225 -0.081 * 227 + 128 = 127.838;四舍五入后 V = 128 = 0x80

  说明转换后的YUV数值正确,接下来我们用Verilog实现转换

四、Verilog实现RGB888转YUV444

  因为转换公式里有小数,而FPGA不能直接对小数进行运算,一般的处理方式就是将浮点数转成定点数来计算,例如0.299可以写成306(0.2991024)然后再进行运算,运算完成后的结果右移10位就行。例如0.299 * 248 = 74.152,浮点数转成定点数运算就是 306248 /1024 = 74.109。计算结果有误差,所以要选择合适的扩大位宽使得计算误差在可接受范围内,Verilog代码如下:

4.1 verilog代码

`timescale 1ns / 1ps
module rgb2yuv(input                                               clk             ,input                                               rst             ,//输入RGB原始信号input                                               i_data_valid    ,input           [7:0]                               i_data_r        ,input           [7:0]                               i_data_g        ,input           [7:0]                               i_data_b        ,//输出转行后的yuv信号output                                              o_data_valid    ,output          [7:0]                               o_data_y        ,output          [7:0]                               o_data_u        ,output          [7:0]                               o_data_v        
);//Y= 0.299*R + 0.587*G + 0.114*B
//U=-0.169*R - 0.331*G + 0.500*B + 128
//V= 0.500*R - 0.419*G - 0.081*B + 128/****************parameter********************/
parameter   Y_PARA_R    = 306,   // 0.299*1024Y_PARA_G    = 601,   // 0.587*1024Y_PARA_B    = 117;   // 0.114*1024parameter   U_PARA_R    = 173,   // 0.169*1024U_PARA_G    = 339,   // 0.331*1024U_PARA_B    = 512;   // 0.500*1024parameter   V_PARA_R    = 512,   // 0.500*1024V_PARA_G    = 429,   // 0.419*1024V_PARA_B    = 83 ;   // 0.081*1024parameter   BASE        = 131072;// 128*1024/*******************reg***********************/
reg     [1:0]   ro_data_valid   ;
reg     [17:0]  ro_data_y       ;
reg     [17:0]  ro_data_u       ;
reg     [17:0]  ro_data_v       ;
reg     [17:0]  r_y1            ;
reg     [17:0]  r_y2            ;
reg     [17:0]  r_y3            ;
reg     [17:0]  r_u1            ;
reg     [17:0]  r_u2            ;
reg     [17:0]  r_u3            ;
reg     [17:0]  r_v1            ;
reg     [17:0]  r_v2            ;
reg     [17:0]  r_v3            ;/******************wire***********************/
/******************assign*********************/
assign o_data_valid = ro_data_valid[1]     ;
assign o_data_y     = ro_data_y[17:10]     ;
assign o_data_u     = ro_data_u[17:10]     ;
assign o_data_v     = ro_data_v[17:10]     ;//1 clock
always @(posedge clk) beginr_y1 = (i_data_r * Y_PARA_R);r_y2 = (i_data_g * Y_PARA_G);r_y3 = (i_data_b * Y_PARA_B);
endalways @(posedge clk) beginr_u1 = (i_data_r * U_PARA_R);r_u2 = (i_data_g * U_PARA_G);r_u3 = (i_data_b * U_PARA_B);
endalways @(posedge clk) beginr_v1 = (i_data_r * V_PARA_R);r_v2 = (i_data_g * V_PARA_G);r_v3 = (i_data_b * V_PARA_B);
end//2 clock
always @(posedge clk) beginro_data_y = r_y1 + r_y2 + r_y3;ro_data_u = r_u3 - r_u1 - r_u2 + BASE;ro_data_v = r_v1 - r_v2 - r_v3 + BASE;
end//sync_validalways @(posedge clk) beginro_data_valid <= {ro_data_valid[0],i_data_valid};
endendmodule

  整体转换周期只有两个时钟周期,所以valid信号要打两拍。

4.2 仿真观察

  整体仿真环境如《详解BMP图片格式以及关于Verilog图像处理的仿真环境搭建》里的一样,直接把本模块例化进去就可以:

rgb2yuv u_rgb2yuv(.clk           ( clk                ),.rst           ( reset              ),.i_data_valid  ( valid_i            ),.i_data_r      ( img_data_i[23:16]  ),.i_data_g      ( img_data_i[15:8]   ),.i_data_b      ( img_data_i[7:0]    ),.o_data_valid  ( valid_o            ),.o_data_y      ( img_data_o[23:16]  ),.o_data_u      ( img_data_o[15:8]   ),.o_data_v      ( img_data_o[7:0]    )
);

  运行仿真:
在这里插入图片描述
  从打印信息来看,文件初始化完成。
在这里插入图片描述
  我们可以看到,输入的第一个RGB是 0xe1e1e3和matlab的数值一样,输出的第一个YUV像素值是0xe1817f和matlab给的0xe18180不一致,这是因为matlab采取的四舍五入,fpga只能取整,所以这就是fpga计算小数带来的误差,我们打开输出文件夹:

在这里插入图片描述
  我们可以看到原始图像已经转换成了yuv格式,图片和matlab显示的一致,至此rgb转yuv已完成。

五、MATLAB实现YUV444转RGB888

5.1 matlab代码

clear all; close all; clc;
% 读取 YCbCr 图像
% 假设 YCbCr 图像文件名为 'input_ycbcr.png'
ycbcr_image = imread('...\...\...\...\....\....\....\new_img.bmp');% 显示原始 YCbCr 图像
figure;
subplot(1, 2, 1);
imshow(ycbcr_image);
title('Original YCbCr Image');% 将 YCbCr 转换为 RGB
Y = double(ycbcr_image(:,:,1));
Cb = double(ycbcr_image(:,:,2));
Cr = double(ycbcr_image(:,:,3));% 计算 RGB
R = Y + 1.402 * (Cr - 128);
G = Y - 0.344136 * (Cb - 128) - 0.714136 * (Cr - 128);
B = Y + 1.772 * (Cb - 128);% 限制 RGB 值在 [0, 255] 范围内
R = min(max(R, 0), 255);
G = min(max(G, 0), 255);
B = min(max(B, 0), 255);% 合并 RGB 分量
RGB = cat(3, uint8(R), uint8(G), uint8(B));% 显示转换后的 RGB 图像
subplot(1, 2, 2);
imshow(RGB);
title('Converted RGB Image');

5.2 matlab结果

在这里插入图片描述
  我们可以看到将已经转成YUV格式的图片通过这个公式有转会了RGB格式,与原图一致。

六、Verilog实现YUV444转RGB888

  YUV转RGB和上面一样,使用浮点数转定点数就好,但是注意溢出的问题,最后还要将RGB数值限幅到[0-255],代码如下:

6.1 Verilog代码

`timescale 1ns / 1ps
module yuv2rgb(input                                               clk             ,input                                               rst             ,//输入YUV原始信号input                                               i_data_valid    ,input           [7:0]                               i_data_y        ,input           [7:0]                               i_data_u        ,input           [7:0]                               i_data_v        ,//输出转行后的rgb信号output                                              o_data_valid    ,output   reg       [7:0]                            o_data_r        ,output   reg       [7:0]                            o_data_g        ,output   reg       [7:0]                            o_data_b        
);//R = Y + 1.402 * (V - 128)                     = Y + 1.402*V - 1.402 * 128
//G = Y - 0.344 * (U - 128) - 0.714 * (V - 128) = Y - 0.344*U - 0.714*V + 1.058*128
//B = Y + 1.772 * (U - 128)                     = Y + 1.772*U - 1.772 * 128/****************parameter********************/
parameter   R_PARA_V    = 1436;   // 1.402*1024parameter   G_PARA_U    = 352,    // 0.344*1024G_PARA_V    = 731;    // 0.714*1024parameter   B_PARA_U    = 1815;   // 1.772*1024parameter   BASE1       = 183763, // 1.402*128*1024BASE2       = 138674, // 1.058*128*1024BASE3       = 223260; // 1.772*128*1024/*******************reg***********************/
reg     [19:0]  r_r1            ;
reg     [19:0]  r_r2            ;
reg     [19:0]  r_g1            ;
reg     [19:0]  r_g2            ;
reg     [19:0]  r_b1            ;
reg     [2:0]   ro_data_valid   ;
reg     [19:0]  ro_data_r       ;
reg     [19:0]  ro_data_g       ;
reg     [19:0]  ro_data_b       ;
/******************assign*********************/
assign o_data_valid = ro_data_valid[2]     ;//1clock
always @(posedge clk) beginr_r1 <= {i_data_y,10'b0};r_r2 <= i_data_v * R_PARA_V;
endalways @(posedge clk) beginr_g1 <= i_data_u * G_PARA_U;r_g2 <= i_data_v * G_PARA_V;
endalways @(posedge clk) beginr_b1 <= i_data_u * B_PARA_U;
end//2clock
always @(posedge clk) beginro_data_r <= r_r1 + r_r2 - BASE1;ro_data_g <= r_r1 - r_g1 - r_g2 + BASE2;ro_data_b <= r_r1 + r_b1 - BASE3;
end//3clock   
always @(posedge clk) beginif(ro_data_r[19]==1'b1)begin     //若结果为负数,则变为0o_data_r <= 8'd0;end else beginif(ro_data_r[18]==1'b1)      //如果超过了255,则限幅到255o_data_r <= 8'd255;elseo_data_r <= ro_data_r[17:10];end
endalways @(posedge clk) beginif(ro_data_g[19]==1'b1)begin     //若结果为负数,则变为0o_data_g <= 8'd0;end else beginif(ro_data_g[18]==1'b1)      //如果超过了255,则限幅到255o_data_g <= 8'd255;elseo_data_g <= ro_data_g[17:10];end
endalways @(posedge clk) beginif(ro_data_b[19]==1'b1)begin     //若结果为负数,则变为0o_data_b <= 8'd0;end else beginif(ro_data_b[18]==1'b1)      //如果超过了255,则限幅到255o_data_b <= 8'd255;elseo_data_b <= ro_data_b[17:10];end
endalways @(posedge clk) beginro_data_valid <= {ro_data_valid[1:0],i_data_valid};
endendmodule

6.2 仿真观察

  打开仿真,先看打印信息:
在这里插入图片描述
  图片初始化完成,我们再来观察数值:
在这里插入图片描述
  输入的第一个YUV数值是0xe1817f,与前文RGB转出的YUV数值一致。我们可以看到输出的RGB数值为0xdfe1eb,转换成十进制就是(223,225,235),而最原始的图片第一个RGB数值是(225,225,227)这也是转换两次后的误差导致。我们打开输出文件夹:

在这里插入图片描述
  可以看到new_img_1就是我们从RGB转到YUV再转到RGB的图片,可以看出图片是一致的,仔细看颜色会有看出有一点点区别,这就是精度的问题;如果想要更高的精度,可以试着把浮点数扩到4096倍再计算。


http://www.mrgr.cn/news/25915.html

相关文章:

  • 【ESP-IDF FreeRTOS】事件组
  • 基于Web的门店管理系统的设计与实现---附源码76269
  • Jetpack Compose Side Effects in Details 副作用的详细信息
  • 通过 Sniper Links 提高您的电子邮件确认率
  • 【SSRF漏洞】——http协议常见绕过
  • K1计划100%收购 MariaDB; TDSQL成为腾讯云核心战略产品; Oracle@AWS/Google/Azure发布
  • java 可变参数
  • java.io.IOException: Too many open files 分析与解决
  • [DCVRP] 基于复杂网络的k-opt算法解空间表示(五)
  • ElementPlus自定义更换主题色
  • Excel图片批量插入单元格排版处理插件【图片大师】
  • 出海公司如何快速搭建海外团队指南
  • MongoDB与Pymongo深度实践:从基础概念到无限级评论应用示例
  • 项目实战应用Redis分布式锁
  • 828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署CodeX Docs文档工具
  • python 实现euler modified变形欧拉法算法
  • 学懂C++(六十):C++ 11、C++ 14、C++ 17、C++ 20新特性大总结(万字详解大全)
  • ArcGIS Pro SDK (十四)地图探索 3 弹出窗口
  • 基于Spark 的零售交易数据挖掘分析与可视化
  • 亚马逊测评自建团队与工作室的五大优势亮点,打造高权重评价系统