commandes utiliseés : Image.open(), split(), merge(), getdatata(), putdata(),save(),ord(),bin(), chr().

Cacher un message dans une image avec Python

On va cacher une chaîne de caractères dans la photo perceval.png. La chaîne à crypter est Il faut blanchir les champignons! Ou un truc comme ça... dans le genre moisi, quoi

Pour cela on va créer une image avec Python qui contiendra le message. Elle s'appellera couverture.png

À gauche l'image originelle et à droite l'image de couverture crée par Python. Survolez les images avec le pointeur de la souris

perceval couverture

Le principe

On va travailler sur la composante rouge de perceval.png . On rapelle que chaque pixel est défini par un triplet de trois nombres entiers compris entre 0 et 255, le premier donnant la composante rouge, le deuxième la verte et le troisième la bleue.

Dans le tableau suivant, on donne à la deuxième ligne la composante rouge des 16 premiers pixels. Dans une première étape, à la ligne 3 , les valeurs sont réduites au plus grand nombre pair inférieur ou égal à la valeur . Ensuite, on va ajouter à ces nombres pairs des 0 et des 1 et ce sont ces 0 et ces 1, regroupés par 8, qui vont transmettre les informations cachées.

Dans les huit premiers pixels, on rentre l'information 01001011 qui est la représentation binaire de 75; c'est le nombre de caractères de la chaîne. On va donc coder l'image sur 8+8×75=608 pixels.

Dans les huit pixels suivants, on rentre l'information 01001001 qui est la représentation binaire de 73 et qui est le code ascii de I (i majuscule). On a donc récupéré le premier caractère de la chaîne.

pixel12345678910111213141516
perceval36373740414445505049484443424038
réduction36363640404444505048484442424038
binaire0100101101001001
couverture36373640414445515049484443424039

Le code

Vous pouvez récupérer le code par copier-coller et copier les images . En plus de python, il vous faut le module Image

Cacher le message dans l'image de couverture

#-*- coding:Latin-1 -*-

import Image
im = Image.open("perceval.png")
#on récupère les dimensions de l'image
w,h=im.size
#On éclate l'image en trois (rouge vert bleu)
r,g,b=im.split()
#on transforme l'image en liste
r=list(r.getdata())

#le message à  coder
c="Il faut blanchir les champignons! Ou un truc comme ça... dans le genre moisi, quoi"
#on note la longueur de la chaine et on la transforme en binaire
u=len(c)
v=bin(len(c))[2:].rjust(8,"0")
#on transforme la chaine en une liste de 0 et de 1 
ascii=[bin(ord(x))[2:].rjust(8,"0") for x in c]
#transformation de la liste en chaine
a=''.join(ascii)
#on code la longueur de la liste dans les 8 premiers pixels rouges
for j in range(8):
    r[j]=2*int(r[j]//2)+int(v[j])

#on code la chaine dans les pixels suivants
for i in range(8*u):
    r[i+8]=2*int(r[i+8]//2)+int(a[i])
#on recrée l'image rouge 
nr = Image.new("L",(16*p,16*q))
nr = Image.new("L",(w,h))
nr.putdata(r)
#fusion des trois nouvelles images
imgnew = Image.merge('RGB',(nr,g,b)) 
imgnew.save("couverture.png") 

Lire le message caché

#-*- coding:Latin-1 -*-

import Image
im = Image.open("couverture.png")
r,g,b=im.split()
r=list(r.getdata())
#lecture de la longueur de la chaine
p=[str(x%2) for x in r[0:8]]
q="".join(p)
q=int(q,2)
#lecture du message
n=[str(x%2) for x in r[8:8*(q+1)]]

b="".join(n)
message=""

for k in range(0,q):
    l=b[8*k:8*k+8]
    message=message+chr(int(l,2))
print (message)   

Commentaires du code

Pour pouvoir utiliser le code ascii, j'ai sélectionné latin1 plutôt que utf-8 pour l'encodage par défaut de mon éditeur (Drpython)

  1. ord(x) donne le code ascii du caractère x; par exemple ord("F") donne 70
  2. bin(70) donne 70 en binaire; la sortie est 0b1000110
  3. pour utiliser bin(70) dans notre exemple, il faut supprimer les deux premiers caractères de la chaîne 0b et rajouter des 0 à gauche pour obtenir 01000110 ; c'est ce que fait bin(ord("F"))[2:].rjust(8,"0")

fait le 17 février 2010