一.实验要求
了解TGA文件的存储格式,将TGA格式文件转换为YUV格式。
二.TGA的文件解析
1.TGA文件头共分为五个部分:TGA文件头、图像/颜色表数据、开发者区域、拓展区域、TGA文件注脚
2.TGA文件头(共18个字节):
字段1:uint8_t idLength 用来存储图像/颜色表数据中的第一个字段图像信息字段的长度
字段2:uint8_t colorMapType 颜色表类型,用来表示颜色表的有无,0表示无调色板,1表示有
字段3:uint8_t ImageType 图像类型
字节内容 | 对应类型 |
---|---|
0 | 无图像数据 |
1 | 无压缩有调色板的图像 |
2 | 无压缩无调色板的RGB图像 |
3 | 无压缩黑白图像 |
9 | 有调色板的RLE编码图像 |
10 | 有压缩(RLE)RGB真彩图像 |
11 | 有压缩的黑白图像 |
32 | 使用Huffman、Delta、RLE编码的有调色板图像 |
33 | 使用Huffman、Delta、RLE编码的有调色板图像四趟四叉树类型处理 |
字段4:uint16_t FirstColIndex 颜色表首地址
字段5:uint16_t ColorMapLength 颜色表长度
字段6:uint8_t ColorMapPerEnSize 颜色表每个表项占用的位数
字段7:uint16_t FirstX x的起始位置
字段8:uint16_t FirstY y的起始位置
字段9:uint16_t Width 图像宽度
字段10:uint16_t Height 图像高度
字段11:uint8_t IndexDipth 像素深度
字段12:uint8_t ImageDescrip 图像描述符
3.图像/颜色表数据:
字段1:可变长 ImageID 存储图像身份信息,其长度由文件头第一个字段决定
字段2:可变长 ColorMapdata 若文件头字段2为0则该字段不存在,否则由文件头中的字段5和字段6共同决定
字段3:可变长 ImageData 这个字段包含Width*Height个像素,每个像素的格式:用伪彩色表示,则是一个颜色图的一个索引号;真彩图,则是属性,红,绿,蓝顺序的数据;用调配色表示,就是独立的颜色表索引。
*TGA文件中字序为低位在前高位在后,像素存储的起始点为最后一行的第一个像素,即图片左下角像素
*后三个区域本次实验中不需要使用,在此不做说明
三.实验过程
1.游程编码(RLE)的说明:游程编码后的图像数据,数据包有两种类型,第一种类型为连续的不同像素,第二种类型为连续的相同像素,每个包中第一字节作为该包的header,除第一个字节外其余字节为该包的数据data。header的首位为标志位,该位为0则说明该数据包为第一类数据包,该位为1则说明该数据包为第二类数据包。若为第一类数据包,则header中余下7位为连续的不同像素的个数减一,data中存储所有像素的数据,若为第二类数据包,则header中余下7位为连续的相同像素个数减一,data中只存储一遍该相同像素对应的数据。
2.实验代码:
1)tgaheader.h
#include<stdio.h>
#include<stdint.h>
struct TGAHEADER
{
uint8_t idLength;//存储图像/颜色表数据中的第一个字段图像信息字段的长度
uint8_t colorMapType;//颜色表类型0表示无颜色表1表示有颜色表
uint8_t ImageType;//图像类型
uint16_t FirstColIndex;//颜色表首地址
uint16_t ColorMapLength;//颜色表长度
uint8_t ColorMapPerEnSize;//颜色表每个表项占用的位数
uint16_t FirstX;//x的起始位置
uint16_t FirstY;//y的起始位置
uint16_t Width;//图像宽度
uint16_t Height;//图像高度
uint8_t IndexDipth;//像素深度
uint8_t ImageDescrip;//图像描述符
};
void Type0A(FILE* fp_tga,unsigned char* YUV_buffer,int size,int dipth,int height,int width,FILE* fp_rgb,FILE* fp_yuv);
2)type0A.cpp
#include<stdio.h>
static float r0299[256],g0587[256],b0114[256],r01684[256],g03316[256],b05[256],r05[256],g04187[256],b00813[256];
void Table()
{
for(int i=0;i<256;i++) r0299[i]=0.299*i;
for(int i=0;i<256;i++) g0587[i]=0.587*i;
for(int i=0;i<256;i++) b0114[i]=0.114*i;
for(int i=0;i<256;i++) r01684[i]=0.1684*i;
for(int i=0;i<256;i++) g03316[i]=0.3316*i;
for(int i=0;i<256;i++) b05[i]=0.5*i;
for(int i=0;i<256;i++) r05[i]=0.5*i;
for(int i=0;i<256;i++) g04187[i]=0.4187*i;
for(int i=0;i<256;i++) b00813[i]=0.0813*i;
}
void Type0A(FILE* fp_tga,unsigned char* YUV__buffer,int size,int dipth,int height,int width,FILE* fp_rgb,FILE* fp_yuv)
{
//Table();
unsigned char data_header;
int count;
unsigned char *data,*data_del;
unsigned char *data_ori;
data=new unsigned char[size*3];
data_ori=data;
data_del=data;
int size_temp=size;
while(size_temp>0)
{
fread(&data_header,sizeof(unsigned char),1,fp_tga);
if((data_header&0x80)==0)
{
count=data_header+1;
fread(data,3*sizeof(unsigned char),count,fp_tga);
data+=3*count;
size_temp-=count;
}
if((data_header&0x80)==0x80)
{
count=int(data_header-128+1);
unsigned char temp[3];
fread(&temp,3*sizeof(unsigned char),1,fp_tga);
while(count)
{
*(data)=temp[0];
data++;
*(data)=temp[1];
data++;
*(data)=temp[2];
data++;
count--;
size_temp--;
}
}
}
/*fwrite(data_ori,3*sizeof(unsigned char),size,fp_rgb);
fclose(fp_rgb);*/
unsigned char *data_pseq,*data_pseq_ori;
data_pseq=new unsigned char[size*3];
//将图像上下颠倒(参考了往届同学的写法)
for(int i=0;i<height;i++)
{
for(int j=0;j<width*3;j=j+3)
{
data_pseq[i*width*3+j]=data_ori[(height-i-1)*width*3+j];
data_pseq[i*width*3+j+1]=data_ori[(height-i-1)*width*3+j+1];
data_pseq[i*width*3+j+2]=data_ori[(height-i-1)*width*3+j+2];
}
}
fwrite(data_pseq_ori,3*sizeof(unsigned char),size,fp_rgb);
fclose(fp_rgb);
unsigned char *RGB__buffer;
RGB__buffer=data_pseq;
//以下为之前RGB转YUV的代码段
Table();
unsigned char *b_buffer,*g_buffer,*r_buffer;
unsigned char *Y_buffer,*U_buffer,*V_buffer;
unsigned char *u_buffer,*v_buffer;
unsigned char *pu_up1,*pu_up2,*pv_up1,*pv_up2;
Y_buffer=YUV__buffer;
U_buffer=YUV__buffer+height*width;
V_buffer=YUV__buffer+height*width*5/4;
u_buffer=new unsigned char[height*width];
v_buffer=new unsigned char[height*width];
unsigned char *u_buffer_del,*v_buffer_del;
u_buffer_del=u_buffer;
v_buffer_del=v_buffer;
pu_up1=u_buffer;
pu_up2=u_buffer+width;
pv_up1=v_buffer;
pv_up2=v_buffer+width;
b_buffer=RGB__buffer;
g_buffer=RGB__buffer+1;
r_buffer=RGB__buffer+2;
unsigned char *pu_buffer,*pv_buffer;
pu_buffer=u_buffer;
pv_buffer=v_buffer;
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
if(r0299[*r_buffer]+g0587[*g_buffer]+b0114[*b_buffer]<0)
*Y_buffer=(unsigned char)0;
else
if(r0299[*r_buffer]+g0587[*g_buffer]+b0114[*b_buffer]>255)
*Y_buffer=(unsigned char)255;
else *Y_buffer=unsigned char (r0299[*r_buffer]+g0587[*g_buffer]+b0114[*b_buffer]);
if(-r01684[*r_buffer]-g03316[*g_buffer]+b05[*b_buffer]<-128)
*pu_buffer=(unsigned char)0;
else
if(-r01684[*r_buffer]-g03316[*g_buffer]+b05[*b_buffer]>128)
*pu_buffer=(unsigned char)255;
else *pu_buffer=unsigned char(-r01684[*r_buffer]-g03316[*g_buffer]+b05[*b_buffer]+128);
if(r05[*r_buffer]-g04187[*g_buffer]-b00813[*b_buffer]<-128)
*pv_buffer=(unsigned char)0;
else
if(r05[*r_buffer]-g04187[*g_buffer]-b00813[*b_buffer]>128)
*pv_buffer=(unsigned char)255;
else *pv_buffer=unsigned char(r05[*r_buffer]-g04187[*g_buffer]-b00813[*b_buffer]+128);
Y_buffer++;
pu_buffer++;
pv_buffer++;
r_buffer+=3;
g_buffer+=3;
b_buffer+=3;
}
}
for(int i=0;i<height/2;i++)
{
for(int j=0;j<width/2;j++)
{
*U_buffer=(*pu_up1+*(pu_up1+1)+*pu_up2+*(pu_up2+1))/4;
*V_buffer=(*pv_up1+*(pv_up1+1)+*pv_up2+*(pv_up2+1))/4;
U_buffer++;
V_buffer++;
pu_up1+=2;
pu_up2+=2;
pv_up1+=2;
pv_up2+=2;
}
if(i!=height/2)
{
pu_up1+=width;
pu_up2+=width;
pv_up1+=width;
pv_up2+=width;
}
}
delete[] u_buffer_del;
delete[] v_buffer_del;
fwrite(YUV__buffer,3*sizeof(unsigned char),size*3/2,fp_yuv);
fclose(fp_yuv);
delete[] data_del;
}
3)主函数
#include<stdio.h>
#include<stdlib.h>
#include"tgaheader.h"
int main(int argc,char** argv)
{
char *filename_tga,*filename_yuv,*filename_rgb;
filename_tga=argv[1];
filename_yuv=argv[2];
filename_rgb=argv[3];
unsigned char *YUV_buffer,*Y_buffer,*U_buffer,*V_buffer;
TGAHEADER header;
FILE *fp_tga,*fp_yuv,*fp_rgb;
fp_tga=fopen(filename_tga,"rb");
fp_yuv=fopen(filename_yuv,"wb");
fp_rgb=fopen(filename_rgb,"wb");
fread(&header.idLength,sizeof(uint8_t),1,fp_tga);
fread(&header.colorMapType,sizeof(uint8_t),1,fp_tga);
fread(&header.ImageType,sizeof(uint8_t),1,fp_tga);
fread(&header.FirstColIndex,sizeof(uint16_t),1,fp_tga);
fread(&header.ColorMapLength,sizeof(uint16_t),1,fp_tga);
fread(&header.ColorMapPerEnSize,sizeof(uint8_t),1,fp_tga);
fread(&header.FirstX,sizeof(uint16_t),1,fp_tga);
fread(&header.FirstY,sizeof(uint16_t),1,fp_tga);
fread(&header.Width,sizeof(uint16_t),1,fp_tga);
fread(&header.Height,sizeof(uint16_t),1,fp_tga);
fread(&header.IndexDipth,sizeof(uint8_t),1,fp_tga);
fread(&header.ImageDescrip,sizeof(uint8_t),1,fp_tga);
int size=(header.Height)*(header.Width);
if(size%4!=0)
{
printf("can not change into YUV\n");
exit(1);
}
YUV_buffer=new unsigned char[3*size/2];
unsigned char *YUV_buffer_del;
YUV_buffer_del=YUV_buffer;
Y_buffer=YUV_buffer;
U_buffer=YUV_buffer+size;
V_buffer=YUV_buffer+5*size/4;
int type=header.ImageType;
if(type==0x0A)
Type0A(fp_tga,YUV_buffer,size,header.IndexDipth,header.Height,header.Width,fp_rgb,fp_yuv);
//fwrite(YUV_buffer,sizeof(unsigned char),size*5/4,fp_yuv);
fclose(fp_tga);
//fclose(fp_yuv);
delete[] YUV_buffer_del;
return 0;
}
四.实验结果
原图:
转换后的图像: