Skip to content
Tags

,

Modifier une image BMP avec Construct (et sans PIL)

31 juillet 2013

Le module le plus utilisé pour manipuler des images en Python est probablement PIL (ou Pillow pour les utilisateurs de pypi). Construct est un module pour analyser et contruire des fichiers binaires. La définition des fichiers binaires doit être faite pour chaque format que l’on souhaite traiter. Cependant, certains formats de fichiers binaires sont déjà définis, comme les formats elf, ext2, bmp, gif, etc. (« etc » n’étant pas un format binaire mais juste l’abréviation de et caetera.) Les objectifs des deux modules sont différents donc il ne faut pas espérer remplacer l’un par l’autre.

L’objectif de l’article est faire une rapide présentation de Construct en changeant la couleur de certains pixels d’une image au format .bmp.

Remplacement des pixels blancs par des pixels noirs

Remplacement des pixels blancs par des pixels noirs

Installer Construct

Pour contruire un virtualenv et installer construct :

$ virtualenv democonstruct
$ ./democonstruct/bin/pip install construct

Lire un fichier

La méthode de base est parse() :

import construct.formats.graphics.bmp
with open("/tmp/python_fond_blanc.bmp") as f:
    content = f.read()
obj = construct.formats.graphics.bmp.bitmap_file.parse(content)

Il existe aussi une méthode plus courte pour faire l’équivalent :

obj = construct.formats.graphics.bmp.bitmap_file.parse_stream(open("/tmp/python_fond_blanc.bmp"))

L’objet analysé dispose en attributs des données extraites du fichier :

Container({'palette': <construct.lib.container.LazyContainer object at 0xe23fc8>,
           'vertical_dpi': 2835,
           'image_data_size': 20160,
           'compression': 'Uncompressed',
           'planes': 1,
           'bpp': 24,
           'signature': 'BM',
           'important_colors': 0,
           'header_size': 40,
           'height': 80,
           'width': 84,
           'version': 'v3',
           'data_offset': 54,
           'file_size': 20214,
           'number_of_pixels': 6720,
           'horizontal_dpi': 2835,
           'pixels': <construct.lib.container.LazyContainer object at 0xdea7e0>,
           'colors_used': 0})

Ici, le fichier est encodé selon la version 3 du format, fait 84 pixels de large et 80 pixels de hauts, etc. Par contre, les pixels ne sont pas directement accessibles mais le sont par l’intermédiaire d’un LazyContainer, un conteneur paresseux dans le but de ne pas charger en mémoire des données qui pourraient être énormes (par exemple une photo à taille réelle d’une grosse baleine en surpoid).
Pour accéder aux pixels, il suffit de faire :

>>> obj.pixels
<construct.lib.container.LazyContainer object at 0x287c7e0>
>>> obj.pixels.value
[[[255, 255, 255], [255, 255, 255], #ça continue encore et encore...
...                                 #c'est que le début, d'accord, d'accord
, [255, 255, 255], [255, 255, 255], [255, 255, 255]]]

obj.pixels.value est une liste de lignes. Chaque ligne est une liste de pixels. Chaque pixel est une liste de 3 entiers compris entre 0 et 255 (rouge, vert et bleu codées sur un octet).

Modifier un fichier

Il est possible de construire un fichier binaire en passant un objet en paramètre de la méthode build(). L’objet peut être défini complètement ou obtenu en modifiant un objet existant préalablement. Dans l’exemple ci-dessous, on va changer les pixels blancs (défini par [255, 255, 255]) en noirs (défini par [0, 0, 0]).

def blanc_vers_noir(src_pixels):
    BLANC = [255, 255, 255]
    NOIR = [0, 0, 0]
    dst_pixels = []
    for index_ligne, ligne in enumerate(src_pixels):
        dst_pixels.append([])
        for pixel in ligne:
            if pixel == BLANC:
                dst_pixels[index_ligne].append(NOIR)
            else:
                dst_pixels[index_ligne].append(pixel)
    return dst_pixels

conversion = blanc_vers_noir(obj.pixels.value)
obj.pixels = conversion

with open("/tmp/python_fond_noir.bmp", "w") as f:
    f.write(construct.formats.graphics.bmp.bitmap_file.build(obj))

La nouvelle image est enregistrée avec les pixels transformés. À noter que les yeux ont aussi été convertis et que le détourage du logo est mal fait car la valeur n’est pas celle d’un blanc pur.

Utilité et limites de Construct

S’il s’agit de modifier des images, PIL fait probablement mieux l’affaire car il s’agit d’une bibliothèque de plus haut niveau (par exemple, et en toute objectivité, le meilleur exemple du monde). La seule utilité que j’y vois serait pour générer automatiquement des dégradés ou des figures géométriques simples.

D’un point de vue plus général, Construct permet de décrire par une structure Python un format de données binaires pour naviguer et modifier plus facilement des fichiers. L’utilisation qui en est faite ici est surtout à but de démonstration. S’il est possible de s’en servir pour naviguer dans des fichiers texte, d’autres bibliothèques seront plus adaptées (BeautifulSoup ou lxml pour du HTML ou XML).

Construct devrait surtout servir à décrire les fichiers binaires à manipuler dans une application. C’est faisable en créant une nouvelle classe décrivant la structure binaire du fichier. Les méthodes parse() et build() sont alors disponible automatiquement pour la nouvelle classe.

From → Python

Laisser un commentaire

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :