从 24bpp 位图中获取每个像素的 RGB 值,以便在 C 中转换为 GBA 格式
Posted
技术标签:
【中文标题】从 24bpp 位图中获取每个像素的 RGB 值,以便在 C 中转换为 GBA 格式【英文标题】:Getting RGB values for each pixel from a 24bpp Bitmap for conversion to GBA format in C 【发布时间】:2009-10-07 12:15:19 【问题描述】:我想从.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 信息写入文件?
【问题讨论】:
RGB结构中保留字节的作用是什么? 【参考方案1】:如果您在 Windows 上,您可以使用 win32 中的 LoadBitmap 函数
然后给定句柄将其转换为 DIB 位图并以这种方式获取像素
【讨论】:
【参考方案2】:我做了一些测试并稍微扩展了 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 忘了写一些东西来文件打开代码,但这留给你作为练习;)。
【讨论】:
谢谢 - 这对我来说也很好,几乎完全符合我的想法,现在我必须稍微处理一下代码,以便它适合我的需要,但我有一个很好的开始,谢谢;) 非常努力。我建议不要逐像素读取,因为这往往非常慢。更容易的是预先分配 (width+padding)*height*sizeof(RGB) 并一口气阅读整个内容。之后,您只需查看正确的内存位置即可查看所有 RGB 值。【参考方案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);
【讨论】:
然后当我将此信息写入文件时,我只是: fwrite(palette,sizeof(RGB),1,inFile); ? 调色板包含它所包含的每种颜色的所有合成值。如果您只想将调色板复制到一个文件中,您可以在不打开文件的情况下执行fwrite(palette, sizeof(RGB), info.numColors, outFile);
:fopen("path", "wb");
然后这取决于您想要做什么。
感谢您的回复,但是看,我要写入该文件的内容以及我需要从 bmp 文件中使用的是每个像素的 RGB,因此我需要读取图像之后的信息标题(如果我说的有道理)
如果我理解您想将所有组件值写入文本文件?
不一定是 txt 文件,它可能有任何扩展名 .. 但是是的,这就是我想要做的 .. 所以我可以使用这些文件并将它们显示在 gba 上 - 我只能读取信息并将其写入内存以显示它【参考方案4】:
如果你保证它是一个未压缩的 24bpp 位图并且它的宽度可以被 4 整除,那就比较简单了:
-
在文件开头读取
BmpHeader
。
不用寻找,阅读BmpImageInfo
。
从文件的开头查找BmpHeader
的offset
字节。请注意,24 位位图中没有调色板(至少,不是我们关心的)!
读取 BGR 三元组(按此顺序,这里没有 reserved
未使用的成员)。将有(BmpImageInfo
的成员)width * abs(height)
三胞胎。我记得,如果height
是正数(标准情况),您读取的第一行颜色将是图像的底部 行,从那里向上。但是,如果height
为负数,则文件中的第一行颜色是图像的顶部,从那里向下。
如果你不能做出这些保证,那么事情就会变得更加复杂。
免责声明:我在这里无缘无故地吹响自己的号角。 几年前,我编写了一个小型(一个源文件)实用程序来完全按照您的意思做,在便携式(100% ANSI C) 方式:glbmp libbmpread。它的来源可能会对您的问题有所启发——它可以处理任何位深度的未压缩(无 RLE)位图,并且应该在 GBA 上运行良好。
【讨论】:
嘿,感谢您为我指明了正确的方向,明天我会看看并测试该工具:D。我还在方便的 svens 帖子中发现,确实在 ds/gba 上它会向后读取 RGB -> BGR,所以我还有一些事情要做,但我希望让它工作。再次感谢。 glbmp 已移至github。给我带来了微笑;我原以为小而快的 C 的日子已经一去不复返了。 Camille,我将使用新链接更新问题。谢谢!【参考方案5】:查看 .bmp 文件格式的***页面,该页面提供了有关文件结构的大量信息,应该可以帮助您解析它。
http://en.wikipedia.org/wiki/BMP_file_format
在您的代码中,您首先必须读取数据的起始地址(在文件头中指定)和图像的高度。然后寻求这个位置。之后,您逐像素读取一行(标题指定每个像素有多少位),并且必须注意其末尾的 32 位填充。请注意,在 24 位图像 (RGB) 中,信息以 BGR 顺序存储。此外,如果高度值不是负数,则图像会倒置存储。
【讨论】:
【参考方案6】:您可能会发现查看我多年前编写的用于读取和写入 BMP 文件的一些 C 代码很有用,位于:
http://david.tribble.com/src/bmp/bmp.html
我相信它可以处理各种像素位大小 (1/2/4/8/24) 以及 RLE 压缩。
【讨论】:
非常有趣。 bin2bmp.exe 似乎是 16 位版本,bmpdump.exe 有效。 :)【参考方案7】:我不熟悉 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 上的格式规范
【讨论】:
我将始终将其用于 32x32 位图,因此我不需要标题信息 - 我只想知道如何准确获取每个像素的信息。 好的,很公平。您仍然可以阅读它以便您可以跳过它,或者如果您知道它的长度而不阅读它,则寻找它。更详细地检查格式规范以查看标题信息是否是固定长度的,在这种情况下您可以尝试寻找过去。它看起来固定长度,但我没有仔细看。【参考方案8】:如果 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);
【讨论】:
仅当调色板存在且包含 256 种颜色而调色板可以包含 0 到 2^n 种颜色时才有效。 @Patrice Bernassola,当然。我假设七号想要阅读 256 调色板。以上是关于从 24bpp 位图中获取每个像素的 RGB 值,以便在 C 中转换为 GBA 格式的主要内容,如果未能解决你的问题,请参考以下文章
lcd中像素深度bpp和像素格式(比如RGB,YUV)的关系