需求
图像中存储的数据既有可能是 0.0 ~ 1.0 代表颜色强度的值,也有可能是代表 0 ~ 255 的序号。如果是序号,则用普通的观察方式会比较困难,尤其是当序号差别较小时,眼睛是无法区分的,比如:
(不过我并不确定上图中数据的含义是否真的是序号)
因此,我想做一段C++小程序来读取TGA图片中的数据,然后将值输出到文本中。
我最开始参考的是C++读写Tga文件_猫哥-CSDN博客,这里的程序很简洁,但是对于文件头并没有解释,而且似乎只针对于“32位色的无压缩”。我之后又看到了https://github.com/aseprite/tga,这个比较全,也定义了文件头结构体,我的代码中的TGA文件头的定义也拷贝自它。
另外,TGA的wiki上有对这个格式包括文件头更详细的描述。我在附录中贴出wiki上的文件头解释。
代码
#include<iostream>
#include<fstream>
#include <iomanip>
using namespace std;
//TGA文件头
struct TGAHeader
{
uint8_t idLength;
uint8_t colormapType;
uint8_t imageType; //图像类型
uint16_t colormapOrigin;
uint16_t colormapLength;
uint8_t colormapDepth;
uint16_t xOrigin;
uint16_t yOrigin;
uint16_t width; //宽度
uint16_t height; //高度
uint8_t bitsPerPixel; //每像素多少位
uint8_t imageDescriptor;
};
//ImageType对应的含义,来源于Wiki:https://en.wikipedia.org/wiki/Truevision_TGA
string GetImageTypeString(uint8_t imageType)
{
if (imageType == 0) return "no image data is present";
else if (imageType == 1) return "uncompressed color-mapped image";
else if (imageType == 2) return "uncompressed true-color image";
else if (imageType == 3) return "uncompressed black-and-white(grayscale) image";
else if (imageType == 9) return "run-length encoded color-mapped image";
else if (imageType == 10) return "run-length encoded true-color image";
else if (imageType == 11) return "run-length encoded black-and-white(grayscale) image";
}
int main()
{
//TGA文件名:
const string TGAFileName = "PP2_rgb_XVect_a_XExtent.TGA";
//打开TGA文件
FILE* file_tga;
fopen_s(&file_tga, TGAFileName.c_str(), "rb");
//读取文件头
TGAHeader data_header;
{
//fread(&data_header, sizeof(TGAHeader), 1, file_tga);//直接读取会有结构体的对齐问题
fread(&data_header.idLength, sizeof(uint8_t), 1, file_tga);
fread(&data_header.colormapType, sizeof(uint8_t), 1, file_tga);
fread(&data_header.imageType, sizeof(uint8_t), 1, file_tga);
fread(&data_header.colormapOrigin, sizeof(uint16_t), 1, file_tga);
fread(&data_header.colormapLength, sizeof(uint16_t), 1, file_tga);
fread(&data_header.colormapDepth, sizeof(uint8_t), 1, file_tga);
fread(&data_header.xOrigin, sizeof(uint16_t), 1, file_tga);
fread(&data_header.yOrigin, sizeof(uint16_t), 1, file_tga);
fread(&data_header.width, sizeof(uint16_t), 1, file_tga);
fread(&data_header.height, sizeof(uint16_t), 1, file_tga);
fread(&data_header.bitsPerPixel, sizeof(uint8_t), 1, file_tga);
fread(&data_header.imageDescriptor, sizeof(uint8_t), 1, file_tga);
}
//输出文件头的一些信息
{
cout << "Image Type : " << GetImageTypeString(data_header.imageType) << endl;
cout << "每像素位数 : " << (int)data_header.bitsPerPixel << endl;
cout << "宽度 : " << data_header.width << endl;
cout << "高度 : " << data_header.height << endl;
}
if (data_header.bitsPerPixel != 32)//只考虑32位的数据
return 0;
//像素数据从尺寸:
int dataSize = data_header.width * data_header.height * 4;
//读取颜色数据
uint8_t* data_color = new uint8_t[dataSize];
fread(data_color, sizeof(uint8_t), dataSize, file_tga);
//存储到TXT中:
{
//TXT名字:
string TXTFileName = TGAFileName;
TXTFileName.replace(TXTFileName.length() - 3, 3, "txt");
//打开写入的txt文件
ofstream file_txt;
file_txt.open(TXTFileName, ios::out);
for (int h = 0; h < data_header.height; h++)
{
for (int w = 0; w < data_header.width; w++)
{
int index = data_header.width * h + w;
file_txt << "(";
file_txt << setw(3) << (int)data_color[index * 4 + 0];//R
file_txt << ",";
file_txt << setw(3) << (int)data_color[index * 4 + 1];//G
file_txt << ",";
file_txt << setw(3) << (int)data_color[index * 4 + 2];//B
file_txt << ",";
file_txt << setw(3) << (int)data_color[index * 4 + 3];//A
file_txt << ") ";
}
file_txt << endl;
}
file_txt.close();
}
}
最后将输出txt文本:
这样就可以看清具体的值了。
问题记录:结构体对齐问题
一开始出了问题,因为没有考虑到结构体的对齐问题。这个问题在这里【原创&交流】使用标准C库读文件时需要注意的一个问题也有讨论。
比如对于我这里的TGA文件头,其实只有18Byte的尺寸,但是如果以sizeof
输出则有20:
因此最后我选择了单独为每段数据读取。