un cours de traitement d'image sans image
This commit is contained in:
parent
694e493c8f
commit
328b313a9f
5 changed files with 826 additions and 1 deletions
710
453/Cours/chap1.tex
Normal file
710
453/Cours/chap1.tex
Normal file
|
@ -0,0 +1,710 @@
|
|||
\documentclass[main.tex]{subfiles}
|
||||
\begin{document}
|
||||
\section{Introduction}
|
||||
\subsection{Domaines d'applications}
|
||||
Refaire le graphe stylé en TikZ
|
||||
\emph{Whoua c'est vachement stylé le TI}
|
||||
\subsection{Définion}
|
||||
|
||||
\begin{defin}
|
||||
\begin{itemize}
|
||||
\item Une \emph{image} est une répresentaiton continue d'une fonction $f(x,y)$ qui relie $f$ à l'intensité lumineuse du point $(x,y)$.
|
||||
\item Une \emph{Image numérique} Échantillonnage $I(x,y)$ dicret (Matrice 2D) de $f$ qui relie $I(x,y)$ à l'intensité lumineuse d'une case $(x,y)$ nommé \emph{pixel}.
|
||||
\item Une Image numérique possède également une \emph{quantification}: nombre de bits pour décrire une couleur /intensité d'un pixel.
|
||||
\end{itemize}
|
||||
\end{defin}
|
||||
|
||||
\subsection{Notation et structure}
|
||||
|
||||
Pour accéder aux pixel d'une image:
|
||||
|
||||
\begin{itemize}
|
||||
\item $w$ : nombre de colonne $i\in[0,w-1]$
|
||||
\item $h$ : nombre de lignes $j\in [0,h-1]$
|
||||
\end{itemize}
|
||||
Ainsi $I(i,j)$ représente la valeur du pixel à la $i$ème colonne et $j$ème ligne.
|
||||
|
||||
Chaque pixel possède une valeur , quantifié qui peux correspondre à :
|
||||
\begin{description}
|
||||
\item[gris] intensité comme scalaire en $[0,2^n-1]$ où $n$ est la dynamique de l'image (la plupart du temps $n=8$)
|
||||
|
||||
\item[couleur] Triplet $R,G,B$ , chaque canal correspond à un 'niveau de couleur' codé de meme manière que le gris.
|
||||
\end{description}
|
||||
|
||||
\subsection{Amélioration d'image}
|
||||
|
||||
\paragraph{Objectifs} Réhausser le contraste d'une image pour faire apparaitre les objets distinctements.
|
||||
|
||||
Pour cela on utilise des histogrammes
|
||||
|
||||
\subsubsection{Histogrammes d'une image}
|
||||
|
||||
Après quantification on peux compter les pixels de meme valeurs
|
||||
\begin{defin}
|
||||
\begin{itemize}
|
||||
\item Un \emph{histogramme} est une application :
|
||||
\[
|
||||
H:
|
||||
\begin{cases}
|
||||
[0,N-1] \to [0,wh]\\
|
||||
H(z) \mapsto card(\{(x,y)\in[0,w]\times [0,h] | I(x,y) = z \})
|
||||
\end{cases}
|
||||
\]
|
||||
\item On a ensuite \emph{l'histogramme normalisé} :
|
||||
\[
|
||||
H_n:
|
||||
\begin{cases}
|
||||
[0,N-1] \to [01]\\
|
||||
H_n(z) \mapsto H(z)/(wh)
|
||||
\end{cases}
|
||||
\]
|
||||
L'histogramme normalisé est une distribution de proba empirique.
|
||||
\item Et \emph{l'histogramme cumulé normalisé}
|
||||
\[
|
||||
H_{cn}:
|
||||
\begin{cases}
|
||||
[0,N-1] \to [0,1]\\
|
||||
H_{cn}(z) \mapsto\sum_{k=0}^{z} H_n(k)
|
||||
\end{cases}
|
||||
\]
|
||||
L'histogramme cumulé normalisé est une fonction croissante et une focntion de répartition empirique.
|
||||
\end{itemize}
|
||||
\end{defin}
|
||||
|
||||
|
||||
\begin{listing}[H]
|
||||
\begin{minted}{C}
|
||||
int H[N]; // histogramme
|
||||
float Hn[N]; // histogramme cumulé
|
||||
float Hcn[N]; // histogramme cumulé normalisé
|
||||
for (i = 0; i<N; i++){
|
||||
H[i] = 0;
|
||||
Hn[i] = 0;
|
||||
Hc[i] = 0;
|
||||
}
|
||||
// calcul de H
|
||||
for (i = 0; i<w; i++)
|
||||
for (j = 0; j<h; j++){
|
||||
int val = I(i,j);
|
||||
H[val] = H[val] + 1;
|
||||
}
|
||||
// calcul de Hn,Hcn
|
||||
Hc[0] = Hn[0] = H[0] / (w*h);
|
||||
for (i = 1; i<N; i++){
|
||||
Hn[i] = H[i] / (w*h);
|
||||
Hcn[i] = Hcn[i-1] + Hn[i];
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Calcul des histogrammes}
|
||||
\end{listing}
|
||||
|
||||
\subsubsection{Transformation de l'histogramme}
|
||||
|
||||
On souhaite conserver la relation d'ordre de l'histogramme pour $I(x_1,y_1) <= I(x_2,y_2) \implies I'(x_1,y_1) <= I'(x_2,y_2)$.
|
||||
Pour cela :
|
||||
|
||||
\begin{itemize}
|
||||
\item Étalement de la dynamique via une transformation affine:
|
||||
\[
|
||||
\boxed{
|
||||
z' = z'_{min} + (z'_{max}-z'_{min}) \frac{z-z_{min}}{z_{max}-z_{min}}
|
||||
}
|
||||
\]
|
||||
\item Quantification $z' = round(z)$
|
||||
|
||||
\end{itemize}
|
||||
|
||||
\begin{rem}
|
||||
Généralement on fais entre 0 et 255 (8 bits).
|
||||
\end{rem}
|
||||
|
||||
Une autre possibilité et de faire de l'égalisation d'histogramme pour se rapprocher d'un histogramme cumulé uniformément croissant, cela peux passer par une transformation non linéaire.
|
||||
|
||||
|
||||
\[
|
||||
z' = \frac{N-1}{wh}\sum_{i=0}^{z}H(i) = (N-1)H_{cn}(z)
|
||||
\]
|
||||
|
||||
|
||||
\subsection{Bruit}
|
||||
|
||||
\paragraph{Apparition}
|
||||
\begin{itemize}
|
||||
\item erreurs générant dans les pixels des valeurs différentes des valeurs réelles
|
||||
\item additif, multiplicatif, impulsionnel
|
||||
\item sources : capteur, transmission, interférences
|
||||
\end{itemize}
|
||||
|
||||
\paragraph{Bruit gaussien}
|
||||
\begin{itemize}
|
||||
\item bon modèle pour bruit capteurs :
|
||||
\[ \rho(\eta)= \frac{1}{\sqrt{2\pi}\sigma}\exp(-\frac{(\eta-\mu)^2}{2\sigma^2})\]
|
||||
\item exemple en astronomie : sur l'exemple on a $\sigma=64$ (très élevé, pour des valeurs comprises entre 0 et 255). On moyenne donc sur plusieurs acquisitions pour augmenter la qualité (bruit gaussien de moyenne nulle). Pour 128 mesures, on divise l'écart-type par $\sqrt{128}$.
|
||||
\end{itemize}
|
||||
|
||||
\paragraph{Bruit multiplicatif}
|
||||
\begin{itemize}
|
||||
\item images radar, laser
|
||||
\item effets photochimiques (bruit "grain")
|
||||
\item $I(i,j)=I_0(i,j)\eta(i,j)$ avec $E[\eta]=1$
|
||||
\end{itemize}
|
||||
|
||||
\paragraph{Bruit convolutif}
|
||||
\begin{itemize}
|
||||
\item effet de flou
|
||||
\item défaut de mise au point
|
||||
\item mouvement rapide de la caméra
|
||||
\item $I(i,j)=I_0(i,j)*g+\eta(i,j)$
|
||||
\item déconvolution (Fergus et al.) : il faut avoir le mouvement ("masque") pour pouvoir déconvoluer. Par minimisation de l'énergie, on essaye de trouver cette trajectoire de l'échec.
|
||||
\end{itemize}
|
||||
|
||||
\section{Filtrage}
|
||||
\paragraph{Caractéristiques}
|
||||
\begin{itemize}
|
||||
\item processus qui élimine une composante indésirable d’un signal
|
||||
\item parfois utilisé pour créer un effet artistique etc.
|
||||
\item en général associé à une perte d’information
|
||||
\item utilise le voisinage du pixel pour calculer sa nouvelle valeur
|
||||
classifications très variées :
|
||||
\begin{itemize}
|
||||
\item filtrage linéaire et non-linéaire
|
||||
\item filtrage passe-bas, passe-bande et passe-haut
|
||||
\item etc.
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\section{Filtrage Linéaire}
|
||||
\subsection{Généralités}
|
||||
\subsubsection{Formulation de Base (1D)}
|
||||
\begin{defin}
|
||||
Un filtre linéaire réalise une opération de convolution en le signal $x(t)$ et la réponse impulsionnelle $h(t)$
|
||||
|
||||
\[
|
||||
(x \star h) (t) = \int_{-\infty}^{+\infty}x(\tau)h(t-\tau)d\tau
|
||||
\]
|
||||
\end{defin}
|
||||
\begin{prop}
|
||||
Le produit de convolution est:
|
||||
\begin{itemize}
|
||||
\item commutatif
|
||||
\item distributif
|
||||
\item associatif
|
||||
\item Équivalent à un produit classique dans le domaine fréquentiel:
|
||||
\[
|
||||
x \star h = \mathcal{F}^{-1}[\mathcal{F}(x)\mathcal{F}(h)]
|
||||
\]
|
||||
\end{itemize}
|
||||
\end{prop}
|
||||
|
||||
\subsubsection{Utilisation en TI (2D discret)}
|
||||
|
||||
\begin{defin}
|
||||
Pour une image $I$ et un filtre $g$:
|
||||
|
||||
\[
|
||||
(I\star g)(i,j) =\sum_{n=-\infty}^{+\infty}\sum_{m=-\infty}^{+\infty}I(i-n,j-m)g(n,m)
|
||||
\]
|
||||
En général le support de $g$ est compact, de dimension impaire.
|
||||
\end{defin}
|
||||
|
||||
\subsection{Filtres courants pour réduire le bruit}
|
||||
|
||||
\begin{prop}[Filtre moyenneur]
|
||||
Le niveau de gris du pixel central est remplacé apr la moyenne des niveaux de gris des pixels environnants.
|
||||
\[
|
||||
w = \frac{1}{9}
|
||||
\begin{pmatrix}
|
||||
1 &1 &1\\
|
||||
1 &1 &1\\
|
||||
1 &1 &1
|
||||
\end{pmatrix}
|
||||
\]
|
||||
\end{prop}
|
||||
\begin{prop}[Filtre Gaussien]
|
||||
le niveau de gris du pixel central est remplacé par la moyenne des niveaux de
|
||||
gris des pixels environnants, pondérée par une Gaussienne 2D centrée dans ce pixel.
|
||||
\begin{itemize}
|
||||
\item filtre lisseur (donc passe-bas)
|
||||
\item taille du support en fonction du paramètre $\sigma : l = Int + (3\sigma)$
|
||||
\item élimine moins brutalement les hautes fréquences et préserve mieux les détails
|
||||
\end{itemize}
|
||||
|
||||
\end{prop}
|
||||
\begin{exemple}[pour $\sigma = 0.625, l = 2$]
|
||||
\[
|
||||
w = 0.4\times10^{-2}\times
|
||||
\begin{pmatrix}
|
||||
0.03& 0.16& 5.98& 0.16&0.03\\
|
||||
0.16& 7.7 &27.8 &7.7&0.16\\
|
||||
5.98& 27.8& 100 &27.8&5.98\\
|
||||
0.16& 7.7 &27.8 &7.7&0.16\\
|
||||
0.03& 0.16& 5.98 &0.16&0.03\\
|
||||
\end{pmatrix}
|
||||
\]
|
||||
\end{exemple}
|
||||
|
||||
\begin{prop}[Filtre médian]
|
||||
\begin{itemize}
|
||||
\item remplace par la valeur médiane de tous les pixels de la fenêtre d’analyse centrée sur le pixe
|
||||
\item filtre non-linéaire, plus coûteux
|
||||
\item très bien adapté au bruit impulsionnel
|
||||
\end{itemize}
|
||||
\end{prop}
|
||||
|
||||
\subsection{Filtre de sobel}
|
||||
|
||||
\begin{defin}
|
||||
On défini les dérivées discrètes:
|
||||
\[
|
||||
\begin{cases}
|
||||
I_x[x,y] = I[x+1,y]-I[x-1,y]\\
|
||||
I_y[x,y] = I[x,y+1]-I[x,y-1]\\
|
||||
\end{cases}
|
||||
\]
|
||||
\end{defin}
|
||||
\begin{rem}
|
||||
\begin{itemize}
|
||||
\item Le calcul de la dérivé est equivalent a une convolution avec
|
||||
$\vect{-1 & 0 &1}$ resp. $\vect{-1 & 0 &1}^T$
|
||||
\item Le filtre est sensible est bruit et lisse dans la direction orthogonale.
|
||||
\end{itemize}
|
||||
\end{rem}
|
||||
|
||||
|
||||
\begin{prop}[Sobel]
|
||||
On Utilise les deux filtres suivants pour déterminer les dérivées dans chacune des deux directions:
|
||||
\[
|
||||
H_x =
|
||||
\begin{pmatrix}
|
||||
-1 & 0 &1 \\
|
||||
-2 & 0 &1\\
|
||||
-1 & 0 &1
|
||||
\end{pmatrix}
|
||||
\quad \quad
|
||||
H_y =
|
||||
\begin{pmatrix}
|
||||
-1 & -2&-1 \\
|
||||
0 & 0 & 0\\
|
||||
1 & 2 & 1
|
||||
\end{pmatrix}
|
||||
\]
|
||||
|
||||
|
||||
On en déduit alors la \emph{magnitude du gradient}:
|
||||
|
||||
\[
|
||||
\|\nabla I \| = \sqrt{(I\star H_x)^2+(I\star H_y)^2}
|
||||
\]
|
||||
et son \emph{orientation}:
|
||||
|
||||
\[
|
||||
\theta = \arctan( \frac{I \star H_y}{I \star H_x})
|
||||
\]
|
||||
\end{prop}
|
||||
|
||||
|
||||
\subsection{Implémentation du filtrage}
|
||||
\begin{itemize}
|
||||
\item complexité d'un algorithme : très importante en systèmes embarqués
|
||||
\item classes d'algorithmes :
|
||||
\begin{description}
|
||||
\item[sub-linéaires] en général $O(log(n))$ : optimal (par exemple dichotomie)
|
||||
\item[linéaires] en $O(n)$. À noter que $O(nlog(n))$ est aussi considéré comme "rapide"
|
||||
\item[autres] plus longs
|
||||
\end{description}
|
||||
\item séparabilité : permet de réduire le nombre d'opérations nécessaires.
|
||||
\item image intégrale : calcul linéaire en nombre de pixels (en calculant à l'aide des termes précédents)
|
||||
\[ \sum_{n=a}^b \sum_{m=c}^d I(n,m) = IN(b,d) - IN(a,d) - IN(b,c) + IN(a,c) \]
|
||||
La somme d'un rectangle : c'est donc seulement 4 opérations si on a fait le calcul de l'image intégrale auparavant.
|
||||
\end{itemize}
|
||||
|
||||
Petit exo : quelle est la condition pour qu'un filtre 2D soit séparable ?
|
||||
|
||||
\section{Filtrage pour la détection de contours}
|
||||
\subsection{Filtre passe-haut : Laplacien}
|
||||
|
||||
\paragraph{Généralité}
|
||||
\begin{defin}
|
||||
On appelle laplacien l'opérateur différentiel :
|
||||
\[
|
||||
\Delta f = \divv( \grad(f)) = \nabla\cdot \nabla f = \nabla^2 f = \sum_{i=1}^{n}\derivpp[f]{x_i}
|
||||
\]
|
||||
Le laplacien est étroitement lié aux phénomènes de diffusion, il représente le taux de variation moyen de $f(x)$ sur une sphère centrée en $x$ quand la sphère varie.
|
||||
\end{defin}
|
||||
|
||||
\begin{prop}[Laplacien d'une image]
|
||||
\[ \nabla^2 I = \derivpp[I]{x_i} + \derivpp[I]{y_i} = I_{xx} + I_{yy} \]
|
||||
Le masque qui correspond au calcul de $I_{xx}$ est
|
||||
\[ L_{xx} = \vect{ 1 & -2 & 1} \]
|
||||
On a donc le masque suivant
|
||||
\[ L =
|
||||
\begin{pmatrix}
|
||||
0 & 1 & 0\\
|
||||
1 & -4 & 1\\
|
||||
0 & 1 & 0
|
||||
\end{pmatrix}
|
||||
\]
|
||||
\end{prop}
|
||||
\emph{Calcul du masque}
|
||||
|
||||
Développement de Taylor :
|
||||
\[f(x+1) = f(x) + f'(x) + \frac{1}{2}f''(x)\]
|
||||
|
||||
Or, on a $f'(x)=f(x)-f(x-1)$ donc
|
||||
\[ f''(x) = 2(f(x+1)-2f(x)+f(x-1) \]
|
||||
|
||||
\begin{rem}
|
||||
\begin{itemize}
|
||||
\item Ce masque est à symétrie centrale, ne dépend pas (en théorie) de la direction de référence. Ce filtre est invariant par rotation.
|
||||
\item Détection de contour : passage par zéro du Laplacien. Plus le contour est net, plus l'amplitude autour de zéro du laplacien est grande.
|
||||
\item Mais le Laplacien est \emph{très} sensible au bruit.
|
||||
\end{itemize}
|
||||
\begin{prop}
|
||||
Pour limiter les effet du bruits on peux filtrer avec une gaussienne avant d'appliquer le laplacien.
|
||||
|
||||
Avec les propriétés de la convolution on peux directement convolué avec la dérivé seconde du laplacien (LoG) fonction ``chapeau mexicain''
|
||||
\end{prop}
|
||||
|
||||
|
||||
\end{rem}
|
||||
|
||||
\subsection{Diffusion dans une image}
|
||||
|
||||
\subsubsection{Diffusion isotrope}
|
||||
Le filtre Gaussien est équivalente au processus de diffusion \textbf{isotrope linéaire} suivant :
|
||||
\[ \derivp[u]{t}=\divv (d\nabla u) \]
|
||||
|
||||
Le processus de diffusion \textbf{non linéaire isotrope} s'écrit :
|
||||
\[ \derivp[u]{t}=\divv (D(x,y)\nabla u) \]
|
||||
|
||||
Cependant, le flux de diffusion est toujours parallèle au gradient !
|
||||
|
||||
|
||||
Choix du coefficient de diffusion :
|
||||
\[D(x,y)=\frac{1}{1+\frac{|\nabla u(x,y)|^2}{\lambda^2}}\]
|
||||
|
||||
$\lambda$ est le paramètre de contraste.\\
|
||||
|
||||
Autre coefficient possible :
|
||||
\[D(x,y)=e^{\frac{|\nabla u(x,y)|^2}{\lambda^2}}\]
|
||||
|
||||
\subsubsection{Diffusion anisotrope} On veut diffuser perpendiculairement au gradient (selon les contours) :
|
||||
\[ \derivp[u]{t}=\divv (T(x,y)\nabla u) \]
|
||||
|
||||
$T(x,y)$ tenseur de diffusion, dont les vecteurs propres perpendiculaires donnent les directions de diffusions, et les valeurs propres la diffusivité dans les directions respectives.
|
||||
|
||||
\subsubsection{Filtrage (gaussien) bilatéral}
|
||||
\begin{defin}
|
||||
Le filtre gaussien peux s'écrire
|
||||
\[I_G(p)=\sum_{q\in S}I(q).G_{\sigma_s}(|p-q|)\]
|
||||
\end{defin}
|
||||
|
||||
Sur un contour, la distance spatiale entre $p$ et $q$ est faible, mais la différence de poids est grande. On risque donc de faire tout baver en ne pénalisant que la distance dans l'espace image.
|
||||
\begin{prop}
|
||||
On fait intervenir un second terme qui pénalise la distance dans l'espace des "poids" des pixels. On empêche les pixels de valeur très différente d'intervenir dans le filtrage.
|
||||
\[I_G(p)=\sum_{q\in S}I(q).G_{\sigma_s}(|p-q|).G_{\sigma_r}(|I(p)-I(q)|)\]
|
||||
|
||||
\end{prop}
|
||||
\subsection{Application : Détection de droites}
|
||||
|
||||
Une fois qu'on a détecté les contours (on a une image binaire), on veut essayer de détecter des droites.\\
|
||||
\begin{defin}
|
||||
|
||||
On représente une droite donnée par un couple de valeur $(r,\theta)$.
|
||||
|
||||
L'espace de représentation des droites est donc $[0,2\pi]\times[0,r_{max}]$
|
||||
|
||||
Équation des droites passant par $(x_0,y_0)$ : $r=x_0\cos\theta + y_0\sin\theta$
|
||||
\end{defin}
|
||||
|
||||
|
||||
On se fixe $(x_0,y_0)$ et dans l'espace de représentation des droites, l'ensemble des droites passant par $(x_0,y_0)$ est $r=x_0\cos\theta + y_0\sin\theta$ et donne une sinusoïde.
|
||||
|
||||
Avec un second couple $(x_1,y_1)$, on a une autre sinusoïde.\\
|
||||
|
||||
Pour tous les points potentiellement sur des droites, on trace les sinusoïdes dans l'espace de représentation des droites, puis on regarde les méga-intersections.
|
||||
|
||||
A retenir : on peut utiliser cette méthode dès qu'on peut décrire les formes par des ensembles de paramètres. On fait un système de "vote" et c'est la droite qui a le plus de votes qui est élue Miss Droite.
|
||||
|
||||
|
||||
\subsection{Application: Detection de cercle}
|
||||
|
||||
De la meme manière qu'on peux détecter les droites (2 paramètres) on peux détecter des cercles avec la transformée de Hough.
|
||||
|
||||
\begin{defin}
|
||||
Dans cette méthode, un cercle est décrit par son équation cartésienne:
|
||||
\[
|
||||
(x – a)2 + (y – b)2 = r2
|
||||
\]
|
||||
où le point de coordonnées $(a, b)$ est le centre du cercle et $r$ en est le rayon.
|
||||
\end{defin}
|
||||
|
||||
\paragraph{Méthode}
|
||||
|
||||
Dans l'espace (a, b, r), un cercle est caractérisé par un point. L'ensemble des cercles passant par un point M(x, y) donné forme un cône de sommet (a = x, b = y, r = 0) et d'axe r. Un « bon candidat » correspond à l'intersection de plusieurs cônes.
|
||||
|
||||
Si le rayon du cercle recherché est connu, on peut alors se placer dans le plan (a, b). Dans ce plan, l'ensemble des cercles passant par M est décrit par le cercle de centre (a = x, b = y) et de rayon r. Un bon candidat est donc à l'intersection de plusieurs cercles. On construit une matrice d'accumulation A : chaque élément Ai, j de la matrice contient le nombre de cercles passant par le point, ou bien par un carré de plusieurs pixels, correspondant à cet élément.
|
||||
|
||||
Si le rayon est inconnu, la méthode de recherche consiste à construire une hypermatrice d'accumulation dont chaque cellule Ai, j, k correspond à un cube de l'espace (a, b, r), en balayant tous les rayons possible de 1 pixel jusqu'à la dimension de l'image.
|
||||
|
||||
\begin{listing}
|
||||
\inputminted{python}{hough.py}
|
||||
\caption{Création de l'accumulateur de Hough et détection des maximums}
|
||||
\end{listing}
|
||||
|
||||
\section{Points d'intérêt}
|
||||
|
||||
\subsection{Nécessité de l'invariance en TI}
|
||||
|
||||
Les contours : traitement peu coûteux, détection robuste de courbes paramétriques mais pas suffisamment invariant.
|
||||
|
||||
\paragraph{Objectif} identifier des structures invariantes par rapport aux rotations, changements échelle, etc.
|
||||
|
||||
\subsection{Détection de coins}
|
||||
\subsubsection{Les bases : Détecteur de Harris}
|
||||
\begin{defin}
|
||||
Un \emph{coin} est endroit de l'image qui présente une forte variation d'intensité en deux directions différentes.
|
||||
\end{defin}
|
||||
|
||||
\paragraph{Stratégie : } Le contenu d'un patch centré dans le coin devrait varier dans toutes les directions.
|
||||
|
||||
Déplacement du patch
|
||||
\begin{itemize}
|
||||
\item dans une zone homogène : pas coin
|
||||
\item le long d'un contour : pas coin
|
||||
\item près d'un coin : coin !
|
||||
\end{itemize}
|
||||
|
||||
On cherche donc un changement d'intensité par shift de ($\Delta x,\Delta y$):
|
||||
\begin{defin}
|
||||
On utilise la fonction d'estimation de coins :
|
||||
\[
|
||||
E(x,y,\Delta x,\Delta y) = \sum_{x}^{}\sum_{y}^{}\underbracket{w(x,y)}_{support}\left[ \underbracket{I(x,y)}_{\text{Intensité}}-\underbracket{I(x+\Delta x,y+\Delta y)}_{\text{Intensite shifée}} \right]^2
|
||||
\]
|
||||
plus E est grand et plus on a de chance de trouver un coin.
|
||||
\end{defin}
|
||||
|
||||
Cette methode est lourde en calcul et peux etre rendu plus rapide:
|
||||
|
||||
\begin{prop}
|
||||
En faisant une approx au premier ordre de la fonction on a :
|
||||
|
||||
\[
|
||||
E(x,y) = \vect{\Delta x & \Delta y}
|
||||
\underbrace{\begin{bmatrix}
|
||||
<I_x^2> & <I_xI_y> \\
|
||||
<I_xI_y> & <I_y^2>
|
||||
\end{bmatrix}}_{\text{ tenseur de structure}}
|
||||
\vect{\Delta x \\ \Delta y}
|
||||
\]
|
||||
\end{prop}
|
||||
|
||||
\begin{rem}
|
||||
Le tenseur de structure est composé des dérivée de l'image sur le patch moyennée. Les vecteurs propres indiquent les direction principales de variation de gradient
|
||||
dans le voisinage du point (voir l’ellipse du changement constant)
|
||||
\end{rem}
|
||||
|
||||
\paragraph{Méthode} :
|
||||
On peux calculer $\lambda_1$ et $\lambda_2$ explicitement mais c'est lourd. On préfère utiliser :
|
||||
\[
|
||||
R = \det(M) - \alpha tr(M)^2 = \lambda_1\lambda_2 -\alpha(\lambda_1+\lambda_2)^2
|
||||
\]
|
||||
|
||||
Et on choisi $\alpha \in[0.04; 0.06]$. Les valeurs propres intéressante sont alors des maximums locaux de $R$.
|
||||
|
||||
\paragraph{Algorithme}
|
||||
\begin{itemize}
|
||||
\item Calcul des gradients gaussien de l'image
|
||||
\item Calcul du tenseur des structures
|
||||
\item Calcul de $R$
|
||||
\item Seuillage et suppression des valeurs non maximales.
|
||||
\end{itemize}
|
||||
\begin{listing}
|
||||
\inputminted{python}{harris.py}
|
||||
\end{listing}
|
||||
|
||||
|
||||
\paragraph{Comment gérer le changement d'échelle ?} Algorithme qui détecte en chaque endroit de l'image l'échelle qui ferait que cet endroit serait un coin.
|
||||
|
||||
|
||||
\subsection{Détecteur FAST}
|
||||
\subsection{Détecteur SIFT}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
\section{La couleur}
|
||||
|
||||
\subsection{Les descripteurs}
|
||||
|
||||
En niveaux de gris, le descripteur le plus simple est la luminosité. Un autre descripteur possible (Local Binary Pattern LBP) prend le voisinage 3x3 d'un pixel et fait un seuil des valeurs des pixels par rapport au pixel central.
|
||||
|
||||
En couleurs, c'est plus complexe car il y a 3 canaux.\\
|
||||
|
||||
La couleur n'est pas forcément un atout car l'information supplémentaire n'est pas nécessairement un atout. En général, les algorithmes de traitement de couleurs sont construits pour des applications particulières : par exemple, suppression fond vert.\\
|
||||
|
||||
\paragraph{Capteurs RGB} on peut difficilement réaliser un système permettant de séparer les canaux pour les envoyer sur 3 capteurs différents. On réalise donc une matrice du type :
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{cccccc}
|
||||
R & V & R & V & R & V \\
|
||||
V & B & V & B & V & B \\
|
||||
R & V & R & V & R & V \\
|
||||
V & B & V & B & V & B \\
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
||||
En 1 point donné, on interpole entre les données voisines pour les différents canaux.
|
||||
Il y a plus de capteurs verts car c'est la couleur à laquelle l'oeil humain est le plus sensible.\\
|
||||
|
||||
Avantage : l'image RGB a donc la taille d'une image en niveaux de gris. \\
|
||||
|
||||
Inconvénient : sur les contours, on aura des aberrations à cause des interpolations. Par exemple, à la frontière entre une zone rouge et une zone verte, on fera apparaître du jaune.
|
||||
|
||||
\paragraph{Sources lumineuses} on caractérise la lumière de manière spectrale, et on a don une grande variétés de spectres d'émission selon les sources. Donc en ne changeant ni l'objet, ni l'observateur, on peut avoir de grandes différences dans ce qui sera observé en fonction de l'éclairage "ambiant".
|
||||
|
||||
\paragraph{Perception} l'important n'est pas ce qui compose l'environnement mais la manière dont on le perçoit. Par exemple : un damier sur laquelle une ombre est projetée, le cerveau humain sait que les carrés blancs sont toujours plus foncés que les gris, mais ce n'est pas forcément vrai entre un carré blanc "à l'ombre" et un carré gris dans la lumière.
|
||||
|
||||
\paragraph{Descripteurs} principe de la trichromie : 3 couleurs primaires permettent de reproduire la quasi-totalité des couleurs
|
||||
|
||||
\begin{itemize}
|
||||
\item Observateur standard RGB 1931 : il existe une partie négative de la partie rouge, qui n'est pas cohérente avec la théorie additive.
|
||||
\item Observateur standard CIE 1931 :
|
||||
\[ \vect{ X \\ Y \\ Z } = \frac{1}{0.17697}
|
||||
\begin{bmatrix}
|
||||
0.49 & 0.31 & 0.20\\
|
||||
0.17697 & 0.81240 & 0.01063\\
|
||||
0 & 0.01 & 0.99
|
||||
\end{bmatrix}
|
||||
\vect{ R \\ G \\ B } \]
|
||||
Le $Y$ est proche du $V$, le $Z$ est proche du $B$.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Seuillage optimal méthode de Otsu}
|
||||
|
||||
la méthode d'Otsu est utilisée pour effectuer un seuillage automatique à partir de la forme de l'histogramme de l'image1, ou la réduction d'une image à niveaux de gris en une image binaire. L'algorithme suppose alors que l'image à binariser ne contient que deux classes de pixels puis calcule le seuil optimal qui sépare ces deux classes afin que leur variance intra-classe soit minimale.
|
||||
|
||||
\begin{prop}
|
||||
On cherche le seuil $t$ tel que :
|
||||
\[
|
||||
\arg\max \omega_1(t)\omega_2(t)[\mu_1(t)-\mu_2(t)]^2
|
||||
\]
|
||||
Avec:
|
||||
\begin{itemize}
|
||||
\item $\omega_1(t) = \sum_{i=0}^{t}H_n(i)$
|
||||
\item $\mu_1(t) = \frac{1}{\omega_1(t)}\sum_{i=0}^{t} i \cdot H_n(i)$.
|
||||
\item idem pour $\omega_2$ et $\mu_2$.
|
||||
\end{itemize}
|
||||
\end{prop}
|
||||
\section{Estimation}
|
||||
|
||||
À partir d'une image on cherche à estimer un ensemble $\theta$ de paramètres (déplacememt, état d'un d'un système )
|
||||
|
||||
\paragraph{Source du problème}
|
||||
On ne pas ignorer les outliers et déterminer les paramètre du modèle. Les methodes de tyoe moindres carré sont très sensibles aux outliers à cause de la fonction d'erreur quadratique associée.
|
||||
|
||||
Différentes approches sont possibles:
|
||||
|
||||
\subsection{Analyse de l'ensemble des résidus}
|
||||
|
||||
On utilise des variation de la méthode des moindres carrés:
|
||||
|
||||
\begin{prop}[Least Median of Squares]
|
||||
On remplace la somme par la médiane :
|
||||
\[
|
||||
\theta = \arg\min_{\theta}~\text{med } \rho(r_i)
|
||||
\]
|
||||
\end{prop}
|
||||
|
||||
\begin{prop}[Least Trimmed Squares]
|
||||
On tri les résidus et on en sélectionne $M$ où $N/2 < M< N$.
|
||||
\[
|
||||
\theta \arg \min \sum_{i=0}^{M}\rho(r_i)
|
||||
\]
|
||||
\end{prop}
|
||||
|
||||
Une autre alternative est d'utilisé une autre fonction de cout (cf UE 451),qui doit rester symétrique définie positive
|
||||
|
||||
\subsection{RANSAC}
|
||||
\begin{defin}
|
||||
`` RANSAC, abréviation pour RANdom SAmple Consensus, est une méthode pour estimer les paramètres de certains modèles mathématiques. Plus précisément, c'est une méthode itérative utilisée lorsque l'ensemble de données observées peut contenir des valeurs aberrantes (outliers). Il s'agit d'un algorithme non-déterministe dans le sens où il produit un résultat correct avec une certaine probabilité seulement, celle-ci augmentant à mesure que le nombre d'itérations est grand. L'algorithme a été publié pour la première fois par Fischler et Bolles en 1981''
|
||||
\end{defin}
|
||||
|
||||
\paragraph{Algorithme}~\\
|
||||
\textbf{entrées :}\vspace{-1em}
|
||||
\begin{verbatim}
|
||||
data - un ensemble d'observations
|
||||
modele - un modèle qui peut être ajusté à des données
|
||||
n - nb min de données nécessaires pour ajuster le modèle
|
||||
k - nb max d'itérations de l'algorithme
|
||||
t - seuil d'appartenance au modèle
|
||||
d - seuil de données nécessaire pour valider le modèle
|
||||
\end{verbatim}
|
||||
\textbf{sorties :}\vspace{-1em}
|
||||
\begin{verbatim}
|
||||
meilleur_modèle - les paramètres du modèle qui correspondent le mieux aux données
|
||||
meilleur_ensemble_points - données à partir desquelles ce modèle a été estimé
|
||||
meilleure_erreur - l'erreur de ce modèle par rapport aux données
|
||||
\end{verbatim}
|
||||
|
||||
\begin{listing}[H]
|
||||
\begin{verbatim}
|
||||
itérateur := 0
|
||||
meilleur_modèle := aucun
|
||||
meilleur_ensemble_points := aucun
|
||||
meilleure_erreur := infini
|
||||
tant que itérateur < k
|
||||
points_aléatoires := n valeurs choisies au hasard à partir des données
|
||||
modèle_possible := paramètres du modèle correspondant aux points_aléatoires
|
||||
ensemble_points := points_aléatoires
|
||||
|
||||
Pour chaque point des données pas dans points_aléatoires
|
||||
si le point s'ajuste au modèle_possible avec une erreur inférieure à t
|
||||
Ajouter un point à ensemble_points
|
||||
|
||||
si le nombre d'éléments dans ensemble_points est > d
|
||||
(ce qui implique que nous avons peut-être trouvé un bon modèle,
|
||||
on teste maintenant dans quelle mesure il est correct)
|
||||
modèle_possible := paramètres du modèle réajusté sur ensemble_points
|
||||
erreur := ecart entre données et modèle.
|
||||
si erreur < meilleure_erreur
|
||||
(nous avons trouvé un modèle qui est mieux que tous les précédents,
|
||||
le garder jusqu'à ce qu'un meilleur soit trouvé)
|
||||
meilleur_modèle := modèle_possible
|
||||
meilleur_ensemble_points := ensemble_points
|
||||
meilleure_erreur := erreur
|
||||
|
||||
incrémentation de l’itérateur
|
||||
|
||||
retourne meilleur_modèle, meilleur_ensemble_points, meilleure_erreur
|
||||
\end{verbatim}
|
||||
\caption{Algorithme de la méthode randsac}
|
||||
\end{listing}
|
||||
|
||||
|
||||
\subsection{La segmentation}
|
||||
\emph{poly de l'ENSTA très bien fait}
|
||||
|
||||
La segmentation consiste à regrouper les différents pixels de l'image en un nombre (donné) de région (peut aussi consister a une taille de région maximale, les deux, etc...). En pratique on réalise une partition de l'image
|
||||
|
||||
|
||||
\subsection{Segmentation par découpage}
|
||||
\begin{defin}
|
||||
L'idée des algorithmes de type « Split \& Merge » est de produire
|
||||
automatiquement une partition initiale en régions petites (Split), qui vont ensuite croître en se regroupant (Merge)
|
||||
\begin{itemize}
|
||||
\item La partition initiale (Split) est réalisée en \emph{divisant récursivement} l'image en régions de tailles identiques lorsqu'un certain critère d'homogéneité n'est pas satisfait: \emph{$R$ est divisée} (ex: si $\sigma_R> seuil$)
|
||||
|
||||
\item Lors de cette phase, le graphe d'adjacence, ou Region Adjacency Graph (RAG) est créé : à chaque région est associé un sommet du graphe, et des arêtes relient les sommets correspondants à deux régions qui se touchent.
|
||||
\item La \emph{phase de regroupement} (Merge) utilise le RAG pour modifier la partition initiale : pour chaque sommet R du RAG, on cherche s'il existe un sommet $R'$ voisin dans le RAG et de valeur suffisamment proche, et si c'est le cas, on les fusionne:
|
||||
$R et R'$ sont fusionnées si $\mu_{R}-\mu_{R'} < seuil$
|
||||
\end{itemize}
|
||||
\end{defin}
|
||||
|
||||
|
||||
\end{document}
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: "main"
|
||||
%%% End:
|
45
453/Cours/harris.py
Normal file
45
453/Cours/harris.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
def harris(image, sigma=10, kappa=0.04):
|
||||
derivees = gradient(image)
|
||||
d_x = derivees[0]
|
||||
d_y = derivees[1]
|
||||
noyau = noyau_gaussien3(sigma)
|
||||
#TODO: convol DoG
|
||||
d_xx_lissee = ndimage.convolve(d_x * d_x,noyau , mode = 'nearest')
|
||||
d_yx_lissee = ndimage.convolve(d_x * d_y,noyau , mode = 'nearest')
|
||||
d_yy_lissee = ndimage.convolve(d_y * d_y,noyau , mode = 'nearest')
|
||||
|
||||
determinant = d_xx_lissee * d_yy_lissee -d_yx_lissee * d_yx_lissee
|
||||
trace = d_xx_lissee + d_yy_lissee
|
||||
|
||||
image_harris = determinant - kappa * trace * trace
|
||||
return image_harris
|
||||
|
||||
def maxlocal(image_harris, seuil,size_patch=10):
|
||||
""" Array*float -> Array """
|
||||
output = np.zeros(image_harris.shape)
|
||||
voisinage = np.zeros((3,3))
|
||||
for ligne in range(size_patch,image_harris.shape[0]-size_patch):
|
||||
for colonne in range(size_patch,image_harris.shape[1]-size_patch):
|
||||
current_val = image_harris[ligne, colonne]
|
||||
voisinage = image_harris[ligne - 1: ligne + 2, colonne - 1:colonne + 2].copy()
|
||||
voisinage[(1,1)] = 0
|
||||
output[ligne, colonne] = np.logical_and(current_val > seuil,
|
||||
current_val > np.amax(voisinage))
|
||||
print(output)
|
||||
return(output)
|
||||
|
||||
def coord_maxlocal(image,seuil,size_patch=3):
|
||||
#seuil=np.mean(image_extrema)
|
||||
""" Array -> Array """
|
||||
x,y = np.nonzero(maxlocal(image,seuil))
|
||||
return np.array((y,x)).T
|
||||
|
||||
def get_corners(pict):
|
||||
image_harris = harris(pict,sigma=3,kappa=0.06)
|
||||
corners = coord_maxlocal(image_harris,np.mean(image_harris))
|
||||
return corners
|
||||
|
||||
pict1 = np.array(Image.open("set1-1.png").convert('L'),dtype='float')
|
||||
pict2 = np.array(Image.open("set1-2.png").convert('L'),dtype='float')
|
||||
corners1=get_corners(pict1)
|
||||
corners2=get_corners(pict2)
|
40
453/Cours/hough.py
Normal file
40
453/Cours/hough.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
def calcul_acc_cercles(img_s, rad_min=5, rad_max=100):
|
||||
#init acc :
|
||||
[c_max, r_max] = img_s.shape
|
||||
r_min=1
|
||||
delta_r = 1
|
||||
N_r = int((r_max-r_min+delta_r)/delta_r)
|
||||
delta_c = 1
|
||||
c_min = 1
|
||||
N_c = int((c_max-c_min+delta_c)/delta_c)
|
||||
delta_rad = 1
|
||||
N_rad = int((rad_max-rad_min+delta_rad)/delta_rad)
|
||||
acc = np.zeros((N_rad,N_r,N_c))
|
||||
print("Taille de l'accumulateur" + str(acc.shape))
|
||||
x,y = np.nonzero(img_s)
|
||||
for i in range(len(x)):
|
||||
for r in range(r_min, r_max+1, delta_r):
|
||||
for c in range(c_min, c_max+1, delta_c):
|
||||
if (x[i],y[i]) != (r,c):
|
||||
rad = np.sqrt((r-x[i])**2 + (c-y[i])**2)
|
||||
if rad < rad_max and rad > rad_min:
|
||||
i_id = int((r-r_min)/delta_r)
|
||||
j_id = int((c-c_min)/delta_c)
|
||||
k_id = int((rad-rad_min)/delta_rad)
|
||||
acc[k_id,i_id,j_id] += 1 /rad
|
||||
return acc
|
||||
def cherche_N_maxima_cercles(accumulateur, exclusion_xy,
|
||||
exclusion_rayon, N):
|
||||
accu2 = np.copy(accumulateur)
|
||||
[rad_max0, c_max0, r_max0] = accumulateur.shape
|
||||
liste_max = []
|
||||
for cercle in range(N):
|
||||
[rayon, w_0, h_0] = np.unravel_index(np.argmax(accu2),accu2.shape)
|
||||
rayon += 5
|
||||
w_0 += 1
|
||||
h_0 += 1
|
||||
accu2[rayon - exclusion_rayon : rayon + exclusion_rayon,
|
||||
w_0 -exclusion_xy : w_0 + exclusion_xy,
|
||||
h_0 - exclusion_xy : h_0 + exclusion_xy] =0
|
||||
liste_max.append([rayon, w_0, h_0])
|
||||
return liste_max
|
29
453/Cours/main.tex
Normal file
29
453/Cours/main.tex
Normal file
|
@ -0,0 +1,29 @@
|
|||
\documentclass[openany]{../../cours}
|
||||
\usepackage{../../raccourcis}
|
||||
% bug avec minted sinon
|
||||
\let\framed\relax \let\endframed\relax
|
||||
\let\shaded\relax \let\endshaded\relax
|
||||
\let\leftbar\relax \let\endleftbar\relax
|
||||
\let\snugshade\relax \let\endsnugshade\relax
|
||||
\usepackage{minted}
|
||||
\setminted{linenos=true, tabsize=4, fontsize=\small, xleftmargin=5pt, xrightmargin=5pt,bgcolor=gray!10,frame=lines,framesep=2mm}
|
||||
% Mise en page
|
||||
\title{Notes de Cours}
|
||||
\author{Pierre-Antoine Comby}
|
||||
\teacher{Emmanuel Aldea \& Thomas Rodet}
|
||||
\module{453 - Traitement d'Image}
|
||||
\usepackage{multicol}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\maketitle
|
||||
\chapter{Introduction au traitement d'image}
|
||||
\subfile{chap1.tex}
|
||||
\chapter{Chapitre2}
|
||||
|
||||
\end{document}
|
||||
|
||||
%%% Local Variables:
|
||||
%%% mode: latex
|
||||
%%% TeX-master: t
|
||||
%%% End:
|
|
@ -38,6 +38,7 @@
|
|||
\newcommand{\ddd}[2]{\frac{\text{d}^2 #1}{\text{d} {#2}^2}}
|
||||
\newcommand{\dr}[2]{\frac{\partial #1}{\partial #2}}
|
||||
\newcommand{\divv}{\text{div}}
|
||||
\newcommand{\grad}{\text{grad}}
|
||||
%Macros mieux :)
|
||||
\newcommand{\chimie}[1]{$\mathrm{#1}$}
|
||||
\newcommand{\chimiecite}[1]{\[\mathrm{#1}\]}
|
||||
|
|
Loading…
Reference in a new issue