由于目录过长不便查阅,故不使用TOC生成目录,可以使用CSDN侧边导航栏导航,在正文左侧,随时都可点击!
1 实验目的
- 编写 RGB 转化为YUV 程序,注意要使用部分查找表的初始化和调用。将得到的 RGB 文件转换为 YUV 文件,用
YUV Viewer
播放器观看,验证是否正确。 - 将 YUV 转换为 RGB 的程序。将给定的实验数据用该程序转换为 RGB 文件。 并与原 RGB 文件进行比较, 如果有误差,分析误差来自何处。
- 总结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
R−Y、B−Y则可以直接根据亮度方程算出:
{
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}
⎩⎪⎨⎪⎧YR−YB−Y===0.2990R+0.5870G+0.1140B0.7010R−0.5870G−0.1140B−0.2990R−0.5870G+0.8860B(1)
2.1.2 色差信号压缩后得到模拟分量 P b 、 P r Pb、Pr Pb、Pr
接着,在
R
−
Y
、
B
−
Y
R-Y、B-Y
R−Y、B−Y信号的基础上,为了使色差信号的动态范围控制在±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(B−Y)=−0.1684R−0.3316G+0.5000B0.713(R−Y)=+0.5000R−0.4187G−0.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(B−Y)0.877(R−Y)(3)
而Pr、Pb虽然也是模拟分量,但是是为了色差信号的动态范围控制在±0.5之内,总动态范围与亮度信号动态范围保持一致(0-1V),才进行的归一化处理,两者目的不同。
由于本文之后不涉及模拟电视制式,故U和V不指代NTSC制(模拟电视制式)中的模拟分量,而是指后文提到的Cb、Cr。
2.1.3 量化为数字信号及码电平的分配
随后,我们对 Y 、 P r Y、Pr Y、Pr和 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{maxE−minEmaxQ−minQ×数字电平公式+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}
⎩⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎧Y′CbCr===int{0.5−(−0.5)235−16×Y+16}int{1−0240−16×Pb+128}int{1−0240−16×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)×2n−8+16}int{(−38×R−74×G+112×B)×2n−8+128}int{(112×R−94×G−18×B)×2n−8+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⎦⎤=2561⎣⎡66−3811212974−9425112−18⎦⎤⎣⎡RGB⎦⎤+⎣⎡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×(Y−C)A−1AR=256×A−1(Y−C)R=256×A−1(Y−C)(9)
由于
A
−
1
\boldsymbol A^{-1}
A−1数量级小,为避免误差,将
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⎦⎤=2561A1−1⎣⎡Y−16U−128V−128⎦⎤(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函数的命令行参数argc
和argv
.
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 |
---|---|
- 观察图像,得到以下结论:
- 生成图像由于显示使用YUVViewer打开,模式选择的是gbmp24,故图像与原图像是上下颠倒的,这是由于.bmp文件的存储方式(倒序存储)导致的,对本实验无任何实质影响。
- 图像出现了严重的失真,但可以确定的是各像素空间位置没错误。为什么可以确定后者?一是经过了一个验证:将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 巨人的肩膀(大腿)
- 《现代电视原理》课件
- 《数字电视广播技术与应用》课本
- 课上给的参考程序
- RGB与YUV色彩空间的相互转换
- 刘宗鑫的实验报告