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以下,好累。。。