彩色空间转换实验实验报告

实验2.1C语言实现YUV文件与RGB文件互换格式 文章目录前言实验内容转换公式实验思路实验步骤命令参数的设置实验结果结果分析总结一级目录二级目录三级目录 前言 如图,本次实验的要求和所用rgb文件和yuv文件(4:2:0)的命名,格式,大小如上图所示。 4:2:0格式是指色差信号U,V的取样频率为亮度信号取样频率的四分之一,在水平方 向和垂直方向上的取样点数均为Y的一半。 课上老师已讲解如何从RGB转到YUV格式,并给出了程序,阅读理解后自己写出从YUV转到RGB格式的程序。 实验内容 转换公式 本次实验中Y,U,V与R,G,B的值域都是0~255 实 阅读详情

彩色空间转换实验

基本原理

RGB与YUV色彩空间的基础知识

RGB
RGB计色系统

RGB计色系统的创立是为了准确地对颜色进行表述和计算。1931年CIE对三基色做了统一规定,如下表:

标准色光波长
红基色光700nm
绿基色光546.1nm
蓝基色光435.8nm

至于为什么选择它们,则是因为它们的获取方法简单,色度稳定准确,而且可以混配出来的颜色较多。

为了计算方便,还要规定基色的单位。

物理计色系统规定:各以1各单位的三种基色光混合后,恰能产出等能白光。经过实验,选取了如下基色单位:

  • 700nm,光通量1光瓦的红光作为1个红基色单位。
  • 546.1nm,光通量4.5907光瓦的绿光为1个绿基色单位。
  • 435.9nm,光通量0.0601光瓦的蓝光为1个蓝基色单位。

至此,任何一个色光均可以表示为:

F = R ( R ) + G ( G ) + B ( B ) F=R(R)+G(G)+B(B) F=R(R)+G(G)+B(B)

其中 ( R ) , ( G ) , ( B ) (R),(G),(B) (R),(G),(B)即为上面提到的基色单位。

XYZ计色系统

在XYZ计色系统中,三个基色单位为 ( X ) , ( Y ) , ( Z ) (X),(Y),(Z) (X),(Y),(Z),任何一个颜色均可以表示为:

F = X ( x ) + Y ( Y ) + Z ( Z ) F=X(x)+Y(Y)+Z(Z) F=X(x)+Y(Y)+Z(Z)

XYZ系统的建立是为了克服RGB计色系统的缺点,它有以下特点:

  • 用他们配出实际颜色时,XYZ均为正值。
  • 合成光的亮度仅由Y决定,并规定1(Y)的光通量为1光瓦。
  • 当 X = Y = Z X=Y=Z X=Y=Z,且大于零时,代表等能白光即E白。

根据特点,列出方程可以计算得到:

( Y ) = 0.4185 ( R ) − 0.0192 ( G ) + 0.0009 ( B ) (Y) = 0.4185(R) - 0.0192(G) + 0.0009(B) (Y)=0.4185(R)−0.0192(G)+0.0009(B)

(X),(Z)同理。通过这组方程联立可以得到系数X,Y,Z与R,G,B的关系。

显像三基色计色系统

以上两个计色系统均是用来进行理论分析的,实际上我们指通常所指的R,G,B并不是RGB计色系统中的三基色,而是取决于的荧光粉,称为电视三基色,分别用 ( R e ) , ( G e ) , ( G b ) (R_e),(G_e),(G_b) (Re​),(Ge​),(Gb​)表示,并且NTSC制中有:

1 ( R e ) + 1 ( G e ) + 1 ( B e ) = 1 C 白 1(R_e)+1(G_e)+1(B_e)=1C白 1(Re​)+1(Ge​)+1(Be​)=1C白

通过计算可以得到对应的亮度公式:

Y = 0.2990 R + 0.5870 G + 0.1140 B Y=0.2990R+0.5870G+0.1140B Y=0.2990R+0.5870G+0.1140B

这就是我们最常用的亮度公式。

YUV

YUV中的Y其实是来自于XYZ标准记色系统中的Y。

YUV与RGB的转换
模拟信号

Y = 0.2990 R + 0.5870 G + 0.1140 B Y=0.2990R+0.5870G+0.1140B Y=0.2990R+0.5870G+0.1140B

U = B − Y = − 0.2990 R − 0.5870 G + 0.8860 B U=B-Y=-0.2990R-0.5870G+0.8860B U=B−Y=−0.2990R−0.5870G+0.8860B

V = R − Y = 0.7010 R − 0.5870 G − 0.1140 B V=R-Y=0.7010R-0.5870G-0.1140B V=R−Y=0.7010R−0.5870G−0.1140B

数字信号

为了便于处理,模拟信号变为数字信号时需要进行归一化处理,使得色差信号的动态范围控制在-0.5-0.5之间。

归一化之后有:

U ′ = 0.564 ( B − Y ) = − 0.1684 R − 0.3316 G + 0.5 B U'=0.564(B-Y)=-0.1684R-0.3316G+0.5B U′=0.564(B−Y)=−0.1684R−0.3316G+0.5B
V ′ = 0.713 ( V − Y ) = 0.5 R − 0.4187 G − 0.0813 B V'=0.713(V-Y)=0.5R-0.4187G-0.0813B V′=0.713(V−Y)=0.5R−0.4187G−0.0813B

亮度信号量化后的电平分配

在这里插入图片描述
如图所示,在8bit均匀量化中,亮度信号占220个量化级,峰值电平对应235,消隐电平对应16。为了防止信号变动造成过载,在256级上端留20级,下端留16级作为信号超越动态范围的保护带。 其中0与255为保护电平,不允许出现在视频数据流中。

色差信号量化后的电平分配

量化

以8bit为例,色差信号经过归一化处理后,动态范围为-0.5-0.5,让色差零电平对应码电平128,色差信号总共占225个量化级。在256级上端留15级,下端留16级作为信号超越动态范围的保护带。

表达式

基于上述分析,可以得到表达式:

D Y = I N T ( 219 Y + 16 ) × 2 n − 8 D_Y=INT(219Y+16)\times{2^{n-8}} DY​=INT(219Y+16)×2n−8

D U = I N T ( 224 U ′ + 128 ) × 2 n − 8 D_U=INT(224U'+128)\times{2^{n-8}} DU​=INT(224U′+128)×2n−8

D V = I N T ( 224 V ′ + 128 ) × 2 n − 8 D_V=INT(224V'+128)\times{2^{n-8}} DV​=INT(224V′+128)×2n−8

其中 D Y , D U , D V D_Y,D_U,D_V DY​,DU​,DV​即为计算机(数字信号)中的数值, I N T ( ) INT() INT()为向下取整, n n n为量化比特数。

上述过程中默认r、g、b的值为0-1之间的,而计算机中的数据则是在0-255范围内,所以修需要将上述公式中r、g、b的从0-255映射到0-1。

即:

D Y = I N T ( 219 Y 225 + 16 ) × 2 n − 8 D_Y=INT(\frac{219Y}{225} + 16)\times{2^{n-8}} DY​=INT(225219Y​+16)×2n−8

D U = I N T ( 224 U ′ 225 + 128 ) × 2 n − 8 D_U=INT(\frac{224U'}{225}+128)\times{2^{n-8}} DU​=INT(225224U′​+128)×2n−8

D V = I N T ( 224 V 225 ′ + 128 ) × 2 n − 8 D_V=INT(\frac{224V}{225}'+128)\times{2^{n-8}} DV​=INT(225224V​′+128)×2n−8

以8bit为例:

D Y = I N T ( 219 Y 225 ) + 16 D_Y=INT(\frac{219Y}{225})+16 DY​=INT(225219Y​)+16

D U = I N T ( 224 U ′ 225 ) + 128 D_U=INT(\frac{224U'}{225})+128 DU​=INT(225224U′​)+128

D V = I N T ( 224 V 225 ) + 128 D_V=INT(\frac{224V}{225})+128 DV​=INT(225224V​)+128

那么很容易就可以得到逆公式:

Y = D Y × 2 8 − n − 16 219 Y=\frac{D_Y\times{2^{8-n}}-16}{219} Y=219DY​×28−n−16​

U ’ = D U × 2 8 − n − 128 224 U’=\frac{D_U\times{2^{8-n}}-128}{224} U’=224DU​×28−n−128​

V ′ = D V × 2 8 − n − 128 224 V'=\frac{D_V\times{2^{8-n}}-128}{224} V′=224DV​×28−n−128​

之后可以利用:

B = U ′ 0.564 + Y B=\frac{U'}{0.564}+Y B=0.564U′​+Y

R = V ′ 0.713 + Y R=\frac{V'}{0.713}+Y R=0.713V′​+Y

来进行还原。

最终表达式

以8bit量化为例最终的表达式如下:
[ D Y D U D V ] = [ 0.2568 0.5041 0.0979 − 0.1448 − 0.2913 0.4392 0.4392 0.3677 0.0714 ] [ R G B ] + [ 16 128 128 ] \left[\begin{matrix} D_Y\\ D_U\\ D_V \end{matrix} \right]=\left[\begin{matrix} 0.2568& 0.5041 &0.0979\\ -0.1448&-0.2913&0.4392 \\ 0.4392&0.3677&0.0714 \\ \end{matrix} \right]\left[\begin{matrix} R\\ G\\ B \end{matrix} \right]+ \left[\begin{matrix} 16\\ 128\\ 128 \end{matrix} \right] ⎣⎡​DY​DU​DV​​⎦⎤​=⎣⎡​0.2568−0.14480.4392​0.5041−0.29130.3677​0.09790.43920.0714​⎦⎤​⎣⎡​RGB​⎦⎤​+⎣⎡​16128128​⎦⎤​
计算所得的结果后向下取整即为数字化的Y、U、V。

直接利用逆来求反变换也很容易得出:
[ R G B ] = [ − 2.8958 0.0001 3.9701 3.2285 − 0.3918 − 2.0168 1.1866 2.0171 − 0.02879 ] [ D Y − 16 D U − 128 D V − 128 ] \left[\begin{matrix} R\\ G\\ B \end{matrix} \right]=\left[\begin{matrix} -2.8958& 0.0001 & 3.9701\\ 3.2285 & -0.3918 & -2.0168\\ 1.1866& 2.0171& -0.02879 \end{matrix} \right]\left[\begin{matrix} D_Y-16\\ D_U-128\\ {D_V-128} \end{matrix} \right] ⎣⎡​RGB​⎦⎤​=⎣⎡​−2.89583.22851.1866​0.0001−0.39182.0171​3.9701−2.0168−0.02879​⎦⎤​⎣⎡​DY​−16DU​−128DV​−128​⎦⎤​
同样,计算完成后向下取整即可。

然而实际计算中却发现这种计算方法由于rgb转换成yuv时经过了量化,而且各项的系数较大,小数点级别的误差就会造成与真实值较大的偏离,误差很大,于是采用上面描述的方法,先转换成 Y , U ′ , V ′ Y,U',V' Y,U′,V′再利用 Y , U ′ , V ′ Y,U',V' Y,U′,V′与 R , G , B R,G,B R,G,B的关系联立方程求解,最后得到的公式如下:
[ R G B ] = [ 1.1644 0.0000 1.5960 1.1644 − 0.3917 − 0.8127 1.1644 2.0170 − 0.0014 ] [ D Y − 16 D U − 128 D V − 128 ] \left[\begin{matrix} R\\ G\\ B \end{matrix} \right]=\left[\begin{matrix} 1.1644 &0.0000 &1.5960\\ 1.1644 &-0.3917 &-0.8127\\ 1.1644 &2.0170& -0.0014 \end{matrix} \right]\left[\begin{matrix} D_Y-16\\ D_U-128\\ D_V-128 \end{matrix} \right] ⎣⎡​RGB​⎦⎤​=⎣⎡​1.16441.16441.1644​0.0000−0.39172.0170​1.5960−0.8127−0.0014​⎦⎤​⎣⎡​DY​−16DU​−128DV​−128​⎦⎤​

数据类型的分析

YUV

取样结构

这组图图每一行即为一行像素点,以Y为基准,蓝色和红色表示 C b / U C_b/U Cb​/U和 C r / V C_r/V Cr​/V

取样结构图示
4:4:44:4:4
4:2:24:2:2
4:1:14:1:1
4:2:0格式14:2:0-1
4:2:0格式24:2:0-2
存储结构

一下均指一帧的情况,实际上是一帧内容按照相应格式存储完成后,再存储下一帧。

YUVY(4:2:2)

Y 0 C b 0 Y 1 C r 0 Y 2 C b 1 Y 3 C r 2 ⋯ Y_0C_{b_0}Y_1C_{r_0}Y_2C_{b_1}Y_3C_{r_2}\cdots Y0​Cb0​​Y1​Cr0​​Y2​Cb1​​Y3​Cr2​​⋯
即 Y Y Y后跟 C b C_b Cb​与 Y Y Y后跟 C r C_r Cr​交替出现。

UYVY(4:2:2)

C b 0 Y 0 C r 0 Y 1 C b 1 Y 2 C r 2 Y 3 ⋯ C_{b_0}Y_0C_{r_0}Y_1C_{b_1}Y_2C_{r_2}Y_3\cdots Cb0​​Y0​Cr0​​Y1​Cb1​​Y2​Cr2​​Y3​⋯。
即 C b C_b Cb​后跟 Y Y Y与 C r C_r Cr​后跟 Y Y Y交替出现。

YUV422P(4:2:2)

先存储完所有Y,再存储完所有 C b C_b Cb​,最后再存储所有 C r C_r Cr​。

YV12,YU12(4:2:0)

先存储完所有Y,再存储完所有 C b C_b Cb​,最后再存储所有 C r C_r Cr​。

NV12,NV21(4:2:0)

先存储完所有Y,之后 C b x C_{b_x} Cbx​​ C b x + 1 C_{b_{x+1}} Cbx+1​​ C r x C_{r_x} Crx​​ C r x + 1 C_{r_{x+1}} Crx+1​​的结构交替出现,即两字节 C b C_b Cb​之后再存储两字节 C r C_r Cr​。

本次为YV12格式。

RGB/BMP

与BMP文件相比,本次所给的rgb文件只缺少了文件头和信息头。为了方便观察结果,本文先打算讲rgb文件添加文件头变成bmp文件。

位图文件头(BITMAPFILEHEADER)
名称占用空间内容
bfType2B标识,就是"BM"
bfSize4B整个BMP文件的大小
bfReserbed1/24B保留字
bfOffBits4B偏移数,即位图文件头+位图信息+调色板的大小

PS:数据是按照字节倒着存储的,如在文件中为12 34 56 78,那么实际数据则是78 56 34 12。

位图信息头
名称占用空间内容
biSize4B位图信息头的大小,为40
biWidth4B位图宽度,单位为像素
biHeight4B位图高度,单位是像素
biPlanes2Bu固定值为1
biBitCount2B每个像素的位数,1-黑白图,4为16色,8为256色,24为真彩色
biCompression4B压缩方式,0为不压缩
biSizeImage4B位图全部像素占用的字节数,BI_RGB时可以设置为0
biXPelsPerMeter4B水平分辨率(像素/米)
biYpelsPerMeter4B垂直分辨率(像素/米)
biClrUsed4B位图使用的颜色数,如果为0则颜色数为2的biBitCount次方
biClrlmportant4B重要的颜色数,0表示所有颜色都重要

为了简便,自己实现的时候并未采用这么冗长的名称写法。

颜色表

即本次所给的文件部分,按照B、G、R顺序排列,每个像素占用1个字节,用来表示颜色。

相关编程知识

文件

C语言是用FILE指针来进行文件操作。

FILE指针

FILE指针定义如下:

typedef struct 
{ 
    short level;  /*缓冲区满空程度*/ 
    unsigned flags;  /*文件状态标志*/ 
    char fd;     /*文件描述符*/ 
    unsigned char hold;   /*无缓冲则不读取字符*/ 
    short bsize;  /*缓冲区大小*/ 
    unsigned char *buffer; /*数据缓冲区*/ 
    unsigned char *curp;   /*当前位置指针*/ 
    unsigned istemp; /*临时文件指示器*/ 
    short token;  /*用于有效性检查*/ 
} FILE;
文件的打开与关闭

fopen

定义:

FILE *fopen(char *filename, char *mode);

  • filename为文件路径
  • mode为打开模式

打开模式

打开模式描述
r只读,打开已有文件,不能写
w只写,创建或打开,覆盖已有文件
a追加,创建或打开,在已有文件末尾追加
r+读写,打开已有文件
w+读写,创建或打开,覆盖已有文件
a+读写,创建或打开,在已有文件末尾追加
t按文本方式打开 (缺省)
b按二进制方式打开

fclose()

定义:

int fclose(FILE *fp);

若成功返回0,否则返回EOF(-1)

常用函数
  • 单个字符或者字
    • fputc,fgetc,putc,getc,putw,getw
  • 格式化输入输出
    • fprintf,fscanf
  • 二进制文件读写
    • fread,fwrite

其使用方法和c++中流的名称类似的函数一致。

因为C中文件读写较为繁琐,本次还是采用流式文件输入输出。

流

详见C++文件读写

问题的分析与实现

流程分析

转化为BMP
  • 由于所给rgb文件没有文件头和信息头,无法用照片浏览器打开,所以首先应该添加文件头。
  • 根据rgb文件实际属性向文件头填充数据。
  • 由于bmp文件rgb是从下向上存储,故还需要对rgb数据进行反向。
  • 将文件头和信息头与反向后的rgb数据合并
  • 输出数据
RGB转YUV
  • 根据文件保存的格式,从rgb文件中读取r,g,b的数值
  • 计算实际y,u,v分量的大小
  • 按照yuv文件的格式输出数据
YUV转RGB
  • 根据文件存储的格式从yuv文件中读取y,u,v文件的值
  • 计算实际r,g,b分量的大小
  • 按照rgb文件的格式输出数据

代码实现

header.h
  • 完成一些定义的头文件,包含
    • 函数定义
    • bmp文件信息头和文件头的定义
#pragma once

#include <iostream>
#include <fstream>
#include <cstdio>
using namespace std;
int char2int(char *s);
void rgb2bmp(unsigned char* inBuffer, int width, int height, string &outPath);
void rgb2yuv(unsigned char* inBuffer, int width, int height, string &outPath);
void yuv2rgb(unsigned char* inBuffer, int width, int height, string &outPath);
void Compare(string &pathA, string &pathB);
#pragma pack(1)
struct bmpHeader
{
	short type = 0;
	unsigned int size = 0;
	unsigned int reserved = 0;
	unsigned int offBits = 0;
};
struct bmpInfo
{
	unsigned int size = 40;
	unsigned int width = 0;
	unsigned int height = 0;
	short planes = 1;
	short count = 24;
	unsigned int compressed = 0;
	unsigned int imageSize = 0;
	unsigned int xPels = 0;
	unsigned int yPels = 0;
	unsigned int colorNum = 0;
	unsigned int importantColor = 0;
};
functions.cpp
  • 实现字符串转换成int
  • 实现两个rgb文件的比较
#include "header.h"

int char2int(char *s)
{
	int ans = 0, pos = 0;
	while (s[pos])
	{
		ans = ans * 10 + (s[pos++] - '0');
	}
	return ans;
}
void Compare(string& pathA, string& pathB)
{
	ifstream inA(pathA, ios::binary), inB(pathB, ios::binary);
	if (!inA || !inB) { cout << pathA << " or " << pathB << " open failed." << endl; exit(1); }
	inA.seekg(0,ios::end);
	int size = inA.tellg();
	inA.seekg(0, ios::beg);

	unsigned char* a = new unsigned char[size], * b = new unsigned char[size];
	inA.read((char *)a, size); inB.read((char* )b, size);
	int num = 0, variance = 0;
	for (int i = 0; i < size; ++i)
	{
		if (a[i] != b[i])
		{
			num++;
			variance += (a[i] - b[i]) * (a[i] - b[i]);
		}
	}
	cout << "variance: " << variance << ", the number of different pixels: " << num << endl;
	inA.close(); inB.close();
	for (auto i : { &a, &b })
		if (*i != nullptr)
			delete[] *i;
}

RGB2BMP.cpp
  • 将纯rgb数据转换为bmp图像的函数。
#include "header.h"
void rgb2bmp(unsigned char* buffer, int width, int height, string &outPath)
{
	ofstream out(outPath, ios::binary);
	if (!out) { cout << outPath << " open failed." << endl; exit(0); }
	bmpHeader header; bmpInfo info;
	unsigned int size;
	header.type = 0x4d42;
	size = header.size = width * height * 3;
	header.offBits = 54;
	info.width = width;
	info.height = height;
	out.write((char*)&header, sizeof(bmpHeader));
	out.write((char*)&info, sizeof(info));
	unsigned char* outBuffer = new unsigned char[size];
	for (int h = 0; h < height; ++h)
	{
		int W = width * 3;
		for (int w = 0; w < W; ++w)
		{
			outBuffer[(height - 1 - h) * W + w] = buffer[h * W + w];
		}
	}
	out.write((char*)outBuffer, size);
	delete[] outBuffer;
	out.close();
}
RGB2YUV.cpp
  • 将rgb数据转换为yuv数据的函数。
#include "header.h"

bool haveRGBTable = 0;
const int maxn = 256;
float rgb02568[maxn],rgb05401[maxn],rgb00979[maxn];
float rgb01448[maxn], rgb02913[maxn], rgb04392[maxn];
float rgb03677[maxn], rgb00714[maxn];
void getRGBTable()
{
	for (int i = 0; i < 256; i++)
	{
		rgb00714[i] = 0.0714 * i;
		rgb00979[i] = 0.0979 * i;
		rgb01448[i] = 0.1448 * i;
		rgb02568[i] = 0.2568 * i;
		rgb02913[i] = 0.2913 * i;
		rgb03677[i] = 0.3677 * i;
		rgb04392[i] = 0.4392 * i;
		rgb05401[i] = 0.5401 * i;
	}

}
void rgb2yuv(unsigned char* inBuffer, int width, int height, string &outPath)
{
	if (!haveRGBTable)
	{
		haveRGBTable = 1;
		getRGBTable();
	}
	ofstream out(outPath, ios::binary);
	if (!out.is_open()) { cout << outPath << " open failed." << endl; exit(0); }
	int size = width * height;
	unsigned char* Y, * U, * V;
	Y = new unsigned char[size];
	for (auto i : { &U, &V }) *i = new unsigned char[size/4];

	int pos = 0;
	for (int i = 0; i < size; ++i)
	{
		unsigned char r, g, b, j = 0;
		for (auto c : { &b, &g, &r }) *c = inBuffer[i * 3 + (j++)];

		Y[i] = int(rgb02568[r] + rgb05401[g] + rgb00979[b]) + 16;
		int h = i / width, w = i % width;
		if ((h & 1) || (w & 1)) continue;
		U[pos] = int(-rgb01448[r] - rgb02913[g] + rgb04392[b]) + 128;
		V[pos ++] = int(rgb04392[r] - rgb03677[g] - rgb00714[b]) + 128;
	}
	out.write((char*)Y, size);
	for( auto i : {&U, &V}) out.write((char*)(*i), size / 4);
	out.close();
	for (auto i : { &Y, &U, &V }) if ((*i) != nullptr) delete[] * i;
	return;
}
YUV2RGB.cpp
  • 实现将yuv数据转化为rgb数据的函数。
#include "header.h"
unsigned char organized(int n)
{
	if (n > 255) n = 255;
	if (n < 0) n = 0;
	return (unsigned char)n;
}
const int maxn = 256;
float yuv11644[maxn], yuv15960[maxn];
float yuv03917[maxn], yuv08127[maxn];
float yuv20170[maxn], yuv00014[maxn];
bool haveYUVTable = 0;
void getYUVTable()
{
	for (int i = 0; i < 256; ++i)
	{
		yuv00014[i] = 0.0014 * (i - 128);
		yuv03917[i] = 0.3917 * (i - 128);
		yuv08127[i] = 0.8127 * (i - 128);
		yuv11644[i] = 1.1644 * (i - 16);
		yuv15960[i] = 1.5960 * (i - 128);
		yuv20170[i] = 2.0170 * (i - 128);
	}
}
void yuv2rgb(unsigned char* inBuffer, int width, int height, string &outPath)
{
	ofstream out(outPath, ios::binary);
	if (!out.is_open()) { cout << outPath << " open failed." << endl; exit(1); }

	if (!haveYUVTable)
	{
		haveYUVTable = 1;
		getYUVTable();
	}
	unsigned char* Y, * U, * V;
	int size = width * height;
	Y = new unsigned char[size];
	for (auto i : { &U, &V }) *i = new unsigned char[size / 4];
	int pos = 0;
	for (int i = 0; i < size; ++i) Y[i] = inBuffer[pos++];
	for (int i = 0; i < size / 4; ++i) U[i] = inBuffer[pos++];
	for (int i = 0; i < size / 4; ++i) V[i] = inBuffer[pos++];
	
	for (int i = 0; i < size; ++i)
	{
		int y, u, v, r, g, b;
		int h = i / width, w = i % width, pos;
		if (w & 1) w--; w >>= 1;
		if (h & 1) h--; h >>= 1;
		pos = h * width / 2 + w;
		y = Y[i]; u = U[pos]; v = V[pos];
		r = yuv11644[y] + yuv15960[v];
		g = yuv11644[y] - yuv03917[u] - yuv08127[v];
		b = yuv11644[y] + yuv20170[u]- yuv00014[v];
		out << organized(b) << organized(g) << organized(r);
	}
	for (auto i : { &Y, &U, &V })
	{
		if (*i != nullptr) delete[] * i;
	}
	return;
}
main.cpp
  • 调用上述函数并做验证。
#include "header.h"
int main()
{
    string inRGB = (string)__argv[1];
    string outRGB = "", outYUV = "", outBmpOrigin = "", outBmpProssed = "";
    int height = char2int(__argv[2]), width = char2int(__argv[3]);
    if (__argc == 4)
    {
        string tmp;
        for (auto i : inRGB)
        {
            if (i == '.') break;
            tmp.push_back(i);
        }
        outRGB = tmp + "p.rgb";
        outYUV = tmp + ".yuv";
        outBmpOrigin = tmp + "o.bmp";
        outBmpProssed = tmp + "p.bmp";
    }
    else
    {
        int pos = 3;
        for (auto s : { &outRGB, &outYUV, &outBmpOrigin, &outBmpProssed })
            *s = (string)__argv[++pos];
    }

    int imageSize = height * width * 3;
    unsigned char* rgbBuffer = new unsigned char[imageSize];
    ifstream in(inRGB, ios::binary);
    if (!in)
    {
        cout << inRGB << " open failed." << endl;
        exit(1);
    }
    in.read((char*)rgbBuffer, imageSize);

    rgb2bmp(rgbBuffer, width, height, outBmpOrigin);
    rgb2yuv(rgbBuffer, width, height, outYUV);
    in.close();
    if (rgbBuffer != nullptr) delete[] rgbBuffer;

    in.open(outYUV, ios::binary);
    if (!in.is_open()) { cout << outYUV << " open failed." << endl; exit(1); }
    unsigned char* yuvBuffer = new unsigned char[imageSize];
    in.read((char*)yuvBuffer, imageSize);
    yuv2rgb(yuvBuffer, width, height, outRGB);
    in.close();
    if (yuvBuffer != nullptr) delete[] yuvBuffer;

    in.open(outRGB, ios::binary);
    if (!in.is_open()) { cout << outRGB << " open failed." << endl; exit(1); }
    rgbBuffer = new unsigned char[imageSize];
    in.read((char * )rgbBuffer, imageSize);
    rgb2bmp(rgbBuffer, width, height, outBmpProssed);
    in.close();
    if (rgbBuffer != nullptr) delete[] rgbBuffer;

    Compare(inRGB, outRGB);
}


实验结果与总结

过程与结果

  1. 首先设定命令参数

    为了简便,自己写了一个函数,只要输入一个文件以及分辨率即可,自动生成其他文件名;当然也可以手工指定。这里使用缺省的方法,只输入原始文件和它的分辨率。

    在这里插入图片描述

  2. 之后先将原始文件转换成bmp,这里命名为downo.bmp,o表示原来的图像。
    原始rgb

  3. 随后转换成4:2:0格式的yuv文件
    文件大小
    转换完成后可以看到文件大小恰好是原始文件的一半,用YUV播放器验证:
    yuv
    文件可以正常打开。

  4. 将4:2:0格式的yuv文件转换成rgb格式

  5. 将转换成的rgb格式转换成bmp文件,这里叫downp.rgb,p表示经过处理。
    对比

  6. 对比两个文件,统计不同的像素数和像素值的方差
    差别

总结

转换公式

经过量化后的公式:
[ D Y D U D V ] = [ 0.2568 0.5041 0.0979 − 0.1448 − 0.2913 0.4392 0.4392 0.3677 0.0714 ] [ R G B ] + [ 16 128 128 ] \left[\begin{matrix} D_Y\\ D_U\\ D_V \end{matrix} \right]=\left[\begin{matrix} 0.2568& 0.5041 &0.0979\\ -0.1448&-0.2913&0.4392 \\ 0.4392&0.3677&0.0714 \\ \end{matrix} \right]\left[\begin{matrix} R\\ G\\ B \end{matrix} \right]+ \left[\begin{matrix} 16\\ 128\\ 128 \end{matrix} \right] ⎣⎡​DY​DU​DV​​⎦⎤​=⎣⎡​0.2568−0.14480.4392​0.5041−0.29130.3677​0.09790.43920.0714​⎦⎤​⎣⎡​RGB​⎦⎤​+⎣⎡​16128128​⎦⎤​

[ R G B ] = [ 1.1644 0.0000 1.5960 1.1644 − 0.3917 − 0.8127 1.1644 2.0170 − 0.0014 ] [ D Y − 16 D U − 128 D V − 128 ] \left[\begin{matrix} R\\ G\\ B \end{matrix} \right]=\left[\begin{matrix} 1.1644 &0.0000 &1.5960\\ 1.1644 &-0.3917 &-0.8127\\ 1.1644 &2.0170& -0.0014 \end{matrix} \right]\left[\begin{matrix} D_Y-16\\ D_U-128\\ D_V-128 \end{matrix} \right] ⎣⎡​RGB​⎦⎤​=⎣⎡​1.16441.16441.1644​0.0000−0.39172.0170​1.5960−0.8127−0.0014​⎦⎤​⎣⎡​DY​−16DU​−128DV​−128​⎦⎤​

误差来源

可以看到误差还是挺多的,对于误差大致有如下来源:

  • rgb转换成yuv时需要进行量化
  • 浮点计算误差
  • 4:2:2格式取样会y和u会各舍去1/4的像素点
  • yuv转换成rgb时又会有浮点计算误差和量化误差
  • 计算过程会导致部分数据溢出
彩色空间转换 实验报告 实验目的 1.学会从计算和程序的角度分析问题通过完成本实验,理解计算思维,即从问题出发,通过逐步分析和分解,把原问题转化为可用程序方式解决的问题。在此过程中设计出一个解决方案。 2.进一步理解彩色空间的概念并掌握不同彩色空间转换的基本方程。 3.通过逐步设计程序,掌握编程细节:如查找表的设计,内存分配,对 U 和 V 信号进行下采样,文件读写过程等。掌握程序调试的基本方法 实验步骤 1.编写RGB... 阅读详情

相关推荐

数据压缩原理 实验报告一 彩色空间转换

数据压缩 实验报告

liu_yuchang的博客 1014

实验5 彩色图像处理与图像变换

彩色图像处理和图像变换实验讲义

WHS-_-2022的博客 2365

数据压缩实验一实验报告

数据压缩实验一实验报告

yingqi_lok的博客 2967

【实验一】彩色空间转换

文章目录1.rgb空间与yuv空间对应关系:2.量化电平的分配3.利用RGB转YUV的转换公式,整理得到:4.main函数的参数5.查找表的使用(1)RGB转YUV(1)YUV转RGB6.代码部分(1)main.cpp(2)rgb2yuv.h(3)yuv2rgb.h(4)yuv2rgb.cpp(5)rgb2yuv.cpp7.实验结果分析(1)rgb转yuv(2)yuv转rgb8.误差分析 1.rgb空间与yuv空间对应关系: 根据电视原理相关知识可知RGB与的YUV对应关系为: Y=0.299R +0..

weixin_43878172的博客 820

RGB颜色模型算法(颜色渐变立方体)实验报告.zip

分为三大部分:数据及结构分析、算法的设计与实现、效果图

【图文步骤】EC-Engineer部分操作详细步骤

EC-Engineer部分功能详细使用手册

weixin_42212468的博客 6171

实验(一):彩色空间转换实验 RGB—YUV

实验目标:将所给的RGB空间的 down.rgb 转换为YUV色彩空间的 down.yuv,再将其重新转换为RGB空间,分析结果 一.rgb与yuv格式文件的数据 要对文件中的数据进行处理,首先当然要了解这种文件格式的数据特点。 说是rgb,yuv文件,其实本质上都是没有文件头的raw文件,文件头开始读就直接是数据。而rgb与yuv的数据存储也有一定的规范,即如下图所示: 其中YUV的...

weixin_43175007的博客 761

终端以太网接入Internet实验

实验过程: 1.布局 2.连接 3.配置Route0和Route1 4.配置RIP 5.static: 6.查看Route0,Route1的路由表: 7.完成PC2和server0 IP地址,子网掩码和默认网关地址的配置: 8.命令行: 9.启动PC0桌面下的PPPoe宽带连接程序: 10.启动PC1桌面下的PPPoe宽带连接程序: 11.验证PC0和PC2的ICMP报文传输过程 12.验证PC1和server0的ICMP报文传输过程 关键命令说明

chaojidagouzi的博客 4082

02 数字图像技术——颜色空间转换与颜色空间分割实验结果与分析——python

二、实验内容 1.拆分并显示图像RGB空间的三个通道。 2.RGB空间转换为HSI空间。 3.图像的强度分层。 4.灰度值到彩色变换并画出映射函数。 5.颜色空间分割

yuhaix的博客 4059

保姆级教程:用STM32标准库搞定TIM1的PWM输出(附完整代码和避坑点)

本文提供STM32F103高级定时器TIM1的PWM输出保姆级教程,涵盖从工程搭建到完整代码实现的详细步骤。重点解析TIM1作为高级定时器的特殊配置要点,包括时钟初始化、PWM通道设置和GPIO复用功能,并附常见问题排查指南和动态调整占空比的实用技巧,帮助开发者高效实现精准PWM控制。

weixin_30673611的博客 433

搭建PX4开发系统(Ubuntu 22.04)

本文是《从零开始学 PX4 无人机开发》系列第二篇,适合开发者在 Ubuntu 系统下快速搭建 PX4 编译与仿真环境,支持 QGroundControl、Gazebo、MAVSDK 等完整工具链。

qq_15856721的博客 4200

彩色空间转换实验报告

一、实验目的 1.学会从计算和程序的角度分析问题 通过完成本实验,理解计算思维,即从问题出发,通过逐步分析和分解,把原问题转化 为可用程序方式解决的问题。在此过程中设计出一个解决方案。 2.进一步理解彩色空间的概念并掌握不同彩色空间转换的基本方程。 3.通过逐步设计程序,掌握编程细节:如查找表的设计,内存分配,对 U 和 V 信号进 行下采样,文件读写过程等。掌握程序调试的基本方法。 二、知识储备 1.彩色空间转换的基本思想及转换公式 (1)YUV与RGB空间的相互转换 由电视原理可知,亮度和色差信号的构成

m0_51642395的博客 425

智能驾驶感知模块的测试评估方法(上)

对于智能驾驶系统而言,感知模块是开发难度最大、投入资源最多的核心部分。在智能驾驶开发团队内部,感知模块需要独立进行评测才能及早发现潜在缺陷或判断性能是否有改进。随着近几年BEV、OCC、E2E等新技术方案被越来越多企业应用,感知的评测变得越来越艰难,效率也变得越来越低下。本文作者基于在智驾测试行业多年经验,试图总结感知评测的基本规律和效率提升方法,以供读者一些启发。

vincent_321的博客 2559

实验报告:彩色空间转换实验

实验报告:彩色空间转换实验 1.实验内容 ①编写RGB转化为YUV程序,重点掌握函数定义,部分查找表的初始化和调用,缓冲区 分配。将得到的RGB文件转换为YUV文件,用YUV Viewer播放器观看,验证是否正确。 ②编写将YUV转换为RGB的程序。将给定的实验数据用该程序转换为RGB文件。并与原 RGB文件进行比较,如果有误差,分析误差来自何处。 2.实验步骤 YUV2RGB文件转换流程分析 ①...

weixin_46338503的博客 399

智能车实战——从ADC采集到PWM控制,让电磁车精准循迹

本文详细介绍了电磁循迹智能车的实现原理与实战技巧,涵盖从ADC信号采集、PWM控制到PID算法整定的全流程。通过STM32代码示例解析电感信号处理、差比和偏差计算及电机驱动等关键技术,帮助开发者快速掌握智能车精准循迹的核心方法,提升电磁车竞赛项目的开发效率。

weixin_29192211的博客 301

PCL 直通滤波

内容抄自优快云点云侠:[【2024最新版】PCL点云处理算法汇总(C++长期更新版)](https://blog.youkuaiyun.com/qq_36686437/article/details/114160640)。质量无忧,永久免费,可放心复制粘贴。

m0_51204289的博客 903

实验1:彩色空间转换实验报告

一、实验目的 1.学会从计算和程序的角度分析问题 通过完成本实验,理解计算思维,即从问题出发,通过逐步分析和分解,把原问题转化为可用程序方式解决的问题。在此过程中设计出一个解决案。 2.进一步理解彩色空间的概念并掌握不同彩色空间转换的基本方程。 3.通过逐步设计程序,掌握编程细节:如查找表的设计,内存分配,对 U 和 V 信号进行下采样,文件读写过程等。掌握程序调试的基本方法。 二、实验内容 1....

weixin_45432000的博客 501

18、深入解析航空飞行数据中的秘密:从数据清理到高级分析

本文详细解析了如何使用R语言处理和分析航空飞行数据,从基础的数据清理到复杂的回归分析和机器学习模型构建。文章涵盖了探索性数据分析、天气数据的整合、地理信息可视化、时间序列预测以及航班延误的优化策略等内容,展示了如何通过数据挖掘技术提高航空运营效率和乘客满意度,并探讨了数据质量评估与隐私保护的重要性。

convnet3designer的博客 107

彩色空间之间的转换实验报告

一.实验原理 二.代码实现 1.RGB转YUV实验 (1)rgb2yuv.h 创建头文件并声明函数 int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip); void InitLookupTable(); (2)rgb2yuv.cpp 实现将rgb数据转化为...

cuc李滢的博客 258

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

“数据压缩实验之彩色空间转换”实验报告_201810413045_陈诚

文章目录 #1.实验名称 彩色空间转换 #2.实验软件 Visual Studio 2019 #3.实验目的 1.学会从计算和程序的角度分析问题 通过完成本实验,理解计算思维,即从问题出发,通过逐步分析和分解,把原问题转化 为可用程序方式解决的问题。在此过程中设计出一个解决方案。 2.进一步理解彩色空间的概念并掌握不同彩色空间转换的基本方程。 3.通过逐步设计程序,掌握编程细节:如查找表的设计,内存分配,对 U 和 V 信号进 行下采样,文件读写过程等。掌握程序调试的基本方法。 #4.实验原理(思路) ##

qq_45639597的博客 397

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

数据压缩原理与应用 彩色空间转换 实验报告

一、实验原理 1.图像文件的存储格式 图像在文件中均以数据流的形式存储,RGB格式图像在文件中按扫描行顺序,依次存储每一像素的B、G、R值;而YUV格式图像在文件中各分量独立保存,先存储一帧图像中所有像素的Y分量值,按扫描行顺序排列,紧接着再存储一帧图像所有像素的U分量值,最后是一帧图像所有像素的V分量值。 2.彩色空间转换的转换公式及分析 (1)由RGB转换为YUV公式:Y=0.2990

zgyggy的博客 1102

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

数据压缩实验报告1 彩色空间转换

实验内容:1.编写RGB转化为YUV程序,重点掌握函数定义,部分查找表的初始化和调用,缓冲区 分配。将得到的RGB文件转换为YUV文件,用YUV Viewer播放器观看,验证是否正确。 2.编写将YUV转换为RGB的程序。将给定的实验数据用该程序转换为RGB文件。并与原 RGB文件进行比较,如果有误差,分析误差来自何处。 YUV与RGB空间的相互转换 由电视原理可知,亮度和色差信号的构成如下: Y...

Tangent_Chase的博客 308

数据压缩 实验报告一 彩色空间转换

实验原理YUV与RGB转换公式RGB信号转换为YUV信号的公式为Y= 0.2990R + 0.5870G + 0.1140B U= -0.1684R - 0.3316G + 0.5B V= 0.5R - 0.4187G - 0.0813B YUV信号转换为RGB信号的公式为R= Y + 1.4075(V−128) G= Y − 0.3455(U−128) − 0.7169(V−12

MayNine的博客 690

数据压缩原理与应用之彩色空间转换 实验报告1

1、实验基本原理 (1)YUV 与 RGB 空间的相互转换 Y=0.2990R+0.5870G+0.1140B R-Y=0.7010R -0.5870G -0.1140B B-Y=-0.2990R -0.5870G+0.8860B 归一 化后的色差信号为: U=-0.1684R -0.3316G+0.5B V=0.5R -0.4187G -0.0813B 为将两个色差

m0_37644417的博客 313

【实验一】使用C++实现YUV与RGB色彩空间转换

实验一@TOC 1、实验目的 1.学会从计算和程序的角度分析问题 2.通过完成本实验,理解计算思维,即从问题出发,通过逐步分析和分解,把原问题转化为可用程序方式解决的问题。在此过程中设计出一个解决方案。 3.进一步理解彩色空间的概念并掌握不同彩色空间转换的基本方程。 3.通过逐步设计程序,掌握编程细节:如查找表的设计,内存分配,对 U 和 V 信号进行下采样,文件读写过程等。掌握程序调试的基本方法。 4.实验设备:安装 Visual Studio 及其他软件的个人计算机。 2.实验原理: 1.彩色空间转换的

weixin_47048525的博客 644
上一篇: RGB文件的处理与分析
下一篇: CIF文件转换为QCIF
Cross_Entropy
博客等级 码龄7年 69粉丝 45原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值