实验二 图像文件的读写和转换(BMP转YUV)
一、实验基本原理
1.BMP位图文件格式
BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),采用位映射存储格式,除了图像深度可选(1、4、8、16、24、32bit)以外,在绝大多数应用中不进行其他任何压缩,因此BMP文件所占用的空间很大。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是 Windows 环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。BMP位图文件默认的文件扩展名是bmp或者dib。
BMP文件大体上分为四个部分:
(1)位图头文件数据结构
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; /* 说明文件的类型 */
DWORD bfSize; /* 说明文件的大小,用字节为单位 */
WORD bfReserved1; /* 保留,设置为0 */
WORD bfReserved2; /* 保留,设置为0 */
DWORD bfOffBits; /* 说明从BITMAPFILEHEADER结构开始到实际的图像数据之间的字节偏移量 */
} BITMAPFILEHEADER;
(2)位图信息数据结构
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;
(3)调色板
调色板实际上是一个数组,它所包含的元素与位图所具有的颜色数相同,决定于biClrUsed和biBitCount字段。数组中每个元素的类型是一个RGBQUAD结构。这部分信息并不是所有类型的位图都有,真彩色图如24 bit的BMP不需要调色板。
typedef struct tagRGBQUAD
{
BYTE rgbBlue; /*指定蓝色分量*/
BYTE rgbGreen; /*指定绿色分量*/
BYTE rgbRed; /*指定红色分量*/
BYTE rgbReserved; /*保留,指定为0*/
} RGBQUAD;
(1)(2)(3)部分的结构体在#include<windows.h>头文件中已定义。
(4)图像数据字节阵列:即位图数据。
对于用到调色板的位图,图像数据就是该像素颜色在调色板中的索引值(逻辑色),真彩色位图如24bit BMP直接使用实际的R、G、B值。
2.相关重要知识点
(1) 掩码组:
实验过程中发现,RGB565格式的16bit位图与压缩参数biCompression=3 (BI_BITFIELDS:位域存放方式)的32bit位图——这两类图片的头部信息中虽然没有伪彩色图片的调色板信息,但是在调色板的位置存在有指示RGB比特位的掩码组信息,为了简化后续的程序算法我在头文件中定义了两个代表掩码组数据信息的结构体如下:
//define mask struct
typedef struct bit32Mask
{
unsigned int rgbRed;
unsigned int rgbGreen;
unsigned int rgbBlue;
unsigned int reserved;
}Mas32;
typedef struct bit16Mask
{
unsigned int rgbRed;
unsigned int rgbGreen;
unsigned int rgbBlue;
}Mas16;
为了能更清楚掩码组的作用,下面以一幅biCompression=3的32bit位图为例,对掩码组信息解读过程如下简图:
(2) DWORD对齐:
图像的每一扫描行由表示图像像素的连续的字节构成,每一行的字节数取决于图像的颜色数目和用像素表示的图像宽度,特别的: 规定每一扫描行的字节数必须是4的倍数,即DWORD对齐方式。写入位图文件数据时,如果图像每行像素字节总数[宽 X biBiCount % 8 != 0 ],系统会自动在每行最后填充若干0值使满足整数字节,接着,如果[每行像素字节数 % 4 != 0 ],系统会自动在每行最后填充若干字节0值使满足DWORD对齐。所以我们对位图加载处理时要注意判断每行数据是否有0值填充,若有0值填充则从位图中读取数据的过程中要注意指针偏移量和即时跳转。
(3)自下而上扫描:
扫描行是由底向上存储的,即:阵列中的第一个字节表示位图左下角的像素,而最后一个字节表示位图右上角的像素。
如下简图:
(4) 字节序:
计算机系统存储数据采用的字节序有两种:小尾字节序(Little Endian)和大尾字节序(Big Endian)。Intel处理器大多数采用小尾字节序,“低位在前高位在后”,Motorola处理器大多数使用大尾字节序,“高位在前低位在后”。
为更好地理解Little Endian的数据存储格式,这里以一张RGB565 16bit的BMP位图为例,对头部信息的解读示意图如下所示:
二、实验流程
实验流程图:
算法并不是很难,这里仅以16bitBMP提取RGB过程作一个简单示意图如下:
三、具体算法源代码
1.BMP2YUV.h
#ifndef BMP2YUV_H_
#define BMP2YUV_H_
//define mask struct
typedef struct bit32Mask
{
unsigned int rgbRed;
unsigned int rgbGreen;
unsigned int rgbBlue;
unsigned int reserved;
}Mas32;
typedef struct bit16Mask
{
unsigned int rgbRed;
unsigned int rgbGreen;
unsigned int rgbBlue;
}Mas16;
int BMP2RGB32bit(int bitcount, int x_dim, int y_dim, void *bmpbuf, void *rgbbuf, void *mask);
int BMP2RGB24bit(int bitcount, int x_dim, int y_dim, void *bmpbuf, void *rgbbuf);
int BMP2RGB16bit(int bitcount, int x_dim, int y_dim, void *bmpbuf, void *rgbbuf,void *mask);
int BMP2RGBNOT24bit(int bitcount, int x_dim, int y_dim, void *bmpbuf, void *rgbbuf, void *pRGB);
int RGB2YUV(int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip);
void InitLookupTable();
void adjust(unsigned char *b, unsigned char *g, unsigned char *r, unsigned char *y, unsigned char *u, unsigned char *v);
#endif
2.main.c
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <math.h>
#include "bmp2yuv.h"
#define u_int8_t unsigned __int8
#define u_int unsigned __int32
#define u_int32_t unsigned __int32
int main(int argc,char **argv)
{
int count;
BITMAPFILEHEADER File_header;
BITMAPINFOHEADER Info_header;
RGBQUAD *pRGB = NULL;
FILE* bmp = NULL;
FILE* yuvFile = NULL;
Mas16 *mask16 = NULL;
Mas32 *mask32 = NULL;
u_int8_t* yBuf = NULL;
u_int8_t* uBuf = NULL;
u_int8_t* vBuf = NULL;
u_int8_t* rgbBuf = NULL;
u_int8_t* bmpBuf = NULL;
u_int8_t* mask = NULL;
u_int frameWidth; /* --width=<uint> */
u_int frameHeight;
u_int bitcount;
u_int py;
u_int m;
u_int8_t i;
int sum = 0;
char bmpf[][50] = { "park.bmp", "tree.bmp", "girlstwo.bmp", "02.bmp", "bea.bmp", "street.bmp" };
char yuvname[50] = "CometoYUV.yuv";
FILE *yuv=NULL;
int framenumber;
/* build the RAW file */
fopen_s(&yuv, yuvname, "wb+");
if (yuv == NULL)
{
printf("Fail to Build yuv file\n");
exit(0);
}
else
{
printf("The output rgb file is %s\n", yuvname);
printf("\n");
printf("-------------------------------------------------------------------------\n");
}
for (i = 0; i<6; i++)
{
count = 0;
framenumber = atoi(argv[i + 1]);
//open the bmp file
fopen_s(&bmp, bmpf[i], "rb");
if (!bmp)
{
printf("cannot find the specific file %s:\n", bmpf[i]);
exit(0);
}
else
{
printf("The input bmp file: %s\n", bmpf[i]);
}
if (!framenumber)
{
printf("\n写入帧数:0\n");
continue;
}
else
{
if (fread(&File_header, sizeof(BITMAPFILEHEADER), 1, bmp) != 1)
{
printf("read file header error!");
exit(0);
}
if (File_header.bfType != 0x4D42)
{
printf("Not bmp file!");
exit(0);
}
//printf("this is a 0x%04X:\n", File_header.bfType);
if (fread(&Info_header, sizeof(BITMAPINFOHEADER), 1, bmp) != 1)
{
printf("read info header error!");
exit(0);
}
// end read header
frameWidth = Info_header.biWidth; /* --width=<uint> */
frameHeight = Info_header.biHeight;
py = File_header.bfOffBits;
bitcount = Info_header.biBitCount;
/* get an output buffer for a frame */
if ((frameWidth*bitcount % 8 == 0) && (frameWidth*bitcount / 8 % 4 == 0))//DWORD 对齐
{
yBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
uBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);
vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);
/* get an input buffer for a frame */
bmpBuf = (u_int8_t*)malloc(frameWidth * frameHeight * bitcount / 8);
/* get an output buffer for a frame */
rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);
if (rgbBuf == NULL || yBuf == NULL || uBuf == NULL || vBuf == NULL || bmpBuf == NULL)
{
printf("no enought memory\n");
exit(1);
}
BMP 2 RGB
while (framenumber)
{
fseek(bmp, py, SEEK_SET);
if (!fread(bmpBuf, 1, frameWidth * frameHeight * bitcount / 8, bmp))
{
printf("the image has problems!");
return 0;
}
if (bitcount == 32)
{
if (Info_header.biCompression==0)
{
if (BMP2RGB32bit(bitcount, frameWidth, frameHeight, bmpBuf, rgbBuf,0))
{
printf("32bit BMP2RGB program runs error!");
return 0;
}
}
else if (Info_header.biCompression == 3)
{
//取掩码组
m = 4*4;
mask32 = (Mas32*)malloc(sizeof(Mas32));
fseek(bmp, sizeof(BITMAPFILEHEADER) + Info_header.biSize - m, SEEK_SET);
fread(mask32, sizeof(Mas32),1, bmp);
if (BMP2RGB32bit(bitcount, frameWidth, frameHeight, bmpBuf, rgbBuf, mask32))
{
printf("32bit BMP2RGB program runs error!");
return 0;
}
}
}
else if (bitcount == 24)
{//真彩位图
if (BMP2RGB24bit(bitcount, frameWidth, frameHeight, bmpBuf, rgbBuf))
{
printf("24bit BMP2RGB program runs error!");
return 0;
}
}
else if (bitcount == 16)
{
//16bit BMP RGB比特位根据biCompression确定
if (Info_header.biCompression == 0)//555
{
if (BMP2RGB16bit(bitcount, frameWidth, frameHeight, bmpBuf, rgbBuf, 0))
{
printf("16bit BMP2RGB program runs error!");
return 0;
}
}
else if (Info_header.biCompression == 3)
{
//取掩码组
m = 4 * 3;
mask16 = (Mas16*)malloc(sizeof(Mas16));
fseek(bmp, py-m, SEEK_SET);
fread(mask16, 1, m, bmp);
if (BMP2RGB16bit(bitcount, frameWidth, frameHeight, bmpBuf, rgbBuf, mask16))
{
printf("16bit BMP2RGB program runs error!");
return 0;
}
}
}
else
{
if ((py - sizeof(BITMAPFILEHEADER) - Info_header.biSize) == sizeof(RGBQUAD)*pow(2, bitcount))
{//1、2、4、8 bit 有调色板部分
m = (unsigned int)pow(2, bitcount);
pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*m);
fseek(bmp, sizeof(BITMAPFILEHEADER) + Info_header.biSize, SEEK_SET);
fread(pRGB, sizeof(RGBQUAD), m, bmp);
if (BMP2RGBNOT24bit(bitcount, frameWidth, frameHeight, bmpBuf, rgbBuf, pRGB))
{
printf("BMP 2 RGB program runs error!");
return 0;
}
}
}
/RGB2YUV
if (RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, 0/*flip=0*/))//bmp图像格式从最后一行起逐行扫描
{
printf("RGB2YUV program runs error!");
return 0;
}
fwrite(yBuf, 1, frameWidth * frameHeight, yuv);
fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuv);
fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuv);
printf("\r...%d", ++count);
framenumber--;
}
printf("\n写入帧数:%u %ux%u(%d bit)\n", count, frameWidth, frameHeight, bitcount);
sum += count;
printf("\n");
printf("\n");
}
}
}
printf("%d帧YUV写入成功!\n",sum);
/* cleanup */
fclose(bmp);
fclose(yuv);
//free the memory
if (yBuf) { free(yBuf); }
if (uBuf) { free(uBuf); }
if (vBuf) { free(vBuf); }
if (rgbBuf) { free(rgbBuf); }
if (bmpBuf) { free(bmpBuf); }
if (pRGB) { free(pRGB); }
if (mask16) { free(mask16); }
if (mask32) { free(mask32); }
return 0;
}
3.BMP2RGB.c
#include "stdlib.h"
#include "bmp2yuv.h"
#include <windows.h>
#include <math.h>
int BMP2RGB32bit(int bitcount, int x_dim, int y_dim, void *bmpbuf, void *rgbbuf, void *mask)
{
long i;
unsigned char *bmp, *rgb;
Mas32 *mas;
long size = x_dim*y_dim;
bmp = (unsigned char *)bmpbuf;
rgb = (unsigned char *)rgbbuf;
mas = (Mas32 *)mask;
if (mask == NULL)
{
for (i = 0; i < size; i++)
{
*(rgb + 0) = *(bmp + 0);
*(rgb + 1) = *(bmp + 1);
*(rgb + 2) = *(bmp + 2);
rgb += 3;
bmp += 4;
}
return 0;
}
else
{//根据掩码确定RGB比特位
int Gkey, Bkey, Rkey;
if (mas->rgbGreen == 0)
Gkey= 0;
else if (mas->rgbGreen == 0xFF000000)
Gkey = 3;
else if (mas->rgbGreen == 0xFF0000)
Gkey = 2;
else if (mas->rgbGreen == 0xFF00)
Gkey = 1;
else
return 1;
if (mas->rgbBlue == 0)
Bkey = 0;
else if (mas->rgbBlue == 0xFF000000)
Bkey = 3;
else if (mas->rgbBlue == 0xFF0000)
Bkey = 2;
else if (mas->rgbBlue == 0xFF00)
Bkey = 1;
else
return 1;
if (mas->rgbRed == 0)
Rkey = 0;
else if (mas->rgbRed == 0xFF000000)
Rkey = 3;
else if (mas->rgbRed == 0xFF0000)
Rkey = 2;
else if (mas->rgbRed == 0xFF00)
Rkey = 1;
else
return 1;
for (i = 0; i < size; i++)
{
*(rgb + 0) = *(bmp + Bkey);
*(rgb + 1) = *(bmp + Gkey);
*(rgb + 2) = *(bmp + Rkey);
rgb += 3;
bmp += 4;
}
return 0;
}
}
int BMP2RGB24bit(int bitcount, int x_dim, int y_dim, void *bmpbuf, void *rgbbuf)
{
long i;
unsigned char *rgb;// , *raw;
unsigned char *bmp;
long size = x_dim*y_dim;
rgb = (unsigned char *)rgbbuf;
bmp = (unsigned char *)bmpbuf;
for (i = 0; i < size*3; i++)
{
*(rgb + i) = *(bmp + i);
}
return 0;
}
int BMP2RGB16bit(int bitcount, int x_dim, int y_dim, void *bmpbuf, void *rgbbuf, void *mask)
{
long loop;
unsigned char *Data, *rgbDataOut;
long size = x_dim*y_dim*bitcount / 8;
Data = (unsigned char*)bmpbuf;
rgbDataOut = (unsigned char*)rgbbuf;
Mas16 *mas;
mas = (Mas16 *)mask;
if (mask == NULL)
{
for (loop = 0; loop < size; loop += 2)
{
*rgbDataOut = (*(Data + loop) & 0x1F) << 3;
*(rgbDataOut + 1) = ((*(Data + loop) & 0xE0) >> 2) + ((*(Data + loop + 1) & 0x03) << 6);
*(rgbDataOut + 2) = (*(Data + loop + 1) & 0x7C) << 1;
rgbDataOut += 3;
}
}
else//555 OR 565决定于rgbGreen的比特位
{
if (mas->rgbGreen == 0x07E0)
{
for (loop = 0; loop < size; loop += 2)
{
*rgbDataOut = (*(Data + loop) & 0x1F) << 3;
*(rgbDataOut + 1) = ((*(Data + loop) & 0xE0) >> 3) + ((*(Data + loop + 1) & 0x07) << 5);
*(rgbDataOut + 2) = (*(Data + loop + 1) & 0xF8);
rgbDataOut += 3;
}
}
else
{
for (loop = 0; loop < size; loop += 2)
{
*rgbDataOut = (*(Data + loop) & 0x1F) << 3;
*(rgbDataOut + 1) = ((*(Data + loop) & 0xE0) >> 2) + ((*(Data + loop + 1) & 0x03) << 6);
*(rgbDataOut + 2) = (*(Data + loop + 1) & 0x7C) << 1;
rgbDataOut += 3;
}
}
}
return 0;
}
int BMP2RGBNOT24bit(int bitcount, int x_dim, int y_dim, void *bmpbuf, void *rgbbuf, void *ppRGB)//1\4\8 bit BMP
{
unsigned char *rgb;
unsigned char *bmp;
unsigned char index;
bmp = (unsigned char *)bmpbuf;
rgb = (unsigned char *)rgbbuf;
int shiftCnt;
unsigned char mask;
long loop = 0;
unsigned char *Data, *rgbDataOut;
RGBQUAD* p;
long size = x_dim*y_dim*bitcount/8;
Data = (unsigned char*)bmpbuf;
rgbDataOut = (unsigned char*)rgbbuf;
p = (RGBQUAD*)ppRGB;
for (loop = 0; loop<size; loop++)
{
shiftCnt = 1;
mask = (unsigned char)pow(2, bitcount) - 1;
mask = mask << (8-bitcount);
while (mask)
{
//索引号的确定
index = (mask == 0xFF) ? *(Data + loop) : (*(Data + loop) & mask) >> (8 - shiftCnt * bitcount);
*rgbDataOut = (p + index)->rgbBlue;
*(rgbDataOut + 1) = (p + index)->rgbGreen;
*(rgbDataOut + 2) = (p + index)->rgbRed;
if (bitcount == 8)
mask = 0;
else
mask >>= bitcount;
rgbDataOut += 3;
shiftCnt++;
}
}
return 0;
}
4.RGB2YUV.c
#include "stdlib.h"
#include "bmp2yuv.h"
static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip)
{
static int init_done = 0;
long i, j, size;
unsigned char *r, *g, *b;
unsigned char *y, *u, *v;
unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;
unsigned char *y_buffer, *u_buffer, *v_buffer;
unsigned char *sub_u_buf, *sub_v_buf;
if (init_done == 0)
{
InitLookupTable();
init_done = 1;
}
// check to see if x_dim and y_dim are divisible by 2
if ((x_dim % 2) || (y_dim % 2)) return 1;
size = x_dim * y_dim;
// allocate memory
y_buffer = (unsigned char *)y_out;
sub_u_buf = (unsigned char *)u_out;
sub_v_buf = (unsigned char *)v_out;
u_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
v_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
if (!(u_buffer && v_buffer))
{
if (u_buffer) free(u_buffer);
if (v_buffer) free(v_buffer);
return 2;
}
b = (unsigned char *)bmp;
y = y_buffer;
u = u_buffer;
v = v_buffer;
// convert RGB to YUV
if (!flip)
{
for (j = 0; j < y_dim; j ++)
{
y = y_buffer + (y_dim - j - 1) * x_dim;
u = u_buffer + (y_dim - j - 1) * x_dim;
v = v_buffer + (y_dim - j - 1) * x_dim;
for (i = 0; i < x_dim; i ++) {
g = b + 1;
r = b + 2;
adjust(b, g, r, y, u, v);
b += 3;
y ++;
u ++;
v ++;
}
}
} else {
for (i = 0; i < size; i++)
{
g = b + 1;
r = b + 2;
adjust(b, g, r, y, u,v);
b += 3;
y ++;
u ++;
v ++;
}
}
// subsample UV
for (j = 0; j < y_dim/2; j ++)
{
psu = sub_u_buf + j * x_dim / 2;
psv = sub_v_buf + j * x_dim / 2;
pu1 = u_buffer + 2 * j * x_dim;
pu2 = u_buffer + (2 * j + 1) * x_dim;
pv1 = v_buffer + 2 * j * x_dim;
pv2 = v_buffer + (2 * j + 1) * x_dim;
for (i = 0; i < x_dim/2; i ++)
{
*psu = (*pu1 + *(pu1+1) + *pu2 + *(pu2+1)) / 4;
*psv = (*pv1 + *(pv1+1) + *pv2 + *(pv2+1)) / 4;
psu ++;
psv ++;
pu1 += 2;
pu2 += 2;
pv1 += 2;
pv2 += 2;
}
}
free(u_buffer);
free(v_buffer);
return 0;
}
void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) RGBYUV02990[i] = (float)0.2990 * i;
for (i = 0; i < 256; i++) RGBYUV05870[i] = (float)0.5870 * i;
for (i = 0; i < 256; i++) RGBYUV01140[i] = (float)0.1140 * i;
for (i = 0; i < 256; i++) RGBYUV01684[i] = (float)0.1684 * i;
for (i = 0; i < 256; i++) RGBYUV03316[i] = (float)0.3316 * i;
for (i = 0; i < 256; i++) RGBYUV04187[i] = (float)0.4187 * i;
for (i = 0; i < 256; i++) RGBYUV00813[i] = (float)0.0813 * i;
}
void adjust(unsigned char *b, unsigned char *g, unsigned char *r, unsigned char *y, unsigned char *u, unsigned char *v)
{
float temp = 0;
temp = (float)(RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
temp = temp>235 ? 235 : temp;
temp = temp<16 ? 16 : temp;
*y = (unsigned char)temp;
temp = (float)(-RGBYUV01684[*r] - RGBYUV03316[*g] + (*b) / 2 + 128);
temp = temp>240 ? 240 : temp;
temp = temp<16 ? 16 : temp;
*u = (unsigned char)temp;
temp = (float)((*r) / 2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
temp = temp>240 ? 240 : temp;
temp = temp<16 ? 16 : temp;
*v = (unsigned char)temp;
}
四、实验结果
根据实验指导书要求:生成带有个人logo的多张bmp图片(测试程序使用6幅图片),程序执行时可在命令行中设置每个画面出现的帧数,最后形成的YUV文件不少于200帧。
测试图片源文件均来自网络,后续测试中所用的1、4、8、16、32bit位图是利用画图板/PS软件由原图转换得到。
由命令行设置每幅图片播放帧数:
程序执行结果如下图所示:
生成的YUV文件取其中六帧截图如下:对于1、4、8 bit颜色深度的BMP位图到明显颜色内容的失真。
五、实验总结
缓冲区分配、结构体操作、倒序读写文件、函数定义等操作是这次实验应该重点掌握的内容。