有几个关键点:首先yuv420p的采样规则一定要明白。代码中注释已经写明了,还有rgb的数据,是不是倒序,还有是rgb排序,还是brg排序,根据rgb数据源的不同,程序处理也会不一样。
#include
#include
#include
#include
typedef struct /**** BMP file header structure (14 bytes) ****/
{
uint16_t bfType; /* file type ,set 0x4d42 */
uint32_t bfSize; /* file size 24位bmp=sizeof(BMPFILEHEADER) + sizeof(BMPINFOHEADER)+width*height*3*/
uint16_t bfReserved1; /* 保留 ,set 0 */
uint16_t bfReserved2; /* 保留 ,set 0 */
uint32_t bfOffBits; /*pos:10 Offset to bitmap data,一般设置为0x36 */
} BMPFILEHEADER;
typedef struct /**** BMP file info structure(40 bytes) ****/
{
uint32_t biSize; /* 本信息头的大小 */
uint32_t biWidth; /*pos:18 图像宽度,单位是像素*/
uint32_t biHeight; /*pos:22 图像高度,单位是像素,如果这是一个正数,说明图像数据是从图像左下角到右上角排列的 */
uint16_t biPlanes; /* Number of color planes ,set 1*/
uint16_t biBitCount; /* pos:28 每个像素占多少bit ,24位bmp值是24*/
uint32_t biCompression; /* 图像的压缩类型,最常用的就是0(BI_RGB),表示不压缩 */
uint32_t biSizeImage; /* pos:32位图数据的大小,当用BI_RGB格式时,可以设置为0 */
int32_t biXPelsPerMeter; /* X pixels per meter ,0x0EC4 */
int32_t biYPelsPerMeter; /* Y pixels per meter ,0x0EC4*/
uint32_t biClrUsed; /* Number of colors used,说明位图使用的调色板中的颜色索引数,为0说明使用所有 */
uint32_t biClrImportant; /* Number of important colors 说明对图像显示有重要影响的颜色索引数,为0说明都重要*/
} BMPINFOHEADER;
void bmp2rgb(const char* filename, uint8_t* rgbbuf){
FILE* fp = fopen(filename, "rb");
fseek(fp, 10, SEEK_SET); //跳到偏移量标记处
int bfOffBits = 0;
fread(&bfOffBits, 4, 1, fp);
int biSizeImage = 0;
fseek(fp, 0, SEEK_END);//文件指针指向文件尾
long filelen = ftell(fp); //获取文件大小,用于计算rgb数据大小
int datalen = filelen - bfOffBits;
rgbbuf = new uint8_t[datalen];
fseek(fp, bfOffBits, SEEK_SET);
fread(rgbbuf, 1, datalen, fp);
fclose(fp);
}
void bmp2rgbstore(const char* filename, char* output_filename){
FILE* outfp = fopen(output_filename, "wb");
FILE* fp = fopen(filename, "rb");
fseek(fp, 10, SEEK_SET); //跳到偏移量标记处
int bfOffBits = 0;
fread(&bfOffBits, 4, 1, fp);
fseek(fp, bfOffBits, SEEK_SET);
while (!feof(fp)){
uint8_t* temp = new uint8_t[1];
if (fread(temp, 1, 1, fp) > 0){
fwrite(temp, 1, 1, outfp);
}
delete[] temp;
}
fclose(fp);
fclose(outfp);
}
int main(int argc, char **argv){
//uint8_t* rgbbuf = NULL;
//bmp2rgb("sucai.bmp", rgbbuf);
//bmp2rgbstore("sucai.bmp", "sucai_rgb.rgb");
FILE* fp = fopen("sucai_rgb.rgb", "rb");
FILE* fpyuv = fopen("out.yuv", "wb");
int w = 400, h = 400;
unsigned char b, g, r;
unsigned char* ybuf = new unsigned char[w*h];
unsigned char* ubuf = new unsigned char[w*h / 4];
unsigned char* vbuf = new unsigned char[w*h / 4];
unsigned char*y = ybuf;
unsigned char* u = ubuf;
unsigned char *v = vbuf;
for (int i = 0; i 1 - i) * 3, SEEK_SET); //图像数据是左下角为图像第一个像素,从左往右,从下往上排列的。所以要反向读取。
for (int j = 0; j1, 1, fp);
fread(&g, 1, 1, fp);
fread(&r, 1, 1, fp); //图像是以BGR排序的
unsigned char Y = (unsigned char)((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
*y = Y;
y++;
/*
yuv420的采样规则:
一行一行的扫描采样,第一行采集u,不采集v。且y和u比例,2:1(即偶数位才采集u)
第二行采集v,不采集u,且y和v比例,2:1(即偶数位才采集v)
以此递推...
故整体采样比例y:u:v=8:2:2=4:1:1
*/
if (j % 2 == 0){//列数为偶素行才采集U或V
if (i % 2 == 0){//行数位偶数行采集U
unsigned char U = (unsigned char)((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
*(u++) = U;
}
else{
unsigned char V = (unsigned char)((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
*(v++) = V;
}
}
}
}
//yuv420p,先写y,再写u,在写v
fwrite(ybuf, 1, w*h, fpyuv);
fwrite(ubuf, 1, w*h / 4, fpyuv);
fwrite(vbuf, 1, w*h / 4, fpyuv);
fclose(fp);
fclose(fpyuv);
getchar();
return 0;
}