Как читать bmp файл c

Обновлено: 06.07.2024

1. Если есть возможность использовать GDI+, то задача решается совсем просто. Там есть класс Bitmap, а в нём функции FromFile() и GetPixel().

3. Если нет возможности воспользоваться предыдущими решениями, могу дать примитивнейший тест для чтения BMP. Но только он даже не все форматы BMP сможет прочитать, не говоря уже о других. Дать?

Давай!
GetPixel - это фактически считывается интенсивность пикселя с экрана, а мне нада именно с файла. (GetPixel даёт разные результаты на разных компах, отличаются они мало, но . На разных видеоадаптерах - разные результаты.) MSDN: bitmaps [Win32]->Using bitmaps->Storing an Image
Статья по сохранению в файл bitmap, прочитать и сделать все наоборот :)

Спасибо за помощь!
Я уже сам роздуплился.

filename - имя файла, который открываем.

FILE *filePtr;
BITMAPFILEHEADER bitmapFileHeader;
unsigned char *bitmapImage;
unsigned char tempRGB;
int imageIdx=0;

//открываем файл для чтения
filePtr = fopen(filename,"rb");
if (filePtr == NULL)
return NULL;

//считываем заголовок файла
fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER,1,filePtr);

//проверяем, что это - bmp файл
if (bitmapFileHeader.bfType !=0x4D42)
fclose(filePtr);
return NULL;
>

//читаем заголовок информации растра
fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER),1,filePtr);

//перемещаемся для чтения области данных .bmp файла
fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

//выделение объёма памяти, достаточно для чтения файла
bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);

//проверка выделеной памяти
if (!bitmapImage)
free(bitmapImage);
fclose(filePtr);
return NULL;
>

//чтение данных .bmp файла
fread(bitmapImage,bitmapInfoHeader->biSizeImage,filePtr);

//проверяем считались ли данные
if (bitmapImage == NULL)
fclose(filePtr);
return NULL;
>

//меняем местами r и b чтобы получить RGB вместо BGR
for (imageIdx = 0,imageIdx < bitmapInfoHeader->biSizeImage;imageIdx+=3)
tempRGB = bitmapImage[imageIdx];
bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
bitmapImage[imageIdx + 2] = tempRGB;
>

Есть задача с помощью средств самого Си считать монохромный BMP-файл размером 320x240 (9600 байт). Естественно я сразу полез на Википедию, дабы узнать как устроен формат.
Итак, собственно вопрос: при попытке считать первую же структуру BITMAPFILEHEADER из файла получается фэйл. Вот текст самой программы.

И это очевидно неправильно. Во-первых размер файла 0. Я решил тип поля bfSize заменить на unsigned short. Результат был таким
Почему получается все поля принимают правильные значение, а поле bfOffBits такое кривое? Не могу понять Последний раз редактировалось MooNDeaR; 27.05.2012 в 20:55 .

На С не пишу, так что сразу прошу извинить за, возможно, глупые вопросы.

Взял первый попавшийся BMP-файл и заглянул в него HEX-редактором - все правильно, значение 36h, т.е. 54 - как и положено.

Может, объявленная Вами структура имеет выравнивание на 4 байта?
Прочитайте первые 14 байт побайтно и выведите результат.

PS. Честно говоря, я первые 14 байт при чтении всегда пропускаю - в них все равно нет ничего интересного.

PPS. Да, еще, выведите размер структуры. На Паскале это было бы sizeof(bmpheader), как на С - не знаю, может, так же.

Последний раз редактировалось s-andriano; 27.05.2012 в 18:46 . Не знаю где глюк, наверное в википедии, но после замены структуры на такой вид:
Повторю свою рекомендацию выяснить размер структуры. Если она окажется равной 18, а не 14 байтам - дело в выравнивании.
Я рекомендую все-таки разобраться с этим, потому что после заголовка файла будет заголовок изображения, в котором тоже чередуются поля разного размера.

Узнал размер структуры. Она выравнивается до 16 байт. Что всё равно не объясняет кривое считывание первых данных, ведь теоретически выравнивание дописывает байты к концу структуры, что испортило бы мне считывание следующей структуры BITMAPINFOHEADER, но никак не Хедера.

Нашел причину в кривой работе функции fread(). Она ОЧЕНЬ странно как-то считает данные. Не более двух байт за раз. Получаются вот такие перлы:
Есть последовательность байтов: 00000000 00000000 00000000 00101000 (число 40 в формате ulong)

Если записать структуру так:

Затем считать её фунцией fread() вот таким образом:

а затем посмотреть значения X и Y, то получим, как и ожидали значения 0 (первые 2 байта нули) и 40 (вторые два байта).

НО! Вот если эту же структуру записать так:

и считать те же четыре байта, то должны получить, теоретически, число 40. Или я не прав? Что же мы получаем на самом деле (просмотрел двоичный вид числа):

Т.е. получается он сначала считал первые 2 байта и записал их в конец, потом еще 2 байта и записал их "сверху тех" и в результате получается число 2621440.

Что за шутки я не знаю, и понятия пока не имею как правильно это обходить

P.S.
Посмотрел на всё это дело из HEX-редактора.
Все числа оказывается записаны с конца, т.е. от младшего байта, к старшему. Т.е. поледовательность:

Тобишь, согласно Википедии порядок байтов идет как Little-endian. Тогда мне вообще непонятен принцип работы функции fread().

Язык C читает и сохраняет изображения в формате BMP

Иногда бывает необходимо проанализировать данные BMP в процессе разработки.Следующее кратко описывает состав данных BMP, а затем язык C для чтения и сохранения кода изображения в формате BMP.

Типичный формат файла растрового изображения обычно содержит следующие блоки данных:

  1. Заголовок файла BMP: сохранить общую информацию файла растрового изображения.
  2. Заголовок информации о растровом изображении: сохранить подробную информацию о растровом изображении. Информация о растровом изображении: сохраните подробную информацию о растровом изображении.
  3. Палитра: сохраните определение используемых цветов. Палитра: сохраните определение используемых цветов.
  4. Растровые данные: сохраните фактическое изображение одного пикселя за другим. Растровые данные: сохраните фактическое изображение одного пикселя за другим.

1. Заголовок файла BMP (14 байт)

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

Эта часть является идентификационной информацией.Типичная прикладная программа сначала считывает эту часть данных, чтобы убедиться, что это действительно растровый файл и не поврежден.

Структура заголовка битовой карты определяется следующим образом:

2. Заголовок растровой информации (40 байт).

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

Структура заголовка информации о битовой карте определяется следующим образом:

Переменные структуры разрешаются следующим образом:

  • uint32_t size; 15-18 байт: определяет размер следующего блока (BitmapInfoHeader), используемого для описания изображения, то есть количество байтов, занимаемых этой структурой, его значение: 40
  • int32_t width; 19-22 байта: ширина растрового изображения в пикселях.
  • int32_t height; 23-26 байтов: высота растрового изображения в пикселях.
  • uint16_t planes; 27-28 байт: сохранить количество используемых цветовых плоскостей. Используется не часто.
  • uint16_t bit_count; 29-30 байт: сохранить количество бит на пиксель, которое представляет собой глубину цвета изображения. Общие значения: 1 (двухцветная шкала серого), 4 (16-цветная шкала серого), 8 (256-цветная шкала серого) и 24 (цвет).
  • uint32_t сжатие; 31-34 байта: определяют используемый алгоритм сжатия. Допустимые значения: 0, 1, 2, 3, 4, 5.
    • 0-без сжатия (также обозначается BI_RGB)
    • Кодирование длины 1 прогона 8 бит / пиксель (также выражается как BI_RLE8)
    • Кодирование длиной 2 прогона 4 бита / пиксель (также выражается посредством BI_RLE4)
    • 3-битное поле (также обозначается как BI_BITFIELDS)
    • Изображение 4-JPEG (также обозначается как BI_JPEG)
    • 5-PNG изображение (также представленное BI_PNG)

    3. Палитра

    Структура палитры BMP определяется следующим образом:

    Размер таблицы цветов зависит от используемого цветового режима: двухцветные изображения - 8 байтов; 16-цветные изображения - 64 байта; 256-цветные изображения - 1024 байта. Среди них каждые 4 байта представляют цвет, а также B (синий), G (зеленый), R (красный), альфа (значение прозрачности 32-битного растрового изображения, как правило, не требуется). То есть первые 4 байта представляют цвет цвета номер 1, следующие представляют цвет цвета номер 2 и так далее.

    Количество данных структуры RGBQUAD в таблице цветов определяется параметром biBitCount. Когда biBitCount = 1,4,8, имеется 2,16,256 записей соответственно:

    • Когда biBitCount = 1, это двухцветное изображение, в растровом изображении BMP есть 2 структуры данных RGBQUAD, а палитра занимает 4 байта данных, поэтому длина палитры двухцветного изображения составляет 2 * 4, что составляет 8 байтов.
    • Когда biBitCount = 4, это 16-цветное изображение, имеется 16 структур данных RGBQUAD в растровом изображении BMP, а палитра занимает 4 байта данных, поэтому длина палитры из 16 изображений составляет 16 * 4, что составляет 64 байта.
    • Когда biBitCount = 8, это 256-цветное изображение, 256 структур данных RGBQUAD в растровом изображении BMP, а палитра занимает 4 байта данных, поэтому длина палитры 256-цветного изображения составляет 256 * 4, что составляет 1024 байта.
    • Когда biBitCount = 16, 24 или 32, таблица цветов отсутствует.

    4. Растровые данные

    Данные растрового изображения записывают значение каждого пикселя растрового изображения.
    Пиксели сохраняются снизу вверх, слева направо。
    Каждый пиксель представлен одним или несколькими байтами.
    Если количество байтов горизонтальной линии изображения не кратно 4, эта строка заполняется нулевыми байтами, обычно кодом ASCII 0。

    Изображение 5 * 6 имеет 30 пикселей, поскольку номер столбца 6 не кратен 4, он будет отображаться как:
    xxxxxx00 xxxxxx00 xxxxxx00 xxxxxx00 xxxxxx00
    , где x представляет номер цветовой палитры, а 0 представляет заполненный нулевой байт

    Картинка 4 * 4 имеет 16 пикселей, поскольку количество столбцов точно кратно 4, оно будет отображаться как:
    xxxx xxxx xxxx xxxx

    Подскажите как это можно сделать, если никаких специальных библиотек не стоит.

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



    Немного вроде разобралась со структурой BMP:

    HEADER // блок заголовка с фиксированной длиной 14 байт

    WORD bfType "BM" // говорит о том, что данный файл BMP
    DWORD bfSize // общая длина в байтах
    WORD bfrez-1 0 // первое поле зарезервировано со значением 0
    WORD dfrez-2 0 // второе поле зарезервировано со значением 0
    DWORD bfoffBits // смещение блока битового изображения относительно начала файла

    INFO // информационный блок с фиксированной длиной 40 байт

    DWORD bisize 40 // длина блока INFO
    LONG biWidth // ширина растрового изображения
    LONG biHeight // высота растрового изображения
    WORD biPlanes 1 // количество цветовых слоёв, пока 1
    WORD biBitCount // глубина цвета
    DWORD biCompress 0 // признак сжатия, пока 0
    DWORD biSizeImage // размер битового массива изображения в байтах
    LONG biXRels // разрешение по оси Х в пикселях на метр
    LONG biYRels // разрешение по оси У в пикселях на метр
    DWORD biClrUsed 0 // количество используемых цветов
    DWORD biClrImport 0 // количество важных цветов

    Дальше идут: необязательный блок палитры и битовый массив изображения

    Добавлено 28.04.11, 20:34
    Если есть какой-нибудь файл, например: 1.bmp и в переменную adress занесён адрес начала этого файла,

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



    Файл - уже считан в буфер в памяти или отображен на память?

    Описываешь нужные структуры как структуры C/C++
    адрес буфера приводишь к адресу на описанную структуру (если файл лежит на диске, считываешь нужную порцию в буфер)
    Считываешь нужное поле структуры.

    если я правильно поняла, то должно получиться что-то вроде этого

    struct bmpFILE
    unsigned short type;
    int dlinaFILE; // длина файла
    unsigned short rezerv1,
    rezerv2;
    int smeshenie , // смещение от начала до битового изображения
    dlinaINFO,
    shirina, // ширина растра
    visota; // высота растра
    unsigned short sloi,
    glubina; // глубина цвета
    int szhatie,
    dlina , // длина битового массива изображения
    Xtochek, // количество пикселей по оси Х
    Ytochek, // количество пикселей по оси У
    kolichestvo,
    tsveta;
    . И ГДЕ-ТО ЗДЕСЬ ДОЛЖНА БЫТЬ ПАЛИТРА И БИТОВОЕ ИЗОБРАЖЕНИЕ .
    >

    Добавлено 29.04.11, 07:54
    Что можно дописать в структуру для хранения палитры и битового изображения,
    если файл может быть произвольной длины?



    Ссылка классная, но я так чувствую, что долго буду разбираться в этом коде

    Добавлено 29.04.11, 09:12
    в коде есть строчка HANDLE hIn, hOut; ,
    а что такое HANDLE ?



    Уууууу. Как все запущено.
    Это тип данных такой.
    А начинать писать программы лучше с Hello World. в коде есть строчка HANDLE hIn, hOut;,
    а что такое HANDLE?
    HANDLE - это тип данных такой, дескриптор ресурса, в том конкретном примере - это дескриптор файла, уникальный идентификатор - может так понятней будет.



    Подскажите как это можно сделать, если никаких специальных библиотек не стоит.

    Получилась вот такая программка

    unsigned int* fun(сchar* fin)
    BITMAPFILEHEADER bfh;
    BITMAPINFOHEADER bih;
    HANDLE hIn;
    unsigned int width,
    height,
    position=1,
    i, j;
    RGBQUAD palette[256];
    BYTE *inByf;
    DWORD RW;

    hIn=CreateFile(fin, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);

    if(hIn==INVALID_HANDLE_VALUE)
    CloseHandle(hIn);
    return NULL;
    >
    else
    ReadFile(hIn, &bfh, sizeof(bfh), &RW, NULL);
    ReadFile(hIn, &bih, sizeof(bih), &RW, NULL);
    ReadFile(hIn, palette, 256*sizeof(RGBQUAD), &RW, NULL);

    SetFilePointer(hIn, bfh.bfOffBits, NULL, FILE_BEGIN);

    massiv=new unsigned int [3*height*width+1];
    massiv[0]=width;

    for(i=0; i<height; i++)
    ReadFile(hIn, inBuf, width, &RW, NULL);
    for(j=0; j<width; j++)
    massiv[position]=palette[inBuf[j]].rgbRed;
    massiv[position+1]=palette[inBuf[j]].rgbGreen;
    massiv[position+2]=palette[inBuf[j]].rgbBlue;
    position+=3;
    >
    SetFilePointer(hIn, (3*width)%4, NULL, FILE_CURRENT);
    >
    delete inBuf; delete massiv;
    CloseHandle(hIn);

    Добавлено 30.04.11, 19:48
    правда она недоделанная немного

    выдаёт ошибку при создании динамического массива в следующей строке
    massiv=new unsigned int [3*height*width+1];

    undeclared identifier (необъявленный идентификатор),
    не могу понять почему. Может кто подскажет?

    unsigned int* fun(char* fin)
    BITMAPFILEHEADER bfh; // в hfh хранятся данные из блока HEADER
    BITMAPINFOHEADER bih; // в bih хранятся данные из блока INFO
    HANDLE hIn; // hIn - поток, в котором читается переданный файл BMP
    unsigned int width, // переменная для хранения ширины растра
    height, // переменная для хранения высоты растра
    position=2, // переменная, указывающая на свободное место в массиве
    i, j, // переменные-счётчики для циклов for
    *massiv; // массив, в котором будут храниться цвета пикселей
    RGBQUAD palette[256]; // массив для хранения палитры (ТОЛЬКО ДЛЯ 256-ЦВЕТНЫХ КАРТИНОК)
    BYTE *inBuf; // массив, в который будет считываться строка пикселей
    DWORD RW; //

    hIn=CreateFile(fin, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); // в hIn открывается переданный файл

    if(hIn==INVALID_HANDLE_VALUE) // если возникает ошибка, то
    CloseHandle(hIn); // закрываем поток hIn
    return NULL; // и функция fun возвращает указатель равный NULL
    >
    else // если ошибки чтения файла не возникает
    ReadFile(hIn, &bfh, sizeof(bfh), &RW, NULL); // прочитаем из файла блок HEADER в bfh
    ReadFile(hIn, &bih, sizeof(bih), &RW, NULL); // прочитаем из файла блок INFO в bih
    ReadFile(hIn, palette, 256*sizeof(RGBQUAD), &RW, NULL); // прочитаем палитру в массив palette

    width=bih.biWidth; // ширина растра заносится в width
    height=bih.biHeight; // высота растра заносится в height

    SetFilePointer(hIn, bfh.bfOffBits, NULL, FILE_BEGIN); // читаем файл через bfh.bfOffBits от начала файла

    inBuf=new BYTE[width]; // создаём динамический массив для хранения строки растра
    massiv = new unsigned int [3*height*width+2]; // создаём динамический массив для хранения значений цветовых
    //компонент пикселей
    massiv[0]=width; // нулевой элемент массива хранит количество пикселей в строке
    massiv[1]=height; // первый элемент массива хранит количество строк
    // отсюда можно рассчитать длину массива как 3*height*width+2
    for(i=0; i<height; i++) // перебираем все строки растра
    ReadFile(hIn, inBuf, width, &RW, NULL); // читаем значения пикселей строки i в массив inBuf
    for(j=0; j<width; j++)
    massiv[position]=palette[inBuf[j]].rgbRed; // сохраняем значение красной компоненты
    massiv[position+1]=palette[inBuf[j]].rgbGreen; // сохраняем значение зелёной компоненты
    massiv[position+2]=palette[inBuf[j]].rgbBlue; // сохраняем значение синей компоненты
    position+=3; // смещаем номер свободного элемента в массиве на 3 позиции
    >
    SetFilePointer(hIn, (3*width)%4, NULL, FILE_CURRENT);// начинаем читать из файла со следующей строки
    >
    delete inBuf; // удаляем динамический массив inBuf
    CloseHandle(hIn); // закрываем поток hIn

    return massiv; // функция fun возвращает указатель на нулевой элемент массива
    >
    >

    Читайте также: