PotatoPie 4.0 实验教程(19) —— FPGA实现摄像头RGB图像转YCbCr-Anlogic-安路社区-FPGA CPLD-ChipDebug

PotatoPie 4.0 实验教程(19) —— FPGA实现摄像头RGB图像转YCbCr

手机扫码

20240416075513933-1713225291635

链接直达

https://item.taobao.com/item.htm?ft=t&id=776516984361

什么是色彩空间?

“色彩空间”一词源于西方的“Color Space”,又称作“色域”,色彩学中,人们建立了多种色彩模型,以一维、二维、三维甚至四维空间坐标来表示某一色彩,这种坐标系统所能定义的色彩范围即色彩空间。我们经常用到的色彩空间主要有RGB色彩空间和YUV色彩空间。

20240406165120796-image

RGB 颜色空间

20240406165252704-image

RGB 颜色系统可能是您更熟悉的一种。 RGB 色彩空间是红色、绿色和蓝色的缩写,是一个在每个屏幕像素中使用这三种颜色来显示照片和视频的系统。

与 YCbCr 色彩空间不同,RGB 同时处理颜色和亮度,因此您的文件将占用更多存储空间。

全 RGB 的颜色系统有 255 个级别 – 0 级表示绝对黑色,而 255 级表示绝对白色。在第 1 级到第 254 级之间,您将获得通过使用红色、绿色和蓝色的不同组合创建的全光谱颜色。

RGB 系统最常用于计算机显示器、电视、摄像、摄影等。它也是视频游戏、网页设计和数字媒体的首选色彩系统。

YCbCr色彩空间

 YCbCr 根据亮度 (luma)(又称整体亮度)定义颜色。 “Y”代表亮度,“Cb”代表蓝色减去亮度,“Cr”代表红色减去亮度。

YCbCr 格式能够分离亮度和色度分量,从而尽可能地从文件中删除冗余。

考虑到人眼对亮度比颜色更敏感,YCbCr优先捕捉亮度细节,消除一些不必要和多余的色度细节。这使您的设备可以更好地压缩 YCbCr 文件并节省一些存储空间,同时尽可能保留视频和图像的详细信息。

考虑到这一点,YCbCr 格式仍然可以产生令人惊讶的广泛颜色范围,使其成为印刷机的绝佳选择。

YCbCr不是一种绝对色彩空间,是YUV压缩和偏移的版本。YCbCr的Y与YUV中的Y含义一致,Cb和Cr与UV同样都指色彩,Cb指蓝色色度,Cr指红色色度。YCbCr 也是视频的本机格式,在应用上很广泛,JPEG、MPEG、DVD、摄影机、数字电视等皆采此一格式。因此一般俗称的YUV大多是指YCbCr。

YCbCr 色彩空间与 YUV 色彩空间在概念上很相似,实际上在很多应用中,它们的术语常常被互换使用。但在技术上有一些微妙的区别:

  1. YCbCr 色彩空间是被国际电信联盟(ITU)标准化的,而 YUV 并非官方标准,通常用于描述模拟视频信号。

  2. YCbCr 色彩空间的转换公式经过了精确的数学推导和标准化,而 YUV 的转换公式可能会有一些变化。

  3. YCbCr 色彩空间更为严格地定义了色度和亮度分量的范围,以便于数字信号的编码和解码。

总的来说,YCbCr 色彩空间是一种常用于数字视频处理的标准化色彩编码系统,它将图像的亮度和色度分开表示,便于对彩色图像进行压缩、传输和处理。

YCbCr格式有:

  • 4∶4∶4
  • 4∶2∶2
  • 4∶1∶1
  • 4∶2∶0

YCbCr 和 RGB 颜色之间的联系

20240406165859845-image

YCbCr 和 RGB 都是决定成像系统可以表示的颜色的主要色彩空间 – 然而,这就是两者之间仅有的联系点。

RGB 将所有颜色表示为不同级别的红、绿和蓝的独特组合。所有三个通道都必须以全分辨率存储,因此您的图像和视频会更重并占用更多空间。

另一方面,YCbCr 将亮度和颜色分开,因此您始终可以以较高分辨率存储亮度以获取更多细节,并以较低分辨率存储颜色,因为无论如何人眼都无法完全看到它们。

YCbCr RGB 的转换公式

RGB转YCbCr
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;

YCbCr转RGB
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);

python实现上YCbCr转RGB

代码读取一张图片并将其转换为 YCbCr 色彩空间。然后提取出 Y、Cb、Cr 分量,并显示每个分量的图像。请将 lena.jpg 替换为您要读取的图片文件的路径。

以下是对提供的代码段的详细说明:

  1. 导入模块

    • cv2:OpenCV库,用于图像处理。
    • numpy as np:NumPy库,用于数组操作。
  2. 定义 RGB 到 YCbCr 转换函数

    • rgb_to_ycbcr(rgb):接受一个RGB图像作为输入,并返回相应的YCbCr图像。
    • 分别计算了Y、Cb和Cr分量,并将它们合并成一个YCbCr图像。
  3. 导入 os 模块

    • 使用os模块来获取当前脚本所在的目录路径。
  4. 读取图像文件

    • cv2.imread()函数读取了一张名为”Lena.jpg”的图像文件,并将其存储在变量img中。
    • 图像文件路径是通过拼接当前脚本所在目录和文件名得到的。
  5. 显示原始图像

    • 使用cv2.imshow()函数显示了原始的RGB图像,窗口标题为”Original Image”。
  6. 调用转换函数

    • 调用了rgb_to_ycbcr()函数,将RGB图像转换为YCbCr色彩空间,并将结果存储在变量ycbcr中。
  7. 提取 Y、Cb、Cr 分量

    • 从YCbCr图像中提取了Y、Cb和Cr分量,并分别存储在变量YCbCr中。
  8. 显示 Y、Cb、Cr 分量图

    • 使用cv2.imshow()函数分别显示了Y、Cb和Cr分量图像,窗口标题分别为”Y Component”、”Cb Component”和”Cr Component”。
  9. 等待按下任意键继续并关闭窗口

    • 使用cv2.waitKey(0)函数等待用户按下任意键,然后使用cv2.destroyAllWindows()函数关闭所有打开的窗口。

在powershell输入如下命令:

20240415104242652-image

运行结果如下图

20240414100835360-image

matlab实现上YCbCr转RGB

这个Matlab函数实现了RGB到YCbCr色彩空间的转换,并显示了原始图像以及转换后的Y、Cb、Cr分量图像。

以下是对提供的代码段的详细说明:

  1. 导入模块

    • cv2:OpenCV库,用于图像处理。
    • numpy as np:NumPy库,用于数组操作。
  2. 定义 RGB 到 YCbCr 转换函数

    • rgb_to_ycbcr(rgb):这是一个自定义函数,用于将RGB图像转换为YCbCr色彩空间。它接受一个RGB图像作为输入,并返回相应的YCbCr图像。
    • 在函数内部,首先将RGB图像的各个通道分离出来,然后根据YCbCr转换的公式计算Y、Cb和Cr分量,并最终将它们合并成一个YCbCr图像返回。
  3. 导入 os 模块

    • 使用os模块来获取当前脚本所在的目录路径。
  4. 读取图像文件

    • 使用cv2.imread()函数读取了一张名为”Lena.jpg”的图像文件,并将其存储在变量img中。
    • 图像文件路径是通过拼接当前脚本所在目录和文件名得到的。
  5. 显示原始图像

    • 使用cv2.imshow()函数显示了原始的RGB图像,窗口标题为”Original Image”。
  6. 调用转换函数

    • 调用了rgb_to_ycbcr()函数,将RGB图像转换为YCbCr色彩空间,并将结果存储在变量ycbcr中。
  7. 提取 Y、Cb、Cr 分量

    • 从YCbCr图像中提取了Y、Cb和Cr分量,并分别存储在变量YCbCr中。
  8. 显示 Y、Cb、Cr 分量图

    • 使用cv2.imshow()函数分别显示了Y、Cb和Cr分量图像,窗口标题分别为”Y Component”、”Cb Component”和”Cr Component”。
  9. 等待按下任意键继续并关闭窗口

    • 使用cv2.waitKey(0)函数等待用户按下任意键,然后使用cv2.destroyAllWindows()函数关闭所有打开的窗口。

matlab运行脚本时会出现类似下图的错误,提示rgb2ycbcr.m已在matlab路径中存在,直接点更改文件夹即可。

20240415090748798-image

20240415085455673-image

FPGA工程分析

工程数据流图

层次分析

20240406174955136-image

模块代码分析

与demo18相比,只是多了一个rgb2ycbcr的模块,也就是这一段代码,在从SDRAM读出来之后,经它处理后再输出hdmi_tx模块

rgb2ycbcr u_rgb2ycbcr
(
    .i_clk(clk_pixel),    
    .i_rst_n(sys_rst_n),  
    .i_vs(VGA_VS),             
    .i_hs(VGA_HS),             
    .i_de(VGA_DE),               
    .i_r(VGA_RGB[23:16]), 
    .i_g(VGA_RGB[15:8]),   
    .i_b(VGA_RGB[7:0]),       
    .o_vs(ycbcr_vs),       
    .o_hs(ycbcr_hs),       
    .o_de(ycbcr_de),      
    .o_y(ycbcr_Y),        
    .o_cb(ycbcr_Cb),      
    .o_cr(ycbcr_Cr)       
);

EG4 FPGA可以实现的转换公式

前面的转换公式存在浮点数,我们需要进行浮点转定点的转换。

方法是将式子的右边先乘以一个256,四舍五入取整,然后再除以一个256,这样等式右边相当于没变(不过有一点误差),而除以256对于逻辑来说就是右移256。当然也不是一定要乘以256,乘以1024或2048也是可以的,只是没有必要。

最终算式变成这样:

Y = (66*image_in_r + 129*image_in_g + 25*image_in_b + 4096 )>>8;
Cb = (-38*image_in_r - 74*image_in_g + 112*image_in_b + 32768)>>8;
Cr = (112*image_in_r - 94*image_in_g - 18*image_in_b + 32768 )>>8;

代码中主要就两段。

第一步先计算乘法:

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) ;

第二步计算加减法:

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) ;

需要注意几点:

  • 上面的代码之所以要将公式拆分成两步的原因是为了优化时序,所以做了一个两级流水。
  • 由于上一步做了流水分隔,所以相应的行场信号要往后延一拍。
  • hs <= {hs[0],i_hs};
    vs <= {vs[0],i_vs};
    de <= {de[0],i_de};
  • 要注意因为计算y_d0,bc_d0, br_d0的时候先乘了256,因此需要位宽要16位,这样带来另一个好处,直接取高位就相当于右移了8位。

管脚约束

PotatoPie 4.0 实验教程(18) —— FPGA实现OV5640摄像头采集以SDRAM作为显存进行HDMI输出显示相同,不作赘述。

时序约束

PotatoPie 4.0 实验教程(18) —— FPGA实现OV5640摄像头采集以SDRAM作为显存进行HDMI输出显示相同,不作赘述。

实验现象

实验结果

只显示Y分量的图像

20240406174144630-image

只显示Cb分量

20240406174844205-image

只显示Cr分量

20240406174905154-image

请登录后发表评论

    没有回复内容