8bpp BMP — привязка пикселей к таблице цветов; хочу прочитать только одну строку пикселей; Переполнение стека

У меня проблема с чтением 8-битного оттенка серого BMP. Я могу получить информацию из заголовка и прочитать палитру, но я не могу отнести значения пикселей к записям палитры. Вот Я нашел, как читать данные пикселей, но не на самом деле, как использовать их в случае bmp с палитрой. Я начинающий. Моя цель — читать только один ряд пикселей за раз.

Код:

#include <iostream>
#include <fstream>
using namespace std;

int main(int arc, char** argv)
{   const char* filename="Row_tst.bmp";
remove("test.txt");
ofstream out("test.txt",ios_base::app);//file for monitoring the results

FILE* f = fopen(filename, "rb");
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f); // read the header

int width = *(int*)&info[18];
int height = *(int*)&info[22];

unsigned char palette[1024]; //read the palette
fread(palette, sizeof(unsigned char), 1024, f);
for(int i=0;i<1024;i++)
{   out<<"\n";
out<<(int)palette[i];
}

int paletteSmall[256]; //1024-byte palette won't be needed in the future
for(int i=0;i<256;i++)
{   paletteSmall[i]=(int)palette[4*i];
out<<paletteSmall[i]<<"\n";
}

int size = width;

//for(int j=0;j<height;j++)
{   unsigned char* data = new unsigned char[size];
fread(data, sizeof(unsigned char), size, f);
for(int i=0;i<width;i++)
{   cout<<"\n"<<i<<"\t"<<paletteSmall[*(int*)&data[i]];
}
delete [] data;
}

fclose(f);

return 0;
}

То, что я получаю в test.txt, выглядит нормально — первые значения от 0 0 0 0 до 255 255 255 0 (палитра), следующие значения от 0 до 255 (paletteSmall).

Проблема в том, что я не могу отнести значения пикселей к записям таблицы цветов. Мое приложение перестает работать, и симптомы, вероятно, указывают на то, что оно пытается использовать какой-то несуществующий элемент таблицы. Если я правильно понимаю, пиксель из BMP с таблицей цветов должен содержать номер элемента таблицы цветов, поэтому я понятия не имею, почему он не работает. Я прошу вашей помощи.

0

Решение

Вы заставляете свои 8-битные значения быть прочитанными как int:

cout<<"\n"<<i<<"\t"<<paletteSmall[*(int*)&data[i]];

Количество приведений указывает, что у вас возникли проблемы и, возможно, вы решили добавить одно приведение за другим, пока оно не будет «скомпилировано». Как выясняется, составление без ошибок не такой как за работой без ошибок.

Что здесь происходит, так это то, что вы сила указатель данных для чтения 4 байта (или столько, сколько ваш локальный int размер в любом случае) и поэтому значение почти всегда будет превышать размер paletteSmall, (Кроме того, последняя пара значений будет недействительной в все обстоятельства, потому что вы читаете байты за пределами допустимого диапазона data.)

Поскольку данные изображения являются 8-битными, все, что вам нужно здесь, это

cout<<"\n"<<i<<"\t"<<paletteSmall[data[i]];

Нет необходимости бросать; data является беззнаковым символом *, поэтому его значения ограничены от 0 до 255, и paletteSmall это точно правильный размер.


На кастинге

Вопрос с Кастинг является то, что ваш компилятор будет жаловаться, если вы скажете, что для обработки определенного типа значения как будто это совсем другой тип. Используя актерский состав, вы говорите: «Поверь мне. Я знаю что делаю

Это может привести к нескольким проблемам, если вы на самом деле не знать 🙂

Например: такая строка, как ваша

int width = *(int*)&info[18];

кажется, работает, потому что он возвращает правильную информацию, но на самом деле это счастливый случай.

Массив info содержит несколько отключенных unsigned char значения, и вы говорите компилятору, что там является int хранится начиная с позиции № 18 — он доверяет вам и читает целое число. Это предполагает что (1) количество байтов что вы хотите объединить в целое число на самом деле число байтов, которое само использует для int (sizeof(int)) и (2) отдельные байты находятся в одном порядок как он использует внутренне (Порядок байтов).

Если любое из этих предположений неверно, вы можете получить удивительные результаты; и почти наверняка не то, что вы хотели.

Правильная процедура заключается в сканировании Формат файла BMP за то, как значение для width сохраняется, а затем использует эту информацию для получения данных, которые вы хотите. В этом случае, width «хранится в формате с прямым порядком байтов» и со смещением 18 как 4 байта. При этом вы можете использовать это вместо:

int width = info[18]+(info[19]<<8)+(info[20]<<16)+(info[21]<<24);

Нет предположений о том, насколько большой int есть (кроме того что должно быть по крайней мере 4 байта), без предположения о порядке (смещение значений «внутренне» не зависит от порядкового номера).

Так почему же это работает (по крайней мере, на вашем компьютере)? Самый распространенный размер для int в этом десятилетии 4 байта. Самый популярный тип ЦП хранит многобайтовые значения в том же порядке, в котором они хранятся в BMP. Добавьте это вместе, и ваш код будет работать на большинстве компьютеров в этом десятилетии. Счастливая случайность.

Выше может не быть верным, если вы хотите скомпилировать свой код на компьютере другого типа (например, встроенной системе ARM, которая использует другой порядок байтов), или когда используемый компилятор имеет меньший размер (… который сейчас будет очень старый компилятор) или больший размер для int (просто подождите еще 10 лет или около того), или если вы хотите настроить свой код для чтения файлов других типов (которые будут иметь свои собственные параметры, а используемый порядок байтов является одним из них).

1

Другие решения


По вопросам рекламы [email protected]