Connexion élèves

Choisir le(s) module(s) à installer :

Des tableaux... de tableaux !

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 :

Structure d'une matrice

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]]
				

Adressage dans une matrice

Adressage d'une "ligne" entière :

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]
					

Adressage d'une "case"

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...

Adressage d'une "colonne"

Impossible à faire directement...Il faudra parcourir la matrice ( cf chapitre suivant ).

Nombre d'éléments dans une matrice

Nombre de "lignes"

Il est égal aux nombres de "sous-tableaux", donc au nombre d'éléments de la matrice :


>>> len(matrice)

4
			

Nombre de "colonnes"

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
			

Nombre de "cases"

A votre avis ? On fait comment ??

Construction d'une matrice

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 ...]
				
  • l'indice j (situé à l'intérieur des lignes) représente l'indice de colonne,
  • l'indice i (situé à l'extérieur des lignes) représente l'indice de ligne.

QCM d'entraînement

Exercices

Construction de matrices

Construire, en complétant l'instruction de l'éditeur ci-dessous, puis afficher, les matrices suivantes :

  1. une matrice de 5 lignes et 10 colonnes dont chaque élément est un entier aléatoire entre 0 et 100
  2. une matrice mat2 d'entiers aléatoires comme celle-ci :
    
    [[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]]				
     					
  3. la matrice mat3 ci-dessous :
    
    [[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]] 				
     					
  4. la matrice mat4 suivante :
    			
    [[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]]
     					
  5. une matrice mat5 identique à celle ci-dessous :
    				
    [[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]]				
    					
  6. la matrice mat6 donnée en introduction :
    
    matrice = [[0,1,2,3],[4,5,6,7],[8,9,10,11],[12,13,14,15]]
    			
#Modèle : matX = [[..... for j in range(...)] for i in range (....)]

Lien vers les RÉPONSES

Mutation/Copie de matrices

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 :

  1. Compléter la fonction viser_coins() qui :
    • prend en paramètre une matrice de caractères 'X' ou 'O' comprenant 6 lignes et 5 colonnes,
    • la mute afin que les valeurs présentes dans les coins soient égales à 'X' (sans modifier les autres valeurs).

    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']]
    					
  2. Compléter la fonction viser_les_coins() qui :
    • prend en paramètre une matrice de caractères 'X' ou 'O' désormais de taille quelconque (mais non vide),
    • la mute afin que les valeurs présentes dans les coins soient égales à 'X' (sans modifier les autres valeurs).

    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']]
    					
  3. Compléter la fonction viser_les_coins_copie() qui :
    • prend en paramètre une matrice mat de caractères 'X' ou 'O' désormais de taille quelconque (mais non vide),
    • renvoie une nouvelle matrice, copie de mat avec les valeurs présentes dans les coins égales à 'X' (sans modifier les autres valeurs).

    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']]
    					
import copy def viser_coins(mat: list[list])->list[list]: """Matrice de 6 lignes et 5 colonnes""" mat[...][...] = ... mat[...][...] = ... mat[...][...] = ... mat[...][...] = ... def viser_les_coins(mat: list[list])->list[list]: """Matrice de taille quelconque""" nb_l = len(...) #<--- nombre de lignes nb_c = len(...) #<--- nombre de colonnes mat[...][...] = ... mat[...][...] = ... mat[...][...] = ... mat[...][...] = ... def viser_les_coins_copie(mat: list[list])->list[list]: """Copie de matrice de taille quelconque""" copie = copy.deepcopy(...) nb_l = len(...) #<--- nombre de lignes nb_c = len(...) #<--- nombre de colonnes ...[...][...] = ... ...[...][...] = ... ...[...][...] = ... ...[...][...] = ... return ...

Lien vers les RÉPONSES

Images matricielles

Qu'est-ce qu'une image matricielle ?

Images matricielles et vectorielles

Mario

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 :

  • pour être la plus fidèle à la réalité, une image doit donc être composée de pixels les plus petits possibles : ils ne doivent même pas être discernables par l’œil. Si c'est le cas, on dit que l'image est pixelisée.
  • plus les pixels sont petits, plus leur nombre sera donc grand pour coder une image donnée; le fichier qui stockera les informations relatives à l'image sera donc d'autant plus gros. Selon la situation ( taille du support de stockage, lourdeur de l'envoi de l'image sur un réseau, ....), cette taille peut poser problème.

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.

Image pixelisée
Image pixelisée
Image non pixelisée
Image non pixelisée

Quelques notions à savoir sur les images matricielles

Représentation d'une image matricielle en mémoire

On voit tout de suite le lien avec la notion de matrices pour représenter une image matricielle en mémoire d'un ordinateur :

Coordonnées d'un pixel
  • chaque pixel correspondra à une "case" d'une matrice, qui aura autant de "lignes" et de "colonnes" que nécessite la définition de l'image; les coordonnées de chaque pixel dans l'image correspondront aux index de l'élément correspondant dans la matrices ( [ligne][colonne] ).
  • l'information stockée dans chaque "case" correspondra à la valeur du pixel : un nombre codant la luminosité pour les images monochrome ou en niveaux de gris, et un ensemble de trois valeurs ( un tuple par exemple...) codant les 3 couleurs primaires dans le cas d'une image en couleur.

Les formats d'image

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.

Le module 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.

Travail à faire

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.

Les différentes fonctions seront appelées à partir du programme principal, dans lequel vous écrirez également les instructions pour enregistrer les fichiers images correspondant.

Voila un exemple qui crée une image de 200 x 200 pixels tous noirs :

from PIL import Image def image_noire()->list: ''' Fonction qui créé une matrice correspondant à une image de 200 x 200 pixels noirs Entrée : Aucune Sortie : la matrice des 200 x 200 valeurs des pixels ''' m = [[0 for col in range(200)] for lin in range(200)] return m data = image_noire() ## Construction de l'image img = Image.new("L" ,(len(data[0]), len(data))) data = [pixel for ligne in data for pixel in ligne] img.putdata(data) img.show(img)

Du noir et du blanc

Commençons pour simplifier par des images en niveaux de gris.

Une image grise

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 aléatoire

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.

Le quadrillage
  1. une image de 200 x 200 pixels avec 10 "lignes" verticales
  2. même chose avec 10 "lignes" horizontales
Le dégradé de gris
  1. Une image de 256 x 256 pixels montrant un dégradé de gris horizontal, depuis la valeur 0 à gauche jusqu'à la valeur 255 à droite
  2. Modifier la fonction précédente pour que le dégradé soit vertical
Image grise
Image grise
Image aléatoire
Image aléatoire
Lignes verticales
Lignes verticales
Lignes horizontales
Lignes horizontales
Dégradé de gris
Dégradé
from PIL import Image def image_grise(l: int, h: int, niv: int)->list: pass def image_aleatoire(l: int, h: int)->list: pass def lignes_verticales()->list: pass def lignes_horizontales()->list: pass def degrade_gris_horizontal()->list: pass def degrade_gris_vertical()->list: pass ## Construction de l'image img = Image.new("L" ,(len(data[0]), len(data))) data = [pixel for ligne in data for pixel in ligne] img.putdata(data) img.show()

Lien vers les RÉPONSES

De la couleur

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 :

from PIL import Image def image_rouge()->list: ''' Fonction qui créé une matrice correspondant à une image de 200 x 200 pixels rouges Entrée : Aucune Sortie : la matrice des 200 x 200 valeurs des pixels ''' m = [[(255,0,0) for col in range(200)] for lin in range(200)] return m data = image_rouge() ## Construction de l'image img = Image.new("RGB" ,(len(data[0]), len(data))) data = [pixel for ligne in data for pixel in ligne] img.putdata(data) img.show()
Une image colorée aléatoire

Une image contenant des pixels ayant chacun une couleur aléatoire, et dont la largeur, la hauteur sont passées comme arguments à la fonction.

Le dégradé coloré
  1. Une image de 256 x 256 pixels montrant un dégradé progressif entre deux couleurs quelconques.
  2. Modifier la fonction pour que le dégradé soit vertical
Drapeaux
  1. le drapeau français en 256x200 pixels; arriverez-vous à construire la matrice correspondante par compréhension 😎 ?
  2. le drapeau italien en 256x200 pixels.
Couleurs aléatoires
Couleurs aléatoires
Dégradé coloré
Dégradé coloré
Drapeau français
Drapeau français
Drapeau italien
Drapeau italien
from PIL import Image def image_aleatoire(l: int, h: int)->list: pass def degrade_vertical()->list: pass def degrade_horizontal()->list: pass def drapeau_francais()->list: pass def drapeau_italien()->list: pass ## Construction de l'image img = Image.new("RGB" ,(len(data[0]), len(data))) data = [pixel for ligne in data for pixel in ligne] img.putdata(data) img.show()

Lien vers les RÉPONSES