数据压缩(四)——彩色空间转换(不采样版)

实验一:

  • 编写RGB转化为YUV程序,重点掌握函数定义,部分查找表的初始化和调用,缓冲区分配。将得到的RGB文件转换为YUV文件,用YUV Viewer播放器观看,验证是否正确。

  • 编写将YUV转换为RGB的程序。将给定的实验数据用该程序转换为RGB文件。并与原

    RGB文件进行比较,如果有误差,分析误差来自何处。

  此版文章仅是关于 4 : 4 : 4 4:4:4 4:4:4 R G B RGB RGB文件与 4 : 4 : 4 的 Y U V 4:4:4的YUV 444YUV文件之间的相互转换,具体彩色空间转换公式的推导及采样版代码的实现详见下一篇博客(博客中对于此篇博客进行了复述,可直接跳转)

数据压缩(五)——彩色空间转换(完整版)

(一)YUV和RGB的转换公式及文件存储格式

R = ( 298 × Y + 411 × V − 57344 ) > > 8 G = ( 298 × Y − 101 × U − 211 × V + 34739 ) > > 8 B = ( 298 × Y + 519 × U − 71117 ) > > 8 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 \begin{aligned} &R=(298\times Y+411\times V-57344)>>8\\ &G=(298\times Y-101\times U-211\times V+34739)>>8\\ &B=(298\times Y+519\times U-71117)>>8\\ &Y=(66\times R+129\times G+25\times B)>>8+16\\ &U=(-38\times R-74\times G+112\times B)>>8+128\\ &V=(112\times R-94\times G-18\times B)>>8+128 \end{aligned} R=(298×Y+411×V57344)>>8G=(298×Y101×U211×V+34739)>>8B=(298×Y+519×U71117)>>8Y=(66×R+129×G+25×B)>>8+16U=(38×R74×G+112×B)>>8+128V=(112×R94×G18×B)>>8+128

  以上公式是已经量化过的公式。

  查阅资料可知, R G B RGB RGB文件的存储格式为 B G R B G R B G R ⋯ BGR BGR BGR\cdots BGRBGRBGR, Y U V YUV YUV文件的存储格式为先存全部的 Y Y Y,再存全部的 U U U,最后存全部的 V V V

(二)main函数的命令行参数

2.1 表示方法

  一个程序的 m a i n ( ) main() main()函数可以包含两个参数:

  • 第一个参数为 i n t int int类型;
  • 第二个参数为字符串数组;

  通常情况下,将第一个参数命名为 a r g c argc argc,第二个参数为 a r g v argv argv。由于字符串数组在函数头中的声明可以有两种形式,所以 m a i n ( ) main() main()函数也有两种写法。

  1. m a i n ( ) main() main()函数写法一

    int main(int argc, char** argv)
    {
        return 0;
    }
    
  2. m a i n ( ) main() main()函数写法二:

    int main(int argc, char* argv[])
    {
        return 0;
    }
    

2.2 使用方法

  • 参数的含义:

      int argc​:表示字符串的数量。argc = 1 + 用户输入的字符串数目,argc的值由操作系统自动完成计算,程序员不需要对其进行赋值。

      char argv[]*:存放的是多个字符串,字符串的形式如下:

    argv[0] = 可执行文件的名称。例如change.exe。(这个字符串不需要用户输入,与argc相同,操作系统可自动生成。

    argv[1] = 字符串1

    argv[2] = 字符串2

    argv[3] = 字符串3

    ⋮ \vdots

  • 编程模式下如何进行参数输入?

  使用平台为 V i s u a l S t u d i o 2019 Visual Studio 2019 VisualStudio2019,需要使用的文件为 d o w n . r g b down.rgb down.rgb,需要生成的文件为 u p . y u v , c h o . r g b up.yuv,cho.rgb up.yuv,cho.rgb,参数输入的步骤如下图流程所示:

1.打开上方任务栏调试界面的属性窗口在这里插入图片描述
2.选择配置属性中的调试在这里插入图片描述
3.按照要求修改命令参数在这里插入图片描述

(三)彩色空间转换(不采样)代码初步实现

  由上述知识,可以轻易地进行彩色空间转换的初步实现。代码由头文件 r g b 2 y u v . h , y u v 2 r g b . h rgb2yuv.h,yuv2rgb.h rgb2yuv.h,yuv2rgb.h和源文件 m a i n . c p p , r g b 2 y u v . c p p , y u v 2 r g b . c p p main.cpp,rgb2yuv.cpp,yuv2rgb.cpp main.cpp,rgb2yuv.cpp,yuv2rgb.cpp组成。

  解决方案资源管理器如下图所示:

在这里插入图片描述

  实验代码如下:

main.cpp

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

#define size 196608
#define usize 65536
#define vsize 131072
using namespace std;

int main(int argc, char** argv)
{
	ifstream infile(argv[1],ios::binary);
	ofstream outYUV(argv[2], ios::binary);
	ofstream outRGB(argv[3], ios::binary);
	if (!infile) { cout << "error to open file1!" << endl; }
	if (!outYUV) { cout << "error to open file2" << endl; }
	if (!outRGB) { cout << "error to open file3" << endl; }

	unsigned char* in = new unsigned char[size];
	unsigned char* YUV = new unsigned char[size];
	unsigned char* RGB = new unsigned char[size];

	infile.read((char*)in, size);
	rgb2yuv(in,YUV,size, usize, vsize);//第一次转换
	yuv2rgb(YUV, RGB, usize, vsize);//第二次转换

	/*for (int i = 0; i < size; i++)
	{
		if (abs(in[i] - RGB[i]) > 5)
			cout << "i=" << i << " in[" << i << "]=" << int(in[i]) << " RGB[" << i << "]=" << int(RGB[i]) << endl;
	}*/
	outYUV.write((char*)YUV, size);
	outRGB.write((char*)RGB, size);
	delete in;
	delete YUV;
	delete RGB;
	infile.close();
	outYUV.close();
	outRGB.close();

	return 0;
}

rgb2yuv.h

#pragma once
void rgb2yuv(unsigned char* rgb, unsigned char* yuv, int size,int usize,int vsize);

rgb2yuv.cpp

void rgb2yuv(unsigned char* rgb, unsigned char* yuv,int size,int usize,int vsize)
{
	unsigned char r, g, b, y, u, v;
	int j = 0;
	for (int i = 0;i < size;)
	{
		b = *(rgb + i);
		g = *(rgb + i + 1);
		r = *(rgb + i + 2);
		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;
		*(yuv + j) = y;
		*(yuv + j + usize) = u;
		*(yuv + j + vsize) = v;
		i = i + 3;//每个rgb为1组
		j++;
	}
}

yuv2rgb.h

#pragma once
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize);

yuv2rgb.cpp

#pragma once
#include "yuv2rgb.h"
#include <iostream>
using namespace std;
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize)
{
	unsigned char r, g, b, y, u, v;
	int j = 0;
	for (int i = 0; i < usize; i++)
	{
		y = *(yuv + i);
		u = *(yuv + i + usize);
		v = *(yuv + i + vsize);
		r = (298 * y + 411 * v - 57344) >> 8;
		g = (298 * y - 101 * u - 211 * v + 34739) >> 8;
		b = (298 * y + 519 * u - 71117) >> 8;
		*(rgb + j) = b;
		*(rgb + j + 1) = g;
		*(rgb + j + 2) = r;
		j = j + 3;
	}
}

实验结果

down.rgbup.yuvcho.rgb
在这里插入图片描述在这里插入图片描述在这里插入图片描述

  其中, d o w n . r g b down.rgb down.rgb c h o . r g b cho.rgb cho.rgb使用 Y U V v i e w e r P l u s YUVviewerPlus YUVviewerPlus打开的打开方式为:

在这里插入图片描述

  打开的图像是倒置的图像(由于 b m p bmp bmp图像格式是倒着存储的,所以 . r g b .rgb .rgb图像用 b m p bmp bmp方式打开时会倒)。上述表格中的图片为了便于分辨,已经用微信进行过旋转,但是 Y U V YUV YUV文件和 R G B RGB RGB文件之间依然有镜像的翻转,不过不影响观看与比对。

   u p . y u v up.yuv up.yuv使用 Y U V v i e w e r P l u s YUVviewerPlus YUVviewerPlus打开的方式为:

在这里插入图片描述

  由三幅图像的对比图可知, R G B t o Y U V RGB to YUV RGBtoYUV的实验成功完成,而 Y U V t o R G B YUVtoRGB YUVtoRGB的实验有一点问题,转出的 c h o . r g b cho.rgb cho.rgb图像中有较多红色的杂点。

(四)实验错误原因分析及代码修改

错误修改

  推断可得,在进行 Y U V t o R G B YUVtoRGB YUVtoRGB的转换时,得到的 R G B RGB RGB三个数据可能超过了 u n s i g n e d   c h a r unsigned\ char unsigned char类型可以表示的范围,即可能 < 0 <0 <0或者 > 255 >255 >255

  因此需要对 y u v 2 r g b . c p p yuv2rgb.cpp yuv2rgb.cpp文件进行适当的修正, > 255 >255 >255的值都直接 = 255 =255 =255, < 0 <0 <0的值都直接 = 0 =0 =0

  修改后 y u v 2 r g b . c p p yuv2rgb.cpp yuv2rgb.cpp的如下:

#pragma once
#include "yuv2rgb.h"
#include <iostream>
using namespace std;
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize)
{
	int r, g, b, y, u, v;
	int j = 0;
	for (int i = 0; i < usize; i++)
	{
		y = int(*(yuv + i));
		u = int(*(yuv + i + usize));
		v = int(*(yuv + i + vsize));
		r = (298 * y + 411 * v - 57344) >> 8;
		if (r > 255) { r = 255; }
		if (r < 0) { r = 0; }
		g = (298 * y - 101 * u - 211 * v + 34739) >> 8;
		if (g > 255) { g = 255; }
		if (g < 0) { g = 0; }
		b = (298 * y + 519 * u - 71117) >> 8;
		if (b > 255) { b = 255; }
		if (b < 0) { b = 0; }
		*(rgb + j) = unsigned char(b);
		*(rgb + j + 1) = unsigned char(g);
		*(rgb + j + 2) = unsigned char(r);
		j = j + 3;
	}
}

实验结果

down.rgbup.yuvcho.rgb
在这里插入图片描述在这里插入图片描述在这里插入图片描述

  至此,差不多完成了 R G B t o Y U V RGB to YUV RGBtoYUV Y U V t o R G B YUVtoRGB YUVtoRGB两个实验。

(五)优化代码(使用查找表的方法)

  利用查找表,对代码进行了优化。代码由头文件 y u v r g b . h yuvrgb.h yuvrgb.h和源文件 m a i n . c p p , y u v r g b . c p p main.cpp,yuvrgb.cpp main.cpp,yuvrgb.cpp组成。

  解决方案资源管理器如下图所示:

在这里插入图片描述

main.cpp

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

#define size 196608
#define usize 65536
#define vsize 131072
#define height 256
#define weight 256

//查找表初始化
int* RGBYUV298 = new int[256];
int* RGBYUV411 = new int[256];
int* RGBYUV101 = new int[256];
int* RGBYUV211 = new int[256];
int* RGBYUV519 = new int[256];
int* RGBYUV66 = new int[256];
int* RGBYUV129 = new int[256];
int* RGBYUV25 = new int[256];
int* RGBYUV38 = new int[256];
int* RGBYUV74 = new int[256];
int* RGBYUV112 = new int[256];
int* RGBYUV94 = new int[256];
int* RGBYUV18 = new int[256];

int main(int argc, char** argv)
{
	initLookupTable();

	ifstream infile(argv[1],ios::binary);
	ofstream outYUV(argv[2], ios::binary);
	ofstream outRGB(argv[3], ios::binary);
	if (!infile) { cout << "error to open file1!" << endl; }
	if (!outYUV) { cout << "error to open file2" << endl; }
	if (!outRGB) { cout << "error to open file3" << endl; }

	unsigned char* infi = new unsigned char[size];
	unsigned char* YUVfi = new unsigned char[size];
	unsigned char* RGBfi = new unsigned char[size];

	infile.read((char*)infi, size);	
	rgb2yuv(infi, YUVfi, size, usize, vsize);
	yuv2rgb(YUVfi, RGBfi, usize, vsize);
	outYUV.write((char*)YUVfi, size);
	outRGB.write((char*)RGBfi, size);
	
	fileend(infi,YUVfi,RGBfi);

	infile.close();
	outYUV.close();
	outRGB.close();

	return 0;
}

yuvrgb.h

#pragma once
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize);
void rgb2yuv(unsigned char* rgb, unsigned char* yuv, int size, int usize, int vsize);
void initLookupTable();
void fileend(unsigned char* infi, unsigned char* YUVfi, unsigned char* RGBfi);

yuvrgb.cpp

#pragma once
#include "yuvrgb.h"
#include <iostream>
using namespace std;

extern int* RGBYUV298;
extern int* RGBYUV411;
extern int* RGBYUV101;
extern int* RGBYUV211;
extern int* RGBYUV519;
extern int* RGBYUV66 ;
extern int* RGBYUV129;
extern int* RGBYUV25 ;
extern int* RGBYUV38 ;
extern int* RGBYUV74 ;
extern int* RGBYUV112;
extern int* RGBYUV94 ;
extern int* RGBYUV18 ;

void initLookupTable()
{
	for (int i = 0; i < 256; i++)
	{
		RGBYUV298[i] = 298 * i;
		RGBYUV411[i] = 411 * i;
		RGBYUV101[i] = 101 * i;
		RGBYUV211[i] = 211 * i;
		RGBYUV519[i] = 519 * i;
		RGBYUV66[i] = 66 * i;
		RGBYUV129[i] = 129 * i;
		RGBYUV25[i] = 25 * i;
		RGBYUV38[i] = 38 * i;
		RGBYUV74[i] = 74 * i;
		RGBYUV112[i] = 112 * i;
		RGBYUV94[i] = 94 * i;
		RGBYUV18[i] = 18 * i;
	}
}
void yuv2rgb(unsigned char* yuv, unsigned char* rgb,int usize,int vsize)
{
	int r, g, b, y, u, v;
	int j = 0;
	for (int i = 0; i < usize; i++)
	{
		y = int(*(yuv + i));
		u = int(*(yuv + i + usize));
		v = int(*(yuv + i + vsize));
		/*r = (298 * y + 411 * v - 57344) >> 8;*/
		r = (RGBYUV298[y]+ RGBYUV411[v]-57344)>>8;
		if (r > 255) { r = 255; }
		if (r < 0) { r = 0; }
		/*g = (298 * y - 101 * u - 211 * v + 34739) >> 8;*/
		g = (RGBYUV298[y] - RGBYUV101[u] - RGBYUV211[v] + 34739) >> 8;
		if (g > 255) { g = 255; }
		if (g < 0) { g = 0; }
		/*b = (298 * y + 519 * u - 71117) >> 8;*/
		b = (RGBYUV298[y] + RGBYUV519[u] - 71117) >> 8;
		if (b > 255) { b = 255; }
		if (b < 0) { b = 0; }
		*(rgb + j) = unsigned char(b);
		*(rgb + j + 1) = unsigned char(g);
		*(rgb + j + 2) = unsigned char(r);
		j = j + 3;
	}
}

void rgb2yuv(unsigned char* rgb, unsigned char* yuv, int size, int usize, int vsize)
{
	int r, g, b, y, u, v;
	int j = 0;
	for (int i = 0; i < size;)
	{
		b = int(*(rgb + i));
		g = int(*(rgb + i + 1));
		r = int(*(rgb + i + 2));
		/*y = ((66 * r + 129 * g + 25 * b) >> 8) + 16;*/
		y = ((RGBYUV66[r] + RGBYUV129[g] + RGBYUV25[b]) >> 8) + 16;
		/*u = ((-38 * r - 74 * g + 112 * b) >> 8) + 128;*/
		u = ((-RGBYUV38[r] - RGBYUV74[g] + RGBYUV112[b]) >> 8) + 128;
		/*v = ((112 * r - 94 * g - 18 * b) >> 8) + 128;*/
		v = ((RGBYUV112[r] - RGBYUV94[g] - RGBYUV18[b]) >> 8) + 128;
		/*if ((y > 255) || (u > 255) || (v > 255) || (y < 0) || (u < 0) || (v < 0))
		{
			cout << "y=" << y << "u=" << u << "v=" << v << endl;
		}*/
		*(yuv + j) = unsigned char(y);
		*(yuv + j + usize) = unsigned char(u);
		*(yuv + j + vsize) = unsigned char(v);
		i = i + 3;//每个rgb为1组
		j++;
	}
}

void fileend(unsigned char* infi, unsigned char* YUVfi, unsigned char* RGBfi)
{
	delete infi;
	delete YUVfi;
	delete RGBfi;
	delete	RGBYUV298;
	delete	RGBYUV411;
	delete	RGBYUV101;
	delete	RGBYUV211;
	delete	RGBYUV519;
	delete	RGBYUV66;
	delete	RGBYUV129;
	delete	RGBYUV25;
	delete	RGBYUV38;
	delete	RGBYUV74;
	delete	RGBYUV112;
	delete	RGBYUV94;
	delete	RGBYUV18;
}

实验结果

down.rgbup.yuvcho.rgb
在这里插入图片描述在这里插入图片描述在这里插入图片描述

  至此,完成了4:4:4的 R G B RGB RGB文件与4:4:4的 Y U V YUV YUV文件之间的转换。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值