数据压缩试验:YUV和RGB的互相转换


作者:赤赤子

RGB和YUV的相互转换

例:RGB to YUV的理解

给出代码

int width;
int height;
char* rgbFileName=NULL;
char* yuvFileName=NULL;
File* rgbFile=NULL;
File* yuvFile=NULL;
width=atoi(argv[3]);
height=atoi(argv[4]);
rgbFileName=argv[1];
yuvFileName=argv[2];
rgbFile=fopen(rgbFileName,"rb");
yuvFile=fopen(yuvFileName,"wb");
rgbBuf=(unsigned char*)malloc(width*height*3);
yBuf=(unsigned char*)malloc(width*height);
uBuf=(unsigned char*)malloc(width*height/4);
vBuf=(unsigned char*)malloc(width*height/4);
fread(rgbBuf,1,width*height*3,rgbFile);
RGB2YUV(width,height,rgbBuf,yBuf,uBuf,vBuf);
fwrite(yBuf,1,width*height,yuvFile);
fwrite(uBuf,1,(width*height)/4,yuvFile);
fwrite(vBuf,1,(width*height)/4,yuvFile);
free(rgbBuf);
free(yBuf);
free(uBuf);
free(vBuf);
fclose(rgbFile);
fclose(yuvFile);

解释图片与说明

老师上课给出的图片
1.fread函数fwrite函数
fread(a,b,c,d):把d文件,一次b个字节读入a数据区一共读c次。
fwrite(a,b,c,d):把a数据区的数据,一次b个字节读入d文件,一共读c次。
2.File*rgbFile=NULL;//rgbFile是一个指向文件的指针,暂时什么也没指,为了可靠声明为NULL;
rgbFile=fopen(rgbFileName,“rb”);我们将名为rgbFileName的这个文件打开放入这个指针里面,rb打开二进制文件,wb则为创建一个空的新文件;
3.制造一个函数RGB2YUV进行转换,将GRB域转换到YUV域里面。
4.用fwrite将数据区的数据写成文件。
5.atoi:将字符转成数字。

转换公式

公式
可根据公式编写函数。大致思路图如下:老师给出的函数思路图

程序中看不懂的段落以及查询求解

//选自示例程序第79行开始
for (i = 0; i < frameWidth*frameHeight; i++){
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;
if (vBuf[i] < 16) vBuf[i] = 16;
if (vBuf[i] > 240) vBuf[i] = 240;
}

解释:YCbCr: 量化后的YUV Y~[16,235] Cb~[16-240] Cr~[16-240]
关于为什么要量化为16-235 ?
量化是为了解决滤波后的过冲现象。将小于16的都规定为16,将大于235的都规定为235.

设计:YUVtoRGB

思路说明

参考老师给出的思路以及程序,其实都是照葫芦画瓢但是能做出来我仍然很骄傲,下面边给出代码边进行说明。

一.主函数分析

1.接口声明

yuvFileName = argv[1];
rgbFileName = argv[2];
frameWidth = atoi(argv[3]);
frameHeight = atoi(argv[4]);

2.打开fuv文件并检错

 yuvFile = fopen(yuvFileName, "rb");
 if (yuvFile == NULL)
 {
  printf("cannot find yuv file\n");
  exit(1);
 }
 else
 {
  printf("The input yuv file is %s\n", yuvFileName);
 }

3.打开rgb文件并检错

 rgbFile = fopen(rgbFileName, "wb");
 if (rgbFile == NULL)
 {
  printf("cannot find yuv file\n");
  exit(1);
 }
 else
 {
  printf("The output rgb file is %s\n", rgbFileName);
 }

4.分配输入输出框并检错

 rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);
 yuvBuf = (u_int8_t*)malloc(frameWidth * frameHeight*3/2);
  if (rgbBuf == NULL || yuvBuf == NULL)
 {
  printf("no enought memory\n");
  exit(1);
 }

5.读入文件并检错,如果成功则作标记

while (fread(yuvBuf, 1, frameWidth * frameHeight * 3/2, yuvFile)) 
 {
  if(YUV2RGB(frameWidth, frameHeight, yuvBuf, rgbBuf, flip))
  {
   printf("error");
   return 0;
  }
     printf("\r...%d", ++videoFramesWritten);
 }
 printf("\n%u %ux%u video frames written\n", videoFramesWritten, frameWidth, frameHeight);

6.对图像进行中心对称变换

 rgbBuf1 = (u_int8_t*)malloc(frameWidth * frameHeight * 3);
 for(y=0,x=frameHeight*frameWidth*3;y<frameHeight*frameWidth*3;y++)
 { 
  rgbBuf1[y]=rgbBuf[x];
  x--;
     }

7.写出文件

fwrite(rgbBuf1, 1, frameWidth * frameHeight*3, rgbFile);

二.转换函数的设计与分析

查找资料后得到转换公式如下:
//公式如下 图片来源Internet
设计思路如下:
用1个指针指向YUV的首位设置为y,另外两个指针用 y分别表示,由于对应关系比较特殊,所以设置循环变量,第一重令y逐个像素指针后移一位对应,第二重第三重则完成分别让u和v在每两行和每两列递增一。但是这个循环较难完成,我采取的办法是分别设置两个u,v指针,第一个指针负责奇数行,第二个负责偶数行,再设置一个小循环让每个u,v再行内循环两次,就完成啦!
以下是循环部分代码:
(由于这个CSDN一复制粘贴大量代码就崩溃我也不知道咋回事所以只能贴个图)
费了半天劲才弄出来的图
这里面的o p q三个变量都是为了颜色矫正才设置的,最开始我写的时候并没有这么写,颜色矫正的那行代码也是一样的。这个后面在问题出现与解决部分还会提到。

下面给出完整代码段

1.main.cpp

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "yuv2rgb.h"
#define u_int8_t unsigned __int8
#define u_int  unsigned __int32
#define u_int32_t unsigned __int32
#define FALSE  false
#define TRUE  true
int main(int argc, char** argv)
{
 /* variables controlable from command line */
 u_int frameWidth = 352;   /* --width=<uint> */
 u_int frameHeight = 240;  /* --height=<uint> */
 bool flip = TRUE;         /* --flip */
 unsigned int x,y;         /*计数变量*/
  /* internal variables */
 char* rgbFileName = NULL;
 char* yuvFileName = NULL;
 FILE* rgbFile = NULL;
 FILE* yuvFile = NULL;
 u_int8_t* rgbBuf = NULL;
 u_int8_t* yuvBuf = NULL;
 u_int8_t* rgbBuf1 = NULL;
 u_int32_t videoFramesWritten = 0;
  /* begin process command line */
 /* point to the specified file names */
 yuvFileName = argv[1];
 rgbFileName = argv[2];
 frameWidth = atoi(argv[3]);
 frameHeight = atoi(argv[4]);
 /* open the YUV file */
 yuvFile = fopen(yuvFileName, "rb");
 if (yuvFile == NULL)
 {
  printf("cannot find yuv file\n");
  exit(1);
 }
 else
 {
  printf("The input yuv file is %s\n", yuvFileName);
 }
 /* open the RGB file */
 rgbFile = fopen(rgbFileName, "wb");
 if (rgbFile == NULL)
 {
  printf("cannot find rgb file\n");
  exit(1);
 }
  else
 {
  printf("The output rgb file is %s\n", rgbFileName);
 }
 /* get an input buffer for a frame */
 rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);
 /* get the output buffers for a frame */
 yuvBuf = (u_int8_t*)malloc(frameWidth * frameHeight*3/2);
 if (rgbBuf == NULL || yuvBuf == NULL)
 {
  printf("no enought memory\n");
  exit(1);
 }
 while (fread(yuvBuf, 1, frameWidth * frameHeight * 3/2, yuvFile)) 
 {
  if(YUV2RGB(frameWidth, frameHeight, yuvBuf, rgbBuf, flip))
  {
   printf("error");
   return 0;
  }
   //fwrite(rgbBuf, 1, frameWidth * frameHeight*3, rgbFile);
     printf("\r...%d", ++videoFramesWritten);
 }
 printf("\n%u %ux%u video frames written\n", videoFramesWritten, frameWidth, frameHeight);
  //图像垂直翻转
    rgbBuf1 = (u_int8_t*)malloc(frameWidth * frameHeight * 3);
 for(y=0,x=frameHeight*frameWidth*3;y<frameHeight*frameWidth*3;y++)//中心对称变换
 { 
  rgbBuf1[y]=rgbBuf[x];
  x--;
  }
  fwrite(rgbBuf1, 1, frameWidth * frameHeight*3, rgbFile);
 /* cleanup */
 free(rgbBuf);
 free(yuvBuf);
 free(rgbBuf1);
 fclose(yuvFile);
 fclose(rgbFile); 
 return(0);
 }
     

2.yuv2rgbFunction.cpp

#include "stdlib.h"
#include "yuv2rgb.h"
#include "stdio.h"
static float YUVRGB014075[256], YUVRGB003455[256];
static float YUVRGB007169[256], YUVRGB017790[256];
int YUV2RGB (int x_dim, int y_dim, void *bmp, void *rgb_out, int flip)
{
	static int init_done = 0;
	int k=0,i=0,j=0;//计数参数
	long size;
	float o1,p1,q1,o2,p2,q2;
	unsigned char *r, *g, *b;
	unsigned char *y, *u1, *v1,*u2,*v2;
	unsigned char *rgb_buffer;
	if (init_done == 0)
	{
		InitLookupTable();
		init_done = 1;
	}
// check to see if x_dim and y_dim are divisible by 2
	if ((x_dim % 2) || (y_dim % 2)) return 1;
	size = x_dim * y_dim;
// allocate memory分配内存
	rgb_buffer = (unsigned char *)rgb_out;
//分配指针,表示uv指针
	b=rgb_buffer;
    y = (unsigned char *)bmp;
	u1= y+x_dim*y_dim;
	u2=u1;
	v1= y+x_dim*y_dim*5/4;
	v2=v1;
	//循环体
	for(i=0;i<y_dim/2;i++)
	{
		for(j=0;j<x_dim/2;j++)
		{
					for(k=0;k<2;k++)
					{	
			g = b + 1;
			r = b + 2;
			o1=(*y) + YUVRGB014075[*v1]-YUVRGB014075[128];
			p1=(*y) - YUVRGB003455[*u1]+YUVRGB003455[128]-YUVRGB007169[*v1]+YUVRGB007169[128];
			q1=(*y) + YUVRGB017790[*u1]-YUVRGB017790[128];
			if(o1<0)o1=0;if(p1<0)p1=0;if(q1<0)q1=0;if(o1>255)o1=255;if(p1>255)p1=255;if(q1>255)q1=255;//颜色矫正
             *r = (unsigned char)(o1);
			 *g = (unsigned char)(p1);
			 *b = (unsigned char)(q1);
		y++;
		b=b+3;
					}
		u1++;
		v1++;
		}
		for(j=0;j<x_dim/2;j++)
		{
					for(k=0;k<2;k++)
					{
			g = b + 1;
			r = b + 2;
			o2=(*y) + YUVRGB014075[*v2]-YUVRGB014075[128];
			p2=(*y) - YUVRGB003455[*u2]+YUVRGB003455[128]-YUVRGB007169[*v2]+YUVRGB007169[128];
			q2=(*y) + YUVRGB017790[*u2]-YUVRGB017790[128];
			if(o2<0)o2=0;if(p2<0)p2=0;if(q2<0)q2=0;if(o2>255)o2=255;if(p2>255)p2=255;if(q2>255)q2=255;//颜色矫正
			 *r = (unsigned char)(o2);
			 *g = (unsigned char)(p2);
			 *b = (unsigned char)(q2);
		y++;
		b=b+3;
					}
		u2++;
		v2++;
		}		
	}
	return 0;
}
	//计算公式打表
void InitLookupTable()
{
	int i;
	for (i = 0; i < 256; i++) YUVRGB014075[i] = (float)1.4075 * i;
	for (i = 0; i < 256; i++) YUVRGB003455[i] = (float)0.3455 * i;
	for (i = 0; i < 256; i++) YUVRGB007169[i] = (float)0.7169 * i;
	for (i = 0; i < 256; i++) YUVRGB017790[i] = (float)1.779 * i;
}

3.头文件

int YUV2RGB (int x_dim, int y_dim, void *bmp, void *rgb_out, int flip);
void InitLookupTable();

实验结果截图

实验结果
实验结果

实验中遇到的问题说明

其实我一开始做这个实验,遇到了很多困难,而在其中最开始我做出来的图象是倒着的而且有明显的颜色溢出现象,大概是这样。实验图片
首先我设计了一个函数将图片倒过来的函数,但我最开始想的是这个翻转bgr一组一个像素一个像素的翻转,但是输出的全都是乱码条纹,结果是直接整个rgbbuffer直接翻转就可以了,我弄了好久好久啊一直不知道错哪,最后试了试直接把所有的反转,便得到了如下结果:
不过为什么是反的呢?

实验图片
为什么变成了紫色呢?红点也变成了绿点。
然后我采取了颜色溢出的处理,但是这个处理我一开始想的是对函数中的*r,*g,*b来进行处理,结果没有用,出现了下图情况:实验图片
在询问老师之后呀,老师告诉我:计算出来的值有可能是负的,然后你做了强制类型转换,后面你再做判断就没意义了。之后我就想在主函数Buf数组里面对数据直接动手脚,发现也没啥用。后来我决定在转换成unsigned char之前就把他在long的时候就给他做处理。
过了好久好久。
最后成功了,其实在之中出现了好多好多小错误,我执行了好多好多遍程序,最后的最后才成功(其实还差一点)为什么是反的呢?图像最开始处理之后是反的就导致我之后再进行中心对称变换也是镜像对称的,最好做的是上下翻转。
小瑕疵
为什么和原图像有差距,其实我觉得还是因为这个公式的问题,而且yuv已经经过了量化处理,所以进行变换时会出现一部分的数据溢出,说是处理了其实只是给溢出的像素规定为一个数值而已,我觉得应该整体提高赋值,把小于零的像素变成正数,不过我没有那么做,我猜那么做之后整体的亮度和色度差会有所变化,这个留到日后再探究。

总结

其实用了很长时间,有图为证,都是我回收站里我做出来的down.converted.yuv不容易啊
嘿嘿,功夫不负有心人哦!!
好啦,要去写别的实验了,唉这学期课不多,实验好多,有点小烦。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值