BMP文件读写以及RGB与YUV转换
来源:互联网 发布:清理恶意软件 编辑:程序博客网 时间:2024/05/16 15:33
最近学习图像信息处理,没想到会在BMP文件的读写以及RGB与YUV转换花费这么多时间。写下这篇文章,总结一下自己的经验教训。
一、BMP文件读写
1.1 BMP文件格式
位图文件(Bitmap-File,BMP)格式是Windows采用的图像文件存储格式,在Windows环境下运行的所有图像处理软件都支持这种格式。BMP位图文件默认的文件扩展名是bmp或者dib。BMP文件大体上分为四个部分:位图文件头、位图信息头、调色板
紧跟在调色板之后的是图像数据字节阵列。对于用到调色板的位图,图像数据就是该像素颜色在调色板中的索引值(逻辑色)。对于真彩色图,图像数据就是实际的R、G、B值。图像的每一扫描行由表示图像像素的连续的字节组成,每一行的字节数取决于图像的颜色数目和用像素表示的图像宽度。规定每一扫描行的字节数必需是4的整倍数,也就是DWORD对齐的。扫描行是由底向上存储的,这就是说,阵列中的第一个字节表示位图左下角的像素,而最后一个字节表示位图右上角的像素。如果是没有调色板的24位真彩色BMP,其格式可用下列结构定义(共54字节):
typedef struct bmp{ //14字节 unsigned short bfType; //文件标识 2字节 必须为BM unsigned int bfSize; //文件大小 4字节 unsigned short bfReserved1; //保留,每字节以"00"填写 2字节 unsigned short bfReserved2; //同上 2字节 unsigned int bfOffBits; //记录图像数据区的起始位置(图象数据相对于文件头字节的偏移量)。 4字节 //40字节 unsigned int biSize; //表示本结构的大小 4字节 int biWidth; //位图的宽度 4字节 int biHeight; //位图的高度 4字节 unsigned short biPlanes; //永远为1 , 2字节 unsigned short biBitCount; //位图的位数 分为1 4 8 16 24 32 ,2字节 unsigned int biCompression; //压缩说明 4字节 unsigned int biSizeImage; //表示位图数据区域的大小以字节为单位 4字节 int biXPelsPerMeter; //用象素/米表示的水平分辨率 4字节 int biYPelsPerMeter; //用象素/米表示的垂直分辨率 4字节 unsigned int biClrUsed; //位图使用的颜色索引数,如果为0,则颜色数为2的biBitCount次方 4字节 unsigned int biClrImportant; //对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要 4字节} BMP;1.2 BMP读写
可以参考这位大神的博客http://blog.csdn.net/leixiaohua1020/article/details/13506099 对1-8位与16位的BMP进行读写。
我为了方便操作,直接选用了24位BMP图片。这个比较简单,直接按照B/G/R的顺序写入缓冲区即可。
有一个坑点是图片前54个字节一定要写对 不然图片是无法正常显示的!!!
下面是我写的检测并读取BMP图片前54位字节的函数:
int ReadBmpHead(FILE * outfp,FILE* fp,char* buf_head,int* picwidth,int* picheight,long int biSizeImage){ short ind; char bm[2]; //读取文件头与图像头信息 判断是否为24位bmp图片 fseek(fp,0L,0); fread(bm,2,1,fp); fseek(fp,0L,0); fread(buf_head, 54, 1, fp); fwrite(buf_head, 1, 54, outfp); fseek(fp,28L,0); fread(&ind,2,1,fp); if(bm[0]!='B'||bm[1]!='M'||ind!=24){ printf("\n非24位BMP图片\n"); return 1; } fseek(fp, 18L, 0); fread(picwidth, 4, 1, fp); fread(picheight, 4, 1, fp); fseek(fp,34L,0);//图像数据区的大小 fread(&biSizeImage,4,1,fp); fseek(fp, 54L, 0); printf("图片尺寸%d*%d ",*picwidth,*picheight); // fread(buf_total, 1, 54+biSizeImage, fp); return 0;};之后,结合fread、fseek、fwrite函数进行文件流的读写即可,下面给出一个RGB直接转灰度图[方法比较粗暴]的例子:
fseek(fp, 54L, 0); for (i=0;i<picheight;i++) for (j = 0; j < picwidth; j++){ fread(&b, 1, 1, fp); fread(&g, 1, 1, fp); fread(&r, 1, 1, fp); //为防止rgb溢出,将其值赋给int型的r1,b1,g1进行之后的公式运算 r1 = r; b1 = b; g1 = g; //注意:这之后的步骤可根据需要自行修改 fwrite(&b1, 1, 1, outfp2); fwrite(&b1, 1, 1, outfp2); fwrite(&b1, 1, 1, outfp2); }
二、RGB转YUV 再转回RGB
YUV数据格式可以参考这篇文章:http://blog.csdn.net/beyond_cn/article/details/12998247
我的目标是RGB转YUV 再转回RGB,这个步骤的重点是解决转换过程中的溢出问题。为了简单,我直接用公式法;我选用的公式是
y= 0.2990*r1+0.5869*g1+0.1140*b1;
u= -0.1687*r1-0.3313*g1+0.5000*b1 +128;
v= 0.5000*r1-0.4187*g1-0.0813*b1 + 128;
中间再处理一下数据不在【0,255】之间的情况即可。这个是含偏移量的公式。经验证可以正确转换出我想看到的效果。其他的公式总是会出bug当然,这种方法没什么优化,都是浮点数运算。优化的话,可以参考这个:http://blog.csdn.net/chen825919148/article/details/7921475
附一张我用的测试图:【获得24位BMP图片的方式也很简单,直接用windows自带的画图工具,另存一下即可】
- BMP文件读写以及RGB与YUV转换
- 24位BMP图像RGB与YUV转换
- RGB与YUV转换
- RGB与YUV转换
- RGB与YUV转换
- RGB与YUV转换
- RGB与YUV的转换
- YUV与RGB的转换
- YUV与RGB格式转换
- RGB与YUV相互转换
- YUV与RGB格式转换
- YUV与RGB格式转换
- YUV 与 RGB的转换
- YUV与RGB格式转换
- RGB与YUV相互转换
- YUV与RGB格式转换
- YUY2(YUV) 与 RGB 格式图片的相互转换 以及 基于YUY2(YUV)的blending
- YUY2(YUV) 与 RGB 格式图片的相互转换 以及 基于YUY2(YUV)的blending
- HandlerThread用法
- MongoDB基本管理命令
- 第一个OpenGL出现无法识别glGenVertexArrays问题
- 算典05_例题_01_UVA-10474
- java多线程编程核心技术6-单例模式与多线程
- BMP文件读写以及RGB与YUV转换
- [李景山php]每天laravel[027]-laravel 基础知识 --- 请求
- 大白话系列之C#委托与事件讲解(三)
- 如何在CakePHP中使用Ajax请求
- 蓝桥杯 算法训练 最大最小公倍数
- python变量和输出
- 简单优先队列实现-基于最小堆
- HTML5中form表单基础内容
- LeetCode Week 1