Parseur/lecteur de fichier bitmap

Description

Ce code lit directement depuis le fichier bitmap sans utiliser de librairie. Il permet ensuite de lafficher via wx. Il renvoi aussi un tableau au dimension de limage ou chaque cellule est un pixel avec ses valeurs RGB.

C'est interessant pour une introduction au module struct et aux manipulations binaires (notamment, pour la bitmap monochrome et 4 bits per pixels (16 colors)).

Le module prend pour linstant en charge que les fichiers non compressé (RLE viendra) et que les bitmap monochrome, 4bpp, 8bpp et 24 bpp. Les formats 16 et 32 ayant l'air d'etre assez rare et peu utiliser je ne l'ai pas implementer (surtout que paint ne permet pas denregistrer ds ce format). Dans le zip se trouve un fichier bitmap 256 couleurs (8 bpp).

Source / Exemple :


#code du module parseur :
# -*- coding: cp1252 -*-
import os.path
import struct as s
import wx, Afficheur, time, sys

class BitmapError(Exception):
    pass

class NotOpened(BitmapError):
    pass
class NotExists(BitmapError):
    pass

class bitmap:
    """class permettant de charger et de parser un fichier Bitmap
    """
    def __init__(self):
        self.opened=0
        self.identifier={'BM' : 'Windows 3.1x, 95, NT',
                         'BA' : 'OS/2 Bitmap Array',
                         'CI' : 'OS/2 Color Icon',
                         'CP' : 'OS/2 Color Pointer',
                         'IC' : 'OS/2 Icon',
                         'PT' : 'OS/2 Pointer',
                        }
    def load(self, bitmap):
        if(os.path.exists(bitmap)==False) : RaiseNotExists, bitmap+' n\'existe pas'
        f=open(bitmap, 'rb')
        self.ident=self.identifier[f.read(2)]
        self.filesize=s.unpack('L', f.read(4))[0]
        self.reserved=s.unpack('L', f.read(4))[0]
        self.offset=s.unpack('L', f.read(4))[0]
        self.headersize=s.unpack('L', f.read(4))[0]
        self.size=(s.unpack('L', f.read(4))[0], s.unpack('L', f.read(4))[0])
        self.planes=s.unpack('h', f.read(2))[0]
        self.bpp=s.unpack('h', f.read(2))[0]
        self.compression=s.unpack('L', f.read(4))[0]
        self.datasize=s.unpack('L', f.read(4))[0]
        self.hres=s.unpack('L', f.read(4))[0]
        self.vres=s.unpack('L', f.read(4))[0]
        self.colors=s.unpack('L', f.read(4))[0]
        self.impcolors=s.unpack('L', f.read(4))[0]

        #creation du tableau de pixels
        self.Pixels=[]
        for i in range(self.size[0]):
            self.Pixels.append([])
            for j in range(self.size[1]):
                self.Pixels[i].append((0,0,0))
                
        if(self.bpp==24 and self.compression==0):
            #1 pixel = 3 octect
            f.seek(self.offset)
            for y in range(self.size[1], 0, -1):
                i=0
                for x in range(self.size[0]):
                    pix=f.read(3)
                    i+=3
                    RGB=(s.unpack('B', pix[2])[0], s.unpack('B', pix[1])[0], s.unpack('B', pix[0])[0])
                    self.Pixels[x][y-1]=RGB
                if(i%4!=0): f.read(4-(i%4))
        elif(self.bpp==1 and self.compression==0):
            #1pixel=1bits
            colors=[]
            for i in range(2):
                pix=f.read(4)
                colors.append((s.unpack('B', pix[2])[0], s.unpack('B', pix[1])[0], s.unpack('B', pix[0])[0]))
            print colors

            x=0
            y=self.size[1]-1
            i=0
            f.seek(self.offset)
            while 1:
                a=s.unpack('B', f.read(1))[0]
                i+=1
                for j in range(8):
                    bit=a & (0x80>>j)
                    if(int(bit)!=0): self.Pixels[x][y]=colors[1]
                    else: self.Pixels[x][y]=colors[0]
                    x+=1
                    if(x==self.size[0]):
                        x=0
                        y-=1
                        if(i%4!=0):
                            f.read(4-(i%4))
                            i=0
                            break
                    if(y<0) : break
                if(y<0) : break
        elif(self.bpp==4 and self.compression==0):
            #1 pixel = 1/2 octet
            colors=[]
            for i in range(16):
                pix=f.read(4)
                colors.append((s.unpack('B', pix[2])[0], s.unpack('B', pix[1])[0], s.unpack('B', pix[0])[0]))
            print colors
            palindice=['0000', '0100', '0010', '0110', '0001',
                       '0101', '0011', '0111', '1000', '1100',
                       '1010', '1110', '1001', '1101', '1011', '1111'] ##rangement ds lordre des bits qui corresponde a lordre
            #des couleurs ds la palettes : 0000 --> indice 0 de la palette
            x=0
            y=self.size[1]-1
            i=0
            f.seek(self.offset)
            while 1:
                a=s.unpack('B', f.read(1))[0]
                i+=1
                for j in range(2):
                    res=''
                    for l in range(j*4, 4*j+4):
                        set=a&(0x80>>l)
                        if(set!=0) : res+='1'
                        else : res+='0'
                    self.Pixels[x][y]=colors[palindice.index(res)]
                    res=''
                    x+=1
                    if(x==self.size[0]):
                        x=0
                        y-=1
                        if(i%4!=0):
                            f.read(4-(i%4))
                            i=0
                            break
                    if(y<0) : break
                if(y<0) : break
        elif(self.bpp==8 and self.compression==0):
            #1 pixel=1 octet
            colors=[]
            for i in range(256):
                pix=f.read(4)
                colors.append((s.unpack('B', pix[2])[0], s.unpack('B', pix[1])[0], s.unpack('B', pix[0])[0]))
            #print colors
            f.seek(self.offset)
            for y in range(self.size[1], 0, -1):
                i=0
                for x in range(self.size[0]):
                    pix=f.read(1)
                    i+=1
                    self.Pixels[x][y-1]=colors[s.unpack('B', pix)[0]]
                if(i%4!=0): f.read(4-(i%4))
        self.opened=1
    def get_type(self):
        if(self.opened==0):
            raise NotOpened, 'aucun fichier chargé'
        else :
            return self.ident
    def get_filesize(self):
        if(self.opened==0):
            raise NotOpened, 'aucun fichier chargé'
        else :
            return self.filesize
    def get_offset(self):
        if(self.opened==0):
            raise NotOpened, 'aucun fichier chargé'
        else :
            return self.offset
    def get_headersize(self):
        if(self.opened==0):
            raise NotOpened, 'aucun fichier chargé'
        else :
            return self.headersize
    def get_size(self):
        if(self.opened==0):
            raise NotOpened, 'aucun fichier chargé'
        else :
            return self.size
    def get_Pixels(self):
        if(self.opened==0):
            raise NotOpened, 'aucun fichier chargé'
        else :
            return self.Pixels
    def Output(self, pixels):
        img=wx.EmptyImage()
        img.Create(self.size[0], self.size[1])
        for x in range(self.size[0]):
            for y in range(self.size[1]):
                img.SetRGB(x,y, pixels[x][y][0], pixels[x][y][1], pixels[x][y][2])
        return img

def affiche(img):
    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    frame_1 = Afficheur.MyFrame(None, -1, "")
    app.SetTopWindow(frame_1)
    frame_1.set_bitmap(wx.BitmapFromImage(img))
    frame_1.Show()
    app.MainLoop()

if __name__=='__main__':
    b=bitmap()
    b.load('test.bmp')
    print b.get_type()
    print b.get_filesize()
    print b.get_offset()
    print b.get_headersize()
    size=b.get_size()
    print size

    affiche(b.Output(b.get_Pixels()))

Conclusion :


Voila donc normalement Aucun bug fonctionne meme avec les bitmap dont la taille de la largeur n'est pas un multiple de 4 octets.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.