数据压缩(七)——TGA2YUV文件转换

任务:将tga文件转换为yuv文件


(一)TGA文件解析

TGA文件结构

TGA文件原始文件结构(v1.0)

  由两个部分组成:文件头、图像/颜色表数据。

TGA文件扩展文件结构(v2.0)

  由5个部分组成:文件头、图像/颜色表数据、开发者自定义区域、扩展区域和文件尾。
  其中,v2.0的版本中文件头和图像/颜色表数据与v1.0保持一致。

  • 文件头:由图像描述信息字段长度、颜色表类型、图像类型、颜色表说明和图像说明五个字段组成,总计18字节,描述了图像存储的基本信息,应用程序可依据该部分字段值读写图像数据。
  • 图像/颜色表数据:由图像描述信息(可选)、颜色表数据和图像数据三部分组成,用于存储图片的图像信息。
  • 开发者自定义区域:包含开发者定义字段列表和开发者字典(用于存储开发者定义字段的值),该区域为开发者扩展该文件格式提供接口,以便存储额外的信息。
  • 扩展区域:由扩展区域大小、作者姓名、作者注释、日期/时间、工作名称/ID、工作累计耗时、编辑软件的名称、编辑软件的版本、关键颜色、像素宽高比、灰度值、颜色校正表偏移量、缩略图偏移量、扫描线表偏移量、alpha通道类型、扫描线表、缩略图图像数据和颜色校正表组成,为Truevision公司定义的标准扩展功能,以提供更多的图像附加信息。
  • 文件尾:由扩展区域偏移量、开发者目录偏移量和TGA文件扩展格式签名三部分组成,用于验证TGA文件扩展格式,并可以确定扩展区域和开发者字典的位置。

  在整个任务的完成过程中,后续的开发者自定义区域、扩展区域、文件尾的数据并没有进行读入与处理,因为对于 Y U V YUV YUV文件的格式转换来说,只需要 R 、 G 、 B R、G、B RGB三个分量的数值从而计算出 Y 、 U 、 V Y、U、V YUV即可。

  图片来源TGA文件格式深度解析(一)
在这里插入图片描述
在这里插入图片描述
  文件头的定义格式(来自TGA格式说明):
在这里插入图片描述

(二)TGA2YUV具体实现

  由上述准备知识,我们就可以进行 T G A 2 Y U V TGA2YUV TGA2YUV的具体实现了。
  关于 Y U V YUV YUV文件格式的介绍请参照数据压缩(五)——彩色空间转换(完整版)
  以下是整个工程的实现逻辑:
在这里插入图片描述
  在具体实现的过程中,值得注意的一点是 T G A TGA TGA文件图像数据的存储不是从左上方第一个像素点开始一行一行进行,而是从左下方最后一行的第一个像素点开始倒着往上存,在进行图像数据到 R G B RGB RGB数据转换的过程中要记得数据的重排列
  项目的解决方案资源管理器界面如下:
在这里插入图片描述

main.cpp

#include <iostream>
#include <cstdio>
#include <fstream>
#include <windows.h>
#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;
}

rgb2yuv.h

#pragma once
void rgb2yuv(unsigned char* b, unsigned char* g,unsigned char* r,unsigned char* yuv, int width,int height);

rgb2yuv.cpp

#pragma once
#include "rgb2yuv.h"
#include <iostream>
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++;
		}
	}
}

tga2yuv.h

#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);

tga2yuv.cpp

#include <iostream>
#include <cstdio>
#include <fstream>
#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文件运行结果截图
在这里插入图片描述在这里插入图片描述在这里插入图片描述

  至此,实验结束。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值