Frage Wie kann ich BMP Pixelwerte in ein Array lesen?


Ich schreibe Code in C ++ (unter Windows) und ich versuche, die Pixelwerte eines Graustufen-BMP zu extrahieren. Es ist mir egal, irgendwelche der Metadaten zu behalten und nur die Pixelwerte in einem Char-Array zu speichern. Ich war nicht in der Lage, eine standardmäßige oder "typische" Art zu finden, dies manuell zu tun, also frage ich mich, ob es vielleicht eine einfache Bibliothek gibt, die Leute verwenden, um Bitmaps in den Speicher zu laden.

Danke im Voraus!


5
2018-04-22 02:31


Ursprung


Antworten:


Lesen Sie die gesamte Datei in den Speicher. Es wird eine kleine Kopfzeile an der Vorderseite geben, und der Rest wird die Pixelwerte sein.

Der erste Teil wird ein BITMAPFILEHEADER Struktur. Der einzige Teil, der Ihnen am Herzen liegt, ist der bfOffBits, der die Anzahl der Bytes vom Anfang der Datei bis zu den Pixelwerten angibt.

Der nächste Teil nach dem BITMAPFILEHEADER wird ein ... sein BITMAPINFOHEADER. Dies ist nützlich, um das Format der Pixel zu bestimmen.

Dies wird von einer Palette gefolgt, wenn die Bittiefe eine erfordert.

Es gibt ein paar Fehler mit den Pixelwerten. Die erste ist, dass die Reihenfolge (blau, grün, rot) genau umgekehrt ist, wie alle anderen es tun. Zweitens gehen die Zeilen von unten nach oben, wieder zurück von allen anderen. Schließlich wird die Anzahl der Bytes in einer Zeile immer bis zum nächsten Vielfachen von 4 aufgefüllt.

Fast hätte ich vergessen zu erwähnen, dass eine JPEG- oder PNG-Datei als BMP kodiert werden kann, aber das ist nicht üblich. Sehen Sie sich das Feld biCompression des BITMAPINFOHEADERWenn es etwas anderes als BI_RGB ist, benötigen Sie ein wenig mehr Hilfe.


5
2018-04-22 02:58



und bereit, Code zu gehen, getestet mit g ++ (nicht Windows, kann aber jemandem helfen):

#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;
}

In bmp.h habe ich Strukturen definiert:

#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;

12
2017-07-31 16:37



Wenn Sie in Visual Studios codieren, bevor Sie Ihre Strukturen tagBITMAPFILEHEADER und tagBITMAPINFOHEADER (in Yolas Antwort) deklarieren, achten Sie darauf, "#pragma pack (2)" einzufügen. Andernfalls wird die Struktur bis zur nächsten 4-Byte-Grenze aufgefüllt, anstelle der nächsten 2-Byte-Grenze, und die Daten werden unbrauchbar.

Referenz http://tipsandtricks.runicsoft.com/Cpp/BitmapTutorial.html 


4
2017-08-03 02:26



Du könntest es versuchen Zauberstab eine API der ImageMagic Bibliothek.


2
2018-04-22 02:35



Es gibt definitiv Bibliotheken da draußen (siehe andere Antworten), aber im Handumdrehen ist es offen gesagt ein hirntot einfaches Dateiformat, das Sie ganz leicht parsen können. Details sind hier:

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

(Ich war ein paar Jahre außerhalb von Win32, aber die LoadImage Funktion kann Ihnen eine HBITMAP aus einer BMP-Datei holen. Ich bin mir nicht sicher, wie ich das direkt in ein Pixel-Array umwandeln könnte, aber ich könnte mir vorstellen, dass es eine Verzerrung mit einem DC gibt, mit der man die Werte erfassen kann. http://support.microsoft.com/kb/158898

Weitere Hinweise: http://alexkr.com/source-code/26/accessing-bitmap-pixel-in-gdi/)


2
2018-04-22 02:38



Sie haben 2 gute Möglichkeiten:

  1. Laden und analysieren Sie die BMP-Datei selbst. BMP-Dateien beginnen mit einem BITMAPFILEHADER, gefolgt von einem BITMAPINFOHEADER, gefolgt von 0 oder mehr RGBQUADs (Paletteneintrag). Offset zu Pixeldaten ist in BITMAPFILEHADER, aber Sie sollten BITMAPINFOHEADER überprüfen, um sicherzustellen, dass das Bildformat das ist, was Sie erwarten / unterstützen.

  2. Rufen Sie LoadImage () API mit LR_CREATEDIBSECTION Flag auf, es wird ein Handle zu einem DIB-Abschnitt zurückgeben. Als nächstes rufen Sie GetObject () auf, übergeben das zurückgegebene Handle und einen Zeiger auf eine DIBSECTION-Struktur. Dann lesen Sie DIBSECTION-Struktur für Bitmap-Größe, Format, Zeiger auf Pixeldaten usw.

Option 2 ist besser, wenn Sie unter Windows arbeiten, da LoadImage () vermutlich nach ungültigen Dateiformaten sucht und mehr als nur BMP-Dateien laden kann.

Achten Sie beim Zugriff auf Windows BMP-Pixel darauf, dass Zeilen immer DWORD-ausgerichtet sind.


2
2018-04-22 19:56



In Erweiterung dessen, was Yola schrieb, sollte dies in der Lage sein, eine Datei einzulesen und auszugeben. Es ist nicht gut getestet, scheint aber zu funktionieren. Es verwendet das Format der Datei, die es liest, wenn es ausgegeben wird.

#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
2018-06-28 23:15