实验1 | RGB与YUV两彩色空间之间相互转换的代码实现

12 篇文章 2 订阅

由于目录过长不便查阅,故不使用TOC生成目录,可以使用CSDN侧边导航栏导航,在正文左侧,随时都可点击!

1 实验目的

  1. 编写 RGB 转化为YUV 程序,注意要使用部分查找表的初始化和调用。将得到的 RGB 文件转换为 YUV 文件,用YUV Viewer播放器观看,验证是否正确。
  2. 将 YUV 转换为 RGB 的程序。将给定的实验数据用该程序转换为 RGB 文件。 并与原 RGB 文件进行比较, 如果有误差,分析误差来自何处。
  3. 总结RGB和 YUV 彩色空间转换的转换公式及编程实现的算法并写成实验报告。

2 实验原理

2.1 R G B RGB RGB Y U V YUV YUV 彩色空间转换的转换公式的推导

2.1.1亮度方程

根据电视原理的知识,我们知亮度方程是根据三基色信号加权生成的, R − Y 、 B − Y R-Y、B-Y RYBY则可以直接根据亮度方程算出:
{ Y = 0.2990 R + 0.5870 G + 0.1140 B R − Y = 0.7010 R − 0.5870 G − 0.1140 B B − Y = − 0.2990 R − 0.5870 G + 0.8860 B (1) \left \{ \begin{aligned} Y & = & 0.2990R+0.5870G+0.1140B\\ R-Y & = & 0.7010R-0.5870G-0.1140B \\ B-Y & = & -0.2990R-0.5870G+0.8860B \end{aligned} \right. \tag{1} YRYBY===0.2990R+0.5870G+0.1140B0.7010R0.5870G0.1140B0.2990R0.5870G+0.8860B(1)

2.1.2 色差信号压缩后得到模拟分量 P b 、 P r Pb、Pr PbPr

接着,在 R − Y 、 B − Y R-Y、B-Y RYBY信号的基础上,为了使色差信号的动态范围控制在±0.5之内,我们对色差信号引入压缩系数。
{ P b = 0.564 ( B − Y ) = − 0.1684 R − 0.3316 G + 0.5000 B P r = 0.713 ( R − Y ) = + 0.5000 R − 0.4187 G − 0.0813 B (2) \left \{ \begin{aligned} P_b & = &0.564(B-Y)=-0.1684R-0.3316G+0.5000B\\ P_r & = &0.713(R-Y)=+0.5000R-0.4187G-0.0813B \\ \end{aligned} \right. \tag{2} {PbPr==0.564(BY)=0.1684R0.3316G+0.5000B0.713(RY)=+0.5000R0.4187G0.0813B(2)

U、V分量与Pr、Pb分量的区别:我们将信号进行归一化并生成U、V模拟分量。其主要作用是在NTSC制(模拟电视制式)中,避免在正交平衡调幅时将色度信号加在亮度信号上时,使总幅度过多超出黑白电视规定的标准。
{ U = 0.493 ( B − Y ) V = 0.877 ( R − Y ) (3) \left \{ \begin{aligned} U & = & 0.493(B-Y)\\ V & = & 0.877(R-Y) \\ \end{aligned} \right. \tag{3} {UV==0.493(BY)0.877(RY)(3)
而Pr、Pb虽然也是模拟分量,但是是为了色差信号的动态范围控制在±0.5之内,总动态范围与亮度信号动态范围保持一致(0-1V),才进行的归一化处理,两者目的不同。
由于本文之后不涉及模拟电视制式,故U和V不指代NTSC制(模拟电视制式)中的模拟分量,而是指后文提到的Cb、Cr。

2.1.3 量化为数字信号及码电平的分配

随后,我们对 Y 、 P r Y、Pr YPr P b Pb Pb进行了量化。

亮度信号

亮度信号在进行8 bit量化时,需要在上下两端留出HEADROOM,作为信号超越动态范围的保护带。

在256级的上端留出20级,下端留出16级作为余量,即 Y Y Y的动态范围为16—235;
在这里插入图片描述

色度信号

对于两个色差信号,在256级的上端留出15级,下端留出16级作为余量,即的动态范围为16—240。

C r Cr Cr C b Cb Cb
在这里插入图片描述在这里插入图片描述

根据量化公式

量 化 级 = i n t { m a x Q − m i n Q m a x E − m i n E × 数 字 电 平 公 式 + 0 电 平 量 化 级 数 } (4) 量化级=int \left\{ \frac{max Q-min Q}{maxE-minE} \times数字电平公式+ 0电平量化级数\right\} \tag{4} =int{maxEminEmaxQminQ×+0}(4)其中,Q为量化等级值,E为模拟电平值。

由(4)可以得到
{ Y ′ = i n t { 235 − 16 0.5 − ( − 0.5 ) × Y + 16 } C b = i n t { 240 − 16 1 − 0 × P b + 128 } C r = i n t { 240 − 16 1 − 0 × P r + 128 } (5) \left \{ \begin{aligned} Y' & = & int\left\{ \frac{235-16}{0.5-(-0.5)} \times Y+ 16\right\} \\ C_b & = & int\left\{ \frac{240-16}{1-0} \times P_b+ 128\right\} \\ C_r & = & int\left\{ \frac{240-16}{1-0} \times P_r+ 128\right\} \\ \end{aligned} \right. \tag{5} YCbCr===int{0.5(0.5)23516×Y+16}int{1024016×Pb+128}int{1024016×Pr+128}(5)

2.1.4 Y U V YUV YUV to R G B RGB RGB公式

将(2)代入(5),我们就得到了最终的 Y U V YUV YUV R G B RGB RGB的转换式。(其中字母的意义已在前有所说明,此处 U V = C b C r UV=CbCr UV=CbCr,而非NTSC模拟电视的 U V UV UV分量)
{ Y = i n t { ( 66 × R + 129 × G + 25 × B ) × 2 n − 8 + 16 } U = i n t { ( − 38 × R − 74 × G + 112 × B ) × 2 n − 8 + 128 } V = i n t { ( 112 × R − 94 × G − 18 × B ) × 2 n − 8 + 128 } (6) \left \{ \begin{aligned} Y & = & int\left\{ (66 \times R+129 \times G + 25 \times B)\times 2^{n-8}+16\right\} \\ U & = & int\left\{ (-38 \times R-74 \times G + 112 \times B)\times 2^{n-8}+128\right\} \\ V & = & int\left\{ (112 \times R-94 \times G -18 \times B)\times 2^{n-8}+128\right\} \\ \end{aligned} \right. \tag{6} YUV===int{(66×R+129×G+25×B)×2n8+16}int{(38×R74×G+112×B)×2n8+128}int{(112×R94×G18×B)×2n8+128}(6)

为了加快C++程序的运算,我们在使用查找表的同时,可以使用按位运算操作>>,这也同时减小了误差。可得C++表达式:

Y = (66 * R + 129 *G + 25 * B) >> 8 + 16;
U = (-38 * R - 74 *G + 112 * B) >> 8 + 128;
V = (112 * R - 94 *G - 18 * B) >> 8 + 128;

这里询问了老师,示例代码中直接使用了公式(2)进行计算,对于溢出到HEADROOM或更外的地方直接写了if判断语句进行了一刀切,归置到最高电平。而使用公式(6)则是相当于将原来模拟的范围再次进行压缩,线性均分到了16-235(240)的范围,因此,极值会更白/更黑。但是中间的量化误差可能会比前一种方法更大。

2.1.5 R G B RGB RGB to Y U V YUV YUV 公式推导

Y U V YUV YUV to R G B RGB RGB 公式(6)写作矩阵形式:
[ Y U V ] = 1 256 [ 66 129 25 − 38 74 112 112 − 94 − 18 ] [ R G B ] + [ 16 128 128 ] (7) \begin{bmatrix} Y\\ U\\ V\\ \end {bmatrix}=\frac{1}{256}\begin{bmatrix} 66 & 129 & 25\\-38 & 74 & 112 \\112 & -94 &-18 \end {bmatrix}\begin{bmatrix} R\\ G\\ B\\ \end {bmatrix}+\begin{bmatrix} 16\\ 128\\ 128\\ \end {bmatrix} \tag{7} YUV=2561663811212974942511218RGB+16128128(7)
令上式对应 Y = 1 256 A R + C (8) \boldsymbol Y= \frac{1}{256}\boldsymbol A\boldsymbol R+\boldsymbol C \tag{8} Y=2561AR+C(8)
则(8)可变形为:
A R = 256 × ( Y − C ) A − 1 A R = 256 × A − 1 ( Y − C ) R = 256 × A − 1 ( Y − C ) (9) \boldsymbol {AR} =256 \times\boldsymbol {(Y-C)}\\ \boldsymbol A ^{-1}\boldsymbol {AR}=256 \times \boldsymbol A ^{-1}\boldsymbol {(Y-C)}\\ \boldsymbol {R} =256 \times \boldsymbol A ^{-1}\boldsymbol {(Y-C)}\tag{9} AR=256×(YC)A1AR=256×A1(YC)R=256×A1(YC)(9)

由于 A − 1 \boldsymbol A^{-1} A1数量级小,为避免误差,将 A \boldsymbol A A中的每个元素缩小为原来的 1 256 × 256 \frac{1}{256 \times 256} 256×2561,设为 A 1 \boldsymbol A_1 A1后再进行上述(9)的运算。
[ R G B ] = 1 256 A 1 − 1 [ Y − 16 U − 128 V − 128 ] (10) \begin{bmatrix} R\\ G\\ B\\ \end {bmatrix}=\frac{1}{256} \boldsymbol A_1^{-1}\begin{bmatrix} Y-16\\ U-128\\ V-128\\ \end {bmatrix} \tag{10} RGB=2561A11Y16U128V128(10)
最终得到C++表达式:

R = (298 * Y + 411 * V - 57376) >> 8;
G = (298 * Y - 101 *U - 211 *V + 35168) >> 8 ;
B = (298 * Y + 519 *U - 71200) >> 8 ;

2.2 下采样

下采样,就是RGB转换为YUV图像时,会产生同样多个数量的Y\U\V,若YUV想要4:2:0的采样制式,则需要进行下采样。

具体实现方法:U\V同理,以U为例。将相邻的四个U计算加权平均值,合成为一个。
在这里插入图片描述

2.3 查找表

查找表,即将所有分量转换后对应的值存储在一个数组里,使用时可以直接取用,是一种以空间为代价换时间效率的策略。

2.4 RGB和YUV的文件结构

可参考我的上篇博客中的部分3 文件的存储格式和图像关键数值的计算

3 参考程序的阅读和学习

仔细阅读下发的程序,学习到以下精髓:

3.1. 程序对于变量的选择、内存的占用非常精确。

变量空间几乎没有冗余,也不会产生溢出。在处理24bits(B8 G8 R8)的RGB图像时,为其每个像素单个分量开辟的空间都为8bits,不产生任何的浪费。

	/* define datatype*/
	#define u_int8_t	unsigned __int8
	#define u_int		unsigned __int32
	#define u_int32_t	unsigned __int32
	#define FALSE		false
	#define TRUE		true
	
	rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);

3.2. 程序在对Y/U/V分量进行赋值时,对超过数字电平范围的值进行了规定化,预留出了HEADROOM,防止值溢出。

	for (i = 0; i < frameWidth*frameHeight; i++) /*prevent value overflow*/
			{
				if (yBuf[i] < 16) yBuf[i] = 16;
				if (yBuf[i] > 235) yBuf[i] = 235;
			}
	
			for (i = 0; i < frameWidth*frameHeight/4; i++)
			{
				if (uBuf[i] < 16) uBuf[i] = 16;
				if (uBuf[i] > 240) uBuf[i] = 240;  /* u & v's headroom is 16-240*/
	
				if (vBuf[i] < 16) vBuf[i] = 16;
				if (vBuf[i] > 240) vBuf[i] = 240;
			}

3.3. 程序有完备的应对异常的机制。

几乎每个读写文件、操作都做了是否成功的判断;读文件时使用while循环确保读取直到EOF

if (rgbFile == NULL)
		{
			printf("cannot find rgb file\n");
			exit(1);
		}
		else
		{
			printf("The input rgb file is %s\n", rgbFileName);
		}
		
	while (fread(rgbBuf, 1, frameWidth * frameHeight * 3, rgbFile))  

3.4. 程序使用了main函数的命令行参数argcargv.

main函数可以表示为int main(int argc,char ** argv )
int main(int argc,char * argv[] )

其中,argc=用户输入字符串的数量+1,作为传入argv数组的参数的数量。+1 是指argv[0],是用来表示可执行文件的名称。argc自动生成。

argv[]是一个数组指针,存放了传入的每一个参数。其中argv[0]可省略,是用来表示可执行文件的名称。

3.5. 程序设置了filp参数应对翻转的图像

如果图像的BGR是倒序排列的,即图像开始点为右下角的点,直接设置fip为1,程序就倒着循环了。

4 程序实现

4.1 RGB to YUV

经过我的研究,我发现给的参考程序已经是一个完美的程序,即使我再写一个,以我的水平最多也只能与该程序达到的效率、严谨程度、健壮性、可用性持平,而不能超越,故直接采用了参考程序。

但是为了证明自己真的完全理解了程序,我仍然写了一个远不如该程序的程序,不在此贴出占用宝贵的实验报告空间,甩一个传送门:RGB to YUV的更弱一些的实现

4.2 YUV to RGB

4.2.1 初步实现

建立解决方案,文件结构如下:在这里插入图片描述

  • main.cpp实现文件的读取、写入和调用yuv2rgb算法的整个流程
  • yuv2rgb.cpp和yuv2rgb.h实现YUV空间转换到RGB空间的算法

main.cpp不必多言,无需解释:

# include<iostream>
# include <fstream>
# include"yuv2rgb.h"
using namespace std;

int main(int argc, char ** argv) {
	ifstream originYUV(argv[1], ios::binary);
	ofstream RGB_out(argv[2], ios::binary);
	if (!originYUV || !RGB_out) {
		cout << "open file failed!" << endl;
		return 0;
	}
	int width = atoi(argv[3]);
	int height = atoi(argv[4]);
	int size = width * height; 
	int yuvsize = size * 1.5;/*-- uv分量是y的1/4 --*/
	int rgbsize = size * 3;
	unsigned char* YUV_Buffer = new unsigned char[yuvsize];
	unsigned char* RGB_Buffer_out = new unsigned char[rgbsize];
	originYUV.read((char*)YUV_Buffer, yuvsize);

	yuv2rgb(YUV_Buffer,RGB_Buffer_out,size,width);
	RGB_out.write((char*)RGB_Buffer_out,rgbsize);

	delete[] YUV_Buffer;
	delete[] RGB_Buffer_out;
	originYUV.close();
	RGB_out.close();

	return 0;
}

yuv2rgb.h

#pragma once
void yuv2rgb(unsigned char* YUV_Buffer, unsigned char* RGB_Buffer_out, int size,int width);

yuv2rgb.cpp

  • 由于yuv文件是4:2:0采样的,U、V的采样点数是Y的 1 4 \frac{1}{4} 41,这也就意味着我们在进行空间转换时,UV分量必须要做扩张以使每个Y点都有对应的U、V,即插值计算。此处的插值计算采用了最简单的方式:U、V一个像素点对应整幅图像相邻的四个像素点。
    在这里插入图片描述
    使用数学归纳法,就可以得到每个U、V点对应整幅图像左上角位置的表达式。设右侧的编号为i,则左侧的每个分组左上角的那个编号就是2*(i%(width/2))+i/(width/2)*2*width。如,i=10,可得到整幅图像左上角位置的索引为36.
void yuv2rgb(unsigned char* YUV_Buffer, unsigned char* RGB_Buffer_out, int size,int width) {
	int uoffset = size;
	int voffset = 1.25 * size;
	
	unsigned char* U_dataExpand = new unsigned char[size];
	unsigned char* V_dataExpand = new unsigned char[size];

	/*下面循环体完成U和V的扩张(1->4)*/
	for (int i = 0; i < size/4; i++) {
		unsigned char temp = *(YUV_Buffer + i + uoffset);
		int offset = 2*(i%(width/2))+i/(width/2)*2*width;
		*(U_dataExpand + offset)= temp;
		*(U_dataExpand + offset+1) = temp;
		*(U_dataExpand + offset+width) = temp;
		*(U_dataExpand + offset + 1+width) = temp;

		temp= *(YUV_Buffer + i + voffset);
		*(V_dataExpand + offset) = temp;
		*(V_dataExpand + offset + 1) = temp;
		*(V_dataExpand + offset + width) = temp;
		*(V_dataExpand + offset + 1 + width) = temp;
	}

	/*下面循环体完成RGB空间对应值的计算*/
	for (int i = 0, j = 0; i < size; i++, j += 3) {
		unsigned char Y = *(YUV_Buffer + i);
		unsigned char U = *(U_dataExpand + i);
		unsigned char V = *(V_dataExpand + i);
		unsigned char B = (298 * Y + 519 * U - 71200) >> 8;
		if (B > 255) B = 255;
		if(B < 0) B = 0;
		unsigned char G = (298 * Y - 101 * U - 211 * V + 35168) >> 8;
		if (G > 255) G = 255;
		if (G < 0) G = 0;
		unsigned char R = (298 * Y + 411 * V - 57376) >> 8;
		if (R > 255) R = 255;
		if (R < 0) R = 0;
		*(RGB_Buffer_out + j) = unsigned char(B);
		*(RGB_Buffer_out + 1 + j) = unsigned char(G);
		*(RGB_Buffer_out + 2 + j) = unsigned char(R);

	}
	
	delete[] U_dataExpand;
	delete[] V_dataExpand;
	
}

4.2.2 数值范围超过类型允许范围导致溢出的解决

至此,成功生成.RGB图片。使用YUVViewer得到的图像却是这样的:

源.RGB生成的.YUV
在这里插入图片描述在这里插入图片描述
  • 观察图像,得到以下结论:
  1. 生成图像由于显示使用YUVViewer打开,模式选择的是gbmp24,故图像与原图像是上下颠倒的,这是由于.bmp文件的存储方式(倒序存储)导致的,对本实验无任何实质影响。
  2. 图像出现了严重的失真,但可以确定的是各像素空间位置没错误。为什么可以确定后者?一是经过了一个验证:将U、V全部置为128,生成的图像为正确的灰度图;二是仔细观看,发现红色噪点的边缘轮廓仍然是处在图像的景象物体轮廓上的。也就是说,只有值的失真。
  • 那为什么会出现值的失真呢?明明已经在程序中做了范围越界的判断。经过我的仔细研究和调试,我发现这是由于计算出的RGB值会超过unsigned char允许的范围(0-255),然后赋值时相当于int赋值给char,越界后在位运算时直接循环,导致了值的异常。

解决:修改RGB变量类型为int,经过越界的判断后再强制类型转换为unsigned char赋值给最终的输出。

/*下面循环体完成RGB空间对应值的计算*/
	for (int i = 0, j = 0; i < size; i++, j += 3) {
		unsigned char Y = *(YUV_Buffer + i);
		unsigned char U = *(U_dataExpand + i);
		unsigned char V = *(V_dataExpand + i);
		int B = (298 * Y + 519 * U - 71200) >> 8;
		if (B > 255) B = 255;
		if(B < 0) B = 0;
		int G = (298 * Y - 101 * U - 211 * V + 35168) >> 8;
		if (G > 255) G = 255;
		if (G < 0) G = 0;
		int R = (298 * Y + 411 * V - 57376) >> 8;
		if (R > 255) R = 255;
		if (R < 0) R = 0;
		*(RGB_Buffer_out + j) = unsigned char(B);
		*(RGB_Buffer_out + 1 + j) = unsigned char(G);
		*(RGB_Buffer_out + 2 + j) = unsigned char(R);

	}

再次观察图像,问题已解决:
在这里插入图片描述

4.2.3 算法优化——使用查找表

使用查找表,即将所有分量转换后对应的值存储在一个数组里,使用时可以直接取用,是一种以空间为代价换时间效率的策略。仅需修改yuv2rgb.cpp文件:

static int YUV2RGB298[256], YUV2RGB411[256];
static int YUV2RGB101[256], YUV2RGB211[256];
static int YUV2RGB519[256];

void initLookupTable() {
	int i;

	for (i = 0; i < 256; i++) YUV2RGB298[i] = 298 * i;
	for (i = 0; i < 256; i++) YUV2RGB411[i] = 411 * i;
	for (i = 0; i < 256; i++) YUV2RGB101[i] = 101 * i;
	for (i = 0; i < 256; i++) YUV2RGB211[i] = 211 * i;
	for (i = 0; i < 256; i++) YUV2RGB519[i] = 519 * i;

}

void yuv2rgb(unsigned char* YUV_Buffer, unsigned char* RGB_Buffer_out, int size,int width) {
	initLookupTable();
	int uoffset = size;
	int voffset = 1.25 * size;
	
	unsigned char* U_dataExpand = new unsigned char[size];
	unsigned char* V_dataExpand = new unsigned char[size];

	/*下面循环体完成U和V的扩张(1->4)*/
	for (int i = 0; i < size/4; i++) {
		unsigned char temp = *(YUV_Buffer + i + uoffset);
		int offset = 2*(i%(width/2))+i/(width/2)*2*width;
		*(U_dataExpand + offset)= temp;
		*(U_dataExpand + offset+1) = temp;
		*(U_dataExpand + offset+width) = temp;
		*(U_dataExpand + offset + 1+width) = temp;

		temp= *(YUV_Buffer + i + voffset);
		*(V_dataExpand + offset) = temp;
		*(V_dataExpand + offset + 1) = temp;
		*(V_dataExpand + offset + width) = temp;
		*(V_dataExpand + offset + 1 + width) = temp;
	}

	/*下面循环体完成RGB空间对应值的计算*/
	for (int i = 0, j = 0; i < size; i++, j += 3) {
		unsigned char Y = *(YUV_Buffer + i);
		unsigned char U = *(U_dataExpand + i);
		unsigned char V = *(V_dataExpand + i);
		int B = (YUV2RGB298[Y] + YUV2RGB519[U] - 71200) >> 8;
		if (B > 255) B = 255;
		if(B < 0) B = 0;
		int G = (YUV2RGB298[Y] - YUV2RGB101[U] - YUV2RGB211[V] + 35168) >> 8;
		if (G > 255) G = 255;
		if (G < 0) G = 0;
		int R = (YUV2RGB298[Y] + YUV2RGB411[V] - 57376) >> 8;
		if (R > 255) R = 255;
		if (R < 0) R = 0;
		*(RGB_Buffer_out + j) = unsigned char(B);
		*(RGB_Buffer_out + 1 + j) = unsigned char(G);
		*(RGB_Buffer_out + 2 + j) = unsigned char(R);

	}
	
	delete[] U_dataExpand;
	delete[] V_dataExpand;
	
}

结果与上图完全一致,同时执行时间更短。

5 实验结果

原图down.rgb转换出的.yuv原图down.yuv转换出的.rgb
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

6 误差分析

6.1 定性分析

  • 在RGB图像转换为4:2:0的YUV图像时,是无损的;
  • 但是在4:2:0的YUV图像转换为RGB图像时,进行了插值计算,U、V分量被插值为原来的4倍,这是误差的主要来源。
  • 色彩空间转换的过程中,出现了小数运算和移位运算,势必带来计算误差。
  • YUV转换为RGB时,存在数据的溢出,被归置到最高/最低电平。其本质是由于RGB和YUV空间并不是完全重合的空间。
    在这里插入图片描述
  • 示例代码中直接使用了公式(2)进行计算,对于溢出到HEADROOM或更外的地方直接写了if判断语句进行了一刀切,归置到最高电平。而使用公式(6)则是相当于将原来模拟的范围再次进行压缩,线性均分到了16-235(240)的范围,因此,极值会更白/更黑。但是中间的量化误差可能会比前一种方法更大。

6.2 定量分析

导出转换后的图像的各分量数值为.csv到python进行分析。分析方法:将源RGB与yuv转换得来的RGB图像作差,按照两像素之间的误差值大小为横坐标作直方图,统计误差为该值的像素个数。其中图像横轴只画到最大的误差值。

import numpy as np
import matplotlib.pyplot as plt
from pandas import read_csv

ori=open('data_ori.csv')
out=open('data.csv')
ori=read_csv(ori)
out=read_csv(out)
plt.rcParams['font.sans-serif'] = ['Songti SC'] # 指定默认字体

i=0;
R= abs(ori['R']-out['R']);
maxR=max(R)
G= abs(ori['G']-out['G']);
maxG=max(G)
B= abs(ori['B']-out['B']);
maxB=max(B)
# R\G分量作图同下
plt.hist(x = B, # 指定绘图数据
           bins = maxB, # 指定直方图中条块的个数
          color = 'blue', # 指定直方图的填充色
         edgecolor = 'white' # 指定直方图的边框色
         )

plt.xlabel('与原图的值的误差')
plt.ylabel('误差为该值的像素个数')
plt.title('B分量的误差分布')
plt.show()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以发现,RGB最大误差值分别为37、28、24,相对来说都较小;若又恰好处于非线性感知的平缓区域,那对视觉上的影响就更小了。难怪肉眼几乎看不出区别。
在这里插入图片描述

7 巨人的肩膀(大腿)

  1. 《现代电视原理》课件
  2. 《数字电视广播技术与应用》课本
  3. 课上给的参考程序
  4. RGB与YUV色彩空间的相互转换
  5. 刘宗鑫的实验报告
  • 4
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值