TGA转换成YUV420

TGA文件分析

TGA简介

TGA是由美国Truevision公司为其显示卡开发的一种图像文件格式,已被国际上的图形、图像工业所接受。现已成为数字化图像,以及运用光线跟踪算法所产生的高质量图像的常用格式。TGA文件的扩展名为.tga,该格式支持压缩,使用不失真的压缩算法,可以带通道图,另外还支持行程编码压缩。
TGA在兼顾了BMP的图象质量的同时又兼顾了JPEG的体积优势。并且还有自身的特点:通道效果、方向性。在CG领域常作为影视动画的序列输出格式,因为兼具体积小和效果清晰的特点。

文件格式分析

TGA原始文件结构(v1.0)由两个部分组成:

  1. 文件头(Tga File Header):由图像描述信息字段长度、颜色表类型、图像类型、颜色表说明和图像说明五个字段组成,总计18字节,描述了图像存储的基本信息,应用程序可依据该部分字段值读写图像数据。
  2. 图像/颜色表数据(Image/Color Map Data):由图像描述信息(可选)、颜色表数据和图像数据三部分组成,用于存储图片的图像信息。

TGA扩展文件结构(v2.0)在此基础上增加了三个部分:

  1. 开发者自定义区域(Developer Area):包含开发者定义字段列表和开发者字典(用于存储开发者定义字段的值),该区域为开发者扩展该文件格式提供接口,以便存储额外的信息。
  2. 扩展区域(Extension Area):由扩展区域大小、作者姓名、作者注释、日期/时间、工作名称/ID、工作累计耗时、编辑软件的名称、编辑软件的版本、关键颜色、像素宽高比、灰度值、颜色校正表偏移量、缩略图偏移量、扫描线表偏移量、alpha通道类型、扫描线表、缩略图图像数据和颜色校正表组成,为Truevision公司定义的标准扩展功能,以提供更多的图像附加信息。
  3. 文件尾(TGA File Footer):由扩展区域偏移量、开发者目录偏移量和TGA文件扩展格式签名三部分组成,用于验证TGA文件扩展格式,并可以确定扩展区域和开发者字典的位置。
    在这里插入图片描述
    在这里插入图片描述

代码实现

main.cpp

#include <iostream>
#include <cstdio>
#include <fstream>
#include <windows.h>
#include "rgb2yuv.h"
#pragma pack(1)
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 */
    WORD CMapStart;       /* 03h  Color map origin */
    WORD CMapLength;      /* 05h  Color map length */
    BYTE CMapDepth;       /* 07h  Depth of color map entries */
    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 */
} TGAHEAD;
void Read_rgb(ifstream& file, unsigned char* r_buff, unsigned char* g_buff, unsigned char* b_buff, int height, int width) {
    for (int i = height - 1; i >= 0; i--) {
        for (int j = 0; j < width; j++) {
            file.read((char*)(b_buff + (i * width + j)), 1);
            file.read((char*)(g_buff + (i * width + j)), 1);
            file.read((char*)(r_buff + (i * width + j)), 1);
        }
    }
}
int main()
{
    ifstream TgaFile("D:\\数据压缩实验\\4.tga", ios::binary);     /*以二进制输入方式打开文件*/
    ofstream YuvFile("test4.yuv", ios::binary);
    if (!TgaFile) { cout << "打开tga文件失败!" << endl; }
    else cout << "打开tga文件成功" << endl;
    if (!YuvFile) { cout << "打开yuv文件失败!" << endl; }
    else cout << "打开yuv文件成功" << endl;
    TGAHEAD my_tgaheader;
    TgaFile.read((char*)(&my_tgaheader), 18);
    unsigned char* ImageData = NULL;
    unsigned char* b = NULL;
    unsigned char* g = NULL;
    unsigned char* r = NULL;
    unsigned char* Y = NULL;
    unsigned char* U = NULL;
    unsigned char* V = NULL;
    unsigned char* u = NULL;
    unsigned char* v = NULL;
    Init_table();
    int offset = 18;
    int width = my_tgaheader.Width;
    int height = my_tgaheader.Height;
    cout << "test图像的分辨率:" << width << "*" << height << endl;
    r = new unsigned char[width * height];
    g = new unsigned char[width * height];
    b = new unsigned char[width * height];
    Y = new unsigned char[width * height];
    U = new unsigned char[width * height];
    V = new unsigned char[width * height];
    if (my_tgaheader.IDLength == 0) { cout << "无图像信息字段" << endl; }
    else {
        cout << "存在图像信息字段" << endl;
        offset = offset + my_tgaheader.IDLength;
    }
    if (my_tgaheader.ColorMapType == 0) { cout << "不使用颜色板" << endl; }
    else if (my_tgaheader.ColorMapType == 1) { cout << "使用颜色板" << endl; }
    else { cout << "其他图像类型" << endl; }
    if (my_tgaheader.ImageType == 1)
    {
        cout << "未压缩的颜色表图像" << endl;
        cout << "元素表颜色总数:" << int(my_tgaheader.CMapLength) << endl;
        cout << "每个元素大小:" << int(my_tgaheader.CMapDepth) << endl;
        cout << "像素深度:" << int(my_tgaheader.PixelDepth) << endl;
        offset = offset + (my_tgaheader.CMapLength * my_tgaheader.CMapDepth);
        TgaFile.seekg(offset, ios::beg);
    }
    else if (my_tgaheader.ImageType == 2) {
        cout << "未压缩的真彩色图像" << endl;
        TgaFile.seekg(offset, ios::beg);
        Read_rgb(TgaFile, r, g, b, height, width);
    }
    else {
        cout << "其他图像类型" << endl;
        return 0;
    }
    u = new unsigned char[int(width * height / 4)];
    v = new unsigned char[int(width * height / 4)];
    CaculateYUV(height, width, r, g, b, Y, U, V);
    SampleYUV(height, width, U, V, u, v);
    WriteYUV(height, width, Y, u, v, YuvFile);
}

rgb2yuv



#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <malloc.h>
using namespace std;

float RGB2YUV02990[256],RGB2YUV05870[256],RGB2YUV01140[256],
RGB2YUV01684[256],RGB2YUV03316[256],RGB2YUV05000[256],
RGB2YUV04187[256],RGB2YUV00813[256];

void Init_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;
       }
}

// 计算YUV
void CaculateYUV(int height, int width,
unsigned char* Red, unsigned char* Green, unsigned char* Blue, unsigned char*
Y, unsigned char* U, unsigned char* V) {

       float temp;
       for(int i = 0; i < height * width; i++) {
//处理Y分量
              temp= RGB2YUV02990[*(Red + i)] + RGB2YUV05870[*(Green +i)] + RGB2YUV01140[*(Blue +i)];
              temp= temp > 236 ? 236 : temp;
              temp= temp < 16 ? 16 : temp;
              *(Y+ i) = (unsigned char)temp;
//处理U分量
              temp= -RGB2YUV01684[*(Red + i)] - RGB2YUV03316[*(Green+i)] + RGB2YUV05000[*(Blue
+ i)] + 128;
              temp= temp > 236 ? 236 : temp;
              temp= temp < 16 ? 16 : temp;
              *(U+ i) = (unsigned char)temp;
//处理V分量
              temp= RGB2YUV05000[*(Red + i)] - RGB2YUV04187[*(Green+i)] - RGB2YUV00813[*(Blue +i)] + 128;
              temp= temp > 236 ? 236 : temp;
              temp= temp < 16 ? 16 : temp;
              *(V+ i) = (unsigned char)temp;
       }
}

// 写入YUV文件422格式
void WriteYUV(int Height, int Width,
unsigned char* Y, unsigned char* U, unsigned char* V, ofstream& YuvFile) {
       YuvFile.write((char*)Y,Height * Width);
       YuvFile.write((char*)U,Height * Width / 4);
       YuvFile.write((char*)V,Height * Width / 4);
       YuvFile.close();
}

// 对UV进行下采样
void SampleYUV(int Height, int Width,
unsigned char* U, unsigned char* V, unsigned char* u, unsigned char* v)
{
       int k = 0;
       float average_u, average_v;
       for(int i = 0; i < Height * Width / 4; i++) {
              average_u= (float)(*(U + k) + *(U + k + 1) + *(U + k +Width) + *(U + k + 1 + Width)) /4.0;
              average_v= (float)(*(V + k) + *(V + k + 1) + *(V + k +Width) + *(V + k + 1 + Width)) /4.0;
              *(u + i) = (unsigned char)average_u;
              *(v + i) = (unsigned char)average_v;
              if((i + 1) % (Width / 2) == 0) {
                     k= k + 2 + Width;
              }
              else{
                     k= k + 2;
              }
       }
}

实验结果及分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述通过此程序,我对tga文件格式有了进一步了解,包括如何用代码读取TGA图片文件偶信息和每个像素的RGB信息。
在编程过程中,值得注意的两点是:

  1. 结构体是按字节对齐的方式存储的,即以结构体成员中占内存最多的数据类型所占的字节数为标准,所有的成员在分配内存时都要与这个长度对齐。需要通过 #pragma pack(1) 取消字节对齐。
  2. 文件流类提供了许多不同的成员函数,可以用来在文件中移动。其中的一个方法如下: seekg(offset, place); 新位置将从由 place 给出的起始位置开始,偏移 offset 个字节。通过读取 Image ID 和 Color Map Data 的长度,可以确定offset值使文件从Image Data开始读。

参考文献:
http://www.fileformat.info/format/tga/egff.htm
http://blog.sina.com.cn/s/blog_814e83d801014t3m.html
https://baike.baidu.com/item/tga/27071#viewPageContent

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值