本次试验通过C++,实现将TGA文件转换为YUV文件。目前只支持非压缩无颜色表24、32位格式以及非压缩有颜色表格式的转换。
一、基本原理
1. TGA文件结构
具体参见:TGA文件格式解析
2. RGB2YUV彩色空间转换基本原理
具体参见:C++实现RGB2YUV/YUV2RGB
二、试验流程
三、代码实现
//main.cpp
#include <stdio.h>
#include <windows.h>
#include "pch.h"
#include <iostream>
#include "tga2yuv.h"
struct TGA_HEADER
{
unsigned short IDLength;
unsigned short ImageType;
unsigned short ColorMapLen;
unsigned char ColorMapBits;//颜色表项位数
unsigned short Width;
unsigned short Height;
unsigned char bitsPerPixel;//像素位数
}TGAHEARD;
struct COLOR_MAP
{
unsigned char Blue;
unsigned char Green;
unsigned char Red;
};
void STRUCTTGA(FILE *Tfile)
{
unsigned char typeHeader[18] = { 0 };
fread(typeHeader, sizeof(unsigned char), 18, Tfile);//fread改变文件指针的指向位置
TGAHEARD.IDLength = typeHeader[0];
TGAHEARD.ImageType = typeHeader[2];
TGAHEARD.ColorMapLen = (typeHeader[6] << 8) + typeHeader[5];
TGAHEARD.ColorMapBits = typeHeader[7];
TGAHEARD.Width = (typeHeader[13] << 8) + typeHeader[12];
TGAHEARD.Height = (typeHeader[15] << 8) + typeHeader[14];
TGAHEARD.bitsPerPixel = (typeHeader[17] << 8) + typeHeader[16];
}
void COLORMAPDATA(unsigned char *ColorMapBuffer, struct COLOR_MAP *COLORMAP,int offset,int Len)
{
int j = 0;
for (int i = 0; i <Len; i++)
{
COLORMAP[i].Blue=ColorMapBuffer[j];
COLORMAP[i].Green = ColorMapBuffer[j+1];
COLORMAP[i].Red = ColorMapBuffer[j+2];
j += 3;
}
}
int main(int argc, char *argv[])
{
FILE *tgaFile = NULL, *yuvFile = NULL;
char *tgaFileName = argv[1];
char *yuvFileName = argv[2];
unsigned short width = 0, height = 0, pixel = 0,ColorPixel=0;
int offset = 0;
int W = 0;
//打开文件
errno_t err;
if ((err = fopen_s(&tgaFile, tgaFileName, "rb")) == 0)//打开tga文件
printf("The file\t%s\twas opened\n", tgaFileName);
else
printf("The file\t%s\twas not opened\n", tgaFileName);
if ((err = fopen_s(&yuvFile, yuvFileName, "wb")) == 0)//打开yuv文件
printf("The file\t%s\twas opened\n", yuvFileName);
else
printf("The file\t%s\twas not opened\n", yuvFileName);
//数组初始化
STRUCTTGA(tgaFile);
pixel = TGAHEARD.bitsPerPixel / 8;
ColorPixel = TGAHEARD.ColorMapBits / 8;
int T = TGAHEARD.ImageType;
int Len = TGAHEARD.ColorMapLen;
if ((TGAHEARD.Width % 2) == 0)
width = TGAHEARD.Width;
else
width = TGAHEARD.Width; +1;
if ((TGAHEARD.Height % 2) == 0)
height = TGAHEARD.Height;
else
height = TGAHEARD.Height + 1;
printf("W=%d\n", width);
printf("H=%d\n",height);
//四个动态内存区指针
unsigned char* tgaBuffer = NULL;
unsigned char* rgbBuffer = NULL;
unsigned char* yBuffer = NULL;
unsigned char* uBuffer = NULL;
unsigned char* vBuffer = NULL;
unsigned char* ColorBuffer = NULL;
unsigned char *ColorMapBuffer = NULL;
//分配动态内存
yBuffer = (unsigned char *)malloc(width*height);
uBuffer = (unsigned char *)malloc(width*height / 4);
vBuffer = (unsigned char *)malloc(width*height / 4);
int i, j, b = 0, g = 1, r = 2;
if (T == 1)//有颜色表存在
{
tgaBuffer = (unsigned char *)malloc(width*height*ColorPixel);//存tga中取出的bgr信息
rgbBuffer = (unsigned char *)malloc(width*height*ColorPixel);//存上下颠倒后的bgr信息
struct COLOR_MAP *COLORMAP = (struct COLOR_MAP*)malloc(Len*sizeof(struct COLOR_MAP));//*sizeof(COLOR_MAP)
ColorMapBuffer = (unsigned char *)malloc(3*Len);
fseek(tgaFile,18, SEEK_SET);
fread(ColorMapBuffer, sizeof(unsigned char), 3*Len,tgaFile);
COLORMAPDATA(ColorMapBuffer, COLORMAP, offset, Len);//调色盘数据初始化
ColorBuffer = (unsigned char *)malloc(width*height*pixel);
offset = 18 + TGAHEARD.IDLength + TGAHEARD.ColorMapLen * 3;
fseek(tgaFile, offset, SEEK_SET);
fread(ColorBuffer, sizeof(unsigned char), width*height*pixel, tgaFile);
for (i = 0; i < width*height; i++)
{
tgaBuffer[b] = COLORMAP[ColorBuffer[i]].Blue;
tgaBuffer[g] = COLORMAP[ColorBuffer[i]].Green;
tgaBuffer[r] = COLORMAP[ColorBuffer[i]].Red;
b += 3;
g += 3;
r += 3;
}
free(COLORMAP);
//使图像上下颠倒
W = width * ColorPixel;
for (i = 0; i < height; i++)
for (j = 0; j < W; j++)
{
rgbBuffer[W*i + j] = tgaBuffer[W*(height - 1 - i) + j];
}
}
if(T==2)//无颜色表
{
rgbBuffer = (unsigned char *)malloc(width*height*3);
tgaBuffer = (unsigned char *)malloc(width*height*3);
offset = 18 + TGAHEARD.IDLength;
fseek(tgaFile,offset, SEEK_SET);
switch (pixel)
{
case 3: //3字节
fread(tgaBuffer, sizeof(unsigned char), width*height*pixel, tgaFile);
break;
case 4: //4字节
tgaBuffer1 = (unsigned char *)malloc(width*height*pixel);
fread(tgaBuffer1, sizeof(unsigned char), width*height*pixel, tgaFile);
int b0 = 0, b1 = 0;
for(i=0;i<height;i++)
for (j = 0; j < width; j++)
{
tgaBuffer[b0] = tgaBuffer1[b1];
tgaBuffer[b0+1] = tgaBuffer1[b1+1];
tgaBuffer[b0+2] = tgaBuffer1[b1+2];
b0 += 3;
b1 += 4;
}
free(tgaBuffer1);
}
for (i = 0; i < height; i++)
for (j = 0; j <width*3; j++)
{
rgbBuffer[width*3*i+j] = tgaBuffer[width*3*(height-1-i)+j];
}
}
//调用RGB2YUV函数实现转换
if ((RGB2YUV(rgbBuffer, yBuffer, uBuffer, vBuffer, width, height)) == 0)
printf("changed successfully");
else
{
printf("fail");
exit(1);
}
//动态内存写入yuv
fwrite(yBuffer, sizeof(unsigned char), width*height, yuvFile);
fwrite(uBuffer, sizeof(unsigned char), width*height / 4, yuvFile);
fwrite(vBuffer, sizeof(unsigned char), width*height / 4, yuvFile);
//释放内存,关闭文件
free(yBuffer);
free(uBuffer);
free(vBuffer);
if(tgaBuffer)
free(tgaBuffer);
if (ColorBuffer)
free(ColorBuffer);
if(rgbBuffer)
free(rgbBuffer);
if (ColorMapBuffer)
free(ColorMapBuffer);
fclose(tgaFile);
fclose(yuvFile);
system("pause");
return 0;
}
//RGB2YUV.cpp
#include "pch.h"
#include <iostream>
#include "tga2yuv.h"
static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];
int RGB2YUV(void* rgb, void* y_out, void* u_out, void* v_out, int W, int H)
{
unsigned char *b = NULL, *g = NULL, *r = NULL;
unsigned char *y, *u, *v;
b = (unsigned char *)rgb;
y = (unsigned char *)y_out;
u = (unsigned char *)u_out;
v = (unsigned char *)v_out;
InitLookupTable();//调用数据表
int i, j;
//亮度信号转换
for (i = 0; i < W*H; i++)
{
g = b + 1;
r = b + 2;
*y = (unsigned char)(RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
b += 3;
y++;
}
//色差信号转换
b = (unsigned char *)rgb;
for (j = 0; j < H; j++)
{
for (i = 0; i < W; i++)
{
g = b + 1;
r = b + 2;
if (i % 2 == 0 && j % 2 == 0)
{
*u = (unsigned char)(-RGBYUV01684[*r] - RGBYUV03316[*g] + (*b) / 2 + 128);
*v = (unsigned char)((*r) / 2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
u++;
v++;
}
b += 3;
}
}
return 0;
}
void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) RGBYUV02990[i] = (float)0.2990 * i;
for (i = 0; i < 256; i++) RGBYUV05870[i] = (float)0.5870 * i;
for (i = 0; i < 256; i++) RGBYUV01140[i] = (float)0.1140 * i;
for (i = 0; i < 256; i++) RGBYUV01684[i] = (float)0.1684 * i;
for (i = 0; i < 256; i++) RGBYUV03316[i] = (float)0.3316 * i;
for (i = 0; i < 256; i++) RGBYUV04187[i] = (float)0.4187 * i;
for (i = 0; i < 256; i++) RGBYUV00813[i] = (float)0.0813 * i;
}
//tga2yuv.h
int RGB2YUV(void* rgb, void* y_out, void* u_out, void* v_out, int W, int H);
void InitLookupTa
四、结果
图片描述 | tga原图 | yuv图 |
---|---|---|
非压缩有颜色表8位 | ||
非压缩无颜色表24位 | ||
非压缩无颜色表32位 |
以下依次为图1、2、3的运行结果