Как я могу прочитать значения пикселей BMP в массив? - PullRequest
9 голосов
/ 22 апреля 2011

Я пишу код на C ++ (в Windows) и пытаюсь извлечь значения пикселей в bmp в градациях серого. Меня не волнует сохранение каких-либо метаданных, я просто хочу сохранить значения пикселей в массиве символов. Мне не удалось найти стандартный или «типичный» способ сделать это вручную, поэтому мне интересно, есть ли, возможно, простая библиотека, которую люди используют для загрузки растровых изображений в память.

Заранее спасибо!

Ответы [ 7 ]

13 голосов
/ 31 июля 2012

и готовый к использованию код, протестированный на g ++ (не Windows, но может кому-то помочь):

#pragma pack(1)

#include <iostream>
#include <fstream>
#include <vector>

using namespace std;

#include "bmp.h"

vector<char> buffer;
PBITMAPFILEHEADER file_header;
PBITMAPINFOHEADER info_header;

void fill() {
    std::ifstream file("data.bmp");

    if (file) {
        file.seekg(0,std::ios::end);
        streampos length = file.tellg();
        file.seekg(0,std::ios::beg);

        buffer.resize(length);
        file.read(&buffer[0],length);

        file_header = (PBITMAPFILEHEADER)(&buffer[0]);
        info_header = (PBITMAPINFOHEADER)(&buffer[0] + sizeof(BITMAPFILEHEADER));
    }
}

int main() {
    fill();

    cout << buffer[0] << buffer[1] << endl;
    cout << file_header->bfSize << endl;
    cout << info_header->biWidth << " " << info_header->biHeight << endl;

    return 0;
}

В bmp.h я определил структуры:

#pragma once

typedef int LONG;
typedef unsigned short WORD;
typedef unsigned int DWORD;

typedef struct tagBITMAPFILEHEADER {
  WORD  bfType;
  DWORD bfSize;
  WORD  bfReserved1;
  WORD  bfReserved2;
  DWORD bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;
  LONG  biWidth;
  LONG  biHeight;
  WORD  biPlanes;
  WORD  biBitCount;
  DWORD biCompression;
  DWORD biSizeImage;
  LONG  biXPelsPerMeter;
  LONG  biYPelsPerMeter;
  DWORD biClrUsed;
  DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
5 голосов
/ 22 апреля 2011

Прочитать весь файл в память. Спереди будет небольшой заголовок, а остальное будет в пикселях.

Первая часть будет иметь структуру BITMAPFILEHEADER. Единственная часть, которая вас волнует, - это bfOffBits, которая задает количество байтов от начала файла до значений пикселей.

Следующая часть после BITMAPFILEHEADER будет BITMAPINFOHEADER. Это будет полезно для определения формата пикселей.

После этого следует палитра, если битовая глубина требует ее.

Есть несколько ошибок со значениями пикселей. Во-первых, порядок (синий, зеленый, красный) прямо противоположен тому, как это делают все остальные. Во-вторых, строки проходят снизу вверх, снова и снова от всех остальных. Наконец, число байтов в строке всегда будет дополнено до следующего кратного 4.

Я почти забыл упомянуть, что файл JPEG или PNG может быть закодирован как BMP, но это не распространено. Посмотрите на поле biCompression BITMAPINFOHEADER, если это не BI_RGB, вам понадобится немного больше помощи.

4 голосов
/ 03 августа 2012

при кодировании в Visual Studios, перед объявлением структур tagBITMAPFILEHEADER и tagBITMAPINFOHEADER (показанных в ответе Йолы) обязательно включите "#pragma pack (2)". В противном случае структура будет дополнена следующей 4-байтовой границей, а не следующей 2-байтовой границей, и данные станут мусором.

ссылка http://tipsandtricks.runicsoft.com/Cpp/BitmapTutorial.html

2 голосов
/ 29 июня 2015

Расширяя то, что написал Йола, он сможет читать и выводить файл. Это не очень хорошо проверено, но, кажется, работает. При выводе он использует формат файла, который читает.

#include <iostream>
#include <unistd.h>
#include <fstream>

using std::cout;
using std::endl;
using std::ofstream;
using std::ifstream;

#pragma pack(1)
#pragma once

typedef int LONG;
typedef unsigned short WORD;
typedef unsigned int DWORD;

typedef struct tagBITMAPFILEHEADER {
    WORD bfType;
    DWORD bfSize;
    WORD bfReserved1;
    WORD bfReserved2;
    DWORD bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;
    LONG biWidth;
    LONG biHeight;
    WORD biPlanes;
    WORD biBitCount;
    DWORD biCompression;
    DWORD biSizeImage;
    LONG biXPelsPerMeter;
    LONG biYPelsPerMeter;
    DWORD biClrUsed;
    DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;

unsigned char** reds;
unsigned char** greens;
unsigned char** blues;
int rows;
int cols;

void ColorTest() {
    // Makes Red Rectangle in top left corner. Rectangle stretches to right alot
    for (int i = rows / 10; i < 3 * rows / 10; i++)
        for (int j = cols / 10; j < 7 * cols / 10; j++)
            reds[i][j] = 0xff;

// Makes small green box in bottom right
    for (int i = 8 * rows / 10; i < rows; i++)
        for (int j = 8 * cols / 10; j < cols; j++)
            greens[i][j] = 0xff;

// Makes White box in the middle of the screeene    
    for (int i = rows * 4 / 10; i < rows * 6 / 10; i++)
        for (int j = cols * 4 / 10; j < cols * 6 / 10; j++) {
            greens[i][j] = 0xff;
            reds[i][j] = 0xff;
            blues[i][j] = 0xff;
        }

// Blue verticle rectangle bottom left
    for (int i = rows * 6 / 10; i < rows; i++)
        for (int j = cols * 0; j < cols * 1 / 10; j++)
            blues[i][j] = 0xff;
}

void RGB_Allocate(unsigned char**& dude) {
    dude = new unsigned char*[rows];
    for (int i = 0; i < rows; i++)
        dude[i] = new unsigned char[cols];
}

bool FillAndAllocate(char*& buffer, const char* Picture, int& rows, int& cols, int& BufferSize) { //Returns 1 if executed sucessfully, 0 if not sucessfull
    std::ifstream file(Picture);

    if (file) {
        file.seekg(0, std::ios::end);
        std::streampos length = file.tellg();
        file.seekg(0, std::ios::beg);

        buffer = new char[length];
        file.read(&buffer[0], length);

        PBITMAPFILEHEADER file_header;
        PBITMAPINFOHEADER info_header;

        file_header = (PBITMAPFILEHEADER) (&buffer[0]);
        info_header = (PBITMAPINFOHEADER) (&buffer[0] + sizeof(BITMAPFILEHEADER));
        rows = info_header->biHeight;
        cols = info_header->biWidth;
        BufferSize = file_header->bfSize;
        return 1;
    }
    else {
        cout << "File" << Picture << " don't Exist!" << endl;
        return 0;
    }
}

void GetPixlesFromBMP24(unsigned char** reds, unsigned char** greens, unsigned char** blues, int end, int rows, int cols, char* FileReadBuffer) { // end is BufferSize (total size of file)
    int count = 1;
int extra = cols % 4; // The nubmer of bytes in a row (cols) will be a multiple of 4.
    for (int i = 0; i < rows; i++){
count += extra;
    for (int j = cols - 1; j >= 0; j--)
        for (int k = 0; k < 3; k++) {
                switch (k) {
                case 0:
                    reds[i][j] = FileReadBuffer[end - count++];
                    break;
                case 1:
                    greens[i][j] = FileReadBuffer[end - count++];
                    break;
                case 2:
                    blues[i][j] = FileReadBuffer[end - count++];
                    break;
                }
            }
            }
}

void WriteOutBmp24(char* FileBuffer, const char* NameOfFileToCreate, int BufferSize) {
    std::ofstream write(NameOfFileToCreate);
    if (!write) {
        cout << "Failed to write " << NameOfFileToCreate << endl;
        return;
    }
    int count = 1;
    int extra = cols % 4; // The nubmer of bytes in a row (cols) will be a multiple of 4.
    for (int i = 0; i < rows; i++){
        count += extra;
        for (int j = cols - 1; j >= 0; j--)
            for (int k = 0; k < 3; k++) {
                switch (k) {
                case 0: //reds
                    FileBuffer[BufferSize - count] = reds[i][j];
                    break;
                case 1: //green
                    FileBuffer[BufferSize - count] = greens[i][j];
                    break;
                case 2: //blue
                    FileBuffer[BufferSize - count] = blues[i][j];
                    break;
                }
                count++;
            }
            }
    write.write(FileBuffer, BufferSize);
}


int main(int args, char** cat) {
char* FileBuffer; int BufferSize;

#define Picture "ReadInPicture.bmp"
if (!FillAndAllocate(FileBuffer, Picture, rows, cols, BufferSize)){cout << "File read error" << endl; return 0;}
cout << "Rows: " << rows << " Cols: " << cols << endl;

RGB_Allocate(reds);
RGB_Allocate(greens);
RGB_Allocate(blues);
GetPixlesFromBMP24( reds,  greens, blues,BufferSize, rows, cols, FileBuffer);
ColorTest();
#define WriteOutFile "OutputPicture.bmp"
WriteOutBmp24(FileBuffer,  WriteOutFile,BufferSize);
    return 1;
}
2 голосов
/ 22 апреля 2011

У вас есть 2 хороших варианта:

  1. Загрузите и проанализируйте файл BMP самостоятельно. BMP-файлы начинаются с BITMAPFILEHADER, за которым следует BITMAPINFOHEADER, а затем 0 или более RGBQUAD (запись палитры). Смещение в пиксельные данные находится в BITMAPFILEHADER, но вы должны проверить BITMAPINFOHEADER, чтобы убедиться, что формат изображения соответствует ожидаемому / поддерживаемому.

  2. Вызовите API-интерфейс LoadImage () с флагом LR_CREATEDIBSECTION, он вернет дескриптор секции DIB. Затем вы вызываете GetObject (), передавая возвращенный дескриптор и указатель на структуру DIBSECTION. Затем вы читаете структуру DIBSECTION для определения размера, формата, указателя на данные в пикселях и т. Д.

Вариант 2 предпочтительнее, если вы работаете в Windows, поскольку предположительно LoadImage () проверяет наличие недопустимых форматов файлов и может загружать не только файлы BMP.

При доступе к пикселям Windows BMP помните, что линии всегда выровнены по DWORD.

2 голосов
/ 22 апреля 2011

Определенно есть библиотеки (см. Другие ответы), но в один миг, это, откровенно говоря, смертельно простой формат файла, который вы можете легко разобрать. Подробности здесь:

http://www.fileformat.info/format/bmp/egff.htm

(Я был вне Win32 в течение нескольких лет, но функция LoadImage может получить HBITMAP из файла BMP. Я не уверен, как превратить это в пиксельный массив напрямую, но я ' Я могу представить, что есть некоторое искажение с DC, которое позволит вам получить значения. http://support.microsoft.com/kb/158898

Дополнительные подсказки: http://alexkr.com/source-code/26/accessing-bitmap-pixels-in-gdi/)

2 голосов
/ 22 апреля 2011

Вы можете попробовать MagicWand API библиотеки ImageMagic .

...