Une image au format Portable Bitmap est codée ligne par
ligne en partant du haut. Chaque ligne est codée de gauche à droite. Puisque
chaque ligne comporte au maximum 70
caractères, il peut être
très intéressant de passer à la ligne pour séparer chaque pixel plutôt
qu'utiliser des espaces.
Seul le format .pbm
pourrait en être exempté à condition qu'il
ait une largeur inférieure ou égale à 70
pixels.
L'image ci-contre illustre la lecture linéaire (ou l'écriture) des
pixels dans une image matricielle encodée sous forme de fichier texte.
Pour concevoir ce type d'image, il faut donc écrire les composantes couleurs
de ces pixels depuis le premier pixel situé en haut à gauche.
Les exercices ci-dessous permettent de manipuler le format
Portable Bitmap dans différentes situations.
Certains ont été donnés à des élèves, d'autres sont à destination du
professeur qui cherche à concevoir un exercice pour ses élèves.
Le format .pbm
est un format simple d'image matricielle en noir
et blanc, c'est-à-dire formée d'un rectangle de pixels.
Une image de 10 pixels sur 10 pixels au format .pbm
est tout
simplement un fichier texte contenant 10 lignes, chacune de ces lignes étant
constituée de 10 caractères '0'
et/ou de
'1'
. Dans ce format, chaque
'0'
est interprété par un pixel blanc et
chaque '1'
par un pixel noir.
Voici un exemple de script Python permettant
de concevoir une image au format .pbm
:
##----- Variables et constantes -----##
largeur = 10
hauteur = 10
##----- Ouverture/création des Fichiers -----##
f = open('Lignes_Horizontales.pbm', 'w')
##------ En-tête ------##
f.write('P1 \n') # P1 en ligne 1 pour déclarer le format .pbm puis passage à la ligne
f.write('{} {} \n'.format(largeur, hauteur)) # Largeur et hauteur de l'image, séparées par un espace
##------ Instructions principales ------##
for ligne in range(hauteur):
for colonne in range(largeur):
if ligne%2 == 0: # Lorsque le numéro de ligne est pair
f.write('0')
else:
f.write('1')
f.write('\n')
##----- Fermeture des Fichiers -----##
f.close()
En ouvrant ce fichier avec un logiciel de visualisation d'image, on obtient
l'alternance de lignes horizontales ci-contre (sans les pointillés.
En ouvrant ce fichier avec un éditeur de texte, on obtient l'affichage
ci-dessous :
P1
10 10
0000000000
1111111111
0000000000
1111111111
0000000000
1111111111
0000000000
1111111111
0000000000
1111111111
Concevoir les programmes en Python permettant
d'obtenir les images suivantes, de dimensions 150*150
:
Un carré noir |
Colonnes noires et blanches |
Croix sur fond blanc |
Losange noir évidé |
 |
 |
 |
 |
- Image n°1
- Image n°2
- Image n°3
- Image n°4
Pour le carré noir, tout va bien :
##----- Variables et constantes -----##
largeur = 150
hauteur = 150
##----- Ouverture/création des Fichiers -----##
f = open('Carre_Noir.pbm', 'w')
##------ En-tête ------##
f.write('P1 \n') # P1 en ligne 1 pour déclarer le format .pbm puis passage à la ligne
f.write('{} {} \n'.format(largeur, hauteur)) # Largeur et hauteur de l'image, séparées par un espace
##------ Instructions principales ------##
for ligne in range(hauteur):
for colonne in range(largeur):
f.write('1')
f.write('\n')
##----- Fermeture des Fichiers -----##
f.close()
On reprend l'exemple et on teste la parité sur les numéros de
colonne, avec une petite astuce supplémentaire...
##----- Variables et constantes -----##
largeur = 150
hauteur = 150
##----- Ouverture/création des Fichiers -----##
f = open('Lignes_Verticales.pbm', 'w')
##------ En-tête ------##
f.write('P1 \n') # P1 en ligne 1 pour déclarer le format .pbm puis passage à la ligne
f.write('{} {} \n'.format(largeur, hauteur)) # Largeur et hauteur de l'image, séparées par un espace
##------ Instructions principales ------##
for ligne in range(hauteur):
for colonne in range(largeur):
f.write(str(colonne%2)) # On inscrit 0 lorsque colonne est pair, 1 sinon
f.write('\n')
##----- Fermeture des Fichiers -----##
f.close()
Pour la croix, un peu de mathématiques s'imposent :
- Un pixel est sur la diagonale «descendante» lorsque son numéro
de ligne est égal à son numéro de colonne ;
- Un pixel est sur la diagonale «montante» lorsque son numéro
de ligne ajouté à son numéro de colonne donne la longueur d'un côté du carrée.
##----- Variables et constantes -----##
largeur = 150
hauteur = 150
##----- Ouverture/création des Fichiers -----##
f = open('Croix.pbm', 'w')
##------ En-tête ------##
f.write('P1 \n') # P1 en ligne 1 pour déclarer le format .pbm puis passage à la ligne
f.write('{} {} \n'.format(largeur, hauteur)) # Largeur et hauteur de l'image, séparées par un espace
##------ Instructions principales ------##
for ligne in range(hauteur):
for colonne in range(largeur):
if (ligne == colonne) or (ligne+colonne == hauteur-1):
f.write('1')
else :
f.write('0')
f.write('\n')
##----- Fermeture des Fichiers -----##
f.close()
Pour le losange, ce sont les mêmes mathématiques que pour la croix,
en séparant la première de la deuxième moitié des lignes.
##----- Variables et constantes -----##
largeur = 150
hauteur = 150
demi = hauteur//2
##----- Ouverture/création des Fichiers -----##
f = open('Losange_Vide.pbm', 'w')
##------ En-tête ------##
f.write('P1 \n') # P1 en ligne 1 pour déclarer le format .pbm puis passage à la ligne
f.write('{} {} \n'.format(largeur, hauteur)) # Largeur et hauteur de l'image, séparées par un espace
##------ Instructions principales ------##
for ligne in range(demi): # Parcours de la 1ère moitié des lignes
for colonne in range(largeur):
if (colonne+ligne == demi-1) or (colonne-ligne == demi-1): # 2ème diagonale du demi-carré et 1ère diagonale
f.write('1 \n')
else :
f.write('0 \n')
for ligne in range(demi): # Parcours de la 2nde moitié des lignes
for colonne in range(largeur):
if (colonne == ligne) or (colonne+ligne == largeur-1): # 1ère diagonale et 2nde diagonale
f.write('1 \n')
else :
f.write('0 \n')
##----- Fermeture des Fichiers -----##
f.close()
L'image originale du logo Python a été
téléchargée sur internet (sans vérifier sa licence d'utilisation,
ce qui n'est pas bien !). Elle est en couleur, au format
.png
et téléchargeable en cliquant sur l'image ci-contre.
Afin de ne pas rajouter de contrainte inutile aux élèves, on peut leur
proposer l'image donnée à l'exercice précédent. Sinon, voici une procédure
possible :
- Ouvrir l'image avec
IrfanView
(par exemple) et
convertir cette image au format .pgm
en sélectionnant
Ascii encoding
.
- Ouvrir l'image avec un éditeur de texte pour vérifier son en-tête
(P2, dimensions, valeur max correspondant au blanc) et
éventuellement supprimer les commentaires.
- Concevoir un programme qui lit ce fichier puis crée un nouveau fichier où
un seul pixel (codé par son intensité en niveau de gris) est
écrit par ligne.
##----- Ouverture des Fichiers -----##
fsource = open('Logo_Python_Original.pgm', 'r') # Ouverture (lecture) du fichier
fresult = open('Logo_Python_Modifie.pgm', 'w') # Ouverture (création) du fichier
##------ En-tête ------##
for i in range(3): # Les 3 lignes d'en-tête à recopier :
fresult.write(fsource.readline()) # P2, dimensions, valeur max correspondant au blanc
##------ Instructions principales ------##
for element in fsource: # Chaque ligne est une chaîne de caractères...
valeurs = element.split() # ...transformée en liste de nombres au format "str"
for valeur in valeurs:
fresult.write(valeur+'\n')
##----- Fermeture des Fichiers -----##
fsource.close()
fresult.close()
Cliquer sur l'image ci-contre pour télécharger le logo
Python au format .ppm
. Le
fichier a été modifié pour que, sur chaque ligne, se trouve une unique
composante couleur d'un pixel.
Concevoir un programme permettant d'obtenir l'image affichée ci-contre.
Pour cela, le script doit permuter les composantes de chaque pixel.
##----- Ouverture des Fichiers -----##
fsource = open('Logo_Python_Compo.ppm', 'r') # Ouverture (lecture) du fichier
fresult = open('Logo_Python_Inverse.ppm', 'w') # Ouverture (création) du fichier
##------ En-tête ------##
for i in range(3): # Les 3 lignes d'en-tête à recopier :
fresult.write(fsource.readline()) # P2, dimensions, valeur max correspondant au blanc
##------ Instructions principales ------##
temp = [] # Stockage temporaire des composantes
for element in fsource: # Chaque ligne est une chaîne de caractères
if len(temp) == 3:
r, v, b = temp[0], temp[1], temp[2]
fresult.write('{0} {1} {2} \n'.format(b, r, v))
temp = [element]
else:
temp.append(element)
##----- Fermeture des Fichiers -----##
fsource.close()
fresult.close()