Pré-requis indispensable : être à l'aise avec la notion de tableau vu au chapitre précédent, et comment créer un tableau, le modifier, s'adresser à un de ses éléments, les parcourir, ...
Dans le cas contraire : revoir le chapitre !!
Vous avez vu aux chapitres précédents la notion de tableaux, correspondant au type list
de Python.
Rien n’empêche en Python ( et dans de nombreux autres langages de programmation ) de construire des tableaux à deux dimensions, ou plutôt des "tableaux de tableaux", c'est à dire des tableaux dont les éléments eux-mêmes sont des tableaux !
Si les "sous-tableaux" de ces "tableaux de tableaux" contiennent tous le même nombre d'éléments, alors on parle dans ce cas de matrices, que l'on peut se représenter comme une "grille" formée de "colonnes" et de "lignes", l'intersection de chaque "colonne" avec chaque "ligne" représentant une "case" dans cette grille.
C'est ce type de structure de données que l'on étudiera dans ce chapitre; en effet, les matrices ont de nombreuses applications en informatique, comme par exemple :
Un matrice de 4 lignes et 4 colonnes pourrait se présenter sous cette forme :
matrice = [[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11],
[12, 13, 14, 15]]
On retrouve les crochets déjà utilisés pour définir un tableau et les virgules séparant les différents éléments...
Les sauts de ligne sont là pour structurer l'affichage ci-dessus de la matrice de façon à ce qu'elle ressemble effectivement à un tableau avec des "lignes" et des "colonnes"; mais en réalité, ces sauts ne sont pas à faire figurer.
Cette matrice doit en réalité être écrite ainsi :
matrice = [[0, 1, 2, 3],[4, 5, 6, 7],[8, 9, 10, 11],[12, 13, 14, 15]]
Pour adresser une "ligne" complète d'un tableau, ( donc un des "sous-tableau" ), on utilise la notation déjà vue : un élément est repéré par son index donnant sa position dans la tableau.
>>> matrice[2] # soit : afficher la ligne d'index 2
[8, 9,10,11]
>>> matrice[0]
[0,1,2,3]
Une "case" de la matrice ( donc un élément dans un des "sous-tableau'... ) devra par contre être repérée par deux index : le numéro de la "ligne" et le numéro de la "colonne" où il est stocké.
on donne toujours le numéro de la ligne en premier selon la syntaxe suivante :
nom_de_la_matrice[ligne][colonne]
Exemples :
>>> matrice[2][0] # soit : afficher le 1er élément de la 3ème ligne
8
>>> matrice[3][1] # 2ème élément de la 4ème ligne
13
Pas évident !! Il faudra bien y faire attention...
Impossible à faire directement...Il faudra parcourir la matrice ( cf chapitre suivant ).
Il est égal aux nombres de "sous-tableaux", donc au nombre d'éléments de la matrice :
>>> len(matrice)
4
Il est égal aux nombres d'éléments dans chaque sous-tableau; puisque toutes les "lignes" ont le même nombre de "colonnes", il suffit donc de regarder le nombre d'éléments qu'il y a, par exemple, dans le premier "sous-tableau" :
>>> len(matrice[0])
4
A votre avis ? On fait comment ??
A part la définition complète comme dans l'exemple ci dessus, on peut également créer une matrice par compréhension.
Vous avez déjà vu ça au chapitre sur les tableaux., mais pour une matrice, la construction nécessitera deux boucles : la première pour parcourir les "colonnes", la deuxième pour les "lignes."
Exemples :
Une matrice de 5 lignes et 4 colonnes ne contenant que des 0 :
>>> [[0 for j in range(4)] for i in range(5)] # boucle j = "colonnes" / boucle i = "lignes"
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Pour obtenir dans une matrice les tables de multiplication de 1 à 9 :
>>> [[i*j for j in range(1,10)] for i in range(1,10)]
[[1, 2, 3, 4, 5, 6, 7, 8, 9], [2, 4, 6, 8, 10, 12, 14, 16, 18], [3, 6, 9, 12, 15, 18, 21, 24, 27], [4, 8, 12, 16, 20, 24, 28, 32, 36], [5, 10, 15, 20, 25, 30, 35, 40, 45], [6, 12, 18, 24, 30, 36, 42, 48, 54], [7, 14, 21, 28, 35, 42, 49, 56, 63], [8, 16, 24, 32, 40, 48, 56, 64, 72], [9, 18, 27, 36, 45, 54, 63, 72, 81]]
Méthode très puissante à condition de bien savoir ce que l'on doit écrire !
Il faut donc avoir en tête que dans l'expression :
[[... for j in ...] for i in ...]
Construire, en complétant l'instruction de l'éditeur ci-dessous, puis afficher, les matrices suivantes :
[[3, 8, 7, 2, 1, 6, 4],
[7, 6, 2, 8, 4, 2, 5],
[6, 4, 4, 7, 6, 5, 3],
[6, 7, 5, 4, 7, 4, 9],
[4, 4, 7, 5, 3, 8, 4],
[2, 3, 1, 3, 7, 1, 2],
[3, 5, 3, 8, 7, 6, 2]]
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6]]
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]
[[0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1]]
matrice = [[0,1,2,3],[4,5,6,7],[8,9,10,11],[12,13,14,15]]
Comme les tableaux, les matrices sont des objets mutables : on peut modifier leurs éléments sans avoir à réaffecter commplètement la matrice entière.
Mais par conséquent, on rencontre avec elles les mêmes problèmes que l'on avait évoqués concernant la copie de tableaux, à savoir que modifier une matrice obtenue par "simple" affectation à une variable modifiera aussi la matrice d'origine; il faut donc faire une "vraie" copie d'une matrice pour obtenir un objet "indépendant" de l'original...
Dans l'éditeur ci-dessous :
viser_coins()
qui :
Tester le bon fonctionnement de la fonction avec les matrices ci-dessous :
mat_a = [['X', 'O', 'O', 'O', 'O'],
['O', 'O', 'O', 'O', 'O'],
['O', 'O', 'X', 'O', 'X'],
['X', 'O', 'O', 'O', 'O'],
['X', 'O', 'O', 'O', 'O'],
['O', 'O', 'O', 'O', 'O']]
mat_b = [['O', 'O', 'O', 'O', 'O'],
['O', 'O', 'X', 'O', 'O'],
['O', 'X', 'O', 'O', 'O'],
['O', 'O', 'O', 'O', 'X'],
['O', 'O', 'O', 'O', 'X'],
['X', 'O', 'O', 'O', 'O']]
viser_les_coins()
qui :
Tester votre fonction avec les matrices ci-dessous :
mat_a = [['O', 'O', 'O'],
['O', 'O', 'O'],
['O', 'X', 'O']]
mat_b = [['X', 'O', 'O', 'O'],
['O', 'X', 'O', 'O'],
['O', 'O', 'O', 'O'],
['O', 'O', 'O', 'O'],
['O', 'O', 'O', 'O'],
['O', 'X', 'O', 'O'],
['O', 'O', 'O', 'O']]
mat_c = [['O']]
viser_les_coins_copie()
qui :
Tester votre fonction avec les matrices ci-dessous :
mat_a = [['O', 'O', 'O'],
['O', 'O', 'O'],
['O', 'X', 'O']]
mat_b = [['X', 'O', 'O', 'O'],
['O', 'X', 'O', 'O'],
['O', 'O', 'O', 'O'],
['O', 'O', 'O', 'O'],
['O', 'O', 'O', 'O'],
['O', 'X', 'O', 'O'],
['O', 'O', 'O', 'O']]
mat_c = [['O']]
Une image informatique est constituée d'un ensemble de points élémentaires, appelés pixels; la vision de ces pixels de plus ou moins loin permet à l’œil de reconstituer globalement l'image réelle.
Ces pixels étant organisés en "lignes" et en "colonnes", c'est à dire une matrice ( en anglais bitmap ), on parle d'image matricielle lorsque l'on décrit effectivement l'image pixel par pixel. ( il existe en effet un autre type d'images dites vectorielles, qui ne sont pas décrites point par point mais par des fonctions mathématiques : droites, cercles, courbes,...)
On comprend que ce principe est à l'origine des limitations liées aux images matricielles :
Un compromis est donc généralement à faire entre la finesse de l'image et la taille du fichier résultant; ce compromis dépend de la destination de l'image : affichage en petite ou grande taille, affichage ou impression, envoi ou pas sur un réseau,...
Les fichiers d'images vectorielles sont beaucoup plus "légers", mais par contre, ils ne permettent pas de représenter tous les détails d'une photo par exemple.
Pour coder par exemple un pixel rouge en mode 8 bits, les trois valeurs seront ainsi : (255,0,0); pour un bleu : (0,0,255); un jaune ( mélange de rouge et de vert ) : (255,255,0),etc...
On voit tout de suite le lien avec la notion de matrices pour représenter une image matricielle en mémoire d'un ordinateur :
Toutes ces informations sur les pixels, il va bien falloir les stocker dans un fichier à un moment donné !
De nombreux formats de fichiers image existent, un des plus connus, et le plus utilisé de nos jours, est le format JPEG (Join Photographic Expert Group).
Ce type de fichier est compressé, la compression des données permettant de faire en sorte qu'elles occupent moins d'espace ( les pixels d'une image RVB de définition 800x600 px prendraient plus de 11 Mo d'espace si les données n'étaient pas compressées ! ) .
Vous utiliserez le module Python PIL qui permet ( notamment ) de manipuler les pixels d'une image.
PIL
Ce module permet de faire très simplement de la manipulation d'images matricielles; vous trouverez ici un aperçu des nombreuses possibilités de ce module.
Pour le travail à faire ci-dessous, vous vous contenterez cependant de créer le tableau de tableaux représentant les pixels des images, le reste du code se chargera de créer et d'afficher les images à proprement parler.
Vous allez écrire un unique script qui utilise des matrices pour créer automatiquement des images numériques en niveaux de gris ou en couleurs.
Pour chaque image, vous écrirez une fonction qui construira la matrice des valeurs de pixels, et qui renverra ensuite cette matrice au programme principal.
La matrice sera construite selon une méthode à votre choix : élément par élément, ou par compréhension.
Voila un exemple qui crée une image de 200 x 200 pixels tous noirs :
Commençons pour simplifier par des images en niveaux de gris.
Une image ne contenant que des pixels gris, et dont la largeur, la hauteur et le niveau de gris ( entre 0 et 255 ) sont passées comme arguments à la fonction.
La fonction devra vérifier que le valeur du niveau de gris donnée par l'utilisateur est correcte.
Une image contenant des pixels de valeurs aléatoires ( entre 0 et 255 ), et dont la largeur, la hauteur sont passées comme arguments à la fonction.
Et maintenant, un peu de couleur...Vous pourrez souvent vous inspirer des scripts précédents.
Rappelez-vous que dans ce cas, la couleur d'un pixel est codée non pas par une valeur unique, mais par un tuple de 3 valeurs représentant, dans l'ordre, la composante Rouge, la Verte et la Bleue ( chaque composante variant entre 0 et 255 ).
Chaque "case" des matrices représentant l'image contiendra donc un tuple de 3 valeurs et non pas une valeur unique.
Voila un exemple qui crée une image de 200 x 200 pixels tous rouges :
Une image contenant des pixels ayant chacun une couleur aléatoire, et dont la largeur, la hauteur sont passées comme arguments à la fonction.