Bitmap laden

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
Blackshie
User
Beiträge: 13
Registriert: Mittwoch 17. Januar 2018, 15:45

Hallo,
Ich bin relativ neu in Python.
Habe aber schon Erfahrungen in anderen Programmiersprachen gesammelt.

Für ein Projekt wollte ich eine einfache Klasse zum einlesen von Bitmaps schreiben.
Ich bin den Code jetzt schon oft durchgegangen und finde den Fehler nicht.
Ich schaffe es den Header korrekt einzulesen.
Bei den Pixel stimmt aber irgendetwas nicht. Sie sind nicht korrekt. Obwohl bei einer Farbtiefe von 24 Bit jedes Pixel ein Byte groß sein müsste, stimmt nur die erste Bildzeile, ab der 2. verschieben sich die Werte.

Hier ist der Auszug aus meinen Code:

Code: Alles auswählen

  def load(self):
        self.all_Pixel = []  # Liste um alle Pixel einzulesen
        tmp_Pixel = []

        self.oPic = open(self.stPath, 'r+b')
        
        self.oPic.seek(10)
        self.iOffBits = int.from_bytes(self.oPic.read(4), 'little')
        self.iSizeHeader = int.from_bytes(self.oPic.read(4), 'little')
        self.iWidth = int.from_bytes(self.oPic.read(4), 'little')
        self.iHeight = int.from_bytes(self.oPic.read(4), 'little')
        
        self.oPic.seek(28)
        self.iBitCount = int.from_bytes(self.oPic.read(2), 'little')
        self.iCompression = int.from_bytes(self.oPic.read(4), 'little')
        self.iSizeImage = int.from_bytes(self.oPic.read(4), 'little')
        
        self.oPic.seek(46)
        self.iClrUsed = int.from_bytes(self.oPic.read(4), 'little')
        self.iClrImportant = int.from_bytes(self.oPic.read(4), 'little')
        ##################################################################    
        print("Offset der Bilddaten in Byte vom Beginn der Datei:", self.iOffBits)
        print("Größe der BITMAPINFOHEADER-Struktur in Byte:", self.iSizeHeader)
        print("Breite der Bitmap in Pixel:", self.iWidth)
        print("Höhe der Bitmap in Pixel:", self.iHeight)
        print("Die Farbtiefe der Bitmap in bpp:", self.iBitCount)
        print("Komprimierung", self.iCompression)
        print("Größe der Bilddaten in Byte:", self.iSizeImage)
        print("Anzahl der Einträge der Farbtabelle:", self.iClrUsed)
        print("Die Anzahl sämtlicher im Bild verwendeten Farben:", self.iClrImportant)
        ##################################################################   
        self.oPic.seek(self.iOffBits)
        
        if self.iCompression == 0:
    
            # Alle Pixel durchgehen
            for i in range(self.iWidth * abs(self.iHeight)):  
                
                if self.iBitCount == 1:  # bpp 1
                    pass
                
                elif self.iBitCount == 4:  # bpp 4
                    pass
                
                elif self.iBitCount == 8:  # bpp 8
                    pass
                
                elif self.iBitCount == 16:  # bpp 16
                    pass
                
                elif self.iBitCount == 24:  # bpp 24
               
                    self.iPixelB = int.from_bytes(self.oPic.read(1), 'little')  # Blau
                    self.iPixelG = int.from_bytes(self.oPic.read(1), 'little')  # Grün
                    self.iPixelR = int.from_bytes(self.oPic.read(1), 'little')  # Rot
                    
                    tmp_Pixel.append([self.iPixelR, self.iPixelG, self.iPixelB])

                elif self.iBitCount == 32:  # bpp 32
                    pass

        # Liste umkehren wenn Bildzeile unten beginnt
        if self.iHeight > 0:
            for pixel in tmp_Pixel[::-1]:
                self.all_Pixel.append(pixel)
        # sonst übernehmen        
        else:
            self.all_Pixel = tmp_Pixel
Vielleicht habe ich einen offensichtlichen logischen Fehler übersehen dem einen von euch sofort auffällt.
Über Hilfe würde ich mich freuen,

LG
Blackshie
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ist das nur eine Fingeruebung? Oder willst du diese Bilder fuer irgendwas benutzen? Denn dann solltest du einfach einer der Bibliotheken benutzen, die es dafuer gibt. Wie Pillow oder pygame oder Qt oder oder oder.

Dann bietet es sich noch an, das Modul struct zu verwenden, damit kannst du den Header gleich in einem Rutsch dekodieren.

Und last but not least: da bei 24 Bit jedes Pixel 3 Byte benoetigt, aber 3 eine ungerade Zahl ist, kann es sein, dass da mit einem Padding gearbeitet wird. Entweder explizit (nennt sich of stride), oder implizit per File-Format-Dokumentation ("one row is always a mutiple of 4, add padding if necessary").
Benutzeravatar
Blackshie
User
Beiträge: 13
Registriert: Mittwoch 17. Januar 2018, 15:45

Sowohl als auch. Ist ne Fingerübung und ich brauche die Daten.
Eine der vorgefertigten Bibliotheken dafür zu nehmen wollte ich nicht, ich denke es ist eine gute Übung um mit der Sprache vertrauter zu werden.
Das Bitmap Format gehört auch nicht zu den kompliziertesten.

Der Tipp mit dem Padding ist gut, werde ich verfolgen.
Und ich denke ich werde mir auch Pillow anschauen, auch zwecks Unterstützung für mehrere Formate.
Die alle selbst zu Implementieren würde unnötig Zeit kosten.

LG
Blackshie
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Blackshie: es ist nicht sinnvoll alles als Attribute an Dein Objekt zu binden. oPic, iPixelR, iPixelG und iPixelB gehören sicher nicht da hin. Konvention ist, Variablen, Attribute und Methoden klein_mit_unterstrich zu schreiben. Das i-Präfix ist unsinnig. Du hast eine lange Liste mit Pixeln, besser wäre eine Liste von Listen; sonst funktioniert auch das Umdrehen nicht. [::-1] liefert schon eine umgedrehte Liste, die for-Schleife ist also unnötig. all_Pixels mit einer leeren Liste zu belegen ist unsinnig, wenn Du sie zum Schluß sowieso wieder überschreibst. Variablen solltest Du erst initialisieren, wenn Du sie auch brauchst, tmp_Pixel also erst vor der ersten for-Schleife; das tmp-Präfix ist unsinnig.
Benutzeravatar
Blackshie
User
Beiträge: 13
Registriert: Mittwoch 17. Januar 2018, 15:45

@Sirius3
Danke für den Tipp, OOP ist noch neu für mich.

Die Präfixe (Ungarische Notation) hatte ich mir von früher angewöhnt. Denke manchmal ist es ganz sinnvoll zwecks Übersicht.

Am liebsten hätte ich ein 2er Array like Pixel[x][y] erstellt um so einfach auf den jeweiligen Pixel zugreifen zu können.
Aber für meine Weiterverarbeitung brauch ich eine Liste von Pixel [R,G,B] von daher passt das soweit.

Danke, dass [::-1] bereits die Liste umdreht wusste ich nicht. Dachte die wird dann von hinten nach vorne iteriert.
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Die Präfixe (Ungarische Notation) hatte ich mir von früher angewöhnt. Denke manchmal ist es ganz sinnvoll zwecks Übersicht.
Mag auch für andere Programmiersprachen gelten, aber es wird dir in Python-Forum mit Sicherheit immer und immer wieder "um die Ohren gehauen", weil es nicht der pythonischer Stil ist. Die PEP8 ist nun mal der heilige Gral der Python-Programmierer :-)
Danke, dass [::-1] bereits die Liste umdreht wusste ich nicht. Dachte die wird dann von hinten nach vorne iteriert.
Nee, iterieren muss du ja explizit. Bzw. machst du in deinem Code ja auch. Nur drehst du halt vorher die Liste um und das Binden an eine Variable hätte den gleichen Effekt.

Gruß, noisefloor
Antworten