数据压缩_作业四_彩色空间转换

实验目的

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

实验内容

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
为了使色差信号的动态范围控制在0.5之间,需要进行归一化,对色差信号引入压缩系数。归一化后的色差信号为:
U=-0.1684R-0.3316G+0.5B
V=0.5R-0.4187G-0.0813B

(2) 码电平分配及数字表达式

  • 亮电平信号量化后码电平分配在对分量信号进行8比特均匀量化时,共分为256个等间隔的量化级。为了防止信号变动造成过载,在256级上端留20级,下端留16级作为信号超越动态范围的保护带。
    即小于16的信号都等于16,大于235的信号都等于235。
  • 色差信号量化后码电平分配色差信号经过归一化处理后,动态范围为-0.5~0.5,让色差零电平对应码电平128,色差信号总共占225个量化级。在256级上端留15级,下端留16级作为信号超越动态范围的保护带。
    即将上述公式求得的U、V值+128,使其范围在0~255,再令小于16的信号都等于16,大于239的信号都等于240。

(3)色度格式 4:2:0格式是指色差信号U,V的取样频率为亮度信号取样频率的四分之一,在水平方向和垂直方向上的取样点数均为Y的一半。
即每四个(2x2)像素点共用一个U、V信号,转换时,U、V取这四个像素点对应U、V数值的平均值。

2.掌握由问题到程序的实现过程
(1)掌握所用的程序语言
只有熟悉所使用的程序语言,精通语言中各种结构(包括其形式和意义),才可能实
现将所要解决的问题转换为程序。。
(2)学会写程序
首先确定适用的程序结构,保证写出的程序结构良好清晰、易于阅读和理解。还应
注意当有些条件或要求改变时,程序是否容易修改去满足新的要求。 (3)检查程序错误的能力
程序错误通常包括语言错误、逻辑错误和算法错误。要熟悉所用工具和编程环境帮
助迅速找到错误并改正。

实现过程

rgb2yuv

1.转换公式
考虑:

  • 度信号的压缩系数;
  • 色差零电平对应码电平128,色差信号总共占225个量化级,即将U、V值+128;
  • 色度格式4:2:0时U、V的取样, 即每四个(2x2)像素点共用一个U、V信号,转换时,U、V取这四个像素点对应U、V数值的平均值。

综上,可得转换公式如下:

Y=0.2990R+0.5870G+0.1140B ;

U[i]=-0.1684*(R[i]+R[i+1]+R[w+i]+R[w+i+1])/4-0.3316*(G[i]+G[i+1]+G[w+i]+G[w+i+1])/4+0.5*(B[i]+B[i+1]+B[w+i]+B[w+i+1])/4 +128;

V[i]=0.5*(R[i]+R[i+1]+R[w+i]+R[w+i+1])/4-0.4187*(G[i]+G[i+1]+G[w+i]+G[w+i+1])/4-0.0813*(B[i]+B[i+1]+B[w+i]+B[w+i+1])/4 +128;

(其中w为图像宽度)

rgb2yuv中U、V具体赋值语句如下:

int temp;	
for (j = 0; j < h/2; j++)	
{
	for (i = 0; i < w/2; i ++)			
	{		
		temp= -0.1687 * ((R[2 * i + j * 2 *w] + R[2 * i + 1 + j * 2 * w] + R[2 * i + (j * 2 + 1) * w] + R[2 * i + 1 + (j * 2 + 1) * w]) / 4)
		- 0.3313 * ((G[2 * i + j * 2 * w] + G[2 * i + 1 + j * 2 * w] + G[2 * i + (j * 2 + 1) * w] + G[2 * i + 1 + (j * 2 + 1) * w]) / 4)
		+ 0.5 * ((B[2 * i + j * 2 * w] + B[2 * i + 1 + j * 2 * w] + B[2 * i + (j * 2 + 1) * w] + B[2 * i + 1 + (j * 2 + 1) * w]) / 4) + 128;			
		if (temp < 16)				
			temp = 16;			
		else if (temp > 240)				
			temp = 240;			
		U[j*w / 2 + i] = temp;
		
		temp= 0.5 * ((R[2 * i + j * 2 * w] + R[2 * i + 1 + j * 2 * w] + R[2 * i + (j * 2 + 1) * w] + R[2 * i + 1 + (j * 2 + 1) * w]) / 4)				
		- 0.4187 * ((G[2 * i + j * 2 * w] + G[2 * i + 1 + j * 2 * w] + G[2 * i + (j * 2 + 1) * w] + G[2 * i + 1 + (j * 2 + 1) * w]) / 4)				
		- - 0.0813 * ((B[2 * i + j * 2 * w] + B[2 * i + 1 + j * 2 * w] + B[2 * i + (j * 2 + 1) * w] + B[2 * i + 1 + (j * 2 + 1) * w]) / 4) + 128;			
		if (temp < 16)				
			temp = 16;		
		else if (temp > 240)				
			temp = 240;			
		V[j*w / 2 + i] =temp;		
	}
}

注意:

  • R、G、B大小为w * h,U、V大小为(w /2) * (h / 2),注意转换公式中的对应;
  • 不能直接用U、V数组判断溢出,因为U、V是unsigned char型,数值范围在0~255之间,对<0 和>255的情况无法判断并修正,可能会出现失真。

2.输出:
YUV格式数据存储在这里采用Y、U、V的简单堆放,即先存储Y数据(数据量为wh),再存U(wh/4),再存V(w*h/4);
具体程序如下:

 for (i = 0; i < w*h; i++)
 {
  fp_yuv.write((char*)(Y+i), sizeof(unsigned char));
 }
 for (i = 0; i < w*h / 4; i++)
 {
  fp_yuv.write((char*)(U+i), sizeof(unsigned char));
 }
 for (i = 0; i < w*h / 4; i++)
 {
  fp_yuv.write((char*)(V+i), sizeof(unsigned char));
 }

yuv2rgb

  1. 转换公式:

R=Y+1.402*(V-128)

G=Y-0.3441*(U-128)-0.7139*(V-128)

B=Y+1.7718*(U-128)-0.0013*(V-128)

思路与rgb2yuv类似,值得注意的是:
rgb转换为yuv图像的过程中丢失了一部分数据(每四个像素点只得到一组U、V值),故再从此yuv还原出的rgb图像与原始图像相比,相邻四个(即一个2x2方块中)像素点U、V值相等。

实现程序如下:

int m, n;	
int temp;
unsigned char B[h*w] = { 0 }, G[h*w] = { 0 }, R[h*w] = { 0 };	
for (j = 0; j < h; j++)	
{	
	n = j / 2;	
	for (i = 0; i < w; i++)		
	{			

		m = i / 2;			
		temp= Y[i + j * w] + 1.402*(V[m + n * w / 2] - 128);	
		if (temp < 0)				
			temp = 0;			
		else if (temp > 255)				
			temp = 255;			
		R[i + j * w] = temp; 	
					
		temp= Y[i + j * w] - 0.3441*(U[m + n * w / 2] - 128) - 0.7139*(V[m + n * w / 2] - 128);			
		if (temp < 0)				
			temp = 0;			
		else if (temp > 255)				
			temp = 255;			
		G[i + j * w] = temp; 		
				
		temp= Y[i + j * w] + 1.7718*(U[m + n * w / 2] - 128) - 0.0013*(V[m + n * w / 2] - 128);			
		if (temp < 0)				
			temp = 0;			
		else if (temp > 255)				
			temp = 255;			
		B[i + j * w] = temp;		
	}	
}
  • 注意:和rgb2yuv一样,此处需要使用一个中间量temp来判断溢出,不同的是,在本次实验中,rgb2yuv中temp的使用与否对最后图像结果影响不大,但在yuv2rgb中,如果不使用temp判断溢出,则最后的rgb图像中将会出现大量红、蓝点,如下图:

转换过程中未处理数值溢出的rgb图像

  1. 将求出的R、G、B数据写入rgb文件时注意:
  • bmp格式中数据排放在行数上倒序,即rgb文件中第一行应写入图像最后一行的数据
  • bmp格式中每个像素点中的数据存储顺序为B、G、R

实现程序如下:

 for (j = h - 1; j >= 0; j--)
 {
  for (i = 0; i < w; i++)
  {
   fp_rgb.write((char*)(B + j * w + i), sizeof(unsigned char));
   fp_rgb.write((char*)(G + j * w + i), sizeof(unsigned char));
   fp_rgb.write((char*)(R + j * w + i), sizeof(unsigned char));
  }
 }

完整代码

rgb2yuv.cpp

#include "pch.h"
#include<iostream>
#include<fstream>
#define h 256
#define w 256
using namespace std;


int main()
{
 ifstream fp_rgb;
 ofstream fp_yuv;
 fp_rgb.open("down.rgb", ios::in | ios::binary);
 //打开down.rgb文件,模式选用二进制读取操作
 if (!fp_rgb)
 {
  cout << "down.rgb open failed" << endl;
  return 0;
 }
 fp_yuv.open("down.yuv", ios::out | ios::trunc);
 //预先打开down.yuv文件,模式选用写入操作,覆盖原有信息
 if (!fp_yuv)
 {
  cout << "open failed" << endl;
  return 0;
 }

 unsigned char B[h*w] = { 0 }, G[h*w] = {0}, R[h*w] = { 0 };
 for (i = 0; i < w * h; i++)
 {
  fp_rgb.read((char*)(B + i), sizeof(unsigned char));
  //直接从rgb数值中读取r、g、b
  fp_rgb.read((char*)(G + i), sizeof(unsigned char));
  fp_rgb.read((char*)(R + i), sizeof(unsigned char));
 }

 unsigned char Y[h*w] = { 0 }, U[h*w / 4] = { 0 }, V[h*w / 4] = { 0 };
 for (i = 0; i < w*h; i++)
 {
  Y[i] = 0.299*R[i] + 0.587*G[i] + 0.114*B[i];
  if (Y[i] < 16)
   Y[i] = 16;
  else if (Y[i] > 235)
   Y[i] = 235;
 }
 int temp;
 for (j = 0; j < h/2; j++)
 {
  for (i = 0; i < w/2; i ++) 
  {    
   temp= -0.1687 * ((R[2 * i + j * 2 *w] + R[2 * i + 1 + j * 2 * w] + R[2 * i + (j * 2 + 1) * w] + R[2 * i + 1 + (j * 2 + 1) * w]) / 4)
    - 0.3313 * ((G[2 * i + j * 2 * w] + G[2 * i + 1 + j * 2 * w] + G[2 * i + (j * 2 + 1) * w] + G[2 * i + 1 + (j * 2 + 1) * w]) / 4)
    + 0.5 * ((B[2 * i + j * 2 * w] + B[2 * i + 1 + j * 2 * w] + B[2 * i + (j * 2 + 1) * w] + B[2 * i + 1 + (j * 2 + 1) * w]) / 4) + 128;
   if (temp < 16)
    temp = 16;
   else if (temp > 240)
    temp = 240;
   U[j*w / 2 + i] = temp;
   
   temp= 0.5 * ((R[2 * i + j * 2 * w] + R[2 * i + 1 + j * 2 * w] + R[2 * i + (j * 2 + 1) * w] + R[2 * i + 1 + (j * 2 + 1) * w]) / 4)
    - 0.4187 * ((G[2 * i + j * 2 * w] + G[2 * i + 1 + j * 2 * w] + G[2 * i + (j * 2 + 1) * w] + G[2 * i + 1 + (j * 2 + 1) * w]) / 4)
    - 0.0813 * ((B[2 * i + j * 2 * w] + B[2 * i + 1 + j * 2 * w] + B[2 * i + (j * 2 + 1) * w] + B[2 * i + 1 + (j * 2 + 1) * w]) / 4) + 128;
   if (temp < 16)
    temp = 16;
   else if (temp > 240)
    temp = 240;
   V[j*w / 2 + i] =temp;
  }  
 }
 
  for (i = 0; i < w*h; i++)
 {
  fp_yuv.write((char*)(Y+i), sizeof(unsigned char));
 }
 for (i = 0; i < w*h / 4; i++)
 {
  fp_yuv.write((char*)(U+i), sizeof(unsigned char));
 }
 for (i = 0; i < w*h / 4; i++)
 {
  fp_yuv.write((char*)(V+i), sizeof(unsigned char));
 }
 
 fp_rgb.close();
 fp_yuv.close();
 //释放缓冲区
}

yuv2rgb

#include "pch.h"
#include<iostream>
#include<fstream>
#define h 256
#define w 256
using namespace std;

int main()
{
 ifstream fp_yuv;
 ofstream fp_rgb;
 fp_yuv.open("down.yuv", ios::in | ios::binary);
 //打开down.rgb文件,模式选用二进制读取操作
 if (!fp_yuv)
 {
  cout << "down.yuv open failed" << endl;
  return 0;
 }
 fp_rgb.open("down.rgb", ios::out | ios::binary);
 //预先打开down.yuv文件,模式选用二进制写入操作
 if (!fp_rgb)
 {
  cout << "open failed" << endl;
  return 0;
 }
 
 int i, j;
 unsigned char Y[h*w] = { 0 }, U[h*w / 4] = { 0 }, V[h*w / 4] = { 0 };
 for (i = 0; i < w*h; i++)
 {
  fp_yuv.read((char*)(Y + i), sizeof(unsigned char));
 }
 for (i = 0; i < w*h / 4; i++)
 {
  fp_yuv.read((char*)(U + i), sizeof(unsigned char));
 }
 for (i = 0; i < w*h / 4; i++)
 {
  fp_yuv.read((char*)(V + i), sizeof(unsigned char));
 }
 
 int m, n;
 int temp;
 unsigned char B[h*w] = { 0 }, G[h*w] = { 0 }, R[h*w] = { 0 };
 for (j = 0; j < h; j++)
 {
  n = j / 2;
  for (i = 0; i < w; i++)
  {
   m = i / 2;
   temp= Y[i + j * w] + 1.402*(V[m + n * w / 2] - 128);
   if (temp < 0)
    temp = 0;
   else if (temp > 255)
    temp = 255;
   R[i + j * w] = temp;
   
   temp= Y[i + j * w] - 0.3441*(U[m + n * w / 2] - 128) - 0.7139*(V[m + n * w / 2] - 128);
   if (temp < 0)
    temp = 0;
   else if (temp > 255)
    temp = 255;
   G[i + j * w] = temp;
   
   temp= Y[i + j * w] + 1.7718*(U[m + n * w / 2] - 128) - 0.0013*(V[m + n * w / 2] - 128);
   if (temp < 0)
    temp = 0;
   else if (temp > 255)
    temp = 255;
   B[i + j * w] = temp;
  }
 }
 
  for (j = h - 1; j >= 0; j--)
 {
  for (i = 0; i < w; i++)
  {
   fp_rgb.write((char*)(B + j * w + i), sizeof(unsigned char));
   fp_rgb.write((char*)(G + j * w + i), sizeof(unsigned char));
   fp_rgb.write((char*)(R + j * w + i), sizeof(unsigned char));
  }
 }
  
 fp_rgb.close();
 fp_yuv.close();
 //释放缓冲区
}

运行结果

rgb2yuv

在这里插入图片描述

yuv2rgb

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值