BMP文件序列转YUV文件
bmp文件
由四个部分组成:
1.位图文件头
2. 位图信息头
3. 调色板
4.实际的位图数据
也就是我们需要处理的bgr数据
实验思路
之前一直在用c++写这次实验,但是wingdi.h的库导入老是报错,索性还是用c语言写了,在前面加个windows.h就可以用wingdi.h库了。
由于我们使用的是24位真彩色图像,所以无调色板。在将bgr数据转为yuv数据之前,我们需要先读取文件头和信息头,并且可以从信息头中获取图片的宽高等数据,再用第一周实验的rgb转yuv程序实现批量地转换。
每两幅图之间写了个渐变转场。
代码
#include<stdio.h>
#include<stdbool.h>
#include<windows.h>
#include<wingdi.h>
#define u_int8_t unsigned __int8
void main(){
int i, j, k;
char path[] = ".bmp";
char in_path[20];
const char out_path[20] = "yuv_video.yuv";
FILE* fc;
FILE* fp;
u_int8_t* rgbBuf = NULL;
u_int8_t* yBuf = NULL;
u_int8_t* uBuf = NULL;
u_int8_t* vBuf = NULL;
BITMAPFILEHEADER fileheader;
BITMAPINFOHEADER infoheader;
RGBQUAD rgbq;// 由于用的是24位真彩色图片,所以没有调色板
int w = 512;
int h = 512;
int frame = 42;// 42帧
rgbBuf = (u_int8_t*)malloc(w * h * 3);
yBuf = (u_int8_t*)malloc(w * h);
uBuf = (u_int8_t*)malloc((w * h) / 4);
vBuf = (u_int8_t*)malloc((w * h) / 4);
bool flip = true;
fopen_s(&fc, out_path, "wb"); // 写入二进制
// 先读取第一帧,初始化brgbBuf,用于后续转场
fopen_s(&fp, "0.bmp", "rb"); // 读取二进制
printf("0.bmp");
fread(&fileheader, sizeof(BITMAPFILEHEADER), 1, fp);// 读取文件头
fread(&infoheader, sizeof(BITMAPINFOHEADER), 1, fp);// 信息头
// printf(infoheader.biWeight)
// printf(infoheader.biHeight)
fread(rgbBuf, w * h * 3, 1, fp);
RGB2YUV(w, h, rgbBuf, yBuf, uBuf, vBuf, flip);
for (i = 0; i < w * h; i++)
{
if (yBuf[i] < 16) yBuf[i] = 16;
if (yBuf[i] > 235) yBuf[i] = 235;
}
for (i = 0; i < w * h / 4; i++)
{
if (uBuf[i] < 16) uBuf[i] = 16;
if (uBuf[i] > 240) uBuf[i] = 240;
if (vBuf[i] < 16) vBuf[i] = 16;
if (vBuf[i] > 240) vBuf[i] = 240;
}
fwrite(yBuf, 1, w * h, fc);
fwrite(uBuf, 1, (w * h) / 4, fc);
fwrite(vBuf, 1, (w * h) / 4, fc);
u_int8_t* brgbBuf;// 用于存储前一帧的值
brgbBuf = (u_int8_t*)malloc(w * h * 3);
u_int8_t* trgbBuf;// 用于转场输出的每一帧
trgbBuf = (u_int8_t*)malloc(w * h * 3);
for (i = 0; i < w * h * 3; i++)
{
brgbBuf[i] = rgbBuf[i];
}
fclose(fp);
// 一共有6幅图像
for (j = 1; j < 6; j++)
{
sprintf(in_path,"%d.bmp",j); // 依次打开1.bmp文件,2.bmp文件,3.bmp文件
printf(in_path);
fopen_s(&fp, in_path, "rb"); // 读取二进制
fread(&fileheader, sizeof(BITMAPFILEHEADER), 1, fp);
fread(&infoheader, sizeof(BITMAPINFOHEADER), 1, fp);
fread(rgbBuf, w * h * 3, 1, fp);
// 每两幅图片之间写一个转场
for (k = 0; k < frame; k++)
{
for (i = 0; i < w * h * 3; i++)
{
trgbBuf[i] = brgbBuf[i]+k*(rgbBuf[i]-brgbBuf[i])/frame;// 渐变转场
}
RGB2YUV(w, h, trgbBuf, yBuf, uBuf, vBuf, flip);
for (i = 0; i < w * h; i++)
{
if (yBuf[i] < 16) yBuf[i] = 16;
if (yBuf[i] > 235) yBuf[i] = 235;
}
for (i = 0; i < w * h / 4; i++)
{
if (uBuf[i] < 16) uBuf[i] = 16;
if (uBuf[i] > 240) uBuf[i] = 240;
if (vBuf[i] < 16) vBuf[i] = 16;
if (vBuf[i] > 240) vBuf[i] = 240;
}
fwrite(yBuf, 1, w * h, fc);
fwrite(uBuf, 1, (w * h) / 4, fc);
fwrite(vBuf, 1, (w * h) / 4, fc);
}
// 将当前图像作为上一幅图像
for (i = 0; i < w * h * 3; i++)
{
brgbBuf[i] = rgbBuf[i];
}
fclose(fp);
}
free(rgbBuf);
free(yBuf);
free(uBuf);
free(vBuf);
free(trgbBuf);
free(brgbBuf);
fclose(fc);
}
RGB2YUV程序使用的是老师给的范例程序,参数分别是图像宽高,rgb的数据,y、u、v的数据,是否执行。
实验结果
bmp文件:用PS加了文字,改了大小为512x512,文件名统一为0.bmp,1.bmp,2.bmp等等
yuv文件在博客上播放不了
所以我们用一句话把它转成gif图片播放
ffmpeg -s 512x512 -i yuv_video.yuv output.gif
得到如图gif
用PS压缩了大小和颜色板总算上传成功了!