Licence CC BY-NC-ND, Valérie Roy & Thierry Parmentelat
à télécharger
pour réaliser ce TP localement sur votre ordi, si ce n’est pas déjà fait, commencez par télécharger le zip
TP images (2/2)#
merci à Wikipedia et à stackoverflow
disclaimer
le but de ce TP n’est pas d’apprendre le traitement d’image - on se sert d’images pour égayer des exercices avec numpy
(et parce que quand on se trompe ça se voit)
import numpy as np
from matplotlib import pyplot as plt
→ notions intervenant dans ce TP
sur les tableaux
numpy.ndarrayreshape(), masques booléens, ufunc, agrégation, opérations linéairespour l’exercice
patchwork:
on peut le traiter sans, mais l’exercice se prête bien à l’utilisation d’une indexation d’un tableau par un tableau - voyez par exemple cecipour l’exercice
sepia:
ici aussi on peut le faire “naivement” mais l’utilisation denp.dot()peut rendre le code beaucoup plus court
pour la lecture, l’écriture et l’affichage d’images
utilisez
plt.imread(),plt.imshow()utilisez
plt.show()entre deuxplt.imshow()si vous affichez plusieurs images dans une même cellule
note à propos de l’affichage
nous utilisons les fonctions d’affichage d’images de
pyplotpar souci de simpliciténous ne signifions pas là du tout que ce sont les meilleures!
par exemplematplotlib.pyplot.imsavene vous permet pas de donner la qualité de la compression
alors que la fonctionsavedePILle permetvous êtes libres d’utiliser une autre librairie comme
opencv
si vous la connaissez assez pour vous débrouiller (et l’installer), les images ne sont qu’un prétexte…
Création d’un patchwork#
v1#
on se propose d’écrire un code pour créer des tableaux dans le genre de celui-ci (affiché avec plt.imshow):

# pour cela on se définirait par exemple
colors = [
[255, 0, 0],
[0, 255, 0],
[0, 0, 255],
[255, 255, 0],
[255, 0, 255],
]
après quoi on appellerait la fonction patchwork - que vous allez devoir écrire - comme ceci:
plt.imshow(patchwork(colors))
remarquez les choses suivantes:
si par exemple on avait passé 9 couleurs, on aurait créé un carré 3x3, mais comme ici on a passé à la fonction une liste de 5 couleurs, pour que ça tienne dans un rectangle, on se décide sur un rectangle de taille 2x3
la taille totale de l’image est de 10x15, car par défaut chaque petite tuile a une taille de 5 pixels
du coup le dernier carré est rempli avec une couleur par défaut - ici DarkGray (dans la v2 on pourra utiliser les couleurs par leur nom, mais n’anticipons pas; pour l’instant notez que DarkGray c’est 169, 169, 169)
on va permettre à l’appelant de changer ces valeurs par défaut
ça signifie que si on appelait
# cette fois on passe 10 couleurs (colors + colors est une liste de 10 couleurs)
# et on fixe la taille des tuiles, et la couleur de fond noire
plt.imshow(patchwork(colors + colors, side=10, background=[0, 0, 0]))
on obtiendrait cette fois (observez la taille en pixels de l’image)

exercice
écrivez une fonction
rectangle_sizequi calcule la taille du rectangle en fonction du nombre de couleurs
indice
votre fonction retourne un tuple avec deux morceaux: le nombre de lignes, et le nombre de colonnes
dans un premier temps, vous pouvez vous contenter d’une version un peu brute: on pourrait utiliser juste la racine carrée, et toujours fabriquer des carrés
par exemple avec 5 couleurs créer un carré 3x3 (et remplir les 4 cases restantes avec la couleur de fond)
mais si vous avez le temps, pour 5 couleurs, un rectangle 3x2 c’est quand même mieux !
voici pour vous aider à calculer le rectangle qui contient n couleurs
n
rect
n
rect
n
rect
n
rect
1
1x1
5
2x3
9
3x3
14
4x4
2
1x2
6
2x3
10
3x4
15
4x4
3
2x2
7
3x3
11
3x4
16
4x4
4
2x2
8
3x3
12
3x4
17
4x5
# votre code
def rectangle_size(n):
"""
return a tuple (lines, cols) for
the smallest rectangle that contains n cells
"""
# your code goes here...
...
écrivez la fonction
patchworktelle que décrite en préambule
indices
sont potentiellement utiles pour cet exo:
la fonction
np.indices()
souvenez-vous que chaque “tuile” a une taille réglable
et qu’il vous faut peindre les tuiles surnuméraires avec une couleur de fond paamétrable
# votre code
def patchwork(colors, side=10, background=[169, 169, 169]):
"""
- colors is expected to be a list of n colors; it can be either
* a list like e.g. [[255, 0, 0], [0, 255, 0], ... ]
* or a numpy array of shape n, 3
- side is the "width" of each square
- optional background it used to pad the rest of the image when
the <n> colors are not enough to fill a rectangle
here we use DarkGray as the default
"""
# your code here
...
# si vous voulez tester
# plt.imshow(patchwork(colors));
# si vous voulez tester
# plt.imshow(patchwork(colors+colors, side=10, background=[0, 0, 0]))
v2 (optionnel)#
dans cette version, on a envie de pouvoir faire essentiellement la même chose, mais avec des noms de couleurs
et pour cela on vous fournit un fichier textuel de description des couleurs qui se trouve dans data/rgb-codes.txt et qui ressemble à ceci:
AliceBlue 240 248 255
AntiqueWhite 250 235 215
Aqua 0 255 255
.../...
YellowGreen 154 205 50
Comme vous le devinez, le nom de la couleur est suivi des 3 valeurs
de ses codes R, G et B
# with patchwork v2 one could use this data
color_names = [
'DarkBlue', 'AntiqueWhite', 'LimeGreen', 'NavajoWhite',
'Tomato', 'DarkGoldenrod', 'LightGoldenrodYellow', 'OliveDrab',
'Red', 'Lime',
]
et ce qu’on veut, c’est pouvoir faire par exemple
patchwork2(color_names)
pour obtenir ceci

exercice
lisez le fichier des couleurs en
Python, et rangez cela dans la structure de données qui vous semble adéquate.
# votre code
Affichez, à partir de votre structure, les valeurs rgb entières des couleurs suivantes
'Red','Lime','Blue'
# votre code
Faites une fonction
patchwork2qui fait ce qu’on veutTestez votre fonction en affichant le résultat obtenu sur un jeu de couleurs fourni
un commentaire
telle qu’on l’a appelée ci-dessus i.e. patchwork(color_names), on n’a pas prévu de passer en paramètre la table des couleurs - je veux dire la structure qu’on a construite à l’étape 1
c’est principalement pour simplifier: utilisez cette structure comme une variable globale !
bon sachez juste que dans la vraie vie, on évite cette pratique de passer par une variable globale; il y a plein de façons de faire ça, mais ce n’est pas notre sujet aujourd’hui, et on va rester simple :)
# votre code
def patchwork2(color_names, side=10, background_color="DarkGray"):
'''
create a patchwork image with <color_names>, which are resolved
from the text file loaded above
the other two parameters are passed to the `patchwork` function above
except that the background color is expected to ba a color name too
'''
# your goes goes here
...
# ou encore
#plt.imshow(patchwork2(color_names, side=20, background="DarkGray"));
# et pour le tester
#plt.imshow(patchwork2(color_names));
Tirez aléatoirement une liste de couleurs et appliquez votre fonction à ces couleurs.
# votre code
Sélectionnez toutes les couleurs à base de blanc (i.e. dont le nom contient
white) et affichez leur patchwork
même chose pour des jaunes
# votre code
Appliquez la fonction à toutes les couleurs du fichier
et sauver ce patchwork dans le fichierpatchwork.pngavecplt.imsave
# votre code
Relisez et affichez votre fichier
attention si votre image vous semble floue c’est juste que l’affichage grossit vos pixels
# votre code
vous devriez obtenir quelque chose comme ceci
Image en sépia#
Pour passer en sépia les valeurs R, G et B d’un pixel, on applique la transformation suivante
R' = 0.393 * R + 0.769 * G + 0.189 * B
G' = 0.349 * R + 0.686 * G + 0.168 * B
B' = 0.272 * R + 0.534 * G + 0.131 * B
notes sur les types
dans notre cas on suppose qu’en entrée on a des entiers non-signé 8 bits
mais attention, les calculs vont devoir se faire en flottants, et pas en uint8
pour ne pas avoir, par exemple, 256 devenant 0toutefois on veut tout de même en sortie des entiers non-signé 8 bits !
ça signifie qu’il va sans doute vous falloir faire un peu de gymnastique avec les types de vos tableaux
Tip
indice
vous devriez jeter un coup d’oeil à la fonction np.dot qui est, si on veut, une généralisation du produit matriciel
et dont voici un exemple d’utilisation:
# exemple de produit de matrices avec `numpy.dot`
# le help(np.dot) dit: dot(A, B)[i,j,k,m] = sum(A[i,j,:] * B[k,:,m])
i, j, k, m, n = 2, 3, 4, 5, 6
A = np.arange(i*j*k).reshape(i, j, k)
B = np.arange(m*k*n).reshape(m, k, n)
C = A.dot(B)
# or C = np.dot(A, B)
print(f"en partant des dimensions {A.shape} et {B.shape}")
print(f"on obtient un résultat de dimension {C.shape}")
print(f"et le nombre de termes dans chaque `sum()` est {A.shape[-1]} == {B.shape[-2]}")
en partant des dimensions (2, 3, 4) et (5, 4, 6)
on obtient un résultat de dimension (2, 3, 5, 6)
et le nombre de termes dans chaque `sum()` est 4 == 4
Exercice
Faites une fonction
sepiaqui prend en argument une image RGB et rend une image RGB sépia
# votre code
Passez l’image
data/les-mines.jpgen sépia
# votre code
Voici ce que vous devriez obtenir avec l’images des Mines


Somme dans une image & overflow#
Lisez l’image
data/les-mines.jpg
# votre code
Créez un nouveau tableau
numpy.ndarrayen sommant avec l’opérateur+les valeurs RGB des pixels de votre image
# votre code
Regardez le type de cette image-somme, et son maximum; que remarquez-vous?
Affichez cette image-somme; comme elle ne contient qu’un canal il est habile de l’afficher en “niveaux de gris” (normalement le résultat n’est pas terrible …)niveaux de gris ?
cherchez sur google
pyplot imshow cmap gray
# votre code
Créez un nouveau tableau
numpy.ndarrayen sommant mais cette fois avec la fonction d’agrégationnp.sumles valeurs RGB des pixels de votre image
# votre code
Comme dans le 2., regardez son maximum et son type, et affichez la
# votre code
Les deux images sont de qualité très différente, pourquoi cette différence ? Utilisez le help
np.sum?
# votre code / explication
Passez l’image en niveaux de gris de type entiers non-signés 8 bits
(de la manière que vous préférez)
# votre code
Remplacez dans l’image en niveaux de gris,
les valeurs >= à 127 par 255 et celles inférieures par 0
Affichez l’image avec une carte des couleurs des niveaux de gris
vous pouvez utilisez la fonctionnumpy.where
# votre code
avec la fonction
numpy.unique
regardez les valeurs différentes que vous avez dans votre image en noir et blanc
# votre code
Exemple de qualité de compression#
Importez la librairie
ImagedePIL(pillow)
(vous devez peut être installer PIL dans votre environnement)
# votre code
Quelle est la taille du fichier
data/les-mines.jpgsur disque ?
file = "data/les-mines.jpg"
# votre code
Lisez le fichier ‘data/les-mines.jpg’ avec
Image.openet avecplt.imread
# votre code
Vérifiez que les valeurs contenues dans les deux objets sont proches
# votre code
Sauvez (toujours avec de nouveaux noms de fichiers)
l’image lue parimreadavecplt.imsave
l’image lue parImage.openavecsaveet unequality=100
(saves’applique à l’objet créé parImage.open)
# votre code
Quelles sont les tailles de ces deux fichiers sur votre disque ?
Que constatez-vous ?
# votre code
Relisez les deux fichiers créés et affichez avec
plt.imshowleur différence
# votre code