<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
	<id>http://os-vps418.infomaniak.ch:1250/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ruet</id>
	<title>Wiki du LAMA (UMR 5127) - Contributions [fr]</title>
	<link rel="self" type="application/atom+xml" href="http://os-vps418.infomaniak.ch:1250/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ruet"/>
	<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php/Sp%C3%A9cial:Contributions/Ruet"/>
	<updated>2026-04-16T16:07:42Z</updated>
	<subtitle>Contributions</subtitle>
	<generator>MediaWiki 1.39.4</generator>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10334</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10334"/>
		<updated>2018-05-26T19:24:59Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Deuxième étape : détermination de la &amp;quot;ligne de partage des eaux&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
Pour la deuxième étape de l&#039;algorithme, on utilise l&#039;image gradient. Elle est alors considérée comme un relief où les pixels de faible intensité sont à faible altitude, et où les pixels d&#039;intensité élevée sont à forte altitude.&lt;br /&gt;
&lt;br /&gt;
=== Définitions ===&lt;br /&gt;
&lt;br /&gt;
En topologie la ligne de partage des eaux peut-être définie de plusieurs manières ; elle est la séparation entre deux bassins versants dans un relief.&lt;br /&gt;
&lt;br /&gt;
Notre image est considérée comme un relief, et la ligne de partage des eaux sera la séparation entre les zones de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Un bassin versant est un bassin dont toutes les eaux ont le même exutoire, par exemple une rivière ou l&#039;océan.&lt;br /&gt;
&lt;br /&gt;
Pour déterminer la ligne de partage des eaux, plusieurs méthodes sont possibles :&lt;br /&gt;
&lt;br /&gt;
* On place une source d&#039;eau dans chaque minimum local (donc dans chaque bassin) et on inonde, quand deux bassins se rencontrent on place une ligne de partage des eaux.&lt;br /&gt;
Les algorithmes utilisant ce principe sont appelés des algorithmes par inondation.&lt;br /&gt;
&lt;br /&gt;
* Une deuxième méthode est de considérer que des deux côtés de la ligne l&#039;écoulement de l&#039;eau se termine dans des bassins versants différents.&lt;br /&gt;
Dans l&#039;algorithme associé, on va faire &amp;quot;s&#039;écouler&amp;quot; de l&#039;eau sur chaque pixel et tracer une ligne de partage entre deux pixels (ou sur l&#039;un des deux pixels) quand les eaux qui s&#039;écoulent depuis ces pixels arrive dans des minima locaux différents.&lt;br /&gt;
Ce sont les algorithmes par ruissellement.&lt;br /&gt;
&lt;br /&gt;
=== Algorithme utilisé (créé par Luc Vincent et Pierre Soille) ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme choisi est un algorithme par inondation.&lt;br /&gt;
&lt;br /&gt;
Les pixels de faible intensité vont donc être traités en priorité.&lt;br /&gt;
&lt;br /&gt;
La première étape est donc de trier les pixels de l&#039;image par ordre croissant d&#039;intensité.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme va traiter les pixels d&#039;intensité la plus basse, puis une fois ceux-ci traités, les pixels avec une intensité légèrement supérieure sont traités, et ainsi de suite jusqu&#039;à avoir traité toute l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Trois problèmes se posent alors :&lt;br /&gt;
* Comment inonder un étage, c&#039;est à dire de quelle manière traiter les pixels de même intensité, à partir de bassins connus ?&lt;br /&gt;
* Comment déterminer à quel bassin appartient un pixel ?&lt;br /&gt;
* Comment repérer les nouveaux bassins ?&lt;br /&gt;
&lt;br /&gt;
==== Inonder un étage (pixels d&#039;intensité identique) ====&lt;br /&gt;
&lt;br /&gt;
Chaque bassin doit progresser à la même vitesse sur un étage, c&#039;est à dire que la ligne de partage des eaux qui sépare deux bassins doit être à égale distance de chacun.&lt;br /&gt;
&lt;br /&gt;
Chaque pixel est à une &amp;quot;distance&amp;quot; d&#039;un bassin. Par exemple les pixels collés à un bassin connu sont à une distance de 1. Leurs voisins sont eux à une distance de 2 (à condition qu&#039;ils ne soient pas collés à un bassin connu).&lt;br /&gt;
&lt;br /&gt;
[[File:file_distance.png|frame|Distance à laquelle se trouve les pixels du bassin connu (bleu)]]&lt;br /&gt;
&lt;br /&gt;
Pour que les bassins s&#039;étendent correctement, on traite d&#039;abord tous les pixels à distance 1, puis ceux à distance 2 et ainsi de suite.&lt;br /&gt;
&lt;br /&gt;
Une file d&#039;attente est créée ; la première étape est de placer les pixels à une distance de 1 dans la file d&#039;attente.&lt;br /&gt;
On va pouvoir traiter ces pixels là. On sait également que leurs voisins sont à une distance de 2, et pourront donc être traités lorsque les pixels à distance 1 seront associés à un bassin.&lt;br /&gt;
&lt;br /&gt;
Les voisins non-traités des pixels à une distance de 1 sont donc les prochains pixels de l&#039;étage à traiter.&lt;br /&gt;
Ils sont placés dans une autre file d&#039;attente, qui sera parcourue une fois que la première a été entièrement utilisée.&lt;br /&gt;
&lt;br /&gt;
Ensuite, les voisins des pixels à une distance de 2 seront les prochains pixels à traiter, et on va recommencer cette suite d&#039;opérations.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les pixels sont placés dans la file d&#039;attente :&lt;br /&gt;
&lt;br /&gt;
* Tout d&#039;abord les pixels directement voisins d&#039;un bassin connu sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_1.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Puis une fois que les pixels dans la file d&#039;attente ont été traités, les pixels voisins des pixels qui viennent d&#039;être traités sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_2.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Cette étape se répète tant qu&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_3.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi le traitement d&#039;une file d&#039;attente (file d&#039;attente d&#039;une distance particulière) fait avancer d&#039;un pixel tous les bassins, et tous les bassins progressent à la même vitesse.&lt;br /&gt;
&lt;br /&gt;
==== Détermination du bassin auquel appartient un pixel ====&lt;br /&gt;
&lt;br /&gt;
Avec cette manière de faire progresser les bassins, le bassin auquel appartient un pixel est déterminé par les bassins auxquels appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
Plusieurs cas sont alors possibles lorsqu&#039;un pixel est traité.&lt;br /&gt;
&lt;br /&gt;
[[File:choix_label.png]] &lt;br /&gt;
&lt;br /&gt;
*Un des cas les plus simples est lorsque tous les voisins traités du pixel appartiennent au même bassin.Le bassin s&#039;étend alors, et le pixel à traiter est associé au bassin auquel appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
*Cette même décision est prise si tous les pixels voisins traités appartiennent au même bassin sauf certains qui appartiennent à une ligne de partage des eaux (LPE). L&#039;autre décision possible est de prolonger la LPE, cependant des lignes épaisses (et imprécises) seraient créées.&lt;br /&gt;
&lt;br /&gt;
*Si les pixels voisins traités du pixel appartiennent à une LPE, le bassin auquel appartient le pixel est inconnu. On ne peut pas &amp;quot;prolonger&amp;quot; la ligne car on ne connaît alors pas à quelle distance d&#039;un bassin se trouve le pixel. Si un pixel appartient à une LPE, ses voisins ne sont donc pas placés en file d&#039;attente.&lt;br /&gt;
&lt;br /&gt;
*Lorsque les pixels voisins appartiennent à des bassins différents, alors le pixel appartient à une LPE ; on sépare les bassins au moment de leur rencontre.&lt;br /&gt;
&lt;br /&gt;
*Le dernier cas se présente lorsque tous les pixels voisins sont traités et qu&#039;ils appartiennent tous à une LPE. Pour éviter qu&#039;un bassin d&#039;un seul pixel soit créé, on prolonge la LPE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En résumé :&lt;br /&gt;
*S&#039;il n&#039;y a qu&#039;un seul bassin autour du pixel en excluant les LPE, le pixel appartient à ce bassin.&lt;br /&gt;
*Si en excluant les LPE il y a plusieurs bassins autour du pixel, le pixel appartient à une LPE.&lt;br /&gt;
*Si le pixel est entouré par des LPE, alors il appartient à une LPE.&lt;br /&gt;
&lt;br /&gt;
==== Création de nouveaux bassins ====&lt;br /&gt;
&lt;br /&gt;
Lors de la propagation des bassins, si une file d&#039;attente est vide alors tous les pixels de l&#039;étage qui appartenaient à un bassin déjà connu ont été traités.&lt;br /&gt;
&lt;br /&gt;
Les pixels restants appartiennent donc à de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Un nouveau bassin est donc créé, le premier pixel qui lui appartient est choisi parmi un des pixels de l&#039;étage non-traités.&lt;br /&gt;
&lt;br /&gt;
On réinitialise alors la file d&#039;attente, et on place les voisins non-traités du pixel associé au nouveau bassin dedans.&lt;br /&gt;
&lt;br /&gt;
On recommence alors le traitement de la file d&#039;attente, et tous les pixels qui appartiennent à ce bassin lui seront attribués.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les nouveaux bassins sont créés, sur une petite image de 5x5.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_image.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Plusieurs étapes se déroulent alors :&lt;br /&gt;
&lt;br /&gt;
* Le traitement d&#039;un étage commence. Les pixels appartenant à un bassin connu se situent à un étage inférieur.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_1.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
* Tous les pixels appartenant à un bassin connu ont été traités ; il reste cependant des pixels non-traités à cet étage. Ils appartiennent donc à un ou plusieurs nouveaux bassins. On choisit donc un pixel non-traité de l&#039;étage, et on lui attribue un nouveau bassin.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_2.png|300px]] [[File:minima_legende.png|500px]]  &lt;br /&gt;
&lt;br /&gt;
* On propage le nouveau bassin créé.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_3.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
Ces étapes sont répétées tant qu&#039;il reste des pixels non-traités à l&#039;étage en cours de traitement. On détecte ainsi tous les nouveaux bassins de l&#039;étage.&lt;br /&gt;
&lt;br /&gt;
Une fois que tous les pixels d&#039;un étage ont été traités, on passe à l&#039;étage supérieur.&lt;br /&gt;
&lt;br /&gt;
==== Résultats ====&lt;br /&gt;
Voici un exemple de résultat que donne cet algorithme :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_brute.png]]&#039;&#039;A gauche le gradient d&#039;une image ; à droite le résultat de l&#039;algorithme de détermination des LPE&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On s&#039;aperçoit que l&#039;algorithme est très sensible au bruit, c&#039;est à dire aux légères variations d&#039;intensité qui créent des minima locaux.&lt;br /&gt;
&lt;br /&gt;
On veut donc améliorer les résultats obtenus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Réduire l&#039;impact du bruit ===&lt;br /&gt;
Plusieurs méthodes sont applicables pour réduire l&#039;impact du bruit sur le résultat.&lt;br /&gt;
&lt;br /&gt;
==== Ajout d&#039;une tolérance ====&lt;br /&gt;
La plus efficace est d&#039;ajouter une &amp;quot;tolérance&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
En effet, dans l&#039;algorithme décrit précédemment, les pixels appartiennent à un même étage si leurs intensités sont exactement identiques.&lt;br /&gt;
&lt;br /&gt;
On considére qu&#039;un pixel appartient à l&#039;étage traité si la différence entre son intensité et l&#039;intensité de l&#039;étage traitée est inférieure à un nombre, qu&#039;on appelle tolérance.&lt;br /&gt;
&lt;br /&gt;
Ainsi la sensibilité au bruit de l&#039;algorithme est réduite car les petites variations d&#039;intensité ne créent plus de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple sur une petite image. &lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance_image.png]]&lt;br /&gt;
&lt;br /&gt;
Tout d&#039;abord, s&#039;il n&#039;y a pas de tolérance, les pixels d&#039;intensité 130 et le pixel d&#039;intensité 135 sont à des étages différents.&lt;br /&gt;
Deux bassins sont donc créés lors du traitement de l&#039;étage d&#039;intensité 130 (bleu et vert, le noir représente une LPE).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_sans_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Cependant, avec l&#039;ajout d&#039;une tolérance (par exemple 10), les trois pixels sont considérés au même étage (car on a 135-130 &amp;lt; 10).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Ainsi une unique bassin est créé.&lt;br /&gt;
&lt;br /&gt;
Le résultat est alors bien plus précis sur de vraies images:&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_res_tolerance.png]] &#039;&#039;A gauche le résultat (de cette [[#Résultats|image]]) de l&#039;algorithme sans tolérance, à droite le résultat avec une tolérance de 5&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Flouter l&#039;image avant le traitement ====&lt;br /&gt;
&lt;br /&gt;
Une deuxième méthode est de traiter l&#039;image avant d&#039;appliquer l&#039;algorithme.&lt;br /&gt;
Si l&#039;image est floutée avant application de l&#039;algorithme, le bruit aura moins d&#039;impact, car les changements de valeur seront atténués.&lt;br /&gt;
Dans l&#039;image floutée, chaque valeur d&#039;un pixel est une moyenne des valeurs des pixels voisins à ce pixel dans l&#039;image originale.&lt;br /&gt;
&lt;br /&gt;
Pour cela, on peut convoluer l&#039;image par cette matrice :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Chaque produit de convolution donnera un entier qui devra être divisé par 9 ; on ajoute  les valeurs des 9 pixels dans un carré de 3x3 autour du pixel dont on veut la nouvelle valeur, pour avoir la moyenne des valeurs dans ce carré on doit alors diviser par 9.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas de bons résultats si elle est la seule utilisée. Cependant, combinée avec l&#039;utilisation de la tolérance, le nombre de petites zones est fortement réduit.&lt;br /&gt;
&lt;br /&gt;
Les LPE sont en contrepartie moins précises.&lt;br /&gt;
&lt;br /&gt;
Exemple de résultat :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou_tolerance5.png]]&lt;br /&gt;
&lt;br /&gt;
==== Fusion des petites zones ====&lt;br /&gt;
&lt;br /&gt;
Une troisième solution est d&#039;empêcher la création de zones trop petites, en indiquant une taille minimale.&lt;br /&gt;
Si des bassins plus petits que cette taille rencontrent un autre bassin, alors ils fusionnent, et le point de leur rencontre est associé à ce nouveau bassin issu de la fusion.&lt;br /&gt;
&lt;br /&gt;
Si au point de rencontre des bassins se rencontrent deux zones plus grandes que la taille minimale, on doit tout de même placer une LPE, pour séparer les deux &amp;quot;grands&amp;quot; bassins.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas non plus de résultats intéressants si utilisée seule, il est nécessaire de la combiner avec l&#039;ajout d&#039;une tolérance.&lt;br /&gt;
&lt;br /&gt;
Un des résultats obtenus avec cette méthode est (où la taille minimale d&#039;un bassin est de 5px) :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_fusion5.png]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
Pour conclure, on peut citer deux avantages de cet algorithme. Tout d&#039;abord, les lignes de partage des eaux créées sont peu épaisses ; elles ne font jamais plus de 3 pixels de large, et se limitent dans la plupart des cas à une épaisseur d&#039;un seul pixel.&lt;br /&gt;
&lt;br /&gt;
Ensuite, l&#039;algorithme est de complexité linéaire, c&#039;est à dire que le temps d’exécution dépend linéairement du nombre de pixels de l&#039;image sur laquelle on applique l&#039;algorithme.&lt;br /&gt;
Voici quelques mesures du temps d&#039;exécution selon le nombre de pixels de l&#039;image en entrée (les images utilisées pour ces tests ne représentent rien et contiennent de nombreuses zones).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tableau_temps.png|border|mesures du temps d&#039;exécution]]&lt;br /&gt;
[[File:lpe_temps.png|border|temps d&#039;exécution en fonction du nombre de pixels]]&lt;br /&gt;
&lt;br /&gt;
== Code source ==&lt;br /&gt;
Voici le code source de l&#039;algorithme.&lt;br /&gt;
&lt;br /&gt;
Pour manipuler des images, j&#039;ai utilisé la bibliothèque PILLOW.&lt;br /&gt;
&lt;br /&gt;
Les principales fonctions utilisées dans cette bibliothèque sont :&lt;br /&gt;
*Image.open(fichier) : ouvrir le fichier fichier en tant qu&#039;objet image&lt;br /&gt;
*image.getpixel((x,y)) : récupérer la valeur du pixel aux coordonnées (x,y) dans un objet Image (appelé ici &#039;image&#039;)&lt;br /&gt;
*image.setpixel((x,y),(r,v,b)) : Changer la valeur du pixel aux coordonnées (x,y) dans un objet Image (appelé ici &#039;image&#039;) ; (r,v,b) sont les valeurs rouge,vert,bleu.&lt;br /&gt;
*image.save(nom) : sauvegarder l&#039;image sous le nom souhaité.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
###########################################################&lt;br /&gt;
# RUET Nils&lt;br /&gt;
# Algorithme de détermination de ligne de partage des eaux, créé par Luc Vincent et Pierre Soille&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   IMPORTATIONS&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
from PIL import Image&lt;br /&gt;
from math import sqrt&lt;br /&gt;
from random import randint&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   CONSTANTES ET VARIABLES GLOBALES&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#Chaque pixel a 3 valeurs qui lui sont associées ; son intensité, l&#039;information sur s&#039;il a déjà été placé en file d&#039;attente, et le label qui lui a été associé.&lt;br /&gt;
#Ces informations sont stockés dans un tableau et ces constantes sont les indices auxquels trouver les informations voulues.&lt;br /&gt;
VALEUR = 0&lt;br /&gt;
LABELLED = 1&lt;br /&gt;
LABEL = 2&lt;br /&gt;
&lt;br /&gt;
indiceEtage = 0# Lorsque l&#039;on traite un étage, certains pixels ne sont pas traitables à partir des données connues. Ils correspondent à de nouveaux bassins.&lt;br /&gt;
               # On dispose d&#039;une liste des pixels de l&#039;étage ; lorsqu&#039;on doit créer un bassin, on cherche donc le premier pixel dans cette liste qui n&#039;est pas labellé.&lt;br /&gt;
               # Cette variable contient le nombre de pixels &amp;quot;consécutifs&amp;quot; traités, c&#039;est à dire l&#039;indice du dernier pixel non traité trouvé dans la liste.&lt;br /&gt;
               # Ainsi on n&#039;a pas à reparcourir la liste entière lorsque l&#039;on créé plusieurs nouveaux bassins à un étage car on sait que tous les pixels &amp;quot;avant&amp;quot; (dans la liste) celui choisi pour créer un nouveau bassin ont été labellés.&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   CALCUL DU GRADIENT&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
def deterCoef(mat):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Cette fonction prend un masque de convolution en entrée. Elle permet de déterminer un décalage et un coefficient utilisés pour ramener toutes les valeurs&lt;br /&gt;
    obtenues après convolution entre 0 et 255&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    sommeP=0&lt;br /&gt;
    sommeN=0&lt;br /&gt;
    for i in range(len(mat)):       #On compte les coefficients négatifs et les coefficients positifs dans le masque&lt;br /&gt;
        for j in range(len(mat[0])):&lt;br /&gt;
            if mat[i][j] &amp;gt; 0:&lt;br /&gt;
                sommeP+=mat[i][j]&lt;br /&gt;
            else:&lt;br /&gt;
                sommeN-=mat[i][j]&lt;br /&gt;
    decal = sommeN * 255            #Les coefficients négatifs permettent de connaître la valeur à ajouter pour n&#039;obtenir que des valeurs positives après la convolution&lt;br /&gt;
    coef = sommeN+sommeP            #La somme des valeurs absolues des coefficients permet de savoir comment ramener toutes ces valeurs entre 0 et 255.&lt;br /&gt;
    return (coef, decal)&lt;br /&gt;
&lt;br /&gt;
def Convolution(image, x, y, filtre):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Effectue le produit de convolution entre un filtre et l&#039;enourage du pixel aux coordonnées (x,y) dans l&#039;image &#039;image&#039; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    somme = 0&lt;br /&gt;
    centre = len(filtre)//2&lt;br /&gt;
    for i in range(-centre,centre+1):           #i et j indiquent la position relative du pixel dont on a besoin dans l&#039;image par rapport au pixel dont on calcule la nouvelle valeur.&lt;br /&gt;
        for j in range(-centre,centre+1):&lt;br /&gt;
            if filtre[i+centre][j+centre] != 0: #Vu qu&#039;on multiplie la valeur d&#039;un pixel par une valeur dans le filtre, on vérifie que la valeur dans le filtre ne vaut pas 0.&lt;br /&gt;
                if 0&amp;lt;=x+i&amp;lt;image.width and 0&amp;lt;=y+j&amp;lt;image.height: #Si le pixel voulu est dans l&#039;image, xl (x local) et yl (y local) sont ses coordonnées.&lt;br /&gt;
                    xl = x+i&lt;br /&gt;
                    yl = y+j&lt;br /&gt;
                else:                           #Sinon on va chercher la valeur du pixel en symlétrie par rapport au pixel dont on veut la nouvelle valeur&lt;br /&gt;
                    xl = x-i&lt;br /&gt;
                    yl = y-j&lt;br /&gt;
                    &lt;br /&gt;
                if not 0&amp;lt;=xl&amp;lt;image.width or not 0&amp;lt;=yl&amp;lt;image.height: #Si après cela le pixel n&#039;est toujours pas dans l&#039;image, on se réfère à la valeur du pixel dont on veut la nouvelle valeur.&lt;br /&gt;
                    xl = x&lt;br /&gt;
                    yl = y                    &lt;br /&gt;
                somme+= (image.getpixel((xl,yl))[0] *filtre[i+centre][j+centre])&lt;br /&gt;
    return somme&lt;br /&gt;
&lt;br /&gt;
def AppliqueFiltre(image, filtre):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permet d&#039;appliquer le masque de convolution &#039;filtre&#039; (double tableau) à tous les pixels de l&#039;image &#039;image&#039; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = image.copy()&lt;br /&gt;
    (coef, decal) = deterCoef(filtre)&lt;br /&gt;
    for x in range(image.width):            #Pour chaque pixel de l&#039;image&lt;br /&gt;
        for y in range(image.height):&lt;br /&gt;
            somme = Convolution(image,x,y,filtre) #On prend le résultat de la convolution&lt;br /&gt;
            val = round((somme+decal)/coef)       #On ramène ce résultat entre 0 et 255&lt;br /&gt;
            res.putpixel((x,y),(val,val,val))     #On met le pixel dans l&#039;image résultat à cette valeur&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def Filtre(image, filtre, nom_sortie=&amp;quot;filtre&amp;quot;, saveIm=True):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permet d&#039;appeler la fonction qui permet de convoluer l&#039;image &#039;image&#039; par le masque &#039;filtre&#039; et choisir si le résultat est sauvegardé dans un fichier image ou non&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = AppliqueFiltre(image, filtre)&lt;br /&gt;
    if saveIm:&lt;br /&gt;
        res.save(&amp;quot;{}_{}&amp;quot;.format(nom_sortie, fichierImage))&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
def FiltreSobel(fichierIm):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permer de calculer la norme du gradient de l&#039;image &#039;fichierIm&#039; (nom du fichier image) en chaque point avec la méthode du filtre de Sobel&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    image = Image.open(fichierIm)&lt;br /&gt;
    res = image.copy()&lt;br /&gt;
    masquex = [[-1,0,1],[-2,0,2],[-1,0,1]]&lt;br /&gt;
    masquey = [[-1,-2,-1],[0,0,0],[1,2,1]]&lt;br /&gt;
    for x in range(image.width):                                #Pour chaque pixel de l&#039;image&lt;br /&gt;
        for y in range(image.height):&lt;br /&gt;
            dx = Convolution(image,x,y,masquex)                 #On approxime ses dérivées partielles&lt;br /&gt;
            dy = Convolution(image,x,y,masquey)                 #&lt;br /&gt;
&lt;br /&gt;
            val = round(sqrt(dx**2 + dy**2)*255/(sqrt(2)*1020)) #On calcule la norme du gradient, qu&#039;on ramène entre 0 et 255.&lt;br /&gt;
            res.putpixel((x,y),(val,val,val))                   #Cette valeur est associée au pixel dans le résultat&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#########################################################################################################&lt;br /&gt;
#   ALGORITHME LIGNE DE PARTAGE DES EAUX&lt;br /&gt;
#########################################################################################################&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def InitTableau(image):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Initialise le double tableau contenant les informations sur chaque pixel; aux indices [x][y] on accède aux informations du pixel (x,y). Ces informations sont l&#039;intensité, l&#039;avancement du traitement et le label.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tableau = [0]*image.width                                       #On initialise un tableau de la taille de l&#039;image&lt;br /&gt;
    for i in range(len(tableau)):&lt;br /&gt;
        tableau[i]=[0]*image.height&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(tableau)):                                   #On initialise les informations de chaque pixel :&lt;br /&gt;
        for j in range(len(tableau[i])):&lt;br /&gt;
            tableau[i][j] = [image.getpixel((i,j))[0],False,&amp;quot;init&amp;quot;] #Sa valeur dans l&#039;image, l&#039;avancement du traitement (False si le pixel n&#039;a pas été traité, True sinon), et le label qui lui est associé&lt;br /&gt;
    return tableau&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def InitListeOrdonnee(image):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Initialise la liste des pixels de l&#039;image, triés par intensité ; chaque case contient la position du pixel attribué et son intensité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    liste = []&lt;br /&gt;
    for i in range(image.width):&lt;br /&gt;
        for j in range(image.height):&lt;br /&gt;
            liste.append([image.getpixel((i,j))[0],i,j])#On regarde chaque pixel de l&#039;image et on ajoute dans une case de la liste son intensité, son abcisse, son ordonnée&lt;br /&gt;
    liste.sort(key=lambda pixel: pixel[0])              #On trie cette liste en fonction de l&#039;intensité des pixels (ordre croissant)&lt;br /&gt;
    return liste&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def PixelsEtage(listeOrdonnee, intensite, indice, tablImage, tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend en entrée la liste ordonnée des pixels de l&#039;image, une intensité, l&#039;indice à partir duquel commence cette intensité dans la liste, et une tolérance.&lt;br /&gt;
       Renvoie la liste des pixels d&#039;un &amp;quot;étage&amp;quot; d&#039;intensité, plus précisément :&lt;br /&gt;
       La liste des pixels non-labellés de l&#039;intensité choisie plus ou moins la tolérance donnée. L&#039;indice à partir duquel commence l&#039;intensité choisie est modifié par une autre partie&lt;br /&gt;
       du programme ; on sait que tous les pixels d&#039;une valeur inférieure à l&#039;intensité choisie ont déjà été traités par l&#039;algorithme.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    pixels = []&lt;br /&gt;
    nouvelIndice = indice&lt;br /&gt;
    while nouvelIndice &amp;lt; len(listeOrdonnee) and (listeOrdonnee[nouvelIndice][0] - intensite) &amp;lt;= tolerance:#On ajoute à la liste des pixels de l&#039;étage intensité les pixels qui ont une intensité dans l&#039;intervalle souhaité&lt;br /&gt;
        if not tablImage[listeOrdonnee[nouvelIndice][1]][listeOrdonnee[nouvelIndice][2]][LABELLED]:       #Et non labellés&lt;br /&gt;
            pixels.append(listeOrdonnee[nouvelIndice])&lt;br /&gt;
        nouvelIndice+=1&lt;br /&gt;
    return pixels&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def labelsPixelsVoisins(x, y, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonctions qui renvoie la liste des labels des pixels voisins au pixel situé aux coordonnée entrées en paramètre, ignore les pixels non labellés&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    labels = []&lt;br /&gt;
&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins:&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]) and tablImage[x+i][y+j][LABEL] != &amp;quot;init&amp;quot;:&lt;br /&gt;
            labels.append(tablImage[x+i][y+j][LABEL])        &lt;br /&gt;
    return labels&lt;br /&gt;
&lt;br /&gt;
def PixelTraitable(xPixel, yPixel, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonction qui renvoie True si le pixel aux coordonnées entrées en paramètre est labellable, False sinon&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    labels = labelsPixelsVoisins(xPixel, yPixel, tablImage)&lt;br /&gt;
    for label in labels:&lt;br /&gt;
        if label!=&amp;quot;watershed&amp;quot;:#Un pixel est labellable s&#039;il a un pixel labellé par autre chose qu&#039;une ligne de partage parmi ses voisins.&lt;br /&gt;
            return True&lt;br /&gt;
&lt;br /&gt;
    voisinsDansImage = 0                                                #Sinon si le pixel n&#039;a pas de pixels labellés par autre chose que watershed parmi ses voisins,&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins:                                              #On détermine son nombre de voisins (4 au centre, 3 sur les bords et 2 dans les coins)&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=xPixel+i&amp;lt;len(tablImage) and 0&amp;lt;=yPixel+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
            voisinsDansImage += 1&lt;br /&gt;
    return len(labels)==voisinsDansImage                                #Le pixel est labellable si tous ses voisins sont des lignes de partage des eaux&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def nouveauLabel(listePixels, tablImage, fileAttente, listeLabels, intensite, tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Cette fonction est appelée lorsque l&#039;algorithme n&#039;a pas traité tous les pixels d&#039;un étage, mais qu&#039;aucun pixel n&#039;a été placé en file attente &amp;lt;=&amp;gt; aucun pixel non traité n&#039;est traitable à cet étage.&lt;br /&gt;
       A partir de la liste des pixels de l&#039;étage, de la liste des labels attribués, de l&#039;intensité de l&#039;étage et de la tolérance, on modifie alors la file d&#039;attente&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    global indiceEtage&lt;br /&gt;
    while tablImage[listePixels[indiceEtage][1]][listePixels[indiceEtage][2]][LABELLED]: #On parcourt la liste des pixels de l&#039;étage pour en trouver un non labellé&lt;br /&gt;
        indiceEtage+=1     &lt;br /&gt;
    x = listePixels[indiceEtage][1]&lt;br /&gt;
    y = listePixels[indiceEtage][2]&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins: #On regarde les voisins du premier pixel non labellé trouvé&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]) and not tablImage[x+i][y+j][LABELLED] and (tablImage[x+i][y+j][VALEUR] - intensite) &amp;lt;= tolerance:&lt;br /&gt;
            #Si un voisin est dans le bon intervalle d&#039;intensité, non labellé et dans l&#039;image, alors on le place dans la file d&#039;attente&lt;br /&gt;
            tablImage[x+i][y+j][LABELLED] = True&lt;br /&gt;
            fileAttente[0].append([tablImage[x+i][y+j][VALEUR], x+i, y+j])&lt;br /&gt;
&lt;br /&gt;
    tablImage[x][y][LABEL] = len(listeLabels)#On créé un nouveau label en augmentant de 1 le numéro du dernier label ; cela correspond à la longueur de la liste des labels&lt;br /&gt;
                                             #Puis on l&#039;associe au pixel non traité trouvé dans l&#039;étage&lt;br /&gt;
    tablImage[x][y][LABELLED] = True         #Ce pixel et désormais traité&lt;br /&gt;
    listeLabels.append(1)                    #On ajoute le nouveau label créé à la liste des labels ; le bassin associé à ce label est de taille 1px initalement&lt;br /&gt;
    return fileAttente                       #On renvoie la nouvelle file d&#039;attente ; les voisins traitables du pixel trouvés   &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def AjoutFileAttente(fileAttente, distance, tablImage,intensite,tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;A partir des pixels traités au moment de l&#039;appel de cette fonction, on détermine les prochains pixels qui pourront être traités par la suite.&lt;br /&gt;
       Ces pixels sont les voisins des pixels déjà traités (à l&#039;exception des lignes de partage) du même étage, non traités et dans l&#039;image&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    fileAttente.append([])             #On ajoute une file d&#039;attente pour la distance suivante&lt;br /&gt;
    for pixel in fileAttente[distance]:#Pour chaque pixel traité à la distance précédente&lt;br /&gt;
        x = pixel[1]&lt;br /&gt;
        y = pixel[2]&lt;br /&gt;
        if tablImage[x][y][LABEL] != &amp;quot;watershed&amp;quot;:#S&#039;il n&#039;est pas une ligne de partage, il appartient à un bassin et ses voisins sont potentiellement traitables      &lt;br /&gt;
            voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
            for coords in voisins:&lt;br /&gt;
                i = coords[0]&lt;br /&gt;
                j = coords[1]&lt;br /&gt;
                if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
                    if (tablImage[x+i][y+j][VALEUR] - intensite) &amp;lt;= tolerance and not tablImage[x+i][y+j][LABELLED]:#Les voisins traitables sont les pixel du même étage non labellés&lt;br /&gt;
                        tablImage[x+i][y+j][LABELLED] = True&lt;br /&gt;
                        fileAttente[distance+1].append([tablImage[x+i][y+j][VALEUR], x+i, y+j])#On les ajoute à la file d&#039;attente suivante&lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
def valeursUniques(T):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend un tableau en entrée et supprime les valeurs qui s&#039;y trouvent en double&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for e in T:&lt;br /&gt;
        if not e in res:&lt;br /&gt;
            res.append(e)&lt;br /&gt;
    return res&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
def fusionZones(labelsChange, nouvLabel, listeLabels, x, y, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; A partir d&#039;une liste de labels à changer (labelsChange), du nouveau label qui leur sera associé, et d&#039;un pixel de départ aux coordonnées x,y&lt;br /&gt;
    On fusionne les bassins qui se rencontrent en x,y en modifiant le label des pixels de ces bassins.&lt;br /&gt;
    Chaque fois qu&#039;un pixel change de label, on change le label de ses voisins si cela est nécéssaire&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tablImage[x][y][LABEL] = nouvLabel&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
&lt;br /&gt;
    file = [[x,y]]&lt;br /&gt;
    indiceFile = 0&lt;br /&gt;
    while indiceFile != len(file):#Tant que tous les pixels de la file n&#039;ont pas été associés au nouveau label&lt;br /&gt;
        pixel = file[indiceFile]  #On prend le prochain pixel dans la file&lt;br /&gt;
        for coords in voisins:    #Et on regarde chacun de ses voisins                                 &lt;br /&gt;
            i = coords[0]&lt;br /&gt;
            j = coords[1]&lt;br /&gt;
            if 0&amp;lt;=pixel[0]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[1]+j&amp;lt;len(tablImage[0]) and (tablImage[pixel[0]+i][pixel[1]+j][LABEL] in labelsChange): #Si un pixel voisins dans l&#039;image a un label qui doit être changé&lt;br /&gt;
                file.append([pixel[0]+i,pixel[1]+j])#On l&#039;ajoute à la file&lt;br /&gt;
                tablImage[pixel[0]+i][pixel[1]+j][LABEL] = nouvLabel#On lui attribue son nouveau label&lt;br /&gt;
                listeLabels[nouvLabel]+=1#On augmente le nombre de pixels qui appartiennent au nouveau bassin&lt;br /&gt;
        indiceFile+=1&lt;br /&gt;
  &lt;br /&gt;
def determinationLabels(fileAttente, distance, tablImage, listeLabels, tailleMini):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Procédure qui détermine le label attribué à un pixel en fonction des labels des pixels voisins&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for pixel in fileAttente[distance]:                             #On attribue un label à chaque pixel dans la file d&#039;attente&lt;br /&gt;
        labels = labelsPixelsVoisins(pixel[1], pixel[2], tablImage) #On récupère les labels de ses voisins&lt;br /&gt;
        lstLabels = valeursUniques(labels)                          #On supprime les doublons,&lt;br /&gt;
        if (&amp;quot;watershed&amp;quot; in lstLabels):                              #et le label de ligne de partage des eaux&lt;br /&gt;
            lstLabels.remove(&amp;quot;watershed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        voisinsDansImage = 0&lt;br /&gt;
        voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
        for coords in voisins:                                      #On compte le nombre de pixels voisins dans l&#039;image ; 4 si le pixel est au centre de l&#039;image, 3 s&#039;il est au bord etc.&lt;br /&gt;
            i = coords[0]&lt;br /&gt;
            j = coords[1]&lt;br /&gt;
            if 0&amp;lt;=pixel[1]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[2]+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
                voisinsDansImage += 1&lt;br /&gt;
        if len(lstLabels) == 0 and len(labels)==voisinsDansImage:   #Si le pixel est entouré de watershed, on lui attribue ce label&lt;br /&gt;
            tablImage[pixel[1]][pixel[2]][LABEL] = &amp;quot;watershed&amp;quot;&lt;br /&gt;
                &lt;br /&gt;
        elif len(lstLabels) == 1:                                   #Si le pixel n&#039;a qu&#039;un seul label hormis les watershed autour de lui, on lui attribue ce label&lt;br /&gt;
            tablImage[pixel[1]][pixel[2]][LABEL] = lstLabels[0]&lt;br /&gt;
            listeLabels[lstLabels[0]] += 1&lt;br /&gt;
            &lt;br /&gt;
        elif len(lstLabels) &amp;gt; 1:                                    #S&#039;il y a plusieurs labels autour de lui, il se trouve entre deux bassins et est donc une ligne de partage des eaux ; si les bassins sont petits on les fusionne&lt;br /&gt;
            i = 0&lt;br /&gt;
            plusGrand = []&lt;br /&gt;
            moinsGrand = []&lt;br /&gt;
            while i&amp;lt;len(lstLabels):&lt;br /&gt;
                if listeLabels[lstLabels[i]]&amp;gt;tailleMini:&lt;br /&gt;
                    plusGrand.append(lstLabels[i])&lt;br /&gt;
                else:&lt;br /&gt;
                    moinsGrand.append(lstLabels[i])&lt;br /&gt;
                i+=1&lt;br /&gt;
            if len(moinsGrand)&amp;gt;0 and len(plusGrand)&amp;lt;2:  #Si des bassins sont en dessous de la taille minimale et qu&#039;il n&#039;y a pas plusieurs bassins au dessus de cette taille &lt;br /&gt;
                if len(plusGrand)==1:                   #Si il n&#039;y a qu&#039;un bassin au dessus, tous les petits bassins lui seront rattachés&lt;br /&gt;
                    labelsChange = moinsGrand[:]&lt;br /&gt;
                    nouvLabel = plusGrand[0]&lt;br /&gt;
                elif len(plusGrand)==0:                 #S&#039;il n&#039;y en a aucun, les petits bassins fusionnent entre eux&lt;br /&gt;
                    labelsChange = moinsGrand[1:]&lt;br /&gt;
                    nouvLabel = moinsGrand[0]&lt;br /&gt;
        &lt;br /&gt;
                tablImage[pixel[1]][pixel[2]][LABEL] = nouvLabel #Les bassins vont fusionner, le pixel que l&#039;on regardait appartient donc au nouveau bassin&lt;br /&gt;
                listeLabels[nouvLabel] += 1                      #On augmente le compteur de pixels appartenant à ce bassin&lt;br /&gt;
                &lt;br /&gt;
                voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
                for coords in voisins:                      #On regarde chaque voisin, s&#039;il est dans l&#039;image et que son label doit etre changé, on appelle la procédure qui permet de changer les labels du bassin&lt;br /&gt;
                    i = coords[0]&lt;br /&gt;
                    j = coords[1]&lt;br /&gt;
                    if 0&amp;lt;=pixel[1]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[2]+j&amp;lt;len(tablImage[0]) and tablImage[pixel[1]+i][pixel[2]+j][LABEL] in labelsChange:&lt;br /&gt;
                        fusionZones(labelsChange, nouvLabel, listeLabels, pixel[1]+i, pixel[2]+j, tablImage)&lt;br /&gt;
                        &lt;br /&gt;
            else:                                       #Si aucun bassin n&#039;est trop petit ou si au moins 2 sont au dessus de la taille minimale, le pixel est une ligne de partage des eaux&lt;br /&gt;
                tablImage[pixel[1]][pixel[2]][LABEL] = &amp;quot;watershed&amp;quot;&lt;br /&gt;
&lt;br /&gt;
                &lt;br /&gt;
def Algo(image, tolerance, tailleMini):&lt;br /&gt;
    global indiceEtage&lt;br /&gt;
    &lt;br /&gt;
    #### INITALISATION ###&lt;br /&gt;
&lt;br /&gt;
    #Initaliser tableaux :&lt;br /&gt;
    #   -double tableau qui contient les informations de chaque pixel    &lt;br /&gt;
    tableauImage = InitTableau(image)&lt;br /&gt;
    &lt;br /&gt;
    #   -la liste des labels&lt;br /&gt;
    listeLabels = []&lt;br /&gt;
    &lt;br /&gt;
    #   -Initialiser la liste des pixels triée par leur intensité&lt;br /&gt;
    listePixelsOrdonnee = InitListeOrdonnee(image)&lt;br /&gt;
&lt;br /&gt;
    #   -l&#039;indice de parcours de la liste des pixels triée&lt;br /&gt;
    indice = 0&lt;br /&gt;
    &lt;br /&gt;
    ### TRAITEMENT POUR CHAQUE INTENSITE ###&lt;br /&gt;
    for intensite in range(256):&lt;br /&gt;
        #Initialiser la liste des pixels appartenant à l&#039;étage traité&lt;br /&gt;
        pixelsMemeEtage = PixelsEtage(listePixelsOrdonnee, intensite, indice, tableauImage, tolerance)&lt;br /&gt;
        &lt;br /&gt;
        indice += len(pixelsMemeEtage)#On connaît le nombre de pixels de cet étage, on en déduit l&#039;indice où commencera le prochaine étage&lt;br /&gt;
        indiceEtage = 0         #Initialisation du compteur de pixels &amp;quot;consécutifs&amp;quot; traités à cet étage&lt;br /&gt;
        compteurPxTraites = 0   #Compteur du nombre de pixels traités à cet étage&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        # Initialiser la file d&#039;attente :&lt;br /&gt;
        # La file d&#039;attente est une liste de listes de pixels ; chaque sous-liste correspond à une distance, une étape du traitement&lt;br /&gt;
        # Les premiers pixels traités sont collés à un pixel déjà labellé ; leurs voisins seront traitables par la suite &lt;br /&gt;
        # et sont donc mis dans la file d&#039;attente &amp;quot;suivante&amp;quot;. Ils sont à une &amp;quot;distance&amp;quot; supérieure, et seront traités après le traitement de la distance actuelle&lt;br /&gt;
        # C&#039;est avec ce principe qu&#039;on forme la file d&#039;attente.&lt;br /&gt;
        fileAttente=[[]]&lt;br /&gt;
        &lt;br /&gt;
        # On fait d&#039;abord un parcours de l&#039;étage, pour trouver les pixels collés à des pixels labellés lors du traitement d&#039;étages précédents&lt;br /&gt;
        for pixel in pixelsMemeEtage:&lt;br /&gt;
            # Pour chaque pixel de l&#039;étage, on regarde ses voisins, si au moins un est labellé, le pixel est à distance 0, c&#039;est à dire traitable directement.&lt;br /&gt;
            if PixelTraitable(pixel[1],pixel[2], tableauImage):&lt;br /&gt;
                #Si c&#039;est le cas on l&#039;ajoute à la file d&#039;attente&lt;br /&gt;
                fileAttente[0].append(pixel)&lt;br /&gt;
                tableauImage[pixel[1]][pixel[2]][LABELLED] = True&lt;br /&gt;
                &lt;br /&gt;
        #On initialise le numéro de l&#039;étape du traitement, la distance : elle commence à 0 et augmente à chaque tour de boucle&lt;br /&gt;
        distance = 0&lt;br /&gt;
        &lt;br /&gt;
        while len(pixelsMemeEtage) &amp;gt; compteurPxTraites: #Tant que le nombre de pixels de cet étage traités est inférieur au nombre de pixels de l&#039;étage, on a pas traité tout l&#039;étage.&lt;br /&gt;
        &lt;br /&gt;
            if len(fileAttente[distance])==0:                                                                             #On regarde s&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
                fileAttente = nouveauLabel(pixelsMemeEtage, tableauImage, fileAttente, listeLabels, intensite, tolerance) #Si non alors il n&#039;y a plus aucun bassin connu à cet étage, on en ajoute:&lt;br /&gt;
                compteurPxTraites += 1                                                                                    #On sait qu&#039;un pixel a été traité en ajoutant un nouveau bassin (le premier pixel du bassin), on l&#039;ajoute au compteur&lt;br /&gt;
                &lt;br /&gt;
            else:                                                                                       #Si il y a des pixels à traiter on les traite               &lt;br /&gt;
                while len(fileAttente[distance]) &amp;gt; 0:                                                   #avec ces nouvelles informations de nouveaux pixels seront traitables.&lt;br /&gt;
                                                                                                        #On continue ces actions jusqu&#039;à qu&#039;aucun pixel ne soit traitable&lt;br /&gt;
                    &lt;br /&gt;
                    determinationLabels(fileAttente, distance, tableauImage, listeLabels, tailleMini)   #On commence par déterminer les labels des pixels traitables&lt;br /&gt;
                    AjoutFileAttente(fileAttente, distance, tableauImage, intensite, tolerance)         #On ajoute les pixels désormais traitable dans la file d&#039;attente qui sera parcourue au prochain tour de boucle&lt;br /&gt;
                    compteurPxTraites+=len(fileAttente[distance])                                       #Tous les pixels de la file d&#039;attente à cet étape ont été traités ; on augmente le compteur en conséquence&lt;br /&gt;
                    distance +=1                                                                        #On passe à l&#039;étape suivant de l&#039;algorithme / la nouvelle file d&#039;attente&lt;br /&gt;
                    &lt;br /&gt;
                distance = 0                                         #Une fois que la boucle est terminé, on a labellé tous les pixels possibles à cet étage qui n&#039;appartiennent pas à un nouveau bassin&lt;br /&gt;
                fileAttente = [[]]                                   #On réinitialise la file d&#039;attente pour refaire le même traitement avec un nouveau bassin&lt;br /&gt;
&lt;br /&gt;
                #Si tous les pixels de l&#039;étage n&#039;ont pas été traités, alors on va ainsi créer un nouveau bassin et le propager, jusqu&#039;à que tous les pixels de l&#039;étage aient été traités.&lt;br /&gt;
&lt;br /&gt;
    return AffichageImage(tableauImage, image, listeLabels)#On enregistre les résultats du traitement dans une image&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def AffichageImage(tableauImage, image, listeLabels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonction qui prend le résultat de l&#039;algorithme sur le fichier fichier et le convertit en image ;&lt;br /&gt;
       Les pixels des lignes de partage des eaux sont affichés en noir, et chaque label est associé à une couleur.&lt;br /&gt;
       On enregistre ensuite cette image.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    lpe = image.copy()&lt;br /&gt;
    couleurs = [0]*len(listeLabels)&lt;br /&gt;
    for e in range(len(couleurs)):&lt;br /&gt;
        couleurs[e] = (randint(100,200),randint(100,200),randint(100,200))  &lt;br /&gt;
    for i in range(image.width):&lt;br /&gt;
        for j in range(image.height):&lt;br /&gt;
            if tableauImage[i][j][LABEL] == &amp;quot;watershed&amp;quot;:&lt;br /&gt;
                lpe.putpixel((i,j),(0,0,0))&lt;br /&gt;
            else:&lt;br /&gt;
                lpe.putpixel((i,j),couleurs[tableauImage[i][j][LABEL]])&lt;br /&gt;
    return lpe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
# UTILISATION&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
def LPE(fichierImage, saveGradient=False, tolerance=0, flou=False, tailleMini=0, fichierGradient=&amp;quot;none&amp;quot;):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend un fichier image en entrée et calcule son gradient,&lt;br /&gt;
    avant d&#039;appliquer l&#039;algorithme de détermination de ligne de partage des eaux&lt;br /&gt;
&lt;br /&gt;
    l&#039;option &amp;quot;saveGradient&amp;quot; est un booléen qui permet d&#039;enregistrer le gradient de l&#039;image si souhaité&lt;br /&gt;
&lt;br /&gt;
    l&#039;option fichierGradient permet d&#039;éxécuter uniquement la détermination de la LPE sur un fichier.&lt;br /&gt;
    On peut donc appliquer la deuxième partir de l&#039;algorithme sur une image gradient déjà calculée.&lt;br /&gt;
&lt;br /&gt;
    D&#039;autres options sont disponibles, qui permettent de réduire la sensibilité au bruit de l&#039;algorithme&lt;br /&gt;
        - tolerance : entier ; deux pixels seront traités comme appartenant au même &amp;quot;étage&amp;quot;&lt;br /&gt;
          si cet entier est plus grand que la différence de leurs valeurs&lt;br /&gt;
        - flou : booléen qui si vaut vrai, applique un flou sur l&#039;image de départ afin de diminuer&lt;br /&gt;
          l&#039;impact du bruit sur le résultat&lt;br /&gt;
        - tailleMini : entier ; au moment où plusieurs zones se rencontrent, si cela est possible,&lt;br /&gt;
          on fusionne les zones de taille inférieure (la taille est le nombre de pixels associés à la zone) à cet entier. Cela permet la supression des petites zones.&lt;br /&gt;
 &lt;br /&gt;
    Les options réduisant la sensibilité au bruit peuvent rendre le résultat imprécis en fonction de leurs valeurs&lt;br /&gt;
 &lt;br /&gt;
    Le résultat est sauvegardé sous forme d&#039;image, où chaque zone est associée à une couleur et où les lignes de partage des eaux sont en noir.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    if fichierGradient == &amp;quot;none&amp;quot;:&lt;br /&gt;
        gradient = FiltreSobel(fichierImage)&lt;br /&gt;
        if saveGradient:&lt;br /&gt;
            gradient.save(&amp;quot;gradient_{}&amp;quot;.format(fichierImage))&lt;br /&gt;
    else:&lt;br /&gt;
        gradient = Image.open(fichierGradient)&lt;br /&gt;
    if flou:&lt;br /&gt;
        gradient = Filtre(gradient, [[1,1,1],[1,1,1],[1,1,1]], saveIm=False)&lt;br /&gt;
    lpe = Algo(gradient, tolerance, tailleMini)&lt;br /&gt;
    &lt;br /&gt;
    nom = &amp;quot;lpe_&amp;quot;&lt;br /&gt;
    if flou:&lt;br /&gt;
        nom+=&amp;quot;flou_&amp;quot;&lt;br /&gt;
    if tolerance!=0:&lt;br /&gt;
        nom+=&amp;quot;tolerance-{}_&amp;quot;.format(tolerance)&lt;br /&gt;
    if tailleMini!=0:&lt;br /&gt;
        nom+=&amp;quot;mini-{}_&amp;quot;.format(tailleMini)&lt;br /&gt;
    nom += &amp;quot;{}&amp;quot;.format(fichierImage)&lt;br /&gt;
    lpe.save(nom)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sources ==&lt;br /&gt;
*[https://fr.wikipedia.org/wiki/Segmentation_d%27image Segmentation d&#039;image]&lt;br /&gt;
*[https://fr.wikipedia.org/wiki/Filtre_de_Sobel Filtre de Sobel]&lt;br /&gt;
*[https://fr.wikipedia.org/wiki/Ligne_de_partage_des_eaux Ligne de partage des eaux (géographie)]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Watershed_(image_processing) Algorithmes existants pour calculer des lignes de partage des eaux (segmentation)]&lt;br /&gt;
*[https://pdfs.semanticscholar.org/a381/9dda9a5f00dbb8cd3413ca7422e37a0d5794.pdf Algorithme de Luc Vincent et Pierre Soille]&lt;br /&gt;
&lt;br /&gt;
== Annexes ==&lt;br /&gt;
*[https://pillow.readthedocs.io/en/5.1.x/ Bibliothèque de manipulation d&#039;image utilisée (PILLOW)]&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10333</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10333"/>
		<updated>2018-05-26T19:14:42Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Deuxième étape : détermination de la &amp;quot;ligne de partage des eaux&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
Pour la deuxième étape de l&#039;algorithme, on utilise l&#039;image gradient. Elle est alors considérée comme un relief où les pixels de faible intensité sont à faible altitude, et où les pixels d&#039;intensité élevée sont à forte altitude.&lt;br /&gt;
&lt;br /&gt;
=== Définitions ===&lt;br /&gt;
&lt;br /&gt;
En topologie la ligne de partage des eaux peut-être définie de plusieurs manières ; elle est la séparation entre deux bassins versants dans un relief.&lt;br /&gt;
&lt;br /&gt;
Notre image est considérée comme un relief, et la ligne de partage des eaux sera la séparation entre les zones de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Un bassin versant est un bassin dont toutes les eaux ont le même exutoire, par exemple une rivière ou l&#039;océan.&lt;br /&gt;
&lt;br /&gt;
Pour déterminer la ligne de partage des eaux, plusieurs méthodes sont possibles :&lt;br /&gt;
&lt;br /&gt;
* On place une source d&#039;eau dans chaque minimum local (donc dans chaque bassin) et on inonde, quand deux bassins se rencontrent on place une ligne de partage des eaux.&lt;br /&gt;
Les algorithmes utilisant ce principe sont appelés des algorithmes par inondation.&lt;br /&gt;
&lt;br /&gt;
* Une deuxième méthode est de considérer que des deux côtés de la ligne l&#039;écoulement de l&#039;eau se termine dans des bassins versants différents.&lt;br /&gt;
Dans l&#039;algorithme associé, on va faire &amp;quot;s&#039;écouler&amp;quot; de l&#039;eau sur chaque pixel et tracer une ligne de partage entre deux pixels (ou sur l&#039;un des deux pixels) quand les eaux qui s&#039;écoulent depuis ces pixels arrive dans des minima locaux différents.&lt;br /&gt;
Ce sont les algorithmes par ruissellement.&lt;br /&gt;
&lt;br /&gt;
=== Algorithme utilisé (créé par Luc Vincent et Pierre Soille) ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme choisi est un algorithme par inondation.&lt;br /&gt;
&lt;br /&gt;
Les pixels de faible intensité vont donc être traités en priorité.&lt;br /&gt;
&lt;br /&gt;
La première étape est donc de trier les pixels de l&#039;image par ordre croissant d&#039;intensité.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme va traiter les pixels d&#039;intensité la plus basse, puis une fois ceux-ci traités, les pixels avec une intensité légèrement supérieure sont traités, et ainsi de suite jusqu&#039;à avoir traité toute l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Trois problèmes se posent alors :&lt;br /&gt;
* Comment inonder un étage, c&#039;est à dire de quelle manière traiter les pixels de même intensité, à partir de bassins connus ?&lt;br /&gt;
* Comment déterminer à quel bassin appartient un pixel ?&lt;br /&gt;
* Comment repérer les nouveaux bassins ?&lt;br /&gt;
&lt;br /&gt;
==== Inonder un étage (pixels d&#039;intensité identique) ====&lt;br /&gt;
&lt;br /&gt;
Chaque bassin doit progresser à la même vitesse sur un étage, c&#039;est à dire que la ligne de partage des eaux qui sépare deux bassins doit être à égale distance de chacun.&lt;br /&gt;
&lt;br /&gt;
Chaque pixel est à une &amp;quot;distance&amp;quot; d&#039;un bassin. Par exemple les pixels collés à un bassin connu sont à une distance de 1. Leurs voisins sont eux à une distance de 2 (à condition qu&#039;ils ne soient pas collés à un bassin connu).&lt;br /&gt;
&lt;br /&gt;
[[File:file_distance.png|frame|Distance à laquelle se trouve les pixels du bassin connu (bleu)]]&lt;br /&gt;
&lt;br /&gt;
Pour que les bassins s&#039;étendent correctement, on traite d&#039;abord tous les pixels à distance 1, puis ceux à distance 2 et ainsi de suite.&lt;br /&gt;
&lt;br /&gt;
Une file d&#039;attente est créée ; la première étape est de placer les pixels à une distance de 1 dans la file d&#039;attente.&lt;br /&gt;
On va pouvoir traiter ces pixels là. On sait également que leurs voisins sont à une distance de 2, et pourront donc être traités lorsque les pixels à distance 1 seront associés à un bassin.&lt;br /&gt;
&lt;br /&gt;
Les voisins non-traités des pixels à une distance de 1 sont donc les prochains pixels de l&#039;étage à traiter.&lt;br /&gt;
Ils sont placés dans une autre file d&#039;attente, qui sera parcourue une fois que la première a été entièrement utilisée.&lt;br /&gt;
&lt;br /&gt;
Ensuite, les voisins des pixels à une distance de 2 seront les prochains pixels à traiter, et on va recommencer cette suite d&#039;opérations.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les pixels sont placés dans la file d&#039;attente :&lt;br /&gt;
&lt;br /&gt;
* Tout d&#039;abord les pixels directement voisins d&#039;un bassin connu sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_1.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Puis une fois que les pixels dans la file d&#039;attente ont été traités, les pixels voisins des pixels qui viennent d&#039;être traités sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_2.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Cette étape se répète tant qu&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_3.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi le traitement d&#039;une file d&#039;attente (file d&#039;attente d&#039;une distance particulière) fait avancer d&#039;un pixel tous les bassins, et tous les bassins progressent à la même vitesse.&lt;br /&gt;
&lt;br /&gt;
==== Détermination du bassin auquel appartient un pixel ====&lt;br /&gt;
&lt;br /&gt;
Avec cette manière de faire progresser les bassins, le bassin auquel appartient un pixel est déterminé par les bassins auxquels appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
Plusieurs cas sont alors possibles lorsqu&#039;un pixel est traité.&lt;br /&gt;
&lt;br /&gt;
[[File:choix_label.png]] &lt;br /&gt;
&lt;br /&gt;
*Un des cas les plus simples est lorsque tous les voisins traités du pixel appartiennent au même bassin.Le bassin s&#039;étend alors, et le pixel à traiter est associé au bassin auquel appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
*Cette même décision est prise si tous les pixels voisins traités appartiennent au même bassin sauf certains qui appartiennent à une ligne de partage des eaux (LPE). L&#039;autre décision possible est de prolonger la LPE, cependant des lignes épaisses (et imprécises) seraient créées.&lt;br /&gt;
&lt;br /&gt;
*Si les pixels voisins traités du pixel appartiennent à une LPE, le bassin auquel appartient le pixel est inconnu. On ne peut pas &amp;quot;prolonger&amp;quot; la ligne car on ne connaît alors pas à quelle distance d&#039;un bassin se trouve le pixel. Si un pixel appartient à une LPE, ses voisins ne sont donc pas placés en file d&#039;attente.&lt;br /&gt;
&lt;br /&gt;
*Lorsque les pixels voisins appartiennent à des bassins différents, alors le pixel appartient à une LPE ; on sépare les bassins au moment de leur rencontre.&lt;br /&gt;
&lt;br /&gt;
*Le dernier cas se présente lorsque tous les pixels voisins sont traités et qu&#039;ils appartiennent tous à une LPE. Pour éviter qu&#039;un bassin d&#039;un seul pixel soit créé, on prolonge la LPE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En résumé :&lt;br /&gt;
*S&#039;il n&#039;y a qu&#039;un seul bassin autour du pixel en excluant les LPE, le pixel appartient à ce bassin.&lt;br /&gt;
*Si en excluant les LPE il y a plusieurs bassins autour du pixel, le pixel appartient à une LPE.&lt;br /&gt;
*Si le pixel est entouré par des LPE, alors il appartient à une LPE.&lt;br /&gt;
&lt;br /&gt;
==== Création de nouveaux bassins ====&lt;br /&gt;
&lt;br /&gt;
Lors de la propagation des bassins, si une file d&#039;attente est vide alors tous les pixels de l&#039;étage qui appartenaient à un bassin déjà connu ont été traités.&lt;br /&gt;
&lt;br /&gt;
Les pixels restants appartiennent donc à de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Un nouveau bassin est donc créé, le premier pixel qui lui appartient est choisi parmi un des pixels de l&#039;étage non-traités.&lt;br /&gt;
&lt;br /&gt;
On réinitialise alors la file d&#039;attente, et on place les voisins non-traités du pixel associé au nouveau bassin dedans.&lt;br /&gt;
&lt;br /&gt;
On recommence alors le traitement de la file d&#039;attente, et tous les pixels qui appartiennent à ce bassin lui seront attribués.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les nouveaux bassins sont créés, sur une petite image de 5x5.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_image.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Plusieurs étapes se déroulent alors :&lt;br /&gt;
&lt;br /&gt;
* Le traitement d&#039;un étage commence. Les pixels appartenant à un bassin connu se situent à un étage inférieur.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_1.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
* Tous les pixels appartenant à un bassin connu ont été traités ; il reste cependant des pixels non-traités à cet étage. Ils appartiennent donc à un ou plusieurs nouveaux bassins. On choisit donc un pixel non-traité de l&#039;étage, et on lui attribue un nouveau bassin.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_2.png|300px]] [[File:minima_legende.png|500px]]  &lt;br /&gt;
&lt;br /&gt;
* On propage le nouveau bassin créé.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_3.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
Ces étapes sont répétées tant qu&#039;il reste des pixels non-traités à l&#039;étage en cours de traitement. On détecte ainsi tous les nouveaux bassins de l&#039;étage.&lt;br /&gt;
&lt;br /&gt;
Une fois que tous les pixels d&#039;un étage ont été traités, on passe à l&#039;étage supérieur.&lt;br /&gt;
&lt;br /&gt;
==== Résultats ====&lt;br /&gt;
Voici un exemple de résultat que donne cet algorithme :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_brute.png]]&#039;&#039;A gauche le gradient d&#039;une image ; à droite le résultat de l&#039;algorithme de détermination des LPE&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On s&#039;aperçoit que l&#039;algorithme est très sensible au bruit, c&#039;est à dire aux légères variations d&#039;intensité qui créent des minima locaux.&lt;br /&gt;
&lt;br /&gt;
On veut donc améliorer les résultats obtenus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Réduire l&#039;impact du bruit ===&lt;br /&gt;
Plusieurs méthodes sont applicables pour réduire l&#039;impact du bruit sur le résultat.&lt;br /&gt;
&lt;br /&gt;
==== Ajout d&#039;une tolérance ====&lt;br /&gt;
La plus efficace est d&#039;ajouter une &amp;quot;tolérance&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
En effet, dans l&#039;algorithme décrit précédemment, les pixels appartiennent à un même étage si leurs intensités sont exactement identiques.&lt;br /&gt;
&lt;br /&gt;
On considére qu&#039;un pixel appartient à l&#039;étage traité si la différence entre son intensité et l&#039;intensité de l&#039;étage traitée est inférieure à un nombre, qu&#039;on appelle tolérance.&lt;br /&gt;
&lt;br /&gt;
Ainsi la sensibilité au bruit de l&#039;algorithme est réduite car les petites variations d&#039;intensité ne créent plus de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple sur une petite image. &lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance_image.png]]&lt;br /&gt;
&lt;br /&gt;
Tout d&#039;abord, s&#039;il n&#039;y a pas de tolérance, les pixels d&#039;intensité 130 et le pixel d&#039;intensité 135 sont à des étages différents.&lt;br /&gt;
Deux bassins sont donc créés lors du traitement de l&#039;étage d&#039;intensité 130 (bleu et vert, le noir représente une LPE).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_sans_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Cependant, avec l&#039;ajout d&#039;une tolérance (par exemple 10), les trois pixels sont considérés au même étage (car on a 135-130 &amp;lt; 10).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Ainsi une unique bassin est créé.&lt;br /&gt;
&lt;br /&gt;
Le résultat est alors bien plus précis sur de vraies images:&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_res_tolerance.png]] &#039;&#039;A gauche le résultat (de cette [[#Résultats|image]]) de l&#039;algorithme sans tolérance, à droite le résultat avec une tolérance de 5&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Flouter l&#039;image avant le traitement ====&lt;br /&gt;
&lt;br /&gt;
Une deuxième méthode est de traiter l&#039;image avant d&#039;appliquer l&#039;algorithme.&lt;br /&gt;
Si l&#039;image est floutée avant application de l&#039;algorithme, le bruit aura moins d&#039;impact, car les changements de valeur seront atténués.&lt;br /&gt;
Dans l&#039;image floutée, chaque valeur d&#039;un pixel est une moyenne des valeurs des pixels voisins à ce pixel dans l&#039;image originale.&lt;br /&gt;
&lt;br /&gt;
Pour cela, on peut convoluer l&#039;image par cette matrice :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Chaque produit de convolution donnera un entier qui devra être divisé par 9 ; on ajoute  les valeurs des 9 pixels dans un carré de 3x3 autour du pixel dont on veut la nouvelle valeur, pour avoir la moyenne des valeurs dans ce carré on doit alors diviser par 9.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas de bons résultats si elle est la seule utilisée. Cependant, combinée avec l&#039;utilisation de la tolérance, le nombre de petites zones est fortement réduit.&lt;br /&gt;
&lt;br /&gt;
Les LPE sont en contrepartie moins précises.&lt;br /&gt;
&lt;br /&gt;
Exemple de résultat :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou_tolerance5.png]]&lt;br /&gt;
&lt;br /&gt;
==== Fusion des petites zones ====&lt;br /&gt;
&lt;br /&gt;
Une troisième solution est d&#039;empêcher la création de zones trop petites, en indiquant une taille minimale.&lt;br /&gt;
Si des bassins plus petits que cette taille rencontrent un autre bassin, alors ils fusionnent, et le point de leur rencontre est associé à ce nouveau bassin issu de la fusion.&lt;br /&gt;
&lt;br /&gt;
Si au point de rencontre des bassins se rencontrent deux zones plus grandes que la taille minimale, on doit tout de même placer une LPE, pour séparer les deux &amp;quot;grands&amp;quot; bassins.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas non plus de résultats intéressants si utilisée seule, il est nécessaire de la combiner avec l&#039;ajout d&#039;une tolérance.&lt;br /&gt;
&lt;br /&gt;
Un des résultats obtenus avec cette méthode est (où la taille minimale d&#039;un bassin est de 5px) :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_fusion5.png]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
Pour conclure, on peut citer deux avantages de cet algorithme. Tout d&#039;abord, les lignes de partage des eaux créées sont peu épaisses ; elles ne font jamais plus de 3 pixels de large, et se limitent dans la plupart des cas à une épaisseur d&#039;un seul pixel.&lt;br /&gt;
&lt;br /&gt;
Ensuite, l&#039;algorithme est de complexité linéaire, c&#039;est à dire que le temps d’exécution dépend linéairement du nombre de pixels de l&#039;image sur laquelle on applique l&#039;algorithme.&lt;br /&gt;
Voici quelques mesures du temps d&#039;exécution selon le nombre de pixels de l&#039;image en entrée (les images utilisées pour ces tests ne représentent rien et contiennent de nombreuses zones).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tableau_temps.png|border|mesures du temps d&#039;exécution]]&lt;br /&gt;
[[File:lpe_temps.png|border|temps d&#039;exécution en fonction du nombre de pixels]]&lt;br /&gt;
&lt;br /&gt;
== Code source ==&lt;br /&gt;
Voici le code source de l&#039;algorithme.&lt;br /&gt;
&lt;br /&gt;
Pour manipuler des images, j&#039;ai utilisé la bibliothèque PILLOW.&lt;br /&gt;
&lt;br /&gt;
Les principales fonctions utilisées dans cette bibliothèque sont :&lt;br /&gt;
*Image.open(fichier) : ouvrir le fichier fichier en tant qu&#039;objet image&lt;br /&gt;
*image.getpixel((x,y)) : récupérer la valeur du pixel aux coordonnées (x,y) dans un objet Image (appelé ici &#039;image&#039;)&lt;br /&gt;
*image.setpixel((x,y),(r,v,b)) : Changer la valeur du pixel aux coordonnées (x,y) dans un objet Image (appelé ici &#039;image&#039;) ; (r,v,b) sont les valeurs rouge,vert,bleu.&lt;br /&gt;
*image.save(nom) : sauvegarder l&#039;image sous le nom souhaité.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
###########################################################&lt;br /&gt;
# RUET Nils&lt;br /&gt;
# Algorithme de détermination de ligne de partage des eaux, créé par Luc Vincent et Pierre Soille&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   IMPORTATIONS&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
from PIL import Image&lt;br /&gt;
from math import sqrt&lt;br /&gt;
from random import randint&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   CONSTANTES ET VARIABLES GLOBALES&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#Chaque pixel a 3 valeurs qui lui sont associées ; son intensité, l&#039;information sur s&#039;il a déjà été placé en file d&#039;attente, et le label qui lui a été associé.&lt;br /&gt;
#Ces informations sont stockés dans un tableau et ces constantes sont les indices auxquels trouver les informations voulues.&lt;br /&gt;
VALEUR = 0&lt;br /&gt;
LABELLED = 1&lt;br /&gt;
LABEL = 2&lt;br /&gt;
&lt;br /&gt;
indiceEtage = 0# Lorsque l&#039;on traite un étage, certains pixels ne sont pas traitables à partir des données connues. Ils correspondent à de nouveaux bassins.&lt;br /&gt;
               # On dispose d&#039;une liste des pixels de l&#039;étage ; lorsqu&#039;on doit créer un bassin, on cherche donc le premier pixel dans cette liste qui n&#039;est pas labellé.&lt;br /&gt;
               # Cette variable contient le nombre de pixels &amp;quot;consécutifs&amp;quot; traités, c&#039;est à dire l&#039;indice du dernier pixel non traité trouvé dans la liste.&lt;br /&gt;
               # Ainsi on n&#039;a pas à reparcourir la liste entière lorsque l&#039;on créé plusieurs nouveaux bassins à un étage car on sait que tous les pixels &amp;quot;avant&amp;quot; (dans la liste) celui choisi pour créer un nouveau bassin ont été labellés.&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   CALCUL DU GRADIENT&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
def deterCoef(mat):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Cette fonction prend un masque de convolution en entrée. Elle permet de déterminer un décalage et un coefficient utilisés pour ramener toutes les valeurs&lt;br /&gt;
    obtenues après convolution entre 0 et 255&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    sommeP=0&lt;br /&gt;
    sommeN=0&lt;br /&gt;
    for i in range(len(mat)):       #On compte les coefficients négatifs et les coefficients positifs dans le masque&lt;br /&gt;
        for j in range(len(mat[0])):&lt;br /&gt;
            if mat[i][j] &amp;gt; 0:&lt;br /&gt;
                sommeP+=mat[i][j]&lt;br /&gt;
            else:&lt;br /&gt;
                sommeN-=mat[i][j]&lt;br /&gt;
    decal = sommeN * 255            #Les coefficients négatifs permettent de connaître la valeur à ajouter pour n&#039;obtenir que des valeurs positives après la convolution&lt;br /&gt;
    coef = sommeN+sommeP            #La somme des valeurs absolues des coefficients permet de savoir comment ramener toutes ces valeurs entre 0 et 255.&lt;br /&gt;
    return (coef, decal)&lt;br /&gt;
&lt;br /&gt;
def Convolution(image, x, y, filtre):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Effectue le produit de convolution entre un filtre et l&#039;enourage du pixel aux coordonnées (x,y) dans l&#039;image &#039;image&#039; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    somme = 0&lt;br /&gt;
    centre = len(filtre)//2&lt;br /&gt;
    for i in range(-centre,centre+1):           #i et j indiquent la position relative du pixel dont on a besoin dans l&#039;image par rapport au pixel dont on calcule la nouvelle valeur.&lt;br /&gt;
        for j in range(-centre,centre+1):&lt;br /&gt;
            if filtre[i+centre][j+centre] != 0: #Vu qu&#039;on multiplie la valeur d&#039;un pixel par une valeur dans le filtre, on vérifie que la valeur dans le filtre ne vaut pas 0.&lt;br /&gt;
                if 0&amp;lt;=x+i&amp;lt;image.width and 0&amp;lt;=y+j&amp;lt;image.height: #Si le pixel voulu est dans l&#039;image, xl (x local) et yl (y local) sont ses coordonnées.&lt;br /&gt;
                    xl = x+i&lt;br /&gt;
                    yl = y+j&lt;br /&gt;
                else:                           #Sinon on va chercher la valeur du pixel en symlétrie par rapport au pixel dont on veut la nouvelle valeur&lt;br /&gt;
                    xl = x-i&lt;br /&gt;
                    yl = y-j&lt;br /&gt;
                    &lt;br /&gt;
                if not 0&amp;lt;=xl&amp;lt;image.width or not 0&amp;lt;=yl&amp;lt;image.height: #Si après cela le pixel n&#039;est toujours pas dans l&#039;image, on se réfère à la valeur du pixel dont on veut la nouvelle valeur.&lt;br /&gt;
                    xl = x&lt;br /&gt;
                    yl = y                    &lt;br /&gt;
                somme+= (image.getpixel((xl,yl))[0] *filtre[i+centre][j+centre])&lt;br /&gt;
    return somme&lt;br /&gt;
&lt;br /&gt;
def AppliqueFiltre(image, filtre):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permet d&#039;appliquer le masque de convolution &#039;filtre&#039; (double tableau) à tous les pixels de l&#039;image &#039;image&#039; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = image.copy()&lt;br /&gt;
    (coef, decal) = deterCoef(filtre)&lt;br /&gt;
    for x in range(image.width):            #Pour chaque pixel de l&#039;image&lt;br /&gt;
        for y in range(image.height):&lt;br /&gt;
            somme = Convolution(image,x,y,filtre) #On prend le résultat de la convolution&lt;br /&gt;
            val = round((somme+decal)/coef)       #On ramène ce résultat entre 0 et 255&lt;br /&gt;
            res.putpixel((x,y),(val,val,val))     #On met le pixel dans l&#039;image résultat à cette valeur&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def Filtre(image, filtre, nom_sortie=&amp;quot;filtre&amp;quot;, saveIm=True):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permet d&#039;appeler la fonction qui permet de convoluer l&#039;image &#039;image&#039; par le masque &#039;filtre&#039; et choisir si le résultat est sauvegardé dans un fichier image ou non&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = AppliqueFiltre(image, filtre)&lt;br /&gt;
    if saveIm:&lt;br /&gt;
        res.save(&amp;quot;{}_{}&amp;quot;.format(nom_sortie, fichierImage))&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
def FiltreSobel(fichierIm):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permer de calculer la norme du gradient de l&#039;image &#039;fichierIm&#039; (nom du fichier image) en chaque point avec la méthode du filtre de Sobel&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    image = Image.open(fichierIm)&lt;br /&gt;
    res = image.copy()&lt;br /&gt;
    masquex = [[-1,0,1],[-2,0,2],[-1,0,1]]&lt;br /&gt;
    masquey = [[-1,-2,-1],[0,0,0],[1,2,1]]&lt;br /&gt;
    for x in range(image.width):                                #Pour chaque pixel de l&#039;image&lt;br /&gt;
        for y in range(image.height):&lt;br /&gt;
            dx = Convolution(image,x,y,masquex)                 #On approxime ses dérivées partielles&lt;br /&gt;
            dy = Convolution(image,x,y,masquey)                 #&lt;br /&gt;
&lt;br /&gt;
            val = round(sqrt(dx**2 + dy**2)*255/(sqrt(2)*1020)) #On calcule la norme du gradient, qu&#039;on ramène entre 0 et 255.&lt;br /&gt;
            res.putpixel((x,y),(val,val,val))                   #Cette valeur est associée au pixel dans le résultat&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#########################################################################################################&lt;br /&gt;
#   ALGORITHME LIGNE DE PARTAGE DES EAUX&lt;br /&gt;
#########################################################################################################&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def InitTableau(image):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Initialise le double tableau contenant les informations sur chaque pixel; aux indices [x][y] on accède aux informations du pixel (x,y). Ces informations sont l&#039;intensité, l&#039;avancement du traitement et le label.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tableau = [0]*image.width                                       #On initialise un tableau de la taille de l&#039;image&lt;br /&gt;
    for i in range(len(tableau)):&lt;br /&gt;
        tableau[i]=[0]*image.height&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(tableau)):                                   #On initialise les informations de chaque pixel :&lt;br /&gt;
        for j in range(len(tableau[i])):&lt;br /&gt;
            tableau[i][j] = [image.getpixel((i,j))[0],False,&amp;quot;init&amp;quot;] #Sa valeur dans l&#039;image, l&#039;avancement du traitement (False si le pixel n&#039;a pas été traité, True sinon), et le label qui lui est associé&lt;br /&gt;
    return tableau&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def InitListeOrdonnee(image):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Initialise la liste des pixels de l&#039;image, triés par intensité ; chaque case contient la position du pixel attribué et son intensité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    liste = []&lt;br /&gt;
    for i in range(image.width):&lt;br /&gt;
        for j in range(image.height):&lt;br /&gt;
            liste.append([image.getpixel((i,j))[0],i,j])#On regarde chaque pixel de l&#039;image et on ajoute dans une case de la liste son intensité, son abcisse, son ordonnée&lt;br /&gt;
    liste.sort(key=lambda pixel: pixel[0])              #On trie cette liste en fonction de l&#039;intensité des pixels (ordre croissant)&lt;br /&gt;
    return liste&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def PixelsEtage(listeOrdonnee, intensite, indice, tablImage, tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend en entrée la liste ordonnée des pixels de l&#039;image, une intensité, l&#039;indice à partir duquel commence cette intensité dans la liste, et une tolérance.&lt;br /&gt;
       Renvoie la liste des pixels d&#039;un &amp;quot;étage&amp;quot; d&#039;intensité, plus précisément :&lt;br /&gt;
       La liste des pixels non-labellés de l&#039;intensité choisie plus ou moins la tolérance donnée. L&#039;indice à partir duquel commence l&#039;intensité choisie est modifié par une autre partie&lt;br /&gt;
       du programme ; on sait que tous les pixels d&#039;une valeur inférieure à l&#039;intensité choisie ont déjà été traités par l&#039;algorithme.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    pixels = []&lt;br /&gt;
    nouvelIndice = indice&lt;br /&gt;
    while nouvelIndice &amp;lt; len(listeOrdonnee) and (listeOrdonnee[nouvelIndice][0] - intensite) &amp;lt;= tolerance:#On ajoute à la liste des pixels de l&#039;étage intensité les pixels qui ont une intensité dans l&#039;intervalle souhaité&lt;br /&gt;
        if not tablImage[listeOrdonnee[nouvelIndice][1]][listeOrdonnee[nouvelIndice][2]][LABELLED]:       #Et non labellés&lt;br /&gt;
            pixels.append(listeOrdonnee[nouvelIndice])&lt;br /&gt;
        nouvelIndice+=1&lt;br /&gt;
    return pixels&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def labelsPixelsVoisins(x, y, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonctions qui renvoie la liste des labels des pixels voisins au pixel situé aux coordonnée entrées en paramètre, ignore les pixels non labellés&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    labels = []&lt;br /&gt;
&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins:&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]) and tablImage[x+i][y+j][LABEL] != &amp;quot;init&amp;quot;:&lt;br /&gt;
            labels.append(tablImage[x+i][y+j][LABEL])        &lt;br /&gt;
    return labels&lt;br /&gt;
&lt;br /&gt;
def PixelTraitable(xPixel, yPixel, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonction qui renvoie True si le pixel aux coordonnées entrées en paramètre est labellable, False sinon&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    labels = labelsPixelsVoisins(xPixel, yPixel, tablImage)&lt;br /&gt;
    for label in labels:&lt;br /&gt;
        if label!=&amp;quot;watershed&amp;quot;:#Un pixel est labellable s&#039;il a un pixel labellé par autre chose qu&#039;une ligne de partage parmi ses voisins.&lt;br /&gt;
            return True&lt;br /&gt;
&lt;br /&gt;
    voisinsDansImage = 0                                                #Sinon si le pixel n&#039;a pas de pixels labellés par autre chose que watershed parmi ses voisins,&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins:                                              #On détermine son nombre de voisins (4 au centre, 3 sur les bords et 2 dans les coins)&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=xPixel+i&amp;lt;len(tablImage) and 0&amp;lt;=yPixel+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
            voisinsDansImage += 1&lt;br /&gt;
    return len(labels)==voisinsDansImage                                #Le pixel est labellable si tous ses voisins sont des lignes de partage des eaux&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def nouveauLabel(listePixels, tablImage, fileAttente, listeLabels, intensite, tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Cette fonction est appelée lorsque l&#039;algorithme n&#039;a pas traité tous les pixels d&#039;un étage, mais qu&#039;aucun pixel n&#039;a été placé en file attente &amp;lt;=&amp;gt; aucun pixel non traité n&#039;est traitable à cet étage.&lt;br /&gt;
       A partir de la liste des pixels de l&#039;étage, de la liste des labels attribués, de l&#039;intensité de l&#039;étage et de la tolérance, on modifie alors la file d&#039;attente&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    global indiceEtage&lt;br /&gt;
    while tablImage[listePixels[indiceEtage][1]][listePixels[indiceEtage][2]][LABELLED]: #On parcourt la liste des pixels de l&#039;étage pour en trouver un non labellé&lt;br /&gt;
        indiceEtage+=1     &lt;br /&gt;
    x = listePixels[indiceEtage][1]&lt;br /&gt;
    y = listePixels[indiceEtage][2]&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins: #On regarde les voisins du premier pixel non labellé trouvé&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]) and not tablImage[x+i][y+j][LABELLED] and (tablImage[x+i][y+j][VALEUR] - intensite) &amp;lt;= tolerance:&lt;br /&gt;
            #Si un voisin est dans le bon intervalle d&#039;intensité, non labellé et dans l&#039;image, alors on le place dans la file d&#039;attente&lt;br /&gt;
            tablImage[x+i][y+j][LABELLED] = True&lt;br /&gt;
            fileAttente[0].append([tablImage[x+i][y+j][VALEUR], x+i, y+j])&lt;br /&gt;
&lt;br /&gt;
    tablImage[x][y][LABEL] = len(listeLabels)#On créé un nouveau label en augmentant de 1 le numéro du dernier label ; cela correspond à la longueur de la liste des labels&lt;br /&gt;
                                             #Puis on l&#039;associe au pixel non traité trouvé dans l&#039;étage&lt;br /&gt;
    tablImage[x][y][LABELLED] = True         #Ce pixel et désormais traité&lt;br /&gt;
    listeLabels.append(1)                    #On ajoute le nouveau label créé à la liste des labels ; le bassin associé à ce label est de taille 1px initalement&lt;br /&gt;
    return fileAttente                       #On renvoie la nouvelle file d&#039;attente ; les voisins traitables du pixel trouvés   &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def AjoutFileAttente(fileAttente, distance, tablImage,intensite,tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;A partir des pixels traités au moment de l&#039;appel de cette fonction, on détermine les prochains pixels qui pourront être traités par la suite.&lt;br /&gt;
       Ces pixels sont les voisins des pixels déjà traités (à l&#039;exception des lignes de partage) du même étage, non traités et dans l&#039;image&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    fileAttente.append([])             #On ajoute une file d&#039;attente pour la distance suivante&lt;br /&gt;
    for pixel in fileAttente[distance]:#Pour chaque pixel traité à la distance précédente&lt;br /&gt;
        x = pixel[1]&lt;br /&gt;
        y = pixel[2]&lt;br /&gt;
        if tablImage[x][y][LABEL] != &amp;quot;watershed&amp;quot;:#S&#039;il n&#039;est pas une ligne de partage, il appartient à un bassin et ses voisins sont potentiellement traitables      &lt;br /&gt;
            voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
            for coords in voisins:&lt;br /&gt;
                i = coords[0]&lt;br /&gt;
                j = coords[1]&lt;br /&gt;
                if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
                    if (tablImage[x+i][y+j][VALEUR] - intensite) &amp;lt;= tolerance and not tablImage[x+i][y+j][LABELLED]:#Les voisins traitables sont les pixel du même étage non labellés&lt;br /&gt;
                        tablImage[x+i][y+j][LABELLED] = True&lt;br /&gt;
                        fileAttente[distance+1].append([tablImage[x+i][y+j][VALEUR], x+i, y+j])#On les ajoute à la file d&#039;attente suivante&lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
def valeursUniques(T):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend un tableau en entrée et supprime les valeurs qui s&#039;y trouvent en double&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for e in T:&lt;br /&gt;
        if not e in res:&lt;br /&gt;
            res.append(e)&lt;br /&gt;
    return res&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
def fusionZones(labelsChange, nouvLabel, listeLabels, x, y, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; A partir d&#039;une liste de labels à changer (labelsChange), du nouveau label qui leur sera associé, et d&#039;un pixel de départ aux coordonnées x,y&lt;br /&gt;
    On fusionne les bassins qui se rencontrent en x,y en modifiant le label des pixels de ces bassins.&lt;br /&gt;
    Chaque fois qu&#039;un pixel change de label, on change le label de ses voisins si cela est nécéssaire&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tablImage[x][y][LABEL] = nouvLabel&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
&lt;br /&gt;
    file = [[x,y]]&lt;br /&gt;
    indiceFile = 0&lt;br /&gt;
    while indiceFile != len(file):#Tant que tous les pixels de la file n&#039;ont pas été associés au nouveau label&lt;br /&gt;
        pixel = file[indiceFile]  #On prend le prochain pixel dans la file&lt;br /&gt;
        for coords in voisins:    #Et on regarde chacun de ses voisins                                 &lt;br /&gt;
            i = coords[0]&lt;br /&gt;
            j = coords[1]&lt;br /&gt;
            if 0&amp;lt;=pixel[0]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[1]+j&amp;lt;len(tablImage[0]) and (tablImage[pixel[0]+i][pixel[1]+j][LABEL] in labelsChange): #Si un pixel voisins dans l&#039;image a un label qui doit être changé&lt;br /&gt;
                file.append([pixel[0]+i,pixel[1]+j])#On l&#039;ajoute à la file&lt;br /&gt;
                tablImage[pixel[0]+i][pixel[1]+j][LABEL] = nouvLabel#On lui attribue son nouveau label&lt;br /&gt;
                listeLabels[nouvLabel]+=1#On augmente le nombre de pixels qui appartiennent au nouveau bassin&lt;br /&gt;
        indiceFile+=1&lt;br /&gt;
  &lt;br /&gt;
def determinationLabels(fileAttente, distance, tablImage, listeLabels, tailleMini):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Procédure qui détermine le label attribué à un pixel en fonction des labels des pixels voisins&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for pixel in fileAttente[distance]:                             #On attribue un label à chaque pixel dans la file d&#039;attente&lt;br /&gt;
        labels = labelsPixelsVoisins(pixel[1], pixel[2], tablImage) #On récupère les labels de ses voisins&lt;br /&gt;
        lstLabels = valeursUniques(labels)                          #On supprime les doublons,&lt;br /&gt;
        if (&amp;quot;watershed&amp;quot; in lstLabels):                              #et le label de ligne de partage des eaux&lt;br /&gt;
            lstLabels.remove(&amp;quot;watershed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        voisinsDansImage = 0&lt;br /&gt;
        voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
        for coords in voisins:                                      #On compte le nombre de pixels voisins dans l&#039;image ; 4 si le pixel est au centre de l&#039;image, 3 s&#039;il est au bord etc.&lt;br /&gt;
            i = coords[0]&lt;br /&gt;
            j = coords[1]&lt;br /&gt;
            if 0&amp;lt;=pixel[1]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[2]+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
                voisinsDansImage += 1&lt;br /&gt;
        if len(lstLabels) == 0 and len(labels)==voisinsDansImage:   #Si le pixel est entouré de watershed, on lui attribue ce label&lt;br /&gt;
            tablImage[pixel[1]][pixel[2]][LABEL] = &amp;quot;watershed&amp;quot;&lt;br /&gt;
                &lt;br /&gt;
        elif len(lstLabels) == 1:                                   #Si le pixel n&#039;a qu&#039;un seul label hormis les watershed autour de lui, on lui attribue ce label&lt;br /&gt;
            tablImage[pixel[1]][pixel[2]][LABEL] = lstLabels[0]&lt;br /&gt;
            listeLabels[lstLabels[0]] += 1&lt;br /&gt;
            &lt;br /&gt;
        elif len(lstLabels) &amp;gt; 1:                                    #S&#039;il y a plusieurs labels autour de lui, il se trouve entre deux bassins et est donc une ligne de partage des eaux ; si les bassins sont petits on les fusionne&lt;br /&gt;
            i = 0&lt;br /&gt;
            plusGrand = []&lt;br /&gt;
            moinsGrand = []&lt;br /&gt;
            while i&amp;lt;len(lstLabels):&lt;br /&gt;
                if listeLabels[lstLabels[i]]&amp;gt;tailleMini:&lt;br /&gt;
                    plusGrand.append(lstLabels[i])&lt;br /&gt;
                else:&lt;br /&gt;
                    moinsGrand.append(lstLabels[i])&lt;br /&gt;
                i+=1&lt;br /&gt;
            if len(moinsGrand)&amp;gt;0 and len(plusGrand)&amp;lt;2:  #Si des bassins sont en dessous de la taille minimale et qu&#039;il n&#039;y a pas plusieurs bassins au dessus de cette taille &lt;br /&gt;
                if len(plusGrand)==1:                   #Si il n&#039;y a qu&#039;un bassin au dessus, tous les petits bassins lui seront rattachés&lt;br /&gt;
                    labelsChange = moinsGrand[:]&lt;br /&gt;
                    nouvLabel = plusGrand[0]&lt;br /&gt;
                elif len(plusGrand)==0:                 #S&#039;il n&#039;y en a aucun, les petits bassins fusionnent entre eux&lt;br /&gt;
                    labelsChange = moinsGrand[1:]&lt;br /&gt;
                    nouvLabel = moinsGrand[0]&lt;br /&gt;
        &lt;br /&gt;
                tablImage[pixel[1]][pixel[2]][LABEL] = nouvLabel #Les bassins vont fusionner, le pixel que l&#039;on regardait appartient donc au nouveau bassin&lt;br /&gt;
                listeLabels[nouvLabel] += 1                      #On augmente le compteur de pixels appartenant à ce bassin&lt;br /&gt;
                &lt;br /&gt;
                voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
                for coords in voisins:                      #On regarde chaque voisin, s&#039;il est dans l&#039;image et que son label doit etre changé, on appelle la procédure qui permet de changer les labels du bassin&lt;br /&gt;
                    i = coords[0]&lt;br /&gt;
                    j = coords[1]&lt;br /&gt;
                    if 0&amp;lt;=pixel[1]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[2]+j&amp;lt;len(tablImage[0]) and tablImage[pixel[1]+i][pixel[2]+j][LABEL] in labelsChange:&lt;br /&gt;
                        fusionZones(labelsChange, nouvLabel, listeLabels, pixel[1]+i, pixel[2]+j, tablImage)&lt;br /&gt;
                        &lt;br /&gt;
            else:                                       #Si aucun bassin n&#039;est trop petit ou si au moins 2 sont au dessus de la taille minimale, le pixel est une ligne de partage des eaux&lt;br /&gt;
                tablImage[pixel[1]][pixel[2]][LABEL] = &amp;quot;watershed&amp;quot;&lt;br /&gt;
&lt;br /&gt;
                &lt;br /&gt;
def Algo(image, tolerance, tailleMini):&lt;br /&gt;
    global indiceEtage&lt;br /&gt;
    &lt;br /&gt;
    #### INITALISATION ###&lt;br /&gt;
&lt;br /&gt;
    #Initaliser tableaux :&lt;br /&gt;
    #   -double tableau qui contient les informations de chaque pixel    &lt;br /&gt;
    tableauImage = InitTableau(image)&lt;br /&gt;
    &lt;br /&gt;
    #   -la liste des labels&lt;br /&gt;
    listeLabels = []&lt;br /&gt;
    &lt;br /&gt;
    #   -Initialiser la liste des pixels triée par leur intensité&lt;br /&gt;
    listePixelsOrdonnee = InitListeOrdonnee(image)&lt;br /&gt;
&lt;br /&gt;
    #   -l&#039;indice de parcours de la liste des pixels triée&lt;br /&gt;
    indice = 0&lt;br /&gt;
    &lt;br /&gt;
    ### TRAITEMENT POUR CHAQUE INTENSITE ###&lt;br /&gt;
    for intensite in range(256):&lt;br /&gt;
        #Initialiser la liste des pixels appartenant à l&#039;étage traité&lt;br /&gt;
        pixelsMemeEtage = PixelsEtage(listePixelsOrdonnee, intensite, indice, tableauImage, tolerance)&lt;br /&gt;
        &lt;br /&gt;
        indice += len(pixelsMemeEtage)#On connaît le nombre de pixels de cet étage, on en déduit l&#039;indice où commencera le prochaine étage&lt;br /&gt;
        indiceEtage = 0         #Initialisation du compteur de pixels &amp;quot;consécutifs&amp;quot; traités à cet étage&lt;br /&gt;
        compteurPxTraites = 0   #Compteur du nombre de pixels traités à cet étage&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        # Initialiser la file d&#039;attente :&lt;br /&gt;
        # La file d&#039;attente est une liste de listes de pixels ; chaque sous-liste correspond à une distance, une étape du traitement&lt;br /&gt;
        # Les premiers pixels traités sont collés à un pixel déjà labellé ; leurs voisins seront traitables par la suite &lt;br /&gt;
        # et sont donc mis dans la file d&#039;attente &amp;quot;suivante&amp;quot;. Ils sont à une &amp;quot;distance&amp;quot; supérieure, et seront traités après le traitement de la distance actuelle&lt;br /&gt;
        # C&#039;est avec ce principe qu&#039;on forme la file d&#039;attente.&lt;br /&gt;
        fileAttente=[[]]&lt;br /&gt;
        &lt;br /&gt;
        # On fait d&#039;abord un parcours de l&#039;étage, pour trouver les pixels collés à des pixels labellés lors du traitement d&#039;étages précédents&lt;br /&gt;
        for pixel in pixelsMemeEtage:&lt;br /&gt;
            # Pour chaque pixel de l&#039;étage, on regarde ses voisins, si au moins un est labellé, le pixel est à distance 0, c&#039;est à dire traitable directement.&lt;br /&gt;
            if PixelTraitable(pixel[1],pixel[2], tableauImage):&lt;br /&gt;
                #Si c&#039;est le cas on l&#039;ajoute à la file d&#039;attente&lt;br /&gt;
                fileAttente[0].append(pixel)&lt;br /&gt;
                tableauImage[pixel[1]][pixel[2]][LABELLED] = True&lt;br /&gt;
                &lt;br /&gt;
        #On initialise le numéro de l&#039;étape du traitement, la distance : elle commence à 0 et augmente à chaque tour de boucle&lt;br /&gt;
        distance = 0&lt;br /&gt;
        &lt;br /&gt;
        while len(pixelsMemeEtage) &amp;gt; compteurPxTraites: #Tant que le nombre de pixels de cet étage traités est inférieur au nombre de pixels de l&#039;étage, on a pas traité tout l&#039;étage.&lt;br /&gt;
        &lt;br /&gt;
            if len(fileAttente[distance])==0:                                                                             #On regarde s&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
                fileAttente = nouveauLabel(pixelsMemeEtage, tableauImage, fileAttente, listeLabels, intensite, tolerance) #Si non alors il n&#039;y a plus aucun bassin connu à cet étage, on en ajoute:&lt;br /&gt;
                compteurPxTraites += 1                                                                                    #On sait qu&#039;un pixel a été traité en ajoutant un nouveau bassin (le premier pixel du bassin), on l&#039;ajoute au compteur&lt;br /&gt;
                &lt;br /&gt;
            else:                                                                                       #Si il y a des pixels à traiter on les traite               &lt;br /&gt;
                while len(fileAttente[distance]) &amp;gt; 0:                                                   #avec ces nouvelles informations de nouveaux pixels seront traitables.&lt;br /&gt;
                                                                                                        #On continue ces actions jusqu&#039;à qu&#039;aucun pixel ne soit traitable&lt;br /&gt;
                    &lt;br /&gt;
                    determinationLabels(fileAttente, distance, tableauImage, listeLabels, tailleMini)   #On commence par déterminer les labels des pixels traitables&lt;br /&gt;
                    AjoutFileAttente(fileAttente, distance, tableauImage, intensite, tolerance)         #On ajoute les pixels désormais traitable dans la file d&#039;attente qui sera parcourue au prochain tour de boucle&lt;br /&gt;
                    compteurPxTraites+=len(fileAttente[distance])                                       #Tous les pixels de la file d&#039;attente à cet étape ont été traités ; on augmente le compteur en conséquence&lt;br /&gt;
                    distance +=1                                                                        #On passe à l&#039;étape suivant de l&#039;algorithme / la nouvelle file d&#039;attente&lt;br /&gt;
                    &lt;br /&gt;
                distance = 0                                         #Une fois que la boucle est terminé, on a labellé tous les pixels possibles à cet étage qui n&#039;appartiennent pas à un nouveau bassin&lt;br /&gt;
                fileAttente = [[]]                                   #On réinitialise la file d&#039;attente pour refaire le même traitement avec un nouveau bassin&lt;br /&gt;
&lt;br /&gt;
                #Si tous les pixels de l&#039;étage n&#039;ont pas été traités, alors on va ainsi créer un nouveau bassin et le propager, jusqu&#039;à que tous les pixels de l&#039;étage aient été traités.&lt;br /&gt;
&lt;br /&gt;
    return AffichageImage(tableauImage, image, listeLabels)#On enregistre les résultats du traitement dans une image&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def AffichageImage(tableauImage, image, listeLabels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonction qui prend le résultat de l&#039;algorithme sur le fichier fichier et le convertit en image ;&lt;br /&gt;
       Les pixels des lignes de partage des eaux sont affichés en noir, et chaque label est associé à une couleur.&lt;br /&gt;
       On enregistre ensuite cette image.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    lpe = image.copy()&lt;br /&gt;
    couleurs = [0]*len(listeLabels)&lt;br /&gt;
    for e in range(len(couleurs)):&lt;br /&gt;
        couleurs[e] = (randint(100,200),randint(100,200),randint(100,200))  &lt;br /&gt;
    for i in range(image.width):&lt;br /&gt;
        for j in range(image.height):&lt;br /&gt;
            if tableauImage[i][j][LABEL] == &amp;quot;watershed&amp;quot;:&lt;br /&gt;
                lpe.putpixel((i,j),(0,0,0))&lt;br /&gt;
            else:&lt;br /&gt;
                lpe.putpixel((i,j),couleurs[tableauImage[i][j][LABEL]])&lt;br /&gt;
    return lpe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
# UTILISATION&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
def LPE(fichierImage, saveGradient=False, tolerance=0, flou=False, tailleMini=0, fichierGradient=&amp;quot;none&amp;quot;):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend un fichier image en entrée et calcule son gradient,&lt;br /&gt;
    avant d&#039;appliquer l&#039;algorithme de détermination de ligne de partage des eaux&lt;br /&gt;
&lt;br /&gt;
    l&#039;option &amp;quot;saveGradient&amp;quot; est un booléen qui permet d&#039;enregistrer le gradient de l&#039;image si souhaité&lt;br /&gt;
&lt;br /&gt;
    l&#039;option fichierGradient permet d&#039;éxécuter uniquement la détermination de la LPE sur un fichier.&lt;br /&gt;
    On peut donc appliquer la deuxième partir de l&#039;algorithme sur une image gradient déjà calculée.&lt;br /&gt;
&lt;br /&gt;
    D&#039;autres options sont disponibles, qui permettent de réduire la sensibilité au bruit de l&#039;algorithme&lt;br /&gt;
        - tolerance : entier ; deux pixels seront traités comme appartenant au même &amp;quot;étage&amp;quot;&lt;br /&gt;
          si cet entier est plus grand que la différence de leurs valeurs&lt;br /&gt;
        - flou : booléen qui si vaut vrai, applique un flou sur l&#039;image de départ afin de diminuer&lt;br /&gt;
          l&#039;impact du bruit sur le résultat&lt;br /&gt;
        - tailleMini : entier ; au moment où plusieurs zones se rencontrent, si cela est possible,&lt;br /&gt;
          on fusionne les zones de taille inférieure (la taille est le nombre de pixels associés à la zone) à cet entier. Cela permet la supression des petites zones.&lt;br /&gt;
 &lt;br /&gt;
    Les options réduisant la sensibilité au bruit peuvent rendre le résultat imprécis en fonction de leurs valeurs&lt;br /&gt;
 &lt;br /&gt;
    Le résultat est sauvegardé sous forme d&#039;image, où chaque zone est associée à une couleur et où les lignes de partage des eaux sont en noir.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    if fichierGradient == &amp;quot;none&amp;quot;:&lt;br /&gt;
        gradient = FiltreSobel(fichierImage)&lt;br /&gt;
        if saveGradient:&lt;br /&gt;
            gradient.save(&amp;quot;gradient_{}&amp;quot;.format(fichierImage))&lt;br /&gt;
    else:&lt;br /&gt;
        gradient = Image.open(fichierGradient)&lt;br /&gt;
    if flou:&lt;br /&gt;
        gradient = Filtre(gradient, [[1,1,1],[1,1,1],[1,1,1]], saveIm=False)&lt;br /&gt;
    lpe = Algo(gradient, tolerance, tailleMini)&lt;br /&gt;
    &lt;br /&gt;
    nom = &amp;quot;lpe_&amp;quot;&lt;br /&gt;
    if flou:&lt;br /&gt;
        nom+=&amp;quot;flou_&amp;quot;&lt;br /&gt;
    if tolerance!=0:&lt;br /&gt;
        nom+=&amp;quot;tolerance-{}_&amp;quot;.format(tolerance)&lt;br /&gt;
    if tailleMini!=0:&lt;br /&gt;
        nom+=&amp;quot;mini-{}_&amp;quot;.format(tailleMini)&lt;br /&gt;
    nom += &amp;quot;{}&amp;quot;.format(fichierImage)&lt;br /&gt;
    lpe.save(nom)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sources ==&lt;br /&gt;
*[https://fr.wikipedia.org/wiki/Segmentation_d%27image Segmentation d&#039;image]&lt;br /&gt;
*[https://fr.wikipedia.org/wiki/Filtre_de_Sobel Filtre de Sobel]&lt;br /&gt;
*[https://fr.wikipedia.org/wiki/Ligne_de_partage_des_eaux Ligne de partage des eaux (géographie)]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Watershed_(image_processing) Algorithmes existants pour calculer des lignes de partage des eaux (segmentation)]&lt;br /&gt;
*[https://pdfs.semanticscholar.org/a381/9dda9a5f00dbb8cd3413ca7422e37a0d5794.pdf Algorithme de Luc Vincent et Pierre Soille]&lt;br /&gt;
&lt;br /&gt;
== Annexes ==&lt;br /&gt;
*[https://pillow.readthedocs.io/en/5.1.x/ Bibliothèque de manipulation d&#039;image utilisée (PILLOW)]&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10332</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10332"/>
		<updated>2018-05-26T19:11:52Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Deuxième étape : détermination de la &amp;quot;ligne de partage des eaux&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
Pour la deuxième étape de l&#039;algorithme, on utilise l&#039;image gradient. Elle est alors considérée comme un relief où les pixels de faible intensité sont à faible altitude, et où les pixels d&#039;intensité élevée sont à forte altitude.&lt;br /&gt;
&lt;br /&gt;
=== Définitions ===&lt;br /&gt;
&lt;br /&gt;
En topologie la ligne de partage des eaux peut-être définie de plusieurs manières ; elle est la séparation entre deux bassins versants dans un relief.&lt;br /&gt;
&lt;br /&gt;
Notre image est considérée comme un relief, et la ligne de partage des eaux sera la séparation entre les zones de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Un bassin versant est un bassin dont toutes les eaux ont le même exutoire, par exemple une rivière ou l&#039;océan.&lt;br /&gt;
&lt;br /&gt;
Pour déterminer la ligne de partage des eaux, plusieurs méthodes sont possibles :&lt;br /&gt;
&lt;br /&gt;
* On place une source d&#039;eau dans chaque minimum local (donc dans chaque bassin) et on inonde, quand deux bassins se rencontrent on place une ligne de partage des eaux.&lt;br /&gt;
Les algorithmes utilisant ce principe sont appelés des algorithmes par inondation.&lt;br /&gt;
&lt;br /&gt;
* Une deuxième méthode est de considérer que des deux côtés de la ligne l&#039;écoulement de l&#039;eau se termine dans des bassins versants différents.&lt;br /&gt;
Dans l&#039;algorithme associé, on va faire &amp;quot;s&#039;écouler&amp;quot; de l&#039;eau sur chaque pixel et tracer une ligne de partage entre deux pixels (ou sur l&#039;un des deux pixels) quand les eaux qui s&#039;écoulent depuis ces pixels arrive dans des minima locaux différents.&lt;br /&gt;
Ce sont les algorithmes par ruissellement.&lt;br /&gt;
&lt;br /&gt;
=== Algorithme utilisé (créé par Luc Vincent et Pierre Soille) ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme choisi est un algorithme par inondation.&lt;br /&gt;
&lt;br /&gt;
Les pixels de faible intensité vont donc être traités en priorité.&lt;br /&gt;
&lt;br /&gt;
La première étape est donc de trier les pixels de l&#039;image par ordre croissant d&#039;intensité.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme va traiter les pixels d&#039;intensité la plus basse, puis une fois ceux-ci traités, les pixels avec une intensité légèrement supérieure sont traités, et ainsi de suite jusqu&#039;à avoir traité toute l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Trois problèmes se posent alors :&lt;br /&gt;
* Comment inonder un étage, c&#039;est à dire de quelle manière traiter les pixels de même intensité, à partir de bassins connus ?&lt;br /&gt;
* Comment déterminer à quel bassin appartient un pixel ?&lt;br /&gt;
* Comment repérer les nouveaux bassins ?&lt;br /&gt;
&lt;br /&gt;
==== Inonder un étage (pixels d&#039;intensité identique) ====&lt;br /&gt;
&lt;br /&gt;
Chaque bassin doit progresser à la même vitesse sur un étage, c&#039;est à dire que la ligne de partage des eaux qui sépare deux bassins doit être à égale distance de chacun.&lt;br /&gt;
&lt;br /&gt;
Chaque pixel est à une &amp;quot;distance&amp;quot; d&#039;un bassin. Par exemple les pixels collés à un bassin connu sont à une distance de 1. Leurs voisins sont eux à une distance de 2 (à condition qu&#039;ils ne soient pas collés à un bassin connu).&lt;br /&gt;
&lt;br /&gt;
[[File:file_distance.png|frame|Distance à laquelle se trouve les pixels du bassin connu (bleu)]]&lt;br /&gt;
&lt;br /&gt;
Pour que les bassins s&#039;étendent correctement, on traite d&#039;abord tous les pixels à distance 1, puis ceux à distance 2 et ainsi de suite.&lt;br /&gt;
&lt;br /&gt;
Une file d&#039;attente est créée ; la première étape est de placer les pixels à une distance de 1 dans la file d&#039;attente.&lt;br /&gt;
On va pouvoir traiter ces pixels là. On sait également que leurs voisins sont à une distance de 2, et pourront donc être traités lorsque les pixels à distance 1 seront associés à un bassin.&lt;br /&gt;
&lt;br /&gt;
Les voisins non-traités des pixels à une distance de 1 sont donc les prochains pixels de l&#039;étage à traiter.&lt;br /&gt;
Ils sont placés dans une autre file d&#039;attente, qui sera parcourue une fois que la première a été entièrement utilisée.&lt;br /&gt;
&lt;br /&gt;
Ensuite, les voisins des pixels à une distance de 2 seront les prochains pixels à traiter, et on va recommencer cette suite d&#039;opérations.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les pixels sont placés dans la file d&#039;attente :&lt;br /&gt;
&lt;br /&gt;
* Tout d&#039;abord les pixels directement voisins d&#039;un bassin connu sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_1.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Puis une fois que les pixels dans la file d&#039;attente ont été traités, les pixels voisins des pixels qui viennent d&#039;être traités sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_2.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Cette étape se répète tant qu&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_3.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi le traitement d&#039;une file d&#039;attente (file d&#039;attente d&#039;une distance particulière) fait avancer d&#039;un pixel tous les bassins, et tous les bassins progressent à la même vitesse.&lt;br /&gt;
&lt;br /&gt;
==== Détermination du bassin auquel appartient un pixel ====&lt;br /&gt;
&lt;br /&gt;
Avec cette manière de faire progresser les bassins, le bassin auquel appartient un pixel est déterminé par les bassins auxquels appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
Plusieurs cas sont alors possibles lorsqu&#039;un pixel est traité.&lt;br /&gt;
&lt;br /&gt;
[[File:choix_label.png]] &lt;br /&gt;
&lt;br /&gt;
*Un des cas les plus simples est lorsque tous les voisins traités du pixel appartiennent au même bassin.Le bassin s&#039;étend alors, et le pixel à traiter est associé au bassin auquel appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
*Cette même décision est prise si tous les pixels voisins traités appartiennent au même bassin sauf certains qui appartiennent à une ligne de partage des eaux (LPE). L&#039;autre décision possible est de prolonger la LPE, cependant des lignes épaisses (et imprécises) seraient créées.&lt;br /&gt;
&lt;br /&gt;
*Si les pixels voisins traités du pixel appartiennent à une LPE, le bassin auquel appartient le pixel est inconnu. On ne peut pas &amp;quot;prolonger&amp;quot; la ligne car on ne connaît alors pas à quelle distance d&#039;un bassin se trouve le pixel. Si un pixel appartient à une LPE, ses voisins ne sont donc pas placés en file d&#039;attente.&lt;br /&gt;
&lt;br /&gt;
*Lorsque les pixels voisins appartiennent à des bassins différents, alors le pixel appartient à une LPE ; on sépare les bassins au moment de leur rencontre.&lt;br /&gt;
&lt;br /&gt;
*Le dernier cas se présente lorsque tous les pixels voisins sont traités et qu&#039;ils appartiennent tous à une LPE. Pour éviter qu&#039;un bassin d&#039;un seul pixel soit créé, on prolonge la LPE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En résumé :&lt;br /&gt;
*S&#039;il n&#039;y a qu&#039;un seul bassin autour du pixel en excluant les LPE, le pixel appartient à ce bassin.&lt;br /&gt;
*Si en excluant les LPE il y a plusieurs bassins autour du pixel, le pixel appartient à une LPE.&lt;br /&gt;
*Si le pixel est entouré par des LPE, alors il appartient à une LPE.&lt;br /&gt;
&lt;br /&gt;
==== Création de nouveaux bassins ====&lt;br /&gt;
&lt;br /&gt;
Lors de la propagation des bassins, si une file d&#039;attente est vide alors tous les pixels de l&#039;étage qui appartenaient à un bassin déjà connu ont été traités.&lt;br /&gt;
&lt;br /&gt;
Les pixels restants appartiennent donc à de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Un nouveau bassin est donc créé, le premier pixel qui lui appartient est choisi parmi un des pixels de l&#039;étage non-traités.&lt;br /&gt;
&lt;br /&gt;
On réinitialise alors la file d&#039;attente, et on place les voisins non-traités du pixel associé au nouveau bassin dedans.&lt;br /&gt;
&lt;br /&gt;
On recommence alors le traitement de la file d&#039;attente, et tous les pixels qui appartiennent à ce bassin lui seront attribués.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les nouveaux bassins sont créés, sur une petite image de 5x5.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_image.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Plusieurs étapes se déroulent alors :&lt;br /&gt;
&lt;br /&gt;
* Le traitement d&#039;un étage commence. Les pixels appartenant à un bassin connu se situent à un étage inférieur.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_1.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
* Tous les pixels appartenant à un bassin connu ont été traités ; il reste cependant des pixels non-traités à cet étage. Ils appartiennent donc à un ou plusieurs nouveaux bassins. On choisit donc un pixel non-traité de l&#039;étage, et on lui attribue un nouveau bassin.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_2.png|300px]] [[File:minima_legende.png|500px]]  &lt;br /&gt;
&lt;br /&gt;
* On propage le nouveau bassin créé.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_3.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
Ces étapes sont répétées tant qu&#039;il reste des pixels non-traités à l&#039;étage en cours de traitement. On détecte ainsi tous les nouveaux bassins de l&#039;étage.&lt;br /&gt;
&lt;br /&gt;
Une fois que tous les pixels d&#039;un étage ont été traités, on passe à l&#039;étage supérieur.&lt;br /&gt;
&lt;br /&gt;
==== Résultats ====&lt;br /&gt;
Voici un exemple de résultat que donne cet algorithme :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_brute.png]]&#039;&#039;A gauche le gradient d&#039;une image ; à droite le résultat de l&#039;algorithme de détermination des LPE&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On s&#039;aperçoit que l&#039;algorithme est très sensible au bruit, c&#039;est à dire aux légères variations d&#039;intensité qui créent des minima locaux.&lt;br /&gt;
&lt;br /&gt;
On veut donc améliorer les résultats obtenus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Réduire l&#039;impact du bruit ===&lt;br /&gt;
Plusieurs méthodes sont applicables pour réduire l&#039;impact du bruit sur le résultat.&lt;br /&gt;
&lt;br /&gt;
==== Ajout d&#039;une tolérance ====&lt;br /&gt;
La plus efficace est d&#039;ajouter une &amp;quot;tolérance&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
En effet, dans l&#039;algorithme décrit précédemment, les pixels appartiennent à un même étage si leurs intensités sont exactement identiques.&lt;br /&gt;
&lt;br /&gt;
On considére qu&#039;un pixel appartient à l&#039;étage traité si la différence entre son intensité et l&#039;intensité de l&#039;étage traitée est inférieure à un nombre, qu&#039;on appelle tolérance.&lt;br /&gt;
&lt;br /&gt;
Ainsi la sensibilité au bruit de l&#039;algorithme est réduite car les petites variations d&#039;intensité ne créent plus de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple sur une petite image. &lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance_image.png]]&lt;br /&gt;
&lt;br /&gt;
Tout d&#039;abord, s&#039;il n&#039;y a pas de tolérance, les pixels d&#039;intensité 130 et le pixel d&#039;intensité 135 sont à des étages différents.&lt;br /&gt;
Deux bassins sont donc créés lors du traitement de l&#039;étage d&#039;intensité 130 (bleu et vert, le noir représente une LPE).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_sans_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Cependant, avec l&#039;ajout d&#039;une tolérance (par exemple 10), les trois pixels sont considérés au même étage (car on a 135-130 &amp;lt; 10).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Ainsi une unique bassin est créé.&lt;br /&gt;
&lt;br /&gt;
Le résultat est alors bien plus précis sur de vraies images:&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_res_tolerance.png]] &#039;&#039;A gauche le résultat (de cette [[#Résultats|image]]) de l&#039;algorithme sans tolérance, à droite le résultat avec une tolérance de 5&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Flouter l&#039;image avant le traitement ====&lt;br /&gt;
&lt;br /&gt;
Une deuxième méthode est de traiter l&#039;image avant d&#039;appliquer l&#039;algorithme.&lt;br /&gt;
Si l&#039;image est floutée avant application de l&#039;algorithme, le bruit aura moins d&#039;impact, car les changements de valeur seront atténués.&lt;br /&gt;
Dans l&#039;image floutée, chaque valeur d&#039;un pixel est une moyenne des valeurs des pixels voisins à ce pixel dans l&#039;image originale.&lt;br /&gt;
&lt;br /&gt;
Pour cela, on peut convoluer l&#039;image par cette matrice :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Chaque produit de convolution donnera un entier qui devra être divisé par 9 ; on ajoute  les valeurs des 9 pixels dans un carré de 3x3 autour du pixel dont on veut la nouvelle valeur, pour avoir la moyenne des valeurs dans ce carré on doit alors diviser par 9.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas de bons résultats si elle est la seule utilisée. Cependant, combinée avec l&#039;utilisation de la tolérance, le nombre de petites zones est fortement réduit.&lt;br /&gt;
&lt;br /&gt;
Les LPE sont en contrepartie moins précises.&lt;br /&gt;
&lt;br /&gt;
Exemple de résultat :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou_tolerance5.png]]&lt;br /&gt;
&lt;br /&gt;
==== Fusion des petites zones ====&lt;br /&gt;
&lt;br /&gt;
Une troisième solution est d&#039;empêcher la création de zones trop petites, en indiquant une taille minimale.&lt;br /&gt;
Si des bassins plus petits que cette taille rencontrent un autre bassin, alors ils fusionnent, et le point de leur rencontre est associé à ce nouveau bassin issu de la fusion.&lt;br /&gt;
&lt;br /&gt;
Si au point de rencontre des bassins se rencontrent deux zones plus grandes que la taille minimale, on doit tout de même placer une LPE, pour séparer les deux &amp;quot;grands&amp;quot; bassins.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas non plus de résultats intéressants si utilisée seule, il est nécessaire de la combiner avec l&#039;ajout d&#039;une tolérance.&lt;br /&gt;
&lt;br /&gt;
Un des résultats obtenus avec cette méthode est (où la taille minimale d&#039;un bassin est de 5px) :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_fusion5.png]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
Pour conclure, on peut citer deux avantages de cet algorithme. Tout d&#039;abord, les lignes de partage des eaux créées sont peu épaisses ; elles ne font jamais plus de 3 pixels de large, et se limitent dans la plupart des cas à une épaisseur d&#039;un seul pixel.&lt;br /&gt;
&lt;br /&gt;
Ensuite, l&#039;algorithme est de complexité linéaire, c&#039;est à dire que le temps d’exécution dépend linéairement du nombre de pixels de l&#039;image sur laquelle on applique l&#039;algorithme.&lt;br /&gt;
Voici quelques mesures du temps d&#039;exécution selon le nombre de pixels de l&#039;image en entrée (les images utilisées pour ces tests ne représentent rien et contiennent de nombreuses zones).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tableau_temps.png|border|mesures du temps d&#039;exécution]]&lt;br /&gt;
[[File:lpe_temps.png|border|temps d&#039;exécution en fonction du nombre de pixels]]&lt;br /&gt;
&lt;br /&gt;
== Code source ==&lt;br /&gt;
Voici le code source de l&#039;algorithme.&lt;br /&gt;
&lt;br /&gt;
Pour manipuler des images, j&#039;ai utilisé la bibliothèque PILLOW.&lt;br /&gt;
&lt;br /&gt;
Les principales fonctions utilisées dans cette bibliothèque sont :&lt;br /&gt;
*Image.open(fichier) : ouvrir le fichier fichier en tant qu&#039;objet image&lt;br /&gt;
*image.getpixel((x,y)) : récupérer la valeur du pixel aux coordonnées (x,y) dans un objet Image (appelé ici &#039;image&#039;)&lt;br /&gt;
*image.setpixel((x,y),(r,v,b)) : Changer la valeur du pixel aux coordonnées (x,y) dans un objet Image (appelé ici &#039;image&#039;) ; (r,v,b) sont les valeurs rouge,vert,bleu.&lt;br /&gt;
*image.save(nom) : sauvegarder l&#039;image sous le nom souhaité.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
###########################################################&lt;br /&gt;
# RUET Nils&lt;br /&gt;
# Algorithme de détermination de ligne de partage des eaux, créé par Luc Vincent et Pierre Soille&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   IMPORTATIONS&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
from PIL import Image&lt;br /&gt;
from math import sqrt&lt;br /&gt;
from random import randint&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   CONSTANTES ET VARIABLES GLOBALES&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#Chaque pixel a 3 valeurs qui lui sont associées ; son intensité, l&#039;information sur s&#039;il a déjà été placé en file d&#039;attente, et le label qui lui a été associé.&lt;br /&gt;
#Ces informations sont stockés dans un tableau et ces constantes sont les indices auxquels trouver les informations voulues.&lt;br /&gt;
VALEUR = 0&lt;br /&gt;
LABELLED = 1&lt;br /&gt;
LABEL = 2&lt;br /&gt;
&lt;br /&gt;
indiceEtage = 0# Lorsque l&#039;on traite un étage, certains pixels ne sont pas traitables à partir des données connues. Ils correspondent à de nouveaux bassins.&lt;br /&gt;
               # On dispose d&#039;une liste des pixels de l&#039;étage ; lorsqu&#039;on doit créer un bassin, on cherche donc le premier pixel dans cette liste qui n&#039;est pas labellé.&lt;br /&gt;
               # Cette variable contient le nombre de pixels &amp;quot;consécutifs&amp;quot; traités, c&#039;est à dire l&#039;indice du dernier pixel non traité trouvé dans la liste.&lt;br /&gt;
               # Ainsi on n&#039;a pas à reparcourir la liste entière lorsque l&#039;on créé plusieurs nouveaux bassins à un étage car on sait que tous les pixels &amp;quot;avant&amp;quot; (dans la liste) celui choisi pour créer un nouveau bassin ont été labellés.&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   CALCUL DU GRADIENT&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
def deterCoef(mat):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Cette fonction prend un masque de convolution en entrée. Elle permet de déterminer un décalage et un coefficient utilisés pour ramener toutes les valeurs&lt;br /&gt;
    obtenues après convolution entre 0 et 255&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    sommeP=0&lt;br /&gt;
    sommeN=0&lt;br /&gt;
    for i in range(len(mat)):       #On compte les coefficients négatifs et les coefficients positifs dans le masque&lt;br /&gt;
        for j in range(len(mat[0])):&lt;br /&gt;
            if mat[i][j] &amp;gt; 0:&lt;br /&gt;
                sommeP+=mat[i][j]&lt;br /&gt;
            else:&lt;br /&gt;
                sommeN-=mat[i][j]&lt;br /&gt;
    decal = sommeN * 255            #Les coefficients négatifs permettent de connaître la valeur à ajouter pour n&#039;obtenir que des valeurs positives après la convolution&lt;br /&gt;
    coef = sommeN+sommeP            #La somme des valeurs absolues des coefficients permet de savoir comment ramener toutes ces valeurs entre 0 et 255.&lt;br /&gt;
    return (coef, decal)&lt;br /&gt;
&lt;br /&gt;
def Convolution(image, x, y, filtre):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Effectue le produit de convolution entre un filtre et l&#039;enourage du pixel aux coordonnées (x,y) dans l&#039;image &#039;image&#039; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    somme = 0&lt;br /&gt;
    centre = len(filtre)//2&lt;br /&gt;
    for i in range(-centre,centre+1):           #i et j indiquent la position relative du pixel dont on a besoin dans l&#039;image par rapport au pixel dont on calcule la nouvelle valeur.&lt;br /&gt;
        for j in range(-centre,centre+1):&lt;br /&gt;
            if filtre[i+centre][j+centre] != 0: #Vu qu&#039;on multiplie la valeur d&#039;un pixel par une valeur dans le filtre, on vérifie que la valeur dans le filtre ne vaut pas 0.&lt;br /&gt;
                if 0&amp;lt;=x+i&amp;lt;image.width and 0&amp;lt;=y+j&amp;lt;image.height: #Si le pixel voulu est dans l&#039;image, xl (x local) et yl (y local) sont ses coordonnées.&lt;br /&gt;
                    xl = x+i&lt;br /&gt;
                    yl = y+j&lt;br /&gt;
                else:                           #Sinon on va chercher la valeur du pixel en symlétrie par rapport au pixel dont on veut la nouvelle valeur&lt;br /&gt;
                    xl = x-i&lt;br /&gt;
                    yl = y-j&lt;br /&gt;
                    &lt;br /&gt;
                if not 0&amp;lt;=xl&amp;lt;image.width or not 0&amp;lt;=yl&amp;lt;image.height: #Si après cela le pixel n&#039;est toujours pas dans l&#039;image, on se réfère à la valeur du pixel dont on veut la nouvelle valeur.&lt;br /&gt;
                    xl = x&lt;br /&gt;
                    yl = y                    &lt;br /&gt;
                somme+= (image.getpixel((xl,yl))[0] *filtre[i+centre][j+centre])&lt;br /&gt;
    return somme&lt;br /&gt;
&lt;br /&gt;
def AppliqueFiltre(image, filtre):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permet d&#039;appliquer le masque de convolution &#039;filtre&#039; (double tableau) à tous les pixels de l&#039;image &#039;image&#039; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = image.copy()&lt;br /&gt;
    (coef, decal) = deterCoef(filtre)&lt;br /&gt;
    for x in range(image.width):            #Pour chaque pixel de l&#039;image&lt;br /&gt;
        for y in range(image.height):&lt;br /&gt;
            somme = Convolution(image,x,y,filtre) #On prend le résultat de la convolution&lt;br /&gt;
            val = round((somme+decal)/coef)       #On ramène ce résultat entre 0 et 255&lt;br /&gt;
            res.putpixel((x,y),(val,val,val))     #On met le pixel dans l&#039;image résultat à cette valeur&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def Filtre(image, filtre, nom_sortie=&amp;quot;filtre&amp;quot;, saveIm=True):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permet d&#039;appeler la fonction qui permet de convoluer l&#039;image &#039;image&#039; par le masque &#039;filtre&#039; et choisir si le résultat est sauvegardé dans un fichier image ou non&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = AppliqueFiltre(image, filtre)&lt;br /&gt;
    if saveIm:&lt;br /&gt;
        res.save(&amp;quot;{}_{}&amp;quot;.format(nom_sortie, fichierImage))&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
def FiltreSobel(fichierIm):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permer de calculer la norme du gradient de l&#039;image &#039;fichierIm&#039; (nom du fichier image) en chaque point avec la méthode du filtre de Sobel&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    image = Image.open(fichierIm)&lt;br /&gt;
    res = image.copy()&lt;br /&gt;
    masquex = [[-1,0,1],[-2,0,2],[-1,0,1]]&lt;br /&gt;
    masquey = [[-1,-2,-1],[0,0,0],[1,2,1]]&lt;br /&gt;
    for x in range(image.width):                                #Pour chaque pixel de l&#039;image&lt;br /&gt;
        for y in range(image.height):&lt;br /&gt;
            dx = Convolution(image,x,y,masquex)                 #On approxime ses dérivées partielles&lt;br /&gt;
            dy = Convolution(image,x,y,masquey)                 #&lt;br /&gt;
&lt;br /&gt;
            val = round(sqrt(dx**2 + dy**2)*255/(sqrt(2)*1020)) #On calcule la norme du gradient, qu&#039;on ramène entre 0 et 255.&lt;br /&gt;
            res.putpixel((x,y),(val,val,val))                   #Cette valeur est associée au pixel dans le résultat&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#########################################################################################################&lt;br /&gt;
#   ALGORITHME LIGNE DE PARTAGE DES EAUX&lt;br /&gt;
#########################################################################################################&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def InitTableau(image):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Initialise le double tableau contenant les informations sur chaque pixel; aux indices [x][y] on accède aux informations du pixel (x,y). Ces informations sont l&#039;intensité, l&#039;avancement du traitement et le label.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tableau = [0]*image.width                                       #On initialise un tableau de la taille de l&#039;image&lt;br /&gt;
    for i in range(len(tableau)):&lt;br /&gt;
        tableau[i]=[0]*image.height&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(tableau)):                                   #On initialise les informations de chaque pixel :&lt;br /&gt;
        for j in range(len(tableau[i])):&lt;br /&gt;
            tableau[i][j] = [image.getpixel((i,j))[0],False,&amp;quot;init&amp;quot;] #Sa valeur dans l&#039;image, l&#039;avancement du traitement (False si le pixel n&#039;a pas été traité, True sinon), et le label qui lui est associé&lt;br /&gt;
    return tableau&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def InitListeOrdonnee(image):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Initialise la liste des pixels de l&#039;image, triés par intensité ; chaque case contient la position du pixel attribué et son intensité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    liste = []&lt;br /&gt;
    for i in range(image.width):&lt;br /&gt;
        for j in range(image.height):&lt;br /&gt;
            liste.append([image.getpixel((i,j))[0],i,j])#On regarde chaque pixel de l&#039;image et on ajoute dans une case de la liste son intensité, son abcisse, son ordonnée&lt;br /&gt;
    liste.sort(key=lambda pixel: pixel[0])              #On trie cette liste en fonction de l&#039;intensité des pixels (ordre croissant)&lt;br /&gt;
    return liste&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def PixelsEtage(listeOrdonnee, intensite, indice, tablImage, tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend en entrée la liste ordonnée des pixels de l&#039;image, une intensité, l&#039;indice à partir duquel commence cette intensité dans la liste, et une tolérance.&lt;br /&gt;
       Renvoie la liste des pixels d&#039;un &amp;quot;étage&amp;quot; d&#039;intensité, plus précisément :&lt;br /&gt;
       La liste des pixels non-labellés de l&#039;intensité choisie plus ou moins la tolérance donnée. L&#039;indice à partir duquel commence l&#039;intensité choisie est modifié par une autre partie&lt;br /&gt;
       du programme ; on sait que tous les pixels d&#039;une valeur inférieure à l&#039;intensité choisie ont déjà été traités par l&#039;algorithme.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    pixels = []&lt;br /&gt;
    nouvelIndice = indice&lt;br /&gt;
    while nouvelIndice &amp;lt; len(listeOrdonnee) and (listeOrdonnee[nouvelIndice][0] - intensite) &amp;lt;= tolerance:#On ajoute à la liste des pixels de l&#039;étage intensité les pixels qui ont une intensité dans l&#039;intervalle souhaité&lt;br /&gt;
        if not tablImage[listeOrdonnee[nouvelIndice][1]][listeOrdonnee[nouvelIndice][2]][LABELLED]:       #Et non labellés&lt;br /&gt;
            pixels.append(listeOrdonnee[nouvelIndice])&lt;br /&gt;
        nouvelIndice+=1&lt;br /&gt;
    return pixels&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def labelsPixelsVoisins(x, y, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonctions qui renvoie la liste des labels des pixels voisins au pixel situé aux coordonnée entrées en paramètre, ignore les pixels non labellés&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    labels = []&lt;br /&gt;
&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins:&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]) and tablImage[x+i][y+j][LABEL] != &amp;quot;init&amp;quot;:&lt;br /&gt;
            labels.append(tablImage[x+i][y+j][LABEL])        &lt;br /&gt;
    return labels&lt;br /&gt;
&lt;br /&gt;
def PixelTraitable(xPixel, yPixel, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonction qui renvoie True si le pixel aux coordonnées entrées en paramètre est labellable, False sinon&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    labels = labelsPixelsVoisins(xPixel, yPixel, tablImage)&lt;br /&gt;
    for label in labels:&lt;br /&gt;
        if label!=&amp;quot;watershed&amp;quot;:#Un pixel est labellable s&#039;il a un pixel labellé par autre chose qu&#039;une ligne de partage parmi ses voisins.&lt;br /&gt;
            return True&lt;br /&gt;
&lt;br /&gt;
    voisinsDansImage = 0                                                #Sinon si le pixel n&#039;a pas de pixels labellés par autre chose que watershed parmi ses voisins,&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins:                                              #On détermine son nombre de voisins (4 au centre, 3 sur les bords et 2 dans les coins)&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=xPixel+i&amp;lt;len(tablImage) and 0&amp;lt;=yPixel+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
            voisinsDansImage += 1&lt;br /&gt;
    return len(labels)==voisinsDansImage                                #Le pixel est labellable si tous ses voisins sont des lignes de partage des eaux&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def nouveauLabel(listePixels, tablImage, fileAttente, listeLabels, intensite, tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Cette fonction est appelée lorsque l&#039;algorithme n&#039;a pas traité tous les pixels d&#039;un étage, mais qu&#039;aucun pixel n&#039;a été placé en file attente &amp;lt;=&amp;gt; aucun pixel non traité n&#039;est traitable à cet étage.&lt;br /&gt;
       A partir de la liste des pixels de l&#039;étage, de la liste des labels attribués, de l&#039;intensité de l&#039;étage et de la tolérance, on modifie alors la file d&#039;attente&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    global indiceEtage&lt;br /&gt;
    while tablImage[listePixels[indiceEtage][1]][listePixels[indiceEtage][2]][LABELLED]: #On parcourt la liste des pixels de l&#039;étage pour en trouver un non labellé&lt;br /&gt;
        indiceEtage+=1     &lt;br /&gt;
    x = listePixels[indiceEtage][1]&lt;br /&gt;
    y = listePixels[indiceEtage][2]&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins: #On regarde les voisins du premier pixel non labellé trouvé&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]) and not tablImage[x+i][y+j][LABELLED] and (tablImage[x+i][y+j][VALEUR] - intensite) &amp;lt;= tolerance:&lt;br /&gt;
            #Si un voisin est dans le bon intervalle d&#039;intensité, non labellé et dans l&#039;image, alors on le place dans la file d&#039;attente&lt;br /&gt;
            tablImage[x+i][y+j][LABELLED] = True&lt;br /&gt;
            fileAttente[0].append([tablImage[x+i][y+j][VALEUR], x+i, y+j])&lt;br /&gt;
&lt;br /&gt;
    tablImage[x][y][LABEL] = len(listeLabels)#On créé un nouveau label en augmentant de 1 le numéro du dernier label ; cela correspond à la longueur de la liste des labels&lt;br /&gt;
                                             #Puis on l&#039;associe au pixel non traité trouvé dans l&#039;étage&lt;br /&gt;
    tablImage[x][y][LABELLED] = True         #Ce pixel et désormais traité&lt;br /&gt;
    listeLabels.append(1)                    #On ajoute le nouveau label créé à la liste des labels ; le bassin associé à ce label est de taille 1px initalement&lt;br /&gt;
    return fileAttente                       #On renvoie la nouvelle file d&#039;attente ; les voisins traitables du pixel trouvés   &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def AjoutFileAttente(fileAttente, distance, tablImage,intensite,tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;A partir des pixels traités au moment de l&#039;appel de cette fonction, on détermine les prochains pixels qui pourront être traités par la suite.&lt;br /&gt;
       Ces pixels sont les voisins des pixels déjà traités (à l&#039;exception des lignes de partage) du même étage, non traités et dans l&#039;image&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    fileAttente.append([])             #On ajoute une file d&#039;attente pour la distance suivante&lt;br /&gt;
    for pixel in fileAttente[distance]:#Pour chaque pixel traité à la distance précédente&lt;br /&gt;
        x = pixel[1]&lt;br /&gt;
        y = pixel[2]&lt;br /&gt;
        if tablImage[x][y][LABEL] != &amp;quot;watershed&amp;quot;:#S&#039;il n&#039;est pas une ligne de partage, il appartient à un bassin et ses voisins sont potentiellement traitables      &lt;br /&gt;
            voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
            for coords in voisins:&lt;br /&gt;
                i = coords[0]&lt;br /&gt;
                j = coords[1]&lt;br /&gt;
                if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
                    if (tablImage[x+i][y+j][VALEUR] - intensite) &amp;lt;= tolerance and not tablImage[x+i][y+j][LABELLED]:#Les voisins traitables sont les pixel du même étage non labellés&lt;br /&gt;
                        tablImage[x+i][y+j][LABELLED] = True&lt;br /&gt;
                        fileAttente[distance+1].append([tablImage[x+i][y+j][VALEUR], x+i, y+j])#On les ajoute à la file d&#039;attente suivante&lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
def valeursUniques(T):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend un tableau en entrée et supprime les valeurs qui s&#039;y trouvent en double&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for e in T:&lt;br /&gt;
        if not e in res:&lt;br /&gt;
            res.append(e)&lt;br /&gt;
    return res&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
def fusionZones(labelsChange, nouvLabel, listeLabels, x, y, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; A partir d&#039;une liste de labels à changer (labelsChange), du nouveau label qui leur sera associé, et d&#039;un pixel de départ aux coordonnées x,y&lt;br /&gt;
    On fusionne les bassins qui se rencontrent en x,y en modifiant le label des pixels de ces bassins.&lt;br /&gt;
    Chaque fois qu&#039;un pixel change de label, on change le label de ses voisins si cela est nécéssaire&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tablImage[x][y][LABEL] = nouvLabel&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
&lt;br /&gt;
    file = [[x,y]]&lt;br /&gt;
    indiceFile = 0&lt;br /&gt;
    while indiceFile != len(file):#Tant que tous les pixels de la file n&#039;ont pas été associés au nouveau label&lt;br /&gt;
        pixel = file[indiceFile]  #On prend le prochain pixel dans la file&lt;br /&gt;
        for coords in voisins:    #Et on regarde chacun de ses voisins                                 &lt;br /&gt;
            i = coords[0]&lt;br /&gt;
            j = coords[1]&lt;br /&gt;
            if 0&amp;lt;=pixel[0]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[1]+j&amp;lt;len(tablImage[0]) and (tablImage[pixel[0]+i][pixel[1]+j][LABEL] in labelsChange): #Si un pixel voisins dans l&#039;image a un label qui doit être changé&lt;br /&gt;
                file.append([pixel[0]+i,pixel[1]+j])#On l&#039;ajoute à la file&lt;br /&gt;
                tablImage[pixel[0]+i][pixel[1]+j][LABEL] = nouvLabel#On lui attribue son nouveau label&lt;br /&gt;
                listeLabels[nouvLabel]+=1#On augmente le nombre de pixels qui appartiennent au nouveau bassin&lt;br /&gt;
        indiceFile+=1&lt;br /&gt;
  &lt;br /&gt;
def determinationLabels(fileAttente, distance, tablImage, listeLabels, tailleMini):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Procédure qui détermine le label attribué à un pixel en fonction des labels des pixels voisins&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for pixel in fileAttente[distance]:                             #On attribue un label à chaque pixel dans la file d&#039;attente&lt;br /&gt;
        labels = labelsPixelsVoisins(pixel[1], pixel[2], tablImage) #On récupère les labels de ses voisins&lt;br /&gt;
        lstLabels = valeursUniques(labels)                          #On supprime les doublons,&lt;br /&gt;
        if (&amp;quot;watershed&amp;quot; in lstLabels):                              #et le label de ligne de partage des eaux&lt;br /&gt;
            lstLabels.remove(&amp;quot;watershed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        voisinsDansImage = 0&lt;br /&gt;
        voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
        for coords in voisins:                                      #On compte le nombre de pixels voisins dans l&#039;image ; 4 si le pixel est au centre de l&#039;image, 3 s&#039;il est au bord etc.&lt;br /&gt;
            i = coords[0]&lt;br /&gt;
            j = coords[1]&lt;br /&gt;
            if 0&amp;lt;=pixel[1]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[2]+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
                voisinsDansImage += 1&lt;br /&gt;
        if len(lstLabels) == 0 and len(labels)==voisinsDansImage:   #Si le pixel est entouré de watershed, on lui attribue ce label&lt;br /&gt;
            tablImage[pixel[1]][pixel[2]][LABEL] = &amp;quot;watershed&amp;quot;&lt;br /&gt;
                &lt;br /&gt;
        elif len(lstLabels) == 1:                                   #Si le pixel n&#039;a qu&#039;un seul label hormis les watershed autour de lui, on lui attribue ce label&lt;br /&gt;
            tablImage[pixel[1]][pixel[2]][LABEL] = lstLabels[0]&lt;br /&gt;
            listeLabels[lstLabels[0]] += 1&lt;br /&gt;
            &lt;br /&gt;
        elif len(lstLabels) &amp;gt; 1:                                    #S&#039;il y a plusieurs labels autour de lui, il se trouve entre deux bassins et est donc une ligne de partage des eaux ; si les bassins sont petits on les fusionne&lt;br /&gt;
            i = 0&lt;br /&gt;
            plusGrand = []&lt;br /&gt;
            moinsGrand = []&lt;br /&gt;
            while i&amp;lt;len(lstLabels):&lt;br /&gt;
                if listeLabels[lstLabels[i]]&amp;gt;tailleMini:&lt;br /&gt;
                    plusGrand.append(lstLabels[i])&lt;br /&gt;
                else:&lt;br /&gt;
                    moinsGrand.append(lstLabels[i])&lt;br /&gt;
                i+=1&lt;br /&gt;
            if len(moinsGrand)&amp;gt;0 and len(plusGrand)&amp;lt;2:  #Si des bassins sont en dessous de la taille minimale et qu&#039;il n&#039;y a pas plusieurs bassins au dessus de cette taille &lt;br /&gt;
                if len(plusGrand)==1:                   #Si il n&#039;y a qu&#039;un bassin au dessus, tous les petits bassins lui seront rattachés&lt;br /&gt;
                    labelsChange = moinsGrand[:]&lt;br /&gt;
                    nouvLabel = plusGrand[0]&lt;br /&gt;
                elif len(plusGrand)==0:                 #S&#039;il n&#039;y en a aucun, les petits bassins fusionnent entre eux&lt;br /&gt;
                    labelsChange = moinsGrand[1:]&lt;br /&gt;
                    nouvLabel = moinsGrand[0]&lt;br /&gt;
        &lt;br /&gt;
                tablImage[pixel[1]][pixel[2]][LABEL] = nouvLabel #Les bassins vont fusionner, le pixel que l&#039;on regardait appartient donc au nouveau bassin&lt;br /&gt;
                listeLabels[nouvLabel] += 1                      #On augmente le compteur de pixels appartenant à ce bassin&lt;br /&gt;
                &lt;br /&gt;
                voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
                for coords in voisins:                      #On regarde chaque voisin, s&#039;il est dans l&#039;image et que son label doit etre changé, on appelle la procédure qui permet de changer les labels du bassin&lt;br /&gt;
                    i = coords[0]&lt;br /&gt;
                    j = coords[1]&lt;br /&gt;
                    if 0&amp;lt;=pixel[1]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[2]+j&amp;lt;len(tablImage[0]) and tablImage[pixel[1]+i][pixel[2]+j][LABEL] in labelsChange:&lt;br /&gt;
                        fusionZones(labelsChange, nouvLabel, listeLabels, pixel[1]+i, pixel[2]+j, tablImage)&lt;br /&gt;
                        &lt;br /&gt;
            else:                                       #Si aucun bassin n&#039;est trop petit ou si au moins 2 sont au dessus de la taille minimale, le pixel est une ligne de partage des eaux&lt;br /&gt;
                tablImage[pixel[1]][pixel[2]][LABEL] = &amp;quot;watershed&amp;quot;&lt;br /&gt;
&lt;br /&gt;
                &lt;br /&gt;
def Algo(image, tolerance, tailleMini):&lt;br /&gt;
    global indiceEtage&lt;br /&gt;
    &lt;br /&gt;
    #### INITALISATION ###&lt;br /&gt;
&lt;br /&gt;
    #Initaliser tableaux :&lt;br /&gt;
    #   -double tableau qui contient les informations de chaque pixel    &lt;br /&gt;
    tableauImage = InitTableau(image)&lt;br /&gt;
    &lt;br /&gt;
    #   -la liste des labels&lt;br /&gt;
    listeLabels = []&lt;br /&gt;
    &lt;br /&gt;
    #   -Initialiser la liste des pixels triée par leur intensité&lt;br /&gt;
    listePixelsOrdonnee = InitListeOrdonnee(image)&lt;br /&gt;
&lt;br /&gt;
    #   -l&#039;indice de parcours de la liste des pixels triée&lt;br /&gt;
    indice = 0&lt;br /&gt;
    &lt;br /&gt;
    ### TRAITEMENT POUR CHAQUE INTENSITE ###&lt;br /&gt;
    for intensite in range(256):&lt;br /&gt;
        #Initialiser la liste des pixels appartenant à l&#039;étage traité&lt;br /&gt;
        pixelsMemeEtage = PixelsEtage(listePixelsOrdonnee, intensite, indice, tableauImage, tolerance)&lt;br /&gt;
        &lt;br /&gt;
        indice += len(pixelsMemeEtage)#On connaît le nombre de pixels de cet étage, on en déduit l&#039;indice où commencera le prochaine étage&lt;br /&gt;
        indiceEtage = 0         #Initialisation du compteur de pixels &amp;quot;consécutifs&amp;quot; traités à cet étage&lt;br /&gt;
        compteurPxTraites = 0   #Compteur du nombre de pixels traités à cet étage&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        # Initialiser la file d&#039;attente :&lt;br /&gt;
        # La file d&#039;attente est une liste de listes de pixels ; chaque sous-liste correspond à une distance, une étape du traitement&lt;br /&gt;
        # Les premiers pixels traités sont collés à un pixel déjà labellé ; leurs voisins seront traitables par la suite &lt;br /&gt;
        # et sont donc mis dans la file d&#039;attente &amp;quot;suivante&amp;quot;. Ils sont à une &amp;quot;distance&amp;quot; supérieure, et seront traités après le traitement de la distance actuelle&lt;br /&gt;
        # C&#039;est avec ce principe qu&#039;on forme la file d&#039;attente.&lt;br /&gt;
        fileAttente=[[]]&lt;br /&gt;
        &lt;br /&gt;
        # On fait d&#039;abord un parcours de l&#039;étage, pour trouver les pixels collés à des pixels labellés lors du traitement d&#039;étages précédents&lt;br /&gt;
        for pixel in pixelsMemeEtage:&lt;br /&gt;
            # Pour chaque pixel de l&#039;étage, on regarde ses voisins, si au moins un est labellé, le pixel est à distance 0, c&#039;est à dire traitable directement.&lt;br /&gt;
            if PixelTraitable(pixel[1],pixel[2], tableauImage):&lt;br /&gt;
                #Si c&#039;est le cas on l&#039;ajoute à la file d&#039;attente&lt;br /&gt;
                fileAttente[0].append(pixel)&lt;br /&gt;
                tableauImage[pixel[1]][pixel[2]][LABELLED] = True&lt;br /&gt;
                &lt;br /&gt;
        #On initialise le numéro de l&#039;étape du traitement, la distance : elle commence à 0 et augmente à chaque tour de boucle&lt;br /&gt;
        distance = 0&lt;br /&gt;
        &lt;br /&gt;
        while len(pixelsMemeEtage) &amp;gt; compteurPxTraites: #Tant que le nombre de pixels de cet étage traités est inférieur au nombre de pixels de l&#039;étage, on a pas traité tout l&#039;étage.&lt;br /&gt;
        &lt;br /&gt;
            if len(fileAttente[distance])==0:                                                                             #On regarde s&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
                fileAttente = nouveauLabel(pixelsMemeEtage, tableauImage, fileAttente, listeLabels, intensite, tolerance) #Si non alors il n&#039;y a plus aucun bassin connu à cet étage, on en ajoute:&lt;br /&gt;
                compteurPxTraites += 1                                                                                    #On sait qu&#039;un pixel a été traité en ajoutant un nouveau bassin (le premier pixel du bassin), on l&#039;ajoute au compteur&lt;br /&gt;
                &lt;br /&gt;
            else:                                                                                       #Si il y a des pixels à traiter on les traite               &lt;br /&gt;
                while len(fileAttente[distance]) &amp;gt; 0:                                                   #avec ces nouvelles informations de nouveaux pixels seront traitables.&lt;br /&gt;
                                                                                                        #On continue ces actions jusqu&#039;à qu&#039;aucun pixel ne soit traitable&lt;br /&gt;
                    &lt;br /&gt;
                    determinationLabels(fileAttente, distance, tableauImage, listeLabels, tailleMini)   #On commence par déterminer les labels des pixels traitables&lt;br /&gt;
                    AjoutFileAttente(fileAttente, distance, tableauImage, intensite, tolerance)         #On ajoute les pixels désormais traitable dans la file d&#039;attente qui sera parcourue au prochain tour de boucle&lt;br /&gt;
                    compteurPxTraites+=len(fileAttente[distance])                                       #Tous les pixels de la file d&#039;attente à cet étape ont été traités ; on augmente le compteur en conséquence&lt;br /&gt;
                    distance +=1                                                                        #On passe à l&#039;étape suivant de l&#039;algorithme / la nouvelle file d&#039;attente&lt;br /&gt;
                    &lt;br /&gt;
                distance = 0                                         #Une fois que la boucle est terminé, on a labellé tous les pixels possibles à cet étage qui n&#039;appartiennent pas à un nouveau bassin&lt;br /&gt;
                fileAttente = [[]]                                   #On réinitialise la file d&#039;attente pour refaire le même traitement avec un nouveau bassin&lt;br /&gt;
&lt;br /&gt;
                #Si tous les pixels de l&#039;étage n&#039;ont pas été traités, alors on va ainsi créer un nouveau bassin et le propager, jusqu&#039;à que tous les pixels de l&#039;étage aient été traités.&lt;br /&gt;
&lt;br /&gt;
    return AffichageImage(tableauImage, image, listeLabels)#On enregistre les résultats du traitement dans une image&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def AffichageImage(tableauImage, image, listeLabels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonction qui prend le résultat de l&#039;algorithme sur le fichier fichier et le convertit en image ;&lt;br /&gt;
       Les pixels des lignes de partage des eaux sont affichés en noir, et chaque label est associé à une couleur.&lt;br /&gt;
       On enregistre ensuite cette image.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    lpe = image.copy()&lt;br /&gt;
    couleurs = [0]*len(listeLabels)&lt;br /&gt;
    for e in range(len(couleurs)):&lt;br /&gt;
        couleurs[e] = (randint(100,200),randint(100,200),randint(100,200))  &lt;br /&gt;
    for i in range(image.width):&lt;br /&gt;
        for j in range(image.height):&lt;br /&gt;
            if tableauImage[i][j][LABEL] == &amp;quot;watershed&amp;quot;:&lt;br /&gt;
                lpe.putpixel((i,j),(0,0,0))&lt;br /&gt;
            else:&lt;br /&gt;
                lpe.putpixel((i,j),couleurs[tableauImage[i][j][LABEL]])&lt;br /&gt;
    return lpe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
# UTILISATION&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
def LPE(fichierImage, saveGradient=False, tolerance=0, flou=False, tailleMini=0, fichierGradient=&amp;quot;none&amp;quot;):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend un fichier image en entrée et calcule son gradient,&lt;br /&gt;
    avant d&#039;appliquer l&#039;algorithme de détermination de ligne de partage des eaux&lt;br /&gt;
&lt;br /&gt;
    l&#039;option &amp;quot;saveGradient&amp;quot; est un booléen qui permet d&#039;enregistrer le gradient de l&#039;image si souhaité&lt;br /&gt;
&lt;br /&gt;
    l&#039;option fichierGradient permet d&#039;éxécuter uniquement la détermination de la LPE sur un fichier.&lt;br /&gt;
    On peut donc appliquer la deuxième partir de l&#039;algorithme sur une image gradient déjà calculée.&lt;br /&gt;
&lt;br /&gt;
    D&#039;autres options sont disponibles, qui permettent de réduire la sensibilité au bruit de l&#039;algorithme&lt;br /&gt;
        - tolerance : entier ; deux pixels seront traités comme appartenant au même &amp;quot;étage&amp;quot;&lt;br /&gt;
          si cet entier est plus grand que la différence de leurs valeurs&lt;br /&gt;
        - flou : booléen qui si vaut vrai, applique un flou sur l&#039;image de départ afin de diminuer&lt;br /&gt;
          l&#039;impact du bruit sur le résultat&lt;br /&gt;
        - tailleMini : entier ; au moment où plusieurs zones se rencontrent, si cela est possible,&lt;br /&gt;
          on fusionne les zones de taille inférieure (la taille est le nombre de pixels associés à la zone) à cet entier. Cela permet la supression des petites zones.&lt;br /&gt;
 &lt;br /&gt;
    Les options réduisant la sensibilité au bruit peuvent rendre le résultat imprécis en fonction de leurs valeurs&lt;br /&gt;
 &lt;br /&gt;
    Le résultat est sauvegardé sous forme d&#039;image, où chaque zone est associée à une couleur et où les lignes de partage des eaux sont en noir.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    if fichierGradient == &amp;quot;none&amp;quot;:&lt;br /&gt;
        gradient = FiltreSobel(fichierImage)&lt;br /&gt;
        if saveGradient:&lt;br /&gt;
            gradient.save(&amp;quot;gradient_{}&amp;quot;.format(fichierImage))&lt;br /&gt;
    else:&lt;br /&gt;
        gradient = Image.open(fichierGradient)&lt;br /&gt;
    if flou:&lt;br /&gt;
        gradient = Filtre(gradient, [[1,1,1],[1,1,1],[1,1,1]], saveIm=False)&lt;br /&gt;
    lpe = Algo(gradient, tolerance, tailleMini)&lt;br /&gt;
    &lt;br /&gt;
    nom = &amp;quot;lpe_&amp;quot;&lt;br /&gt;
    if flou:&lt;br /&gt;
        nom+=&amp;quot;flou_&amp;quot;&lt;br /&gt;
    if tolerance!=0:&lt;br /&gt;
        nom+=&amp;quot;tolerance-{}_&amp;quot;.format(tolerance)&lt;br /&gt;
    if tailleMini!=0:&lt;br /&gt;
        nom+=&amp;quot;mini-{}_&amp;quot;.format(tailleMini)&lt;br /&gt;
    nom += &amp;quot;{}&amp;quot;.format(fichierImage)&lt;br /&gt;
    lpe.save(nom)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sources ==&lt;br /&gt;
*[https://fr.wikipedia.org/wiki/Segmentation_d%27image Segmentation d&#039;image]&lt;br /&gt;
*[https://fr.wikipedia.org/wiki/Filtre_de_Sobel Filtre de Sobel]&lt;br /&gt;
*[https://fr.wikipedia.org/wiki/Ligne_de_partage_des_eaux Ligne de partage des eaux (géographie)]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Watershed_(image_processing) Algorithmes existants pour calculer des lignes de partage des eaux (segmentation)]&lt;br /&gt;
*[https://pdfs.semanticscholar.org/a381/9dda9a5f00dbb8cd3413ca7422e37a0d5794.pdf Algorithme de Luc Vincent et Pierre Soille]&lt;br /&gt;
*[https://pillow.readthedocs.io/en/5.1.x/ Bibliothèque de manipulation d&#039;image utilisée (PILLOW)]&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10331</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10331"/>
		<updated>2018-05-26T19:06:51Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Deuxième étape : détermination de la &amp;quot;ligne de partage des eaux&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
Pour la deuxième étape de l&#039;algorithme, on utilise l&#039;image gradient. Elle est alors considérée comme un relief où les pixels de faible intensité sont à faible altitude, et où les pixels d&#039;intensité élevée sont à forte altitude.&lt;br /&gt;
&lt;br /&gt;
=== Définitions ===&lt;br /&gt;
&lt;br /&gt;
En topologie la ligne de partage des eaux peut-être définie de plusieurs manières ; elle est la séparation entre deux bassins versants dans un relief.&lt;br /&gt;
&lt;br /&gt;
Notre image est considérée comme un relief, et la ligne de partage des eaux sera la séparation entre les zones de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Un bassin versant est un bassin dont toutes les eaux ont le même exutoire, par exemple une rivière ou l&#039;océan.&lt;br /&gt;
&lt;br /&gt;
Pour déterminer la ligne de partage des eaux, plusieurs méthodes sont possibles :&lt;br /&gt;
&lt;br /&gt;
* On place une source d&#039;eau dans chaque minimum local (donc dans chaque bassin) et on inonde, quand deux bassins se rencontrent on place une ligne de partage des eaux.&lt;br /&gt;
Les algorithmes utilisant ce principe sont appelés des algorithmes par inondation.&lt;br /&gt;
&lt;br /&gt;
* Une deuxième méthode est de considérer que des deux côtés de la ligne l&#039;écoulement de l&#039;eau se termine dans des bassins versants différents.&lt;br /&gt;
Dans l&#039;algorithme associé, on va faire &amp;quot;s&#039;écouler&amp;quot; de l&#039;eau sur chaque pixel et tracer une ligne de partage entre deux pixels (ou sur l&#039;un des deux pixels) quand les eaux qui s&#039;écoulent depuis ces pixels arrive dans des minima locaux différents.&lt;br /&gt;
Ce sont les algorithmes par ruissellement.&lt;br /&gt;
&lt;br /&gt;
=== Algorithme utilisé (créé par Luc Vincent et Pierre Soille) ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme choisi est un algorithme par inondation.&lt;br /&gt;
&lt;br /&gt;
Les pixels de faible intensité vont donc être traités en priorité.&lt;br /&gt;
&lt;br /&gt;
La première étape est donc de trier les pixels de l&#039;image par ordre croissant d&#039;intensité.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme va traiter les pixels d&#039;intensité la plus basse, puis une fois ceux-ci traités, les pixels avec une intensité légèrement supérieure sont traités, et ainsi de suite jusqu&#039;à avoir traité toute l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Trois problèmes se posent alors :&lt;br /&gt;
* Comment inonder un étage, c&#039;est à dire de quelle manière traiter les pixels de même intensité, à partir de bassins connus ?&lt;br /&gt;
* Comment déterminer à quel bassin appartient un pixel ?&lt;br /&gt;
* Comment repérer les nouveaux bassins ?&lt;br /&gt;
&lt;br /&gt;
==== Inonder un étage (pixels d&#039;intensité identique) ====&lt;br /&gt;
&lt;br /&gt;
Chaque bassin doit progresser à la même vitesse sur un étage, c&#039;est à dire que la ligne de partage des eaux qui sépare deux bassins doit être à égale distance de chacun.&lt;br /&gt;
&lt;br /&gt;
Chaque pixel est à une &amp;quot;distance&amp;quot; d&#039;un bassin. Par exemple les pixels collés à un bassin connu sont à une distance de 1. Leurs voisins sont eux à une distance de 2 (à condition qu&#039;ils ne soient pas collés à un bassin connu).&lt;br /&gt;
&lt;br /&gt;
[[File:file_distance.png|frame|Distance à laquelle se trouve les pixels du bassin connu (bleu)]]&lt;br /&gt;
&lt;br /&gt;
Pour que les bassins s&#039;étendent correctement, on traite d&#039;abord tous les pixels à distance 1, puis ceux à distance 2 et ainsi de suite.&lt;br /&gt;
&lt;br /&gt;
Une file d&#039;attente est créée ; la première étape est de placer les pixels à une distance de 1 dans la file d&#039;attente.&lt;br /&gt;
On va pouvoir traiter ces pixels là. On sait également que leurs voisins sont à une distance de 2, et pourront donc être traités lorsque les pixels à distance 1 seront associés à un bassin.&lt;br /&gt;
&lt;br /&gt;
Les voisins non-traités des pixels à une distance de 1 sont donc les prochains pixels de l&#039;étage à traiter.&lt;br /&gt;
Ils sont placés dans une autre file d&#039;attente, qui sera parcourue une fois que la première a été entièrement utilisée.&lt;br /&gt;
&lt;br /&gt;
Ensuite, les voisins des pixels à une distance de 2 seront les prochains pixels à traiter, et on va recommencer cette suite d&#039;opérations.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les pixels sont placés dans la file d&#039;attente :&lt;br /&gt;
&lt;br /&gt;
* Tout d&#039;abord les pixels directement voisins d&#039;un bassin connu sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_1.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Puis une fois que les pixels dans la file d&#039;attente ont été traités, les pixels voisins des pixels qui viennent d&#039;être traités sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_2.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Cette étape se répète tant qu&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_3.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi le traitement d&#039;une file d&#039;attente (file d&#039;attente d&#039;une distance particulière) fait avancer d&#039;un pixel tous les bassins, et tous les bassins progressent à la même vitesse.&lt;br /&gt;
&lt;br /&gt;
==== Détermination du bassin auquel appartient un pixel ====&lt;br /&gt;
&lt;br /&gt;
Avec cette manière de faire progresser les bassins, le bassin auquel appartient un pixel est déterminé par les bassins auxquels appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
Plusieurs cas sont alors possibles lorsqu&#039;un pixel est traité.&lt;br /&gt;
&lt;br /&gt;
[[File:choix_label.png]] &lt;br /&gt;
&lt;br /&gt;
*Un des cas les plus simples est lorsque tous les voisins traités du pixel appartiennent au même bassin.Le bassin s&#039;étend alors, et le pixel à traiter est associé au bassin auquel appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
*Cette même décision est prise si tous les pixels voisins traités appartiennent au même bassin sauf certains qui appartiennent à une ligne de partage des eaux (LPE). L&#039;autre décision possible est de prolonger la LPE, cependant des lignes épaisses (et imprécises) seraient créées.&lt;br /&gt;
&lt;br /&gt;
*Si les pixels voisins traités du pixel appartiennent à une LPE, le bassin auquel appartient le pixel est inconnu. On ne peut pas &amp;quot;prolonger&amp;quot; la ligne car on ne connaît alors pas à quelle distance d&#039;un bassin se trouve le pixel. Si un pixel appartient à une LPE, ses voisins ne sont donc pas placés en file d&#039;attente.&lt;br /&gt;
&lt;br /&gt;
*Lorsque les pixels voisins appartiennent à des bassins différents, alors le pixel appartient à une LPE ; on sépare les bassins au moment de leur rencontre.&lt;br /&gt;
&lt;br /&gt;
*Le dernier cas se présente lorsque tous les pixels voisins sont traités et qu&#039;ils appartiennent tous à une LPE. Pour éviter qu&#039;un bassin d&#039;un seul pixel soit créé, on prolonge la LPE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En résumé :&lt;br /&gt;
*S&#039;il n&#039;y a qu&#039;un seul bassin autour du pixel en excluant les LPE, le pixel appartient à ce bassin.&lt;br /&gt;
*Si en excluant les LPE il y a plusieurs bassins autour du pixel, le pixel appartient à une LPE.&lt;br /&gt;
*Si le pixel est entouré par des LPE, alors il appartient à une LPE.&lt;br /&gt;
&lt;br /&gt;
==== Création de nouveaux bassins ====&lt;br /&gt;
&lt;br /&gt;
Lors de la propagation des bassins, si une file d&#039;attente est vide alors tous les pixels de l&#039;étage qui appartenaient à un bassin déjà connu ont été traités.&lt;br /&gt;
&lt;br /&gt;
Les pixels restants appartiennent donc à de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Un nouveau bassin est donc créé, le premier pixel qui lui appartient est choisi parmi un des pixels de l&#039;étage non-traités.&lt;br /&gt;
&lt;br /&gt;
On réinitialise alors la file d&#039;attente, et on place les voisins non-traités du pixel associé au nouveau bassin dedans.&lt;br /&gt;
&lt;br /&gt;
On recommence alors le traitement de la file d&#039;attente, et tous les pixels qui appartiennent à ce bassin lui seront attribués.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les nouveaux bassins sont créés, sur une petite image de 5x5.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_image.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Plusieurs étapes se déroulent alors :&lt;br /&gt;
&lt;br /&gt;
* Le traitement d&#039;un étage commence. Les pixels appartenant à un bassin connu se situent à un étage inférieur.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_1.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
* Tous les pixels appartenant à un bassin connu ont été traités ; il reste cependant des pixels non-traités à cet étage. Ils appartiennent donc à un ou plusieurs nouveaux bassins. On choisit donc un pixel non-traité de l&#039;étage, et on lui attribue un nouveau bassin.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_2.png|300px]] [[File:minima_legende.png|500px]]  &lt;br /&gt;
&lt;br /&gt;
* On propage le nouveau bassin créé.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_3.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
Ces étapes sont répétées tant qu&#039;il reste des pixels non-traités à l&#039;étage en cours de traitement. On détecte ainsi tous les nouveaux bassins de l&#039;étage.&lt;br /&gt;
&lt;br /&gt;
Une fois que tous les pixels d&#039;un étage ont été traités, on passe à l&#039;étage supérieur.&lt;br /&gt;
&lt;br /&gt;
==== Résultats ====&lt;br /&gt;
Voici un exemple de résultat que donne cet algorithme :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_brute.png]]&#039;&#039;A gauche le gradient d&#039;une image ; à droite le résultat de l&#039;algorithme de détermination des LPE&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On s&#039;aperçoit que l&#039;algorithme est très sensible au bruit, c&#039;est à dire aux légères variations d&#039;intensité qui créent des minima locaux.&lt;br /&gt;
&lt;br /&gt;
On veut donc améliorer les résultats obtenus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Réduire l&#039;impact du bruit ===&lt;br /&gt;
Plusieurs méthodes sont applicables pour réduire l&#039;impact du bruit sur le résultat.&lt;br /&gt;
&lt;br /&gt;
==== Ajout d&#039;une tolérance ====&lt;br /&gt;
La plus efficace est d&#039;ajouter une &amp;quot;tolérance&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
En effet, dans l&#039;algorithme décrit précédemment, les pixels appartiennent à un même étage si leurs intensités sont exactement identiques.&lt;br /&gt;
&lt;br /&gt;
On considére qu&#039;un pixel appartient à l&#039;étage traité si la différence entre son intensité et l&#039;intensité de l&#039;étage traitée est inférieure à un nombre, qu&#039;on appelle tolérance.&lt;br /&gt;
&lt;br /&gt;
Ainsi la sensibilité au bruit de l&#039;algorithme est réduite car les petites variations d&#039;intensité ne créent plus de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple sur une petite image. &lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance_image.png]]&lt;br /&gt;
&lt;br /&gt;
Tout d&#039;abord, s&#039;il n&#039;y a pas de tolérance, les pixels d&#039;intensité 130 et le pixel d&#039;intensité 135 sont à des étages différents.&lt;br /&gt;
Deux bassins sont donc créés lors du traitement de l&#039;étage d&#039;intensité 130 (bleu et vert, le noir représente une LPE).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_sans_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Cependant, avec l&#039;ajout d&#039;une tolérance (par exemple 10), les trois pixels sont considérés au même étage (car on a 135-130 &amp;lt; 10).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Ainsi une unique bassin est créé.&lt;br /&gt;
&lt;br /&gt;
Le résultat est alors bien plus précis sur de vraies images:&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_res_tolerance.png]] &#039;&#039;A gauche le résultat (de cette [[#Résultats|image]]) de l&#039;algorithme sans tolérance, à droite le résultat avec une tolérance de 5&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Flouter l&#039;image avant le traitement ====&lt;br /&gt;
&lt;br /&gt;
Une deuxième méthode est de traiter l&#039;image avant d&#039;appliquer l&#039;algorithme.&lt;br /&gt;
Si l&#039;image est floutée avant application de l&#039;algorithme, le bruit aura moins d&#039;impact, car les changements de valeur seront atténués.&lt;br /&gt;
Dans l&#039;image floutée, chaque valeur d&#039;un pixel est une moyenne des valeurs des pixels voisins à ce pixel dans l&#039;image originale.&lt;br /&gt;
&lt;br /&gt;
Pour cela, on peut convoluer l&#039;image par cette matrice :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Chaque produit de convolution donnera un entier qui devra être divisé par 9 ; on ajoute  les valeurs des 9 pixels dans un carré de 3x3 autour du pixel dont on veut la nouvelle valeur, pour avoir la moyenne des valeurs dans ce carré on doit alors diviser par 9.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas de bons résultats si elle est la seule utilisée. Cependant, combinée avec l&#039;utilisation de la tolérance, le nombre de petites zones est fortement réduit.&lt;br /&gt;
&lt;br /&gt;
Les LPE sont en contrepartie moins précises.&lt;br /&gt;
&lt;br /&gt;
Exemple de résultat :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou_tolerance5.png]]&lt;br /&gt;
&lt;br /&gt;
==== Fusion des petites zones ====&lt;br /&gt;
&lt;br /&gt;
Une troisième solution est d&#039;empêcher la création de zones trop petites, en indiquant une taille minimale.&lt;br /&gt;
Si des bassins plus petits que cette taille rencontrent un autre bassin, alors ils fusionnent, et le point de leur rencontre est associé à ce nouveau bassin issu de la fusion.&lt;br /&gt;
&lt;br /&gt;
Si au point de rencontre des bassins se rencontrent deux zones plus grandes que la taille minimale, on doit tout de même placer une LPE, pour séparer les deux &amp;quot;grands&amp;quot; bassins.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas non plus de résultats intéressants si utilisée seule, il est nécessaire de la combiner avec l&#039;ajout d&#039;une tolérance.&lt;br /&gt;
&lt;br /&gt;
Un des résultats obtenus avec cette méthode est (où la taille minimale d&#039;un bassin est de 5px) :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_fusion5.png]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
Pour conclure, on peut citer deux avantages de cet algorithme. Tout d&#039;abord, les lignes de partage des eaux créées sont peu épaisses ; elles ne font jamais plus de 3 pixels de large, et se limitent dans la plupart des cas à une épaisseur d&#039;un seul pixel.&lt;br /&gt;
&lt;br /&gt;
Ensuite, l&#039;algorithme est de complexité linéaire, c&#039;est à dire que le temps d’exécution dépend linéairement du nombre de pixels de l&#039;image sur laquelle on applique l&#039;algorithme.&lt;br /&gt;
Voici quelques mesures du temps d&#039;exécution selon le nombre de pixels de l&#039;image en entrée (les images utilisées pour ces tests ne représentent rien et contiennent de nombreuses zones).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tableau_temps.png|border|mesures du temps d&#039;exécution]]&lt;br /&gt;
[[File:lpe_temps.png|border|temps d&#039;exécution en fonction du nombre de pixels]]&lt;br /&gt;
&lt;br /&gt;
== Code source ==&lt;br /&gt;
Voici le code source de l&#039;algorithme.&lt;br /&gt;
&lt;br /&gt;
Pour manipuler des images, j&#039;ai utilisé la bibliothèque PILLOW.&lt;br /&gt;
&lt;br /&gt;
Les principales fonctions utilisées dans cette bibliothèque sont :&lt;br /&gt;
*Image.open(fichier) : ouvrir le fichier fichier en tant qu&#039;objet image&lt;br /&gt;
*image.getpixel((x,y)) : récupérer la valeur du pixel aux coordonnées (x,y) dans un objet Image (appelé ici &#039;image&#039;)&lt;br /&gt;
*image.setpixel((x,y),(r,v,b)) : Changer la valeur du pixel aux coordonnées (x,y) dans un objet Image (appelé ici &#039;image&#039;) ; (r,v,b) sont les valeurs rouge,vert,bleu.&lt;br /&gt;
*image.save(nom) : sauvegarder l&#039;image sous le nom souhaité.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
###########################################################&lt;br /&gt;
# RUET Nils&lt;br /&gt;
# Algorithme de détermination de ligne de partage des eaux, créé par Luc Vincent et Pierre Soille&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   IMPORTATIONS&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
from PIL import Image&lt;br /&gt;
from math import sqrt&lt;br /&gt;
from random import randint&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   CONSTANTES ET VARIABLES GLOBALES&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#Chaque pixel a 3 valeurs qui lui sont associées ; son intensité, l&#039;information sur s&#039;il a déjà été placé en file d&#039;attente, et le label qui lui a été associé.&lt;br /&gt;
#Ces informations sont stockés dans un tableau et ces constantes sont les indices auxquels trouver les informations voulues.&lt;br /&gt;
VALEUR = 0&lt;br /&gt;
LABELLED = 1&lt;br /&gt;
LABEL = 2&lt;br /&gt;
&lt;br /&gt;
indiceEtage = 0# Lorsque l&#039;on traite un étage, certains pixels ne sont pas traitables à partir des données connues. Ils correspondent à de nouveaux bassins.&lt;br /&gt;
               # On dispose d&#039;une liste des pixels de l&#039;étage ; lorsqu&#039;on doit créer un bassin, on cherche donc le premier pixel dans cette liste qui n&#039;est pas labellé.&lt;br /&gt;
               # Cette variable contient le nombre de pixels &amp;quot;consécutifs&amp;quot; traités, c&#039;est à dire l&#039;indice du dernier pixel non traité trouvé dans la liste.&lt;br /&gt;
               # Ainsi on n&#039;a pas à reparcourir la liste entière lorsque l&#039;on créé plusieurs nouveaux bassins à un étage car on sait que tous les pixels &amp;quot;avant&amp;quot; (dans la liste) celui choisi pour créer un nouveau bassin ont été labellés.&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
#   CALCUL DU GRADIENT&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
def deterCoef(mat):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Cette fonction prend un masque de convolution en entrée. Elle permet de déterminer un décalage et un coefficient utilisés pour ramener toutes les valeurs&lt;br /&gt;
    obtenues après convolution entre 0 et 255&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    sommeP=0&lt;br /&gt;
    sommeN=0&lt;br /&gt;
    for i in range(len(mat)):       #On compte les coefficients négatifs et les coefficients positifs dans le masque&lt;br /&gt;
        for j in range(len(mat[0])):&lt;br /&gt;
            if mat[i][j] &amp;gt; 0:&lt;br /&gt;
                sommeP+=mat[i][j]&lt;br /&gt;
            else:&lt;br /&gt;
                sommeN-=mat[i][j]&lt;br /&gt;
    decal = sommeN * 255            #Les coefficients négatifs permettent de connaître la valeur à ajouter pour n&#039;obtenir que des valeurs positives après la convolution&lt;br /&gt;
    coef = sommeN+sommeP            #La somme des valeurs absolues des coefficients permet de savoir comment ramener toutes ces valeurs entre 0 et 255.&lt;br /&gt;
    return (coef, decal)&lt;br /&gt;
&lt;br /&gt;
def Convolution(image, x, y, filtre):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Effectue le produit de convolution entre un filtre et l&#039;enourage du pixel aux coordonnées (x,y) dans l&#039;image &#039;image&#039; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    somme = 0&lt;br /&gt;
    centre = len(filtre)//2&lt;br /&gt;
    for i in range(-centre,centre+1):           #i et j indiquent la position relative du pixel dont on a besoin dans l&#039;image par rapport au pixel dont on calcule la nouvelle valeur.&lt;br /&gt;
        for j in range(-centre,centre+1):&lt;br /&gt;
            if filtre[i+centre][j+centre] != 0: #Vu qu&#039;on multiplie la valeur d&#039;un pixel par une valeur dans le filtre, on vérifie que la valeur dans le filtre ne vaut pas 0.&lt;br /&gt;
                if 0&amp;lt;=x+i&amp;lt;image.width and 0&amp;lt;=y+j&amp;lt;image.height: #Si le pixel voulu est dans l&#039;image, xl (x local) et yl (y local) sont ses coordonnées.&lt;br /&gt;
                    xl = x+i&lt;br /&gt;
                    yl = y+j&lt;br /&gt;
                else:                           #Sinon on va chercher la valeur du pixel en symlétrie par rapport au pixel dont on veut la nouvelle valeur&lt;br /&gt;
                    xl = x-i&lt;br /&gt;
                    yl = y-j&lt;br /&gt;
                    &lt;br /&gt;
                if not 0&amp;lt;=xl&amp;lt;image.width or not 0&amp;lt;=yl&amp;lt;image.height: #Si après cela le pixel n&#039;est toujours pas dans l&#039;image, on se réfère à la valeur du pixel dont on veut la nouvelle valeur.&lt;br /&gt;
                    xl = x&lt;br /&gt;
                    yl = y                    &lt;br /&gt;
                somme+= (image.getpixel((xl,yl))[0] *filtre[i+centre][j+centre])&lt;br /&gt;
    return somme&lt;br /&gt;
&lt;br /&gt;
def AppliqueFiltre(image, filtre):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permet d&#039;appliquer le masque de convolution &#039;filtre&#039; (double tableau) à tous les pixels de l&#039;image &#039;image&#039; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = image.copy()&lt;br /&gt;
    (coef, decal) = deterCoef(filtre)&lt;br /&gt;
    for x in range(image.width):            #Pour chaque pixel de l&#039;image&lt;br /&gt;
        for y in range(image.height):&lt;br /&gt;
            somme = Convolution(image,x,y,filtre) #On prend le résultat de la convolution&lt;br /&gt;
            val = round((somme+decal)/coef)       #On ramène ce résultat entre 0 et 255&lt;br /&gt;
            res.putpixel((x,y),(val,val,val))     #On met le pixel dans l&#039;image résultat à cette valeur&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def Filtre(image, filtre, nom_sortie=&amp;quot;filtre&amp;quot;, saveIm=True):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permet d&#039;appeler la fonction qui permet de convoluer l&#039;image &#039;image&#039; par le masque &#039;filtre&#039; et choisir si le résultat est sauvegardé dans un fichier image ou non&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = AppliqueFiltre(image, filtre)&lt;br /&gt;
    if saveIm:&lt;br /&gt;
        res.save(&amp;quot;{}_{}&amp;quot;.format(nom_sortie, fichierImage))&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
def FiltreSobel(fichierIm):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Permer de calculer la norme du gradient de l&#039;image &#039;fichierIm&#039; (nom du fichier image) en chaque point avec la méthode du filtre de Sobel&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    image = Image.open(fichierIm)&lt;br /&gt;
    res = image.copy()&lt;br /&gt;
    masquex = [[-1,0,1],[-2,0,2],[-1,0,1]]&lt;br /&gt;
    masquey = [[-1,-2,-1],[0,0,0],[1,2,1]]&lt;br /&gt;
    for x in range(image.width):                                #Pour chaque pixel de l&#039;image&lt;br /&gt;
        for y in range(image.height):&lt;br /&gt;
            dx = Convolution(image,x,y,masquex)                 #On approxime ses dérivées partielles&lt;br /&gt;
            dy = Convolution(image,x,y,masquey)                 #&lt;br /&gt;
&lt;br /&gt;
            val = round(sqrt(dx**2 + dy**2)*255/(sqrt(2)*1020)) #On calcule la norme du gradient, qu&#039;on ramène entre 0 et 255.&lt;br /&gt;
            res.putpixel((x,y),(val,val,val))                   #Cette valeur est associée au pixel dans le résultat&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#########################################################################################################&lt;br /&gt;
#   ALGORITHME LIGNE DE PARTAGE DES EAUX&lt;br /&gt;
#########################################################################################################&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def InitTableau(image):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Initialise le double tableau contenant les informations sur chaque pixel; aux indices [x][y] on accède aux informations du pixel (x,y). Ces informations sont l&#039;intensité, l&#039;avancement du traitement et le label.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tableau = [0]*image.width                                       #On initialise un tableau de la taille de l&#039;image&lt;br /&gt;
    for i in range(len(tableau)):&lt;br /&gt;
        tableau[i]=[0]*image.height&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(tableau)):                                   #On initialise les informations de chaque pixel :&lt;br /&gt;
        for j in range(len(tableau[i])):&lt;br /&gt;
            tableau[i][j] = [image.getpixel((i,j))[0],False,&amp;quot;init&amp;quot;] #Sa valeur dans l&#039;image, l&#039;avancement du traitement (False si le pixel n&#039;a pas été traité, True sinon), et le label qui lui est associé&lt;br /&gt;
    return tableau&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def InitListeOrdonnee(image):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Initialise la liste des pixels de l&#039;image, triés par intensité ; chaque case contient la position du pixel attribué et son intensité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    liste = []&lt;br /&gt;
    for i in range(image.width):&lt;br /&gt;
        for j in range(image.height):&lt;br /&gt;
            liste.append([image.getpixel((i,j))[0],i,j])#On regarde chaque pixel de l&#039;image et on ajoute dans une case de la liste son intensité, son abcisse, son ordonnée&lt;br /&gt;
    liste.sort(key=lambda pixel: pixel[0])              #On trie cette liste en fonction de l&#039;intensité des pixels (ordre croissant)&lt;br /&gt;
    return liste&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def PixelsEtage(listeOrdonnee, intensite, indice, tablImage, tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend en entrée la liste ordonnée des pixels de l&#039;image, une intensité, l&#039;indice à partir duquel commence cette intensité dans la liste, et une tolérance.&lt;br /&gt;
       Renvoie la liste des pixels d&#039;un &amp;quot;étage&amp;quot; d&#039;intensité, plus précisément :&lt;br /&gt;
       La liste des pixels non-labellés de l&#039;intensité choisie plus ou moins la tolérance donnée. L&#039;indice à partir duquel commence l&#039;intensité choisie est modifié par une autre partie&lt;br /&gt;
       du programme ; on sait que tous les pixels d&#039;une valeur inférieure à l&#039;intensité choisie ont déjà été traités par l&#039;algorithme.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    pixels = []&lt;br /&gt;
    nouvelIndice = indice&lt;br /&gt;
    while nouvelIndice &amp;lt; len(listeOrdonnee) and (listeOrdonnee[nouvelIndice][0] - intensite) &amp;lt;= tolerance:#On ajoute à la liste des pixels de l&#039;étage intensité les pixels qui ont une intensité dans l&#039;intervalle souhaité&lt;br /&gt;
        if not tablImage[listeOrdonnee[nouvelIndice][1]][listeOrdonnee[nouvelIndice][2]][LABELLED]:       #Et non labellés&lt;br /&gt;
            pixels.append(listeOrdonnee[nouvelIndice])&lt;br /&gt;
        nouvelIndice+=1&lt;br /&gt;
    return pixels&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def labelsPixelsVoisins(x, y, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonctions qui renvoie la liste des labels des pixels voisins au pixel situé aux coordonnée entrées en paramètre, ignore les pixels non labellés&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    labels = []&lt;br /&gt;
&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins:&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]) and tablImage[x+i][y+j][LABEL] != &amp;quot;init&amp;quot;:&lt;br /&gt;
            labels.append(tablImage[x+i][y+j][LABEL])        &lt;br /&gt;
    return labels&lt;br /&gt;
&lt;br /&gt;
def PixelTraitable(xPixel, yPixel, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonction qui renvoie True si le pixel aux coordonnées entrées en paramètre est labellable, False sinon&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    labels = labelsPixelsVoisins(xPixel, yPixel, tablImage)&lt;br /&gt;
    for label in labels:&lt;br /&gt;
        if label!=&amp;quot;watershed&amp;quot;:#Un pixel est labellable s&#039;il a un pixel labellé par autre chose qu&#039;une ligne de partage parmi ses voisins.&lt;br /&gt;
            return True&lt;br /&gt;
&lt;br /&gt;
    voisinsDansImage = 0                                                #Sinon si le pixel n&#039;a pas de pixels labellés par autre chose que watershed parmi ses voisins,&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins:                                              #On détermine son nombre de voisins (4 au centre, 3 sur les bords et 2 dans les coins)&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=xPixel+i&amp;lt;len(tablImage) and 0&amp;lt;=yPixel+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
            voisinsDansImage += 1&lt;br /&gt;
    return len(labels)==voisinsDansImage                                #Le pixel est labellable si tous ses voisins sont des lignes de partage des eaux&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def nouveauLabel(listePixels, tablImage, fileAttente, listeLabels, intensite, tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Cette fonction est appelée lorsque l&#039;algorithme n&#039;a pas traité tous les pixels d&#039;un étage, mais qu&#039;aucun pixel n&#039;a été placé en file attente &amp;lt;=&amp;gt; aucun pixel non traité n&#039;est traitable à cet étage.&lt;br /&gt;
       A partir de la liste des pixels de l&#039;étage, de la liste des labels attribués, de l&#039;intensité de l&#039;étage et de la tolérance, on modifie alors la file d&#039;attente&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    global indiceEtage&lt;br /&gt;
    while tablImage[listePixels[indiceEtage][1]][listePixels[indiceEtage][2]][LABELLED]: #On parcourt la liste des pixels de l&#039;étage pour en trouver un non labellé&lt;br /&gt;
        indiceEtage+=1     &lt;br /&gt;
    x = listePixels[indiceEtage][1]&lt;br /&gt;
    y = listePixels[indiceEtage][2]&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
    for coords in voisins: #On regarde les voisins du premier pixel non labellé trouvé&lt;br /&gt;
        i = coords[0]&lt;br /&gt;
        j = coords[1]&lt;br /&gt;
        if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]) and not tablImage[x+i][y+j][LABELLED] and (tablImage[x+i][y+j][VALEUR] - intensite) &amp;lt;= tolerance:&lt;br /&gt;
            #Si un voisin est dans le bon intervalle d&#039;intensité, non labellé et dans l&#039;image, alors on le place dans la file d&#039;attente&lt;br /&gt;
            tablImage[x+i][y+j][LABELLED] = True&lt;br /&gt;
            fileAttente[0].append([tablImage[x+i][y+j][VALEUR], x+i, y+j])&lt;br /&gt;
&lt;br /&gt;
    tablImage[x][y][LABEL] = len(listeLabels)#On créé un nouveau label en augmentant de 1 le numéro du dernier label ; cela correspond à la longueur de la liste des labels&lt;br /&gt;
                                             #Puis on l&#039;associe au pixel non traité trouvé dans l&#039;étage&lt;br /&gt;
    tablImage[x][y][LABELLED] = True         #Ce pixel et désormais traité&lt;br /&gt;
    listeLabels.append(1)                    #On ajoute le nouveau label créé à la liste des labels ; le bassin associé à ce label est de taille 1px initalement&lt;br /&gt;
    return fileAttente                       #On renvoie la nouvelle file d&#039;attente ; les voisins traitables du pixel trouvés   &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def AjoutFileAttente(fileAttente, distance, tablImage,intensite,tolerance):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;A partir des pixels traités au moment de l&#039;appel de cette fonction, on détermine les prochains pixels qui pourront être traités par la suite.&lt;br /&gt;
       Ces pixels sont les voisins des pixels déjà traités (à l&#039;exception des lignes de partage) du même étage, non traités et dans l&#039;image&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    fileAttente.append([])             #On ajoute une file d&#039;attente pour la distance suivante&lt;br /&gt;
    for pixel in fileAttente[distance]:#Pour chaque pixel traité à la distance précédente&lt;br /&gt;
        x = pixel[1]&lt;br /&gt;
        y = pixel[2]&lt;br /&gt;
        if tablImage[x][y][LABEL] != &amp;quot;watershed&amp;quot;:#S&#039;il n&#039;est pas une ligne de partage, il appartient à un bassin et ses voisins sont potentiellement traitables      &lt;br /&gt;
            voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
            for coords in voisins:&lt;br /&gt;
                i = coords[0]&lt;br /&gt;
                j = coords[1]&lt;br /&gt;
                if 0&amp;lt;=x+i&amp;lt;len(tablImage) and 0&amp;lt;=y+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
                    if (tablImage[x+i][y+j][VALEUR] - intensite) &amp;lt;= tolerance and not tablImage[x+i][y+j][LABELLED]:#Les voisins traitables sont les pixel du même étage non labellés&lt;br /&gt;
                        tablImage[x+i][y+j][LABELLED] = True&lt;br /&gt;
                        fileAttente[distance+1].append([tablImage[x+i][y+j][VALEUR], x+i, y+j])#On les ajoute à la file d&#039;attente suivante&lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
def valeursUniques(T):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend un tableau en entrée et supprime les valeurs qui s&#039;y trouvent en double&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for e in T:&lt;br /&gt;
        if not e in res:&lt;br /&gt;
            res.append(e)&lt;br /&gt;
    return res&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
def fusionZones(labelsChange, nouvLabel, listeLabels, x, y, tablImage):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; A partir d&#039;une liste de labels à changer (labelsChange), du nouveau label qui leur sera associé, et d&#039;un pixel de départ aux coordonnées x,y&lt;br /&gt;
    On fusionne les bassins qui se rencontrent en x,y en modifiant le label des pixels de ces bassins.&lt;br /&gt;
    Chaque fois qu&#039;un pixel change de label, on change le label de ses voisins si cela est nécéssaire&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tablImage[x][y][LABEL] = nouvLabel&lt;br /&gt;
    voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
&lt;br /&gt;
    file = [[x,y]]&lt;br /&gt;
    indiceFile = 0&lt;br /&gt;
    while indiceFile != len(file):#Tant que tous les pixels de la file n&#039;ont pas été associés au nouveau label&lt;br /&gt;
        pixel = file[indiceFile]  #On prend le prochain pixel dans la file&lt;br /&gt;
        for coords in voisins:    #Et on regarde chacun de ses voisins                                 &lt;br /&gt;
            i = coords[0]&lt;br /&gt;
            j = coords[1]&lt;br /&gt;
            if 0&amp;lt;=pixel[0]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[1]+j&amp;lt;len(tablImage[0]) and (tablImage[pixel[0]+i][pixel[1]+j][LABEL] in labelsChange): #Si un pixel voisins dans l&#039;image a un label qui doit être changé&lt;br /&gt;
                file.append([pixel[0]+i,pixel[1]+j])#On l&#039;ajoute à la file&lt;br /&gt;
                tablImage[pixel[0]+i][pixel[1]+j][LABEL] = nouvLabel#On lui attribue son nouveau label&lt;br /&gt;
                listeLabels[nouvLabel]+=1#On augmente le nombre de pixels qui appartiennent au nouveau bassin&lt;br /&gt;
        indiceFile+=1&lt;br /&gt;
  &lt;br /&gt;
def determinationLabels(fileAttente, distance, tablImage, listeLabels, tailleMini):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Procédure qui détermine le label attribué à un pixel en fonction des labels des pixels voisins&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for pixel in fileAttente[distance]:                             #On attribue un label à chaque pixel dans la file d&#039;attente&lt;br /&gt;
        labels = labelsPixelsVoisins(pixel[1], pixel[2], tablImage) #On récupère les labels de ses voisins&lt;br /&gt;
        lstLabels = valeursUniques(labels)                          #On supprime les doublons,&lt;br /&gt;
        if (&amp;quot;watershed&amp;quot; in lstLabels):                              #et le label de ligne de partage des eaux&lt;br /&gt;
            lstLabels.remove(&amp;quot;watershed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        voisinsDansImage = 0&lt;br /&gt;
        voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
        for coords in voisins:                                      #On compte le nombre de pixels voisins dans l&#039;image ; 4 si le pixel est au centre de l&#039;image, 3 s&#039;il est au bord etc.&lt;br /&gt;
            i = coords[0]&lt;br /&gt;
            j = coords[1]&lt;br /&gt;
            if 0&amp;lt;=pixel[1]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[2]+j&amp;lt;len(tablImage[0]):&lt;br /&gt;
                voisinsDansImage += 1&lt;br /&gt;
        if len(lstLabels) == 0 and len(labels)==voisinsDansImage:   #Si le pixel est entouré de watershed, on lui attribue ce label&lt;br /&gt;
            tablImage[pixel[1]][pixel[2]][LABEL] = &amp;quot;watershed&amp;quot;&lt;br /&gt;
                &lt;br /&gt;
        elif len(lstLabels) == 1:                                   #Si le pixel n&#039;a qu&#039;un seul label hormis les watershed autour de lui, on lui attribue ce label&lt;br /&gt;
            tablImage[pixel[1]][pixel[2]][LABEL] = lstLabels[0]&lt;br /&gt;
            listeLabels[lstLabels[0]] += 1&lt;br /&gt;
            &lt;br /&gt;
        elif len(lstLabels) &amp;gt; 1:                                    #S&#039;il y a plusieurs labels autour de lui, il se trouve entre deux bassins et est donc une ligne de partage des eaux ; si les bassins sont petits on les fusionne&lt;br /&gt;
            i = 0&lt;br /&gt;
            plusGrand = []&lt;br /&gt;
            moinsGrand = []&lt;br /&gt;
            while i&amp;lt;len(lstLabels):&lt;br /&gt;
                if listeLabels[lstLabels[i]]&amp;gt;tailleMini:&lt;br /&gt;
                    plusGrand.append(lstLabels[i])&lt;br /&gt;
                else:&lt;br /&gt;
                    moinsGrand.append(lstLabels[i])&lt;br /&gt;
                i+=1&lt;br /&gt;
            if len(moinsGrand)&amp;gt;0 and len(plusGrand)&amp;lt;2:  #Si des bassins sont en dessous de la taille minimale et qu&#039;il n&#039;y a pas plusieurs bassins au dessus de cette taille &lt;br /&gt;
                if len(plusGrand)==1:                   #Si il n&#039;y a qu&#039;un bassin au dessus, tous les petits bassins lui seront rattachés&lt;br /&gt;
                    labelsChange = moinsGrand[:]&lt;br /&gt;
                    nouvLabel = plusGrand[0]&lt;br /&gt;
                elif len(plusGrand)==0:                 #S&#039;il n&#039;y en a aucun, les petits bassins fusionnent entre eux&lt;br /&gt;
                    labelsChange = moinsGrand[1:]&lt;br /&gt;
                    nouvLabel = moinsGrand[0]&lt;br /&gt;
        &lt;br /&gt;
                tablImage[pixel[1]][pixel[2]][LABEL] = nouvLabel #Les bassins vont fusionner, le pixel que l&#039;on regardait appartient donc au nouveau bassin&lt;br /&gt;
                listeLabels[nouvLabel] += 1                      #On augmente le compteur de pixels appartenant à ce bassin&lt;br /&gt;
                &lt;br /&gt;
                voisins = [(-1,0),(1,0),(0,-1),(0,1)]&lt;br /&gt;
                for coords in voisins:                      #On regarde chaque voisin, s&#039;il est dans l&#039;image et que son label doit etre changé, on appelle la procédure qui permet de changer les labels du bassin&lt;br /&gt;
                    i = coords[0]&lt;br /&gt;
                    j = coords[1]&lt;br /&gt;
                    if 0&amp;lt;=pixel[1]+i&amp;lt;len(tablImage) and 0&amp;lt;=pixel[2]+j&amp;lt;len(tablImage[0]) and tablImage[pixel[1]+i][pixel[2]+j][LABEL] in labelsChange:&lt;br /&gt;
                        fusionZones(labelsChange, nouvLabel, listeLabels, pixel[1]+i, pixel[2]+j, tablImage)&lt;br /&gt;
                        &lt;br /&gt;
            else:                                       #Si aucun bassin n&#039;est trop petit ou si au moins 2 sont au dessus de la taille minimale, le pixel est une ligne de partage des eaux&lt;br /&gt;
                tablImage[pixel[1]][pixel[2]][LABEL] = &amp;quot;watershed&amp;quot;&lt;br /&gt;
&lt;br /&gt;
                &lt;br /&gt;
def Algo(image, tolerance, tailleMini):&lt;br /&gt;
    global indiceEtage&lt;br /&gt;
    &lt;br /&gt;
    #### INITALISATION ###&lt;br /&gt;
&lt;br /&gt;
    #Initaliser tableaux :&lt;br /&gt;
    #   -double tableau qui contient les informations de chaque pixel    &lt;br /&gt;
    tableauImage = InitTableau(image)&lt;br /&gt;
    &lt;br /&gt;
    #   -la liste des labels&lt;br /&gt;
    listeLabels = []&lt;br /&gt;
    &lt;br /&gt;
    #   -Initialiser la liste des pixels triée par leur intensité&lt;br /&gt;
    listePixelsOrdonnee = InitListeOrdonnee(image)&lt;br /&gt;
&lt;br /&gt;
    #   -l&#039;indice de parcours de la liste des pixels triée&lt;br /&gt;
    indice = 0&lt;br /&gt;
    &lt;br /&gt;
    ### TRAITEMENT POUR CHAQUE INTENSITE ###&lt;br /&gt;
    for intensite in range(256):&lt;br /&gt;
        #Initialiser la liste des pixels appartenant à l&#039;étage traité&lt;br /&gt;
        pixelsMemeEtage = PixelsEtage(listePixelsOrdonnee, intensite, indice, tableauImage, tolerance)&lt;br /&gt;
        &lt;br /&gt;
        indice += len(pixelsMemeEtage)#On connaît le nombre de pixels de cet étage, on en déduit l&#039;indice où commencera le prochaine étage&lt;br /&gt;
        indiceEtage = 0         #Initialisation du compteur de pixels &amp;quot;consécutifs&amp;quot; traités à cet étage&lt;br /&gt;
        compteurPxTraites = 0   #Compteur du nombre de pixels traités à cet étage&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        # Initialiser la file d&#039;attente :&lt;br /&gt;
        # La file d&#039;attente est une liste de listes de pixels ; chaque sous-liste correspond à une distance, une étape du traitement&lt;br /&gt;
        # Les premiers pixels traités sont collés à un pixel déjà labellé ; leurs voisins seront traitables par la suite &lt;br /&gt;
        # et sont donc mis dans la file d&#039;attente &amp;quot;suivante&amp;quot;. Ils sont à une &amp;quot;distance&amp;quot; supérieure, et seront traités après le traitement de la distance actuelle&lt;br /&gt;
        # C&#039;est avec ce principe qu&#039;on forme la file d&#039;attente.&lt;br /&gt;
        fileAttente=[[]]&lt;br /&gt;
        &lt;br /&gt;
        # On fait d&#039;abord un parcours de l&#039;étage, pour trouver les pixels collés à des pixels labellés lors du traitement d&#039;étages précédents&lt;br /&gt;
        for pixel in pixelsMemeEtage:&lt;br /&gt;
            # Pour chaque pixel de l&#039;étage, on regarde ses voisins, si au moins un est labellé, le pixel est à distance 0, c&#039;est à dire traitable directement.&lt;br /&gt;
            if PixelTraitable(pixel[1],pixel[2], tableauImage):&lt;br /&gt;
                #Si c&#039;est le cas on l&#039;ajoute à la file d&#039;attente&lt;br /&gt;
                fileAttente[0].append(pixel)&lt;br /&gt;
                tableauImage[pixel[1]][pixel[2]][LABELLED] = True&lt;br /&gt;
                &lt;br /&gt;
        #On initialise le numéro de l&#039;étape du traitement, la distance : elle commence à 0 et augmente à chaque tour de boucle&lt;br /&gt;
        distance = 0&lt;br /&gt;
        &lt;br /&gt;
        while len(pixelsMemeEtage) &amp;gt; compteurPxTraites: #Tant que le nombre de pixels de cet étage traités est inférieur au nombre de pixels de l&#039;étage, on a pas traité tout l&#039;étage.&lt;br /&gt;
        &lt;br /&gt;
            if len(fileAttente[distance])==0:                                                                             #On regarde s&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
                fileAttente = nouveauLabel(pixelsMemeEtage, tableauImage, fileAttente, listeLabels, intensite, tolerance) #Si non alors il n&#039;y a plus aucun bassin connu à cet étage, on en ajoute:&lt;br /&gt;
                compteurPxTraites += 1                                                                                    #On sait qu&#039;un pixel a été traité en ajoutant un nouveau bassin (le premier pixel du bassin), on l&#039;ajoute au compteur&lt;br /&gt;
                &lt;br /&gt;
            else:                                                                                       #Si il y a des pixels à traiter on les traite               &lt;br /&gt;
                while len(fileAttente[distance]) &amp;gt; 0:                                                   #avec ces nouvelles informations de nouveaux pixels seront traitables.&lt;br /&gt;
                                                                                                        #On continue ces actions jusqu&#039;à qu&#039;aucun pixel ne soit traitable&lt;br /&gt;
                    &lt;br /&gt;
                    determinationLabels(fileAttente, distance, tableauImage, listeLabels, tailleMini)   #On commence par déterminer les labels des pixels traitables&lt;br /&gt;
                    AjoutFileAttente(fileAttente, distance, tableauImage, intensite, tolerance)         #On ajoute les pixels désormais traitable dans la file d&#039;attente qui sera parcourue au prochain tour de boucle&lt;br /&gt;
                    compteurPxTraites+=len(fileAttente[distance])                                       #Tous les pixels de la file d&#039;attente à cet étape ont été traités ; on augmente le compteur en conséquence&lt;br /&gt;
                    distance +=1                                                                        #On passe à l&#039;étape suivant de l&#039;algorithme / la nouvelle file d&#039;attente&lt;br /&gt;
                    &lt;br /&gt;
                distance = 0                                         #Une fois que la boucle est terminé, on a labellé tous les pixels possibles à cet étage qui n&#039;appartiennent pas à un nouveau bassin&lt;br /&gt;
                fileAttente = [[]]                                   #On réinitialise la file d&#039;attente pour refaire le même traitement avec un nouveau bassin&lt;br /&gt;
&lt;br /&gt;
                #Si tous les pixels de l&#039;étage n&#039;ont pas été traités, alors on va ainsi créer un nouveau bassin et le propager, jusqu&#039;à que tous les pixels de l&#039;étage aient été traités.&lt;br /&gt;
&lt;br /&gt;
    return AffichageImage(tableauImage, image, listeLabels)#On enregistre les résultats du traitement dans une image&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def AffichageImage(tableauImage, image, listeLabels):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Fonction qui prend le résultat de l&#039;algorithme sur le fichier fichier et le convertit en image ;&lt;br /&gt;
       Les pixels des lignes de partage des eaux sont affichés en noir, et chaque label est associé à une couleur.&lt;br /&gt;
       On enregistre ensuite cette image.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    lpe = image.copy()&lt;br /&gt;
    couleurs = [0]*len(listeLabels)&lt;br /&gt;
    for e in range(len(couleurs)):&lt;br /&gt;
        couleurs[e] = (randint(100,200),randint(100,200),randint(100,200))  &lt;br /&gt;
    for i in range(image.width):&lt;br /&gt;
        for j in range(image.height):&lt;br /&gt;
            if tableauImage[i][j][LABEL] == &amp;quot;watershed&amp;quot;:&lt;br /&gt;
                lpe.putpixel((i,j),(0,0,0))&lt;br /&gt;
            else:&lt;br /&gt;
                lpe.putpixel((i,j),couleurs[tableauImage[i][j][LABEL]])&lt;br /&gt;
    return lpe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
########################################################################################################&lt;br /&gt;
# UTILISATION&lt;br /&gt;
########################################################################################################&lt;br /&gt;
&lt;br /&gt;
def LPE(fichierImage, saveGradient=False, tolerance=0, flou=False, tailleMini=0, fichierGradient=&amp;quot;none&amp;quot;):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Prend un fichier image en entrée et calcule son gradient,&lt;br /&gt;
    avant d&#039;appliquer l&#039;algorithme de détermination de ligne de partage des eaux&lt;br /&gt;
&lt;br /&gt;
    l&#039;option &amp;quot;saveGradient&amp;quot; est un booléen qui permet d&#039;enregistrer le gradient de l&#039;image si souhaité&lt;br /&gt;
&lt;br /&gt;
    l&#039;option fichierGradient permet d&#039;éxécuter uniquement la détermination de la LPE sur un fichier.&lt;br /&gt;
    On peut donc appliquer la deuxième partir de l&#039;algorithme sur une image gradient déjà calculée.&lt;br /&gt;
&lt;br /&gt;
    D&#039;autres options sont disponibles, qui permettent de réduire la sensibilité au bruit de l&#039;algorithme&lt;br /&gt;
        - tolerance : entier ; deux pixels seront traités comme appartenant au même &amp;quot;étage&amp;quot;&lt;br /&gt;
          si cet entier est plus grand que la différence de leurs valeurs&lt;br /&gt;
        - flou : booléen qui si vaut vrai, applique un flou sur l&#039;image de départ afin de diminuer&lt;br /&gt;
          l&#039;impact du bruit sur le résultat&lt;br /&gt;
        - tailleMini : entier ; au moment où plusieurs zones se rencontrent, si cela est possible,&lt;br /&gt;
          on fusionne les zones de taille inférieure (la taille est le nombre de pixels associés à la zone) à cet entier. Cela permet la supression des petites zones.&lt;br /&gt;
 &lt;br /&gt;
    Les options réduisant la sensibilité au bruit peuvent rendre le résultat imprécis en fonction de leurs valeurs&lt;br /&gt;
 &lt;br /&gt;
    Le résultat est sauvegardé sous forme d&#039;image, où chaque zone est associée à une couleur et où les lignes de partage des eaux sont en noir.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    if fichierGradient == &amp;quot;none&amp;quot;:&lt;br /&gt;
        gradient = FiltreSobel(fichierImage)&lt;br /&gt;
        if saveGradient:&lt;br /&gt;
            gradient.save(&amp;quot;gradient_{}&amp;quot;.format(fichierImage))&lt;br /&gt;
    else:&lt;br /&gt;
        gradient = Image.open(fichierGradient)&lt;br /&gt;
    if flou:&lt;br /&gt;
        gradient = Filtre(gradient, [[1,1,1],[1,1,1],[1,1,1]], saveIm=False)&lt;br /&gt;
    lpe = Algo(gradient, tolerance, tailleMini)&lt;br /&gt;
    &lt;br /&gt;
    nom = &amp;quot;lpe_&amp;quot;&lt;br /&gt;
    if flou:&lt;br /&gt;
        nom+=&amp;quot;flou_&amp;quot;&lt;br /&gt;
    if tolerance!=0:&lt;br /&gt;
        nom+=&amp;quot;tolerance-{}_&amp;quot;.format(tolerance)&lt;br /&gt;
    if tailleMini!=0:&lt;br /&gt;
        nom+=&amp;quot;mini-{}_&amp;quot;.format(tailleMini)&lt;br /&gt;
    nom += &amp;quot;{}&amp;quot;.format(fichierImage)&lt;br /&gt;
    lpe.save(nom)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sources ==&lt;br /&gt;
*[https://fr.wikipedia.org/wiki/Segmentation_d%27image Segmentation d&#039;image]&lt;br /&gt;
*[https://fr.wikipedia.org/wiki/Filtre_de_Sobel Filtre de Sobel]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Watershed_(image_processing) Algorithmes existants pour calculer des lignes de partage des eaux]&lt;br /&gt;
*[https://pdfs.semanticscholar.org/a381/9dda9a5f00dbb8cd3413ca7422e37a0d5794.pdf Algorithme de Luc Vincent et Pierre Soille)]&lt;br /&gt;
*[https://pillow.readthedocs.io/en/5.1.x/ Bibliothèque de manipulation d&#039;image utilisée (PILLOW)]&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10330</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10330"/>
		<updated>2018-05-26T18:49:33Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Deuxième étape : détermination de la &amp;quot;ligne de partage des eaux&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
Pour la deuxième étape de l&#039;algorithme, on utilise l&#039;image gradient. Elle est alors considérée comme un relief où les pixels de faible intensité sont à faible altitude, et où les pixels d&#039;intensité élevée sont à forte altitude.&lt;br /&gt;
&lt;br /&gt;
=== Définitions ===&lt;br /&gt;
&lt;br /&gt;
En topologie la ligne de partage des eaux peut-être définie de plusieurs manières ; elle est la séparation entre deux bassins versants dans un relief.&lt;br /&gt;
&lt;br /&gt;
Notre image est considérée comme un relief, et la ligne de partage des eaux sera la séparation entre les zones de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Un bassin versant est un bassin dont toutes les eaux ont le même exutoire, par exemple une rivière ou l&#039;océan.&lt;br /&gt;
&lt;br /&gt;
Pour déterminer la ligne de partage des eaux, plusieurs méthodes sont possibles :&lt;br /&gt;
&lt;br /&gt;
* On place une source d&#039;eau dans chaque minimum local (donc dans chaque bassin) et on inonde, quand deux bassins se rencontrent on place une ligne de partage des eaux.&lt;br /&gt;
Les algorithmes utilisant ce principe sont appelés des algorithmes par inondation.&lt;br /&gt;
&lt;br /&gt;
* Une deuxième méthode est de considérer que des deux côtés de la ligne l&#039;écoulement de l&#039;eau se termine dans des bassins versants différents.&lt;br /&gt;
Dans l&#039;algorithme associé, on va faire &amp;quot;s&#039;écouler&amp;quot; de l&#039;eau sur chaque pixel et tracer une ligne de partage entre deux pixels (ou sur l&#039;un des deux pixels) quand les eaux qui s&#039;écoulent depuis ces pixels arrive dans des minima locaux différents.&lt;br /&gt;
Ce sont les algorithmes par ruissellement.&lt;br /&gt;
&lt;br /&gt;
=== Algorithme utilisé (créé par Luc Vincent et Pierre Soille) ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme choisi est un algorithme par inondation.&lt;br /&gt;
&lt;br /&gt;
Les pixels de faible intensité vont donc être traités en priorité.&lt;br /&gt;
&lt;br /&gt;
La première étape est donc de trier les pixels de l&#039;image par ordre croissant d&#039;intensité.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme va traiter les pixels d&#039;intensité la plus basse, puis une fois ceux-ci traités, les pixels avec une intensité légèrement supérieure sont traités, et ainsi de suite jusqu&#039;à avoir traité toute l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Trois problèmes se posent alors :&lt;br /&gt;
* Comment inonder un étage, c&#039;est à dire de quelle manière traiter les pixels de même intensité, à partir de bassins connus ?&lt;br /&gt;
* Comment déterminer à quel bassin appartient un pixel ?&lt;br /&gt;
* Comment repérer les nouveaux bassins ?&lt;br /&gt;
&lt;br /&gt;
==== Inonder un étage (pixels d&#039;intensité identique) ====&lt;br /&gt;
&lt;br /&gt;
Chaque bassin doit progresser à la même vitesse sur un étage, c&#039;est à dire que la ligne de partage des eaux qui sépare deux bassins doit être à égale distance de chacun.&lt;br /&gt;
&lt;br /&gt;
Chaque pixel est à une &amp;quot;distance&amp;quot; d&#039;un bassin. Par exemple les pixels collés à un bassin connu sont à une distance de 1. Leurs voisins sont eux à une distance de 2 (à condition qu&#039;ils ne soient pas collés à un bassin connu).&lt;br /&gt;
&lt;br /&gt;
[[File:file_distance.png|frame|Distance à laquelle se trouve les pixels du bassin connu (bleu)]]&lt;br /&gt;
&lt;br /&gt;
Pour que les bassins s&#039;étendent correctement, on traite d&#039;abord tous les pixels à distance 1, puis ceux à distance 2 et ainsi de suite.&lt;br /&gt;
&lt;br /&gt;
Une file d&#039;attente est créée ; la première étape est de placer les pixels à une distance de 1 dans la file d&#039;attente.&lt;br /&gt;
On va pouvoir traiter ces pixels là. On sait également que leurs voisins sont à une distance de 2, et pourront donc être traités lorsque les pixels à distance 1 seront associés à un bassin.&lt;br /&gt;
&lt;br /&gt;
Les voisins non-traités des pixels à une distance de 1 sont donc les prochains pixels de l&#039;étage à traiter.&lt;br /&gt;
Ils sont placés dans une autre file d&#039;attente, qui sera parcourue une fois que la première a été entièrement utilisée.&lt;br /&gt;
&lt;br /&gt;
Ensuite, les voisins des pixels à une distance de 2 seront les prochains pixels à traiter, et on va recommencer cette suite d&#039;opérations.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les pixels sont placés dans la file d&#039;attente :&lt;br /&gt;
&lt;br /&gt;
* Tout d&#039;abord les pixels directement voisins d&#039;un bassin connu sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_1.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Puis une fois que les pixels dans la file d&#039;attente ont été traités, les pixels voisins des pixels qui viennent d&#039;être traités sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_2.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Cette étape se répète tant qu&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_3.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi le traitement d&#039;une file d&#039;attente (file d&#039;attente d&#039;une distance particulière) fait avancer d&#039;un pixel tous les bassins, et tous les bassins progressent à la même vitesse.&lt;br /&gt;
&lt;br /&gt;
==== Détermination du bassin auquel appartient un pixel ====&lt;br /&gt;
&lt;br /&gt;
Avec cette manière de faire progresser les bassins, le bassin auquel appartient un pixel est déterminé par les bassins auxquels appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
Plusieurs cas sont alors possibles lorsqu&#039;un pixel est traité.&lt;br /&gt;
&lt;br /&gt;
[[File:choix_label.png]] &lt;br /&gt;
&lt;br /&gt;
*Un des cas les plus simples est lorsque tous les voisins traités du pixel appartiennent au même bassin.Le bassin s&#039;étend alors, et le pixel à traiter est associé au bassin auquel appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
*Cette même décision est prise si tous les pixels voisins traités appartiennent au même bassin sauf certains qui appartiennent à une ligne de partage des eaux (LPE). L&#039;autre décision possible est de prolonger la LPE, cependant des lignes épaisses (et imprécises) seraient créées.&lt;br /&gt;
&lt;br /&gt;
*Si les pixels voisins traités du pixel appartiennent à une LPE, le bassin auquel appartient le pixel est inconnu. On ne peut pas &amp;quot;prolonger&amp;quot; la ligne car on ne connaît alors pas à quelle distance d&#039;un bassin se trouve le pixel. Si un pixel appartient à une LPE, ses voisins ne sont donc pas placés en file d&#039;attente.&lt;br /&gt;
&lt;br /&gt;
*Lorsque les pixels voisins appartiennent à des bassins différents, alors le pixel appartient à une LPE ; on sépare les bassins au moment de leur rencontre.&lt;br /&gt;
&lt;br /&gt;
*Le dernier cas se présente lorsque tous les pixels voisins sont traités et qu&#039;ils appartiennent tous à une LPE. Pour éviter qu&#039;un bassin d&#039;un seul pixel soit créé, on prolonge la LPE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En résumé :&lt;br /&gt;
*S&#039;il n&#039;y a qu&#039;un seul bassin autour du pixel en excluant les LPE, le pixel appartient à ce bassin.&lt;br /&gt;
*Si en excluant les LPE il y a plusieurs bassins autour du pixel, le pixel appartient à une LPE.&lt;br /&gt;
*Si le pixel est entouré par des LPE, alors il appartient à une LPE.&lt;br /&gt;
&lt;br /&gt;
==== Création de nouveaux bassins ====&lt;br /&gt;
&lt;br /&gt;
Lors de la propagation des bassins, si une file d&#039;attente est vide alors tous les pixels de l&#039;étage qui appartenaient à un bassin déjà connu ont été traités.&lt;br /&gt;
&lt;br /&gt;
Les pixels restants appartiennent donc à de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Un nouveau bassin est donc créé, le premier pixel qui lui appartient est choisi parmi un des pixels de l&#039;étage non-traités.&lt;br /&gt;
&lt;br /&gt;
On réinitialise alors la file d&#039;attente, et on place les voisins non-traités du pixel associé au nouveau bassin dedans.&lt;br /&gt;
&lt;br /&gt;
On recommence alors le traitement de la file d&#039;attente, et tous les pixels qui appartiennent à ce bassin lui seront attribués.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les nouveaux bassins sont créés, sur une petite image de 5x5.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_image.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Plusieurs étapes se déroulent alors :&lt;br /&gt;
&lt;br /&gt;
* Le traitement d&#039;un étage commence. Les pixels appartenant à un bassin connu se situent à un étage inférieur.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_1.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
* Tous les pixels appartenant à un bassin connu ont été traités ; il reste cependant des pixels non-traités à cet étage. Ils appartiennent donc à un ou plusieurs nouveaux bassins. On choisit donc un pixel non-traité de l&#039;étage, et on lui attribue un nouveau bassin.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_2.png|300px]] [[File:minima_legende.png|500px]]  &lt;br /&gt;
&lt;br /&gt;
* On propage le nouveau bassin créé.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_3.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
Ces étapes sont répétées tant qu&#039;il reste des pixels non-traités à l&#039;étage en cours de traitement. On détecte ainsi tous les nouveaux bassins de l&#039;étage.&lt;br /&gt;
&lt;br /&gt;
Une fois que tous les pixels d&#039;un étage ont été traités, on passe à l&#039;étage supérieur.&lt;br /&gt;
&lt;br /&gt;
==== Résultats ====&lt;br /&gt;
Voici un exemple de résultat que donne cet algorithme :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_brute.png]]&#039;&#039;A gauche le gradient d&#039;une image ; à droite le résultat de l&#039;algorithme de détermination des LPE&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On s&#039;aperçoit que l&#039;algorithme est très sensible au bruit, c&#039;est à dire aux légères variations d&#039;intensité qui créent des minima locaux.&lt;br /&gt;
&lt;br /&gt;
On veut donc améliorer les résultats obtenus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Réduire l&#039;impact du bruit ===&lt;br /&gt;
Plusieurs méthodes sont applicables pour réduire l&#039;impact du bruit sur le résultat.&lt;br /&gt;
&lt;br /&gt;
==== Ajout d&#039;une tolérance ====&lt;br /&gt;
La plus efficace est d&#039;ajouter une &amp;quot;tolérance&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
En effet, dans l&#039;algorithme décrit précédemment, les pixels appartiennent à un même étage si leurs intensités sont exactement identiques.&lt;br /&gt;
&lt;br /&gt;
On considére qu&#039;un pixel appartient à l&#039;étage traité si la différence entre son intensité et l&#039;intensité de l&#039;étage traitée est inférieure à un nombre, qu&#039;on appelle tolérance.&lt;br /&gt;
&lt;br /&gt;
Ainsi la sensibilité au bruit de l&#039;algorithme est réduite car les petites variations d&#039;intensité ne créent plus de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple sur une petite image. &lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance_image.png]]&lt;br /&gt;
&lt;br /&gt;
Tout d&#039;abord, s&#039;il n&#039;y a pas de tolérance, les pixels d&#039;intensité 130 et le pixel d&#039;intensité 135 sont à des étages différents.&lt;br /&gt;
Deux bassins sont donc créés lors du traitement de l&#039;étage d&#039;intensité 130 (bleu et vert, le noir représente une LPE).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_sans_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Cependant, avec l&#039;ajout d&#039;une tolérance (par exemple 10), les trois pixels sont considérés au même étage (car on a 135-130 &amp;lt; 10).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Ainsi une unique bassin est créé.&lt;br /&gt;
&lt;br /&gt;
Le résultat est alors bien plus précis sur de vraies images:&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_res_tolerance.png]] &#039;&#039;A gauche le résultat (de cette [[#Résultats|image]]) de l&#039;algorithme sans tolérance, à droite le résultat avec une tolérance de 5&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Flouter l&#039;image avant le traitement ====&lt;br /&gt;
&lt;br /&gt;
Une deuxième méthode est de traiter l&#039;image avant d&#039;appliquer l&#039;algorithme.&lt;br /&gt;
Si l&#039;image est floutée avant application de l&#039;algorithme, le bruit aura moins d&#039;impact, car les changements de valeur seront atténués.&lt;br /&gt;
Dans l&#039;image floutée, chaque valeur d&#039;un pixel est une moyenne des valeurs des pixels voisins à ce pixel dans l&#039;image originale.&lt;br /&gt;
&lt;br /&gt;
Pour cela, on peut convoluer l&#039;image par cette matrice :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Chaque produit de convolution donnera un entier qui devra être divisé par 9 ; on ajoute  les valeurs des 9 pixels dans un carré de 3x3 autour du pixel dont on veut la nouvelle valeur, pour avoir la moyenne des valeurs dans ce carré on doit alors diviser par 9.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas de bons résultats si elle est la seule utilisée. Cependant, combinée avec l&#039;utilisation de la tolérance, le nombre de petites zones est fortement réduit.&lt;br /&gt;
&lt;br /&gt;
Les LPE sont en contrepartie moins précises.&lt;br /&gt;
&lt;br /&gt;
Exemple de résultat :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou_tolerance5.png]]&lt;br /&gt;
&lt;br /&gt;
==== Fusion des petites zones ====&lt;br /&gt;
&lt;br /&gt;
Une troisième solution est d&#039;empêcher la création de zones trop petites, en indiquant une taille minimale.&lt;br /&gt;
Si des bassins plus petits que cette taille rencontrent un autre bassin, alors ils fusionnent, et le point de leur rencontre est associé à ce nouveau bassin issu de la fusion.&lt;br /&gt;
&lt;br /&gt;
Si au point de rencontre des bassins se rencontrent deux zones plus grandes que la taille minimale, on doit tout de même placer une LPE, pour séparer les deux &amp;quot;grands&amp;quot; bassins.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas non plus de résultats intéressants si utilisée seule, il est nécessaire de la combiner avec l&#039;ajout d&#039;une tolérance.&lt;br /&gt;
&lt;br /&gt;
Un des résultats obtenus avec cette méthode est (où la taille minimale d&#039;un bassin est de 5px) :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_fusion5.png]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
Pour conclure, on peut citer deux avantages de cet algorithme. Tout d&#039;abord, les lignes de partage des eaux créées sont peu épaisses ; elles ne font jamais plus de 3 pixels de large, et se limitent dans la plupart des cas à une épaisseur d&#039;un seul pixel.&lt;br /&gt;
&lt;br /&gt;
Ensuite, l&#039;algorithme est de complexité linéaire, c&#039;est à dire que le temps d’exécution dépend linéairement du nombre de pixels de l&#039;image sur laquelle on applique l&#039;algorithme.&lt;br /&gt;
Voici quelques mesures du temps d&#039;exécution selon le nombre de pixels de l&#039;image en entrée (les images utilisées pour ces tests ne représentent rien et contiennent de nombreuses zones).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tableau_temps.png|border|mesures du temps d&#039;exécution]]&lt;br /&gt;
[[File:lpe_temps.png|border|temps d&#039;exécution en fonction du nombre de pixels]]&lt;br /&gt;
&lt;br /&gt;
== Code source ==&lt;br /&gt;
Voici le code source de l&#039;algorithme.&lt;br /&gt;
&lt;br /&gt;
Pour manipuler des images, j&#039;ai utilisé la bibliothèque PILLOW.&lt;br /&gt;
&lt;br /&gt;
Les principales fonctions utilisées dans cette bibliothèque sont :&lt;br /&gt;
*Image.open(fichier) : ouvrir le fichier fichier en tant qu&#039;objet image&lt;br /&gt;
*image.getpixel((x,y)) : récupérer la valeur du pixel aux coordonnées (x,y) dans un objet Image (appelé ici &#039;image&#039;)&lt;br /&gt;
*image.setpixel((x,y),(r,v,b)) : Changer la valeur du pixel aux coordonnées (x,y) dans un objet Image (appelé ici &#039;image&#039;) ; (r,v,b) sont les valeurs rouge,vert,bleu.&lt;br /&gt;
*image.save(nom) : sauvegarder l&#039;image sous le nom souhaité.&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_tableau_temps.png&amp;diff=10329</id>
		<title>Fichier:Lpe tableau temps.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_tableau_temps.png&amp;diff=10329"/>
		<updated>2018-05-26T18:48:51Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10326</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10326"/>
		<updated>2018-05-26T17:56:00Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Deuxième étape : détermination de la &amp;quot;ligne de partage des eaux&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
Pour la deuxième étape de l&#039;algorithme, on utilise l&#039;image gradient. Elle est alors considérée comme un relief où les pixels de faible intensité sont à faible altitude, et où les pixels d&#039;intensité élevée sont à forte altitude.&lt;br /&gt;
&lt;br /&gt;
=== Définitions ===&lt;br /&gt;
&lt;br /&gt;
En topologie la ligne de partage des eaux peut-être définie de plusieurs manières ; elle est la séparation entre deux bassins versants dans un relief.&lt;br /&gt;
&lt;br /&gt;
Notre image est considérée comme un relief, et la ligne de partage des eaux sera la séparation entre les zones de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Un bassin versant est un bassin dont toutes les eaux ont le même exutoire, par exemple une rivière ou l&#039;océan.&lt;br /&gt;
&lt;br /&gt;
Pour déterminer la ligne de partage des eaux, plusieurs méthodes sont possibles :&lt;br /&gt;
&lt;br /&gt;
* On place une source d&#039;eau dans chaque minimum local (donc dans chaque bassin) et on inonde, quand deux bassins se rencontrent on place une ligne de partage des eaux.&lt;br /&gt;
Les algorithmes utilisant ce principe sont appelés des algorithmes par inondation.&lt;br /&gt;
&lt;br /&gt;
* Une deuxième méthode est de considérer que des deux côtés de la ligne l&#039;écoulement de l&#039;eau se termine dans des bassins versants différents.&lt;br /&gt;
Dans l&#039;algorithme associé, on va faire &amp;quot;s&#039;écouler&amp;quot; de l&#039;eau sur chaque pixel et tracer une ligne de partage entre deux pixels (ou sur l&#039;un des deux pixels) quand les eaux qui s&#039;écoulent depuis ces pixels arrive dans des minima locaux différents.&lt;br /&gt;
Ce sont les algorithmes par ruissellement.&lt;br /&gt;
&lt;br /&gt;
=== Algorithme utilisé (créé par Luc Vincent et Pierre Soille) ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme choisi est un algorithme par inondation.&lt;br /&gt;
&lt;br /&gt;
Les pixels de faible intensité vont donc être traités en priorité.&lt;br /&gt;
&lt;br /&gt;
La première étape est donc de trier les pixels de l&#039;image par ordre croissant d&#039;intensité.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme va traiter les pixels d&#039;intensité la plus basse, puis une fois ceux-ci traités, les pixels avec une intensité légèrement supérieure sont traités, et ainsi de suite jusqu&#039;à avoir traité toute l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Trois problèmes se posent alors :&lt;br /&gt;
* Comment inonder un étage, c&#039;est à dire de quelle manière traiter les pixels de même intensité, à partir de bassins connus ?&lt;br /&gt;
* Comment déterminer à quel bassin appartient un pixel ?&lt;br /&gt;
* Comment repérer les nouveaux bassins ?&lt;br /&gt;
&lt;br /&gt;
==== Inonder un étage (pixels d&#039;intensité identique) ====&lt;br /&gt;
&lt;br /&gt;
Chaque bassin doit progresser à la même vitesse sur un étage, c&#039;est à dire que la ligne de partage des eaux qui sépare deux bassins doit être à égale distance de chacun.&lt;br /&gt;
&lt;br /&gt;
Chaque pixel est à une &amp;quot;distance&amp;quot; d&#039;un bassin. Par exemple les pixels collés à un bassin connu sont à une distance de 1. Leurs voisins sont eux à une distance de 2 (à condition qu&#039;ils ne soient pas collés à un bassin connu).&lt;br /&gt;
&lt;br /&gt;
[[File:file_distance.png|frame|Distance à laquelle se trouve les pixels du bassin connu (bleu)]]&lt;br /&gt;
&lt;br /&gt;
Pour que les bassins s&#039;étendent correctement, on traite d&#039;abord tous les pixels à distance 1, puis ceux à distance 2 et ainsi de suite.&lt;br /&gt;
&lt;br /&gt;
Une file d&#039;attente est créée ; la première étape est de placer les pixels à une distance de 1 dans la file d&#039;attente.&lt;br /&gt;
On va pouvoir traiter ces pixels là. On sait également que leurs voisins sont à une distance de 2, et pourront donc être traités lorsque les pixels à distance 1 seront associés à un bassin.&lt;br /&gt;
&lt;br /&gt;
Les voisins non-traités des pixels à une distance de 1 sont donc les prochains pixels de l&#039;étage à traiter.&lt;br /&gt;
Ils sont placés dans une autre file d&#039;attente, qui sera parcourue une fois que la première a été entièrement utilisée.&lt;br /&gt;
&lt;br /&gt;
Ensuite, les voisins des pixels à une distance de 2 seront les prochains pixels à traiter, et on va recommencer cette suite d&#039;opérations.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les pixels sont placés dans la file d&#039;attente :&lt;br /&gt;
&lt;br /&gt;
* Tout d&#039;abord les pixels directement voisins d&#039;un bassin connu sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_1.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Puis une fois que les pixels dans la file d&#039;attente ont été traités, les pixels voisins des pixels qui viennent d&#039;être traités sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_2.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Cette étape se répète tant qu&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_3.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi le traitement d&#039;une file d&#039;attente (file d&#039;attente d&#039;une distance particulière) fait avancer d&#039;un pixel tous les bassins, et tous les bassins progressent à la même vitesse.&lt;br /&gt;
&lt;br /&gt;
==== Détermination du bassin auquel appartient un pixel ====&lt;br /&gt;
&lt;br /&gt;
Avec cette manière de faire progresser les bassins, le bassin auquel appartient un pixel est déterminé par les bassins auxquels appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
Plusieurs cas sont alors possibles lorsqu&#039;un pixel est traité.&lt;br /&gt;
&lt;br /&gt;
[[File:choix_label.png]] &lt;br /&gt;
&lt;br /&gt;
*Un des cas les plus simples est lorsque tous les voisins traités du pixel appartiennent au même bassin.Le bassin s&#039;étend alors, et le pixel à traiter est associé au bassin auquel appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
*Cette même décision est prise si tous les pixels voisins traités appartiennent au même bassin sauf certains qui appartiennent à une ligne de partage des eaux (LPE). L&#039;autre décision possible est de prolonger la LPE, cependant des lignes épaisses (et imprécises) seraient créées.&lt;br /&gt;
&lt;br /&gt;
*Si les pixels voisins traités du pixel appartiennent à une LPE, le bassin auquel appartient le pixel est inconnu. On ne peut pas &amp;quot;prolonger&amp;quot; la ligne car on ne connaît alors pas à quelle distance d&#039;un bassin se trouve le pixel. Si un pixel appartient à une LPE, ses voisins ne sont donc pas placés en file d&#039;attente.&lt;br /&gt;
&lt;br /&gt;
*Lorsque les pixels voisins appartiennent à des bassins différents, alors le pixel appartient à une LPE ; on sépare les bassins au moment de leur rencontre.&lt;br /&gt;
&lt;br /&gt;
*Le dernier cas se présente lorsque tous les pixels voisins sont traités et qu&#039;ils appartiennent tous à une LPE. Pour éviter qu&#039;un bassin d&#039;un seul pixel soit créé, on prolonge la LPE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En résumé :&lt;br /&gt;
*S&#039;il n&#039;y a qu&#039;un seul bassin autour du pixel en excluant les LPE, le pixel appartient à ce bassin.&lt;br /&gt;
*Si en excluant les LPE il y a plusieurs bassins autour du pixel, le pixel appartient à une LPE.&lt;br /&gt;
*Si le pixel est entouré par des LPE, alors il appartient à une LPE.&lt;br /&gt;
&lt;br /&gt;
==== Création de nouveaux bassins ====&lt;br /&gt;
&lt;br /&gt;
Lors de la propagation des bassins, si une file d&#039;attente est vide alors tous les pixels de l&#039;étage qui appartenaient à un bassin déjà connu ont été traités.&lt;br /&gt;
&lt;br /&gt;
Les pixels restants appartiennent donc à de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Un nouveau bassin est donc créé, le premier pixel qui lui appartient est choisi parmi un des pixels de l&#039;étage non-traités.&lt;br /&gt;
&lt;br /&gt;
On réinitialise alors la file d&#039;attente, et on place les voisins non-traités du pixel associé au nouveau bassin dedans.&lt;br /&gt;
&lt;br /&gt;
On recommence alors le traitement de la file d&#039;attente, et tous les pixels qui appartiennent à ce bassin lui seront attribués.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les nouveaux bassins sont créés, sur une petite image de 5x5.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_image.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Plusieurs étapes se déroulent alors :&lt;br /&gt;
&lt;br /&gt;
* Le traitement d&#039;un étage commence. Les pixels appartenant à un bassin connu se situent à un étage inférieur.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_1.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
* Tous les pixels appartenant à un bassin connu ont été traités ; il reste cependant des pixels non-traités à cet étage. Ils appartiennent donc à un ou plusieurs nouveaux bassins. On choisit donc un pixel non-traité de l&#039;étage, et on lui attribue un nouveau bassin.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_2.png|300px]] [[File:minima_legende.png|500px]]  &lt;br /&gt;
&lt;br /&gt;
* On propage le nouveau bassin créé.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_3.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
Ces étapes sont répétées tant qu&#039;il reste des pixels non-traités à l&#039;étage en cours de traitement. On détecte ainsi tous les nouveaux bassins de l&#039;étage.&lt;br /&gt;
&lt;br /&gt;
Une fois que tous les pixels d&#039;un étage ont été traités, on passe à l&#039;étage supérieur.&lt;br /&gt;
&lt;br /&gt;
==== Résultats ====&lt;br /&gt;
Voici un exemple de résultat que donne cet algorithme :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_brute.png]]&#039;&#039;A gauche le gradient d&#039;une image ; à droite le résultat de l&#039;algorithme de détermination des LPE&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On s&#039;aperçoit que l&#039;algorithme est très sensible au bruit, c&#039;est à dire aux légères variations d&#039;intensité qui créent des minima locaux.&lt;br /&gt;
&lt;br /&gt;
On veut donc améliorer les résultats obtenus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Réduire l&#039;impact du bruit ===&lt;br /&gt;
Plusieurs méthodes sont applicables pour réduire l&#039;impact du bruit sur le résultat.&lt;br /&gt;
&lt;br /&gt;
==== Ajout d&#039;une tolérance ====&lt;br /&gt;
La plus efficace est d&#039;ajouter une &amp;quot;tolérance&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
En effet, dans l&#039;algorithme décrit précédemment, les pixels appartiennent à un même étage si leurs intensités sont exactement identiques.&lt;br /&gt;
&lt;br /&gt;
On considére qu&#039;un pixel appartient à l&#039;étage traité si la différence entre son intensité et l&#039;intensité de l&#039;étage traitée est inférieure à un nombre, qu&#039;on appelle tolérance.&lt;br /&gt;
&lt;br /&gt;
Ainsi la sensibilité au bruit de l&#039;algorithme est réduite car les petites variations d&#039;intensité ne créent plus de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple sur une petite image. &lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance_image.png]]&lt;br /&gt;
&lt;br /&gt;
Tout d&#039;abord, s&#039;il n&#039;y a pas de tolérance, les pixels d&#039;intensité 130 et le pixel d&#039;intensité 135 sont à des étages différents.&lt;br /&gt;
Deux bassins sont donc créés lors du traitement de l&#039;étage d&#039;intensité 130 (bleu et vert, le noir représente une LPE).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_sans_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Cependant, avec l&#039;ajout d&#039;une tolérance (par exemple 10), les trois pixels sont considérés au même étage (car on a 135-130 &amp;lt; 10).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Ainsi une unique bassin est créé.&lt;br /&gt;
&lt;br /&gt;
Le résultat est alors bien plus précis sur de vraies images:&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_res_tolerance.png]] &#039;&#039;A gauche le résultat (de cette [[#Résultats|image]]) de l&#039;algorithme sans tolérance, à droite le résultat avec une tolérance de 5&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Flouter l&#039;image avant le traitement ====&lt;br /&gt;
&lt;br /&gt;
Une deuxième méthode est de traiter l&#039;image avant d&#039;appliquer l&#039;algorithme.&lt;br /&gt;
Si l&#039;image est floutée avant application de l&#039;algorithme, le bruit aura moins d&#039;impact, car les changements de valeur seront atténués.&lt;br /&gt;
Dans l&#039;image floutée, chaque valeur d&#039;un pixel est une moyenne des valeurs des pixels voisins à ce pixel dans l&#039;image originale.&lt;br /&gt;
&lt;br /&gt;
Pour cela, on peut convoluer l&#039;image par cette matrice :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Chaque produit de convolution donnera un entier qui devra être divisé par 9 ; on ajoute  les valeurs des 9 pixels dans un carré de 3x3 autour du pixel dont on veut la nouvelle valeur, pour avoir la moyenne des valeurs dans ce carré on doit alors diviser par 9.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas de bons résultats si elle est la seule utilisée. Cependant, combinée avec l&#039;utilisation de la tolérance, le nombre de petites zones est fortement réduit.&lt;br /&gt;
&lt;br /&gt;
Les LPE sont en contrepartie moins précises.&lt;br /&gt;
&lt;br /&gt;
Exemple de résultat :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou_tolerance5.png]]&lt;br /&gt;
&lt;br /&gt;
==== Fusion des petites zones ====&lt;br /&gt;
&lt;br /&gt;
Une troisième solution est d&#039;empêcher la création de zones trop petites, en indiquant une taille minimale.&lt;br /&gt;
Si des bassins plus petits que cette taille rencontrent un autre bassin, alors ils fusionnent, et le point de leur rencontre est associé à ce nouveau bassin issu de la fusion.&lt;br /&gt;
&lt;br /&gt;
Si au point de rencontre des bassins se rencontrent deux zones plus grandes que la taille minimale, on doit tout de même placer une LPE, pour séparer les deux &amp;quot;grands&amp;quot; bassins.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas non plus de résultats intéressants si utilisée seule, il est nécessaire de la combiner avec l&#039;ajout d&#039;une tolérance.&lt;br /&gt;
&lt;br /&gt;
Un des résultats obtenus avec cette méthode est (où la taille minimale d&#039;un bassin est de 5px) :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_fusion5.png]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
Pour conclure, on peut citer deux avantages de cet algorithme. Tout d&#039;abord, les lignes de partage des eaux créées sont peu épaisses ; elles ne font jamais plus de 3 pixels de large, et se limitent dans la plupart des cas à une épaisseur d&#039;un seul pixel.&lt;br /&gt;
Ensuite, l&#039;algorithme est de complexité linéaire, c&#039;est à dire que le temps d’exécution dépend linéairement du nombre de pixels de l&#039;image sur laquelle on applique l&#039;algorithme.&lt;br /&gt;
Voici quelques mesures du temps d&#039;exécution selon le nombre de pixels de l&#039;image en entrée (les images utilisées pour ces tests ne représentent rien et contiennent de nombreuses zones).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_temps.png|frame|right|temps d&#039;exécution en fonction du nombre de pixels]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Nombre de pixels&lt;br /&gt;
! Temps d&#039;exécution&lt;br /&gt;
|-&lt;br /&gt;
| 10 000&lt;br /&gt;
| 0.81 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 50 000&lt;br /&gt;
| 2.89 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 100 000&lt;br /&gt;
| 5.99 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 200 000&lt;br /&gt;
| 11.67 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 400 000&lt;br /&gt;
| 23.29 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 700 000&lt;br /&gt;
| 42.37 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 1 000 000&lt;br /&gt;
| 60.89 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 2 000 000&lt;br /&gt;
| 120.4 s&lt;br /&gt;
|-}&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_temps.png&amp;diff=10325</id>
		<title>Fichier:Lpe temps.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_temps.png&amp;diff=10325"/>
		<updated>2018-05-26T17:52:40Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10324</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10324"/>
		<updated>2018-05-26T17:49:56Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Deuxième étape : détermination de la &amp;quot;ligne de partage des eaux&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
Pour la deuxième étape de l&#039;algorithme, on utilise l&#039;image gradient. Elle est alors considérée comme un relief où les pixels de faible intensité sont à faible altitude, et où les pixels d&#039;intensité élevée sont à forte altitude.&lt;br /&gt;
&lt;br /&gt;
=== Définitions ===&lt;br /&gt;
&lt;br /&gt;
En topologie la ligne de partage des eaux peut-être définie de plusieurs manières ; elle est la séparation entre deux bassins versants dans un relief.&lt;br /&gt;
&lt;br /&gt;
Notre image est considérée comme un relief, et la ligne de partage des eaux sera la séparation entre les zones de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Un bassin versant est un bassin dont toutes les eaux ont le même exutoire, par exemple une rivière ou l&#039;océan.&lt;br /&gt;
&lt;br /&gt;
Pour déterminer la ligne de partage des eaux, plusieurs méthodes sont possibles :&lt;br /&gt;
&lt;br /&gt;
* On place une source d&#039;eau dans chaque minimum local (donc dans chaque bassin) et on inonde, quand deux bassins se rencontrent on place une ligne de partage des eaux.&lt;br /&gt;
Les algorithmes utilisant ce principe sont appelés des algorithmes par inondation.&lt;br /&gt;
&lt;br /&gt;
* Une deuxième méthode est de considérer que des deux côtés de la ligne l&#039;écoulement de l&#039;eau se termine dans des bassins versants différents.&lt;br /&gt;
Dans l&#039;algorithme associé, on va faire &amp;quot;s&#039;écouler&amp;quot; de l&#039;eau sur chaque pixel et tracer une ligne de partage entre deux pixels (ou sur l&#039;un des deux pixels) quand les eaux qui s&#039;écoulent depuis ces pixels arrive dans des minima locaux différents.&lt;br /&gt;
Ce sont les algorithmes par ruissellement.&lt;br /&gt;
&lt;br /&gt;
=== Algorithme utilisé (créé par Luc Vincent et Pierre Soille) ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme choisi est un algorithme par inondation.&lt;br /&gt;
&lt;br /&gt;
Les pixels de faible intensité vont donc être traités en priorité.&lt;br /&gt;
&lt;br /&gt;
La première étape est donc de trier les pixels de l&#039;image par ordre croissant d&#039;intensité.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme va traiter les pixels d&#039;intensité la plus basse, puis une fois ceux-ci traités, les pixels avec une intensité légèrement supérieure sont traités, et ainsi de suite jusqu&#039;à avoir traité toute l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Trois problèmes se posent alors :&lt;br /&gt;
* Comment inonder un étage, c&#039;est à dire de quelle manière traiter les pixels de même intensité, à partir de bassins connus ?&lt;br /&gt;
* Comment déterminer à quel bassin appartient un pixel ?&lt;br /&gt;
* Comment repérer les nouveaux bassins ?&lt;br /&gt;
&lt;br /&gt;
==== Inonder un étage (pixels d&#039;intensité identique) ====&lt;br /&gt;
&lt;br /&gt;
Chaque bassin doit progresser à la même vitesse sur un étage, c&#039;est à dire que la ligne de partage des eaux qui sépare deux bassins doit être à égale distance de chacun.&lt;br /&gt;
&lt;br /&gt;
Chaque pixel est à une &amp;quot;distance&amp;quot; d&#039;un bassin. Par exemple les pixels collés à un bassin connu sont à une distance de 1. Leurs voisins sont eux à une distance de 2 (à condition qu&#039;ils ne soient pas collés à un bassin connu).&lt;br /&gt;
&lt;br /&gt;
[[File:file_distance.png|frame|Distance à laquelle se trouve les pixels du bassin connu (bleu)]]&lt;br /&gt;
&lt;br /&gt;
Pour que les bassins s&#039;étendent correctement, on traite d&#039;abord tous les pixels à distance 1, puis ceux à distance 2 et ainsi de suite.&lt;br /&gt;
&lt;br /&gt;
Une file d&#039;attente est créée ; la première étape est de placer les pixels à une distance de 1 dans la file d&#039;attente.&lt;br /&gt;
On va pouvoir traiter ces pixels là. On sait également que leurs voisins sont à une distance de 2, et pourront donc être traités lorsque les pixels à distance 1 seront associés à un bassin.&lt;br /&gt;
&lt;br /&gt;
Les voisins non-traités des pixels à une distance de 1 sont donc les prochains pixels de l&#039;étage à traiter.&lt;br /&gt;
Ils sont placés dans une autre file d&#039;attente, qui sera parcourue une fois que la première a été entièrement utilisée.&lt;br /&gt;
&lt;br /&gt;
Ensuite, les voisins des pixels à une distance de 2 seront les prochains pixels à traiter, et on va recommencer cette suite d&#039;opérations.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les pixels sont placés dans la file d&#039;attente :&lt;br /&gt;
&lt;br /&gt;
* Tout d&#039;abord les pixels directement voisins d&#039;un bassin connu sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_1.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Puis une fois que les pixels dans la file d&#039;attente ont été traités, les pixels voisins des pixels qui viennent d&#039;être traités sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_2.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Cette étape se répète tant qu&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_3.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi le traitement d&#039;une file d&#039;attente (file d&#039;attente d&#039;une distance particulière) fait avancer d&#039;un pixel tous les bassins, et tous les bassins progressent à la même vitesse.&lt;br /&gt;
&lt;br /&gt;
==== Détermination du bassin auquel appartient un pixel ====&lt;br /&gt;
&lt;br /&gt;
Avec cette manière de faire progresser les bassins, le bassin auquel appartient un pixel est déterminé par les bassins auxquels appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
Plusieurs cas sont alors possibles lorsqu&#039;un pixel est traité.&lt;br /&gt;
&lt;br /&gt;
[[File:choix_label.png]] &lt;br /&gt;
&lt;br /&gt;
*Un des cas les plus simples est lorsque tous les voisins traités du pixel appartiennent au même bassin.Le bassin s&#039;étend alors, et le pixel à traiter est associé au bassin auquel appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
*Cette même décision est prise si tous les pixels voisins traités appartiennent au même bassin sauf certains qui appartiennent à une ligne de partage des eaux (LPE). L&#039;autre décision possible est de prolonger la LPE, cependant des lignes épaisses (et imprécises) seraient créées.&lt;br /&gt;
&lt;br /&gt;
*Si les pixels voisins traités du pixel appartiennent à une LPE, le bassin auquel appartient le pixel est inconnu. On ne peut pas &amp;quot;prolonger&amp;quot; la ligne car on ne connaît alors pas à quelle distance d&#039;un bassin se trouve le pixel. Si un pixel appartient à une LPE, ses voisins ne sont donc pas placés en file d&#039;attente.&lt;br /&gt;
&lt;br /&gt;
*Lorsque les pixels voisins appartiennent à des bassins différents, alors le pixel appartient à une LPE ; on sépare les bassins au moment de leur rencontre.&lt;br /&gt;
&lt;br /&gt;
*Le dernier cas se présente lorsque tous les pixels voisins sont traités et qu&#039;ils appartiennent tous à une LPE. Pour éviter qu&#039;un bassin d&#039;un seul pixel soit créé, on prolonge la LPE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En résumé :&lt;br /&gt;
*S&#039;il n&#039;y a qu&#039;un seul bassin autour du pixel en excluant les LPE, le pixel appartient à ce bassin.&lt;br /&gt;
*Si en excluant les LPE il y a plusieurs bassins autour du pixel, le pixel appartient à une LPE.&lt;br /&gt;
*Si le pixel est entouré par des LPE, alors il appartient à une LPE.&lt;br /&gt;
&lt;br /&gt;
==== Création de nouveaux bassins ====&lt;br /&gt;
&lt;br /&gt;
Lors de la propagation des bassins, si une file d&#039;attente est vide alors tous les pixels de l&#039;étage qui appartenaient à un bassin déjà connu ont été traités.&lt;br /&gt;
&lt;br /&gt;
Les pixels restants appartiennent donc à de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Un nouveau bassin est donc créé, le premier pixel qui lui appartient est choisi parmi un des pixels de l&#039;étage non-traités.&lt;br /&gt;
&lt;br /&gt;
On réinitialise alors la file d&#039;attente, et on place les voisins non-traités du pixel associé au nouveau bassin dedans.&lt;br /&gt;
&lt;br /&gt;
On recommence alors le traitement de la file d&#039;attente, et tous les pixels qui appartiennent à ce bassin lui seront attribués.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les nouveaux bassins sont créés, sur une petite image de 5x5.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_image.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Plusieurs étapes se déroulent alors :&lt;br /&gt;
&lt;br /&gt;
* Le traitement d&#039;un étage commence. Les pixels appartenant à un bassin connu se situent à un étage inférieur.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_1.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
* Tous les pixels appartenant à un bassin connu ont été traités ; il reste cependant des pixels non-traités à cet étage. Ils appartiennent donc à un ou plusieurs nouveaux bassins. On choisit donc un pixel non-traité de l&#039;étage, et on lui attribue un nouveau bassin.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_2.png|300px]] [[File:minima_legende.png|500px]]  &lt;br /&gt;
&lt;br /&gt;
* On propage le nouveau bassin créé.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_3.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
Ces étapes sont répétées tant qu&#039;il reste des pixels non-traités à l&#039;étage en cours de traitement. On détecte ainsi tous les nouveaux bassins de l&#039;étage.&lt;br /&gt;
&lt;br /&gt;
Une fois que tous les pixels d&#039;un étage ont été traités, on passe à l&#039;étage supérieur.&lt;br /&gt;
&lt;br /&gt;
==== Résultats ====&lt;br /&gt;
Voici un exemple de résultat que donne cet algorithme :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_brute.png]]&#039;&#039;A gauche le gradient d&#039;une image ; à droite le résultat de l&#039;algorithme de détermination des LPE&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On s&#039;aperçoit que l&#039;algorithme est très sensible au bruit, c&#039;est à dire aux légères variations d&#039;intensité qui créent des minima locaux.&lt;br /&gt;
&lt;br /&gt;
On veut donc améliorer les résultats obtenus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Réduire l&#039;impact du bruit ===&lt;br /&gt;
Plusieurs méthodes sont applicables pour réduire l&#039;impact du bruit sur le résultat.&lt;br /&gt;
&lt;br /&gt;
==== Ajout d&#039;une tolérance ====&lt;br /&gt;
La plus efficace est d&#039;ajouter une &amp;quot;tolérance&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
En effet, dans l&#039;algorithme décrit précédemment, les pixels appartiennent à un même étage si leurs intensités sont exactement identiques.&lt;br /&gt;
&lt;br /&gt;
On considére qu&#039;un pixel appartient à l&#039;étage traité si la différence entre son intensité et l&#039;intensité de l&#039;étage traitée est inférieure à un nombre, qu&#039;on appelle tolérance.&lt;br /&gt;
&lt;br /&gt;
Ainsi la sensibilité au bruit de l&#039;algorithme est réduite car les petites variations d&#039;intensité ne créent plus de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple sur une petite image. &lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance_image.png]]&lt;br /&gt;
&lt;br /&gt;
Tout d&#039;abord, s&#039;il n&#039;y a pas de tolérance, les pixels d&#039;intensité 130 et le pixel d&#039;intensité 135 sont à des étages différents.&lt;br /&gt;
Deux bassins sont donc créés lors du traitement de l&#039;étage d&#039;intensité 130 (bleu et vert, le noir représente une LPE).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_sans_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Cependant, avec l&#039;ajout d&#039;une tolérance (par exemple 10), les trois pixels sont considérés au même étage (car on a 135-130 &amp;lt; 10).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Ainsi une unique bassin est créé.&lt;br /&gt;
&lt;br /&gt;
Le résultat est alors bien plus précis sur de vraies images:&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_res_tolerance.png]] &#039;&#039;A gauche le résultat (de cette [[#Résultats|image]]) de l&#039;algorithme sans tolérance, à droite le résultat avec une tolérance de 5&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Flouter l&#039;image avant le traitement ====&lt;br /&gt;
&lt;br /&gt;
Une deuxième méthode est de traiter l&#039;image avant d&#039;appliquer l&#039;algorithme.&lt;br /&gt;
Si l&#039;image est floutée avant application de l&#039;algorithme, le bruit aura moins d&#039;impact, car les changements de valeur seront atténués.&lt;br /&gt;
Dans l&#039;image floutée, chaque valeur d&#039;un pixel est une moyenne des valeurs des pixels voisins à ce pixel dans l&#039;image originale.&lt;br /&gt;
&lt;br /&gt;
Pour cela, on peut convoluer l&#039;image par cette matrice :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Chaque produit de convolution donnera un entier qui devra être divisé par 9 ; on ajoute  les valeurs des 9 pixels dans un carré de 3x3 autour du pixel dont on veut la nouvelle valeur, pour avoir la moyenne des valeurs dans ce carré on doit alors diviser par 9.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas de bons résultats si elle est la seule utilisée. Cependant, combinée avec l&#039;utilisation de la tolérance, le nombre de petites zones est fortement réduit.&lt;br /&gt;
&lt;br /&gt;
Les LPE sont en contrepartie moins précises.&lt;br /&gt;
&lt;br /&gt;
Exemple de résultat :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou_tolerance5.png]]&lt;br /&gt;
&lt;br /&gt;
==== Fusion des petites zones ====&lt;br /&gt;
&lt;br /&gt;
Une troisième solution est d&#039;empêcher la création de zones trop petites, en indiquant une taille minimale.&lt;br /&gt;
Si des bassins plus petits que cette taille rencontrent un autre bassin, alors ils fusionnent, et le point de leur rencontre est associé à ce nouveau bassin issu de la fusion.&lt;br /&gt;
&lt;br /&gt;
Si au point de rencontre des bassins se rencontrent deux zones plus grandes que la taille minimale, on doit tout de même placer une LPE, pour séparer les deux &amp;quot;grands&amp;quot; bassins.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas non plus de résultats intéressants si utilisée seule, il est nécessaire de la combiner avec l&#039;ajout d&#039;une tolérance.&lt;br /&gt;
&lt;br /&gt;
Un des résultats obtenus avec cette méthode est (où la taille minimale d&#039;un bassin est de 5px) :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_fusion5.png]]&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
Pour conclure, on peut citer deux avantages de cet algorithme. Tout d&#039;abord, les lignes de partage des eaux créées sont peu épaisses ; elles ne font jamais plus de 3 pixels de large, et se limitent dans la plupart des cas à une épaisseur d&#039;un seul pixel.&lt;br /&gt;
Ensuite, l&#039;algorithme est de complexité linéaire, c&#039;est à dire que le temps d’exécution dépend linéairement du nombre de pixels de l&#039;image sur laquelle on applique l&#039;algorithme.&lt;br /&gt;
Voici quelques mesures du temps d&#039;exécution selon le nombre de pixels de l&#039;image en entrée (les images utilisées pour ces tests ne représentent rien et contiennent de nombreuses zones).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot;| Nombre de pixels&lt;br /&gt;
! Temps d&#039;exécution&lt;br /&gt;
|-&lt;br /&gt;
| 10 000&lt;br /&gt;
| 0.81 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 50 000&lt;br /&gt;
| 2.89 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 100 000&lt;br /&gt;
| 5.99 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 200 000&lt;br /&gt;
| 11.67 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 400 000&lt;br /&gt;
| 23.29 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 700 000&lt;br /&gt;
| 42.37 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 1 000 000&lt;br /&gt;
| 60.89 s&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| 2 000 000&lt;br /&gt;
| 120.4 s&lt;br /&gt;
|-}&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10322</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10322"/>
		<updated>2018-05-26T17:30:19Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Deuxième étape : détermination de la &amp;quot;ligne de partage des eaux&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
Pour la deuxième étape de l&#039;algorithme, on utilise l&#039;image gradient. Elle est alors considérée comme un relief où les pixels de faible intensité sont à faible altitude, et où les pixels d&#039;intensité élevée sont à forte altitude.&lt;br /&gt;
&lt;br /&gt;
=== Définitions ===&lt;br /&gt;
&lt;br /&gt;
En topologie la ligne de partage des eaux peut-être définie de plusieurs manières ; elle est la séparation entre deux bassins versants dans un relief.&lt;br /&gt;
&lt;br /&gt;
Notre image est considérée comme un relief, et la ligne de partage des eaux sera la séparation entre les zones de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Un bassin versant est un bassin dont toutes les eaux ont le même exutoire, par exemple une rivière ou l&#039;océan.&lt;br /&gt;
&lt;br /&gt;
Pour déterminer la ligne de partage des eaux, plusieurs méthodes sont possibles :&lt;br /&gt;
&lt;br /&gt;
* On place une source d&#039;eau dans chaque minimum local (donc dans chaque bassin) et on inonde, quand deux bassins se rencontrent on place une ligne de partage des eaux.&lt;br /&gt;
Les algorithmes utilisant ce principe sont appelés des algorithmes par inondation.&lt;br /&gt;
&lt;br /&gt;
* Une deuxième méthode est de considérer que des deux côtés de la ligne l&#039;écoulement de l&#039;eau se termine dans des bassins versants différents.&lt;br /&gt;
Dans l&#039;algorithme associé, on va faire &amp;quot;s&#039;écouler&amp;quot; de l&#039;eau sur chaque pixel et tracer une ligne de partage entre deux pixels (ou sur l&#039;un des deux pixels) quand les eaux qui s&#039;écoulent depuis ces pixels arrive dans des minima locaux différents.&lt;br /&gt;
Ce sont les algorithmes par ruissellement.&lt;br /&gt;
&lt;br /&gt;
=== Algorithme utilisé (créé par Luc Vincent et Pierre Soille) ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme choisi est un algorithme par inondation.&lt;br /&gt;
&lt;br /&gt;
Les pixels de faible intensité vont donc être traités en priorité.&lt;br /&gt;
&lt;br /&gt;
La première étape est donc de trier les pixels de l&#039;image par ordre croissant d&#039;intensité.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme va traiter les pixels d&#039;intensité la plus basse, puis une fois ceux-ci traités, les pixels avec une intensité légèrement supérieure sont traités, et ainsi de suite jusqu&#039;à avoir traité toute l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Trois problèmes se posent alors :&lt;br /&gt;
* Comment inonder un étage, c&#039;est à dire de quelle manière traiter les pixels de même intensité, à partir de bassins connus ?&lt;br /&gt;
* Comment déterminer à quel bassin appartient un pixel ?&lt;br /&gt;
* Comment repérer les nouveaux bassins ?&lt;br /&gt;
&lt;br /&gt;
==== Inonder un étage (pixels d&#039;intensité identique) ====&lt;br /&gt;
&lt;br /&gt;
Chaque bassin doit progresser à la même vitesse sur un étage, c&#039;est à dire que la ligne de partage des eaux qui sépare deux bassins doit être à égale distance de chacun.&lt;br /&gt;
&lt;br /&gt;
Chaque pixel est à une &amp;quot;distance&amp;quot; d&#039;un bassin. Par exemple les pixels collés à un bassin connu sont à une distance de 1. Leurs voisins sont eux à une distance de 2 (à condition qu&#039;ils ne soient pas collés à un bassin connu).&lt;br /&gt;
&lt;br /&gt;
[[File:file_distance.png|frame|Distance à laquelle se trouve les pixels du bassin connu (bleu)]]&lt;br /&gt;
&lt;br /&gt;
Pour que les bassins s&#039;étendent correctement, on traite d&#039;abord tous les pixels à distance 1, puis ceux à distance 2 et ainsi de suite.&lt;br /&gt;
&lt;br /&gt;
Une file d&#039;attente est créée ; la première étape est de placer les pixels à une distance de 1 dans la file d&#039;attente.&lt;br /&gt;
On va pouvoir traiter ces pixels là. On sait également que leurs voisins sont à une distance de 2, et pourront donc être traités lorsque les pixels à distance 1 seront associés à un bassin.&lt;br /&gt;
&lt;br /&gt;
Les voisins non-traités des pixels à une distance de 1 sont donc les prochains pixels de l&#039;étage à traiter.&lt;br /&gt;
Ils sont placés dans une autre file d&#039;attente, qui sera parcourue une fois que la première a été entièrement utilisée.&lt;br /&gt;
&lt;br /&gt;
Ensuite, les voisins des pixels à une distance de 2 seront les prochains pixels à traiter, et on va recommencer cette suite d&#039;opérations.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les pixels sont placés dans la file d&#039;attente :&lt;br /&gt;
&lt;br /&gt;
* Tout d&#039;abord les pixels directement voisins d&#039;un bassin connu sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_1.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Puis une fois que les pixels dans la file d&#039;attente ont été traités, les pixels voisins des pixels qui viennent d&#039;être traités sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_2.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Cette étape se répète tant qu&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_3.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi le traitement d&#039;une file d&#039;attente (file d&#039;attente d&#039;une distance particulière) fait avancer d&#039;un pixel tous les bassins, et tous les bassins progressent à la même vitesse.&lt;br /&gt;
&lt;br /&gt;
==== Détermination du bassin auquel appartient un pixel ====&lt;br /&gt;
&lt;br /&gt;
Avec cette manière de faire progresser les bassins, le bassin auquel appartient un pixel est déterminé par les bassins auxquels appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
Plusieurs cas sont alors possibles lorsqu&#039;un pixel est traité.&lt;br /&gt;
&lt;br /&gt;
[[File:choix_label.png]] &lt;br /&gt;
&lt;br /&gt;
*Un des cas les plus simples est lorsque tous les voisins traités du pixel appartiennent au même bassin.Le bassin s&#039;étend alors, et le pixel à traiter est associé au bassin auquel appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
*Cette même décision est prise si tous les pixels voisins traités appartiennent au même bassin sauf certains qui appartiennent à une ligne de partage des eaux (LPE). L&#039;autre décision possible est de prolonger la LPE, cependant des lignes épaisses (et imprécises) seraient créées.&lt;br /&gt;
&lt;br /&gt;
*Si les pixels voisins traités du pixel appartiennent à une LPE, le bassin auquel appartient le pixel est inconnu. On ne peut pas &amp;quot;prolonger&amp;quot; la ligne car on ne connaît alors pas à quelle distance d&#039;un bassin se trouve le pixel. Si un pixel appartient à une LPE, ses voisins ne sont donc pas placés en file d&#039;attente.&lt;br /&gt;
&lt;br /&gt;
*Lorsque les pixels voisins appartiennent à des bassins différents, alors le pixel appartient à une LPE ; on sépare les bassins au moment de leur rencontre.&lt;br /&gt;
&lt;br /&gt;
*Le dernier cas se présente lorsque tous les pixels voisins sont traités et qu&#039;ils appartiennent tous à une LPE. Pour éviter qu&#039;un bassin d&#039;un seul pixel soit créé, on prolonge la LPE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En résumé :&lt;br /&gt;
*S&#039;il n&#039;y a qu&#039;un seul bassin autour du pixel en excluant les LPE, le pixel appartient à ce bassin.&lt;br /&gt;
*Si en excluant les LPE il y a plusieurs bassins autour du pixel, le pixel appartient à une LPE.&lt;br /&gt;
*Si le pixel est entouré par des LPE, alors il appartient à une LPE.&lt;br /&gt;
&lt;br /&gt;
==== Création de nouveaux bassins ====&lt;br /&gt;
&lt;br /&gt;
Lors de la propagation des bassins, si une file d&#039;attente est vide alors tous les pixels de l&#039;étage qui appartenaient à un bassin déjà connu ont été traités.&lt;br /&gt;
&lt;br /&gt;
Les pixels restants appartiennent donc à de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Un nouveau bassin est donc créé, le premier pixel qui lui appartient est choisi parmi un des pixels de l&#039;étage non-traités.&lt;br /&gt;
&lt;br /&gt;
On réinitialise alors la file d&#039;attente, et on place les voisins non-traités du pixel associé au nouveau bassin dedans.&lt;br /&gt;
&lt;br /&gt;
On recommence alors le traitement de la file d&#039;attente, et tous les pixels qui appartiennent à ce bassin lui seront attribués.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les nouveaux bassins sont créés, sur une petite image de 5x5.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_image.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Plusieurs étapes se déroulent alors :&lt;br /&gt;
&lt;br /&gt;
* Le traitement d&#039;un étage commence. Les pixels appartenant à un bassin connu se situent à un étage inférieur.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_1.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
* Tous les pixels appartenant à un bassin connu ont été traités ; il reste cependant des pixels non-traités à cet étage. Ils appartiennent donc à un ou plusieurs nouveaux bassins. On choisit donc un pixel non-traité de l&#039;étage, et on lui attribue un nouveau bassin.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_2.png|300px]] [[File:minima_legende.png|500px]]  &lt;br /&gt;
&lt;br /&gt;
* On propage le nouveau bassin créé.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_3.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
Ces étapes sont répétées tant qu&#039;il reste des pixels non-traités à l&#039;étage en cours de traitement. On détecte ainsi tous les nouveaux bassins de l&#039;étage.&lt;br /&gt;
&lt;br /&gt;
Une fois que tous les pixels d&#039;un étage ont été traités, on passe à l&#039;étage supérieur.&lt;br /&gt;
&lt;br /&gt;
==== Résultats ====&lt;br /&gt;
Voici un exemple de résultat que donne cet algorithme :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_brute.png]]&#039;&#039;A gauche le gradient d&#039;une image ; à droite le résultat de l&#039;algorithme de détermination des LPE&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On s&#039;aperçoit que l&#039;algorithme est très sensible au bruit, c&#039;est à dire aux légères variations d&#039;intensité qui créent des minima locaux.&lt;br /&gt;
&lt;br /&gt;
On veut donc améliorer les résultats obtenus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Réduire l&#039;impact du bruit ===&lt;br /&gt;
Plusieurs méthodes sont applicables pour réduire l&#039;impact du bruit sur le résultat.&lt;br /&gt;
&lt;br /&gt;
==== Ajout d&#039;une tolérance ====&lt;br /&gt;
La plus efficace est d&#039;ajouter une &amp;quot;tolérance&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
En effet, dans l&#039;algorithme décrit précédemment, les pixels appartiennent à un même étage si leurs intensités sont exactement identiques.&lt;br /&gt;
&lt;br /&gt;
On considére qu&#039;un pixel appartient à l&#039;étage traité si la différence entre son intensité et l&#039;intensité de l&#039;étage traitée est inférieure à un nombre, qu&#039;on appelle tolérance.&lt;br /&gt;
&lt;br /&gt;
Ainsi la sensibilité au bruit de l&#039;algorithme est réduite car les petites variations d&#039;intensité ne créent plus de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple sur une petite image. &lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance_image.png]]&lt;br /&gt;
&lt;br /&gt;
Tout d&#039;abord, s&#039;il n&#039;y a pas de tolérance, les pixels d&#039;intensité 130 et le pixel d&#039;intensité 135 sont à des étages différents.&lt;br /&gt;
Deux bassins sont donc créés lors du traitement de l&#039;étage d&#039;intensité 130 (bleu et vert, le noir représente une LPE).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_sans_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Cependant, avec l&#039;ajout d&#039;une tolérance (par exemple 10), les trois pixels sont considérés au même étage (car on a 135-130 &amp;lt; 10).&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_tolerance.png]]&lt;br /&gt;
&lt;br /&gt;
Ainsi une unique bassin est créé.&lt;br /&gt;
&lt;br /&gt;
Le résultat est alors bien plus précis sur de vraies images:&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_res_tolerance.png]] &#039;&#039;A gauche le résultat (de cette [[#Résultats|image]]) de l&#039;algorithme sans tolérance, à droite le résultat avec une tolérance de 5&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Flouter l&#039;image avant le traitement ====&lt;br /&gt;
&lt;br /&gt;
Une deuxième méthode est de traiter l&#039;image avant d&#039;appliquer l&#039;algorithme.&lt;br /&gt;
Si l&#039;image est floutée avant application de l&#039;algorithme, le bruit aura moins d&#039;impact, car les changements de valeur seront atténués.&lt;br /&gt;
Dans l&#039;image floutée, chaque valeur d&#039;un pixel est une moyenne des valeurs des pixels voisins à ce pixel dans l&#039;image originale.&lt;br /&gt;
&lt;br /&gt;
Pour cela, on peut convoluer l&#039;image par cette matrice :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Chaque produit de convolution donnera un entier qui devra être divisé par 9 ; on ajoute  les valeurs des 9 pixels dans un carré de 3x3 autour du pixel dont on veut la nouvelle valeur, pour avoir la moyenne des valeurs dans ce carré on doit alors diviser par 9.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas de bons résultats si elle est la seule utilisée. Cependant, combinée avec l&#039;utilisation de la tolérance, le nombre de petites zones est fortement réduit.&lt;br /&gt;
&lt;br /&gt;
Les LPE sont en contrepartie moins précises.&lt;br /&gt;
&lt;br /&gt;
Exemple de résultat :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_flou_tolerance5.png]]&lt;br /&gt;
&lt;br /&gt;
==== Fusion des petites zones ====&lt;br /&gt;
&lt;br /&gt;
Une troisième solution est d&#039;empêcher la création de zones trop petites, en indiquant une taille minimale.&lt;br /&gt;
Si des bassins plus petits que cette taille rencontrent un autre bassin, alors ils fusionnent, et le point de leur rencontre est associé à ce nouveau bassin issu de la fusion.&lt;br /&gt;
&lt;br /&gt;
Si au point de rencontre des bassins se rencontrent deux zones plus grandes que la taille minimale, on doit tout de même placer une LPE, pour séparer les deux &amp;quot;grands&amp;quot; bassins.&lt;br /&gt;
&lt;br /&gt;
Cette méthode ne donne pas non plus de résultats intéressants si utilisée seule, il est nécessaire de la combiner avec l&#039;ajout d&#039;une tolérance.&lt;br /&gt;
&lt;br /&gt;
Un des résultats obtenus avec cette méthode est (où la taille minimale d&#039;un bassin est de 5px) :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_fusion5.png]]&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_fusion5.png&amp;diff=10321</id>
		<title>Fichier:Lpe fusion5.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_fusion5.png&amp;diff=10321"/>
		<updated>2018-05-26T17:29:50Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_flou_tolerance5.png&amp;diff=10320</id>
		<title>Fichier:Lpe flou tolerance5.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_flou_tolerance5.png&amp;diff=10320"/>
		<updated>2018-05-26T17:22:40Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_flou.png&amp;diff=10319</id>
		<title>Fichier:Lpe flou.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_flou.png&amp;diff=10319"/>
		<updated>2018-05-26T17:17:52Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_res_tolerance.png&amp;diff=10318</id>
		<title>Fichier:Lpe res tolerance.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_res_tolerance.png&amp;diff=10318"/>
		<updated>2018-05-26T17:05:50Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_tolerance.png&amp;diff=10317</id>
		<title>Fichier:Lpe tolerance.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_tolerance.png&amp;diff=10317"/>
		<updated>2018-05-26T17:05:34Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_sans_tolerance.png&amp;diff=10316</id>
		<title>Fichier:Lpe sans tolerance.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_sans_tolerance.png&amp;diff=10316"/>
		<updated>2018-05-26T17:05:15Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_tolerance_image.png&amp;diff=10315</id>
		<title>Fichier:Lpe tolerance image.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_tolerance_image.png&amp;diff=10315"/>
		<updated>2018-05-26T17:04:58Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10314</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10314"/>
		<updated>2018-05-26T16:43:35Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Deuxième étape : détermination de la &amp;quot;ligne de partage des eaux&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
Pour la deuxième étape de l&#039;algorithme, on utilise l&#039;image gradient. Elle est alors considérée comme un relief où les pixels de faible intensité sont à faible altitude, et où les pixels d&#039;intensité élevée sont à forte altitude.&lt;br /&gt;
&lt;br /&gt;
=== Définitions ===&lt;br /&gt;
&lt;br /&gt;
En topologie la ligne de partage des eaux peut-être définie de plusieurs manières ; elle est la séparation entre deux bassins versants dans un relief.&lt;br /&gt;
&lt;br /&gt;
Notre image est considérée comme un relief, et la ligne de partage des eaux sera la séparation entre les zones de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Un bassin versant est un bassin dont toutes les eaux ont le même exutoire, par exemple une rivière ou l&#039;océan.&lt;br /&gt;
&lt;br /&gt;
Pour déterminer la ligne de partage des eaux, plusieurs méthodes sont possibles :&lt;br /&gt;
&lt;br /&gt;
* On place une source d&#039;eau dans chaque minimum local (donc dans chaque bassin) et on inonde, quand deux bassins se rencontrent on place une ligne de partage des eaux.&lt;br /&gt;
Les algorithmes utilisant ce principe sont appelés des algorithmes par inondation.&lt;br /&gt;
&lt;br /&gt;
* Une deuxième méthode est de considérer que des deux côtés de la ligne l&#039;écoulement de l&#039;eau se termine dans des bassins versants différents.&lt;br /&gt;
Dans l&#039;algorithme associé, on va faire &amp;quot;s&#039;écouler&amp;quot; de l&#039;eau sur chaque pixel et tracer une ligne de partage entre deux pixels (ou sur l&#039;un des deux pixels) quand les eaux qui s&#039;écoulent depuis ces pixels arrive dans des minima locaux différents.&lt;br /&gt;
Ce sont les algorithmes par ruissellement.&lt;br /&gt;
&lt;br /&gt;
=== Algorithme utilisé (créé par Luc Vincent et Pierre Soille) ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme choisi est un algorithme par inondation.&lt;br /&gt;
&lt;br /&gt;
Les pixels de faible intensité vont donc être traités en priorité.&lt;br /&gt;
&lt;br /&gt;
La première étape est donc de trier les pixels de l&#039;image par ordre croissant d&#039;intensité.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme va traiter les pixels d&#039;intensité la plus basse, puis une fois ceux-ci traités, les pixels avec une intensité légèrement supérieure sont traités, et ainsi de suite jusqu&#039;à avoir traité toute l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Trois problèmes se posent alors :&lt;br /&gt;
* Comment inonder un étage, c&#039;est à dire de quelle manière traiter les pixels de même intensité, à partir de bassins connus ?&lt;br /&gt;
* Comment déterminer à quel bassin appartient un pixel ?&lt;br /&gt;
* Comment repérer les nouveaux bassins ?&lt;br /&gt;
&lt;br /&gt;
==== Inonder un étage (pixels d&#039;intensité identique) ====&lt;br /&gt;
&lt;br /&gt;
Chaque bassin doit progresser à la même vitesse sur un étage, c&#039;est à dire que la ligne de partage des eaux qui sépare deux bassins doit être à égale distance de chacun.&lt;br /&gt;
&lt;br /&gt;
Chaque pixel est à une &amp;quot;distance&amp;quot; d&#039;un bassin. Par exemple les pixels collés à un bassin connu sont à une distance de 1. Leurs voisins sont eux à une distance de 2 (à condition qu&#039;ils ne soient pas collés à un bassin connu).&lt;br /&gt;
&lt;br /&gt;
[[File:file_distance.png|frame|Distance à laquelle se trouve les pixels du bassin connu (bleu)]]&lt;br /&gt;
&lt;br /&gt;
Pour que les bassins s&#039;étendent correctement, on traite d&#039;abord tous les pixels à distance 1, puis ceux à distance 2 et ainsi de suite.&lt;br /&gt;
&lt;br /&gt;
Une file d&#039;attente est créée ; la première étape est de placer les pixels à une distance de 1 dans la file d&#039;attente.&lt;br /&gt;
On va pouvoir traiter ces pixels là. On sait également que leurs voisins sont à une distance de 2, et pourront donc être traités lorsque les pixels à distance 1 seront associés à un bassin.&lt;br /&gt;
&lt;br /&gt;
Les voisins non-traités des pixels à une distance de 1 sont donc les prochains pixels de l&#039;étage à traiter.&lt;br /&gt;
Ils sont placés dans une autre file d&#039;attente, qui sera parcourue une fois que la première a été entièrement utilisée.&lt;br /&gt;
&lt;br /&gt;
Ensuite, les voisins des pixels à une distance de 2 seront les prochains pixels à traiter, et on va recommencer cette suite d&#039;opérations.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les pixels sont placés dans la file d&#039;attente :&lt;br /&gt;
&lt;br /&gt;
* Tout d&#039;abord les pixels directement voisins d&#039;un bassin connu sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_1.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Puis une fois que les pixels dans la file d&#039;attente ont été traités, les pixels voisins des pixels qui viennent d&#039;être traités sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_2.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Cette étape se répète tant qu&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_3.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi le traitement d&#039;une file d&#039;attente (file d&#039;attente d&#039;une distance particulière) fait avancer d&#039;un pixel tous les bassins, et tous les bassins progressent à la même vitesse.&lt;br /&gt;
&lt;br /&gt;
==== Détermination du bassin auquel appartient un pixel ====&lt;br /&gt;
&lt;br /&gt;
Avec cette manière de faire progresser les bassins, le bassin auquel appartient un pixel est déterminé par les bassins auxquels appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
Plusieurs cas sont alors possibles lorsqu&#039;un pixel est traité.&lt;br /&gt;
&lt;br /&gt;
[[File:choix_label.png]] &lt;br /&gt;
&lt;br /&gt;
*Un des cas les plus simples est lorsque tous les voisins traités du pixel appartiennent au même bassin.Le bassin s&#039;étend alors, et le pixel à traiter est associé au bassin auquel appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
*Cette même décision est prise si tous les pixels voisins traités appartiennent au même bassin sauf certains qui appartiennent à une ligne de partage des eaux (LPE). L&#039;autre décision possible est de prolonger la LPE, cependant des lignes épaisses (et imprécises) seraient créées.&lt;br /&gt;
&lt;br /&gt;
*Si les pixels voisins traités du pixel appartiennent à une LPE, le bassin auquel appartient le pixel est inconnu. On ne peut pas &amp;quot;prolonger&amp;quot; la ligne car on ne connaît alors pas à quelle distance d&#039;un bassin se trouve le pixel. Si un pixel appartient à une LPE, ses voisins ne sont donc pas placés en file d&#039;attente.&lt;br /&gt;
&lt;br /&gt;
*Lorsque les pixels voisins appartiennent à des bassins différents, alors le pixel appartient à une LPE ; on sépare les bassins au moment de leur rencontre.&lt;br /&gt;
&lt;br /&gt;
*Le dernier cas se présente lorsque tous les pixels voisins sont traités et qu&#039;ils appartiennent tous à une LPE. Pour éviter qu&#039;un bassin d&#039;un seul pixel soit créé, on prolonge la LPE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En résumé :&lt;br /&gt;
*S&#039;il n&#039;y a qu&#039;un seul bassin autour du pixel en excluant les LPE, le pixel appartient à ce bassin.&lt;br /&gt;
*Si en excluant les LPE il y a plusieurs bassins autour du pixel, le pixel appartient à une LPE.&lt;br /&gt;
*Si le pixel est entouré par des LPE, alors il appartient à une LPE.&lt;br /&gt;
&lt;br /&gt;
==== Création de nouveaux bassins ====&lt;br /&gt;
&lt;br /&gt;
Lors de la propagation des bassins, si une file d&#039;attente est vide alors tous les pixels de l&#039;étage qui appartenaient à un bassin déjà connu ont été traités.&lt;br /&gt;
&lt;br /&gt;
Les pixels restants appartiennent donc à de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Un nouveau bassin est donc créé, le premier pixel qui lui appartient est choisi parmi un des pixels de l&#039;étage non-traités.&lt;br /&gt;
&lt;br /&gt;
On réinitialise alors la file d&#039;attente, et on place les voisins non-traités du pixel associé au nouveau bassin dedans.&lt;br /&gt;
&lt;br /&gt;
On recommence alors le traitement de la file d&#039;attente, et tous les pixels qui appartiennent à ce bassin lui seront attribués.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les nouveaux bassins sont créés, sur une petite image de 5x5.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_image.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Plusieurs étapes se déroulent alors :&lt;br /&gt;
&lt;br /&gt;
* Le traitement d&#039;un étage commence. Les pixels appartenant à un bassin connu se situent à un étage inférieur.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_1.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
* Tous les pixels appartenant à un bassin connu ont été traités ; il reste cependant des pixels non-traités à cet étage. Ils appartiennent donc à un ou plusieurs nouveaux bassins. On choisit donc un pixel non-traité de l&#039;étage, et on lui attribue un nouveau bassin.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_2.png|300px]] [[File:minima_legende.png|500px]]  &lt;br /&gt;
&lt;br /&gt;
* On propage le nouveau bassin créé.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_3.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
Ces étapes sont répétées tant qu&#039;il reste des pixels non-traités à l&#039;étage en cours de traitement. On détecte ainsi tous les nouveaux bassins de l&#039;étage.&lt;br /&gt;
&lt;br /&gt;
Une fois que tous les pixels d&#039;un étage ont été traités, on passe à l&#039;étage supérieur.&lt;br /&gt;
&lt;br /&gt;
==== Résultats ====&lt;br /&gt;
Voici un exemple de résultat que donne cet algorithme :&lt;br /&gt;
&lt;br /&gt;
[[File:lpe_brute.png]]&#039;&#039;A gauche le gradient d&#039;une image ; à droite le résultat de l&#039;algorithme de détermination des LPE&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On s&#039;aperçoit que l&#039;algorithme est très sensible au bruit, c&#039;est à dire aux légères variations d&#039;intensité qui créent des minima locaux.&lt;br /&gt;
&lt;br /&gt;
On veut donc améliorer les résultats obtenus.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Réduire l&#039;impact du bruit ===&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_brute.png&amp;diff=10313</id>
		<title>Fichier:Lpe brute.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Lpe_brute.png&amp;diff=10313"/>
		<updated>2018-05-26T16:39:54Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10312</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10312"/>
		<updated>2018-05-26T16:26:22Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Deuxième étape : détermination de la &amp;quot;ligne de partage des eaux&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
Pour la deuxième étape de l&#039;algorithme, on utilise l&#039;image gradient. Elle est alors considérée comme un relief où les pixels de faible intensité sont à faible altitude, et où les pixels d&#039;intensité élevée sont à forte altitude.&lt;br /&gt;
&lt;br /&gt;
=== Définitions ===&lt;br /&gt;
&lt;br /&gt;
En topologie la ligne de partage des eaux peut-être définie de plusieurs manières ; elle est la séparation entre deux bassins versants dans un relief.&lt;br /&gt;
&lt;br /&gt;
Notre image est considérée comme un relief, et la ligne de partage des eaux sera la séparation entre les zones de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Un bassin versant est un bassin dont toutes les eaux ont le même exutoire, par exemple une rivière ou l&#039;océan.&lt;br /&gt;
&lt;br /&gt;
Pour déterminer la ligne de partage des eaux, plusieurs méthodes sont possibles :&lt;br /&gt;
&lt;br /&gt;
* On place une source d&#039;eau dans chaque minimum local (donc dans chaque bassin) et on inonde, quand deux bassins se rencontrent on place une ligne de partage des eaux.&lt;br /&gt;
Les algorithmes utilisant ce principe sont appelés des algorithmes par inondation.&lt;br /&gt;
&lt;br /&gt;
* Une deuxième méthode est de considérer que des deux côtés de la ligne l&#039;écoulement de l&#039;eau se termine dans des bassins versants différents.&lt;br /&gt;
Dans l&#039;algorithme associé, on va faire &amp;quot;s&#039;écouler&amp;quot; de l&#039;eau sur chaque pixel et tracer une ligne de partage entre deux pixels (ou sur l&#039;un des deux pixels) quand les eaux qui s&#039;écoulent depuis ces pixels arrive dans des minima locaux différents.&lt;br /&gt;
Ce sont les algorithmes par ruissellement.&lt;br /&gt;
&lt;br /&gt;
=== Algorithme utilisé (créé par Luc Vincent et Pierre Soille) ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme choisi est un algorithme par inondation.&lt;br /&gt;
&lt;br /&gt;
Les pixels de faible intensité vont donc être traités en priorité.&lt;br /&gt;
&lt;br /&gt;
La première étape est donc de trier les pixels de l&#039;image par ordre croissant d&#039;intensité.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme va traiter les pixels d&#039;intensité la plus basse, puis une fois ceux-ci traités, les pixels avec une intensité légèrement supérieure sont traités, et ainsi de suite jusqu&#039;à avoir traité toute l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Trois problèmes se posent alors :&lt;br /&gt;
* Comment inonder un étage, c&#039;est à dire de quelle manière traiter les pixels de même intensité, à partir de bassins connus ?&lt;br /&gt;
* Comment déterminer à quel bassin appartient un pixel ?&lt;br /&gt;
* Comment repérer les nouveaux bassins ?&lt;br /&gt;
&lt;br /&gt;
==== Inonder un étage (pixels d&#039;intensité identique) ====&lt;br /&gt;
&lt;br /&gt;
Chaque bassin doit progresser à la même vitesse sur un étage, c&#039;est à dire que la ligne de partage des eaux qui sépare deux bassins doit être à égale distance de chacun.&lt;br /&gt;
&lt;br /&gt;
Chaque pixel est à une &amp;quot;distance&amp;quot; d&#039;un bassin. Par exemple les pixels collés à un bassin connu sont à une distance de 1. Leurs voisins sont eux à une distance de 2 (à condition qu&#039;ils ne soient pas collés à un bassin connu).&lt;br /&gt;
&lt;br /&gt;
[[File:file_distance.png|frame|Distance à laquelle se trouve les pixels du bassin connu (bleu)]]&lt;br /&gt;
&lt;br /&gt;
Pour que les bassins s&#039;étendent correctement, on traite d&#039;abord tous les pixels à distance 1, puis ceux à distance 2 et ainsi de suite.&lt;br /&gt;
&lt;br /&gt;
Une file d&#039;attente est créée ; la première étape est de placer les pixels à une distance de 1 dans la file d&#039;attente.&lt;br /&gt;
On va pouvoir traiter ces pixels là. On sait également que leurs voisins sont à une distance de 2, et pourront donc être traités lorsque les pixels à distance 1 seront associés à un bassin.&lt;br /&gt;
&lt;br /&gt;
Les voisins non-traités des pixels à une distance de 1 sont donc les prochains pixels de l&#039;étage à traiter.&lt;br /&gt;
Ils sont placés dans une autre file d&#039;attente, qui sera parcourue une fois que la première a été entièrement utilisée.&lt;br /&gt;
&lt;br /&gt;
Ensuite, les voisins des pixels à une distance de 2 seront les prochains pixels à traiter, et on va recommencer cette suite d&#039;opérations.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les pixels sont placés dans la file d&#039;attente :&lt;br /&gt;
&lt;br /&gt;
* Tout d&#039;abord les pixels directement voisins d&#039;un bassin connu sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_1.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Puis une fois que les pixels dans la file d&#039;attente ont été traités, les pixels voisins des pixels qui viennent d&#039;être traités sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_2.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Cette étape se répète tant qu&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_3.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi le traitement d&#039;une file d&#039;attente (file d&#039;attente d&#039;une distance particulière) fait avancer d&#039;un pixel tous les bassins, et tous les bassins progressent à la même vitesse.&lt;br /&gt;
&lt;br /&gt;
==== Détermination du bassin auquel appartient un pixel ====&lt;br /&gt;
&lt;br /&gt;
Avec cette manière de faire progresser les bassins, le bassin auquel appartient un pixel est déterminé par les bassins auxquels appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
Plusieurs cas sont alors possibles lorsqu&#039;un pixel est traité.&lt;br /&gt;
&lt;br /&gt;
[[File:choix_label.png]] &lt;br /&gt;
&lt;br /&gt;
*Un des cas les plus simples est lorsque tous les voisins traités du pixel appartiennent au même bassin.Le bassin s&#039;étend alors, et le pixel à traiter est associé au bassin auquel appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
*Cette même décision est prise si tous les pixels voisins traités appartiennent au même bassin sauf certains qui appartiennent à une ligne de partage des eaux (LPE). L&#039;autre décision possible est de prolonger la LPE, cependant des lignes épaisses (et imprécises) seraient créées.&lt;br /&gt;
&lt;br /&gt;
*Si les pixels voisins traités du pixel appartiennent à une LPE, le bassin auquel appartient le pixel est inconnu. On ne peut pas &amp;quot;prolonger&amp;quot; la ligne car on ne connaît alors pas à quelle distance d&#039;un bassin se trouve le pixel. Si un pixel appartient à une LPE, ses voisins ne sont donc pas placés en file d&#039;attente.&lt;br /&gt;
&lt;br /&gt;
*Lorsque les pixels voisins appartiennent à des bassins différents, alors le pixel appartient à une LPE ; on sépare les bassins au moment de leur rencontre.&lt;br /&gt;
&lt;br /&gt;
*Le dernier cas se présente lorsque tous les pixels voisins sont traités et qu&#039;ils appartiennent tous à une LPE. Pour éviter qu&#039;un bassin d&#039;un seul pixel soit créé, on prolonge la LPE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En résumé :&lt;br /&gt;
*S&#039;il n&#039;y a qu&#039;un seul bassin autour du pixel en excluant les LPE, le pixel appartient à ce bassin.&lt;br /&gt;
*Si en excluant les LPE il y a plusieurs bassins autour du pixel, le pixel appartient à une LPE.&lt;br /&gt;
*Si le pixel est entouré par des LPE, alors il appartient à une LPE.&lt;br /&gt;
&lt;br /&gt;
==== Création de nouveaux bassins ====&lt;br /&gt;
&lt;br /&gt;
Lors de la propagation des bassins, si une file d&#039;attente est vide alors tous les pixels de l&#039;étage qui appartenaient à un bassin déjà connu ont été traités.&lt;br /&gt;
&lt;br /&gt;
Les pixels restants appartiennent donc à de nouveaux bassins.&lt;br /&gt;
&lt;br /&gt;
Un nouveau bassin est donc créé, le premier pixel qui lui appartient est choisi parmi un des pixels de l&#039;étage non-traités.&lt;br /&gt;
&lt;br /&gt;
On réinitialise alors la file d&#039;attente, et on place les voisins non-traités du pixel associé au nouveau bassin dedans.&lt;br /&gt;
&lt;br /&gt;
On recommence alors le traitement de la file d&#039;attente, et tous les pixels qui appartiennent à ce bassin lui seront attribués.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les nouveaux bassins sont créés, sur une petite image de 5x5.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_image.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Plusieurs étapes se déroulent alors :&lt;br /&gt;
&lt;br /&gt;
* Le traitement d&#039;un étage commence. Les pixels appartenant à un bassin connu se situent à un étage inférieur.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_1.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
* Tous les pixels appartenant à un bassin connu ont été traités ; il reste cependant des pixels non-traités à cet étage. Ils appartiennent donc à un ou plusieurs nouveaux bassins. On choisit donc un pixel non-traité de l&#039;étage, et on lui attribue un nouveau bassin.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_2.png|300px]] [[File:minima_legende.png|500px]]  &lt;br /&gt;
&lt;br /&gt;
* On propage le nouveau bassin créé.&lt;br /&gt;
&lt;br /&gt;
[[File:minima_3.png|300px]] [[File:minima_legende.png|500px]] &lt;br /&gt;
&lt;br /&gt;
Ces étapes sont répétées tant qu&#039;il reste des pixels non-traités à l&#039;étage en cours de traitement. On détecte ainsi tous les nouveaux bassins de l&#039;étage.&lt;br /&gt;
&lt;br /&gt;
Une fois que tous les pixels d&#039;un étage ont été traités, on passe à l&#039;étage supérieur.&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_image.png&amp;diff=10311</id>
		<title>Fichier:Minima image.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_image.png&amp;diff=10311"/>
		<updated>2018-05-26T16:24:18Z</updated>

		<summary type="html">&lt;p&gt;Ruet : Ruet a téléversé une nouvelle version de Fichier:Minima image.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_image.png&amp;diff=10310</id>
		<title>Fichier:Minima image.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_image.png&amp;diff=10310"/>
		<updated>2018-05-26T16:24:07Z</updated>

		<summary type="html">&lt;p&gt;Ruet : Ruet a téléversé une nouvelle version de Fichier:Minima image.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_image.png&amp;diff=10309</id>
		<title>Fichier:Minima image.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_image.png&amp;diff=10309"/>
		<updated>2018-05-26T16:23:41Z</updated>

		<summary type="html">&lt;p&gt;Ruet : Ruet a téléversé une nouvelle version de Fichier:Minima image.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_legende.png&amp;diff=10308</id>
		<title>Fichier:Minima legende.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_legende.png&amp;diff=10308"/>
		<updated>2018-05-26T16:15:54Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_image.png&amp;diff=10307</id>
		<title>Fichier:Minima image.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_image.png&amp;diff=10307"/>
		<updated>2018-05-26T16:15:46Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_3.png&amp;diff=10306</id>
		<title>Fichier:Minima 3.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_3.png&amp;diff=10306"/>
		<updated>2018-05-26T16:15:38Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_2.png&amp;diff=10305</id>
		<title>Fichier:Minima 2.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_2.png&amp;diff=10305"/>
		<updated>2018-05-26T16:15:30Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_1.png&amp;diff=10304</id>
		<title>Fichier:Minima 1.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Minima_1.png&amp;diff=10304"/>
		<updated>2018-05-26T16:15:22Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10303</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10303"/>
		<updated>2018-05-26T15:59:45Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Deuxième étape : détermination de la &amp;quot;ligne de partage des eaux&amp;quot;==&lt;br /&gt;
&lt;br /&gt;
Pour la deuxième étape de l&#039;algorithme, on utilise l&#039;image gradient. Elle est alors considérée comme un relief où les pixels de faible intensité sont à faible altitude, et où les pixels d&#039;intensité élevée sont à forte altitude.&lt;br /&gt;
&lt;br /&gt;
=== Définitions ===&lt;br /&gt;
&lt;br /&gt;
En topologie la ligne de partage des eaux peut-être définie de plusieurs manières ; elle est la séparation entre deux bassins versants dans un relief.&lt;br /&gt;
&lt;br /&gt;
Notre image est considérée comme un relief, et la ligne de partage des eaux sera la séparation entre les zones de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Un bassin versant est un bassin dont toutes les eaux ont le même exutoire, par exemple une rivière ou l&#039;océan.&lt;br /&gt;
&lt;br /&gt;
Pour déterminer la ligne de partage des eaux, plusieurs méthodes sont possibles :&lt;br /&gt;
&lt;br /&gt;
* On place une source d&#039;eau dans chaque minimum local (donc dans chaque bassin) et on inonde, quand deux bassins se rencontrent on place une ligne de partage des eaux.&lt;br /&gt;
Les algorithmes utilisant ce principe sont appelés des algorithmes par inondation.&lt;br /&gt;
&lt;br /&gt;
* Une deuxième méthode est de considérer que des deux côtés de la ligne l&#039;écoulement de l&#039;eau se termine dans des bassins versants différents.&lt;br /&gt;
Dans l&#039;algorithme associé, on va faire &amp;quot;s&#039;écouler&amp;quot; de l&#039;eau sur chaque pixel et tracer une ligne de partage entre deux pixels (ou sur l&#039;un des deux pixels) quand les eaux qui s&#039;écoulent depuis ces pixels arrive dans des minima locaux différents.&lt;br /&gt;
Ce sont les algorithmes par ruissellement.&lt;br /&gt;
&lt;br /&gt;
=== Algorithme utilisé (créé par Luc Vincent et Pierre Soille) ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme choisi est un algorithme par inondation.&lt;br /&gt;
&lt;br /&gt;
Les pixels de faible intensité vont donc être traités en priorité.&lt;br /&gt;
&lt;br /&gt;
La première étape est donc de trier les pixels de l&#039;image par ordre croissant d&#039;intensité.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme va traiter les pixels d&#039;intensité la plus basse, puis une fois ceux-ci traités, les pixels avec une intensité légèrement supérieure sont traités, et ainsi de suite jusqu&#039;à avoir traité toute l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Trois problèmes se posent alors :&lt;br /&gt;
* Comment inonder un étage, c&#039;est à dire de quelle manière traiter les pixels de même intensité, à partir de bassins connus ?&lt;br /&gt;
* Comment déterminer à quel bassin appartient un pixel ?&lt;br /&gt;
* Comment repérer les nouveaux bassins ?&lt;br /&gt;
&lt;br /&gt;
==== Inonder un étage (pixels d&#039;intensité identique) ====&lt;br /&gt;
&lt;br /&gt;
Chaque bassin doit progresser à la même vitesse sur un étage, c&#039;est à dire que la ligne de partage des eaux qui sépare deux bassins doit être à égale distance de chacun.&lt;br /&gt;
&lt;br /&gt;
Chaque pixel est à une &amp;quot;distance&amp;quot; d&#039;un bassin. Par exemple les pixels collés à un bassin connu sont à une distance de 1. Leurs voisins sont eux à une distance de 2 (à condition qu&#039;ils ne soient pas collés à un bassin connu).&lt;br /&gt;
&lt;br /&gt;
[[File:file_distance.png|frame|Distance à laquelle se trouve les pixels du bassin connu (bleu)]]&lt;br /&gt;
&lt;br /&gt;
Pour que les bassins s&#039;étendent correctement, on traite d&#039;abord tous les pixels à distance 1, puis ceux à distance 2 et ainsi de suite.&lt;br /&gt;
&lt;br /&gt;
Une file d&#039;attente est créée ; la première étape est de placer les pixels à une distance de 1 dans la file d&#039;attente.&lt;br /&gt;
On va pouvoir traiter ces pixels là. On sait également que leurs voisins sont à une distance de 2, et pourront donc être traités lorsque les pixels à distance 1 seront associés à un bassin.&lt;br /&gt;
&lt;br /&gt;
Les voisins non-traités des pixels à une distance de 1 sont donc les prochains pixels de l&#039;étage à traiter.&lt;br /&gt;
Ils sont placés dans une autre file d&#039;attente, qui sera parcourue une fois que la première a été entièrement utilisée.&lt;br /&gt;
&lt;br /&gt;
Ensuite, les voisins des pixels à une distance de 2 seront les prochains pixels à traiter, et on va recommencer cette suite d&#039;opérations.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple de la manière dont les pixels sont placés dans la file d&#039;attente :&lt;br /&gt;
&lt;br /&gt;
* Tout d&#039;abord les pixels directement voisins d&#039;un bassin connu sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_1.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Puis une fois que les pixels dans la file d&#039;attente ont été traités, les pixels voisins des pixels qui viennent d&#039;être traités sont placés dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_2.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
* Cette étape se répète tant qu&#039;il y a des pixels dans la file d&#039;attente&lt;br /&gt;
&lt;br /&gt;
[[File:file_3.png|500px]] [[File:file_legende.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi le traitement d&#039;une file d&#039;attente (file d&#039;attente d&#039;une distance particulière) fait avancer d&#039;un pixel tous les bassins, et tous les bassins progressent à la même vitesse.&lt;br /&gt;
&lt;br /&gt;
==== Détermination du bassin auquel appartient un pixel ====&lt;br /&gt;
&lt;br /&gt;
Avec cette manière de faire progresser les bassins, le bassin auquel appartient un pixel est déterminé par les bassins auxquels appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
Plusieurs cas sont alors possibles lorsqu&#039;un pixel est traité.&lt;br /&gt;
&lt;br /&gt;
[[File:choix_label.png]] &lt;br /&gt;
&lt;br /&gt;
*Un des cas les plus simples est lorsque tous les voisins traités du pixel appartiennent au même bassin.Le bassin s&#039;étend alors, et le pixel à traiter est associé au bassin auquel appartiennent ses voisins.&lt;br /&gt;
&lt;br /&gt;
*Cette même décision est prise si tous les pixels voisins traités appartiennent au même bassin sauf certains qui appartiennent à une ligne de partage des eaux (LPE). L&#039;autre décision possible est de prolonger la LPE, cependant des lignes épaisses (et imprécises) seraient créées.&lt;br /&gt;
&lt;br /&gt;
*Si les pixels voisins traités du pixel appartiennent à une LPE, le bassin auquel appartient le pixel est inconnu. On ne peut pas &amp;quot;prolonger&amp;quot; la ligne car on ne connaît alors pas à quelle distance d&#039;un bassin se trouve le pixel. Si un pixel appartient à une LPE, ses voisins ne sont donc pas placés en file d&#039;attente.&lt;br /&gt;
&lt;br /&gt;
*Lorsque les pixels voisins appartiennent à des bassins différents, alors le pixel appartient à une LPE ; on sépare les bassins au moment de leur rencontre.&lt;br /&gt;
&lt;br /&gt;
*Le dernier cas se présente lorsque tous les pixels voisins sont traités et qu&#039;ils appartiennent tous à une LPE. Pour éviter qu&#039;un bassin d&#039;un seul pixel soit créé, on prolonge la LPE.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
En résumé :&lt;br /&gt;
*S&#039;il n&#039;y a qu&#039;un seul bassin autour du pixel en excluant les LPE, le pixel appartient à ce bassin.&lt;br /&gt;
*Si en excluant les LPE il y a plusieurs bassins autour du pixel, le pixel appartient à une LPE.&lt;br /&gt;
*Si le pixel est entouré par des LPE, alors il appartient à une LPE.&lt;br /&gt;
&lt;br /&gt;
==== Création de nouveaux bassins ====&lt;br /&gt;
&lt;br /&gt;
Lorsqu&#039;une file d&#039;attente est vide alors tous les pixels de l&#039;étage qui appartenaient à un bassin déjà connu ont été traités. Les pixels restants appartiennent donc à de nouveau bassin.&lt;br /&gt;
Un nouveau bassin est donc créée, le premier pixel qui lui appartient est choisi parmi un des pixels de l&#039;étage non-traités.&lt;br /&gt;
&lt;br /&gt;
On réinitialise alors la file d&#039;attente, et on place ses voisins non-traités dedans.&lt;br /&gt;
On recommence alors le traitement de la file d&#039;attente, et tous les pixels qui appartiennent à ce bassin lui seront attribués.&lt;br /&gt;
&lt;br /&gt;
Cette étape est répétée tant qu&#039;il reste des pixels non-traités à cet étage.&lt;br /&gt;
&lt;br /&gt;
Une fois que tous les pixels d&#039;un étage ont été traités, on passe à l&#039;étage supérieur.&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Choix_label.png&amp;diff=10302</id>
		<title>Fichier:Choix label.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Choix_label.png&amp;diff=10302"/>
		<updated>2018-05-26T15:48:08Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_legende.png&amp;diff=10301</id>
		<title>Fichier:File legende.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_legende.png&amp;diff=10301"/>
		<updated>2018-05-26T15:42:01Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_3.png&amp;diff=10300</id>
		<title>Fichier:File 3.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_3.png&amp;diff=10300"/>
		<updated>2018-05-26T15:31:22Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_2.png&amp;diff=10299</id>
		<title>Fichier:File 2.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_2.png&amp;diff=10299"/>
		<updated>2018-05-26T15:31:13Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_1.png&amp;diff=10298</id>
		<title>Fichier:File 1.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_1.png&amp;diff=10298"/>
		<updated>2018-05-26T15:30:58Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_distance.png&amp;diff=10297</id>
		<title>Fichier:File distance.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_distance.png&amp;diff=10297"/>
		<updated>2018-05-26T15:24:47Z</updated>

		<summary type="html">&lt;p&gt;Ruet : Ruet a téléversé une nouvelle version de Fichier:File distance.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Distance à laquelle se trouve chaque pixel d&#039;un bassin connu&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_distance.png&amp;diff=10296</id>
		<title>Fichier:File distance.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:File_distance.png&amp;diff=10296"/>
		<updated>2018-05-26T15:22:17Z</updated>

		<summary type="html">&lt;p&gt;Ruet : Distance à laquelle se trouve chaque pixel d&amp;#039;un bassin connu&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Distance à laquelle se trouve chaque pixel d&#039;un bassin connu&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10295</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10295"/>
		<updated>2018-05-26T15:00:25Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10294</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10294"/>
		<updated>2018-05-26T14:57:41Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|100px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par 255/(sqrt(2)*1020)&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|100px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|border|100px|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&#039;&#039;Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Convolution_pixel.png&amp;diff=10293</id>
		<title>Fichier:Convolution pixel.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Convolution_pixel.png&amp;diff=10293"/>
		<updated>2018-05-26T14:57:06Z</updated>

		<summary type="html">&lt;p&gt;Ruet : Ruet a téléversé une nouvelle version de Fichier:Convolution pixel.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10292</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10292"/>
		<updated>2018-05-26T14:52:33Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par 255/(sqrt(2)*1020)&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|150px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.png|thumb|right|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_gradient.png|border|300px|Image et son gradient]]&lt;br /&gt;
&#039;&#039;Remarque : dans cet exemple, l&#039;image gradient n&#039;est pas celle obtenue avec l&#039;algorithme ; les valeurs des pixels ont été augmentées pour que l&#039;impact de cette étape soit plus visible.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
La valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur (ou plutôt, les zones où les valeurs des pixels varient beaucoup).&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Sobel_gradient.png&amp;diff=10291</id>
		<title>Fichier:Sobel gradient.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Sobel_gradient.png&amp;diff=10291"/>
		<updated>2018-05-26T14:48:41Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10290</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10290"/>
		<updated>2018-05-26T14:43:39Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par 255/(sqrt(2)*1020)&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bords.png|border|150px]]&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_coin.jpg|thumb|right|Symétriser l&#039;image ne permet pas de remplacer les valeurs manquantes]]&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient.&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot; ; la valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur.&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Sobel_coin.png&amp;diff=10289</id>
		<title>Fichier:Sobel coin.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Sobel_coin.png&amp;diff=10289"/>
		<updated>2018-05-26T14:43:27Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10288</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10288"/>
		<updated>2018-05-26T14:39:19Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
Exemple, on veut calculer ici la nouvelle valeur du pixel entouré de rouge :&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par 255/(sqrt(2)*1020)&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;br /&gt;
&lt;br /&gt;
=== Calcul des bords de l&#039;image gradient ===&lt;br /&gt;
&lt;br /&gt;
Un problème survient, celui des pixels situés aux bords de l&#039;image. Ils n&#039;ont en effet pas de voisins dans toutes les directions, ainsi il n&#039;est pas possible de convoluer directement leur &amp;quot;entourage&amp;quot; avec les masques du filtre de sobel. &lt;br /&gt;
&lt;br /&gt;
Les pixels &amp;quot;manquants&amp;quot; sont remplacés.&lt;br /&gt;
&lt;br /&gt;
Plusieurs solutions sont possibles :&lt;br /&gt;
* Ne rien faire et enlever les &amp;quot;bords&amp;quot; de l&#039;image obtenue (l&#039;image obtenue après application de l&#039;algorithme est donc plus petite). Cette méthode donne de bons résultats car les résultats de l&#039;algorithme au bord de l&#039;image sont peu intéressants.&lt;br /&gt;
&lt;br /&gt;
* Prolonger l&#039;image. Il existe de nouveaux plusieurs méthodes, comme se référer à la valeur du pixel dans l&#039;image le plus proche lorsque le pixel voulu est en dehors de l&#039;image, ou répéter l&#039;image lorsque la valeur voulue est hors de l&#039;image. La manière de prolonger l&#039;image dépend de la manière dont on va utiliser le résultat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ici j&#039;ai choisi de prolonger l&#039;image. Une méthode pour cela est de symétriser de l&#039;image par rapport au pixel dont on veut calculer le gradient ; cette méthode a pour but de prolonger les formes au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Voici un exemple du résultat attendu, où la ligne bleue représente le bord de l&#039;image :&lt;br /&gt;
&lt;br /&gt;
[[File:Sobel_bord.png|border|150px]]&lt;br /&gt;
&lt;br /&gt;
Ainsi, on prolonge les valeurs au bord de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cependant cette solution ne permet pas calculer les valeurs aux &amp;quot;coins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Les pixels dont on ne peut pas remplacer la valeur par cette symétrie sont remplacés par le pixel dont on veut calculer le gradient (situé dans le coin de l&#039;image).&lt;br /&gt;
&lt;br /&gt;
Après l&#039;utilisation de cette algorithme, l&#039;image obtenue en résultat est une image &amp;quot;gradient&amp;quot; ; la valeur d&#039;un pixel est élevée s&#039;il se trouve dans une zone où les valeurs des pixels varient beaucoup.&lt;br /&gt;
&lt;br /&gt;
Il est facile d&#039;observer que les contours sont mis en valeur.&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Sobel_bords.png&amp;diff=10287</id>
		<title>Fichier:Sobel bords.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Sobel_bords.png&amp;diff=10287"/>
		<updated>2018-05-26T14:37:15Z</updated>

		<summary type="html">&lt;p&gt;Ruet : Ruet a téléversé une nouvelle version de Fichier:Sobel bords.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10286</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10286"/>
		<updated>2018-05-26T14:31:38Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;br /&gt;
&lt;br /&gt;
Chaque valeur d&#039;un pixel &amp;quot;autour&amp;quot; du pixel dont on veut la nouvelle valeur est multipliée par la valeur correspondante dans le masque.&lt;br /&gt;
&lt;br /&gt;
Dans ce cas pour obtenir une approximation de la dérivée partielle selon x, on devrait diviser par 8 le résultat (on utilise 8 valeurs parmi les pixels voisins).&lt;br /&gt;
&lt;br /&gt;
Cependant, on souhaite que la valeur de la norme du gradient en un point soit toujours comprise entre 0 et 255 (pour pouvoir l&#039;enregistrer dans une image).&lt;br /&gt;
&lt;br /&gt;
Cette division n&#039;est donc pas effectuée, les valeurs obtenues avec les deux masques du filtre de Sobel seront utilisées directement pour calculer la norme.&lt;br /&gt;
&lt;br /&gt;
La norme calculée sera ensuite multipliée par un coefficient pour ramener les valeurs entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées de la norme calculée sont proportionellles aux coordonnées de la vraie norme ; elle sont comprises entre -1020 et 1020 (-4*255 et 4*255).&lt;br /&gt;
&lt;br /&gt;
Si ces coordonnées sont appelées dx et dy, on a:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Norme = \sqrt{dx^2 + dy^2}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Donc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
0\leq Norme\leq \sqrt{2}*1020&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que la valeur de cette norme soit comprise entre 0 et 255, elle sera multipliée par &amp;lt;math&amp;gt;\frac{255}{\sqrt{2}*1020}&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
En arrondissant cette valeur, on obtient l&#039;intensité du pixel dans l&#039;image résultat.&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10285</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10285"/>
		<updated>2018-05-26T14:11:49Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;br /&gt;
&lt;br /&gt;
Pour chaque convolution, on utilise le masque et l&#039;entourage d&#039;un pixel dont on veut calculer la valeur.&lt;br /&gt;
&lt;br /&gt;
[[File:Convolution_pixel.png|exemple d&#039;utilisation du produit de convolution]]&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Convolution_pixel.png&amp;diff=10284</id>
		<title>Fichier:Convolution pixel.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Convolution_pixel.png&amp;diff=10284"/>
		<updated>2018-05-26T14:08:53Z</updated>

		<summary type="html">&lt;p&gt;Ruet : Ruet a téléversé une nouvelle version de Fichier:Convolution pixel.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10283</id>
		<title>Segmentation d&#039;image par détection de contours et algorithme &quot;ligne de partage des eaux&quot;</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Segmentation_d%27image_par_d%C3%A9tection_de_contours_et_algorithme_%22ligne_de_partage_des_eaux%22&amp;diff=10283"/>
		<updated>2018-05-26T14:01:04Z</updated>

		<summary type="html">&lt;p&gt;Ruet : Page créée avec «  == Introduction ==  La segmentation d&amp;#039;image est une famille dans le traitement d&amp;#039;image qui consiste à regrouper des pixels d&amp;#039;une image selon certaines de leurs caractér... »&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La segmentation d&#039;image est une famille dans le traitement d&#039;image qui consiste à regrouper des pixels d&#039;une image selon certaines de leurs caractéristiques.&lt;br /&gt;
&lt;br /&gt;
On va ici utiliser un algorithme appelé &amp;quot;ligne de partage des eaux&amp;quot; sur des images en niveaux de gris.&lt;br /&gt;
Dans cet algorithme on utilise deux approches de la segmentation d&#039;image, la segmentation par détection de contours et la segmentation par détection de régions. Deux étapes principales constituent donc l&#039;algorithme.&lt;br /&gt;
La première est de représenter l&#039;image en fonction de l&#039;intensité de ses variations, on met ainsi en valeur les fortes variations de valeur dans l&#039;image, donc les potentiels contours. La deuxième est de regrouper les zones dans cette nouvelle image par homogénéité, on détecte ainsi des régions dans l&#039;image.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Première étape : Approximation du gradient d&#039;une image==&lt;br /&gt;
=== Image et notion de  gradient d&#039;une image ===&lt;br /&gt;
==== Image ====&lt;br /&gt;
Une image est un ensemble de points associés à une couleur.&lt;br /&gt;
Pour afficher une couleur, on combine une intensité de lumière rouge, une intensité de lumière verte et une intensité de lumière bleue.&lt;br /&gt;
Dans une image couleur chaque pixel est donc associé à 3 valeurs, les intensités de ces couleurs.&lt;br /&gt;
Ces intensités sont généralement codées sur 8 bits, donc comprises entre 0 et 255.&lt;br /&gt;
&lt;br /&gt;
Ici, l&#039;image traitée est une image en niveaux de gris. Une couleur est perçue comme un niveau de gris lorsque les intensités de lumière rouge, de lumière verte et de lumière bleue sont identiques.&lt;br /&gt;
Ainsi chaque point est décrit par ses coordonnées et associé à une seule valeur (entre 0 et 255) dans les images que l&#039;algorithme pourra traiter.&lt;br /&gt;
&lt;br /&gt;
D&#039;un point de vue mathématique on peut considérer que l&#039;image est une fonction de R*R vers R : on prend un couple de réels (les coordonnées) auquel un autre réel est associé (un niveau de gris).&lt;br /&gt;
&lt;br /&gt;
==== Gradient ====&lt;br /&gt;
&lt;br /&gt;
Pour réaliser la segmentation d&#039;une image par détection de contour, la première étape est de représenter les variations de valeur dans l&#039;image.&lt;br /&gt;
Si l&#039;image est une fonction, et qu&#039;elle est considérée différentiable, il est possible d&#039;utiliser le gradient pour déterminer les variations de valeur en chaque point.&lt;br /&gt;
&lt;br /&gt;
Le gradient d&#039;une fonction est un vecteur qui représente les variations de la fonction par rapport à ses différents paramètres.&lt;br /&gt;
&lt;br /&gt;
La direction et le sens du gradient indiquent le sens dans lequel la fonction varie le plus et sa norme indique l&#039;intensité de la variation.&lt;br /&gt;
&lt;br /&gt;
Ainsi, plus la norme du gradient est élevée en un point, plus les variations &amp;quot;autour&amp;quot; du point sont élevées.&lt;br /&gt;
&lt;br /&gt;
Les coordonnées du gradient sont les dérivées partielles en fonction de chacun des paramètres de la fonction. Si les deux paramètres d&#039;une fonction de R*R vers R sont appelés x et y, on aura en première coordonnée la dérivée partielle de la fonction selon x et en deuxième coordonnée la dérivée partielle de la fonction selon y. &lt;br /&gt;
&lt;br /&gt;
Pour calculer les dérivées partielles de la fonction, il est nécéssaire de pouvoir connaître ses valeurs en chaque point.&lt;br /&gt;
Hors dans notre cas, les valeurs de la fonction image ne sont connues qu&#039;en certains points, qui sont &amp;quot;distincts&amp;quot; entre eux.&lt;br /&gt;
&lt;br /&gt;
Le gradient n&#039;est donc pas calculable, cependant il est possible d&#039;en avoir une approximation.&lt;br /&gt;
&lt;br /&gt;
=== Filtre de Sobel ===&lt;br /&gt;
&lt;br /&gt;
Le filtre de sobel est un opérateur permettant d&#039;approximer le gradient de chacun des points de l&#039;image.&lt;br /&gt;
&lt;br /&gt;
Cet opérateur utilise des masques de convolution.&lt;br /&gt;
&lt;br /&gt;
L&#039;application des masques de convolution dans une image est lié au produit de convolution, un opérateur mathématique dont je parlerai pas car je ne connais que peu le sujet.&lt;br /&gt;
&lt;br /&gt;
Pour obtenir la nouvelle valeur d&#039;un pixel, on va convoluer la région &amp;quot;autour&amp;quot; du pixel avec un masque.&lt;br /&gt;
&lt;br /&gt;
Le filtre de Sobel utilise deux masques, pour calculer chaque dérivée partielle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_x.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en x&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:sobel_y.png|border|150px]] &#039;&#039;Masque utilisé pour approximer la dérivée partielle en y&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation des masques de convolution ====&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Sobel_y.png&amp;diff=10282</id>
		<title>Fichier:Sobel y.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Sobel_y.png&amp;diff=10282"/>
		<updated>2018-05-26T13:36:48Z</updated>

		<summary type="html">&lt;p&gt;Ruet : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ruet</name></author>
	</entry>
</feed>