Forum: PC-Programmierung Pixeldaten aus bitmap auslesen


von Chandler B. (chandler)


Angehängte Dateien:

Lesenswert?

Hallo,
ich möchte gerne in C die PixelDaten aus einer Bitmap auslesen.
Hier habe ich aber anscheinend noch Probleme mit der Position der Daten.
1
Read file DREIECK.BMP
2
FileHeader.file_type (BM). 
3
FileHeader.file_size (0). 
4
FileHeader.offset_data (2621440). 
5
FileHeader.size (40). 
6
FileHeader.width (64). 
7
FileHeader.height (64). 
8
FileHeader.planes (1). 
9
FileHeader.bit_count (24). 
10
FileHeader.compression (0). 
11
FileHeader.size_image (12288). 
12
FileHeader.x_pixels_per_meter(0). 
13
FileHeader.y_pixels_per_meter (0). 
14
FileHeader.colors_used (0). 
15
FileHeader.colors_important (0). 
16
DATA_OFFSET 54
17
DATA_OFFSET read 2621440
18
data parsed

Ich habe gedacht, dass zum schluss das ein oder andere mal wenigstens 
eine ausgabe kommt.
Aber wie man sieht kommt da ncihts.
auch wenn ich für das offset i fseek die 2621440 nehme, bekomme ich kein 
printf.

: Bearbeitet durch User
von Marci W. (marci_w)


Lesenswert?

Hallo Chandler,

du möchtest den Header der BMP-Datei in Structs abbilden. Das geht 
schief, da der Compiler ein sogenanntes Alignment anwenden darf. Es 
werden also ggf. Füllbytes in die Struktur eingefügt. AFAIK kann der 
Compiler sogar die Reihenfolge der Elemente ändern (sie stehen dann 
anders im Speicher als in der Struktur-Deklaration).
Leider kenne ich die (Compiler-) Einstellungen bzw. pragmas nicht, um 
das Alignment zu umgehen. Das werden andere hier jedoch sicher bald 
ergänzen.
WIMRE ist es allerdings besser, die Strukt-Elemente einzeln zu füllen.

Dast Du die Datei mal im Hex-Editor angeschaut? Dann siehst Du schnell, 
ob der Header Deinen Erwartungen entspricht und wie die Daten dann in 
der Struktur abgelegt werden.

ciao

Marci

von Chandler B. (chandler)


Lesenswert?

Marci W. schrieb:
> Dast Du die Datei mal im Hex-Editor angeschaut? Dann siehst Du schnell,
> ob der Header Deinen Erwartungen entspricht und wie die Daten dann in
> der Struktur abgelegt werden.

Das war ein guter tipp.
Habe es "jetzt" gemacht und gesehen, dass es nicht wirklich passt.
file_type = "BM" und einige andere Werte (im infoHeader) wurde mir immer 
valide ausgegeben. Bin daher ausgegangen, dass es überallpasst. aber 
Anscheinend passt es doch nicht überall.
1
fread(&bmpData_s.fileHeader_s.file_type, sizeof(bmpData_s.fileHeader_s.file_type), 1, imageFile);
2
fread(&bmpData_s.fileHeader_s.file_size, sizeof(bmpData_s.fileHeader_s.file_size), 1, imageFile);
3
fread(&bmpData_s.fileHeader_s.reserved1, sizeof(bmpData_s.fileHeader_s.reserved1), 1, imageFile);
4
fread(&bmpData_s.fileHeader_s.reserved2, sizeof(bmpData_s.fileHeader_s.reserved2), 1, imageFile);
5
fread(&bmpData_s.fileHeader_s.offset_data, sizeof(bmpData_s.fileHeader_s.offset_data), 1, imageFile);

damit bekomme ich die Ausgabe
1
Read file DREIECK.BMP
2
FileHeader.file_type (BM). 
3
FileHeader.file_size (12342). 
4
FileHeader.offset_data (54).

sieht auf jedenfall schon einmal anders aus.

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Marci W. schrieb:
> Leider kenne ich die (Compiler-) Einstellungen bzw. pragmas nicht, um
> das Alignment zu umgehen.

Bei MS z.B. #pragma pack, bei gcc __attribute__((packed)) - Details 
stehen in der Dokumentation.

Marci W. schrieb:
> WIMRE ist es allerdings besser, die Strukt-Elemente einzeln zu füllen.

In erster Linie ist es mühsamer.

von Marci W. (marci_w)


Lesenswert?

Harald K. schrieb:
> In erster Linie ist es mühsamer.

Das stimmt allerdings! Man muss jedoch aufpassen, da bei der Verwendung 
von "packed" je nach Prozessorarchitektur ein Pointer Misalignment 
auftreten kann. So gesehen ist der TO mit den einzelnen reads auf der 
sicheren Seite und plattformunabhängig (OK, es könnte noch Probleme wg. 
Endianess etc. geben) ;-)

ciao

Marci

von Le X. (lex_91)


Lesenswert?

Marci W. schrieb:
> AFAIK kann der
> Compiler sogar die Reihenfolge der Elemente ändern (sie stehen dann
> anders im Speicher als in der Struktur-Deklaration).

Können tut er das freilich.
Dürfen aber nicht.

von Chandler B. (chandler)


Angehängte Dateien:

Lesenswert?

Hallo,
ganz möchte es doch noch nicht so hinhauen.
beim Auslesen der Daten kommt doch noch irgendwas durcheinander.

Damit ich mich einfacher tue, habe ich ein neues Bild erstellt (2x3 
Pixel)

Zum schluss möchte ich mir dann mit printf das bild in der Console 
nachstellen
1
    uint8_t colorData_au8[3] = {0};
2
    for(uint8_t x=0; x<2-1; x++)
3
    {
4
        for(uint8_t y=0; y<3-1; y++)
5
        {
6
            fread(&colorData_au8, sizeof(colorData_au8), 1, imageFile);
7
            bmpData_s.colorData_sa[x][y].red = colorData_au8[2];
8
            bmpData_s.colorData_sa[x][y].green = colorData_au8[1];
9
            bmpData_s.colorData_sa[x][y].blue = colorData_au8[0];
10
        }
11
    }
12
13
    for(uint8_t x=0; x<3-1; x++)
14
    {
15
        for(uint8_t y=0; y<2-1; y++)
16
        {
17
            if ((bmpData_s.colorData_sa[x][y].red > bmpData_s.colorData_sa[x][y].green) && (bmpData_s.colorData_sa[x][y].red > bmpData_s.colorData_sa[x][y].blue))
18
            {
19
                printf("R ");
20
            }
21
            else if ((bmpData_s.colorData_sa[x][y].green > bmpData_s.colorData_sa[x][y].red) && (bmpData_s.colorData_sa[x][y].green > bmpData_s.colorData_sa[x][y].blue))
22
            {
23
                printf("G ");
24
            }
25
            else if ((bmpData_s.colorData_sa[x][y].blue > bmpData_s.colorData_sa[x][y].red) && (bmpData_s.colorData_sa[x][y].blue > bmpData_s.colorData_sa[x][y].green))
26
            {
27
                printf("B ");
28
            }
29
            else if ((bmpData_s.colorData_sa[x][y].blue == 0) && (bmpData_s.colorData_sa[x][y].red == 0) && (bmpData_s.colorData_sa[x][y].green == 0))
30
            {
31
                printf("p ");
32
            }
33
            else
34
            {
35
                printf(". ");
36
            }
37
        }
38
        printf("\n");
39
    }

Die Ausgabe ist allerdings
1
G 
2
p

also das G würde ich verstehen. Das ist unten links.
p wäre schräg davon. auch wird mir nur ein Wert ausgegeben, obwohl die 
for-schleife eigentlich über alles drüber gehen sollte.

von Rolf M. (rmagnus)


Lesenswert?

Chandler B. schrieb:
> for(uint8_t x=0; x<2-1; x++)
>     {
>         for(uint8_t y=0; y<3-1; y++)
>         {

Wozu soll denn das -1 sein? Ist es gewollt, dass die äußere Schleife nur 
einmal durchläuft und die innere zweimal?

von Chandler B. (chandler)


Lesenswert?

Rolf M. schrieb:
> Wozu soll denn das -1 sein? Ist es gewollt, dass die äußere Schleife nur
> einmal durchläuft und die innere zweimal?

nein, das war ein Fehler
1
for(uint8_t x=0; x<3; x++)
2
{
3
    for(uint8_t y=0; y<2; y++)
4
    {
5
        fread(&colorData_au8, sizeof(colorData_au8), 1, imageFile);
6
        bmpData_s.colorData_sa[x][y].red = colorData_au8[2];
7
        bmpData_s.colorData_sa[x][y].green = colorData_au8[1];
8
        bmpData_s.colorData_sa[x][y].blue = colorData_au8[0];
9
        printf("r: %u    g: %u    b: %u\n", colorData_au8[2], colorData_au8[1], colorData_au8[0]);                                                                         
10
    }
11
    printf("\n");
12
}
damit lasse ich mir jeden pixel ausgeben.
1
r: 34    g: 177    b: 76
2
r: 237    g: 28    b: 36
3
4
r: 76    g: 0    b: 0
5
r: 0    g: 34    b: 177
6
7
r: 0    g: 0    b: 0
8
r: 85    g: 91    b: 0

die ersten zwei Pixel passen (habe die Farbwerte mit GIMP überprüft)
Aber ab dann passt es irgendwie gar nicht mehr.

von Chandler B. (chandler)


Lesenswert?

Habe mir das Bild noch einmal im Hexeditor angesehen.
Es scheint so zu sein,
dass drei Bytes Farbe kommen (reihenfolge Blau, Grün, Rot), dann noch 
einmal drei Bytes Farbe (reihenfolge Blau, Grün, Rot) und dann zwei 
Bytes 0x00 da sind.

Compression = 0 (BI_RGB): Bilddaten sind unkomprimiert, bedeutet
"Jede Bildzeile ist durch rechtsseitiges Auffüllen mit Nullen auf ein 
ganzzahliges Vielfaches von 4 Bytes ausgerichtet." (Wikipedia)

Heißt, da ich 6 Bytes Farbdaten habe (2 pixel x 3Bytes/Pixel), bleiben 2 
Bytes übrig um auf 8 Bytes (vielfaches von 4 Bytes) zu kommen.
1
for(uint8_t x=0; x<3; x++)
2
{
3
    for(uint8_t y=0; y<2; y++)
4
    {
5
        fread(&colorData_au8, sizeof(colorData_au8), 1, imageFile);
6
        bmpData_s.colorData_sa[x][y].red = colorData_au8[2];
7
        bmpData_s.colorData_sa[x][y].green = colorData_au8[1];
8
        bmpData_s.colorData_sa[x][y].blue = colorData_au8[0];
9
        printf("r: %u    g: %u    b: %u\n", colorData_au8[2], colorData_au8[1], colorData_au8[0]);                                                                         
10
    }
11
    fseek(imageFile, 2, SEEK_CUR);
12
    printf("\n");
13
}
Das erklärt meine Falschen Zahlwerte.

Muss ich dann jetzt mal weiterschauen, wie ich das machen kann, wenn ich 
die größe des Bildes nicht genau weiß.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Bei BMP gibt es ja verschiedene Unterformate, zum Beispiel:

* Palette oder RGB-Werte
* Unkomprimiert oder RLE (Run-Length Encoding)

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Chandler B. schrieb:
> wenn ich die größe des Bildes nicht genau weiß.
Die Größe, etc. steht doch im Header.

Chandler B. schrieb:
> FileHeader.width (64).

> FileHeader.height (64).

von Martin M. (capiman)


Lesenswert?


von Rainer W. (rawi)


Lesenswert?

Chandler B. schrieb:
> Muss ich dann jetzt mal weiterschauen, wie ich das machen kann, wenn ich
> die größe des Bildes nicht genau weiß.

Warum kannst du die nicht aus dem Header lesen?
https://gibberlings3.github.io/iesdp/file_formats/ie_formats/bmp.htm

von Rüdiger B. (rbruns)


Lesenswert?

In der Arduino Welt gibt es diverse Programme zum BMP anzeigen.

von Harald K. (kirnbichler)


Lesenswert?

Der Threadstarter hat mit keinem Wort verraten, worauf sein Programm 
laufen soll. Die #includes in seinem Sourcefile lassen so etwas wie 
Linux vermuten, jedenfalls nicht den Betrieb auf einem µC.

Also könnte man auch eine der zig bestehenden Libraries verwenden, um 
Bilddateien zu lesen, die würden auch die vielen Möglichkeiten 
unterschiedlichster BMP-Formate erschlagen. Denn BMP ist letztlich nur 
ein Containerformat, da kann alles mögliche drinstecken.

Und mit so einer Library wie z.B. https://libgd.github.io/ kann man dann 
auch gleich ganz andere Dateiformate verarbeiten.

Wenn das ganze aber eine Übung im Selbstverarbeiten komplexer 
Dateiformate sein soll: Header lesen und interpretieren. Dazu die 
Dokumentation genau ansehen.

https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Chandler B. schrieb:

> Muss ich dann jetzt mal weiterschauen, wie ich das machen kann, wenn ich
> die größe des Bildes nicht genau weiß.

Witzig. Genau dafür, dass man das halt sicher weiß, gibt's ja den 
Header. Der beschreibt, wie die Nutzdaten des Bildes zu interpretieren 
sind.

Übrigens: biCompression == BI_RGB kennt auch noch ziemlich viele 
Unterformate. Du hast nur eins davon (RGB24) implementiert. Das ist zwar 
recht verbreitet, aber es gibt auch andere, die sehr weit verbreitet 
sind.
Sehr häufig wird z.B. RGB32 benutzt, weil es einen Alpha-Kanal besitzt. 
Auch recht häufig anzutreffen sind Monochrom- (AKA: "Schwarzweiß"-) 
Bitmaps.

Und nicht zuletzt: auch eins der "alten" indizierten Formate ist noch 
weit verbreitet: die Variante mit 8Bit-Palettenindizes. Das wird sowohl 
für Graustufenbilder als auch für (relativ) platzsparende farbige Icons 
verwendet.

Dazu kommen noch die (allerdings sehr viel seltener verwendeten) Formate 
mit Bitmasken und wie z.B. RGB565, RGB555, ARGB1555 und RGB332 mit 16 
bzw. 8 Bit pro Pixel. Wenn überhaupt heute noch relevant, dann am 
ehesten im Bereich µC-Programmierung von einem Windows-PC aus.

von Rolf M. (rmagnus)


Lesenswert?

Harald K. schrieb:
> Der Threadstarter hat mit keinem Wort verraten, worauf sein Programm
> laufen soll. Die #includes in seinem Sourcefile lassen so etwas wie
> Linux vermuten, jedenfalls nicht den Betrieb auf einem µC.

Wir sind im Unterforum "PC-Programmierung". Da würde ich sowieso nicht 
von einem µC ausgehen.

von Harald K. (kirnbichler)


Lesenswert?

Rolf M. schrieb:
> Wir sind im Unterforum "PC-Programmierung".

So präzise, wie hier Threads von den Verfassern in irgendwelche 
Forenbereiche einsortiert werden (z.B. "Restaurierung eines verrosteten 
Funkgeräts" unter "Projekte & Code" oder "Rasenmäherakku" unter 
"Platinen"), würde ich darauf nicht allzuviel geben.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.