数据压缩TGA2YUV

一、实验目标

1.读取TGA图像数据

2.将TGA格式转为YUV格式

二、实验原理

1.TGA的结构

名称偏移长度解释
图像信息字段01指出图像信息字段长度:0~255
颜色表类型110:无颜色表;1:有颜色表
图像类型码21图像格式
颜色表首址32颜色表首的入口索引,整型
颜色表长度52整型
颜色表项位数7116:16位TGA;24:24位TGA;32:32位TGA
图像X坐标起始位置82图像左下角X坐标的整型值
图像Y坐标起始位置102图像左下角Y坐标的整型值
图像宽度122以像素为单位,图像宽度的整型(低位-高位)
图像高度142
像素深度161
图像描述171bit0-3:每像素对应属性位的位数,TGA16:0或1,TGA24:0,TGA32:8;bit4:0;bit5:屏幕起始位置标志,0:原点在左下角,1:原点在左上角,对于真彩图像值一定为0;bit6-7:交叉数据存储标志
图像信息字段18可变
颜色表数据可变
图像数据可变

2.TGA图像的颜色显示方式

真彩色:在组成一幅彩色图像的每个像素值中,有R、G、B三个基色分量,每个基色分量直接决定显示设备的基色强度产生彩色

伪彩色:每个像素的颜色不由每个基色分量的数值直接决定,而是把像素值当作颜色查找表的表象入口地址,去查找显示图像时使用的R、G、B强度值,用查找出的R、G、B强度值合成色彩。

三、实验步骤

1、打开、读取TGA图像

`//定义指向tga和Yuv的缓冲区
	ifstream tgaFile;
	ofstream yuvFile;
    //	open tga & yuv file
	tgaFile.open(argv[1], ios::in | ios::binary);
	if (tgaFile)
		cout << "tgafile is opened" << endl;
	yuvFile.open(argv[2], ios::binary, ios::trunc);
	if (yuvFile)
		cout << "yuvfile is opened" << endl;`

2、TGA文件结构体定义

`typedef struct tgaheader
{
	BYTE IMAGEINFOLENGTH;//图像信息字段
	BYTE COLORMAPTYPE;//颜色表类型
	BYTE IMAGETYPE;//图像类型码

}TGAHEAD;
typedef struct tgaheader_cm
{
	WORD CMBEGIN;//颜色表首址
	WORD CMLENGTH;//颜色表长度
	BYTE CMNUMBER;//颜色表项位数
}TGA_cm_header;
typedef struct tgaheader_info
{
	WORD XBEGIN;//图像X坐标起始位置
	WORD YBEGIN;//图像Y坐标起始位置
	WORD IMAGEWIDTH;//图像宽度
	WORD IMAGEHEIGHT;//图像高度
	BYTE PIXEDEPTH;//像素深度
	BYTE IMAGEDESCRIPTOR;//图像描述字节
}TGA_Info_header;`

问题:结构体对齐

结构体的一个成员在开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员见填充一定字节。因为颜色表首地址相对结构体首地址偏移量为3,但颜色表首地址字节数为2,因此编译器在图像类型码与颜色首地址之间填充了一个字节,导致结构体之后的数据读取错误。

因此,以颜色表首地址为结构体首地址再开辟一个结构体,即可解决问题。

3、定义TGA文件头,读入TGA文件头

`//定义tga文件头
	TGAHEAD File_header;
	TGA_cm_header cm_header;
	TGA_Info_header Info_header;
	//读入tga文件头
	tgaFile.read((char*)(&File_header), 3);
	tgaFile.read((char*)(&cm_header), 5);
	tgaFile.read((char*)(&Info_header), 10);`

4、开辟缓冲区存放yuv与rgb数据

`unsigned char* rBuff = NULL;
	unsigned char* gBuff = NULL;
	unsigned char* bBuff = NULL;
	unsigned char* yBuff = NULL;
	unsigned char* uBuff = NULL;
	unsigned char* vBuff = NULL;
	//读取TGA图像大小
	int width, height;
	width = Info_header.IMAGEWIDTH;
	height = Info_header.IMAGEHEIGHT;
	cout << "图像大小:" << width << "X" << height << endl;
	rBuff = (unsigned char*)malloc(height * width);
	gBuff = (unsigned char*)malloc(height * width);
	bBuff = (unsigned char*)malloc(height * width);

	yBuff = (unsigned char*)malloc(height * width);
	uBuff = (unsigned char*)malloc((height * width) / 4);
	vBuff = (unsigned char*)malloc((height * width) / 4);`

5.判断图像格式属于真彩还是伪彩

真彩——直接读取RGB数据

伪彩——读入颜色表,在读入图像数据,根据像素数值查找RGB强度

`	//判断图像格式
	if (File_header.IMAGETYPE == 1)
	{
		cout << "未压缩的颜色表图像:" << endl;
		cout << "颜色表长度:" <<int(cm_header.CMLENGTH) << endl;
		cout << "颜色表项位数:" << int(cm_header.CMNUMBER) << endl;
		cout << "像素深度:" << int(Info_header.PIXEDEPTH) << endl;
		if (File_header.IMAGEINFOLENGTH == 0)
			cout << "无图像信息字段" << endl;
		else
		{
			int imagelength = File_header.IMAGEINFOLENGTH;
			cout << "图像信息字段长度为" << imagelength << endl;
		}
		ReadRGB(tgaFile, File_header, cm_header, Info_header, rBuff, gBuff, bBuff);
	}
	else if (File_header.IMAGETYPE == 2)
	{
		cout << "未压缩的真彩色图像" << endl;
		if (File_header.IMAGEINFOLENGTH == 0)
		{
			cout << "无图像信息字段" << endl;
			ReadRGB(tgaFile, File_header, cm_header, Info_header, rBuff, gBuff, bBuff);
		}
		else
		{
			int imagelength = File_header.IMAGEINFOLENGTH;
			cout << "图像信息字段长度为" << imagelength << endl;
			ReadRGB(tgaFile, File_header, cm_header, Info_header, rBuff, gBuff, bBuff);
		}		
	}
	else
	{
		cout << "其他图像类型" << endl;
		`

6.将RGB转换为YUV

`void RGB2YUV(int w, int h, unsigned char* rdata, unsigned char* gdata, unsigned char* bdata, unsigned char* y, unsigned char* u, unsigned char* v)
{
	initLookupTable();//初始化查找表
	unsigned char* utemp = NULL;
	unsigned char* vtemp = NULL;
	utemp = (unsigned char*)malloc(w * h );
	vtemp = (unsigned char*)malloc(w * h );
	unsigned char  nr, ng, nb;
	float temp;
	//对每个像素进行 rgb -> yuv的转换
	for (int i = 0;i < w * h; i++)
	{
		nb = *(bdata + i);
		ng = *(gdata + i);
		nr = *(rdata + i);
		temp = (unsigned char)(RGBYUV02990[nr] + RGBYUV05870[ng] + RGBYUV01140[nb]);
		if (temp < 16) { temp = 16; }
		if (temp > 235) { temp = 235; }
		*(y + i) = (unsigned char)temp;
		temp = (unsigned char)(-RGBYUV01684[nr] - RGBYUV03316[ng] + RGBYUV05000[nb]+ 128);
		if (temp < 16) { temp = 16; }
		if (temp > 235) { temp = 235; }
		*(utemp + i) = (unsigned char)temp;
		temp = (unsigned char)(RGBYUV05000[nr] - RGBYUV04187[ng] - RGBYUV00813[nb] + 128);
		if (temp < 16) { temp = 16; }
		if (temp > 235) { temp = 235; }
		*(vtemp + i) = (unsigned char)temp;
	}
	

	int k = 0;
	for (int i = 0; i < w * h / 4; i++)
	{
		temp = (utemp[k] + utemp[k + 1] + utemp[k + w] + utemp[w + k + 1]) / 4.0;
		*(u + i) = (unsigned char)temp;
		temp = (vtemp[k] + vtemp[k + 1] + vtemp[w + k] + vtemp[w + k + 1]) / 4.0;
		*(v + i) = (unsigned char)temp;
		if ((i + 1) % (w / 2) == 0) {
			k = k + 2 + w;
		}
		else
			k = k + 2;
	}
	free(utemp);
	free(vtemp);

}`

7.将YUV数据写入之前开辟的YUV图像空间中

`void WriteYUV(unsigned char* Y, unsigned char* U, unsigned char* V, unsigned long size, ofstream& outFile)
{
	outFile.write((char*)Y, size);
	outFile.write((char*)U, size/4);
	outFile.write((char*)V, size/4);
}`

注意:该TGA图像都是从左下角开始
四、实验结果
因为电脑PS软件过期无法显示TGA图像,并且TGA图像由谢昱红金主友情提供,因此和她的图像对比可以看出图像没有错误。
YUV图像:
在这里插入图片描述
在这里插入图片描述
YUV图像:
在这里插入图片描述
在这里插入图片描述
附完整代码:
tga.h

#pragma once
#ifndef tga_H_
#include <Windows.h>
#include<iostream>
#include<malloc.h>
#include <stdio.h>
#include<fstream>
using namespace std;

//tga文件结构体定义
typedef struct tgaheader
{
	BYTE IMAGEINFOLENGTH;//图像信息字段
	BYTE COLORMAPTYPE;//颜色表类型
	BYTE IMAGETYPE;//图像类型码

}TGAHEAD;
typedef struct tgaheader_cm
{
	WORD CMBEGIN;//颜色表首址
	WORD CMLENGTH;//颜色表长度
	BYTE CMNUMBER;//颜色表项位数
}TGA_cm_header;
typedef struct tgaheader_info
{
	WORD XBEGIN;//图像X坐标起始位置
	WORD YBEGIN;//图像Y坐标起始位置
	WORD IMAGEWIDTH;//图像宽度
	WORD IMAGEHEIGHT;//图像高度
	BYTE PIXEDEPTH;//像素深度
	BYTE IMAGEDESCRIPTOR;//图像描述字节
}TGA_Info_header;

void ReadRGB(ifstream& pFile,TGAHEAD& file_h ,TGA_cm_header& cm_h, TGA_Info_header& info_h, unsigned char* rdata, unsigned char* gdata, unsigned char* bdata);
void RGB2YUV(int w, int h, unsigned char* rdata, unsigned char* gdata, unsigned char* bdata, unsigned char* y, unsigned char* u, unsigned char* v);
void initLookupTable();
void WriteYUV(unsigned char* Y, unsigned char* U, unsigned char* V, unsigned long size, ofstream& outFile);

#endif // !tga_H_

main.cpp

#include <stdio.h>
#include <windows.h>
#include<iostream>
#include<fstream>
#include "tga.h"
using namespace std;


int main(char argc, char* argv[])
{
	//初始化,定义文件指针,文件头和信息头结构体变量,
	//定义指向tga和Yuv的缓冲区
	ifstream tgaFile;
	ofstream yuvFile;
    //	open tga & yuv file
	tgaFile.open(argv[1], ios::in | ios::binary);
	if (tgaFile)
		cout << "tgafile is opened" << endl;
	yuvFile.open(argv[2], ios::binary, ios::trunc);
	if (yuvFile)
		cout << "yuvfile is opened" << endl;
	//定义tga文件头
	TGAHEAD File_header;
	TGA_cm_header cm_header;
	TGA_Info_header Info_header;
	//读入tga文件头
	tgaFile.read((char*)(&File_header), 3);
	tgaFile.read((char*)(&cm_header), 5);
	tgaFile.read((char*)(&Info_header), 10);

	//开辟缓冲区存放YUV与RGB数据	
	unsigned char* rBuff = NULL;
	unsigned char* gBuff = NULL;
	unsigned char* bBuff = NULL;
	unsigned char* yBuff = NULL;
	unsigned char* uBuff = NULL;
	unsigned char* vBuff = NULL;
	//读取TGA图像大小
	int width, height;
	width = Info_header.IMAGEWIDTH;
	height = Info_header.IMAGEHEIGHT;
	cout << "图像大小:" << width << "X" << height << endl;
	rBuff = (unsigned char*)malloc(height * width);
	gBuff = (unsigned char*)malloc(height * width);
	bBuff = (unsigned char*)malloc(height * width);

	yBuff = (unsigned char*)malloc(height * width);
	uBuff = (unsigned char*)malloc((height * width) / 4);
	vBuff = (unsigned char*)malloc((height * width) / 4);
	//判断图像格式
	if (File_header.IMAGETYPE == 1)
	{
		cout << "未压缩的颜色表图像" << endl;
		cout << "颜色表长度:" <<int(cm_header.CMLENGTH) << endl;
		cout << "颜色表项位数:" << int(cm_header.CMNUMBER) << endl;
		cout << "像素深度:" << int(Info_header.PIXEDEPTH) << endl;
		if (File_header.IMAGEINFOLENGTH == 0)
			cout << "无图像信息字段" << endl;
		else
		{
			int imagelength = File_header.IMAGEINFOLENGTH;
			cout << "图像信息字段长度为" << imagelength << endl;
		}
		ReadRGB(tgaFile, File_header, cm_header, Info_header, rBuff, gBuff, bBuff);
	}
	else if (File_header.IMAGETYPE == 2)
	{
		cout << "未压缩的真彩色图像" << endl;
		if (File_header.IMAGEINFOLENGTH == 0)
		{
			cout << "无图像信息字段" << endl;
			ReadRGB(tgaFile, File_header, cm_header, Info_header, rBuff, gBuff, bBuff);
		}
		else
		{
			int imagelength = File_header.IMAGEINFOLENGTH;
			cout << "图像信息字段长度为" << imagelength << endl;
			ReadRGB(tgaFile, File_header, cm_header, Info_header, rBuff, gBuff, bBuff);
		}		
	}
	else
	{
		cout << "其他图像类型" << endl;
		return 0;
	}

	RGB2YUV(width, height, rBuff, gBuff, bBuff, yBuff, uBuff, vBuff);

	WriteYUV(yBuff, uBuff, vBuff, width * height, yuvFile);

	free(rBuff);
	free(gBuff);
	free(bBuff);
	free(yBuff);
	free(uBuff);
	free(vBuff);
	tgaFile.close();
	yuvFile.close();

	return 0;
}

tga2yuv.cpp

#include "tga.h"

void ReadRGB(ifstream& pFile, TGAHEAD & file_h, TGA_cm_header& cm_h,TGA_Info_header& info_h, unsigned char* rdata, unsigned char* gdata, unsigned char* bdata)
{
	int  w, h, CL;
	CL = cm_h.CMLENGTH;
	unsigned char* r_table = new unsigned char[cm_h.CMLENGTH];
	unsigned char* g_table = new unsigned char[cm_h.CMLENGTH];
	unsigned char* b_table = new unsigned char[cm_h.CMLENGTH];
	w = info_h.IMAGEWIDTH;
	h = info_h.IMAGEHEIGHT;
	int size = w * h;
	unsigned char* image = new unsigned char[size];


	if (file_h.COLORMAPTYPE == 0)
	{
		for (int i = h - 1; i >= 0;i--) {
			for (int j = 0; j < w ; j++)
			{
				pFile.read((char*)(bdata + (i * w + j)), 1);
				pFile.read((char*)(gdata + (i * w + j)), 1);
				pFile.read((char*)(rdata + (i * w + j)), 1);
			}
		}
	}
	else if (file_h.COLORMAPTYPE == 1)
	{
		for (int i = 0; i < cm_h.CMLENGTH; i++)//读取颜色表
		{
			pFile.read((char*)(b_table + i), 1);
			pFile.read((char*)(g_table + i), 1);
			pFile.read((char*)(r_table + i), 1);
		}
		for (int i = h - 1; i >= 0; i--) {
			for (int j = 0; j < w; j++)
			{
				pFile.read((char*)image + (i * w + j), 1);
				*(bdata + (i * w + j)) = *(b_table + (*(image + (i * w + j))));
				*(gdata + (i * w + j)) = *(g_table + (*(image + (i * w + j))));
				*(rdata + (i * w + j)) = *(r_table + (*(image + (i * w + j))));
			}
		}
	}
	delete[] r_table; 
	delete[] g_table; 
	delete[] b_table; 
	delete[] image; 
}
      


float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
float RGBYUV01684[256], RGBYUV03316[256], RGBYUV05000[256];
float RGBYUV04187[256], RGBYUV00813[256];

void initLookupTable();
void RGB2YUV(int w, int h, unsigned char* rdata, unsigned char* gdata, unsigned char* bdata, unsigned char* y, unsigned char* u, unsigned char* v)
{
	initLookupTable();//初始化查找表
	unsigned char* utemp = NULL;
	unsigned char* vtemp = NULL;
	utemp = (unsigned char*)malloc(w * h );
	vtemp = (unsigned char*)malloc(w * h );
	unsigned char  nr, ng, nb;
	float temp;
	//对每个像素进行 rgb -> yuv的转换
	for (int i = 0;i < w * h; i++)
	{
		nb = *(bdata + i);
		ng = *(gdata + i);
		nr = *(rdata + i);
		temp = (unsigned char)(RGBYUV02990[nr] + RGBYUV05870[ng] + RGBYUV01140[nb]);
		if (temp < 16) { temp = 16; }
		if (temp > 235) { temp = 235; }
		*(y + i) = (unsigned char)temp;
		temp = (unsigned char)(-RGBYUV01684[nr] - RGBYUV03316[ng] + RGBYUV05000[nb]+ 128);
		if (temp < 16) { temp = 16; }
		if (temp > 235) { temp = 235; }
		*(utemp + i) = (unsigned char)temp;
		temp = (unsigned char)(RGBYUV05000[nr] - RGBYUV04187[ng] - RGBYUV00813[nb] + 128);
		if (temp < 16) { temp = 16; }
		if (temp > 235) { temp = 235; }
		*(vtemp + i) = (unsigned char)temp;
	}
	
	int k = 0;
	for (int i = 0; i < w * h / 4; i++)
	{
		temp = (utemp[k] + utemp[k + 1] + utemp[k + w] + utemp[w + k + 1]) / 4.0;
		*(u + i) = (unsigned char)temp;
		temp = (vtemp[k] + vtemp[k + 1] + vtemp[w + k] + vtemp[w + k + 1]) / 4.0;
		*(v + i) = (unsigned char)temp;
		if ((i + 1) % (w / 2) == 0) {
			k = k + 2 + w;
		}
		else
			k = k + 2;
	}
	free(utemp);
	free(vtemp);
}

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;
		RGBYUV05000[i] = (float)0.5000 * i;
	}
}

void WriteYUV(unsigned char* Y, unsigned char* U, unsigned char* V, unsigned long size, ofstream& outFile)
{
	outFile.write((char*)Y, size);
	outFile.write((char*)U, size/4);
	outFile.write((char*)V, size/4);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值