一、TGA文件介绍
1.简介
TGA(Targa)格式是计算机上应用最广泛的图象格式。在兼顾了BMP的图象质量的同时又兼顾了JPEG的体积优势。并且还有自身的特点:通道效果、方向性。在CG领域常作为影视动画的序列输出格式,因为兼具体积小和效果清晰的特点。
TGA的结构比较简单,属于一种图形、图像数据的通用格式,在多媒体领域有很大影响,是计算机生成图像向电视转换的一种首选格式。TGA图像格式最大的特点是可以做出不规则形状的图形、图像文件,一般图形、图像文件都为四方形,若需要有圆形、菱形甚至是缕空的图像文件时,TGA就可以派上用场了。
2.TGA文件存储格式
1)是以小端模式存储的,包括规格定义和RGB数据,也就是BGR形式存放。
2)看到的图片的左上角一行像素可能放在文件内存中的最后一行(图片y轴可能是世界坐标系的和屏幕坐标系相反),或者文件内存中的第一行就是左上角一行像素。
3) TGA头文件记录了调色板偏移大小和规格,和图像像素数据的偏移大小和规格。
4) TGA分为压缩编码的和非压缩编码的,压缩算法是采用了RLE算法(由于个人能力不足,本文暂时不涉及RLE压缩算法)
3.文件格式
TGA文件具体格式如下:
二、TGA to YUV的具体实现
1.编程思路
在实现之前,先写了一个思维导图架构了一下工程逻辑:
2.源代码
header.h:
#pragma once
#include <iostream>
typedef struct
{
char IdLength = 0;
char ColourMapType = 0;
char ImageTypeCode = 0;
short ColourMapOffset = 0;
short ColourMapLength = 0;
char ColourMapDepth = 0;
short X_ori = 0;
short Y_ori = 0;
short Width = 0;
short Height = 0;
char BitsPerPixel = 0;
char ImageDescriptor = 0;
} HEADER;
typedef struct
{
unsigned char R, G, B, A;
} DATA;
void ReadTGAHeader(HEADER* TGAHdPtr, FILE* TGAPtr);
void ReadColourData(HEADER* TGAHdPtr, DATA* ColourData, FILE* TGAPtr);
void Transform2RGB(DATA* ColourData, unsigned char* RGBBuff, HEADER* TGAHdPtr);
void RGB2YUV(unsigned char* RGB, unsigned char* Y,
unsigned char* U, unsigned char* V, int Width, int Height);
main.cpp:
#include <iostream>
#include <cstdio>
#include <fstream>
#include <windows.h>
#include "header.h"
using namespace std;
int main(int argc, char** argv)
{
FILE* TGAFile, * yuvFile;
HEADER Hd;
int Width, Height, pxCount;
char* TGAFileName = NULL;
char* yuvFileName = NULL;
TGAFileName = argv[1];
yuvFileName = argv[2];
if (fopen_s(&TGAFile, TGAFileName, "rb") == 0)
{
cout << "File opened! " << TGAFileName << "." << endl;
}
else
{
cout << "File not opened! " << TGAFileName << "." << endl;
exit(0);
}
if (fopen_s(&yuvFile, yuvFileName, "wb+") == 0)
{
cout << "File opened! " << yuvFileName << "." << endl;
}
else
{
cout << "File not opened! " << yuvFileName << "." << endl;
exit(0);
}
ReadTGAHeader(&Hd, TGAFile);
Width = Hd.Height;
Height = Hd.Width;
pxCount = Width * Height;
unsigned char* RGB = (unsigned char*)malloc(sizeof(unsigned char) * (Width * Height * 3));
unsigned char* Y, * U, * V;
unsigned char* R, * G, * B;
R = (unsigned char*)malloc(sizeof(unsigned char) * (Width * Height));
G = (unsigned char*)malloc(sizeof(unsigned char) * (Width * Height));
B = (unsigned char*)malloc(sizeof(unsigned char) * (Width * Height));
Y = (unsigned char*)malloc(sizeof(char) * (Width * Height));
U = (unsigned char*)malloc(sizeof(char) * (Width * Height / 4));
V = (unsigned char*)malloc(sizeof(char) * (Width * Height / 4));
DATA* RGBaData = NULL;
RGBaData = new DATA[Hd.Width * Hd.Height];
memset(RGBaData, 0, Hd.Height * Hd.Width);
RGB = new unsigned char[Hd.Width * Hd.Height * 3];
memset(RGB, 0, Hd.Height * Hd.Width * 3);
int Ost = 0;
Ost += Hd.IdLength;
Ost += Hd.ColourMapType * Hd.ColourMapLength * Hd.ColourMapDepth;
fseek(TGAFile, Ost, SEEK_CUR);
ReadColourData(&Hd, RGBaData, TGAFile);
Width = Hd.Width;
Height = Hd.Height;
for (int i = 0; i < Height; i++)
{
for (int j = 0; j < Width; j++)
{
int RGBPxNum = (Height - 1 - i) * Width + j;
int TGAPxNum = i * Width + j;
RGB[3 * RGBPxNum + 2] = RGBaData[TGAPxNum].R;
RGB[3 * RGBPxNum + 1] = RGBaData[TGAPxNum].G;
RGB[3 * RGBPxNum] = RGBaData[TGAPxNum].B;
}
}
RGB2YUV(RGB, Y, U, V, Width, Height);
free(RGB);
fwrite(Y, sizeof(unsigned char), Width * Height, yuvFile);
fwrite(U, sizeof(unsigned char), Width * Height / 4, yuvFile);
fwrite(V, sizeof(unsigned char), Width * Height / 4, yuvFile);
fclose(yuvFile);
}
ReadTGA.cpp:
#include <iostream>
#include "header.h"
using namespace std;
void ReadTGAHeader(HEADER* TGAHdPtr, FILE* TGAPtr)
{
fread(&TGAHdPtr->IdLength, 1, 1, TGAPtr);
fread(&TGAHdPtr->ColourMapType, 1, 1, TGAPtr);
fread(&TGAHdPtr->ImageTypeCode, 1, 1, TGAPtr);
fread(&TGAHdPtr->ColourMapOffset, 2, 1, TGAPtr);
fread(&TGAHdPtr->ColourMapLength, 2, 1, TGAPtr);
fread(&TGAHdPtr->ColourMapDepth, 1, 1, TGAPtr);
fread(&TGAHdPtr->X_ori, 2, 1, TGAPtr);
fread(&TGAHdPtr->Y_ori, 2, 1, TGAPtr);
fread(&TGAHdPtr->Width, 2, 1, TGAPtr);
fread(&TGAHdPtr->Height, 2, 1, TGAPtr);
fread(&TGAHdPtr->BitsPerPixel, 1, 1, TGAPtr);
fread(&TGAHdPtr->ImageDescriptor, 1, 1, TGAPtr);
if (TGAHdPtr->ImageTypeCode != 2 && TGAHdPtr->ImageTypeCode != 10)
{
cout << "本程序仅支持 Image Type Code 为 2(无压缩、无调色板的RGB图像)或 10(游程编码的RGB图像)\n";
exit(-1);
}
if (TGAHdPtr->BitsPerPixel != 16 && TGAHdPtr->BitsPerPixel != 24 && TGAHdPtr->BitsPerPixel != 32)
{
cout << "本程序仅支持 Image pixel size 为 16,24,32 的图像\n";
exit(-1);
}
if (TGAHdPtr->ColourMapType != 0 && TGAHdPtr->ColourMapType != 1)
{
cout << "本程序仅支持 Colour Map Type 为 0 或 1 的图像\n";
exit(-1);
}
cout << "\nTGA header information:\n";
printf("ID length: %d\n", TGAHdPtr->IdLength);
printf("Colour Map type: %d\n", TGAHdPtr->ColourMapType);
printf("Image type code: %d\n", TGAHdPtr->ImageTypeCode);
printf("Colour map Ost: %d\n", TGAHdPtr->ColourMapOffset);
printf("Colour map length: %d\n", TGAHdPtr->ColourMapLength);
printf("Colour map depth: %d\n", TGAHdPtr->ColourMapDepth);
printf("X origin: %d\n", TGAHdPtr->X_ori);
printf("Y origin: %d\n", TGAHdPtr->Y_ori);
printf("Width: %d\n", TGAHdPtr->Width);
printf("Height: %d\n", TGAHdPtr->Height);
printf("Image pixel size: %d\n", TGAHdPtr->BitsPerPixel);
printf("Descriptor: %d\n\n", TGAHdPtr->ImageDescriptor);
}
RGB2YUV.cpp:
#include<stdio.h>
#include "header.h"
float RGB2YUV02990[256], RGB2YUV05870[256], RGB2YUV01140[256], RGB2YUV01684[256],
RGB2YUV03316[256], RGB2YUV05000[256], RGB2YUV04187[256], RGB2YUV00813[256];
void InitRGB_table() {
for (int i = 0; i < 256; i++)
{
RGB2YUV02990[i] = (float)0.2990 * i;
RGB2YUV05870[i] = (float)0.5870 * i;
RGB2YUV01140[i] = (float)0.1140 * i;
RGB2YUV01684[i] = (float)0.1684 * i;
RGB2YUV03316[i] = (float)0.3316 * i;
RGB2YUV05000[i] = (float)0.5000 * i;
RGB2YUV04187[i] = (float)0.4187 * i;
RGB2YUV00813[i] = (float)0.0813 * i;
}
}
void RGB2YUV(unsigned char* RGB, unsigned char* Y, unsigned char* U, unsigned char* V, int Width, int Height)
{
int y = 0;
for (int i = 0; i < Width * Height * 3; i = i + 3)
{
InitRGB_table();
Y[y] = RGB2YUV01140[RGB[i]] + RGB2YUV05870[RGB[i + 1]] + RGB2YUV02990[RGB[i + 2]];
//Y范围:16-235
if (Y[y] > 235)
Y[y] = 235;
if (Y[y] < 16)
Y[y] = 16;
y++;
}
int m = 0, n = 0;
for (int i = 0; i < Width * Height * 3;)
{
unsigned char* rgb;
rgb = new unsigned char[Width * Height * 3];
rgb[i] = (RGB[i] + RGB[i + 3] + RGB[i + Width * 3] + RGB[i + Width * 3 + 3]) / 4;
InitRGB_table();
U[m] = RGB2YUV05000[rgb[i]] - RGB2YUV03316[rgb[i + 1]] - RGB2YUV01684[rgb[i + 2]] + 128;
V[n] = -RGB2YUV00813[rgb[i]] - RGB2YUV04187[rgb[i + 1]] * +RGB2YUV05000[rgb[i + 2]] + 128;
m++;
n++;
//U,V范围:16-240
if (U[m] > 240)
U[m] = 240;
if (U[m] < 16)
U[m] = 16;
if (V[n] > 240)
V[n] = 240;
if (V[n] < 16)
V[n] = 16;
if ((i / 3) % 256 == 254)
i = i + 258 * 3;
else
i = i + 6;
delete[]RGB;
}
}
三、实验结果
原图:
运行结果:
16位 | 24位 | 32位 |
---|---|---|