Получение значений RGB для каждого пикселя из растрового изображения с разрешением 24 бита на пиксель для преобразования в формат GBA на языке C.
Вопрос
Я хочу прочитать значения RGB для каждого пикселя из .bmp
файл, чтобы я мог преобразовать 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?
Решение
Сначала вам нужно получить количество цветов, доступных во встроенной палитре.Это доступно в заголовке 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);
Другие советы
Если вы используете Windows, вы можете использовать Загрузить битмап функция из Win32
затем, учитывая дескриптор, преобразуйте его в растровое изображение DIB и таким образом доберитесь до пикселей.
Я провел небольшое тестирование и немного расширил программу Патриса.Я не очень хороший программист на 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;
}
Эта программа считывает информацию о пикселях, хранящуюся в файле.Он учитывает заполнение, но работает только с BMP с глубиной цвета 24 бита на пиксель (если вам нужны другие глубины, вам придется настроить структуру Rgb).Надеюсь, это вам поможет, но, как я уже сказал, это всего лишь расширение кода Патриса.
Вот пример вывода моего тестового файла:
$ ./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 изображения.Да, забыл написать что-то, чтобы файл открывался, но это оставлено вам в качестве упражнения ;).
Если вам гарантировано, что это несжатое растровое изображение размером 24 бита на пиксель и его ширина делится на 4, это относительно просто:
- Прочитать
BmpHeader
в начале файла. - Не ища, прочитайте
BmpImageInfo
. - Стремитесь к
BmpHeader
'soffset
байты из начало файла.Обратите внимание, что в 24-битных растровых изображениях нет палитры (по крайней мере, той, которая нас интересует)! - Прочитайте тройки BGR (именно в таком порядке, и здесь нет
reserved
неиспользуемый элемент здесь).Будет (BmpImageInfo
участники)width * abs(height)
тройки.Насколько я помню, еслиheight
положительный (стандартный случай), первая цветная строка, которую вы прочтете, будет нижний линия изображения, идущая оттуда вверх.Еслиheight
отрицательно, однако первая строка цвета в файле — это вершина изображения, спускающегося оттуда.
Если вы не можете дать эти гарантии, тогда все становится намного сложнее.
Отказ от ответственности:Я здесь совершенно безвозмездно трублю в свой рог. Несколько лет назад я написал крошечную утилиту (один исходный файл), которая делает именно то, о чем вы говорите, портативным (100% ANSI C) способом: глбмп libbmpread.Его источник может пролить некоторый свет на вашу проблему — он обрабатывает несжатые (без RLE) растровые изображения любой разрядности и должен нормально работать на GBA.
См. страницу в Википедии с форматом файла .bmp, которая предоставляет много информации о структуре файла и поможет вам его проанализировать.
http://en.wikipedia.org/wiki/BMP_file_format
В вашем коде вам сначала нужно прочитать начальный адрес данных (указанный в заголовке файла) и высоту изображения.Тогда стремитесь к этой позиции.После этого вы читаете одну строку попиксельно (в заголовке указано, сколько бит на пиксель) и вам нужно позаботиться о 32-битном заполнении в конце.Обратите внимание, что в 24-битном изображении (RGB) информация хранится в порядке BGR.Также, если значение высоты не является отрицательным, изображение сохраняется в перевернутом виде.
Я не знаком с форматом файла 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 );
и так далее.Очевидно, что этот код не проверяет наличие ошибок, конца буфера и т. д., поэтому вам придется это сделать.Вам, вероятно, также придется прочитать информацию о палитре, чтобы понять данные изображения.
Или, как сказал кто-то другой, посмотрите спецификацию формата в Википедии.
Если в файле 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);
}
Возможно, вам будет полезно взглянуть на код C, который я написал много лет назад для чтения и записи файлов BMP, расположенный по адресу:
http://david.tribble.com/src/bmp/bmp.html
Я считаю, что он обрабатывает различные размеры пикселей (1/2/4/8/24), а также сжатие RLE.