{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# ❖ Manipulation d'images avec Python\n", "\n", "## ♦ Le module PIL\n", "\n", "Python est un langage *modulaire* c'est à dire qu'on peut ajouter des fonctionnalités en fonction des besoins. Le module qui gère la manipulation des images s'appelle **PIL** pour Python Imaging Library. Le module PIL est lui-même constitué de plusieurs modules, nous nous intéressons ici simplement au module Image qui permet de faire les manipulations de base sur les images.\n", "\n", "On commence donc par importer le module Image de la librairie PIL :" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from PIL import Image" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ♦ Ouvrir et visualiser une image\n", "\n", "Nous savons déjà qu'une variable en Python peut représenter une chaine de caractère comme dans `theme = \"La photo numérique\"` ou encore un entier comme dans `ma_note=16`. De la même façon, une image sera représentée par une variable et pour créer cette variable nous utilisons l'opérateur d'affectation `=`.\n", "\n", "Pour le moment, notre variable sera crée à partir d'une photo déjà existante et présente sur l'ordinateur, il s'agit d'une photographie du mont Fuji au Japon qui *se situe dans le même dossier* que ce Notebook sous le nom de fichier 'Mont_Fuji.jpg'" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Crée une variable de type \"Image\" à partir du fichier \"Mont_Fuji.jpg\"\n", "fuji = Image.open('Mont_Fuji.jpg')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'instruction que nous avons utilisé est donc `open` mais elle vient du module `Image` son nom \"complet\" est donc `Image.open`. Et sa syntaxe est la suivante :\n", "\n", "``` = Image.open() ```\n", "\n", "Maintenant que nous avons crée notre image, nous pouvons bien sur la voir grâce à `show()`.\n", "\n", "**Exécuter** la cellule suivante pour faire apparaître l'image :" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "fuji.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "⚡ A retenir :
    \n", "
  • Pour manipuler des images avec Python on doit d'abord importer le module Image : from PIL import Image\n", "
  • On crée une variable représentant l'image grâce à <var_img> = Image.open(<nom_fichier>) \n", "
  • On affiche l'image contenu dans <var_img> grâce à <var_img>.show()\n", "
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ✎ Exercices \n", "1. Une image du Kilimandjaro au Kenya se trouve aussi dans le même répertoir que ce Notebook. Le nom du fichier est \"kilimanjaro.jpg\". Utiliser Python pour visualiser cette image (elle sera stockée dans une variable nommée kili) \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Taper votre programme ci-dessous :\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. Rendez-vous sur la page Wikipedia de l'Aconcagua, point culminant de la cordillières des andes en amérique du sud. Vérifier que la photographie d'illustration figurant en haut et à droite de la page est **libre de droit**. Compléter ci-dessous le nom de la licence d'utilisation de cette photographie ainsi que les droits qui s'y rattachent." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cette photographie est disponible sous licence ........ ......... ce qui signifie que nous sommes libres de la ........ et de l'........" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "3. Télécharger cette photo au **format 1024 pixels**. Et l'enregistrer sous le nom **\"Aconcagua.jpg\"** dans **le même répertoire** que ce Notebook. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "4. Utiliser Python pour afficher cette photo. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Taper votre programme ci-dessous :\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ♦ Récupérer des informations sur l'image\n", "\n", "Le module Image de **PIL** permet aussi d'afficher des informations sur l'image. \n", "\n", "### Dimensions de l'image\n", "La largeur (*width* en anglais) de l'image contenu dans `` s'obient par exemple avec `.width` et sa hauteur (*height* en anglais) avec `.height`. Ces deux informations sont données en nombre de pixels.\n", "\n", "Par exemple, pour l'image du mon Fuji ci-dessus :" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "L'image du mont Fuji a pour dimensions : 640 x 426 pixels\n" ] } ], "source": [ "fuji_larg = fuji.width\n", "fuji_haut = fuji.height\n", "print(\"L'image du mont Fuji a pour dimensions : \",fuji_larg,\"x\",fuji_haut,\" pixels\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Type du fichier et mode de l'image\n", "\n", "Le format de fichier de l'image se récupère grâce à `.format` :" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "JPEG\n" ] } ], "source": [ "print(fuji.format)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Et le mode couleur grâce à `.mode`" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "RGB\n" ] } ], "source": [ "print(fuji.mode)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sans surprise, on constate que l'image du mont fuji est en couleur (mode RGB) et dans le format de fichier JPEG. Cette même image en niveaux de gris figure aussi dans le même répertoire que ce notebook sous le nom de fichier \"Mont_Fuji_Gris.jpg\".
\n", "Nous allons ouvrir cette nouvelle image, l'afficher et afficher son mode couleur :" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "L\n" ] } ], "source": [ "fuji_gris = Image.open(\"Mont_Fuji_Gris.jpg\")\n", "fuji_gris.show()\n", "print(fuji_gris.mode)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cette fois, Python indique \"L\" pour une indiquer une image en 255 niveaux de gris. Consulter cette page, pour découvrir l'ensemble des formats possibles." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "⚡ A retenir :
    \n", "
  • <var_img>.width donne la largeur de l'image\n", "
  • <var_img>.height donne la hauteur de l'image\n", "
  • <var_img>.mode donne le mode de l'image (RGB pour une image couleur, L pour une image en niveau de gris) \n", "
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ✎ Exercices \n", "5. Afficher grâce à Python, les dimensions de l'image du Kilimandjaro ouverte plus tôt dans ce Notebook sous le nomb kili" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Affichage des dimensions de l'image du mont Kilimanjaro\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "6. L'**Everest** est célèbre comme plus haute montage du monde, on sait moins qu'il doit son nom à Georges Everest, visiter sa page wikipedia. Son portrait figure en haut et à droite de la page. Vérifier que cette image est **libre de droit**. Expliquer pourquoi en complétant la phrase ci-dessous." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cette image est libre de droit car elle se trouve dans le ....... ......" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "7. Télécharger cette image à sa résolution maximale dans le même répertoire que ce Notebook. Utiliser Python pour afficher le mode de cette image. Cette image est-elle en niveau de gris ou en couleur ? ce résultat est-il surprenant, pourquoi ? " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Affichage du \"mode\" du portrait de Georges Everest trouvé sur wikipedia\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ♦ Manipuler une image\n", "\n", "### Redimensionner une image\n", "\n", "Pour redimensionner une image, on utilise `resize` :" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "petit_fuji = fuji.resize((100,100))\n", "petit_fuji.show()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "grand_fuji = fuji.resize((1000,1000))\n", "grand_fuji.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La syntaxe est donc la suivante :
\n", "` = .resize(())`\n", "\n", "⚠ Attention : il y a des doubles parenthèses autour des dimensions.\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remarquons qu'on doit préciser à chaque fois la largeur et la hauteur et que par conséquent, le redimensionnement ne préserve pas nécessairement le rapport longueur sur largeur de l'image. A titre d'exemple, on peut afficher une image du mont Fuji beaucoup plus large que haut ou inversement ..." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "larg_fuji = fuji.resize((500,100))\n", "larg_fuji.show()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "haut_fuji = fuji.resize((100,500))\n", "haut_fuji.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Faire tourner une image\n", "\n", "Pour faire tourner une image on utilise `rotate` en donnant l'angle de rotation en degrés entre parenthèses.On peut par exemple retourner l'image du mont fuji \"tête en bas\" avec un angle de 180° :" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "fuji_inverse = fuji.rotate(60)\n", "fuji_inverse.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Convertir le mode d'une image\n", "\n", "Nous avons vu plus haut que pour Python, une image en niveaux de gris est dans le mode \"L\". Pour convertir une image en niveaux de gris nous utiliserons donc convert en donnant en argument \"L\" :" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "fuji_gris = fuji.convert('L')\n", "fuji_gris.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Peut-être avez-vous été assez curieux pour consulter la page listant les divers modes couleurs des images en Python. Dans ce cas, vous y avez vu que le mode \"1\", correspond à une image en noir et blanc. Ce que l'on peut tester :" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "fuji_nb = fuji.convert('1')\n", "fuji_nb.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Sauvegarder une image\n", "Pour sauvegarder une image sur l'ordinateur, on utilise `save`. Par exemple pour garder une copie de l'image du mont Fuji en noir et blanc sous le nom de fichier \"Mont_Fuji_NB.jpg\", on utilisera :" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fuji_nb.save(\"Mont_Fuji_NB.jpg\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "⚡ A retenir : manipulation simple d'images en Python:
    \n", "
  • <var_img>.resize((<n_larg>,<n_haut>)) redimensionne l'image avec comme nouvelle largeur n_larg et hauteur n_haut. Les proportions ne sont pas forcément respectées. Attention aux doubles parenthèses !\n", "
  • <var_img>.rotate(<angle>) : fait tourner une image de l'angle <angle>. La mesure de l'angle est en degrés\n", "
  • <var_img>.convert(<mode>) : convertit l'image dans un nouveau mode de couleurs. Les trois modes classiques sont \"RGB\" (couleur), \"L\" (niveaux de gris) et \"1\" (noir et blanc). Attention, le mode est une chaîne de caractères et doit donc être entre guillemets.\n", "
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ✎ Exercices \n", "\n", "8. Redimensionner l'image du Mont Fuji pour avoir une taille de 256 pixels en largeur et en hauteur. Mettre cette image en niveau de gris. Faites afficher cette nouvelle image et la sauvegarder sous le nom 'Mont_Fuji_vignette.jpg'. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Programme pour obtenir une image 256x256 en niveau de gris du Mont Fuji\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "9. L'image de George Everest téléchargée plus haut était en mode RGB, la convertir en mode niveau de gris. Afficher les deux images, une différence est-elle visible ? Sauvegarder le portrait en niveau de gris et comparer la taille des fichiers de ces deux images." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Conversion portrait Everest en niveaux de gris, comparaison des tailles de fichiers\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ♦ Modifier les pixels d'une image\n", "\n", "### Lecture des informations d'un pixel \n", "\n", "Comme nous l'avons déjà vu, une image est vue comme un tableu de pixel, on peut lire les informations de couleur d'un pixel d'un point grâce à getpixel(ligne,colonne). Les lignes et les colonnes étant numérotées à partir de 0, les informations de couleur du tout premier pixel situé en haut et à gauche de l'image du mont Fuji s'obtiennent avec" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(156, 191, 211)\n" ] } ], "source": [ "premier_pixel = fuji.getpixel((0,0))\n", "print(premier_pixel)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remarquez que comme cette image est au format \"RGB\", Python retourn un triplet de valeurs entre parenthèses. Ici par exemple la valeur du rouge est 156, celle du vert 191 et celle du bleu 211.
\n", "Notez que ces valeurs peuvent s'obtenir à partir du triplet en écrivant : `premier_pixel[0]` pour la composante rouge, `premier_pixel[1]` pour la composante vert et `premier_pixel[2]` pour la composante bleue, en effet les valeurs du triplet sont numérotées à partir de 0." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Composante rouge : 156\n", "Composante verte : 191\n", "Composante bleue : 211\n" ] } ], "source": [ "print(\"Composante rouge : \",premier_pixel[0])\n", "print(\"Composante verte : \",premier_pixel[1])\n", "print(\"Composante bleue : \",premier_pixel[2])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si l'image est au format \"L\", c'est à dire niveaux de gris, c'est une seule valeur qui est donnée." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "182\n" ] } ], "source": [ "pixel_gris = fuji_gris.getpixel((0,0))\n", "print(pixel_gris)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Modification d'un pixel \n", "\n", "Pour modifier un pixel, on utilise putpixel en indiquant comme argument la ligne et la colonne du pixel puis le nouveau triplet de couleur (pour une image RGB) ou le nouveau niveau de gris (pour une image L).\n", "Par exemple :" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "fuji.putpixel((0,0),(255,0,0))\n", "fuji.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous venons de remplacer le pixel situé aux coordonnées (0,0) par un pixel rouge c'est à dire (255,0,0) en mode. Bien évidemment, la modification est à peine visible, un seul pixel a été modifié !\n", "\n", "---\n", "Rien ne nous empêche d'effectuer cette même modification pour tous les pixels de la première ligne à l'aide d'une boule `for` :" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "# la colonne varie entre 0 et la largeur de l'image (cette largeur s'obtient grâce à fuji.width)\n", "for col in range(fuji.width):\n", " fuji.putpixel((col,0),(255,0,0))\n", "fuji.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On voit clairement apparaître la ligne rouge tout en haut de l'image mais elle ne fait pour le moment qu'un pixel d'épaisseur. Modifions aussi les pixels de la deuxième et troisième ligne :" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "for col in range(fuji.width):\n", " fuji.putpixel((col,0),(0,255,0))\n", " fuji.putpixel((col,1),(0,255,0))\n", " fuji.putpixel((col,2),(0,255,0))\n", "fuji.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Et voilà ... nous avons une bordure rouge de 3 pixels d'épaisseur en haut de notre image !\n", "Remarquon que nous pouvions utiliser une seconde boucle pour la ligne et aller jusqu'à par exemple 5 pixels d'épaisseur :" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for col in range(fuji.width):\n", " for lig in range(6):\n", " fuji.putpixel((col,lig),(255,0,0))\n", "fuji.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lorsqu'on utilise deux boucle l'une dans l'autre comme ci-dessus, on dit qu'elles sont **imbriquées**.
\n", "Observer bien les décalages successifs, chacune des deux boucles devant être absolument **indentées**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "⚡ A retenir : pixel d'une image
    \n", "
  • <var_img>.getpixel((<col>,<lig>)) permet de récupérer les informations de couleur du pixel situé en colonne col et en ligne lig\n", "
  • <var_img>.putpixel((<col>,<lig>),(<couleur>)) permet d'affecter la nouvelle couleur <couleur> au pixel situé en colonne col et en ligne lig. La couleur est un triplet de valeur pour une image en mode RGB et une seule valeur pour une image en niveaux de gris.\n", "
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ✎ Exercices \n", "\n", "10. Vérifier que pour l'image du Mont Fuji, le pixel situé colonne 7 et ligne 42 a pour couleur (163,195,216). " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Taper votre programme ici\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "11. En vous inspirant de ce qui précède, écrire un programme Python permettant de créer une bordure de 4 pixels d'épaisseur autour d'une image RGB (couleur de votre choix)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Programme bordure de 4 pixels à taper ici :\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ♦ Créer une image\n", "\n", "Le module Image permet aussi de créer une image, la syntaxe est alors la suivante :
\n", "`=Image.create(,(,),)`" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "drapeau = Image.new(\"RGB\",(300,200),(255,255,255))\n", "drapeau.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous venons de créer une image de dimension 300 sur 200 au format RGB entièrement blanche (couleur : (255,255,255)). Le format 300 sur 200 n'a pas été choisi au hasard, cela nous donne une proportion 2:3 qui est celle du [drapeau Français](https://fr.wikipedia.org/wiki/Drapeau_de_la_France).\n", "\n", "---\n", "Plus bas, sur cette même page, vous trouverez les [code couleurs du drapeau Français](https://fr.wikipedia.org/wiki/Drapeau_de_la_France#Dimensions_et_couleurs), au format RGB :
\n", "* La couleur rouge est codée (226,25,32)
\n", "* La couleur bleue est codée (5,20,64)
\n", "\n", "Nous pouvons maintenant modifier les pixels de notre image afin d'y faire figurer un rectangle bleue de 100 sur 200 pixels à gauche et un rectangle rouge de 100 sur 200 pixels à droite :" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "# Crée le rectangle bleu à gauche de l'image\n", "for col in range(0,100):\n", " for lig in range(0,200):\n", " drapeau.putpixel((col,lig),(5,20,64))\n", "# Crée le rectangle rouge à droite de l'image\n", "for col in range(200,300):\n", " for lig in range(0,200):\n", " drapeau.putpixel((col,lig),(226,25,32))\n", "# Affiche le drapeau crée :\n", "drapeau.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ✎ Exercices \n", "\n", "12. Créer puis afficher une image de taille 100 sur 100 pixels entièrement jaune (code couleur : (255,255,0)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "# Création et affichage carré jaune de taille 100 sur 100\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "13. Compléter le programme ci-dessous qui crée une image de dimension 256 sur 300 en niveau de gris. Puis remplis cette image avec les 256 nuances de gris possibles de façons à créer un dégradé de gris, chacune de ces nuances occupant une hauteur d'un pixel sur l'image" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Affichage d'un dégradé de gris\n", "# On commence par créer une image en niveau de gris (mode L, de taille 256x300) entièrement noire (code 0)\n", "degrade_gris = Image.new(___,(___,___),0)\n", "# Première boucle de parcours des colonnes :\n", "for col in _____(255):\n", " # Deuxième boucle de parcours des lignes (la hauteur de l'image est 300)\n", " for lig in range(___):\n", " # On affecte au pixel de la colonne le niveau de gris de la colonne \n", " degrade_gris.putpixel((___,___),col)\n", "degrade_gris.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "14. Ecrire un programme Python permettant de créer une image du drapeau de la Suède. Respecter les proportions et couleurs officielles du drapeau \n", "\n", " Cet exercice fait partie de l'évaluation du projet PIL" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.6" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": false, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 2 }