2

我想从.bmp文件中读取每个像素的 RGB 值,因此我可以将其bmp转换为适合 GBA(GameBoy Advance)的格式。

我只需要获取每个像素的 RGB,然后将此信息写入文件。

我正在尝试使用以下<windows.h>结构:

typedef struct
{
    char signature[2];
    unsigned int fileSize;
    unsigned int reserved;
    unsigned int offset;
}BmpHeader;

typedef struct
{
    unsigned int headerSize;
    unsigned int width;
    unsigned int height;
    unsigned short planeCount;
    unsigned short bitDepth;
    unsigned int compression;
    unsigned int compressedImageSize;
    unsigned int horizontalResolution;
    unsigned int verticalResolution;
    unsigned int numColors;
    unsigned int importantColors;

}BmpImageInfo;

typedef struct
{
    unsigned char blue;
    unsigned char green;
    unsigned char red;
    unsigned char reserved;
}Rgb;

typedef struct
{
    BmpHeader header;
    BmpImageInfo info;
    Rgb colors[256];
    unsigned short image[1];
}BmpFile;

但我只需要 RGB 结构。所以假设我读了“in.bmp”:

FILE *inFile, *outFile;
inFile = fopen("C://in.bmp", "rb");

Rgb Palette[256];
for(i=0;i<256;i++)
{
   fread(&Palette[i],sizeof(Rgb),1,inFile);
}

fclose(inFile);

这个对吗?如何仅将 RGB 信息写入文件?

4

8 回答 8

4

我做了一些测试并稍微扩展了 Patrice 的程序。我不是一个优秀的 C 程序员,所以所有的功劳都归功于他,我的部分不如他的优雅——很抱歉。

警告:前方有大量源代码。

#include <stdio.h>
#pragma pack(2)


typedef struct
{
    char signature[2];
    unsigned int fileSize;
    unsigned int reserved;
    unsigned int offset;
} BmpHeader;

typedef struct
{
    unsigned int headerSize;
    unsigned int width;
    unsigned int height;
    unsigned short planeCount;
    unsigned short bitDepth;
    unsigned int compression;
    unsigned int compressedImageSize;
    unsigned int horizontalResolution;
    unsigned int verticalResolution;
    unsigned int numColors;
    unsigned int importantColors;

} BmpImageInfo;

typedef struct
{
    unsigned char blue;
    unsigned char green;
    unsigned char red;
    //unsigned char reserved; Removed for convenience in fread; info.bitDepth/8 doesn't seem to work for some reason
} Rgb;


int main( int argc, char **argv ) {

    FILE *inFile;
    BmpHeader header;
    BmpImageInfo info;
    Rgb *palette;
    int i = 0;

    printf( "Opening file %s for reading.\n", argv[1] );

    inFile = fopen( argv[1], "rb" );
    if( !inFile ) {
        printf( "Error opening file %s.\n", argv[1] );
        return -1;
    }

    if( fread(&header, 1, sizeof(BmpHeader), inFile) != sizeof(BmpHeader) ) {
        printf( "Error reading bmp header.\n" );
        return -1;
    }

    if( fread(&info, 1, sizeof(BmpImageInfo), inFile) != sizeof(BmpImageInfo) ) {
        printf( "Error reading image info.\n" );
        return -1;
    }

    if( info.numColors > 0 ) {
        printf( "Reading palette.\n" );
        palette = (Rgb*)malloc(sizeof(Rgb) * info.numColors);
        if( fread(palette, sizeof(Rgb), info.numColors, inFile) != (info.numColors * sizeof(Rgb)) ) {
            printf( "Error reading palette.\n" );
            return -1; // error
        }
    }

    printf( "Opening file %s for writing.\n", argv[2] );
    FILE *outFile = fopen( argv[2], "wb" );
    if( !outFile ) {
        printf( "Error opening outputfile.\n" );
        return -1;
    }
    Rgb *pixel = (Rgb*) malloc( sizeof(Rgb) );
    int read, j;
    for( j=0; j<info.height; j++ ) {
        printf( "------ Row %d\n", j+1 );
        read = 0;
        for( i=0; i<info.width; i++ ) {
            if( fread(pixel, 1, sizeof(Rgb), inFile) != sizeof(Rgb) ) {
                printf( "Error reading pixel!\n" );
                return -1;
            }
            read += sizeof(Rgb);
            printf( "Pixel %d: %3d %3d %3d\n", i+1, pixel->red, pixel->green, pixel->blue );
        }
        if( read % 4 != 0 ) {
            read = 4 - (read%4);
            printf( "Padding: %d bytes\n", read );
            fread( pixel, read, 1, inFile );
        }
    }

    printf( "Done.\n" );
    fclose(inFile);
    fclose(outFile);

    printf( "\nBMP-Info:\n" );
    printf( "Width x Height: %i x %i\n", info.width, info.height );
    printf( "Depth: %i\n", (int)info.bitDepth );

    return 0;

}

该程序读取存储在文件中的像素信息。它考虑了填充,但仅适用于每像素颜色深度为 24 位的 bmps(如果您需要其他深度,则必须自定义 Rgb 结构)。希望这对您有所帮助,但正如我所说,它只是 Patrice 代码的扩展。

这是我的测试文件的示例输出:

$ ./a.out test.bmp out.txt
Opening file test.bmp for reading.
Opening file out.txt for writing.
------ Row 1
Pixel 1:   0   0   0
Pixel 2:   0   0   0
Pixel 3:   0   0   0
Pixel 4:   0   0   0
Pixel 5:   0   0   0
Padding: 1 bytes
------ Row 2
Pixel 1:   0   0   0
Pixel 2: 232  33  33
Pixel 3:   0   0   0
Pixel 4: 232  33  33
Pixel 5:   0   0   0
Padding: 1 bytes
------ Row 3
Pixel 1:   0   0   0
Pixel 2:   0   0   0
Pixel 3: 232  33  33
Pixel 4:   0   0   0
Pixel 5:   0   0   0
Padding: 1 bytes
------ Row 4
Pixel 1:   0   0   0
Pixel 2: 232  33  33
Pixel 3:   0   0   0
Pixel 4: 232  33  33
Pixel 5:   0   0   0
Padding: 1 bytes
------ Row 5
Pixel 1:   0   0   0
Pixel 2:   0   0   0
Pixel 3:   0   0   0
Pixel 4:   0   0   0
Pixel 5:   0   0   0
Padding: 1 bytes
Done.

BMP-Info:
Width x Height: 5 x 5
Depth: 24

编辑:是的,我的图像显示一个红十字。请注意,图像是倒置存储的,因此文件的第 1 行实际上是图像的第 5 行。D'oh 忘了写一些东西来文件打开代码,但这留给你作为练习;)。

于 2009-10-07T15:32:17.603 回答
4

如果您在 Windows 上,您可以使用win32中的LoadBitmap函数

然后给定句柄将其转换为 DIB 位图并以这种方式获取像素

于 2009-10-07T12:19:42.220 回答
3

您首先需要获取嵌入式调色板中可用的颜色数量。这在 DIB 标头中可用。

然后您可以读取包含调色板的所有颜色组件。

您可以查看所有标头信息,例如偏移量以了解查找位置:http ://en.wikipedia.org/wiki/BMP_file_format 。

这应该有效:(编辑:添加代码以写入文件)

FILE *inFile, *outFile;
BMPHeader header;
BMPImageInfo info;
RGB *palette, *p;
int i = 0;

inFile = fopen("C://in.bmp", "rb");
if( !inFile )
   return;

if( fread(&header, sizeof(BMPHeader), 1, inFile) != 1 )
   return; // Manage error and close file

if( fread&info, sizeof(BMPImageInfo), 1, inFile) != 1 )
   return; // Manage error and close file

if( info.numColors > 0 )
{
   palette = (RGB*)malloc(sizeof(RGB) * info.numColors);
   if( fread(palette, sizeof(RGB), info.numColors, inFile) != info.numColors )
      return; // manage error and close file
}

fclose(inFile)

// Binary method => if read later by another computer
outFile = fopen("path", "wb");
if( !outFile )
   return;

if( fwrite(&info.numColors, sizeof(unsigned int), 1, outFile) != 1 )
   return; // Manage Error and close file

if( fwrite(&palette, sizeof(RGB), info.numColors, outFile) != info.numColors )
   return; // Manage error and close file

fclose(outFile);

// Text method => if read later by human
outFile = fopen("path", "w");
if( !outFile )
   return;

for( i=0; i<info.numColors; ++i )
{
   p = &palette[i];
   if( fprintf(outFile, "R:%d, G:%d, B:%d\n", p->red, p->green, p->blue) < 0 )
      return; // Manage error and close file
}

fclose(outFile);
于 2009-10-07T12:29:16.083 回答
2

如果你保证它是一个未压缩的 24bpp 位图并且它的宽度可以被 4 整除,那么它相对简单:

  1. 读取BmpHeader文件开头的 a。
  2. 不求,读一读BmpImageInfo
  3. 从文件开头查找BmpHeader'soffset字节。请注意,24 位位图中没有调色板(至少,不是我们关心的调色板)!
  4. 阅读 BGR 三元组(按此顺序,这里没有reserved未使用的成员)。将有 (BmpImageInfo的成员)width * abs(height)三胞胎。我记得,如果height是正数(标准情况),您读取的第一行颜色将是图像的底线,从那里向上。但是,如果height为负数,则文件中的第一行颜色是图像的顶部,从那里向下。

如果你不能做出这些保证,那么事情就会变得更加复杂。

免责声明:我在这里无缘无故地吹响自己的号角。 几年前,我编写了一个很小的(一个源文件)实用程序,以一种可移植的(100% ANSI C)方式来完成你所说的事情:glbmp 库bmpread。它的来源可能会对您的问题有所启发——它可以处理任何位深度的未压缩(无 RLE)位图,并且应该在 GBA 上运行良好。

于 2009-10-07T16:51:32.060 回答
1

您可能会发现查看我多年前编写的用于读取和写入 BMP 文件的一些 C 代码很有用,这些代码位于:

http://david.tribble.com/src/bmp/bmp.html

我相信它可以处理各种像素位大小(1/2/4/8/24)以及 RLE 压缩。

于 2009-10-07T20:42:16.823 回答
1

请参阅 .bmp 文件格式的维基百科页面,该页面提供了有关文件结构的大量信息,应该可以帮助您解析它。

http://en.wikipedia.org/wiki/BMP_file_format

在您的代码中,您首先必须读取数据的起始地址(在文件头中指定)和图像的高度。然后寻求这个位置。之后,您逐像素读取一行(标题指定每个像素有多少位),并且必须注意其末尾的 32 位填充。请注意,在 24 位图像 (RGB) 中,信息以 BGR 顺序存储。此外,如果高度值不是负数,则图像会倒置存储。

于 2009-10-07T12:25:51.397 回答
0

我对 BMP 文件格式不熟悉,但您不需要先读入标头信息吗?就像是:

BmpHeader header;
fread(&header,sizeof(BmpHeader),1,inFile);

并阅读您需要的详细图像信息:

BmpImageInfo info;
fread(&info,sizeof(BmpImageInfo),1,inFile);

并阅读调色板信息。

一旦你有了它,你就会知道文件大小和数据偏移量。您可以预先分配足够的空间并一次读取所有内容,这可能是最简单的。或者,您可以分块读取并在进行时进行解析(减少没有足够内存的机会,但解析更复杂)。

您还可以从信息部分了解是否启用了压缩、图像尺寸等。

如果您一次读取所有内容,请跳转到数据的偏移量,然后执行以下操作:

Rgb* rgb = offset;
blueVal = rgb->blue;
greenVal = rgb->green;
redVal = rgb->red;
rgb += sizeof( Rgb );

等等。显然,该代码不会检查错误、缓冲区结束等,因此您需要这样做。您可能还必须阅读调色板信息才能理解图像数据。

或者,正如其他人所说,查看 Wikipedia 上的格式规范

于 2009-10-07T12:34:15.967 回答
0

如果 BMP 文件有调色板,那么下面的代码应该可以工作:

  FILE *inFile, *outFile;
  inFile = fopen("C:/in.bmp", "rb");
  Rgb Palette[256];
  if ( inFile ) {
    // Bypass headers
    fseek(inFile, sizeof(BmpHeader) + sizeof(BmpImageInfo), SEEK_SET);
    // Load the whole palette
    fread(Palette, sizeof(Palette), 1, inFile);
    fclose(inFile);
  }
于 2009-10-07T12:39:20.743 回答