数据压缩(四)——彩色空间转换(不采样版)
实验一:
编写RGB转化为YUV程序,重点掌握函数定义,部分查找表的初始化和调用,缓冲区分配。将得到的RGB文件转换为YUV文件,用YUV Viewer播放器观看,验证是否正确。
编写将YUV转换为RGB的程序。将给定的实验数据用该程序转换为RGB文件。并与原
RGB文件进行比较,如果有误差,分析误差来自何处。
文章目录
(一)YUV和RGB的转换公式及文件存储格式
以上公式是已经量化过的公式。
查阅资料可知,文件的存储格式为,文件的存储格式为先存全部的,再存全部的,最后存全部的。
(二)main函数的命令行参数
2.1 表示方法
一个程序的函数可以包含两个参数:
- 第一个参数为类型;
- 第二个参数为字符串数组;
通常情况下,将第一个参数命名为,第二个参数为。由于字符串数组在函数头中的声明可以有两种形式,所以函数也有两种写法。
-
函数写法一:
int main(int argc, char** argv) { return 0; }
-
函数写法二:
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
-
编程模式下如何进行参数输入?
使用平台为,需要使用的文件为,需要生成的文件为,参数输入的步骤如下图流程所示:
1.打开上方任务栏调试界面的属性窗口 | |
---|---|
2.选择配置属性中的调试 | |
3.按照要求修改命令参数 |
(三)彩色空间转换(不采样)代码初步实现
由上述知识,可以轻易地进行彩色空间转换的初步实现。代码由头文件和源文件组成。
解决方案资源管理器如下图所示:
实验代码如下:
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.rgb | up.yuv | cho.rgb |
---|---|---|
其中,和使用打开的打开方式为:
打开的图像是倒置的图像(由于图像格式是倒着存储的,所以图像用方式打开时会倒)。上述表格中的图片为了便于分辨,已经用微信进行过旋转,但是文件和文件之间依然有镜像的翻转,不过不影响观看与比对。
使用打开的方式为:
由三幅图像的对比图可知,的实验成功完成,而的实验有一点问题,转出的图像中有较多红色的杂点。
(四)实验错误原因分析及代码修改
错误修改
推断可得,在进行的转换时,得到的三个数据可能超过了类型可以表示的范围,即可能或者。
因此需要对文件进行适当的修正,的值都直接,的值都直接。
修改后的如下:
#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.rgb | up.yuv | cho.rgb |
---|---|---|
至此,差不多完成了和两个实验。
(五)优化代码(使用查找表的方法)
利用查找表,对代码进行了优化。代码由头文件和源文件组成。
解决方案资源管理器如下图所示:
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.rgb | up.yuv | cho.rgb |
---|---|---|
至此,完成了4:4:4的文件与4:4:4的文件之间的转换。
上一篇: MPEG音频编码实验
下一篇: 压缩算法——lzw算法实现