Commit 6dd3dc57 authored by Matthieu PERREIRA DA SILVA's avatar Matthieu PERREIRA DA SILVA
Browse files

Modif de la question sur la vitesse d'execution

Pour prendre en compte les 3 façons (et plus deux)
parent 2e6440a4
%% Cell type:code id: tags:
``` python
%matplotlib notebook
# makes nicer graphics (maybe) if want to export notebook to pdf
%config InlineBackend.figure_formats = {'png', 'retina'}
from skimage import data, filters, io
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
import scipy.stats as stats
import math
from numba import jit
```
%% Cell type:markdown id: tags:
## Preliminaire : le code vectorisé
%% Cell type:markdown id: tags:
Dans cette petite manipulation préliminaire python, on examine deux façons d'écrire un algorithme de seuillage d'image. L'opération sur l'image considérée ici est un exemple simple quelconque, le sujet est la ** vectorisation de code **.
%% Cell type:markdown id: tags:
* Que fait le code ci-dessous ?
* Quelle fonction de seuillage est plus facile à lire ?
* Laquelle est plus facile à maintenir ?
* Quel est le rapport (ratio) des temps d'execution entre les deux procédés ? (expliquer les raisons techniques)
* Quel est le rapport (ratio) des temps d'execution entre les trois procédés ? (expliquer les raisons techniques)
Le même principe s'applique pour des programmes en R, en Matlab, en Julia (un langage/environnement qui ressemble à python mais se donne pour objectif d'être rapide à l'execution comme du C http://julialang.org), même si ce dernier se donne pour objectif d'être aussi rapide pour l'écriture "déroulée" du code - car tous les algorithmes ne peuvent pas se vectoriser.
%% Cell type:code id: tags:
``` python
image = data.coins() # Une image avec des pieces de monnaie, fournie dans la librairie python
nb_lignes, nb_colonnes = image.shape
seuil=145
def seuillage_deroule(image): # La fonction naïve
image_seuillee = np.copy(image)
for i in range(nb_lignes):
for j in range(nb_colonnes):
if image[i,j]>seuil:
image_seuillee[i,j]=255
else:
image_seuillee[i,j]=0
return(image_seuillee)
def seuillage_vectorise(image): # La fonction moins naïve
image_seuillee = np.copy(image)
image_seuillee = image>seuil
return(image_seuillee)
@jit(nopython=False)
def seuillage_deroule_jit(image): # La fonction naïve
image_seuillee = np.copy(image)
for i in range(nb_lignes):
for j in range(nb_colonnes):
if image[i,j]>seuil:
image_seuillee[i,j]=255
else:
image_seuillee[i,j]=0
return(image_seuillee)
print ("Boucle déroulée")
%timeit image_seuillee = seuillage_deroule(image)
print ("\n \n Boucle déroulée en compilation juste à temps")
%timeit image_seuillee = seuillage_deroule_jit(image)
print ("\n \n Code vectorisé")
%timeit image_seuillee = seuillage_vectorise(image)
#pour pouvoir afficher l'image seuillée
image_seuillee = seuillage_vectorise(image)
plt.figure(figsize=(11,5))
plt.subplot(1,2,1)
plt.imshow(image, cmap = cm.Greys_r)
plt.subplot(1,2,2)
plt.imshow(image_seuillee, cmap = cm.Greys_r)
```
%% Output
Boucle déroulée
222 ms ± 6.56 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Boucle déroulée en compilation juste à temps
13.7 µs ± 481 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Code vectorisé
20.5 µs ± 466 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
<matplotlib.image.AxesImage at 0x11c1a4410>
%% Cell type:markdown id: tags:
## Détection de contours
%% Cell type:markdown id: tags:
Cette section examine une manière simple de détecter des contours dans des images. Les contours peuvent ensuite servir à reconnaître l'objet selon sa forme, ou de point de vue 3D de la caméra sur cet objet, ou encore à faire un suivi de cet objet dans une séquence d'images.
On va utiliser la convolution de l'image avec des filtres de Sobel (deux filtres : un horizontal et un vertical) pour détecter la présence de contours.
* Trouver sur internet les valeurs de ces filtres de Sobel : expliquer les signes de valeurs numériques, les directionalités de ces filtres
* La convolution de l'image avec ces filtres aboutit-elle directement à une décision (binaire) par pixel ?
* Vérifier sur l'experience ci-dessous que les contours horizontaux et verticaux sont bien détectés selon le filtre employé.
* Comment combine t-on dans le code les résultats des détections horizontale et verticale ? (on pourrait procéder différemment)
* La détection d'un contour en un pixel dépend t-elle de ce qui est décidé aux pixels voisins ? Est-ce le procédé qui semble le plus intéressant pour obtenir des détections de contours fiables ? Le résultat de la détection de contour par la méthode de Canny est fourni en bas à droite.
%% Cell type:code id: tags:
``` python
from skimage import filters,feature
from skimage.color import rgb2gray
#im = data.coins() #enlever le commentaire pour utiliser cette image
im = rgb2gray(io.imread("polytech.jpg"))
#im = data.camera()
fig=plt.figure(figsize=(12,8))
plt.imshow(im, cmap = cm.Greys_r)
# Regarder si un filtrage passe-bas préalable améliore le résultat
# im=filters.gaussian(im, sigma=2)
seuil = 0.1
sx = filters.sobel_h(im)
ssx = abs(sx)>seuil
sy = filters.sobel_v(im)
ssy = abs(sy)>seuil
plt.figure(figsize=(8,8))
plt.subplot(2,2,1)
plt.imshow(sx, cmap = cm.Greys_r)
plt.subplot(2,2,2)
plt.imshow(ssx, cmap = cm.Greys_r)
plt.subplot(2,2,3)
plt.imshow(sy, cmap = cm.Greys_r)
plt.subplot(2,2,4)
plt.imshow(ssy, cmap = cm.Greys_r)
fig.tight_layout()
plt.figure(figsize=(8,8))
ss = np.maximum(ssx,ssy)
plt.imshow(ss, cmap = cm.Greys_r)
#from skimage.morphology import erosion, dilation, opening, closing
#from skimage.morphology import disk
#struct_elem = disk(4)
#dilated = dilation(ss, struct_elem)
#eroded = erosion(dilated, struct_elem)
#plt.subplot(3,2,6)
#plt.imshow(eroded, cmap = cm.Greys_r)
#plt.imshow(feature.canny(im), cmap = cm.Greys_r)
```
%% Cell type:markdown id: tags:
# Détection des pièces
%% Cell type:markdown id: tags:
Pour l'image de pièces ci-dessous, le but est de décider correctement pour chaque pixel s'il appartient à une pièce (foreground) ou au fond (background).
%% Cell type:markdown id: tags:
Chargeons une image (fournie dans le système de librairies python), affichons l'image et son histogramme
* Y a t-il, à observer l'histogramme, un seuil séparant nettement les pièces du fond ?
%% Cell type:code id: tags:
``` python
image = data.coins()
plt.figure(figsize=(11,5))
plt.subplot(1,2,1)
plt.imshow(image, cmap = cm.Greys_r)
plt.subplot(1,2,2)
plt.hist(image.flatten(),256,fc='k')
plt.xlabel('niveau de gris')
plt.ylabel('nombre de pixels')
plt.show()
```
%% Cell type:markdown id: tags:
## Détections manquées, fausses alarmes
%% Cell type:markdown id: tags:
Le code ci-dessous réalise un simple seuillage (variable `seuil` dans le code) sur le niveau de gris de chaque pixel, pour décider si ce pixel appartient à la classe 'pièces' ou non. L'image de détection est indiquée ci-dessous
* De façon générale, quels sont les deux types d'erreurs possibles dans un problème de détection ?
* Une courbe dite "ROC" est indiquée en bas à droite avec une croix sur la position correspondant au seuil réglé.
* Pour calculer cette courbe comme fait ici, a t-on besoin de disposer de la "vraie image de détection" ?
* A quelles situations correspondent les 4 coins du graphique ?
* Trouver différents réglages du seuil permettant de balayer la courbe.
* Quelle est la situation optimale ?
%% Cell type:code id: tags:
``` python
coins_truth=io.imread('coins_ground_truth.png')
nb_el = nb_lignes * nb_colonnes
#plt.figure(figsize=(10,5))
#plt.imshow(coins_truth, cmap = cm.Greys_r)
fig=plt.figure(figsize=(8,8))
seuil = 110
decision = (data.coins()>seuil).astype(int)*255
false_alarm = np.greater(decision,coins_truth)
misdetection = np.less(decision,coins_truth)
plt.subplot(2,2,1)
plt.imshow(decision, cmap = cm.Greys_r)
plt.xlabel('blanc = decision favorable à la classe "pièce" ')
plt.subplot(2,2,2)
plt.imshow(misdetection, cmap = cm.Greys_r)
plt.xlabel('blanc = détections manquées')
plt.subplot(2,2,3)
plt.imshow(false_alarm, cmap = cm.Greys_r)
plt.xlabel('blanc = fausses alarmes')
misd=[]
fal=[]
for i in range(0,255):
decision = (data.coins()>i).astype(int)*255
false_alarm = np.greater(decision,coins_truth)
misdetection = np.less(decision,coins_truth)
misd.append(1-np.count_nonzero(misdetection)/nb_el)
fal.append(np.count_nonzero(false_alarm)/nb_el)
if i==seuil:
misdx=1-np.count_nonzero(misdetection)/nb_el
faly=np.count_nonzero(false_alarm)/nb_el
plt.subplot(2,2,4)
plt.plot(fal,misd)
plt.plot(faly,misdx,'*')
plt.xlabel('taux de fausses alarmes')
plt.ylabel('taux de bonnes détection')
fig.tight_layout()
```
%% Cell type:markdown id: tags:
# Classification statistique supervisée
%% Cell type:markdown id: tags:
On va maintenant automatiser le réglage du seuil de décision, grâce à un apprentissage préalable de la distribution des niveaux de gris de chacune des deux classes.
On a découpé, à la main, deux petites images, l'une extraite du fond et l'autre extraite d'une pièce, pour constituer des données d'apprentissage.
%% Cell type:markdown id: tags:
## Partie apprentissage : modélisation de la distribution de probabilité des niveaux de gris pour chacune de ces deux classes.
%% Cell type:markdown id: tags:
On calcule la moyenne et la variance de chacun des deux échantillons et on utilise ces moyennes et variances comme paramètres de deux lois normales modélisant la distribution de probabilité des niveaux de gris pour chaque classe.
Au vu des graphiques ci-dessous :
* dans quelle mesure l'hypothèse "loi gaussienne" est-elle valable ?
* quel est le seuil de décision implicitement construit ?
Pour étudier la dépendence de la technique au choix du jeu d'apprentissage extrait à la main, on a extrait à la main d'autres zones du fond (`coins_background2.jpg,coins_background3.jpg,coins_background4.jpg`), pour voir si cela affecte les résultats.
* Revoir les réponses ci-dessous pour chacun des cas.
* Les résultats (c.a.d. la qualité) de la classification varient-ils fortement selon les cas ? Est-ce un problème ?
%% Cell type:code id: tags:
``` python
coins_background = io.imread("coins_background.jpg")
coins_foreground = io.imread("coins_foreground.jpg")
mean_background = coins_background.flatten().mean()
mean_foreground = coins_foreground.flatten().mean()
variance_background = coins_background.flatten().var()
variance_foreground = coins_foreground.flatten().var()
fig=plt.figure(figsize=(10,5))
ax=fig.add_subplot(111)
ax.hist(coins_foreground.flatten(),bins=256,range=(0,255),alpha=0.3,normed=True)
ax.hist(coins_background.flatten(),bins=256,range=(0,255),alpha=0.3,normed=True)
x = np.linspace(0,255,255)
ax.plot(x,stats.norm.pdf(x,mean_background,math.sqrt(variance_background)),label='background')
ax.plot(x,stats.norm.pdf(x,mean_foreground,math.sqrt(variance_foreground)),label='foreground')
plt.xlabel('niveau de gris')
plt.ylabel('nombre de pixels')
plt.legend()
plt.show()
```
%% Cell type:markdown id: tags:
## Classifions chacun des pixels de l'image selon le critère du maximum de vraisemblance
%% Cell type:markdown id: tags:
on peut utiliser les modèles probabilistes construits ci-dessus pour maintenant classer chaque pixel de l'image selon le critère du maximum de vraisemblance.
%% Cell type:code id: tags:
``` python
image_classification = image.copy()
# calcul de fonction gaussienne vectorisé sur une image
image_likelihood_foreground = stats.norm.pdf(image,mean_foreground,math.sqrt(variance_foreground))
image_likelihood_background = stats.norm.pdf(image,mean_background,math.sqrt(variance_background))
# opérateur de comparaison vectorisé sur une image
image_classification= image_likelihood_foreground>image_likelihood_background
plt.figure(figsize=(10,5))
im=plt.imshow(image_classification, cmap = cm.Greys_r)
plt.show()
```
%% Cell type:markdown id: tags:
## Post-traitement du résultat de classification par morphologie mathématique
%% Cell type:markdown id: tags:
De la section précédente, retenir le cas correspondant à la meilleure image de détection (même si elle est loin d'être parfaite). On va maintenant tenter de l'améliorer avec des opérations de morphologie mathématique.
* quelle opération de morphologie mathématique permet de boucher les petits trous noirs dans le résultat précédent ?
* quel est l'inconvénient de cette opération pour la précision des contours des pièces ?
* comment rétablir à peu près la bonne localisation des contours des pièces ?
* expérimenter l'inversion de l'ordre dilatation/erosion ci-dessous et des éléments structurants plus grands (changer la taille du disque). Quel réglage semble optimal ?
%% Cell type:code id: tags:
``` python
from skimage.morphology import erosion, dilation, opening, closing
from skimage.morphology import disk
struct_elem = disk(2)
dilated = dilation(image_classification, struct_elem)
eroded = erosion(dilated, struct_elem)
plt.figure(figsize=(10,5))
im=plt.imshow(eroded, cmap = cm.Greys_r)
plt.show()
```
%% Cell type:markdown id: tags:
## Classification non supervisée d'image
%% Cell type:markdown id: tags:
Dans cette section, on examine la possibilité de trouver automatiquement les régions, homogènes au sens de la couleur, composant une image. Pour cela, l'algorithme k-means est utilisé
* Essayer l'opération sur des images diverses
* Essayer avec divers nombres de régions
* Pour un nombre restreint de couleur : se représenter l'espace de couleur, les centres des groupes. Y a t-il besoin de calculer des distances entre couleurs ?
* Les régions trouvées sont-elles spatialement connexes ? Pouvez-vous trouver des avantages et inconvénients à cela ?
* Comment cette technique pourrait-elle servir à faire de la compression d'image ?
%% Cell type:code id: tags:
``` python
# Adapté de :
#
# Authors: Robert Layton <robertlayton@gmail.com>
# Olivier Grisel <olivier.grisel@ensta.org>
# Mathieu Blondel <mathieu@mblondel.org>
#
# License: BSD 3 clause
print(__doc__)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.metrics import pairwise_distances_argmin
from sklearn.datasets import load_sample_image
from sklearn.utils import shuffle
from time import time
n_colors = 15
image = io.imread("polytech.jpg")
# Convert to floats instead of the default 8 bits integer coding. Dividing by
# 255 is important so that plt.imshow behaves works well on float data (need to
# be in the range [0-1])
image = np.array(image, dtype=np.float64) / 255
# Load Image and transform to a 2D numpy array.
w, h, d = original_shape = tuple(image.shape)
assert d == 3
image_array = np.reshape(image, (w * h, d))
# Fitting model on a small sub-sample of the data
image_array_sample = shuffle(image_array, random_state=0)[:1000]
kmeans = KMeans(n_clusters=n_colors, random_state=0).fit(image_array_sample)
labels = kmeans.predict(image_array)
codebook_random = shuffle(image_array, random_state=0)[:n_colors + 1]
labels_random = pairwise_distances_argmin(codebook_random,
image_array,
axis=0)
def recreate_image(codebook, labels, w, h):
"""Recreate the (compressed) image from the code book & labels"""
d = codebook.shape[1]
image = np.zeros((w, h, d))
label_idx = 0
for i in range(w):
for j in range(h):
image[i][j] = codebook[labels[label_idx]]
label_idx += 1
return image
fig=plt.figure(figsize=(12,6))
plt.subplot(1,2,1)
plt.imshow(image)
plt.title('Image originale')
plt.subplot(1,2,2)
image_regions = recreate_image(codebook_random, labels_random, w, h)
plt.imshow(image_regions)
plt.title('Régions ')
fig.tight_layout()
```
%% Cell type:code id: tags:
``` python
from numba import jit
import numpy as np
x = np.arange(100).reshape(10, 10)
@jit(nopython=True) # Set "nopython" mode for best performance, equivalent to @njit
def go_fast(a): # Function is compiled to machine code when called the first time
trace = 0
for i in range(a.shape[0]): # Numba likes loops
trace += np.tanh(a[i, i]) # Numba likes NumPy functions
return a + trace # Numba likes NumPy broadcasting
print(go_fast(x))
```
%% Cell type:code id: tags:
``` python
```
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment