Symétrie .
L'image à charger ici est une photo au format jpg.
L'objectif est de retourner la photo, c'est à dire de passer de la première image à la seconde.


L'image à charger ici est une photo au format jpg.
L'objectif est de retourner la photo, c'est à dire de passer de la première image à la seconde.
Une résolution :
# import d'un module de manipulation
# d'images :
from PIL import Image
# ouverture de l'image initiale :
imageSource=Image.open('ptpanda.jpg')
# récupération de ses dimensions :
largeur, hauteur=imageSource.size
# création d'une image de mêmes dimensions :
imageBut=Image.new('RGB',(largeur,hauteur))
# parcours des pixels :
for y in range(hauteur) :
for x in range(largeur) :
# récupération de la couleur du pixel (x,y) :
p=imageSource.getpixel((x,y))
# coordonnées pour ce pixel dans l'image finale :
imageBut.putpixel((x,-y+hauteur-1),p)
# sauvegarde de l'image créée :
imageBut.save('sym_panda.jpg')
# on lance une visualisation :
imageBut.show()
Il existe une fonction du module PIL automatisant cette tâche.
from PIL import Image
# ouverture de l'image initiale :
imageSource=Image.open('ptpanda.jpg')
# rotation :
imageSource=imageSource.rotate(180)
# sauvegarde du résultat sous un nouveau nom :
imageSource.save('pandateteenbas.jpg')
# on lance une visualisation :
imageSource.show()
L'image à charger ici est une photo au format png.
L'objectif est de remettre la tête d'Angelina dans le bon sens.
L'utilisation de rotate est interdite. On travaillera pixel par pixel.
Il suffit de reprendre le code de l'exercice précédent en changeant le nom du fichier.
from PIL import Image
# ouverture image :
imageSource=Image.open("ange.png")
# largeur et hauteur en pixels de l'image
largeur,hauteur=imageSource.size
imageBut=Image.new("RGB",(largeur,hauteur))
# pour chaque ligne :
for y in range(hauteur):
#pour chaque colonne :
for x in range(largeur):
# code du pixel
p=imageSource.getpixel((x,y))
# création du pixel correspondant dans la nv image :
imageBut.putpixel((x,-y+hauteur-1),p)
# sauvegarde de l'image créée :
imageBut.save("sym_axe.jpg")
# on montre l'image :
imageBut.show()
Avec rotate :
from PIL import Image
# ouverture de l'image initiale :
imageSource=Image.open('ange.png')
# rotation :
imageSource=imageSource.rotate(180)
# sauvegarde du résultat sous un nouveau nom :
imageSource.save('tetehaute.png')
# on lance une visualisation :
imageSource.show()
La recherche d'algorithmes sur les images est encore aujourd'hui en plein essor avec certaines applications que vous connaissez : reconnaissance des visages par les appareils photo, de façon plus générale reconnaissance de formes, reconnaissance de mots,...
L'étude de ces algorithmes s'appuient pour la plupart sur des notions mathématiques qui dépassent le niveau de la terminale. Toutefois l'expression de l'algorithme lui-même peut être parfois extrêmement simple.
Nous allons ici mettre en oeuvre un algorithme simple de "mise en relief".
L'image à charger ici est une image au format pgm : chaque pixel est, dans ce format, codé par un seul entier, entre 0 et 255, cet entier correspond à un 'niveau' de gris.
a | b | c |
d | e | f |
g | h | i |
L = []
for k in range(-1020, 1021) :
L.append(k//8)
print(L)
Ou :
# L, liste qui contiendra les résultats
# de k//8+128 avec k entre -1020 et +1020 :
L = []
for k in range(-1020, 1021) :
L.append(k//8 + 128)
# on transforme L en ensemble pour supprimer les répétitions
L =set(L)
# on ordonne L
L = list(L)
L.sort()
# on compare L à la liste des entiers 0,1,2,..., 255
print( L == list(range(0,256)) )
from PIL import Image
imageSource=Image.open("lpa.pgm")
largeur,hauteur=imageSource.size
imageBut=Image.new("L",(largeur,hauteur))
# traitement des bords :
for x in range(largeur):
imageBut.putpixel((x,0),0)
imageBut.putpixel((x,hauteur-1),0)
for y in range(hauteur):
imageBut.putpixel((0,y),0)
imageBut.putpixel((largeur-1,y),0)
# pour chaque ligne :
for y in range(1,hauteur-1):
#pour chaque colonne :
for x in range(1,largeur-1):
# code du pixel (niveau de gris)
a=imageSource.getpixel((x-1,y-1))
b=imageSource.getpixel((x,y-1))
d=imageSource.getpixel((x-1,y))
f=imageSource.getpixel((x+1,y))
h=imageSource.getpixel((x,y+1))
i=imageSource.getpixel((x+1,y+1))
q=-2*a-b-d+f+h+2*i
q=q//8
q+=128
imageBut.putpixel((x,y),q)
# sauvegarde de l'image créée :
imageBut.save("convol.pgm")
# on montre l'image :
imageBut.show()
On obtient l'image à charger ici.
Exécuter le programme suivant. Modifier la valeur de n (valeurs entre 0 et 255) et la valeur du masque (entre 0b00000000 et 0b11111111).
n = 145 # entier entre 0 et 255
masque = 0b11110000 # entier en binaire, 8 bits
m = n & masque
print('n en décimal : {}, et en binaire : {}.'.format(n, bin(n)) )
print('masque en décimal : {}, et en binaire : {}.'.format(masque, bin(masque)) )
print('m en décimal : {}, et en binaire : {}.'.format(m, bin(m)) )
Expliquer ensuite pourquoi on parle ici de masquage.
Tester le programme ci-dessous, modifier les valeurs de n, essayer pour dec d'autres valeurs que sa valeur par défaut.
def decale(n, dec=4) :
return n>>dec
n=52
print('n en décimal : {}, et en binaire : {}.'.format(n ,bin(n)) )
m=decale(n)
print('m en décimal : {}, et en binaire : {}'.format(m,bin(m)) )
Vous testerez également l'effet d'un décalage obtenu avec <<
.
A l'aide de ce qui précède, imaginer une opération permettant de cacher une image à l'intérieur d'une autre.
Vous écrirez également le programme permettant de récupérer l'image cachée.
Vous utiliserez l'image du chat précédente, et l'image (de mêmes largeur et hauteur) suivante. Dans le résultat final, on devra voir l'image des tortues, l'image du chat étant cachée dans l'image des tortues.
Le résultat de l'opération n & masque s'écrit en binaire avec des chiffres 0 aux rangs correspondant à des 0 sur le masque et avec les mêmes chiffres (0 ou 1) que l'écriture binaire de n aux rangs correspondants à des 1 sur le masque.
Exemple (a,b,c,d,e,f,g,h étant les 0 et 1 de l'écriture binaire de n) :
Ecriture binaire de n | a | b | c | d | e | f | g | h |
---|---|---|---|---|---|---|---|---|
masque en binaire | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 1 |
n & masque en binaire | a | 0 | c | d | 0 | 0 | 0 | h |
L'opération consiste ainsi à masquer la valeur de certains bits de l'entier de départ en les mettant systématiquement à 0.
n >> 4
décale les bits de l'écriture binaire de n vers la droite, ce qui revient à dire que l'on efface
les 4 bits de poids faibles. Le bit de poids 4 devient donc le bit de poids 0 dans le résultat, le bit
de poids 5 devient le bit de poids 1...
On masque chaque entier des composantes RGB par le programme suivant :
from PIL import Image
def masque(n) :
return n & 0b11110000
# ouverture de l'image initiale :
imageSource=Image.open('cat.png')
# récupération de ses dimensions :
largeur, hauteur=imageSource.size
# création d'une image de mêmes dimensions :
imageBut=Image.new('RGB',(largeur,hauteur))
# parcours des pixels :
for y in range(hauteur) :
for x in range(largeur) :
# récupération de la couleur du pixel (x,y) :
r,v,b = imageSource.getpixel((x,y))
# dans l'image finale :
imageBut.putpixel( (x,y),(masque(r),masque(v), masque(b)) )
# sauvegarde de l'image créée :
imageBut.save('catuni.png')
# on lance une visualisation :
imageBut.show()
L'image obtenue au final est encore largement reconnaissable. On a toutefois une perte de qualité visible sur certaines zones.
En masquant les bits de poids faibles, on modifie les composantes mais de manière peu prononcée : on décale ainsi les couleurs de chaque pixel mais en gardant à peu près les couleurs de départ. L'opération de masquage aura pour effet de masquer les différences de couleurs entre couleurs relativement peu éloignées.
Par contre si l'on remplace la fonction masque par la suivante :
def masque(n) :
return n & 0b00001111
on aura cette fois une perte complète des informations de départ puisqu'on ramène toutes les composantes entre 0b0000 et 0b1111 (c'est à dire entre 0 et 15), masquant cette fois les différences entre des couleurs très éloignées.
Ce que nous ont appris les manipulations précédentes, c'est que l'essentiel de l'information définissant l'image se trouve dans les bits de poids forts des composantes RGB.
L'idée est donc de récupérer les 4 bits de poids forts des composantes de l'image à cacher et de remplacer les 4 bits de poids faibles de l'image qui restera apparente par ces quatre bits.
composante R sur l'image du chat | a7 | a6 | a5 | a4 | a3 | a2 | a1 | a0 |
---|---|---|---|---|---|---|---|---|
composante R sur l'image des tortues | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
composante R sur l'image des tortues cachant le chat | b7 | b6 | b5 | b4 | a7 | a6 | a5 | a4 |
composante R sur l'image d'un chat cachant les tortues | a7 | a6 | a5 | a4 | b7 | b6 | b5 | b4 |
Programme pour cacher le chat dans l'image des tortues :
from PIL import Image
def masquelow(n) :
return n & 0b11110000
def decale(n, dec=4) :
return n>>dec
# ouverture des images de départ :
chat=Image.open('cat.png')
tortue=Image.open('turtle.png')
# récupération de leurs dimensions communes :
largeur, hauteur=chat.size
# variable pour image tortue cachant chat :
tortuechat=Image.new('RGB',(largeur,hauteur))
# parcours des pixels :
for y in range(hauteur) :
for x in range(largeur) :
# récupération de la couleur du pixel (x,y) :
rc, vc, bc = chat.getpixel((x,y))
rc, vc, bc = decale(rc), decale(vc), decale(bc)
rt, vt, bt = tortue.getpixel((x,y))
rt, vt, bt = masquelow(rt), masquelow(vt), masquelow(bt)
# dans l'image finale :
tortuechat.putpixel( (x,y),(rc+rt, vc+vt, bc+bt) )
# sauvegarde de l'image créée :
tortuechat.save('ghostcat.png')
# on lance une visualisation :
tortuechat.show()
Code de récupération du chat :
from PIL import Image
def decale(n, dec=4) :
"""on décale cette fois vers la gauche.
Et on ne garde que 8 bits."""
return (n << dec) & 0b11110000
# ouverture de l'image "truquée" :
tortuechat=Image.open('ghostcat.png')
# dimensions communes :
largeur, hauteur=tortuechat.size
# variable pour image de récupération du chat :
newcat=Image.new('RGB',(largeur,hauteur))
# parcours des pixels :
for y in range(hauteur) :
for x in range(largeur) :
# récupération de la couleur du pixel (x,y) :
r,v,b = tortuechat.getpixel((x,y))
r,v,b = decale(r),decale(v),decale(b)
# dans l'image finale :
newcat.putpixel((x,y),(r,v,b))
# sauvegarde de l'image créée :
newcat.save('resurrection.png')
# on lance une visualisation :
newcat.show()
L'image à charger ici est une image au format jpeg. Cette image étant 'lourde' (trop lourde par exemple pour l'usage usuel sur une page web), on souhaite la réduire.
Il existe bien sûr des logiciels se chargeant de cela. Mais l'objectif est ici de le faire en agissant directement sur les pixels.
Pour cela, on se propose de mettre en oeuvre l'idée suivante :
Mettre en oeuvre cette idée.
Un programme possible :
# import d'un module de manipulation
# d'images :
from PIL import Image
# ouverture de l'image initiale :
source=Image.open('tronctortues.JPG')
# récupération de ses dimensions :
largeur, hauteur=source.size
# facteur de réduction :
fr=4
# image réduite :
nvlg=largeur//fr
nvht=hauteur//fr
but=Image.new('RGB',(nvlg,nvht))
# parcours des pixels
# on néglige éventuellement des colonnes à droite
# et des lignes en bas
for y in range(0,nvht*fr,fr) :
for x in range(0,nvlg*fr,fr) :
# récupération des couleurs initiales et moyenne
rouge=sum([source.getpixel((a,b))[0] for a in range(x,x+fr) for b in range(y,y+fr)])//(fr*fr)
vert=sum([source.getpixel((a,b))[1] for a in range(x,x+fr) for b in range(y,y+fr)])//(fr*fr)
bleu=sum([source.getpixel((a,b))[2] for a in range(x,x+fr) for b in range(y,y+fr)])//(fr*fr)
# coordonnées pour ce pixel dans l'image finale :
but.putpixel((x//fr,y//fr),(rouge,vert,bleu))
# sauvegarde de l'image créée :
but.save('reduc.jpg')
# on lance une visualisation :
but.show()
Prenez le temps de vérifier sur l'image finale que largeur et hauteur ont été divisées par fr.
Il existe une fonction du module PIL automatisant cette tâche de réduction.
Si l'on veut par exemple réduire notre image de tortues aux dimensions (324, 216) :
from PIL import Image
# ouverture de l'image initiale :
imageSource=Image.open('tronctortues.jpg')
# réduction :
imageSource=imageSource.resize((324,216))
# sauvegarde du résultat sous un nouveau nom :
imageSource.save('petitestortues.jpg')
# on lance une visualisation :
imageSource.show()