Semaine 8 : fichiers et flux de données#

À faire

2025-2026

Renforcer les tests automatiques, et les faire vérifier par nbgrader. Pour cela, il faudra:

  • automatiser la compilation et l’exécution des programmes.

  • enregistrer sous git les images générées à la main.

Éviter la dépendance en imagemagick, en utilisant par exemple netpbmfile pour lire et écrire des images pbm.

Cours#

TD : fichiers et images numériques#

TP#

Notes aux enseignants

Les élèves ont tendance à confondre les différents «types» de fichiers et leur utilisation (ils essaient par exemple d’ouvrir un exécutable avec un éditeur de texte, ou d’exécuter une image). Ne pas hésiter à leur expliquer / rappeler en début de TP (ou mieux, en fin de TD) qu’on ne peut pas faire n’importe quoi avec n’importe quel fichier : les fichiers .txt s’ouvrent avec un éditeur de texte, les fichiers .mp3 s’ouvrent avec un lecteur de musique, etc. Les fichiers .pbm ont la particularité de pouvoir être ouverts à la fois avec un éditeur de texte et avec une visionneuse d’images. On va utiliser cette particularité dans l’exercice 2 du TP pour écrire une image .pbm grâce à un éditeur de texte puis la lire grâce à une visionneuse d’images.

2024-2025: cela pourrait être particulièrement vrai cette année du fait que les étudiants auront eu une semaine de moins de compilation de fichiers.

Exercice 0 : Jupyter : l’indentation#

À faire chez vous!

Exercice 1 : écrire dans un fichier - 20 minutes#

Indication

Rappels

  • Pour exécuter un programme, il faut d’abord avoir compilé le fichier .cpp avec la commande clang++ puis lancer l’exécutable créé par la compilation. Consultez au besoin les instructions des TP précédents.

  • Pensez à organiser votre espace de travail et à ajuster la taille des caractères (avec Ctrl-+ et Ctrl--) pour avoir une vue confortable simultanée de tous les éléments requis : consignes, code, terminal.

    https://nicolas.thiery.name/Enseignement/Info111/media/screencast-espace-de-travail-compilation.gif

    Fig. 2 Vidéo: organiser son espace de travail avec JupyterLab#

  1. Consultez le fichier fichier-ecriture.cpp.

  2. Essayez de deviner ce que fait ce programme.

  3. Exécutez ce programme.

  4. Trouvez le fichier .txt qui a été créé à l’exécution et l’ouvrir avec un éditeur de texte. Que contient-il?

  5. Copiez le contenu de fichier-ecriture.cpp dans fichier-ecriture-v2.cpp, et adaptez le pour qu’à l’exécution il écrive un fichier nommé essai.txt contenant le texte «17 fois 23 vaut» suivi de la valeur de ce nombre (valeur que le programme calcule par lui-même).

  6. Exécutez ce programme puis ouvrez le fichier essai.txt afin de vérifier son contenu.

  7. Lisez et essayez à nouveau dans le cours l’exemple de lecture depuis le clavier avec cin.

  8. En vous en inspirant, complétez le programme cin.cpp pour que, à l’exécution, il demande à l’utilisateur d’entrer deux entiers, puis qu’il écrive un fichier nommé multiplication.txt contenant un texte similaire au fichier essai.txt de la question précédente, avec 17 et 23 remplacés respectivement par les deux entiers choisis par l’utilisateur.

    Note

    Pour faire des affichages à l’écran et pour faire saisir des données depuis le clavier, il faut inclure la bibliothèque iostream en écrivant #include <iostream>. Pour lire et écrire dans un fichier de texte, il faut inclure la bibliothèque fstream en écrivant #include <fstream>

  9. Exécutez le programme puis ouvrez le fichier multiplication.txt pour vérifier son contenu.

Notes aux enseignants

Les élèves ont tendance à oublier d’inclure la bibliothèque iostream et/ou la bibliothèque fstream.

Certains ne mettent pas de message pour demander à l’utilisateur d’entrer des entiers, et s’étonnent que quand ils lancent le programme «il ne se passe rien». En fait le programme attend qu’ils entrent des entiers et eux ne le font pas… Profitez en pour leur dire/rappeler que quand on utilise cin, il faut toujours mettre juste avant un message pour l’utilisateur lui indiquant ce qu’il doit entrer.

Certains ont du mal à comprendre la différence entre cout << et fichier <<.

Exercice 2 : ouvrir le smiley - 5 minutes#

Le fichier media/smiley.pbm (dans le répertoire media donc!) contient l’image en noir et blanc du TD.

  1. Ouvrez ce fichier d’abord avec un éditeur de texte. Depuis le navigateur de fichiers de JupyterLab: clic droit -> Ouvrir avec -> Éditeur.

  2. Ouvrez ce même fichier avec un logiciel de vision d’images. Pour cela, depuis Jupyter, vous pouvez utiliser la cellule ci-dessous :

from open_ppm import open_ppm                # importe la commande open_ppm fournie
im = open_ppm("media/smiley.pbm")            # charge l'image
im.resize((256*im.width // im.height, 256))  # affiche l'image avec une taille donnée

Alternativement :

  • Sous Windows, vous pouvez utiliser l’application irfanview.

  • Sous Linux, vous pouvez utiliser l’application xviewer : utilisez «Control +» et «Control -» pour zoomer / dezoomer. Vous remarquerez que l’image est lissée / floutée. Allez dans le menu «Edit -> Preferences» et décochez «Smooth images when zoomed in» pour mieux voir les pixels.

Exercice 3 : petit damier - 15 minutes#

  1. Utilisez un éditeur de texte pour écrire à la main un fichier texte damier.pbm contenant une image au format PBM (Portable Bit Map) de taille 10×10 représentant un damier :

    un damier
  2. Visualisez le résultat, par exemple avec :

from open_ppm import open_ppm
im = open_ppm("damier.pbm")
im.resize((256*im.width // im.height, 256))

Tests automatiques :

assert im.size == (10, 10), "l'image n'est pas de taille 10×10"
import numpy as np
M = np.array(im, dtype=int)
assert np.all(M[1:,:] + M[:-1,:] == 1), "l'image n'est pas un damier horizontalement"
assert np.all(M[:,1:] + M[:,:-1] == 1), "l'image n'est pas un damier verticalement"

Exercice 4 : grand damier#

Complétez le programme C++ damier.cpp qui, lorsqu’on l’exécute, écrit un fichier image damier-automatique.pbm comme le précédent, mais cette fois pour un damier 100x100.

Visualisez et vérifiez au fur et à mesure le résultat avec :

im = open_ppm("damier-automatique.pbm")
im.resize((512*im.width // im.height, 512))

Indication

Indications

  • Vous pouvez vous inspirer de fichier-ecriture.cpp.

  • Commencez par un programme pour un damier 10×10 avant de passer à 100×100.

  • L’exécution de votre programme devrait être instantanée ou presque. Si ce n’est pas le cas, interrompez le avec Ctrl-C.

  • Vérifiez la taille du fichier produit en tapant la commande suivante dans le terminal :

    ls -lh *.pbm
    

    Il devrait faire dans les 20 kilo octets (20K). Veillez à le supprimer s’il fait nettement plus que cela.

  • Si le fichier produit ne donne pas l’image attendue, ou si vous n’arrivez pas à le visualiser: reportez vous aux indications de l’exercice précédent.

  • Si cela vous aide, vous pouvez aussi dans un premier temps afficher le texte produit à l’écran avec cout; puis écrivez le dans le fichier dans un deuxième temps.

Notes aux enseignants

En cas de difficulté de débogage, vérifier que le fichier ouvert par l’élève est bien celui écrit par le programme! Autre erreur classique: mettre 11 (resp. 101) caractères par ligne au lieu de 10 (resp. 100), ce qui décale tout et ne donne pas tout à fait l’image attendue.

Certains élèves ont un premier réflexe de vouloir utiliser un tableau (vector), dans ce cas leur dire qu’il n’y a aucun besoin de vector dans ce TP (pourquoi prendre un marteau piqueur pour écraser une mouche ? Et pourquoi encombrer la mémoire du programme au lieu d’écrire directement dans le fichier ?). Si vraiment ils y arrivent avec un vector alors qu’ils n’y arrivent pas sans, à la limite pourquoi pas, mais c’est peu probable : en général les élèves qui n’y arrivent pas sans vector n’y arrivent pas plus avec, et inversement ceux qui y arrivent avec sont aussi capables de faire sans si on leur demande.

Tests automatiques :

assert im.size == (100, 100), "l'image produite n'est pas de taille 100×100"
import numpy as np
M = np.array(im, dtype=int)
assert np.all(M[1:,:] + M[:-1,:] == 1), "l'image n'est pas un damier horizontalement"
assert np.all(M[:,1:] + M[:,:-1] == 1), "l'image n'est pas un damier verticalement"

Exercice 5 : dégradé de gris#

Pour créer une image avec des nuances de gris, il faut enregistrer un fichier de texte au format PGM (Portable Gray Map). Ce fichier doit contenir des nombres entre 0 et une valeur maximale (au choix) qui représenteront les différentes nuances de gris.

  1. Écrivez à la main, dans un éditeur de texte, un fichier essai.pgm représentant une image de taille 10×10 contenant différentes nuances de gris de votre choix. Vérifiez au fur et à mesure le résultat avec :

im = open_ppm("essai.pgm")
im.resize((256*im.width // im.height, 256))

Tests automatiques :

assert im.size == (10, 10), "l'image produite n'est pas de taille 10×10"
  1. Implantez un programme degrade.cpp qui écrit un fichier contenant une image degrade.pgm de taille 128x128, avec un dégradé de gris comme celui-ci :

    un dégradé de gris
im = open_ppm("degrade.pgm")
im.resize((256*im.width // im.height, 256))

Tests automatiques :

assert im.size == (128, 128), "l'image produite n'est pas de taille 128×128"
  1. Répétez, avec une image de taille 100×100

Exercice 6 : inversion d’image#

Implantez un programme videoInverse.cpp qui lit un fichier contenant une image au format PGM (par exemple le fichier media/image.pgm fourni), et écrit un fichier contenant la même image en vidéo inverse (clair remplacé par sombre et réciproquement).

Indication

Indication Implantez une fonction

    /** Image en vidéo inverse
     * @param image1: le nom du fichier contenant l'image à lire
     * @param image2: le nom du fichier pour l'image à écrire
     **/
    void videoInverse(string image1, string image2);

Exercice 7 : dégradé circulaire de rouge ♣#

  1. Implantez un programme degrade-circulaire.cpp qui écrit un fichier contenant une image degrade-circulaire.ppm au format PPM de taille 255×255, avec un dégradé circulaire de rouge:

    un dégradé circulaire
  2. Répétez, avec une image de taille 100×100.