BMP转换YUV实验报告

BMP转YUV实验报告

 学号:201510413025                姓名: 宋靳锞                       班级:  15广电工3班

本次实验结果如下:

24位图


16位图


8位


4位:


1位:


300*299的图片

右边的黑色就是补得黑框


一、BMP文件的基础知识

位图由4部分组成

位图文件头

BITMAPFILEHEADER

包含 BMP 图像文件的类型、显示内容等信息

位图信息头

BITMAPINFOHEADER

 BMP 图像的宽、高、压缩方法,以及定义颜色等信息
调色板PALETTE
可选,真彩色图(24位的 BMP)就不需要调色板
位图数据IMAGEDATA
 24 位图中直接使用 RGB,而其他的小于 24 位的使用调色板中颜色索引值
BMP的各个部分都有结构体定义

位图文件头

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;

        调色板实际上是一个数组,它所包含的元素与位图所具有的颜色数相同,决定于biClrUsedbiBitCount字段。数组中每个元素的类型是一个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 <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "rgb2yuv.h" 
#include <Windows.h>


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=<uint> */
	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 <stdio.h>
#include <malloc.h>
#include <math.h>
#include <windows.h>
//调色板
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);

}
三、本次实验的注意点
1.8位图的时候
RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned char)pow(2, info_h.biBitCount));
错误如下:

删除的地方,是不对的,调色板在1~8位的时候调用,8位时2^8=256,但是unsigned char最大是255,改成unsigned int型的最大为2^32,就不会溢出了

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的图,是可以运行出来的。



发布了6 篇原创文章 · 获赞 3 · 访问量 6018
展开阅读全文

这个bmp的RGB转YUV,再用Y值输出灰度图为什么运行不出来?

01-23

# 谢谢诸位大佬,我觉得没什么毛病了啊…… ``` #include<stdio.h> #include<stdlib.h> #include<malloc.h> typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; /* bmp文件头 */ #pragma pack(1) typedef struct BMPFILEHEADER { BYTE bfType; //文件类型,必须是0x424D,即字符“BM” DWORD bSize; // 文件的大小 WORD bReserved1; // 保留值,必须设置为0 WORD bReserved2; // 保留值,必须设置为0 DWORD bOffset; // 文件头的最后到图像数据位开始的偏移量 }BMPHeader; #pragma pack() /* 位图数据信息结构*/ #pragma pack(1) typedef struct BMP_INFO { DWORD bInfoSize; // 结构的大小 long bWidth; // 图像的宽度 long bHeight; // 图像的高度 WORD bPlanes; // 图像的平面数 WORD bBitCount; // 颜色/像素的位数 DWORD bCompression; // 压缩类型 DWORD bmpImageSize; // DIB数据区的大小,以字节为单位 long bXPelsPerMeter; // 水平分辨率 long bYPelsPerMeter; // 垂直分辨率 DWORD bClrUsed; // 使用的色彩数 DWORD bClrImportant; // 重要的颜色数 }BMPInfo; #pragma pack() /* 彩色表:调色板 */ #pragma pack(1) typedef struct RGB_QUAD { BYTE rgbBlue; // 蓝色强度 BYTE rgbGreen; // 绿色强度 BYTE rgbRed; // 红色强度 BYTE rgbReversed; // 保留值 }RGB; #pragma pack() int main() { FILE *fp,*fg; BMPHeader *fh; BMPInfo *fi; RGB *fq; BYTE data[2000][2000][3]; //存储RGB图像的像素点 BYTE yuv[2000][2000][3]; //yuv BYTE data_gray[2000]; //存储灰度图像的像素点 int i,j,k; printf("%d",0); fp=fopen("test.bmp","rb"); //打开bmp文件 if (fp==NULL){ printf("Can't open the file!\n"); return 0; } fh=(BMPHeader*)malloc(sizeof(BMPHeader)); fi=(BMPInfo*)malloc(sizeof(BMPInfo)); //读取位图头结构和信息头 fread(fh,sizeof(BMPHeader),1,fp); fread(fi,sizeof(BMPInfo),1,fp); //修改头信息 fi->bBitCount=8; fi->bmpImageSize=((fi->bWidth*3+3)/4)*4*fi->bHeight; //fi->biClrUsed=256; fh->bOffset=sizeof(BMPHeader)+sizeof(BMPInfo)+256*sizeof(RGB); fh->bSize=fh->bOffset+fi->bmpImageSize; printf("%d",123); for(i=0;i<fi->bHeight;i++) //读取RGB图像像素 { for(j=0;j<(fi->bWidth+3)/4*4;j++) { for(k=0;k<3;k++){ fread(&data[i][j][k],1,1,fp); } } } /*rgb2yuv*/ for(i=0;i<fi->bHeight;i++) { for(j=0;j<(fi->bWidth+3)/4*4;j++) { yuv[i][j][0]=0.299*data[i][j][2]+0.587*data[i][j][1]+0.114*data[i][j][0];//Y yuv[i][j][1]=0.493*(data[i][j][2]-yuv[i][j][0]);//U yuv[i][j][2]=0.877*(data[i][j][2]-yuv[i][j][0]);//V } } /*创建灰色图像*/ fg=fopen("gray.bmp","wb"); if(fg==NULL) printf("Wrong!(write a gray bmp)\n"); //创建调色板 fq=(RGB*)malloc(256*sizeof(RGB)); for(i=0;i<256;i++) { fq[i].rgbBlue=fq[i].rgbGreen=fq[i].rgbRed=i; //fq[i].rgbReversed=0; } fwrite(fh,sizeof(BMPHeader),1,fg); //将头信息写入 fwrite(fi,sizeof(BMPInfo),1,fg); fwrite(fq,sizeof(RGB),256,fg); for(i=0;i<fi->bHeight;i++){ //读取yuv中的Y值并写入灰度图 for(j=0;j<(fi->bWidth+3)/4*4;j++){ data_gray[j]=(int)yuv[i][j][0]; } fwrite(data_gray,j,1,fg); } free(fh); free(fi); free(fq); fclose(fp); fclose(fg); return 0; } ``` 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览