【问题标题】:Read bitmap file into structure将位图文件读入结构
【发布时间】:2012-12-26 02:22:22
【问题描述】:

我想将一个位图文件读入一个结构并像这样操作它。制作镜像效果,但我不明白我应该创建哪种结构才能读入它。

感谢您的帮助。

【问题讨论】:

  • @H2CO3:一个字:错。
  • @Agent_L 为什么,准确地说?
  • @H2CO3 因为 BMP 文件格式是在结构中指定的,并且有可用的结构并准备好将文件读入其中。使用普通数组不会提供有关数据结构的任何信息。
  • @Agent_L 看来你误会了我 :) 我说的是实际的 BMP 数据(这比一些标题混乱更有趣,这是必要的,但我们对这些信息感到好奇操作是位图本身。)
  • @H2CO3 当然可以,但是要进入数组阶段,您需要至少打2级结构。

标签: c bitmap structure bitmapimage


【解决方案1】:

»这是手动加载 .BMP 文件的方式

位图文件格式:

  • 位图文件头
  • 位图信息标头
  • 调色板数据
  • 位图数据

以此类推,代码部分。这是我们需要创建的结构来保存位图文件头。

#pragma pack(push, 1)

typedef struct tagBITMAPFILEHEADER
{
    WORD bfType;  //specifies the file type
    DWORD bfSize;  //specifies the size in bytes of the bitmap file
    WORD bfReserved1;  //reserved; must be 0
    WORD bfReserved2;  //reserved; must be 0
    DWORD bfOffBits;  //specifies the offset in bytes from the bitmapfileheader to the bitmap bits
}BITMAPFILEHEADER;

#pragma pack(pop)

bftype 字段检查您是否确实在加载 .BMP 文件,如果是,则该字段应为 0x4D42。

现在我们需要创建我们的 bitmapinfoheader 结构。这包含有关我们位图的信息。

#pragma pack(push, 1)

typedef struct tagBITMAPINFOHEADER
{
    DWORD biSize;  //specifies the number of bytes required by the struct
    LONG biWidth;  //specifies width in pixels
    LONG biHeight;  //specifies height in pixels
    WORD biPlanes;  //specifies the number of color planes, must be 1
    WORD biBitCount;  //specifies the number of bits per pixel
    DWORD biCompression;  //specifies the type of compression
    DWORD biSizeImage;  //size of image in bytes
    LONG biXPelsPerMeter;  //number of pixels per meter in x axis
    LONG biYPelsPerMeter;  //number of pixels per meter in y axis
    DWORD biClrUsed;  //number of colors used by the bitmap
    DWORD biClrImportant;  //number of colors that are important
}BITMAPINFOHEADER;

#pragma pack(pop)

现在开始加载我们的位图。

unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{
    FILE *filePtr;  //our file pointer
    BITMAPFILEHEADER bitmapFileHeader;  //our bitmap file header
    unsigned char *bitmapImage;  //store image data
    int imageIdx=0;  //image index counter
    unsigned char tempRGB;  //our swap variable

    //open file in read binary mode
    filePtr = fopen(filename,"rb");
    if (filePtr == NULL)
        return NULL;

    //read the bitmap file header
    fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER),1,filePtr);

    //verify that this is a .BMP file by checking bitmap id
    if (bitmapFileHeader.bfType !=0x4D42)
    {
        fclose(filePtr);
        return NULL;
    }

    //read the bitmap info header
    fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER),1,filePtr); 

    //move file pointer to the beginning of bitmap data
    fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

    //allocate enough memory for the bitmap image data
    bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);

    //verify memory allocation
    if (!bitmapImage)
    {
        free(bitmapImage);
        fclose(filePtr);
        return NULL;
    }

    //read in the bitmap image data
    fread(bitmapImage,bitmapInfoHeader->biSizeImage,1,filePtr);

    //make sure bitmap image data was read
    if (bitmapImage == NULL)
    {
        fclose(filePtr);
        return NULL;
    }

    //swap the R and B values to get RGB (bitmap is BGR)
    for (imageIdx = 0;imageIdx < bitmapInfoHeader->biSizeImage;imageIdx+=3)
    {
        tempRGB = bitmapImage[imageIdx];
        bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
        bitmapImage[imageIdx + 2] = tempRGB;
    }

    //close file and return bitmap image data
    fclose(filePtr);
    return bitmapImage;
}

现在要利用所有这些:

BITMAPINFOHEADER bitmapInfoHeader;
unsigned char *bitmapData;
// ...
bitmapData = LoadBitmapFile("mypic.bmp",&bitmapInfoHeader);
//now do what you want with it, later on I will show you how to display it in a normal window

稍后我将发布写入 .BMP,以及如何加载 targa 文件以及如何显示它们。«

引自:http://www.vbforums.com/showthread.php?261522-C-C-Loading-Bitmap-Files-%28Manually%29(用户:BeholderOf)。 (做了一些小的修正)

【讨论】:

  • 你忘记了结构填充。在 struct 中使用 WORD 与 32 位(或更多)系统上的二进制读取配对是自找麻烦。
  • 您的意思是需要添加#pragma pack(push,1)
  • 当然。编译指示,或为项目全局设置。否则你不知道bfReserved1bfReserved2 相距多远。可能是 2 个字节(如您所愿),可能是 4 个字节,也可能是其他字节。
  • 我刚刚了解到它也应该在 GCC 中工作:gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html(我认为这是 Microsoft 特有的功能)
  • 使用 gcc 您也可以使用打包属性:__attribute__((__packed__))(参见:gcc.gnu.org/onlinedocs/gcc-3.1.1/gcc/Type-Attributes.html
【解决方案2】:

here 一个简短的工作示例。

它将一个 wav 文件转换为 bmp(很久以前我有这样的乐趣)。

代码:

#include <stdio.h>
#include <strings.h>
#include <sndfile.h>
#include <stdlib.h>
#include <math.h>

#define RATE 44100

typedef struct {
    unsigned short type;                 /* Magic identifier            */
    unsigned int size;                       /* File size in bytes          */
    unsigned int reserved;
    unsigned int offset;                     /* Offset to image data, bytes */
} HEADER;
typedef struct {
    unsigned int size;               /* Header size in bytes      */
    int width,height;                /* Width and height of image */
    unsigned short planes;       /* Number of colour planes   */
    unsigned short bits;         /* Bits per pixel            */
    unsigned int compression;        /* Compression type          */
    unsigned int imagesize;          /* Image size in bytes       */
    int xresolution,yresolution;     /* Pixels per meter          */
    unsigned int ncolours;           /* Number of colours         */
    unsigned int importantcolours;   /* Important colours         */
} INFOHEADER;
typedef struct {
    unsigned char r,g,b,junk;
} COLOURINDEX;


int main(int argc, char *argv[]){
    int i,j,rd;
    int gotindex = 0;
    unsigned char grey,r,g,b;
    double ampl;
    short _2byte[2];
    HEADER header;
    INFOHEADER infoheader;
    COLOURINDEX colourindex[256];
    FILE *fptr; 
    SNDFILE* sndfile = NULL;
    SF_INFO sfinfo;
    long rate = RATE;

    void (*bmpread)();
    void _eightbit(){
        if(fread(&grey, sizeof(unsigned char), 1, fptr) != 1){
        fprintf(stderr,"Image read failed\n");
        exit(-1);
        }
        if (gotindex){
            ampl =  colourindex[grey].r * 64. +
                colourindex[grey].g * 128.+
                colourindex[grey].b * 64.;
        } else {
            ampl = grey * 256. - 32768.;
        }
//      printf("%.2f\n", ampl);
    }
    void _twentyfourbit(){
        do{
            if((rd = fread(&b, sizeof(unsigned char), 1, fptr)) != 1) break;
            if((rd = fread(&g, sizeof(unsigned char), 1, fptr)) != 1) break;
            if((rd = fread(&r, sizeof(unsigned char), 1, fptr)) != 1) break;
        }while(0);
        if(rd != 1){    
            fprintf(stderr,"Image read failed\n");
            exit(-1);
        }
        ampl = r * 64. + g * 128. + b * 64. - 32768.;
//      printf("%.2f\n", ampl);
    }
    if (argc < 3){
        printf("Usage: %s <input.bmp> <output.wav> [samplerate]\n", argv[0]);
        printf("For example:\n\t%s pict.bmp sample.wav 44100 2\n", argv[0]);
        exit(0);
    }
    printf("Input file: %s\n", argv[1]);
    printf("Output file: %s\n", argv[2]);
    if(argc > 3) rate = atoi(argv[3]);
    if(rate < 4000) rate = 4000;
    //if(argc > 4) channels = atoi(argv[4]);        
    sfinfo.samplerate = rate;
    sfinfo.channels = 2;
    sfinfo.format = SF_FORMAT_WAV|SF_FORMAT_PCM_16;
    if((fptr = fopen(argv[1],"r")) == NULL) {
        fprintf(stderr,"Unable to open BMP file \"%s\"\n",argv[1]);
        exit(-1);
    }
        /* Read and check BMP header */
    if(fread(&header.type, 2, 1, fptr) != 1){
        fprintf(stderr, "Failed to read BMP header\n");
        exit(-1);
    }
    if(header.type != 'M'*256+'B'){
        fprintf(stderr, "File is not bmp type\n");
        exit(-1);
    }
    do{
        if((rd = fread(&header.size, 4, 1, fptr)) != 1) break;
        printf("File size: %d bytes\n", header.size);
        if((rd = fread(&header.reserved, 4, 1, fptr)) != 1) break;
        if((rd = fread(&header.offset, 4, 1, fptr)) != 1) break;
        printf("Offset to image data is %d bytes\n", header.offset);
    }while(0);
    if(rd =! 1){
        fprintf(stderr, "Error reading file\n");
        exit(-1);
    }
    /* Read and check the information header */
    if (fread(&infoheader, sizeof(INFOHEADER), 1, fptr) != 1) {
        fprintf(stderr,"Failed to read BMP info header\n");
        exit(-1);
    }
    printf("Image size = %d x %d\n", infoheader.width, infoheader.height);
    printf("Number of colour planes is %d\n", infoheader.planes);
    printf("Bits per pixel is %d\n", infoheader.bits);
    printf("Compression type is %d\n", infoheader.compression);
    printf("Number of colours is %d\n", infoheader.ncolours);
    printf("Number of required colours is %d\n", infoheader.importantcolours);
    /* Read the lookup table if there is one */
    for (i=0; i<255; i++){
        colourindex[i].r = rand() % 256;
        colourindex[i].g = rand() % 256;
        colourindex[i].b = rand() % 256;
        colourindex[i].junk = rand() % 256;
    }
    if (infoheader.ncolours > 0) {
        for (i=0; i<infoheader.ncolours; i++){
            do{
            if ((rd = fread(&colourindex[i].b, sizeof(unsigned char),1,fptr)) != 1)
                break;
            if ((rd = fread(&colourindex[i].g, sizeof(unsigned char),1,fptr)) != 1)
                break;
            if ((rd = fread(&colourindex[i].r, sizeof(unsigned char),1,fptr)) != 1)
                break;
            if ((rd = fread(&colourindex[i].junk, sizeof(unsigned char),1,fptr)) != 1)
                break;
            }while(0);
            if(rd != 1){
                fprintf(stderr,"Image read failed\n");
                exit(-1);
            }           
            printf("%3d\t%3d\t%3d\t%3d\n", i,
            colourindex[i].r, colourindex[i].g, colourindex[i].b);
        }
        gotindex = 1;
    }
    if(infoheader.bits < 8){
        printf("Too small image map depth (%d < 8)\n", infoheader.bits);
        exit(-1);
    }
    /* Seek to the start of the image data */
    fseek(fptr, header.offset, SEEK_SET);
    printf("Creating 16bit WAV %liHz.\n", rate);
    sndfile = sf_open(argv[2], SFM_WRITE, &sfinfo);
    if(sndfile == NULL){
        fprintf(stderr, "Cannot open output file!\n"); exit(-1);
    }
    bmpread = _eightbit;
    if(infoheader.bits == 24)
        bmpread = _twentyfourbit;

    /* Read the image */
    for (j=0;j<infoheader.height;j++) {
        _2byte[1] = 32700;
        for (i=0;i<infoheader.width;i++) {
            bmpread();
            _2byte[0] = (short)ampl;
            sf_write_short(sndfile, _2byte, 2);
            _2byte[1] = 0;
        } // i
    } // j
    fclose(fptr);
    sf_close(sndfile);
}

【讨论】:

  • 您需要可移植地读取二进制文件。便携式读取例程在这里github.com/MalcolmMcLean/ieee754/blob/master/binaryio.c,虽然整数例程很简单,但它们可能并不明显。便携式 BMP 格式阅读器在这里github.com/MalcolmMcLean/babyxrc/blob/master/src/bmp.c
  • @MalcolmMcLean 您的代码非常慢。更好的例子是here。此外,您的代码不可移植,因为它使用 int/short 等固定类型(如 int64_t、uint32_t 等)。在 8/16 或 32 位架构上尝试!
  • 该代码不可移植。如果您有保证的位宽类型,那么使用它是合理的。但这并不能解决问题,只是将其硬编码。我的将适用于任何超过 16 位的 int 大小,因此适用于任何符合 C 的编译器。
【解决方案3】:

因此,要读取位图,您需要@ollo 回答的结构,但也有一种更简单的方法。

  • 如果您使用的是 Windows,则只需在代码中添加 #include &lt;wingdi.h&gt;,您就可以访问所有结构,而无需将它们写入单独的文件中。
  • 如果您需要一些示例代码,那么我已经对 bmp 文件实现了各种过滤器,例如:
  1. 灰度
  2. 边缘检测
  3. 模糊
  4. 棕褐色

在 CS50x 课程中,但问题是生成的 output.bmp 在 CS50 ide 上完全正确,但它在我装有 Windows 10 的本地计算机上不起作用.所以我对它做了一些改变,它奏效了。该代码对于 Linux 和其他操作系统也是可移植的。我所做更改并将在您的系统上运行的代码是 here

【讨论】:

  • 你的github链接有必要吗?
  • 我的项目中有多个文件,如果我提供指向我的 github 的链接有什么问题。事实上它更好,因为我已经上传了一个详细的 README,并且对于不同的作品有不同的文件,如果有人想在其系统上运行该 BMP 程序,那么他/她可以简单地 fork 存储库。
  • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
猜你喜欢
  • 1970-01-01
  • 2021-02-13
  • 2016-10-24
  • 1970-01-01
  • 1970-01-01
  • 2016-06-11
  • 1970-01-01
相关资源
最近更新 更多