24位图
16位图
8位
4位:
1位:
300*299的图片
右边的黑色就是补得黑框
位图由4部分组成
位图文件头 BITMAPFILEHEADER |
包含 BMP 图像文件的类型、显示内容等信息 |
位图信息头 BITMAPINFOHEADER |
BMP 图像的宽、高、压缩方法,以及定义颜色等信息 |
调色板PALETTE |
可选,真彩色图(24位的 BMP)就不需要调色板 |
位图数据IMAGEDATA |
24 位图中直接使用 RGB,而其他的小于 24 位的使用调色板中颜色索引值 |
位图文件头
typedef struct tagBITMAPFILEHEADER {
WORD bfType; /* 说明文件的类型 */
DWORD bfSize; /* 说明文件的大小,用字节为单位 */
WORD bfReserved1; /* 保留,设置为 0 */
WORD bfReserved2; /* 保留,设置为 0 */
DWORD bfOffBits; /* 说明从 BITMAPFILEHEADER 结构开始到实际的图像数
据之间的字节偏移量 */
} BITMAPFILEHEADER;
信息头文件
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;
调色板
typedef struct tagRGBQUAD {
BYTE rgbBlue; /*指定蓝色分量*/
BYTE rgbGreen; /*指定绿色分量*/
BYTE rgbRed; /*指定红色分量*/
BYTE rgbReserved; /*保留,指定为0*/
} RGBQUAD;
调色板实际上是一个数组,它所包含的元素与位图所具有的颜色数相同,决定于biClrUsed和biBitCount字段。数组中每个元素的类型是一个RGBQUAD结构。真彩色无调色板部分。
主函数里面有6个命令行参数,5个不同场景的bmp文件,和输出的yuv文件
bmpFileName[0] = argv[1];
bmpFileName[1] = argv[2];
bmpFileName[2] = argv[3];
bmpFileName[3] = argv[4];
bmpFileName[4] = argv[5];
yuvFileName = argv[6];
要包含200帧的画面,每个画面循环写40次
for (i = 0; i < 40; i++)
{
fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
}
主函数里面的流程:
1.定义好yuv,bmp的文件指针
2.打开bmp文件的文件头和信息头
3.开辟rgb、y/u/v的动态存储空间
4.调用readrgb,rgb2yuv的函数
5写yuv文件里面
代码如下:
#include
#include
#include
#include "rgb2yuv.h"
#include
void ReadRGB(FILE *pbmp, unsigned char *rgbbuf, BITMAPFILEHEADER &file_h, BITMAPINFOHEADER & info_h);
#define u_int8_t unsigned __int8
#define u_int unsigned __int32
#define u_int32_t unsigned __int32
/*
* rgb2yuv
* required arg1 should be the input RAW RGB24 file
* required arg2 should be the output RAW YUV12 file
*/
int main(int argc, char** argv)
{
/* variables controlable from command line */
u_int frameWidth = 256; /* --width= */
u_int frameHeight = 256;
bool flip = FALSE; /* --flip */
unsigned int i;
/* internal variables */
char* bmpFileName[5];
char* yuvFileName = NULL;
FILE* bmpFile =NULL;
FILE* yuvFile = NULL;
u_int8_t* rgbBuf = NULL;
u_int8_t* yBuf = NULL;
u_int8_t* uBuf = NULL;
u_int8_t* vBuf = NULL;
u_int32_t videoFramesWritten = 0;
/* begin process command line */
/* point to the specified file names */
bmpFileName[0] = argv[1];
bmpFileName[1] = argv[2];
bmpFileName[2] = argv[3];
bmpFileName[3] = argv[4];
bmpFileName[4] = argv[5];
yuvFileName = argv[6];
/* open the RAW file */
yuvFile = fopen(yuvFileName, "wb");
if (yuvFile == NULL)
{
printf("cannot find yuv file\n");
exit(1);
}
else
{
printf("The output yuv file is %s\n", yuvFileName);
}
/* open the BMP file */
for (int m = 0; m < 5; m++)
{
bmpFile = fopen(bmpFileName[m], "rb");
if (bmpFile == NULL)
{
printf("cannot find bmp file\n");
exit(1);
}
else
{
printf("The input bmp file is %s\n", bmpFileName[m]);
}
BITMAPINFOHEADER info_header;
BITMAPFILEHEADER file_header;
// read file & info header
if (fread(&file_header, sizeof(BITMAPFILEHEADER), 1, bmpFile) != 1)
{
printf("read file header error!");
exit(0);
}
if (file_header.bfType != 0x4D42)
{
printf("Not bmp file!");
exit(0);
}
/*else
{
printf("this is a %s\n", file_header.bfType);
}*/
if (fread(&info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1)
{
printf("read info header error!");
exit(0);
}//end read
if (info_header.biWidth*info_header.biBitCount % 32 == 0)
{
frameWidth = info_header.biWidth;
}
else
{
frameWidth = (info_header.biWidth*info_header.biBitCount +31) / 32 * (32 / info_header.biBitCount);
}
//规定每一扫描行的字节数必须是 4 的整倍数(32位的整数倍),也就是DWORD 对齐的
//一行总的数据量(按位来说),是宽度*位深
//(x/32)得整数,再加上32就是比x大的最近的32的倍数,除以8就是像素的字节数
if (info_header.biHeight % 2 == 0)
{
frameHeight = info_header.biHeight;
}
else
{
frameHeight = info_header.biHeight + 1;
}
//宽(按字节)必须是4的的整数倍,高(按字节)必须是2的整数倍
//24位.宽*高*3,16位.宽*高*2,8位.宽*高,4位.宽*高/2,2位.宽*高/4,1位.宽*高/8
//要保证一行四字节扫描,所以宽为4字节的整数倍
//要保证1位的时候数据为字节的整数倍
/* get an input buffer for a frame */
rgbBuf = (u_int8_t*)malloc(frameWidth*frameHeight*3);
/* get the output buffers for a frame */
yBuf = (u_int8_t*)malloc(frameWidth * frameHeight);
uBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);
vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);
if (rgbBuf == NULL || yBuf == NULL || uBuf == NULL || vBuf == NULL)
{
printf("no enought memory\n");
exit(1);
}
ReadRGB(bmpFile,rgbBuf,file_header,info_header);
if (RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip))
{
printf("error");
return 0;
}
for (i = 0; i < frameWidth*frameHeight; i++)
{
if (yBuf[i] < 16) yBuf[i] = 16;
if (yBuf[i] > 235) yBuf[i] = 235;
}
for (i = 0; i < frameWidth*frameHeight / 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;
}
for (i = 0; i < 40; i++)
{
fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
}
printf("\r...%d", ++videoFramesWritten);
}
printf("\n%u %ux%u video frames written\n",
videoFramesWritten, frameWidth, frameHeight);
/* cleanup */
fclose(bmpFile);
fclose(yuvFile);
free(rgbBuf);
free(yBuf);
free(uBuf);
free(vBuf);
return(0);
}
READRGB里面的流程:
1.先把图片的数据信息存到开辟的动态空间里面
2.再根据不同的位深,把data里面的数据写到rgbbu里面
程序如下:
#include "stdlib.h"
#include "rgb2yuv.h"
#include
#include
#include
#include
//调色板
bool MakePalette(FILE * pFile, BITMAPFILEHEADER &file_h, BITMAPINFOHEADER & info_h, RGBQUAD *pRGB_out)
{
if ((file_h.bfOffBits - sizeof(BITMAPFILEHEADER) - info_h.biSize) == sizeof(RGBQUAD)*pow(2, info_h.biBitCount))
{
fseek(pFile, sizeof(BITMAPFILEHEADER) + info_h.biSize, 0);
fread(pRGB_out, sizeof(RGBQUAD), (unsigned int)pow(2, info_h.biBitCount), pFile);
return true;
}
else
return false;
}
//读取RGB文件
void ReadRGB(FILE *pbmp, unsigned char *rgbbuf, BITMAPFILEHEADER &file_h, BITMAPINFOHEADER & info_h)
{
unsigned long h, w, pix_h, pix_w;
if (info_h.biWidth *info_h.biBitCount% 32 == 0)
{
pix_w = info_h.biWidth;
}
else
{
pix_w = (info_h.biWidth*info_h.biBitCount+31 ) / 32 * (32 / info_h.biBitCount);
}
//规定每一扫描行的字节数必须是 4 的整倍数(32位的整数倍),也就是DWORD 对齐的
//一行总的数据量(按位来说),是宽度*位深
//(x/32)得整数,再加上32就是比x大的最近的32的倍数,除以8就是像素的字节数
if (info_h.biHeight % 2 == 0)
{
pix_h = info_h.biHeight;
}
else
{
pix_h = info_h.biHeight + 1;
}
//宽(按字节)必须是4的的整数倍,高(按字节)必须是2的整数倍
//24位.宽*高*3,16位.宽*高*2,8位.宽*高,4位.宽*高/2,2位.宽*高/4,1位.宽*高/8
//要保证一行四字节扫描,所以宽为4字节的整数倍
//要保证1位的时候数据为字节的整数倍
w = pix_w * info_h.biBitCount/8;
h = pix_h;//为了方便定义的宽高
unsigned char *data,*be_data;
//be_data = (unsigned char*)malloc(h*w);//buffer大小应该与bmp中有效数据大小相同
data=(unsigned char*)malloc(w*h);//开辟一个缓冲区,把bmp的图像数据存在缓冲区里面
fseek(pbmp,file_h.bfOffBits,0);//指针指向图像的数据处
fread(data,w*h,1,pbmp);//把图像数据存在缓冲区里面
RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned int)pow(2, info_h.biBitCount));
if (info_h.biBitCount == 24)
{
//24位的话就把数据copy到rgbbuf里面
//memcpy是c和c++使用的内存拷贝函数,
//void *memcpy(void *dest, const void *src, size_t n);
memcpy(rgbbuf, data, w*h);
}
else if (info_h.biBitCount == 16)
{
if (file_h.bfOffBits == 70)
{
for (unsigned long Loop = 0; Loop < h * w; Loop += 2)
{
*rgbbuf = (data[Loop] & 0x1F) << 3;
//B:用0001 1111取出低字节的右五位,再放到目标字节的高5位(通过右移3位),得到五位的B
*(rgbbuf + 1) = ((data[Loop] & 0xE0) >> 3) + ((data[Loop + 1] & 0x07) << 5);
// G:11100000取出低字节的左三位,00000011取出高字节的右三位,合并后,放到再放到目标字节的高5位,得到6位的G
*(rgbbuf + 2) = (data[Loop + 1] & 0xF8);
///R:1111 1000取出高字节的中间五位,再放到目标字节的高5位,得到5位的R
rgbbuf += 3;
}
}
else
{
for (unsigned long Loop = 0; Loop < h * w; Loop += 2)
{
*rgbbuf = (data[Loop] & 0x1F) << 3;
*(rgbbuf + 1) = ((data[Loop] & 0xE0) >> 2) + ((data[Loop + 1] & 0x03) << 6);
*(rgbbuf + 2) = (data[Loop + 1] & 0x7C) << 1;
rgbbuf += 3;
}
}
}
//判断若为其他位深数,要通过调色板来解决
else
{
if (!MakePalette(pbmp, file_h, info_h, pRGB))
printf("Nopalette!");
unsigned char mask = 0;
for (unsigned long Loop = 0; Loop < w*h; Loop++)
{
switch (info_h.biBitCount)
{
case 1: mask = 0x80; break;
case 2: mask = 0xC0; break;
case 4: mask = 0xF0; break;
case 8: mask = 0xFF; break;
default:printf("biBitCount error!"); break;
}
int shiftCnt = 1;
while (mask)//一个字节里面的解析
{
unsigned char index = mask == 0xFF ? data[Loop] : ((data[Loop] & mask) >> (8 - shiftCnt * info_h.biBitCount));
*rgbbuf = pRGB[index].rgbBlue;
*(rgbbuf + 1) = pRGB[index].rgbGreen;
*(rgbbuf + 2) = pRGB[index].rgbRed;
if (info_h.biBitCount == 8) mask = 0;
else mask >>= info_h.biBitCount;
rgbbuf += 3;
shiftCnt++;
}
}
}
if (data)
free(data);
if (pRGB)
free(pRGB);
}
三、本次实验的注意点
RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned char)pow(2, info_h.biBitCount));
2.注意点
在原来写的rgb2yuv里面有这项宽高为偶数的规定如下
//if ((x_dim % 2) || (y_dim % 2)) return 1;
所以这次有高必须为2的整数倍的要求,否则当高为奇数时,会返回error
if (RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip))
{
printf("error");
return 0;
}
当我们把rgb2yuv里面的要求注释掉时,就不会产生这样的错误了,也是可以成功实现的,如下就是一个300*299的图,是可以运行出来的。