[课程作业]基于Qt的一个简单的数字图像处理软件的开发——BMP文件的读入 - Magic Cpatain

时间:2024-02-29 19:32:09

本学期选修了陈老师的数字图像处理,使用的课本是冈萨雷斯的《数字图像处理》,这门课程目前的作业是搭建一个简单的图像处理平台,由于我平时对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

posted on 2011-10-28 23:49  Magic Cpatain  阅读(2890)  评论(0编辑  收藏  举报