Skip to content
Snippets Groups Projects
Commit dfe3fe16 authored by Romain Vuillemot's avatar Romain Vuillemot
Browse files

Mise à jour (petites corrections dans le texte)

parent 372afcb3
Branches
No related tags found
No related merge requests found
%% Cell type:markdown id:54466d2f tags: %% Cell type:markdown id:c51c2e29 tags:
NAME: NAME:
%% Cell type:markdown id:b97bad7e-82ff-44a7-9779-13c139085623 tags: %% Cell type:markdown id:b97bad7e-82ff-44a7-9779-13c139085623 tags:
# INF TC1 - TD4 (2h) - Images # INF TC1 - TD4 (2h) - Images
%% Cell type:markdown id:1bb26026-8560-4a3c-90e6-2cfd7a49320a tags: %% Cell type:markdown id:1bb26026-8560-4a3c-90e6-2cfd7a49320a tags:
--- ---
%% Cell type:markdown id:99ee8fad-7f32-4fe2-85d3-3b8da49f317f tags: %% Cell type:markdown id:99ee8fad-7f32-4fe2-85d3-3b8da49f317f tags:
<details style="border: 1px"> <details style="border: 1px">
<summary> RAPPELS SUR L'UTILISATION DES NOTEBOOKS</summary> <summary> RAPPELS SUR L'UTILISATION DES NOTEBOOKS</summary>
### Comment utiliser ces notebooks ? ### Comment utiliser ces notebooks ?
Le but de votre travail est de répondre aux questions des exercices en **remplissant certaines cellules de ce notebook avec votre solution**. Ces cellules, une foit remplies et lancées au fur et à mesure de vos avancées, permettront de valider des tests écrits dans d'autres cellules de ce notebook. **Il est donc important de bien suivre les instructions et répondre aux questions dans l'ordre**, et ne pas changer le nom des fonctions et/ou les cellules. En particulier : Le but de votre travail est de répondre aux questions des exercices en **remplissant certaines cellules de ce notebook avec votre solution**. Ces cellules, une foit remplies et lancées au fur et à mesure de vos avancées, permettront de valider des tests écrits dans d'autres cellules de ce notebook. **Il est donc important de bien suivre les instructions et répondre aux questions dans l'ordre**, et ne pas changer le nom des fonctions et/ou les cellules. En particulier :
1) Répondez aux questions dans les cellules en dessous des questions. 1) Répondez aux questions dans les cellules en dessous des questions.
2) Votre code devra remplacer le texte suivant : 2) Votre code devra remplacer le texte suivant :
```python ```python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
(vous pouvez effacer ces deux lignes quand vous les rencontrez mais ne modifiez pas les noms de fonctions sinon les tests ne marchent plus). (vous pouvez effacer ces deux lignes quand vous les rencontrez mais ne modifiez pas les noms de fonctions sinon les tests ne marchent plus).
3) Exécuter enfin les cellules dans leur ordre d'apparition, de haut en bas et si votre code est correct alors les tests (sous forme d'`assert` seront validés (ils ne lanceront pas d'exception du type `AssertionError` ). Vous pouvez lancer plusieurs fois la même cellule, cela ne pose pas de soucis. 3) Exécuter enfin les cellules dans leur ordre d'apparition, de haut en bas et si votre code est correct alors les tests (sous forme d'`assert` seront validés (ils ne lanceront pas d'exception du type `AssertionError` ). Vous pouvez lancer plusieurs fois la même cellule, cela ne pose pas de soucis.
4) Vous pouvez créer de nouvelles cellules comme bon vous semble. 4) Vous pouvez créer de nouvelles cellules comme bon vous semble.
**En cas de problème, une solution est de relancer les cellules depuis le début du notebook une par une.** Pensez à bien sauvegarder ce notebook et ne pas le remplacer par un notebook qui a le même nom. **En cas de problème, une solution est de relancer les cellules depuis le début du notebook une par une.** Pensez à bien sauvegarder ce notebook et ne pas le remplacer par un notebook qui a le même nom.
</details> </details>
%% Cell type:markdown id:d48155b2-8db8-4557-a66b-363351712560 tags: %% Cell type:markdown id:d48155b2-8db8-4557-a66b-363351712560 tags:
## Objectif du TD ## Objectif du TD
Ce TD vous fera manipuler des images en Python, et réaliser un algorithme de remplissage basé sur le contenu. Nous verrons en particulier la structure de données matricielle et les méthodes de parcours associées. Enfin ce TD sera une préparation au TD 5 qui fera l'objet d'un rendu à réaliser à partir des concepts et du code abordés. Ce TD vous fera manipuler des images en Python, et réaliser un algorithme de remplissage basé sur le contenu. Nous verrons en particulier la structure de données matricielle et les méthodes de parcours associées. Enfin ce TD sera une préparation au TD 5 qui fera l'objet d'un rendu à réaliser à partir des concepts abordés dans ce TD.
IMPORTANT : Dans le cadre de ce TD, nous n'autorisons pas l'utilisation des modules [OpenCV](https://docs.opencv.org/4.x/) ou [NumPy](https://numpy.org/) (ou toute fonction de [Pillow](https://pillow.readthedocs.io/en/stable/) sauf celles indiquées). IMPORTANT : Dans le cadre de ce TD, nous n'autorisons pas l'utilisation des modules [OpenCV](https://docs.opencv.org/4.x/) ou [NumPy](https://numpy.org/) (et concernant le module [Pillow](https://pillow.readthedocs.io/en/stable/) seules celles indiquées sont autorisées).
%% Cell type:markdown id:647cb3f9-cd85-4bf3-8a18-18262f58041c tags: %% Cell type:markdown id:647cb3f9-cd85-4bf3-8a18-18262f58041c tags:
## Exercice 1 : Charger une image et dessiner ## Exercice 1 : Charger une image et dessiner
Une image en informatique est stockée sous forme d'une matrice de pixels qui contiennent les couleurs. Le model classique de couleurs est dit "RGB" (Red, Green, Blue) [(doc)](https://fr.wikipedia.org/wiki/Rouge-vert-bleu) où chaque pixel contient une information colorimétrique encodée sous forme de triplets `(r, g, b)` (red, green, blue). Les valeurs de couleur peuvent varier de 0 à 255 pour chaque composante de couleur. Par exemple le rouge est encodé en `(255, 0, 0)`, le gris en `(128, 128, 128)`, etc. Ces couleurs sont organisées en matrice de dimension égale à celle de l'image, les couleurs sont indépendantes les unes des autres. Une image en informatique est stockée sous forme d'une matrice de pixels qui contiennent les couleurs. Le model classique de couleurs est dit "RGB" (Red, Green, Blue) [(doc)](https://fr.wikipedia.org/wiki/Rouge-vert-bleu) où chaque pixel contient une information colorimétrique encodée sous forme de triplets `(r, g, b)` (red, green, blue). Les valeurs de couleur peuvent varier de 0 à 255 pour chaque composante de couleur. Par exemple le rouge est encodé en `(255, 0, 0)`, le gris en `(128, 128, 128)`, etc. Ces couleurs sont organisées en une matrice de dimension égale à celle de l'image, les couleurs sont indépendantes les unes des autres.
Dans ce TD nous allons utiliser un module Python appelé PIL (Pillow [doc](https://pillow.readthedocs.io/en/stable/)). Ce module permettra également de créer des images. Il est possible de l'initialiser comme suit pour charger une image dans une variable `px` dite d'accès de pixel `PixelAccess` [(doc)](https://pillow.readthedocs.io/en/stable/reference/PixelAccess.html). Le module Pillow est normalement installé, si ce n'est pas le cas, vous devez exécuter la commande suivante dans une fenêtre de terminal Anaconda (Menu Démarrer / Anaconda 64bit / Anaconda PowerShell Prompt) : `pip3 install Pillow`. Dans ce TD nous allons utiliser un module Python appelé PIL (Pillow [doc](https://pillow.readthedocs.io/en/stable/)). Ce module permettra de créer et manipuler des images. Il est possible de l'initialiser avec le code ci-dessous en important le module correspondant. Une fois le module importé, vous pourrez charger une image dans une variable `px` dite d'accès de pixel `PixelAccess` [(doc)](https://pillow.readthedocs.io/en/stable/reference/PixelAccess.html). Le module Pillow est normalement installé, si ce n'est pas le cas, vous devez exécuter la commande suivante dans une fenêtre de terminal Anaconda (Menu Démarrer / Anaconda 64bit / Anaconda PowerShell Prompt) : `pip3 install Pillow`.
Pour tester sur le module est présent sur votre ordinateur, charger une image comme suit : Pour tester si le module est présent sur votre ordinateur, charger une image comme suit :
%% Cell type:code id:5a97d177-3289-451f-bb64-3d97ad023cd4 tags: %% Cell type:code id:5a97d177-3289-451f-bb64-3d97ad023cd4 tags:
``` python ``` python
from PIL import Image from PIL import Image
from IPython.display import display from IPython.display import display
im = Image.open("lyon.png") im = Image.open("lyon.png")
im = im.convert("RGB") # important pour bien avoir 3 couleurs im = im.convert("RGB") # important pour bien avoir 3 couleurs
px = im.load() px = im.load()
W, H = im.size # taille de l'image W, H = im.size # taille de l'image
r, g, b = px[10, 20] # on récupère un pixel r, g, b = px[10, 20] # on récupère un pixel
px[10, 21] = r, g, b # on change un pixel px[10, 21] = r, g, b # on change un pixel
im = im.resize((W//2, H//2)) im = im.resize((W//2, H//2))
display(im) # on affiche l'image dans la cellule display(im) # on affiche l'image dans la cellule
# im.show() # on affiche l'image si vous n'utilisez pas de notebook # im.show() # on affiche l'image si vous n'utilisez pas de notebook
``` ```
%% Cell type:markdown id:c2a6f43f-7d5d-46ff-85cb-37d0fb717c85 tags: %% Cell type:markdown id:c2a6f43f-7d5d-46ff-85cb-37d0fb717c85 tags:
Vous pouvez également créer une **nouvelle image** `im2` vide (noire) de taille identique à `im` : Vous pouvez également créer une **nouvelle image** `im2` vide (noire) de taille identique à `im` :
%% Cell type:code id:11e98fc1-9f60-476b-909b-d632d802f3f4 tags: %% Cell type:code id:11e98fc1-9f60-476b-909b-d632d802f3f4 tags:
``` python ``` python
im2 = Image.new('RGB', (im.width, im.height)) im2 = Image.new('RGB', (im.width, im.height))
px2 = im2.load() px2 = im2.load()
display(im2) display(im2)
``` ```
%% Cell type:markdown id:482f0b25-342e-403c-975b-65a42283e1cc tags: %% Cell type:markdown id:482f0b25-342e-403c-975b-65a42283e1cc tags:
**Question 1.1 -** Définissez une fonction de lecture qui renvoie la couleur d'un pixel à la position $(x, y)$ d'une image donnée. Inspirez vous du code précédent ou de la documentation Pillow [(doc)](https://pillow.readthedocs.io/en/stable/)). **Question 1.1 -** Ecrire une fonction de lecture d'une image donnée, qui renvoie la couleur d'un pixel à la position $(x, y)$. Inspirez vous du code précédent ou de la documentation Pillow [(doc)](https://pillow.readthedocs.io/en/stable/)).
%% Cell type:code id:567ca46f-9a29-44da-93a0-9f3992f39b03 tags: %% Cell type:code id:567ca46f-9a29-44da-93a0-9f3992f39b03 tags:
``` python ``` python
def getPixel(x: int, y: int, px) -> tuple: def getPixel(x: int, y: int, px) -> tuple:
"""Get the color of a pixel. """Get the color of a pixel.
Args: Args:
x (int): pixel x coordinate x (int): pixel x coordinate
y (int): pixel y coordinate y (int): pixel y coordinate
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
Returns: Returns:
tuple: color of the pixel tuple: color of the pixel
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:08846407-027f-4b9e-86c7-43cce4234cd6 tags: %% Cell type:code id:08846407-027f-4b9e-86c7-43cce4234cd6 tags:
``` python ``` python
assert getPixel(0, 0, px) == (69, 119, 170) # bleu assert getPixel(0, 0, px) == (69, 119, 170) # bleu
assert getPixel(0, 0, px2) == (0, 0, 0) # noir assert getPixel(0, 0, px2) == (0, 0, 0) # noir
``` ```
%% Cell type:markdown id:49116049-3a09-4415-84c7-e71c6ea85c55 tags: %% Cell type:markdown id:49116049-3a09-4415-84c7-e71c6ea85c55 tags:
Afin de vérifier visuellement votre résultat, nous vous fournissons la fonction `draw_rectangle` qui permet de dessiner la couleur d'un pixel dans une cellule : Afin de vérifier visuellement votre résultat, nous vous fournissons la fonction `draw_rectangle` qui permet de dessiner la couleur d'un pixel dans une cellule :
%% Cell type:code id:b2098f50-57c2-4c4b-9a07-b695103590fa tags: %% Cell type:code id:b2098f50-57c2-4c4b-9a07-b695103590fa tags:
``` python ``` python
from IPython.display import display, HTML from IPython.display import display, HTML
def draw_rectangle(rgb_color): def draw_rectangle(rgb_color):
color = f'rgb({rgb_color[0]}, {rgb_color[1]}, {rgb_color[2]})' color = f'rgb({rgb_color[0]}, {rgb_color[1]}, {rgb_color[2]})'
html = f'<svg width="100" height="100"><rect width="100" height="100" fill="{color}" /></svg>' html = f'<svg width="100" height="100"><rect width="100" height="100" fill="{color}" /></svg>'
display(HTML(html)) display(HTML(html))
# utilisation : draw_rectangle((69, 119, 170)) # utilisation : draw_rectangle((69, 119, 170))
``` ```
%% Cell type:code id:c02ced7f-22bb-41c0-83d8-e91485dc4c5c tags: %% Cell type:code id:c02ced7f-22bb-41c0-83d8-e91485dc4c5c tags:
``` python ``` python
draw_rectangle(getPixel(0, 0, px)) # bleu draw_rectangle(getPixel(0, 0, px)) # bleu
``` ```
%% Cell type:code id:aa9391e7-8623-4c7e-9865-99ce35dc8683 tags: %% Cell type:code id:aa9391e7-8623-4c7e-9865-99ce35dc8683 tags:
``` python ``` python
draw_rectangle(getPixel(0, 0, px2)) # noir draw_rectangle(getPixel(0, 0, px2)) # noir
``` ```
%% Cell type:markdown id:69c1f988-3e28-45a7-a6c2-e0a7075c19f6 tags: %% Cell type:markdown id:69c1f988-3e28-45a7-a6c2-e0a7075c19f6 tags:
**Question 1.2 -** Définissez une fonction d'écriture d'un pixel à une position $(x, y)$ d'une image avec une couleur donnée en argument sous forme de `tuple` $(r, g, b)$. **Question 1.2 -** Définissez une fonction d'écriture d'un pixel à une position $(x, y)$ dans une image avec une couleur donnée en argument sous forme de `tuple` $(r, g, b)$.
%% Cell type:code id:df54ba88-6681-4eaa-9608-20c8d8db67ae tags: %% Cell type:code id:df54ba88-6681-4eaa-9608-20c8d8db67ae tags:
``` python ``` python
def setPixel(x: int, y:int, color: tuple, px) -> None: def setPixel(x: int, y:int, color: tuple, px) -> None:
"""Set the color of a pixel. """Set the color of a pixel.
Args: Args:
x (int): pixel x coordinate x (int): pixel x coordinate
y (int): pixel y coordinate y (int): pixel y coordinate
color (tuple): color to set color (tuple): color to set
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:ab0b00f6-5a33-4da0-a97e-5ce74e827122 tags: %% Cell type:code id:ab0b00f6-5a33-4da0-a97e-5ce74e827122 tags:
``` python ``` python
r, g, b = (0, 0, 0) r, g, b = (0, 0, 0)
setPixel(0, 0, (r, g, b), px2) setPixel(0, 0, (r, g, b), px2)
assert getPixel(0, 0, px2) == (r, g, b) assert getPixel(0, 0, px2) == (r, g, b)
``` ```
%% Cell type:markdown id:a46817a7-f6be-4f1b-b79f-0347669844cc tags: %% Cell type:markdown id:a46817a7-f6be-4f1b-b79f-0347669844cc tags:
**Question 1.3 -** Écrire une fonction permettant de peindre un rectangle de l'image avec une même couleur. Coloriez avec la couleur moyenne de cette région (et donc définir une fonction qui calcule cette couleur `moyenne`). **Question 1.3 -** Écrire une fonction permettant de remplir une région de l'image avec une même couleur qui sera la moyenne des couleurs. Dans un premier temps définissez cette fonction de calcule de `moyenne` d'une région donnée.
%% Cell type:code id:1583490b-8d8a-4dab-94df-05281ebc668c tags: %% Cell type:code id:1583490b-8d8a-4dab-94df-05281ebc668c tags:
``` python ``` python
def moyenne(corner_x, corner_y, region_w, region_h, px) -> tuple: def moyenne(corner_x: int, corner_y: int, region_w: int, region_h, px: int) -> tuple:
"""Compute the average color of a region. """Compute the average color of a region.
Args: Args:
corner_x (int): top left corner x coordinate corner_x (int): top left corner x coordinate
corner_y (int): top left corner y coordinate corner_y (int): top left corner y coordinate
region_w (int): region width region_w (int): region width
region_h (int): region height region_h (int): region height
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
Returns: Returns:
tuple: average color of the region tuple: average color of the region
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:913c742d-04ba-4220-adce-861bf39d999d tags: %% Cell type:markdown id:913c742d-04ba-4220-adce-861bf39d999d tags:
Dans la fonction de dessin de région `setRegion`la variable `color` contient le triplet de couleurs à utiliser. Ensuite écrire la fonction `setRegion` qui permet de remplir une région donnée avec une couleur (par exemple la couleur moyenne de cette région.
%% Cell type:code id:a7ba398a-bf70-4c70-83d2-28c55bdf2762 tags: %% Cell type:code id:a7ba398a-bf70-4c70-83d2-28c55bdf2762 tags:
``` python ``` python
def setRegion(x, y, w, h, color, px) -> None: def setRegion(x: int, y: int, w: int, h: int, color: tuple, px) -> None:
"""Set the color of a region. """Set the color of a region.
Args: Args:
x (int): top left corner x coordinate x (int): top left corner x coordinate
y (int): top left corner y coordinate y (int): top left corner y coordinate
w (int): region width w (int): region width
h (int): region height h (int): region height
color (tuple): color to set color (tuple): color to set
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:02374e5a-e9d5-4689-88dd-b2d53738c13d tags: %% Cell type:code id:02374e5a-e9d5-4689-88dd-b2d53738c13d tags:
``` python ``` python
assert moyenne(0, 0, 1, 1, px2) == (0.0, 0.0, 0.0) # région noire assert moyenne(0, 0, 1, 1, px2) == (0.0, 0.0, 0.0) # région noire
``` ```
%% Cell type:markdown id:6e7ce82a-7fd9-4c4d-b76d-17f96da87cd5 tags: %% Cell type:markdown id:6e7ce82a-7fd9-4c4d-b76d-17f96da87cd5 tags:
Le code ci-dessous doit dessiner un rectangle blanc au milieu d'une image noire. Le code ci-dessous doit dessiner un rectangle blanc au milieu d'une image noire.
%% Cell type:code id:6b9fbf24-ac6a-4b08-a633-fd9a6bc4708f tags: %% Cell type:code id:6b9fbf24-ac6a-4b08-a633-fd9a6bc4708f tags:
``` python ``` python
im2 = Image.new('RGB', (im.width, im.height)) im2 = Image.new('RGB', (im.width, im.height))
px2 = im2.load() px2 = im2.load()
W, H = im.size W, H = im.size
setRegion(W//3, H//3, W//3, H//3, (255, 255, 255), px2) setRegion(W//3, H//3, W//3, H//3, (255, 255, 255), px2)
display(im2) display(im2)
``` ```
%% Cell type:markdown id:38f3a987-f93a-4119-a1fa-d87eaf55f9d1 tags: %% Cell type:markdown id:38f3a987-f93a-4119-a1fa-d87eaf55f9d1 tags:
Nous allons maintenant réaliser un remplissage un peu plus intéressant de l'image à partir de son contenu (dans notre cas nous nous baserons sur les couleurs contenues dans l'image). Nous allons maintenant réaliser un remplissage un peu plus avancé de l'image à partir de son contenu (dans notre cas nous nous baserons sur les couleurs contenues dans l'image). Avant de commencer, nous avons besoin d'une fonction de calcul de distance entre deux couleurs.
%% Cell type:markdown id:daa5c6f4-eabc-4f00-8d32-86d5695af06b tags: %% Cell type:markdown id:daa5c6f4-eabc-4f00-8d32-86d5695af06b tags:
**Question 1.4 -** Ecrire une fonction de calcul de [distance Euclidienne](https://fr.wikipedia.org/wiki/Distance_(math%C3%A9matiques)) entre deux couleurs RGB (Red, Green, Blue) $C_1$ et $C_2$ comme suit : **Question 1.4 -** Ecrire une fonction de calcul de [distance Euclidienne](https://fr.wikipedia.org/wiki/Distance_(math%C3%A9matiques)) entre deux couleurs RGB (Red, Green, Blue) $C_1$ et $C_2$ comme suit :
$d_{\text{euclidienne}} = \sqrt{(R_2 - R_1)^2 + (G_2 - G_1)^2 + (B_2 - B_1)^2}$ $d_{\text{euclidienne}} = \sqrt{(R_2 - R_1)^2 + (G_2 - G_1)^2 + (B_2 - B_1)^2}$
Attention il s'agit de distances _entre_ les couleurs à comparer et non pas la distance entre les positions des pixels. Attention il s'agit de distances _entre_ les couleurs à comparer et non pas la distance entre les positions des pixels.
%% Cell type:code id:9be2d008-74fd-4f97-bdaf-d88525142f95 tags: %% Cell type:code id:9be2d008-74fd-4f97-bdaf-d88525142f95 tags:
``` python ``` python
from math import sqrt
def distance(c1: tuple, c2: tuple) -> float: def distance(c1: tuple, c2: tuple) -> float:
"""Compute the distance between two colors. """Compute the distance between two colors.
Args: Args:
c1 (tuple): first color c1 (tuple): first color
c2 (tuple): second color c2 (tuple): second color
Returns: Returns:
float: distance between the two colors float: distance between the two colors
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:f673184f-81f9-4172-ac4f-58e6cccceffc tags: %% Cell type:code id:f673184f-81f9-4172-ac4f-58e6cccceffc tags:
``` python ``` python
assert distance((0, 0, 0), (0, 0, 0)) == 0.0 assert distance((0, 0, 0), (0, 0, 0)) == 0.0
``` ```
%% Cell type:markdown id:3e09490e-9955-4639-b149-53c16855e8df tags: %% Cell type:markdown id:3e09490e-9955-4639-b149-53c16855e8df tags:
**Question 1.5 -** Nous allons désormais travailler sur une méthode de remplissage de région basée sur l'homogénéité des couleurs dans la région. Pour cela nous allons ré-utiliser les méthodes ci-dessus en particulier la distance Euclidenne, en utilisant l'algorithme dit de _flood fill_ [(doc)](https://en.wikipedia.org/wiki/Flood_fill) et qui fonctionne comme suit : **Question 1.5 -** Nous allons désormais travailler sur une méthode de remplissage de région basée sur son contenu, en utilisant un critère d'homogénéité des couleurs dans la région. Pour cela nous allons ré-utiliser les méthodes ci-dessus en particulier la distance Euclidenne, en utilisant l'algorithme dit de _flood fill_ [(doc)](https://en.wikipedia.org/wiki/Flood_fill) et qui fonctionne comme suit :
1. Charger une image et initialiser deux listes vides : une liste de pixels à visiter et une liste de pixels déjà visités 1. Charger une image et initialiser deux listes vides : une liste de pixels à visiter et une liste de pixels déjà visités
2. Définir un pixel de départ $p_{(x, y)}$ et le rajouter dans la liste de pixels à visiter 2. Définir un pixel de départ $p_{(x, y)}$ et le rajouter dans la liste de pixels à visiter
3. Extraire un pixel $p_{(i, j)}$ de la liste des pixels à visiter, il constituera la couleur $c_{(i, j)}$ de la région homogène et le rajouter dans une troisième liste de pixels homogènes à colorier avec cette couleur 3. Extraire un pixel $p_{(i, j)}$ de la liste des pixels à visiter, il constituera la couleur $c_{(i, j)}$ de la région homogène et le rajouter dans une troisième liste de pixels homogènes à colorier avec cette couleur
4. Tant que la liste de pixels homogènes n'est pas vide, extraire un pixel de cette liste : 4. Tant que la liste de pixels homogènes n'est pas vide, extraire un pixel de cette liste :
- Colorier le pixel avec la couleur $c_{(i, j)}$ et le rajouter dans la liste des pixels visités - Colorier le pixel avec la couleur $c_{(i, j)}$ et le rajouter dans la liste des pixels visités
- Explorer les 4 voisins autour du pixel (haut, bas, gauche, droite) et pour chaque voisin : - Explorer les 4 voisins autour du pixel (haut, bas, gauche, droite) et pour chaque voisin :
- Si la couleur du voisin est en dessous d'un seuil d'homogénéité alors l'inclure dans la liste de pixels homogènes - Si la couleur du voisin est en dessous d'un seuil d'homogénéité alors l'inclure dans la liste de pixels homogènes
- Sinon rajouter le pixel dans la liste de pixels à visiter - Sinon rajouter le pixel dans la liste de pixels à visiter
- Répéter cela tant que la liste de pixels homogènes n'est pas vide - Répéter cela tant que la liste de pixels homogènes n'est pas vide
5. Répéter cela tant que la liste des pixels non visités n'est pas vide 5. Répéter cela tant que la liste des pixels non visités n'est pas vide
Le résultat attendu est une image remplie coloriée avec un nombre de couleur inférieur au nombre initial de couleurs. Le résultat attendu est une image remplie coloriée avec un nombre de couleur inférieur au nombre initial de couleurs.
%% Cell type:code id:7dfc1c84-e0ca-49d1-b089-8a23175f715f tags: %% Cell type:code id:7dfc1c84-e0ca-49d1-b089-8a23175f715f tags:
``` python ``` python
def floodFill(w: int, h: int, start_x: int, start_y: int, seuil: int, px, px2) -> tuple: def floodFill(w: int, h: int, start_x: int, start_y: int, seuil: int, px, px2) -> tuple:
"""Flood fill algorithm. """Flood fill algorithm.
Args: Args:
w (int): image width w (int): image width
h (int): image height h (int): image height
start_x (int): starting x coordinate start_x (int): starting x coordinate
start_y (int): starting y coordinate start_y (int): starting y coordinate
seuil (int): color distance threshold seuil (int): color distance threshold
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
px2 (PixelAccess): image pixel access px2 (PixelAccess): image pixel access
Returns: Returns:
tuple: visited pixels, not visited pixels, unique colors used tuple: visited pixels, not visited pixels, unique colors used
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:1a50b940-d3b5-4dd5-9b9f-cd4977c9bc24 tags: %% Cell type:markdown id:1a50b940-d3b5-4dd5-9b9f-cd4977c9bc24 tags:
Le code ci-dessous va tester votre solution avec une image fournie et générer quelques statistiques liés au nombre de couleurs utilisées. Le code ci-dessous va tester votre solution avec une image fournie et générer quelques statistiques liés au nombre de couleurs utilisées.
%% Cell type:code id:102f2245-a8ce-41d6-9250-e163c96bb952 tags: %% Cell type:code id:102f2245-a8ce-41d6-9250-e163c96bb952 tags:
``` python ``` python
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:e2eb0bd7-ef80-41fd-8aba-2460de5b6ad4 tags: %% Cell type:markdown id:e2eb0bd7-ef80-41fd-8aba-2460de5b6ad4 tags:
## Pour aller plus loin ## Pour aller plus loin
- Tester le remplissage dans 8 directions (en prenant en compte les diagonales) au moyen dans la fonction d'exploration de voisinage des pixels. - Tester le remplissage dans 8 directions (en prenant en compte les diagonales) au moyen dans la fonction d'exploration de voisinage des pixels.
%% Cell type:markdown id:0e3f9555-c2fe-4c68-b4cd-b659b928755e tags: %% Cell type:markdown id:0e3f9555-c2fe-4c68-b4cd-b659b928755e tags:
## Exercice 2 : Traitement d'image par filtre ## Exercice 2 : Traitement d'image par filtre
Nous allons aborder un deuxième aspect de manipulation d'image : le traitement d'image, afin d'en extraire des informations intéressantes (contours, formes, etc.). En particulier nous allons créer différents _filtres_ dont le but sera de transformer les valeurs des pixels afin de par exemple réduire le bruit que les images peuvent contenir (à savoir les variations locales de valeur). Nous allons aborder un deuxième aspect de la manipulation d'image : le traitement d'image, afin d'en extraire des informations intéressantes (contours, formes, etc.). En particulier nous allons créer différents _filtres_ dont le but sera de transformer les valeurs des pixels afin de par exemple réduire le bruit que les images peuvent contenir (à savoir les variations locales de valeur).
La plupart de ces méthodes étant couteuses en temps, nous travaillerons sur une version en niveau de gris. La plupart de ces méthodes étant couteuses en temps, nous travaillerons sur une version en niveau de gris.
%% Cell type:markdown id:057fa44d-2c9f-423c-af12-16731b401884 tags: %% Cell type:markdown id:057fa44d-2c9f-423c-af12-16731b401884 tags:
**Question 2.1 -** Écrire une fonction de conversion d'image en niveaux de gris (soit la moyenne des triplets `(r,g,b)` ou en utilisant la formule suivante : **Question 2.1 -** Écrire une fonction de conversion d'image en niveaux de gris (soit la moyenne des triplets `(r,g,b)` ou en utilisant la formule suivante :
$ C_{gray} = (0.3 \times R) + (0.59 \times G) + (0.11 \times B)$ $ C_{gray} = (0.3 \times R) + (0.59 \times G) + (0.11 \times B)$
%% Cell type:code id:cee93e0f-a63b-4a19-87e7-00c736191465 tags: %% Cell type:code id:cee93e0f-a63b-4a19-87e7-00c736191465 tags:
``` python ``` python
def conversion_gris(px, W: int, H: int) -> None: def conversion_gris(px, W: int, H: int) -> None:
"""Convert an image to grayscale. """Convert an image to grayscale.
Args: Args:
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
W (int): image width W (int): image width
H (int): image height H (int): image height
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:4828de37-3b06-48f6-b32a-6d6e53285787 tags: %% Cell type:code id:4828de37-3b06-48f6-b32a-6d6e53285787 tags:
``` python ``` python
im = Image.open("lyon.png") im = Image.open("lyon.png")
im = im.convert("RGB") im = im.convert("RGB")
px = im.load() px = im.load()
W, H = im.size W, H = im.size
conversion_gris(px, W, H) conversion_gris(px, W, H)
display(im) display(im)
``` ```
%% Cell type:markdown id:1f8656cb-ec1f-4ce3-a837-2b938e36f1e5 tags: %% Cell type:markdown id:1f8656cb-ec1f-4ce3-a837-2b938e36f1e5 tags:
Nous commençon avec le filtre dit de _Flou Gaussien_, basé sur une opération dite de _convolution_, permettant d'appliquer une fonction de distribution gaussienne aux voisins d'un pixel et d'en faire la moyenne. Autrement dit il s'agira de réaliser la moyenne pondérée de chaque pixel en réalisant la moyenne du pixel et de ses voisins en utilisant par exemple la matrice ci-dessous (dont les valeurs sont définies par la distribution gaussienne donnée en annexe pour une matrice $3 \times 3$) : Nous commençon avec le filtre dit de _Flou Gaussien_, basé sur une opération dite de _convolution_, permettant d'appliquer une fonction de distribution gaussienne aux voisins d'un pixel et d'en faire la moyenne. Autrement dit il s'agira de réaliser la moyenne pondérée de chaque pixel en réalisant la moyenne du pixel et de ses voisins en utilisant par exemple la matrice ci-dessous (dont les valeurs sont définies par la distribution gaussienne donnée en annexe pour une matrice $3 \times 3$) :
$G(x) = \frac{1}{\sqrt[]{2 \pi } \sigma} e^{- \frac{x^{2}}{2 \sigma ^{2}}}$ $G(x) = \frac{1}{\sqrt[]{2 \pi } \sigma} e^{- \frac{x^{2}}{2 \sigma ^{2}}}$
%% Cell type:code id:9faad901-6b61-4011-be8e-55d85d3b7ded tags: %% Cell type:code id:9faad901-6b61-4011-be8e-55d85d3b7ded tags:
``` python ``` python
gauss3 = [[1,2,1], gauss3 = [[1,2,1],
[2,4,2], [2,4,2],
[1,2,1]] [1,2,1]]
gauss7 = [[1,1,2,2,2,1,1], gauss7 = [[1,1,2,2,2,1,1],
[1,2,2,4,2,2,1], [1,2,2,4,2,2,1],
[2,2,4,8,4,2,2], [2,2,4,8,4,2,2],
[2,4,8,16,8,4,2], [2,4,8,16,8,4,2],
[2,2,4,8,4,2,2], [2,2,4,8,4,2,2],
[1,2,2,4,2,2,1], [1,2,2,4,2,2,1],
[1,1,2,2,2,1,1]] [1,1,2,2,2,1,1]]
``` ```
%% Cell type:markdown id:d90ad339-06fc-4423-a302-034c1d872301 tags: %% Cell type:markdown id:d90ad339-06fc-4423-a302-034c1d872301 tags:
**Question 2.2 -** Implémentez le filtre gaussien tel que défini ci-dessus en définissant les filtres donnés ci-dessous. La fonction doit effectuer la multiplication des pixels centrés sur le pixel en cours avec les valeurs de la matrice, puis la somme pondérée. Vous pourrez utiliser les matrices ci-dessus d'approximation du filtre (en commençant par le filtre Gaussien $3 \times 3$ dont le total des valeurs est $16$). Utilisez la version de l'image en niveaux de gris afin de simplifier les traitements. **Question 2.2 -** Implémentez le filtre gaussien tel que défini ci-dessus en définissant les filtres donnés ci-dessous. La fonction doit effectuer la multiplication des pixels centrés sur le pixel en cours avec les valeurs de la matrice, puis la somme pondérée. Vous pourrez utiliser les matrices ci-dessus d'approximation du filtre (en commençant par le filtre Gaussien $3 \times 3$ dont le total des valeurs est $16$). Utilisez la version de l'image en niveaux de gris afin de simplifier les traitements.
Commencez tout d'abord par définir une fonction qui calcul la somme des valeurs d'une matrice (de type `gauss3`). Commencez tout d'abord par définir une fonction qui calcul la somme des valeurs d'une matrice (de type `gauss3`).
%% Cell type:code id:06cd5d47-9fde-4b0f-a833-b7ce415e6349 tags: %% Cell type:code id:06cd5d47-9fde-4b0f-a833-b7ce415e6349 tags:
``` python ``` python
def somme_matrice(m: list = []) -> float: def somme_matrice(m: list = []) -> float:
"""Compute the sum of all the elements in a matrix. """Compute the sum of all the elements in a matrix.
Args: Args:
m (list): matrix m (list): matrix
Returns: Returns:
float: sum of all the elements in the matrix float: sum of all the elements in the matrix
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:f02e6ba8-d870-40e0-8ed9-67b35ef12e57 tags: %% Cell type:code id:f02e6ba8-d870-40e0-8ed9-67b35ef12e57 tags:
``` python ``` python
assert somme_matrice(gauss3) == 16 assert somme_matrice(gauss3) == 16
``` ```
%% Cell type:markdown id:c0030b8b-159b-463d-a830-d6d554e6382b tags: %% Cell type:markdown id:c0030b8b-159b-463d-a830-d6d554e6382b tags:
Ecrire la fonction de convolution. Pensez à prendre en compte les bords de l'image. Conseil : ne vous approchez pas trop du bord afin de ne pas réaliser une convolution en dehors de l'image. Ecrire la fonction de convolution. Pensez à prendre en compte les bords de l'image. Conseil : ne vous approchez pas trop du bord afin de ne pas réaliser une convolution en dehors de l'image.
%% Cell type:code id:0c295c65-4e2c-4751-91a8-cb8108e18ba2 tags: %% Cell type:code id:0c295c65-4e2c-4751-91a8-cb8108e18ba2 tags:
``` python ``` python
def convolution(px, W: int, H: int, m: list) -> None: def convolution(px, W: int, H: int, m: list) -> None:
"""Apply a convolution matrix on an image. """Apply a convolution matrix on an image.
Args: Args:
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
W (int): image width W (int): image width
H (int): image height H (int): image height
m (list): convolution matrix m (list): convolution matrix
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:code id:726a449a-e975-4647-9f57-e31f3581124d tags: %% Cell type:code id:726a449a-e975-4647-9f57-e31f3581124d tags:
``` python ``` python
im = Image.open("lyon.png") im = Image.open("lyon.png")
im = im.convert("RGB") im = im.convert("RGB")
px = im.load() px = im.load()
W, H = im.size W, H = im.size
conversion_gris(px, W, H) conversion_gris(px, W, H)
convolution(px, W, H, gauss3) convolution(px, W, H, gauss3)
display(im) display(im)
``` ```
%% Cell type:markdown id:746bd3e7-f8fc-4ed2-a89b-2b4e52d6ace1 tags: %% Cell type:markdown id:746bd3e7-f8fc-4ed2-a89b-2b4e52d6ace1 tags:
Vous pouvez comparer votre floutage avec celui de PIL : Vous pouvez comparer votre floutage avec celui de PIL :
%% Cell type:code id:513a179c-89bc-43e6-87e2-ca033ffee304 tags: %% Cell type:code id:513a179c-89bc-43e6-87e2-ca033ffee304 tags:
``` python ``` python
from PIL import Image, ImageFile, ImageDraw, ImageChops, ImageFilter from PIL import Image, ImageFile, ImageDraw, ImageChops, ImageFilter
_im = im.filter(ImageFilter.GaussianBlur) _im = im.filter(ImageFilter.GaussianBlur)
display(_im) display(_im)
``` ```
%% Cell type:markdown id:7bb648b1-450b-4bd9-b52a-c798107d6079 tags: %% Cell type:markdown id:7bb648b1-450b-4bd9-b52a-c798107d6079 tags:
**Question 2.3 -** Testez votre code avec ces filtres ci-dessous avec un facteur de normalisation/pondération de 1 : que se passe-t-il ? [(doc)](https://en.wikipedia.org/wiki/Kernel_(image_processing)). **Question 2.3 -** Testez votre code avec ces filtres ci-dessous avec un facteur de normalisation/pondération de 1 : que se passe-t-il ? [(doc)](https://en.wikipedia.org/wiki/Kernel_(image_processing)).
%% Cell type:code id:f213b45e-768f-4d89-b456-f12050a3217c tags: %% Cell type:code id:f213b45e-768f-4d89-b456-f12050a3217c tags:
``` python ``` python
sobely3 = [[-1, 0, 1], sobely3 = [[-1, 0, 1],
[-2, 0, 2], [-2, 0, 2],
[-1, 0, 1]] [-1, 0, 1]]
sobelx3 = [[-1, -2, -1], sobelx3 = [[-1, -2, -1],
[0, 0, 0], [0, 0, 0],
[1, 2, 1]] [1, 2, 1]]
``` ```
%% Cell type:code id:760d4ca7-5f31-457f-8b09-6e0dfc12c7a8 tags: %% Cell type:code id:760d4ca7-5f31-457f-8b09-6e0dfc12c7a8 tags:
``` python ``` python
def convolution_sobel(px, W: int, H: int, m: list, f = 1) -> None: def convolution_sobel(px, W: int, H: int, m: list, f = 1) -> None:
"""Apply a sobel filter convolution on an image. """Apply a sobel filter convolution on an image.
Args: Args:
px (PixelAccess): image pixel access px (PixelAccess): image pixel access
W (int): image width W (int): image width
H (int): image height H (int): image height
m (list): convolution matrix m (list): convolution matrix
f (int): factor f (int): factor
""" """
# YOUR CODE HERE # YOUR CODE HERE
raise NotImplementedError() raise NotImplementedError()
``` ```
%% Cell type:markdown id:00c62110-6e7b-42d6-8450-d8ed7c7e233a tags: %% Cell type:markdown id:00c62110-6e7b-42d6-8450-d8ed7c7e233a tags:
## Tests pour la fonction de floodfill ## Tests pour la fonction de floodfill
%% Cell type:code id:d61c4763-f479-42fe-91bb-27311925f6c0 tags: %% Cell type:code id:d61c4763-f479-42fe-91bb-27311925f6c0 tags:
``` python ``` python
W, H = 100, 100 W, H = 100, 100
im = Image.new('RGB', (W, H)) im = Image.new('RGB', (W, H))
px = im.load() px = im.load()
setRegion(W//3, H//3, W//3, H//3, (255, 255, 255), px) setRegion(W//3, H//3, W//3, H//3, (255, 255, 255), px)
display(im) display(im)
im2 = Image.new('RGB', (W, H)) im2 = Image.new('RGB', (W, H))
px2 = im2.load() px2 = im2.load()
display(im2) display(im2)
d = distance((0, 0, 0), (255, 255, 255)) # pour trouver le seuil d = distance((0, 0, 0), (255, 255, 255)) # pour trouver le seuil
print("on est en dessous du seuil on garde les 2 couleurs") print("on est en dessous du seuil on garde les 2 couleurs")
print("on part du coin en haut à gauche -> noir") print("on part du coin en haut à gauche -> noir")
visited, not_visited, colors = floodFill(W, H, 0, 0, d - 1, px, px2) visited, not_visited, colors = floodFill(W, H, 0, 0, d - 1, px, px2)
print("nombre de noeuds coloriés", len(visited)) print("nombre de noeuds coloriés", len(visited))
print("nombre de noeuds non visités", len(not_visited)) print("nombre de noeuds non visités", len(not_visited))
print(f"nombre de couleurs utilisées {colors}") print(f"nombre de couleurs utilisées {colors}")
display(im2) display(im2)
print("on part du milieu -> blanc") print("on part du milieu -> blanc")
visited, not_visited, colors = floodFill(W, H, W/2, H/2, d - 1, px, px2) visited, not_visited, colors = floodFill(W, H, W/2, H/2, d - 1, px, px2)
print("nombre de noeuds coloriés", len(visited)) print("nombre de noeuds coloriés", len(visited))
print("nombre de noeuds non visités", len(not_visited)) print("nombre de noeuds non visités", len(not_visited))
print(f"nombre de couleurs utilisées {colors}") print(f"nombre de couleurs utilisées {colors}")
display(im2) display(im2)
print("on est au dessus du seuil on ne garde qu'une couleur") print("on est au dessus du seuil on ne garde qu'une couleur")
print("on part du coin en haut à gauche -> noir") print("on part du coin en haut à gauche -> noir")
visited, not_visited, colors = floodFill(W, H, 0, 0, d + 1, px, px2) visited, not_visited, colors = floodFill(W, H, 0, 0, d + 1, px, px2)
print("nombre de noeuds coloriés", len(visited)) print("nombre de noeuds coloriés", len(visited))
print("nombre de noeuds non visités", len(not_visited)) print("nombre de noeuds non visités", len(not_visited))
print(f"nombre de couleurs utilisées {colors}") print(f"nombre de couleurs utilisées {colors}")
display(im2) display(im2)
print("on part du milieu -> blanc") print("on part du milieu -> blanc")
visited, not_visited, colors = floodFill(W, H, W/2, H/2, d + 1, px, px2) visited, not_visited, colors = floodFill(W, H, W/2, H/2, d + 1, px, px2)
print("nombre de noeuds coloriés", len(visited)) print("nombre de noeuds coloriés", len(visited))
print("nombre de noeuds non visités", len(not_visited)) print("nombre de noeuds non visités", len(not_visited))
print(f"nombre de couleurs utilisées {colors}") print(f"nombre de couleurs utilisées {colors}")
display(im2) display(im2)
``` ```
%% Cell type:code id:980dfda1-8ac9-49da-ac66-6b460d91b5f2 tags:
``` python
```
......
%% Cell type:markdown id:052d9fc3 tags: %% Cell type:markdown id:4b2f9189 tags:
NAME: NAME:
%% Cell type:markdown id:b97bad7e-82ff-44a7-9779-13c139085623 tags: %% Cell type:markdown id:b97bad7e-82ff-44a7-9779-13c139085623 tags:
# INF TC1 - TD5 (2h) - Devoir à rendre #1 # INF TC1 - TD5 (2h) - Devoir à rendre #1
%% Cell type:markdown id:1bb26026-8560-4a3c-90e6-2cfd7a49320a tags: %% Cell type:markdown id:1bb26026-8560-4a3c-90e6-2cfd7a49320a tags:
--- ---
%% Cell type:markdown id:dd8d4905-55f9-4957-8008-a963cc6de061 tags: %% Cell type:markdown id:dd8d4905-55f9-4957-8008-a963cc6de061 tags:
Vous serez évalué sur le rendu de ce TD qui sera à déposer sur Moodle **deux (2) semaines** après les séances d'autonomie et de TD. Le rendu sera à réaliser sous forme de **notebook** qui contient votre code et images. Vous serez évalué sur le rendu de ce TD qui sera à déposer sur Moodle **deux (2) semaines** après les séances d'autonomie et de TD. Le rendu sera à réaliser sous forme de **notebook** qui contient votre code et images.
%% Cell type:markdown id:99ee8fad-7f32-4fe2-85d3-3b8da49f317f tags: %% Cell type:markdown id:99ee8fad-7f32-4fe2-85d3-3b8da49f317f tags:
<details style="border: 1px"> <details style="border: 1px">
<summary> MODALITES DE RENDU</summary> <summary> MODALITES DE RENDU</summary>
### Comment rendre son devoir ? ### Comment rendre son devoir ?
Vous serez évalué sur le rendu de ce TD qui sera à déposer sur Moodle **deux (2) semaines** après les séances d'autonomie et de TD. Vous devrez créer une archive (zip, rar, etc.) nomée `nom1-nom2-inf-tc1-td5.zip` qui contiendra tous les éléments de votre rendu (rapport en notebook, code, images de test). Vous pouvez rendre ce rapport seul ou en binôme. Le rendu du TD doit contenir a minima : Vous serez évalué sur le rendu de ce TD qui sera à déposer sur Moodle **deux (2) semaines** après les séances d'autonomie et de TD. Vous devrez créer une archive (zip, rar, etc.) nomée `nom1-nom2-inf-tc1-td5.zip` qui contiendra tous les éléments de votre rendu (rapport en notebook, code, images de test). Vous pouvez rendre ce rapport seul ou en binôme. Le rendu du TD doit contenir a minima :
1. Toutes les étapes jusqu'à la 6ème doivent avoir été abordées 1. Toutes les étapes jusqu'à la 6ème doivent avoir été abordées
2. Justifications, illustrations et tests sur plusieurs images 2. Justifications, illustrations et tests sur plusieurs images
**A garder en tête :** **A garder en tête :**
- Un code fonctionnel et les tests appropriés devront être fournis dans l'archive qui doit être autonome (le correcteur ne doit pas avoir à rajouter d'image ou de fichier supplémentaire) - Un code fonctionnel et les tests appropriés devront être fournis dans l'archive qui doit être autonome (le correcteur ne doit pas avoir à rajouter d'image ou de fichier supplémentaire)
- Vous fournirez les images de test et leurs résultats; évitez cependant de prendre des tailles d'images trop importantes. - Vous fournirez les images de test et leurs résultats; évitez cependant de prendre des tailles d'images trop importantes.
- Le rapport **devra être au format Notebook Jupyter** et comprendre : - Le rapport **devra être au format Notebook Jupyter** et comprendre :
- Le détail des étapes que vous avez suivies - Le détail des étapes que vous avez suivies
- La description de parties de code difficiles - La description de parties de code difficiles
- Tout souci ou point bloquant dans votre code - Tout souci ou point bloquant dans votre code
- Les graphiques et diagrammes nécessaires - Les graphiques et diagrammes nécessaires
- Des analyses et discussions en lien avec votre approche - Des analyses et discussions en lien avec votre approche
- Des exemples simples mais aussi difficiles - Des exemples simples mais aussi difficiles
**Tout travail supplémentaire (méthode originale, optimisation poussée) fera l'objet de points en bonus.** **Tout travail supplémentaire (méthode originale, optimisation poussée) fera l'objet de points en bonus.**
*Voici une suggestion afin de se faire un ordre d'idée* *Voici une suggestion afin de se faire un ordre d'idée*
En dessous de 10 : En dessous de 10 :
- Les étapes suivies - Les étapes suivies
- Un code fonctionnel et les méthodes basiques - Un code fonctionnel et les méthodes basiques
- Un rapport de quelques pages - Un rapport de quelques pages
- Un code certes fonctionnel mais peu commenté - Un code certes fonctionnel mais peu commenté
- Les exemples d'images fournies - Les exemples d'images fournies
Un groupe avec une note entre 10 et 12 : Un groupe avec une note entre 10 et 12 :
- Les étapes suivies - Les étapes suivies
- Un code fonctionnel et les méthodes basiques - Un code fonctionnel et les méthodes basiques
- Un rapport de quelques pages - Un rapport de quelques pages
- Un code certes fonctionnel mais peu commenté - Un code certes fonctionnel mais peu commenté
- Les exemples d'images fournies - Les exemples d'images fournies
Un groupe entre 12 et 14 a en plus proposé : Un groupe entre 12 et 14 a en plus proposé :
- Des structures de données avancées (Set, Files, etc) - Des structures de données avancées (Set, Files, etc)
- Une justification de chaque étape - Une justification de chaque étape
- Une méthode un petit peu plus poussée - Une méthode un petit peu plus poussée
Un groupe entre 14 et 16 a en plus proposé : Un groupe entre 14 et 16 a en plus proposé :
- Une méthode originale (K-Means, etc) - Une méthode originale (K-Means, etc)
- Une démarche expérimentale très détaillée sur les optimisations - Une démarche expérimentale très détaillée sur les optimisations
- Des tests plutôt originaux - Des tests plutôt originaux
Un groupe au-dessus de 16 comporte une ou plusieurs parties exceptionnelles : Un groupe au-dessus de 16 comporte une ou plusieurs parties exceptionnelles :
- Rapport très détaillé et exemplaire sur le fond comme sur la forme - Rapport très détaillé et exemplaire sur le fond comme sur la forme
- Une démarche expérimentale très détaillée sur les optimisations - Une démarche expérimentale très détaillée sur les optimisations
- Code et tests - Code et tests
</details> </details>
%% Cell type:markdown id:d48155b2-8db8-4557-a66b-363351712560 tags: %% Cell type:markdown id:d48155b2-8db8-4557-a66b-363351712560 tags:
## Objectif du devoir ## Objectif du devoir
Le but de ce devoir est de **déterminer automatiquement une palette de couleurs optimale** pour une image donnée. Cette palette devra valider les contraintes suivantes : Le but de ce devoir est de **déterminer automatiquement une palette de couleurs optimale** pour une image donnée. Cette palette devra valider les contraintes suivantes :
1. de taille réduite par rapport au nombre initial de couleurs 1. avoir une taille réduite par rapport au nombre initial de couleurs de l'image
2. la plus représentative possible des couleurs initiales. 2. être la plus représentative possible des couleurs initiales.
En effet une image affichée sur un ordinateur peut être encodée sur 8 bits par composantes rouge, verte et bleue (soit 256 valeurs possibles par composante) ainsi potentiellement utiliser $256 \times 256 \times 256 = 16 777 216$ couleurs. En réalité, beaucoup moins sont utilisées et surtout perceptibles par l'humain. Réduire le nombre de couleur ou réaliser une "_quantification de couleurs_" est une tâche fréquente et c'est une fonctionnalité classique des outils éditeurs d'images (Photoshop, Gimp, etc.) implémentée aussi dans le module Pillow de Python. A noter que cette réduction s'effectue avec perte de couleurs et doit être réalisée avec les bons paramètres (nombre et choix des couleurs) ce qui est votre objectif. Comme nous l'avons vu dans le TD 4, les couleurs peuvent être encodée sur 8 bits par composantes rouge, verte et bleue (soit 256 valeurs possibles par composante) ainsi potentiellement utiliser $256 \times 256 \times 256 = 16 777 216$ couleurs. En réalité, beaucoup moins sont utilisées et surtout perceptibles par l'humain. Réduire le nombre de couleur ou réaliser une "_quantification de couleurs_" est une tâche fréquente et c'est une fonctionnalité classique des outils éditeurs d'images (Photoshop, Gimp, etc.) implémentée aussi dans le module Pillow de Python. A noter que cette réduction s'effectue avec perte de couleurs et doit être réalisée avec les bons paramètres (nombre et choix des couleurs) ce qui est votre objectif.
La figure ci-dessous illustre le problème à résoudre : étant donnée une image en entrée, proposer une liste de couleurs (que l'on appellera la palette), afin de re-colorier une image en sortie. La figure ci-dessous illustre le problème à résoudre : étant donnée une image en entrée, proposer une liste de couleurs (que l'on appellera la palette), afin de re-colorier une image en sortie.
<div style="text-align:center;"> <div style="text-align:center;">
<table> <table>
<tr> <tr>
<td> <td>
<img src="figures/color-rainbow.png" alt="Image originale" style="height:5cm;"> <img src="figures/color-rainbow.png" alt="Image originale" style="height:5cm;">
<p>Image originale</p> <p>Image originale</p>
</td> </td>
<td> <td>
<img src="figures/rainbow-palette-8.png" alt="Palette de 8 couleurs représentatives" style="height:5cm;"> <img src="figures/rainbow-palette-8.png" alt="Palette de 8 couleurs représentatives" style="height:5cm;">
<p>Palette de 8 couleurs représentatives</p> <p>Palette de 8 couleurs représentatives</p>
</td> </td>
<td> <td>
<img src="figures/rainbow-recoloriee.png" alt="Image originale recoloriée avec la palette" style="height:5cm;"> <img src="figures/rainbow-recoloriee.png" alt="Image originale recoloriée avec la palette" style="height:5cm;">
<p>Image originale recoloriée avec la palette</p> <p>Image originale recoloriée avec la palette</p>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
%% Cell type:markdown id:fd464e65-adfe-4e11-bf87-f12c513fbaea tags: %% Cell type:markdown id:fd464e65-adfe-4e11-bf87-f12c513fbaea tags:
## Étapes de travail ## Étapes de travail
Voici des étapes de travail suggérées : Voici des étapes de travail suggérées :
1. Prendre en main une image de votre choix (pas trop grande) en la chargeant avec PIL. Lister les couleurs présentes, identifier celles qui sont uniques et leur fréquence. Vous pouvez pour cela utiliser [Matplotlib](https://matplotlib.org/stable/gallery/index.html). 1. Prendre en main une image de votre choix (pas trop grande) en la chargeant avec PIL. Lister les couleurs présentes, identifier celles qui sont uniques et leur fréquence. Vous pouvez pour cela utiliser [Matplotlib](https://matplotlib.org/stable/gallery/index.html).
2. Proposer une méthode (naïve pour commencer) de choix d'une palette de $k$ couleurs. Affichez là sous forme d'image (exemple de d'image au milieu de la figure du dessus) avec une nouvelle image PIL. Utilisez également des images simples où le résultat attendu est connu comme mour les images ci-dessous : 2. Proposer une méthode (naïve pour commencer) de choix d'une palette de $k$ couleurs. Affichez là sous forme d'image (exemple de d'image au milieu de la figure du dessus) avec une nouvelle image PIL. Utilisez également des images simples où le résultat attendu est connu comme mour les images ci-dessous :
<div style="text-align:center;"> <div style="text-align:center;">
<table> <table>
<tr> <tr>
<td> <td>
<img src="figures/1-color-back.png" alt="1 couleur noir" style="width:3cm;"> <img src="figures/1-color-back.png" alt="1 couleur noir" style="width:3cm;">
<p>1 couleur noir</p> <p>1 couleur noir</p>
</td> </td>
<td> <td>
<img src="figures/4-color.png" alt="4 couleurs" style="width:3cm;"> <img src="figures/4-color.png" alt="4 couleurs" style="width:3cm;">
<p>4 couleurs</p> <p>4 couleurs</p>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
3. Re-colorier une image avec une palette de $k$ couleurs, et afficher le résultat sous forme d'image PIL. Pour re-colorier chaque pixel, prendre la couleur la plus proche dans la palette en utilisant une fonction de distance (Euclidienne par exemple). 3. Re-colorier une image avec une palette de $k$ couleurs, et afficher le résultat sous forme d'image PIL. Pour re-colorier chaque pixel, prendre la couleur la plus proche dans la palette en utilisant une fonction de distance (Euclidienne par exemple).
4. Proposer une méthode de validation de votre approche. Par exemple afficher la différence entre l'image originale et celle re-coloriée. Calculer un score global d'erreur. 4. Proposer une méthode de validation de votre approche. Par exemple afficher la différence entre l'image originale et celle re-coloriée. Calculer un score global d'erreur.
5. Améliorer le choix des $k$ couleurs afin de minimiser l'erreur entre l'image originale et re-coloriée. Une piste possible est de trier les couleurs dans une liste, diviser cette liste en $k$ intervals de couleurs et prendre la couleur du milieu de chaque interval. D'autres méthodes plus avancées peuvent être explorées ! 5. Améliorer le choix des $k$ couleurs afin de minimiser l'erreur entre l'image originale et re-coloriée. Une piste possible est de trier les couleurs dans une liste, diviser cette liste en $k$ intervals de couleurs et prendre la couleur du milieu de chaque interval. D'autres méthodes plus avancées peuvent être explorées !
6. Tester sur plusieurs images de votre choix ou générées automatiquement avec un nombre et une distribution connue de couleurs. Comparer les performances de vos techniques avec d'autres méthodes (cette fois vous pouvez utiliser un éditeur de texte ou la fonction _quantize_ de PIL [(doc)](https://pillow.readthedocs.io/en/stable/reference/Image.html). 6. Tester sur plusieurs images de votre choix ou générées automatiquement avec un nombre et une distribution connue de couleurs. Comparer les performances de vos techniques avec d'autres méthodes (cette fois vous pouvez utiliser un éditeur de texte ou la fonction _quantize_ de PIL [(doc)](https://pillow.readthedocs.io/en/stable/reference/Image.html).
7. Utiliser un pré-traitement des images (flou gaussien, etc) afin de lisser les couleurs est une piste afin de choisir de meilleurs couleurs représentatives. Proposez une quantification de cette amélioration (ou de déterioration éventuelle). 7. Utiliser un pré-traitement des images (flou gaussien, etc) afin de lisser les couleurs est une piste afin de choisir de meilleurs couleurs représentatives. Proposez une quantification de cette amélioration (ou de déterioration éventuelle).
7. Proposer une méthode d'amélioration de calcul de la distance entre deux couleurs, vous pouvez vous baser sur d'autres espaces de couleur [(doc)](https://fr.wikipedia.org/wiki/Espace_de_couleur). Cette partie est difficile, les espaces de couleurs possibles sont complexes à comprendre. 7. Proposer une méthode d'amélioration de calcul de la distance entre deux couleurs, vous pouvez vous baser sur d'autres espaces de couleur [(doc)](https://fr.wikipedia.org/wiki/Espace_de_couleur). Cette partie est difficile, les espaces de couleurs possibles sont complexes à comprendre.
8. Optimiser les étapes précédentes (complexité, espace nécessaire, structures de données, etc.). 8. Optimiser les étapes précédentes (complexité, espace nécessaire, structures de données, etc.).
### Bonus ### Bonus
10. Créer une palette représentative à partir de plusieurs images. 10. Créer une palette représentative à partir de plusieurs images.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment