c ++ - Hvordan kan jeg læse BMP pixelværdier i en matrix?

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg skriver kode i C ++ (på Windows), og jeg forsøger at udtrække pixelværdierne i en gråskala bmp. Jeg er ligeglad med at holde metadata, og vil bare gemme pixelværdierne i et char array. Jeg har ikke kunnet finde en standard eller 'typisk' måde at gøre dette manuelt på, så jeg undrer mig hvis der måske er et simpelt bibliotek, som folk bruger til at indlæse bitmaps til hukommelsen.


Tak på forhånd!

Bedste reference


Læs hele filen i hukommelsen. Der vil være et lille overskrift på forsiden, og resten af ​​det bliver pixelværdierne.


Den første del er en BITMAPFILEHEADER struktur. Den eneste del, du er interesseret i, er bfOffBits, som giver antallet af bytes fra starten af ​​filen til pixelværdierne. [9]


Den næste del efter BITMAPFILEHEADER vil være en BITMAPINFOHEADER. Dette vil være nyttigt for at bestemme formatet på pixels. [10]


Dette vil blive efterfulgt af en palette, hvis bitdybden kræver en.


Der er et par gotchas med pixelværdierne. For det første er ordren (blå, grøn, rød), lige modsat den måde, alle andre gør. For det andet er det at rækkerne går fra bunden til toppen af ​​billedet, igen bagud fra alle andre. Endelig vil antallet af bytes i træk altid blive polstret op til det næste flertal på 4.


Jeg har næsten glemt at nævne, det er muligt for en JPEG- eller PNG-fil at blive kodet som en BMP, men det er ikke almindeligt. Se på biCompression-feltet på BITMAPINFOHEADER, hvis det er andet end BI\_RGB, skal du have lidt mere hjælp.

Andre referencer 1


og klar til at gå kode, testet med g ++ (ikke Windows, men kan hjælpe nogen):


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


I bmp.h har jeg definerede strukturer:


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

Andre referencer 2


hvis kodning i Visual Studios, før du erklærer din tagBITMAPFILEHEADER og tagBITMAPINFOHEADER structs (vist i Yolas svar), skal du medtage '#pragma pack (2)'. Udenvis vil strukturen blive polstret til næste 4 byte grænse i stedet for næste 2 byte grænse, og dataene vil blive skrald.


reference http://tipsandtricks.runicsoft.com/Cpp/BitmapTutorial.html [11]

Andre referencer 3


Du kan prøve MagicWand som et API i ImageMagic-biblioteket. [12] [13]

Andre referencer 4


Der er helt sikkert biblioteker derude (se andre svar), men i et jiffy er det helt ærligt et hjerne-dødt enkelt filformat, som du let kan analysere dig selv. Detaljer er her:


http://www.fileformat.info/format/bmp/egff.htm[14]


(Jeg har været ude af Win32 i et par år, men funktionen LoadImage kan få dig en HBITMAP fra en BMP-fil. Jeg er ikke sikker på, hvordan du omdanner det til en pixel array direkte, men jeg d forestil dig, at der er noget forstyrrelser med en DC, der vil lade dig få fat i værdierne. http://support.microsoft.com/kb/158898[15]


Flere tips: http://alexkr.com/source-code/26/accessing-bitmap-pixels-in-gdi/)[16]

Andre referencer 5


Du har 2 gode muligheder:



  1. Indlæs og analysér BMP-filen selv. BMP-filer starter med en BITMAPFILEHADER efterfulgt af en BITMAPINFOHEADER efterfulgt af 0 eller flere RGBQUAD'er (paletteindtastning). Offset til pixeldata er i BITMAPFILEHADER, men du bør kontrollere BITMAPINFOHEADER for at sikre, at billedformatet er det, du forventer/støtter .

  2. Ring LoadImage () API med LR\_CREATEDIBSECTION flag, det vil returnere et håndtag til en DIB sektion. Dernæst kalder du GetObject () forbi det tilbagevendte håndtag og en pointer til en DIBSECTION-struktur. Derefter læser du DIBSECTION struktur for bitmap størrelse, format, peger til pixel data osv.



Mulighed 2 er bedre, hvis du er på Windows, fordi det formentlig LoadImage () kontrolleres for ugyldige filformater til dig, og kan indlæse mere end bare BMP-filer.


Når du åbner Windows BMP-pixels, skal du huske, at linjer altid er DWORD-justerede.

Andre referencer 6


Udvidelse på, hvad Yola skrev, bør dette være i stand til at læse ind og udgive en fil. Det er ikke godt testet, men synes at virke. Det bruger formatet til den fil, den læser, når den udsender.


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