BMP序列转YUV文件

BMP序列转YUV文件

借鉴链接:https://blog.csdn.net/zyzcuczyu/article/details/115276854

实验原理

首先我们来了解bmp格式文件的结构

BMP文件格式回顾:

位图文件头BITMAPFILEHEADER

位图信息头BITMAPINFOHEADER

调色板Palette

实际的位图数据ImageData

在<Windows.h>中有存储这样的结构体,可直接使用

位图文件头:
typedef struct tagBITMAPFILEHEADER {
        WORD    bfType;		//文件类型
        DWORD   bfSize;		//文件大小,单位:字节
        WORD    bfReserved1;//保留,设为0
        WORD    bfReserved2;//保留,设为0
        DWORD   bfOffBits;	//说明从BITMAPFILEHEADER结构开始到实际的图像数据之间的字节偏移量
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
位图信息头:
typedef struct tagBITMAPINFOHEADER{
        DWORD      biSize;			//说明结构体所需字节数
        LONG       biWidth;			//以像素为单位说明图像的宽度
        LONG       biHeight;		//以像素为单位说明图像的高度
        WORD       biPlanes;		//说明位面数,必须为1
        WORD       biBitCount;		//说明位数像素,1、2、4、8、24
        DWORD      biCompression;	//说明图像是否压缩及压缩类型BI_RGB、BI_RLE8、BI_RLE4、BI_BITFIELDS
        DWORD      biSizeImage;		//以字节为单位说明图像大小,必须是4的整数倍
        LONG       biXPelsPerMeter;	//目标设备的水平分辨率,像素/米
        LONG       biYPelsPerMeter;	//目标设备的垂直分辨率,像素/米
        DWORD      biClrUsed;		//说明图像实际用到的颜色数,如果为0则颜色数为2的 biBitCount次方
        DWORD      biClrImportant;	//说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
位图调色板

24位无调色板,其他都有

调色板实际上是一个数组,它所包含的元素与位图所具有的颜色数相同,决定于biClrUsed和biBitCount字段。数组中每个元素的类型是一个RGBQUAD结构。真彩色无调色板部分。

typedef struct tagRGBQUAD {
        BYTE    rgbBlue;	//蓝色分量
        BYTE    rgbGreen;	//绿色分量
        BYTE    rgbRed;		//红色分量
        BYTE    rgbReserved;//保留,指定为0
} RGBQUAD;
位图数据

位图数据,这部分的内容根据 BMP 位图使用的位数不同而不同,在 24 位图中直接使用 RGB (存储顺序为BGR,每个分量占1个字节),而其他的小于 24 位的使用调色板中颜色索引值。
规定每一扫描行的字节数必须是4的整倍数,也就是DWORD对齐的。扫描行是由底向上存储的,这就是说,阵列中的第一个字节表示位图左下角的像素,而最后一个字节表示位图右上角的像素。

根据出现问题分析原理

主函数的函数调用步骤分析:

1、打开输出文件及指针

2、定义for循环,循环打开输入bmp文件及指针

3、读取bmp文件的两头一板,把指针移到数据部分,然后存储需要的信息,比如宽和高和位深。

4、readrgb存储到缓冲区

5、rgb2yuv

6、写入yuv文件,对一幅图片重复写入好几帧

7、关闭释放

首先结合上次rgb2yuv的原理,rgb与yuv格式转换,调取部分代码,然后再将bmp文件指针读到相应位置,即RGB数据部分进行读出,因此fread需要读过两个头和一个可能存在的板,才能进行数据的读取。数据读取需注意BGR存放为自下向上,自左向右放置。

可优化部分就在调色板的读取,可根据原理进行优化存储,使程序可以读取低于位深24bit的文件。

其实整体逻辑很清晰明了,然鹅~~~

第一次尝试:

在这里插入图片描述

害,为什么如此丑陋

必然是readrgb有问题

我的错误代码部分:

for (int i = h - 1; i > -1; i--) {
	//错误
	for (int j = 0; j < w; j++) {
		rgbBuf[index] = bmpBuf[i * w + j];
		index++;
	}
}

能看出来错哪了吗?反正我没看出来,倒序放的很对啊

5min later…

for (int i = h - 1; i > -1; i--) {
	//修改!
	for (int j = 0; j < w*3; j++) {
		rgbBuf[index] = bmpBuf[i * w*3 + j];
		index++;
	}
}

要注意宽的每一个像素都有rgb三个值。。。

fine , peace & love

错了咱就改,我要看美女·!!!

在这里插入图片描述

嗯哼哼,这是什么毛病,我的头像咋斑驳成这样

仔细看,对比

在这里插入图片描述

白的部分都黑了,这该不会是 溢出??

可是为什么之前的rgb2yuv没出现这个问题呢?

仔细看我那篇博文,发现那个图片都比较暗,没有像我这张如此高亮。。。

好吧,那确实是我写错了。

咱继续改!就像上篇博文修正rgb一样修正yuv

在这里插入图片描述

完美,虽然白的离谱没有纹理,但是还是能看的

代码

大家最期待的代码来啦~~

主函数(其实比较难,因为需要把fread指针摆到对应位置):

#include <iostream>
#include<Windows.h>
#include"BMP2YUV.h"
using namespace std;

int main(int argc,char* argv[])
{
    //定义变量
    BITMAPFILEHEADER fileHeader;
    BITMAPINFOHEADER fileInfoHeader;
   
    FILE* bmpFileP = NULL;
    FILE* yuvFileP = NULL;

    int num = 7;

    //打开输出文件
    fopen_s(&yuvFileP, argv[1], "wb");

    //对输入文件进行转换
    for (int i = 2; i < num+2; i++) {
        fopen_s(&bmpFileP, argv[i], "rb");
        //读文件头,信息头;
        cout << argv[i] << "被读取" << endl;
        if (fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, bmpFileP)!=1) {
            cout << "read file header error!" << endl;
            exit(0);
        }
        if (fileHeader.bfType!=0x4D42) {
            cout << "not bmp file!" << endl;
            exit(0);
        }
        if (fread(&fileInfoHeader, sizeof(BITMAPINFOHEADER), 1, bmpFileP)!=1) {
            cout << "read file info header error!" << endl;
            exit(0);
        }

        RGBQUAD* pRGB = (RGBQUAD*)malloc(sizeof(RGBQUAD) * (unsigned int)pow(2, fileInfoHeader.biBitCount));

        if (!makePalette(bmpFileP, fileHeader, fileInfoHeader, pRGB)) {
            cout << "no palette" << endl;
        }

        int w= fileInfoHeader.biWidth;
        int h = fileInfoHeader.biHeight;

        unsigned char* rgbBuffer = NULL;
        unsigned char* yBuffer = NULL;
        unsigned char* uBuffer = NULL;
        unsigned char* vBuffer = NULL;

        int rgbFileSize = w*h*3;

        rgbBuffer = (unsigned char*)malloc(rgbFileSize);
        yBuffer = (unsigned char*)malloc(rgbFileSize / 3);
        uBuffer = (unsigned char*)malloc(rgbFileSize / 12);
        vBuffer = (unsigned char*)malloc(rgbFileSize / 12);

        if (!readrgb(w, h, rgbFileSize, bmpFileP, rgbBuffer)) {
            cout << "read rgb error!" << endl;
            exit(0);
        }

        initLookupTable();
        if (!_rgb2yuv(yuvFileP, rgbFileSize, rgbBuffer, yBuffer, uBuffer, vBuffer,w,h)) {
            cout << "rgb2yuv error!" << endl;
            exit(0);
        }


        //多写几帧
        for (int i = 0; i < 30; i++) {
            fwrite(yBuffer, sizeof(unsigned char), rgbFileSize / 3, yuvFileP);
            fwrite(uBuffer, sizeof(unsigned char), rgbFileSize / 12, yuvFileP);
            fwrite(vBuffer, sizeof(unsigned char), rgbFileSize / 12, yuvFileP);
        }

        fclose(bmpFileP);
        delete[] rgbBuffer;
        delete[] yBuffer;
        delete[] uBuffer;
        delete[] vBuffer;
    }
    
    fclose(yuvFileP);
    return 0;

}

调色板函数(其实只是做了一个调色板是否存在的判断,待优化)

 //读取调色板
bool makePalette(FILE* pFile, BITMAPFILEHEADER& fileHeader, BITMAPINFOHEADER& fileInfoHeader, RGBQUAD* pRGB_out) {
	//如果存在调色板
	//pow()为幂函数
	if ((fileHeader.bfOffBits - sizeof(BITMAPFILEHEADER) - fileInfoHeader.biSize) == sizeof(RGBQUAD) * pow(2, fileInfoHeader.biBitCount)) {
		fseek(pFile, sizeof(BITMAPFILEHEADER) + fileInfoHeader.biSize, 0);
		fread(pRGB_out, sizeof(RGBQUAD), (unsigned int)pow(2, fileInfoHeader.biBitCount), pFile);
	}
	else {
		return false;
	}
}

读取rgb

int readrgb(int w,int h,int rgbFileSize ,FILE* bmpFileP, unsigned char* rgbBuf) {

	unsigned char* bmpBuf = (unsigned char*)malloc(sizeof(unsigned char) * rgbFileSize);
	
	fread(bmpBuf, sizeof(unsigned char), rgbFileSize, bmpFileP);
	int index = 0;
	for (int i = h - 1; i > -1; i--) {
		//修改!
		for (int j = 0; j < w*3; j++) {
			rgbBuf[index] = bmpBuf[i * w*3 + j];
			index++;
		}
	}
	delete[] bmpBuf;
	return 1;
}

其他的函数部分和上次没啥大差,但记住需要修正yuv

结果

让我们一起来看美女子
妈耶,本来想发视频的结果csdn不给我通过,只好在CSDN上传gif花了我好大的力气,首先录屏然后迅雷影音截取gif再传到网站上压缩至5M以下,好累。。。
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值