0
点赞
收藏
分享

微信扫一扫

[数据压缩]_实验—1 RGB转YUV

戴老师成长记录仪 2022-04-13 阅读 53
c++

文章目录

一、实验目的

RGB转化为YUV程序,重点掌握函数定义,部分查找表的初始化和调用缓冲区分配

将得到的RGB文件转换为YUV文件,用YUV Viewer播放器观看,验证是否正确。

二、实验原理
  • rgb转yuv的公式为

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 = − 0.1684 R + 0.3316 G + 0.5 B + 128 U=-0.1684R+0.3316G+0.5B+128 U=0.1684R+0.3316G+0.5B+128

V = 0.5 R − 0.4187 G − 0.0813 B + 128 V=0.5R-0.4187G-0.0813B+128 V=0.5R0.4187G0.0813B+128

​ 至于为什么U,V分量要加128,不加就不对。

  • 码电平分配以及数字表达式

    对分量信号进行8比特均匀量化时,共分256个量化级,为防止信号变动造成过载,亮度信号在上端留20级,下端留16级作为动态范围的保护带。

    色差信号经过归一化处理后,动态范围时-0.5-0.5,让色差零电平对应128,256级的上端留15级,下端留16级作为信号动态范围的保护带。

  • 色度格式

    4:2:0格式是指色差信号U,V的取样频率为亮度信号取样频率的四分之一,在水平方向和垂直方向上的取样点数均为Y的一半。

三、实验过程

RGB转YUV

代码思路:

在这里插入图片描述

头文件

#pragma once
#include "rgbtoyuv.cpp"
extern int RGB2YUV(int Height, int Width, void* rgb_in, void* y_out, void* u_out, void* v_out)

主函数

#include<iostream>
#define uchar unsigned char
#include "rgb2yuv.cpp"
using namespace std;

int main(int argc,char** argv)
{
	int framewidth, frameheight;
	char* rgbFilename = NULL;
	char* yuvFilename = NULL;
	FILE* rgbFile = NULL;
	FILE* yuvFile = NULL;
	
	//传入参数
	rgbFilename = argv[1];
	yuvFilename = argv[2];
	framewidth = atoi(argv[3]);
	frameheight = atoi(argv[4]);

	//定义指针
	unsigned char* rgbBuf;
	unsigned char* yBuf;
	unsigned char* uBuf;
	unsigned char* vBuf;
    
	//分配空间
	rgbBuf = (unsigned char*)malloc(frameheight * framewidth * 3);
	yBuf = (unsigned char*)malloc(frameheight * framewidth);
	uBuf = (unsigned char*)malloc(frameheight * framewidth / 4);
	vBuf = (unsigned char*)malloc(frameheight * framewidth / 4);

	//打开文件
	fopen_s(&rgbFile,rgbFilename, "rb");
	if (rgbFile == NULL)
	{
		printf("there is error_1");
		exit(1);
	}
	else 
	{
		printf("成功创建存放rgb的文件指针\n");
	}
    
    //创建yuv文件
	fopen_s(&yuvFile, yuvFilename, "wb");
	
	//读取数据
	fread(rgbBuf, sizeof(uchar), frameheight * framewidth * 3, rgbFile);

	//rgb2yuv
	RGB2YUV(frameheight, framewidth,rgbBuf,yBuf,uBuf,vBuf);

	//写入文件
	fwrite(yBuf, sizeof(uchar), frameheight * framewidth, yuvFile);
	fwrite(uBuf, sizeof(uchar), (frameheight * framewidth)/4, yuvFile);
	fwrite(vBuf, sizeof(uchar), (frameheight * framewidth)/4, yuvFile);

	//关闭文件
	fclose(yuvFile);
	fclose(rgbFile);
	delete rgbBuf;
	delete yBuf;
	delete vBuf;
	delete uBuf;
	return 0;
}

rgb2yuv

#include<malloc.h>
#include<iostream>
#include<fstream>
using namespace std;
#define uchar unsigned char
//声明全局变量
static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];
void InitLookupTable();
int RGB2YUV(int Height, int Width,void* rgb_in,void* y_out,void* u_out,void* v_out)
{
	static int init_done = 0;

	uchar* r, * g, * b;
	uchar* y, * u, * v;
	uchar* pu1,*pu2,* pv1, * pv2, * psu, * psv;
	uchar* y_buf, * u_buf, * v_buf;
	uchar* sub_u_buf, * sub_v_buf;

	//把查找表算了
	if (init_done == 0)
	{
		InitLookupTable();
		init_done = 1;
	}
	
	if ((Height % 2) || (Width % 2)) return 1;

	//分配内存
	y_buf = (uchar*)y_out;
	sub_u_buf = (uchar*)u_out;
	sub_v_buf = (uchar*)v_out;
	u_buf = (uchar*)malloc(Height * Width * sizeof(uchar));
	v_buf = (uchar*)malloc(Height * Width * sizeof(uchar));

	b = (uchar*)rgb_in;//指针指向传进来rgb文件的地址
	y = y_buf;		   //y指针指向y_buf开启的内存地址
	u = u_buf;
	v = v_buf;

	//rgb2yuv
	for (int i = 0; i < Height * Width; i++)
	{
		//bgrbgrbgrbgr...
		g = b + 1;
		r = b + 2;
		*y = (uchar)(RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
		*u = (uchar)(-RGBYUV01684[*r] - RGBYUV03316[*g] + (*b) / 2 + 128);
		*v = (uchar)((*r) / 2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
		b += 3;
		y++;
		u++;
		v++;
	}

	// subsample UV ,UV数据以2*2的方块为单元取平均
	for (int j = 0; j < Height / 2; j++)
	{
        //基本指针
		psu = sub_u_buf + j * Width / 2;
		psv = sub_v_buf + j * Width / 2;
        //移动指针,操作当前数据
		pu1 = u_buf + 2 * j * Width;
		pu2 = u_buf + (2 * j + 1) * Width;
		pv1 = v_buf + 2 * j * Width;
		pv2 = v_buf + (2 * j + 1) * Width;
		for (int i = 0; i < Width / 2; i++)
		{
            //*psu->sub_u_buf->u_out
			*psu = (*pu1 + *(pu1 + 1) + *pu2 + *(pu2 + 1)) / 4;
			*psv = (*pv1 + *(pv1 + 1) + *pv2 + *(pv2 + 1)) / 4;
			psu++;
			psv++;
			pu1 += 2;
			pu2 += 2;
			pv1 += 2;
			pv2 += 2;
		}
	}
}

void InitLookupTable()
{
	for (int i = 0; i < 256; i++)
	{
		RGBYUV02990[i] = (float)0.2990 * i;
		RGBYUV05870[i] = (float)0.5870 * i;
		RGBYUV01140[i] = (float)0.1140 * i;
		RGBYUV01684[i] = (float)0.1684 * i;
		RGBYUV03316[i] = (float)0.3316 * i;
		RGBYUV04187[i] = (float)0.4187 * i;
		RGBYUV00813[i] = (float)0.0813 * i;
	}
}

解释

1.本实验采用命令行传参,就是本地rgb文件不是以代码中赋进去的,而是调用int main(int argc,char** argv)中的参数。

在这里插入图片描述

在这里插入图片描述

2.刚开始想验证打印命令行传参的第二个数组元素是否与上图一致,输出格式不对

在这里插入图片描述

3.查找表的使用,提高运行效率

四、实验总结

1.学会用指针去操作数据,以前是用的数组。

2.思考:

  • 用指针和用数组有啥不同点?
    • 指针的话它需要位置的移动 取值是两个不同的事情。
    • 数组的话移到哪里取哪里。
举报

相关推荐

0 条评论