Couleurs
Principe
Actuellement les écrans d’ordinateurs savent afficher 16 millions de couleurs.
D’après une étude menée en 1998, l’œil humain ne pourrait distinguer « que » 2 millions de couleurs en moyenne.
Il existe différentes solutions pour décrire une couleur, celle qui va nous intéresser est le système RGB (ou RVB en français)
pour les trois couleurs primaires Rouge, Vert, Bleu . En utilisant le principe de synthèse additive des couleurs,
on va pouvoir à partir de ces trois couleurs recréer toutes les couleurs dont nous avons besoin. En voici quelques exemples :
Rouge | Vert | Bleu | Résultat |
100% | 100% | 0% | Jaune |
100% | 0% | 100% | Violet |
0% | 100% | 100% | Cyan |
100% | 50% | 0% | Orange |
100% | 100% | 100% | Blanc |
50% | 50% | 50% | Gris |
0% | 0% | 0% | Noir |
... | ... | ... | ... |
Interface de modification de couleurs du logiciel libre GIMP (www.gimp.org)
Vous pouvez utiliser le logiciel libre GIMP
pour créer une couleur ou plus simplement utiliser un outil en ligne comme le suivant :
www.code-couleur.com
Binaire et octet
Nous avons déjà vu le système de numération d’un ordinateur au début d’année.
Les êtres humains comptent en base décimale (base 10), sûrement car ils ont
dix doigts. Un ordinateur ne peut stocker de l’information qu’à l’aide de signaux
électriques qui sont soit su ON soit sur OFF. Il n’existe donc que 2 états
pour une machine : 0 ou 1 que l’on appelle bit.
Un ordinateur compte donc en base 2, on dit qu’il compte en binaire. Nous avons
déjà vu comment convertir un décimal en binaire et inversement.
Hexadécimal
L’écriture binaire d’un nombre, bien que naturelle pour la machine présente un inconvénient de générer
des nombres dont l’écriture est très longue et
peu lisible pour l’être humain. Pour cela on se propose pour un octet (emplacement nécessaire
pour coder un nombre en 8 bits)de regrouper les bits 4 par 4,
on peut écrire 16 valeurs de 0000 à 1111 en binaire ou plus clairement de 0 à 15 en décimal.
Il nous faudrait donc 16 symboles pour représenter ces valeurs
et ainsi travailler en base 16 que l’on appelle base hexadécimale. On a déjà les 10 chiffres
de 0 à 9 que l’on va compléter par 6 lettres : A, B, C, D, E et F.
En hexadécimal, 2 symboles suffisent donc pour écrire un nombre de 0 à 255. Le principe
est toujours le même, on commence à numéroter : 0, 1, …, 8, 9, A,
B, C, D, E, F, puis 10, 11, 12, …, 1F, 20… La conversion d’une base à l’autre se fait aussi
selon les mêmes règles.
Conversion d’un nombre décimal en hexadécimal :
Comme pour le binaire, on multiplie par des puissances de 16, par exemple,
Le nombre en hexadécimal 1A3C est égal en décimal : C×160 + 3×161 + A×162 + 1×163 = 6 716.
Conversion d’un décimal en hexadécimal : la conversion se fait à l’aide de divisions
successives par 16. Par exemple, pour le nombre 6 716
Nombre | Quotient | Reste | Symbole |
6716 | 419 | 12 | C |
419 | 26 | 3 | 3 |
26 | 1 | 10 | A |
1 | 0 | 1 | 1 |
Le nombre 6 716 s’écrit donc en hexadécimal sous la forme 1A3C.
Exercice 1
Compléter le tableau suivant :
Binaire | Décimal | Hexadécimal |
101010 | ... | ... |
... | 723 | ... |
... | ... | BAC |
Pour revenir aux couleurs et Python
En Python, on peut donner un nombre en binaire ou en hexadécimal en utilisant une chaîne
de caractère commençant respectivement
par 0b ou 0x qu’il faut alors transformer en valeur numérique à l’aide de la fonction eval .
Depuis Python 2.6 des fonctions ont été
ajoutées pour passer d’un nombre en écriture décimale à l’écriture binaire ou hexadécimale,
comme le résume le tableau d’exemples qui suit :
Saisie | Résultat |
eval('0b110') | 6 |
eval('0xCA0') | 3 232 |
bin(37) | 0b100101 |
hex(243) | 0xf3 |
Pour revenir à nos couleurs, en Python, avec le module tkinter, un certain nombre de couleurs
existe déjà sous forme de chaînes de
caractères parmi lesquelles :
black, white, red, purple, cyan, maroon, green, blue, orange, yellow, grey ….
Si l’on souhaite une couleur personnalisée, on peut alors préciser la couleur que l’on souhaite
en indiquant le code HTML de la couleur
désirée. Le code HTML d’une couleur est une chaîne de caractère composée de :
- un symbole # pour commencer.
- 2 symboles correspondant au niveau de rouge codé en hexadécimal.
- 2 symboles correspondant au niveau de vert codé en hexadécimal.
- 2 symboles correspondant au niveau de bleu codé en hexadécimal.
Exercice 2
Trouver la proportion de rouge, vert et bleu des couleurs
suivantes données en code HTML :
#B9121B
#E70739
#375D81
#FFF168
Les listes 2 D
Ce paragraphe vous intéressera particulièrement si vous souhaitez réaliser un jeu de plateau.
Nous avons déjà vu comment transformer un fichier texte en plateau de jeu :
Mais jusque là nous n’avons pas conservé ces informations. Or si notre personnage a vocation à se déplacer sur
le plateau, il faudra être en mesure de savoir sa position et ses déplacements dans le plateau par exemple.
Une première solution est de charger notre fichier dans une liste de chaînes de caractères :
fichier = open('ExempleISN.txt','r') # pour ouvrir le fichier exempleISN.txt, pour la lecture
cases = [] # pour initialiser la liste cases
for ligne in fichier:
cases.append(ligne) # pour parcourir le fichier ExemplesISN.txt et stocker
# le contenu dans la liste cases
fichier.close() # fermeture du fichier ExempleISN.txt
chaque élément de la liste correspond à une ligne du plateau, par exemple si on saisit print(cases[8])
(l’avant dernière ligne), on obtient :
I S S N NN
Ainsi on peut savoir ce qu’il y a à la ligne 9 et à la colonne 3 (la numérotation commence à zéro) :
ligne = cases[8]
print(ligne[2]) # ce qui affiche >>> S
mieux encore, en une seule ligne :
print(cases[8][2])
# ce qui affiche >>> S
Cette technique nous permet donc de savoir quel élément de décor se cache sous le personnage.
Cependant les chaînes de
caractères ne permettent pas de modifier un caractère de celles-ci, ce qui peut-être nécessaire
(par exemple éliminer un élément
du plateau ou faire un trou dans le plateau …). A la place d’une chaîne par ligne, il serait donc
préférable d’avoir une liste par ligne.
Voici comment modifier le chargement du fichier ExempleISN.txt pour obtenir une liste de listes :
fichier = open('ExempleISN.txt','r') # pour ouvrir le fichier exempleISN.txt, pour la lecture
cases = [] # pour initialiser la liste cases
for ligne in fichier:
cases.append(list(ligne)) # pour parcourir le fichier ExemplesISN.txt
# et stocker le contenu dans la liste des listes cases
fichier.close() # fermeture du fichier ExempleISN.txt
Si on veut lire le contenu de la ligne 8 et de la colonne 3 :
print(cases[7][2])
Si on veut modifier le contenu de la ligne 8 et de la colonne 3 :
cases[7][2] = ...
Remarque
Notez que l’accès au contenu se fait donc sous la forme : cases[ligne][colonne]
Exemple 1
Voici deux programmes :
Programme 1
cases = [ ]
for i in range(20) :
cases.append([0]*20)
Programme 2
cases = [[0 for i in range(20)] for j in range(20)]
Quel est le rôle de chacun de ces deux programmes ?
Traitement d'images
Changement de format d'une image
la fonction :
img = Image.open('image.format')
permet d'ouvrir une image de tout format (compatible avec la bibliothèque Pil).
Pour afficher par exemple le format, les dimensions et le mode de l’image, on écrit :
print ('image.format', im.format, "%dx%d" % im.size, im.mode)
La fonction
img.save('image.format', "FORMAT" )
permet de transformer le format d’une image img en un autre format :
Exemple 2
Quel est le rôle du programme suivant ?
from PIL import Image
im = Image.open('image.png')
im.save('image.jpg', "JPEG")
im.save('image.bmp', "BMP")
im.save('image.gif', "GIF")
Pour réduire les dimensions d’une image, on utilise la fonction :
img.resize((newl,newh))
Exemple 3
Quel est le rôle du programme suivant ?
from PIL import Image
from PIL import Image
im = Image.open('image.png')
out = im.resize((100,120))
out.save('resize.png')
Déplacer ou recopier plusieurs images
Exemple 4
Recopier et tester le programme suivant :
from PIL import Image
from PIL import ImageDraw
img = Image.new("RGB", (500,400), "white")
draw = ImageDraw.Draw(img)
into = Image.open("explication.png")
w,h=into.size
img.paste(into, (0,0,w,h))
img.paste(into, (300,0,300+w,h))
img.paste(into, (200,200,200+w,200+h))
img.paste(into, (300,300,300+w,300+h))
del draw
img.save("dessin.jpg", "JPEG")
Quel est le rôle de ce programme ?
On peut effectuer une rotation d’un angle donné à une image. Pour incliner une image de α
degrés, on utilise la fonction
img.rotate(α)
Exemple 5
Recopier et tester le programme suivant :
from PIL import Image
img = Image.open('image.png')
out = img.rotate(45)
out.save('NewImage.png')
Quel est le rôle de ce programme ?
On peut aussi effectuer une symétrie axiale à une image. Voici un exemple qui montre comment
construire l’image symétrique par rapport à un axe vertical :
Exemple 6
Recopier et tester le programme suivant :
from PIL import Image
img = Image.open('image.png')
w,h=img.size
box = (0, 0, w, h)
src = img.crop(box)
out = img.resize((w*2,h))
out.paste(src,(0,0,w,h))
src=img.transpose(Image.FLIP_LEFT_RIGHT)
out.paste(src,(w,0,2*w,h))
out.save('NewImage.png')
Quel est le rôle de ce programme ?
Ecrire du texte sur une image
Il est aussi possible d’écrire du texte sur une image :
Exemple 7
Recopier et tester le programme suivant :
#-*- coding:utf8 -*-
import sys
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
txt = 'Salle d'informatique'
txt2 = 'Photo de synthèse'
font = ImageFont.truetype('verdanai.ttf',200)
font2 = ImageFont.truetype('simsun.ttc',200)
im = Image.open('image.png')
w,h = im.size
draw = ImageDraw.Draw(im)
draw.text( (0,50), txt, font=font)
draw.text( (0,300), txt2, font=font2)
del draw
im.save('font.png', "PNG")
Quel est le rôle de ce programme ?
Remarque
Attention si vous importez la librairie tkinter de la manière from kinter import *,
alors son module Image écrasera celui du module PIL (ou inversement, en fonction de la position
des imports). C'est une des raisons qui font qu'il est déconseillé d'utilisez ce schéma pour l'importation.
C'est pour cela qu'il est préférable pour tkinter d'importer de la manière import tkinter Tk
ou import tkinter Canvas .
Décomposition d'une image en RVB
la fonction :
img = Image.open('image.format')
permet d'ouvrir une image de tout format (compatible avec la bibliothèque Pil).
la fonction :
img.getpixel()
retourne un t-uples à 3 composantes contenant
les valeurs des pixels de l'image si l’image est en couleur.
Par exemple pour un pixel de coordonnées (x ; y) d’une image img l’instruction :
rouge[x][y],vert[x][y], bleu[x][y] = img.getpixel(x ; y)
permet
de récupérer les trois composantes RVB du pixel de coordonnés (x ; y).
Exemple 8
Recopier et tester le programme suivant :
from PIL import Image
# Charger l’image qu’on nomme img
img = Image.open('Joconde.jpg')
#Définir les dimensions de l’image
w,h = img.size
#Initialiser les 3 composantes RVB de l’image
rouge = [[0 for j in range(h)] for i in range(w)]
vert = [[0 for j in range(h)] for i in range(w)]
bleu = [[0 for j in range(h)] for i in range(w)]
for j in range(0,h):
for i in range(0,w):
p = img.getpixel((i,j))
rouge[i][j] = p[0]
vert[i][j] = p[1]
bleu[i][j] = p[2]
Quel est le rôle de ce programme ?
Dessiner dans une fenêtre
Pour dessiner dans une fenêtre, on ouvre une fenêtre graphique, dans laquelle on crée un Canvas nommé Fond.
Ensuite on exécute l’instruction
Fond.create_rectangle(x,y,x,y,outline=(rouge, vert, bleu))
qui a pour effet de dessiner un pixel dans la x-ème colonne et la y-ème ligne de cette fenêtre, dont la couleur
est décrite dans le système rouge, vert, bleu par les nombres rouge, vert et bleu. La coordonnée x varie entre 0 et
largeur – 1 et la coordonnée y entre 0 et hauteur – 1. Les nombres rouge, vert et bleu varient entre 0 et 255.
Attention axe vértical
De même que les Anglais roulent à gauche, l’axe vertical, en géométrie algorithmique, est orienté vers le bas.
Savoir-faire : créer une image
Méthode
- Établir une condition sur les coordonnées d’un pixel qui permet de décider s’il appartient
à la figure à tracer ou non.
- Écrire une instruction qui balaye la fenêtre graphique, au moyen de deux boucles imbriquées,
l’une sur les abscisses et l’autre sur les ordonnées.
- Dans le corps de la boucle la plus interne, affecter la couleur appropriée à chaque pixel,
selon qu’il appartient à la figure ou non.
Exemple 9
Recopier et tester le programme suivant :
from tkinter import Tk
from tkinter import Canvas
fen = Tk()
fen.geometry("400x400")
fen.title("Carré Rouge")
Fond=Canvas(fen,width=400,height=400,bg="white")
Fond.place(x=0,y=0)
couleur = "#%02x%02x%02x" % (255,0,0)
for y in range(0,400):
for x in range(0,400):
if 100<=x and x <= 250 and 50 <= y and y <= 200 :
Fond.create_rectangle(x,y,x,y,outline=couleur)
fen.mainloop()
Quel est le rôle de ce programme ?
Produire un fichier au format PPM
Si au lieu de dessiner cette image dans une fenêtre graphique, on veut l’enregistrer dans un fichier, par exemple au format PPM,
afin de pouvoir l’inclure dans une page web ou l’attacher à un courrier, on doit d’abord représenter cette image dans un tableau,
au sens que l’on a donné à ce mot au paragraphe II., puis produire un fichier au format PPM, à partir de ce tableau. Pour représenter
une image en niveaux de gris, il suffit d’utiliser un tableau bidimensionnel t, dont la case t[i][j] contient la valeur du pixel de
coordonnées (i ; j). Pour représenter une image en couleurs, on utilise trois tableaux bidimensionnels rouge, vert et bleu, les cases
rouge[i][j], vert[i][j] et bleu[i][j] contenant les trois composantes de la couleur du pixel de coordonnées (i ; j).
On peut alors transformer le programme ci-avant en remplaçant toutes les instructions
Fond.create_rectangle(x,y,x,y,outline=couleur)
par :
rouge[x][y] = r
vert[x][y] = v
bleu[x][y] = b
Par exemple, le dessin du carré rouge :
Fond.create_rectangle(x,y,x,y,outline=couleur)
par :
couleur = "#%02x%02x%02x" % (255,0,0)
for y in range(0,400):
for x in range(0,400):
if 100<=x and x <= 250 and 50 <= y and y <= 200 :
Fond.create_rectangle(x,y,x,y,outline=couleur)
Une fois le dessin terminé, on peut l’enregistrer au format PPM qui est un fichier texte qui doit avoir le format suivant :
P3
#
400
400
255
rouge[0][0]
vert[0][0]
bleu[0][0]
rouge[1][0]
vert[1][0]
bleu[1][0]
...
La première ligne contient les caractères P3 pour indiquer que c’est un fichier au format PPM, la deuxième est un commentaire,
la troisième la largeur de l’image, la quatrième sa hauteur, la cinquième la valeur 255 pour indiquer que les valeurs des pixels vont
de 0 à 255, puis sur les lignes suivantes les trois valeurs rouge, vert et bleu en énumérant les pixels de gauche à droite et de haut en bas.
Recopier et tester ce programme
fichier = open("Carre.ppm","w")
print("P3",file=fichier)
print("#",file=fichier)
print(400,file=fichier)
print(400,file=fichier)
print(255,file=fichier)
for y in range(0,400):
for x in range(0,400):
print(rouge[x][y], file=fichier)
print(vert[x][y], file=fichier)
print(bleu[x][y], file=fichier)
fichier.close()
Quel le rôle de ce programme ?
Lire un fichier au format PPM
Inversement, on peut écrire un programme qui lit un fichier au format PGM ou PPM dans un tableau ou dans trois, selon que l’image
est en niveaux de gris ou en couleurs.
La seule difficulté, pour lire un tel fichier, est due au fait qu’il est possible d’insérer dans un fichier au format PGM ou PPM
des commentaires, c’est-à-dire des lignes qui commencent par le caractère # et qui doivent être ignorées. En pratique cependant,
la plupart des fichiers au format PGM et PPM ont un seul commentaire, à la deuxième ligne :
P3
# format PPM, cette photo a été prise ...
279
400
255
113
89
72
...
On se limite donc à des fichiers de cette forme si bien que les fichiers PPM que l’on lit sont de la forme suivante :
- deux lignes qui peuvent être ignorées,
- la largeur de l’image, suivie d’un retour à la ligne ou d’un espace,
- la hauteur de l’image, suivie d’un retour à la ligne ou d’un espace,
- la valeur maximale utilisée pour exprimer les niveaux de rouge, vert et bleu, suivie d’un retour à la ligne ou d’un espace,
- la liste des pixels, ligne par ligne, de haut en bas et de gauche à droite, séparés par des retours à la ligne ou des espaces.
On peut lire un tel fichier avec le programme suivant :
fichier = open("Joconde.ppm","r")
s = fichier.readline()
s = fichier.readline()
largeur = eval(fichier.readline())
hauteur = eval(fichier.readline())
maximum = eval(fichier.readline())
rouge = [[0 for j in range(0,hauteur)] for i in range(0,largeur)]
vert = [[0 for j in range(0,hauteur)] for i in range(0,largeur)]
bleu = [[0 for j in range(0,hauteur)] for i in range(0,largeur)]
for j in range(hauteur):
for i in range(largeur):
rouge[i][j] = eval(fichier.readline())
vert[i][j] = eval(fichier.readline())
bleu[i][j] = eval(fichier.readline())
fichier.close()
et ensuite afficher cette image dans une fenêtre :
fen = Tk()
fen.geometry("500x600")
fen.title("Pgm")
Fond=Canvas(fen,width=largeur,height=hauteur,bg="white")
Fond.place(x=0,y=0)
for y in range(0,hauteur):
for x in range(0,largeur):
couleur = "#%02x%02x%02x" % (rouge[x][y],vert[x][y],bleu[x][y])
Fond.create_rectangle(x,y,x,y,outline=couleur)
fen.mainloop()
La lecture et l’affichage d’une image en couleurs, au format PGM sont similaires, sauf qu’il faut
lire un seul nombre pour chaque pixel et le stocker dans un seul tableau.
Transformer des images
Une fois une image est représentée dans un tableau, il est facile de la transformer.
Par exemple, d’inverser la quantité de chaque couleur :
for j in range(0,hauteur):
for i in range(0,largeur):
rougebis[i][j] = maximum - rouge[i][j]
vertbis[i][j] = vert[i][j]
bleubis[i][j] = bleu[i][j]
ce qui transforme l’image (1) en l’image (2).
Savoir-faire : transformer une image en couleurs en une image en niveaux de gris.
Méthode
On remplace chaque pixel de couleur r, v, b par un pixel dont le niveau de gris est la moyenne des nombres r, v et b.
Savoir-faire : augmenter le contraste d’une image en niveaux de gris.
Méthode
On fixe un seuil et on remplace tous les pixels plus clairs que ce seuil par un pixel blanc et tous
les pixels plus sombres que ce seuil par un pixel noir.
Savoir-faire : changer la taille d’une image
Méthode :
On calcule la nouvelle image pixel par pixel.
Voir l'exercice 3