winston 发表于 2011-12-17 20:20:30

用C语言进行BMP文件的读写

bmp是BitMap(位图)的简称,也是所有windows上图片显示的基础。所有的图片格式,都必须转换成bmp才能进行最终的显示。所以,bmp文件的读写,就变得非常重要了。然而,很多人是借助于MFC类,C# 库函数,OpenCV,OpenGL等库函数进行bmp文件的读写。试想一下,如果你要在诸如DSP、FPGA之类的嵌入式设备上进行bmp文件的读写,总不能去安装一个庞大的MFC,C#类库吧?其实,我们完全可以抛开这些庞杂繁琐的类库和API函数,仅仅利用C语言,编写几个函数,就完全可以实现bmp文件的读写了。


本文的意图正在于此。


一个完整的bmp位图文件,可以分为文件信息头,位图信息头和RGB颜色阵列三个部分(希望对这三部分有详细了解的可以参考我的另外一篇文章:http://blog.csdn.net/carson2005/article/details/6227047)。文件信息头主要包含“是否是BMP文件”,文件的大小等信息。而位图信息头则主要包含bmp文件的位图宽度,高度,位平面,通道数等信息。而RGB颜色阵列,里面才真正包含我们所需要的bmp位图的像素数据。需要提醒的是,bmp位图的颜色阵列部分,像素数据的存储是以左下角为原点。也就是说,当你打开一个bmp图片并显示在电脑屏幕上的时,实际在存储的时候,这个图片的最左下角的像素是首先被存储在bmp文件中的。之后,按照从左到右,从下到上的顺序,依次进行像素数据的存储。如果,你存储的是3通道的位图数据(也就是我们通常说的彩图),那么它是按照B0G0R0B1G1R1B2G2R2...的顺序进行存储的,同时,还要考虑到4字节对齐的问题。OK,了解了这些基本概念,相信,自己编程实现一些bmp文件的读写函数并非难事。这里,我给出C语言的版本,仅供参考,如有错误,欢迎指正。

chenLeeCV.h
#ifndef CHENLEECV_H
#define CHENLEECV_H

typedef struct
{
        //unsigned short    bfType;
        unsigned long    bfSize;
        unsigned short    bfReserved1;
        unsigned short    bfReserved2;
        unsigned long    bfOffBits;
} ClBitMapFileHeader;

typedef struct
{
        unsigned longbiSize;
        long   biWidth;
        long   biHeight;
        unsigned short   biPlanes;
        unsigned short   biBitCount;
        unsigned longbiCompression;
        unsigned longbiSizeImage;
        long   biXPelsPerMeter;
        long   biYPelsPerMeter;
        unsigned long   biClrUsed;
        unsigned long   biClrImportant;
} ClBitMapInfoHeader;

typedef struct
{
        unsigned char rgbBlue; //该颜色的蓝色分量
        unsigned char rgbGreen; //该颜色的绿色分量
        unsigned char rgbRed; //该颜色的红色分量
        unsigned char rgbReserved; //保留值
} ClRgbQuad;

typedef struct
{
        int width;
        int height;
        int channels;
        unsigned char* imageData;
}ClImage;

ClImage* clLoadImage(char* path);
bool clSaveImage(char* path, ClImage* bmpImg);

#endif

chenLeeCV.cpp
#include "chenLeeCV.h"
#include <stdio.h>
#include <stdlib.h>

ClImage* clLoadImage(char* path)
{
        ClImage* bmpImg;
        FILE* pFile;
        unsigned short fileType;
        ClBitMapFileHeader bmpFileHeader;
        ClBitMapInfoHeader bmpInfoHeader;
        int channels = 1;
        int width = 0;
        int height = 0;
        int step = 0;
        int offset = 0;
        unsigned char pixVal;
        ClRgbQuad* quad;
        int i, j, k;

        bmpImg = (ClImage*)malloc(sizeof(ClImage));
        pFile = fopen(path, "rb");
        if (!pFile)
        {
                free(bmpImg);
                return NULL;
        }

        fread(&fileType, sizeof(unsigned short), 1, pFile);
        if (fileType == 0x4D42)
        {
                //printf("bmp file! \n");

                fread(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);
                /*printf("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");
                printf("bmp文件头信息:\n");
                printf("文件大小:%d \n", bmpFileHeader.bfSize);
                printf("保留字1:%d \n", bmpFileHeader.bfReserved1);
                printf("保留字2:%d \n", bmpFileHeader.bfReserved2);
                printf("位图数据偏移字节数:%d \n", bmpFileHeader.bfOffBits);*/

                fread(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);
                /*printf("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");
                printf("bmp文件信息头 \n");
                printf("结构体长度:%d \n", bmpInfoHeader.biSize);
                printf("位图宽度:%d \n", bmpInfoHeader.biWidth);
                printf("位图高度:%d \n", bmpInfoHeader.biHeight);
                printf("位图平面数:%d \n", bmpInfoHeader.biPlanes);
                printf("颜色位数:%d \n", bmpInfoHeader.biBitCount);
                printf("压缩方式:%d \n", bmpInfoHeader.biCompression);
                printf("实际位图数据占用的字节数:%d \n", bmpInfoHeader.biSizeImage);
                printf("X方向分辨率:%d \n", bmpInfoHeader.biXPelsPerMeter);
                printf("Y方向分辨率:%d \n", bmpInfoHeader.biYPelsPerMeter);
                printf("使用的颜色数:%d \n", bmpInfoHeader.biClrUsed);
                printf("重要颜色数:%d \n", bmpInfoHeader.biClrImportant);
                printf("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");*/

                if (bmpInfoHeader.biBitCount == 8)
                {
                        //printf("该文件有调色板,即该位图为非真彩色\n\n");
                        channels = 1;
                        width = bmpInfoHeader.biWidth;
                        height = bmpInfoHeader.biHeight;
                        offset = (channels*width)%4;
                        if (offset != 0)
                        {
                                offset = 4 - offset;
                        }
                        //bmpImg->mat = kzCreateMat(height, width, 1, 0);
                        bmpImg->width = width;
                        bmpImg->height = height;
                        bmpImg->channels = 1;
                        bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*width*height);
                        step = channels*width;

                        quad = (ClRgbQuad*)malloc(sizeof(ClRgbQuad)*256);
                        fread(quad, sizeof(ClRgbQuad), 256, pFile);
                        free(quad);

                        for (i=0; i<height; i++)
                        {
                                for (j=0; j<width; j++)
                                {
                                        fread(&pixVal, sizeof(unsigned char), 1, pFile);
                                        bmpImg->imageData[(height-1-i)*step+j] = pixVal;
                                }
                                if (offset != 0)
                                {
                                        for (j=0; j<offset; j++)
                                        {
                                                fread(&pixVal, sizeof(unsigned char), 1, pFile);
                                        }
                                }
                        }                       
                }
                else if (bmpInfoHeader.biBitCount == 24)
                {
                        //printf("该位图为24位真彩色\n\n");
                        channels = 3;
                        width = bmpInfoHeader.biWidth;
                        height = bmpInfoHeader.biHeight;

                        bmpImg->width = width;
                        bmpImg->height = height;
                        bmpImg->channels = 3;
                        bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*width*3*height);
                        step = channels*width;

                        offset = (channels*width)%4;
                        if (offset != 0)
                        {
                                offset = 4 - offset;
                        }

                        for (i=0; i<height; i++)
                        {
                                for (j=0; j<width; j++)
                                {
                                        for (k=0; k<3; k++)
                                        {
                                                fread(&pixVal, sizeof(unsigned char), 1, pFile);
                                                bmpImg->imageData[(height-1-i)*step+j*3+k] = pixVal;
                                        }
                                        //kzSetMat(bmpImg->mat, height-1-i, j, kzScalar(pixVal, pixVal, pixVal));
                                }
                                if (offset != 0)
                                {
                                        for (j=0; j<offset; j++)
                                        {
                                                fread(&pixVal, sizeof(unsigned char), 1, pFile);
                                        }
                                }
                        }
                }
        }

        return bmpImg;
}

bool clSaveImage(char* path, ClImage* bmpImg)
{
        FILE *pFile;
        unsigned short fileType;
        ClBitMapFileHeader bmpFileHeader;
        ClBitMapInfoHeader bmpInfoHeader;
        int step;
        int offset;
        unsigned char pixVal = '\0';
        int i, j;
        ClRgbQuad* quad;

        pFile = fopen(path, "wb");
        if (!pFile)
        {
                return false;
        }

        fileType = 0x4D42;
        fwrite(&fileType, sizeof(unsigned short), 1, pFile);

        if (bmpImg->channels == 3)//24位,3通道,彩图
        {
                step = bmpImg->channels*bmpImg->width;
                offset = step%4;
                if (offset != 4)
                {
                        step += 4-offset;
                }

                bmpFileHeader.bfSize = bmpImg->height*step + 54;
                bmpFileHeader.bfReserved1 = 0;
                bmpFileHeader.bfReserved2 = 0;
                bmpFileHeader.bfOffBits = 54;
                fwrite(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);

                bmpInfoHeader.biSize = 40;
                bmpInfoHeader.biWidth = bmpImg->width;
                bmpInfoHeader.biHeight = bmpImg->height;
                bmpInfoHeader.biPlanes = 1;
                bmpInfoHeader.biBitCount = 24;
                bmpInfoHeader.biCompression = 0;
                bmpInfoHeader.biSizeImage = bmpImg->height*step;
                bmpInfoHeader.biXPelsPerMeter = 0;
                bmpInfoHeader.biYPelsPerMeter = 0;
                bmpInfoHeader.biClrUsed = 0;
                bmpInfoHeader.biClrImportant = 0;
                fwrite(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);

                for (i=bmpImg->height-1; i>-1; i--)
                {
                        for (j=0; j<bmpImg->width; j++)
                        {
                                pixVal = bmpImg->imageData;
                                fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
                                pixVal = bmpImg->imageData;
                                fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
                                pixVal = bmpImg->imageData;
                                fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
                        }
                        if (offset!=0)
                        {
                                for (j=0; j<offset; j++)
                                {
                                        pixVal = 0;
                                        fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
                                }
                        }
                }
        }
        else if (bmpImg->channels == 1)//8位,单通道,灰度图
        {
                step = bmpImg->width;
                offset = step%4;
                if (offset != 4)
                {
                        step += 4-offset;
                }

                bmpFileHeader.bfSize = 54 + 256*4 + bmpImg->width;
                bmpFileHeader.bfReserved1 = 0;
                bmpFileHeader.bfReserved2 = 0;
                bmpFileHeader.bfOffBits = 54 + 256*4;
                fwrite(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);

                bmpInfoHeader.biSize = 40;
                bmpInfoHeader.biWidth = bmpImg->width;
                bmpInfoHeader.biHeight = bmpImg->height;
                bmpInfoHeader.biPlanes = 1;
                bmpInfoHeader.biBitCount = 8;
                bmpInfoHeader.biCompression = 0;
                bmpInfoHeader.biSizeImage = bmpImg->height*step;
                bmpInfoHeader.biXPelsPerMeter = 0;
                bmpInfoHeader.biYPelsPerMeter = 0;
                bmpInfoHeader.biClrUsed = 256;
                bmpInfoHeader.biClrImportant = 256;
                fwrite(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);

                quad = (ClRgbQuad*)malloc(sizeof(ClRgbQuad)*256);
                for (i=0; i<256; i++)
                {
                        quad.rgbBlue = i;
                        quad.rgbGreen = i;
                        quad.rgbRed = i;
                        quad.rgbReserved = 0;
                }
                fwrite(quad, sizeof(ClRgbQuad), 256, pFile);
                free(quad);

                for (i=bmpImg->height-1; i>-1; i--)
                {
                        for (j=0; j<bmpImg->width; j++)
                        {
                                pixVal = bmpImg->imageData;
                                fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
                        }
                        if (offset!=0)
                        {
                                for (j=0; j<offset; j++)
                                {
                                        pixVal = 0;
                                        fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
                                }
                        }
                }
        }
        fclose(pFile);

        return true;
}

main.cpp
#include "stdafx.h"
#include "chenLeeCV.h"


int _tmain(int argc, _TCHAR* argv[])
{
        ClImage* img = clLoadImage("c:/test.bmp");
        bool flag = clSaveImage("c:/result.bmp", img);
        if(flag)
        {
                printf("save ok... \n");
        }
       

        while(1);
        return 0;
}




作者:carson2005 发表于2011-12-17 17:10:22 原文链接
页: [1]
查看完整版本: 用C语言进行BMP文件的读写