本学期选修了陈老师的数字图像处理,使用的课本是冈萨雷斯的《数字图像处理》,这门课程目前的作业是搭建一个简单的图像处理平台,由于我平时对Linux QT等开源技术比较感兴趣,所以我选择了在Ubuntu下的Qt环境作为我的开发环境,在下面的半学期里,我会写一些关于这门课程的总结和心得,以及在学习和开发过程中遇到的问题以及解决方法。
关于课程的第一次作业,作业要求是完成一个BMP,png图像读取并显示,要求BMP自己来做图像解析,png格式利用开源库libpng来做图像解析。虽然非常的简单,但是由于我的编程经验很少,所以做起来还是遇见了很多困难。闲话少说,下面介绍一下我的实现。
关于BMP文件的读取,我将BMP文件的读取封装在一个BitMapClass类中,关于BMP图像格式的讲解可以百度和google一下,很多很多。。。下面是我定义的一些数据结构和这个类的声明
/* * BitmapClass.h * * Created on: 2011-10-7 * Author: */ #ifndef BITMAPCLASS_H_ #define BITMAPCLASS_H_ #include <QVector> #include <QFile> #include<QString> #include <QMessageBox> #include <math.h> typedef quint8 BYTE; //BYTE表示8位无符号整数,一个字节 typedef quint16 WORD; //WORD表示16位无符号整数,两个字节 typedef quint32 DWORD; //DWORD表示32位无符号整数,四个个字节 typedef qint32 LONG; //LONG表示32位整数,四个字节 /*bitmap图像文件的文件头格式定义,一共14个字节*/ typedef struct tagBITMAPFILEHEADER { WORD bfType; //位图文件类型,必须是Ox424D,即字符创“BM” DWORD bfSize; //位图文件大小,包括头文件的四个字节 WORD bfReserved1; //bfReserved1,bfReserved2,Windows保留字,暂时不用 WORD bfReserved2; DWORD bfOffBits; //从文件头到实际位图数据的偏移字节数 } BITMAPFILEHEADER; /*位图信息头BITMAPINFOHEADER结构定义*/ typedef struct tagBITMAPINFOHEADER { DWORD biSize; //本结构长度,为40个字节 LONG biWidth; //位图的宽度,以像素为单位 LONG biHeight; //位图的高度,以像素位单位 WORD biPlanes; //目标设备的级别,必须是1 /*每个像素所占的位数, * 其值必须为 1(黑白图像)、4(16 色图)、8(256色)、24(真彩色图), * 新的 BMP 格式支持 32 位色。 */ WORD biBitCount; /*位图压缩类型,有效的值为 B * I_RGB(未经压缩)、BI_RLE8、BI_RLE4、BI_BITFILEDS *(均为 Windows 定义常量) 这里只讨论未经压缩的情况, biCompression=BI_RGB。 * */ DWORD biCompression; DWORD biSizeImage; //实际的位图数据占用的字节数 LONG biXPelsPerMeter; //指定目标设备的水平分辨率,单位是像素/米。 LONG biYPelsPerMeter; //指定目标设备的垂直分辨率,单位是像素/米。 DWORD biClrUsed; //位图实际用到的颜色数,如果该值为零,则用到的颜色数为 2 的 biBitCount 次幂。 DWORD biClrImportant; //位图显示过程中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的。 } BITMAPINFOHEADER; /*颜色表的结构体 * 颜色表实际上是一个 RGBQUAD 结构的数组,数组的长度由 biClrUsed 指定(如果该值为零,则由 biBitCount 指定,即 2 的 biBitCount 次幂个元素)。 * */ typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD; class BitmapClass { private: BITMAPFILEHEADER BitMapFileHeader; BITMAPINFOHEADER BitMapInfoHeader; /*由于读取的二进制数是倒序的,如读取的文件大小bfSize二进制表示为36 8C 0A 00 * 对应的实际数值的二进制应该为000A8C36h=691254B * 所以在使用二进制数值之前要对其进行转换 * */ quint32 DWORDtoQuint32(DWORD n); quint16 WORDtoQuint16(WORD n); qint32 LONGtoQint32(LONG n); public: BYTE *bmpData; //记录位图数据的指针 quint32 fileSize; //文件的大小 quint16 bitCount; //图像的颜色位数 quint32 dataSize; //位图数据的大小 qint32 width; //位图的宽度 qint32 height; //位图的高度 bool flag; //标志是否有图片保存 BYTE format;//format标志BMP的图片类型(8:表示8位BMP图,1:表示2位位图,24:表示24位真彩色位图) RGBQUAD *pColorTable; QString ReadBitmapFile(QString filename); BitmapClass(); BitmapClass(QString filename); virtual ~BitmapClass(); }; #endif /* BITMAPCLASS_H_ */
这里使用了Qt提供的IO借口来实现文件的读取BMP文件,使用二进制流来读取文件,并将文件内容解析到定义的数据结构中,下面是主要的文件读取操作的源码:
QString BitmapClass::ReadBitmapFile(QString filename) { QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { return "File can not be opened!"; } else { QDataStream in(&file); //创建输入流 in.setVersion(QDataStream::Qt_4_6); /*开始读取文件头信息*/ /*===================================*/ in >> BitMapFileHeader.bfType; //读取文件类型 in >> BitMapFileHeader.bfSize; //读取文件大小 in >> BitMapFileHeader.bfReserved1; //读取两个保留字 in >> BitMapFileHeader.bfReserved2; in >> BitMapFileHeader.bfOffBits; //读取偏移量 /*===================================*/ /**文件头信息读取结束*/ /*开始读取位图信息头*/ /*====================================*/ in >> BitMapInfoHeader.biSize; in >> BitMapInfoHeader.biWidth; in >> BitMapInfoHeader.biHeight; in >> BitMapInfoHeader.biPlanes; in >> BitMapInfoHeader.biBitCount; in >> BitMapInfoHeader.biCompression; in >> BitMapInfoHeader.biSizeImage; in >> BitMapInfoHeader.biXPelsPerMeter; in >> BitMapInfoHeader.biYPelsPerMeter; in >> BitMapInfoHeader.biClrUsed; in >> BitMapInfoHeader.biClrImportant; /*====================================*/ /*位图信息头读取结束*/ /*开始读取颜色表*/ /*====================================*/ //获得图像的颜色位数 format = WORDtoQuint16(BitMapInfoHeader.biBitCount); if (format == 1) //对于黑白图 { pColorTable = new RGBQUAD[2]; //读入颜色表 for (int i = 0; i < 2; i++) { in >> pColorTable[i].rgbBlue; in >> pColorTable[i].rgbGreen; in >> pColorTable[i].rgbRed; in >> pColorTable[i].rgbReserved; } } if (format == 8) //对于灰度图,共有256种颜色 { pColorTable = new RGBQUAD[256]; //读入颜色表 for (int i = 0; i < 256; i++) { in >> pColorTable[i].rgbBlue; in >> pColorTable[i].rgbGreen; in >> pColorTable[i].rgbRed; in >> pColorTable[i].rgbReserved; } } //24位真彩色图没有颜色表 /*====================================*/ /*颜色表读取结束*/ /*开始读取位图数据*/ /*====================================*/ //求得图像数据的字节数 quint32 length = DWORDtoQuint32(BitMapFileHeader.bfSize) - DWORDtoQuint32(BitMapFileHeader.bfOffBits); bmpData = new BYTE[length]; for (quint32 i = 0; i < length; i++) { in >> bmpData[i]; } /*====================================*/ /*位图数据读取结束*/ fileSize = DWORDtoQuint32(BitMapFileHeader.bfSize); bitCount = WORDtoQuint16(BitMapInfoHeader.biBitCount); dataSize = DWORDtoQuint32(BitMapFileHeader.bfSize) - DWORDtoQuint32(BitMapFileHeader.bfOffBits); width = LONGtoQint32(BitMapInfoHeader.biWidth); height = LONGtoQint32(BitMapInfoHeader.biHeight); flag = true; } file.close(); return "Success"; }
按照二进制流读出是按照字节来读出的,其读出数据有一个特点,就是一个字节内的二进制数据是高位在左低位在右,但是在字节之间排列正好相反,举个例子,我们要读取的文件大小bfSize的十六进制表示为36 8C 0A 00,如果将这个数据读入到一个unsigned int32中,其十进制数为915147264(B)约等于872.75GB,这显然是不可能,而图像的的实际大小应该是 00 0A 8C 36,为691254(B)约等于675KB,这才是实际的文件大小,根据这点性质我写了一个转换函数,将逆序的多字节数据转化为正常数据,下面是这两个函数的实现
quint32 BitmapClass::DWORDtoQuint32(DWORD n) { quint32 r = 0x00000000, temp = 0x00000000; temp = n >> 24; //取出第一个字节 r = r + temp; temp = (n & (0x00ff0000)) >> 8; //取出第二个字节 r = r + temp; temp = (n & (0x0000ff00)) << 8; //取出第三个字节 r = r + temp; temp = (n & (0x000000ff)) << 24; //取出第四个字节 r = r + temp; return r; }
值得一提的是在处理有符号数的时候需要考虑实现逻辑右移的问题,要不可能会出错
qint32 BitmapClass::LONGtoQint32(LONG n) { qint32 r = 0x00000000, temp = 0x00000000; temp = n >> 24; //取出第一个字节 temp = temp & 0x000000ff; //为了实现逻辑右移 r = r + temp; temp = (n & (0x00ff0000)) >> 8; //取出第二个字节 temp = temp & 0x00ffffff; //实现右移的逻辑化 r = r + temp; temp = (n & (0x0000ff00)) << 8; //取出第三个字节 r = r + temp; temp = (n & (0x000000ff)) << 24; //取出第四个字节 r = r + temp; return r; }
好了,这就是我对BMP文件读取功能的一个基本的介绍,我的编程能力不强,希望能有大侠对我的代码提出批评,改进~
明天继续总结关于png图像读取的内容。^_^
//Post by Blogilo