任务:将tga文件转换为yuv文件
 由两个部分组成:文件头、图像/颜色表数据。
由5个部分组成:文件头、图像/颜色表数据、开发者自定义区域、扩展区域和文件尾。
其中,v2.0的版本中文件头和图像/颜色表数据与v1.0保持一致。
在整个任务的完成过程中,后续的开发者自定义区域、扩展区域、文件尾的数据并没有进行读入与处理,因为对于 Y U V YUV YUV文件的格式转换来说,只需要 R 、 G 、 B R、G、B R、G、B三个分量的数值从而计算出 Y 、 U 、 V Y、U、V Y、U、V即可。
图片来源TGA文件格式深度解析(一)
文件头的定义格式(来自TGA格式说明):
由上述准备知识,我们就可以进行 T G A 2 Y U V TGA2YUV TGA2YUV的具体实现了。
关于 Y U V YUV YUV文件格式的介绍请参照数据压缩(五)——彩色空间转换(完整版)
以下是整个工程的实现逻辑:
在具体实现的过程中,值得注意的一点是 T G A TGA TGA文件图像数据的存储不是从左上方第一个像素点开始一行一行进行,而是从左下方最后一行的第一个像素点开始倒着往上存,在进行图像数据到 R G B RGB RGB数据转换的过程中要记得数据的重排列。
项目的解决方案资源管理器界面如下:
#include
#include
#include
#include
#include "tga2yuv.h"
#include "rgb2yuv.h"
using namespace std;
typedef struct _TgaHeader
{
BYTE IDLength; /* 00h Size of Image ID field */
BYTE ColorMapType; /* 01h Color map type */
BYTE ImageType; /* 02h Image type code */
}TGAHEAD;
typedef struct _TgaHeaderCMap
{
WORD CMapStart; /* 03h Color map origin */
WORD CMapLength; /* 05h Color map length */
BYTE CMapDepth; /* 07h Depth of color map entries */
}TGAHEADCMAP;
typedef struct _TgaHeaderImage
{
WORD XOffset; /* 08h X origin of image */
WORD YOffset; /* 0Ah Y origin of image */
WORD Width; /* 0Ch Width of image */
WORD Height; /* 0Eh Height of image */
BYTE PixelDepth; /* 10h Image pixel size */
BYTE ImageDescriptor; /* 11h Image descriptor byte */
}TGAHEADIMG;
int main()
{
ifstream TgaFile("png_tga.tga", ios::binary);
ofstream YuvFile("png_tga.yuv", ios::binary);
ofstream RgbFile("png_tga.rgb", ios::binary);
if (!TgaFile) { cout << "error to open fileTga!" << endl; }
if (!YuvFile) { cout << "error to open fileYuv!" << endl; }
if (!RgbFile) { cout << "error to open fileRgb!" << endl; }
TGAHEAD tga_header;
TGAHEADCMAP tga_header_cmap;
TGAHEADIMG tga_header_img;
//int size1 = sizeof(tga_header);
//int size2 = sizeof(tga_header_cmap);
//int size3=sizeof(tga_header_img);
TgaFile.read((char*)(&tga_header), 3);
TgaFile.read((char*)(&tga_header_cmap), 5);
TgaFile.read((char*)(&tga_header_img), 10);
unsigned char* ImageData = NULL;
unsigned char* b = NULL;
unsigned char* g = NULL;
unsigned char* r = NULL;
unsigned char* RGB = NULL;
unsigned char* YUV = NULL;
unsigned char* Cmap = NULL;
unsigned char* CmapB = NULL;
unsigned char* CmapG = NULL;
unsigned char* CmapR = NULL;
if (tga_header.IDLength != 0)
{
unsigned char* ImageID = new unsigned char[tga_header.IDLength];
TgaFile.read((char*)ImageID, tga_header.IDLength);
delete[] ImageID;
}
if (tga_header.ColorMapType != 0)//带颜色表
{
if (tga_header.ImageType == 0)
exit(0);
else if (tga_header.ImageType == 1)//未压缩的颜色表图像
{
int CMapDepth = tga_header_cmap.CMapDepth;//颜色表的位数
int depth = tga_header_img.PixelDepth;//图像深度
int width = tga_header_img.Width;
int height = tga_header_img.Height;
int length = tga_header_cmap.CMapLength;
int CMapSize = CMapDepth * length / 8;
Cmap = new unsigned char[CMapSize];
CmapB = new unsigned char[length];
CmapG = new unsigned char[length];
CmapR = new unsigned char[length];
TgaFile.read((char*)Cmap, CMapSize);
SEPARATE_COLORMAP(Cmap, CmapB, CmapG, CmapR, CMapDepth, length);//读取并分离颜色表数据
cout << "该文件是未压缩的颜色表图像" << endl;
cout << "该文件颜色表每个元素的大小为" << CMapDepth << "bit" << endl;
cout << "该文件颜色表的总数为" << length << endl;
cout << "图像分辨率为" << width << "×" << height << endl;
cout << "每个像素点由" << depth << "bit表示" << endl;
int size = width * height * depth / 8;//整个图像块的大小
int lsize = width * height * 3;
ImageData = new unsigned char[size];
TgaFile.read((char*)ImageData, size);
b = new unsigned char[width * height];
g = new unsigned char[width * height];
r = new unsigned char[width * height];
RGB = new unsigned char[width * height * 3];
YUV = new unsigned char[width * height * 3];//读取TGA的图像数据
TGA2BGR(width, height, depth, ImageData, b, g, r, CmapB, CmapG, CmapR);
WRITEBGR(b, g, r, height, width, RGB);
RgbFile.write((char*)RGB, lsize);
rgb2yuv(b,g,r, YUV, width, height);
YuvFile.write((char*)YUV, lsize);
}
}
else if (tga_header.ColorMapType == 0)//不带颜色表
{
if (tga_header.ImageType == 0)
exit(0);
else if (tga_header.ImageType == 2)//未压缩的真彩色图像
{
int depth = tga_header_img.PixelDepth;
int width = tga_header_img.Width;
int height = tga_header_img.Height;
int size = width * height * depth / 8;//整个图像块的大小
int lsize = width * height * 3;
cout << "该文件是未压缩的真彩色图像" << endl;
cout << "图像分辨率为" << width << "×" << height << endl;
cout << "像素深度为" << depth << "bit" << endl;
ImageData = new unsigned char[size];
TgaFile.read((char*)ImageData, size);
b = new unsigned char[width * height];
g = new unsigned char[width * height];
r = new unsigned char[width * height];
RGB = new unsigned char[width * height * 3];
YUV = new unsigned char[width * height * 3];
NTGA2BGR(width, height, depth, ImageData, b, g, r);
WRITEBGR(b, g, r, height, width,RGB);
RgbFile.write((char*)RGB, lsize);
rgb2yuv(b,g,r, YUV, width, height);
YuvFile.write((char*)YUV, lsize);
}
}
else
exit(0);
delete[] ImageData;
delete[] b;
delete[] g;
delete[] r;
delete[] RGB;
RgbFile.close();
YuvFile.close();
TgaFile.close();
return 0;
}
#pragma once
void rgb2yuv(unsigned char* b, unsigned char* g,unsigned char* r,unsigned char* yuv, int width,int height);
#pragma once
#include "rgb2yuv.h"
#include
using namespace std;
void rgb2yuv(unsigned char* b, unsigned char* g, unsigned char* r, unsigned char* yuv, int width, int height)
{
/*unsigned char r, g, b, y, u, v;*/
/*int j = 0;*/
int size = width * height;
int k = 0;
for (int i = height - 1; i >= 0; i--)
{
for (int j = 0; j < width; j++)
{
yuv[k] = ((66 * r[i*width+j] + 129 * g[i * width + j] + 25 * b[i * width + j]) >> 8) + 16;
yuv[k + size] = ((-38 * r[i * width + j] - 74 * g[i * width + j] + 112 * b[i * width + j]) >> 8) + 128;
yuv[k + 2 * size] = ((112 * r[i * width + j] - 94 * g[i * width + j] - 18 * b[i * width + j]) >> 8) + 128;
k++;
}
}
}
#pragma once
void NTGA2BGR(int width, int height, int depth, unsigned char* ImageData, unsigned char* b, unsigned char* g, unsigned char* r);
void WRITEBGR(unsigned char* b, unsigned char* g, unsigned char* r, int height, int width,unsigned char* RGB);
void SEPARATE_COLORMAP(unsigned char* Cmap, unsigned char* CmapB, unsigned char* CmapG, unsigned char* CmapR, int CMapDepth,int length);
void TGA2BGR(int width, int height, int depth, unsigned char* ImageData, unsigned char* b, unsigned char* g, unsigned char* r, unsigned char* CmapB, unsigned char* CmapG, unsigned char* CmapR);
#include
#include
#include
#include "tga2yuv.h"
using namespace std;
void NTGA2BGR(int width, int height, int depth, unsigned char* ImageData, unsigned char* b, unsigned char* g, unsigned char* r)
{
int size = width * height * depth / 8;//整个图像块的大小
int j = 0;
switch (depth)
{
case 16:
j = 0;
for (int i = 0; i < width * height; i++)
{
b[i] = (ImageData[j] & 0x1F) << 3;
g[i] = ((ImageData[j + 1] & 0x03) << 6) + ((ImageData[j] & 0xE0) >> 2);
r[i] = ((ImageData[j + 1] & 0x7C)) << 1;
j = j + 2;
}
break;
case 24:
j = 0;
for (int i = 0; i < width * height; i++)
{
b[i] = ImageData[j];
g[i] = ImageData[j + 1];
r[i] = ImageData[j + 2];
j = j + 3;
}
break;
case 32:
j = 0;
for (int i = 0; i < width * height; i++)
{
b[i] = ImageData[j];
g[i] = ImageData[j + 1];
r[i] = ImageData[j + 2];
j = j + 4;
}
break;
default:
break;
}
}
void WRITEBGR(unsigned char* b, unsigned char* g, unsigned char* r, int height, int width,unsigned char* RGB)
{
int k = 0;
for (int i = height-1; i >= 0; i--)
{
for (int j = 0; j < width; j++)
{
RGB[k] = b[j + i * width];
RGB[k+1] = g[j + i * width];
RGB[k+2] = r[j + i * width];
k = k + 3;
}
}
}
void SEPARATE_COLORMAP(unsigned char* Cmap, unsigned char* CmapB, unsigned char* CmapG, unsigned char* CmapR, int CMapDepth,int length)
{
int j = 0;
switch (CMapDepth)
{
case 8:
break;
case 16:
break;
case 24:
j = 0;
for (int i = 0; i < length; i++)
{
CmapB[i] = Cmap[j];
CmapG[i] = Cmap[j + 1];
CmapR[i] = Cmap[j + 2];
j = j + 3;
}
break;
case 32:
j = 0;
for (int i = 0; i < length; i++)
{
CmapB[i] = Cmap[j];
CmapG[i] = Cmap[j + 1];
CmapR[i] = Cmap[j + 2];
j = j + 4;
}
break;
default:
break;
}
}
void TGA2BGR(int width, int height, int depth, unsigned char* ImageData, unsigned char* b, unsigned char* g, unsigned char* r, unsigned char* CmapB, unsigned char* CmapG, unsigned char* CmapR)
{
int size = width * height * depth / 8;//整个图像块的大小
switch (depth)
{
case 8://一个字节之内就能存
for (int i = 0; i < size; i++)
{
b[i] = CmapB[ImageData[i]];
g[i] = CmapG[ImageData[i]];
r[i] = CmapR[ImageData[i]];
}
break;
case 16:
for (int i = 0; i < size ; )
{
int k = (ImageData[i] << 8) + ImageData[i + 1];
b[i] = CmapB[k];
g[i] = CmapG[k];
r[i] = CmapR[k];
i = i + 2;
}
break;
default:
break;
}
}
实验采用了三种不同格式的 T G A TGA TGA文件,由于 T G A TGA TGA文件由 P h o t o s h o p Photoshop Photoshop,打开, Y U V YUV YUV文件由 Y U V v i e w e r P l u s YUVviewerPlus YUVviewerPlus打开,最后经过截图得到,所以呈现出来的两种图片大小不太一样,但实际上图片的大小并没有被改变。
tga文件 | yuv文件 | 运行结果截图 |
---|---|---|
tga文件 | yuv文件 | 运行结果截图 |
---|---|---|
tga文件 | yuv文件 | 运行结果截图 |
---|---|---|
至此,实验结束。