]> AND Private Git Repository - these_gilles.git/blob - THESE/Chapters/chapter3/chapter3.tex
Logo AND Algorithmique Numérique Distribuée

Private GIT Repository
MaJ des chronos PI-PD hybride + fps en HD
[these_gilles.git] / THESE / Chapters / chapter3 / chapter3.tex
1 \section{Introduction}
2 La principale difficulté soulevée par l'emploi d'algorithmes de type \textit{snake} orientés contour est le choix de la fonction d'énergie externe et la détermination de la nature des images auxquelles elle convient. 
3 Dans l'approche orientée régions, les deux régions que sont l'extérieur et l'intérieur du contour (cas mono cible) sont prises en compte dans l'estimation de la forme du contour ;  cela permet d'extraire des formes dans des images où les contours de la cible sont mal définis, en raison d'un fort niveau de bruit par exemple.
4 Les algorithmes découlant de cette approche n'ont fait l'objet, à notre connaissance, d'aucune parallèlisation sur GPU, malgré le grand intérêt qu'elles revêtent dans l'interprétation d'images fortement bruitées ( RADAR, médicales,\dots ) et le besoin de réduire suffisamment les temps de traiement pour permettre l'interactivité. 
5
6 Nous proposons dans la suite de ce chapitre de commencer par détailler l'algorithme séquentiel que nous avons pris comme référence, puis d'en présenter la version parallèle pour GPU que nous avons conçue.
7 L'algorithme a été décrit et proposé initialement en 1999 par Chesnaud \textit{et al.} dans \cite{ChesnaudRB99}. L'implémentation que les auteurs ont développée a continué d'être améliorée jusqu'à aujourd'hui et est employée comme brique élémentaire dans des algorithmes plus complexes. La version qui sert de référence ici est une implémentation séquentielle optimisée qui met à profit les capacités de parallélisme des CPU actuels en employant le jeu d'instruction SSE2 des microprocesseurs. La description que nous en faisons dans les lignes qui suivent est très largement inspirée de \cite{ChesnaudRB99} à la différence que nous n'implémentons pas les critères de régularisation du contour ni de minimisation de la longueur de description pour nous focaliser sur la déformation du contour et sa convergence. 
8
9 \section{Présentation de l'algorithme}
10 \subsection{Formulation}
11 À l'intérieur de l'image observée $\bar{v}$, soient $\bar{t}$ le vecteur composé par les niveaux de gris des $N_t$ pixels de la région cible $\Omega_t$ et $\bar{b}$ celui des $N_b$ pixels du fond $\Omega_b$. Les vecteurs $\bar{t}$ et $\bar{b}$ sont supposés non corrélés et sont caractérisés par leurs densités de probabilité (PDF) respectives $p^{\Theta_t}$ et $p^{\Theta_b}$ ; $\Theta_t$ et $\Theta_b$ étant les vecteurs des paramètres de leurs PDF. Dans le cas gaussien que nous supposerons ici parcequ'il est considéré comme prépondérant dans les images naturelles, $\Theta = (\mu, \sigma)$ où $\mu$ est la moyenne et $\sigma^2$ est la variance.
12 On note $\Gamma$ le contour de la région cible ($\Gamma \in \Omega_t$), que l'on suppose continu en connexité à 8 voisins. 
13
14 Le but de la segmentation est alors de déterminer la géométrie de $\Gamma$ qui maximise un critère de vraisemblance généralisée (GL).
15 La vraisemblance sur l'ensemble de l'image, \textit{i.e} la région $\Omega$, est donnée par
16
17 \begin{equation}
18 P\left(\bar{v} | \Omega_t, \Omega_b, \Theta_t, \Theta_b\right) = P\left(\bar{v} | \Omega_t, \Theta_t\right)P\left(\bar{v}|\Omega_b, \Theta_b\right)   
19 \end{equation}
20 soit en développant 
21 \begin{equation}
22 P\left(\bar{v} | \Omega_t, \Omega_b, \Theta_t, \Theta_b\right) = \prod_{x_k\in\Omega_t}p^{\Theta_t}\left(v_k, \Theta_t\right)\prod_{x_k\in\Omega_b}p^{\Theta_b}\left(v_k, \Theta_b\right)  
23 \label{eq-lhprod1}
24 \end{equation}
25
26 Dans le cas gaussien, la PDF étant de la forme 
27 \begin{equation}
28 p(\alpha) = \frac{1}{\sigma\sqrt{2\pi}}\mathrm{e}^{-\frac{\left(\alpha - \mu\right)^2}{2\sigma^2}}  
29 \label{eq-pdfgauss}
30 \end{equation}
31
32 La substitution de \eqref{eq-pdfgauss} dans \eqref{eq-lhprod1}, suivie du logarithme, permet d'obtenir l'expression de la \textit{log-vraisemblance}
33 \begin{equation}
34   \label{eq-gl1}
35   -N_tln\left(\sqrt{2\pi}\right)-N_tln\left(\sigma_t\right)-\frac{1}{2\sigma_t^2}\sum_{x_k\in\Omega_t}\left(v_k-\mu_t\right)^2
36 -N_bln\left(\sqrt{2\pi}\right)-N_bln\left(\sigma_b\right)-\frac{1}{2\sigma_b^2}\sum_{x_k\in\Omega_b}\left(v_k-\mu_b\right)^2
37 \end{equation}
38 dans laquelle les vecteurs $\Theta_t$ et $\Theta_b$ sont estimés suivant la méthode du  maximum de vraisemblance, qui donne l'expression générique suivante pour l'estimée de $\Theta_t$, notée $\widehat{\Theta_t}$ et transposable à l'identique pour $\Theta_b$ 
39 \begin{equation}
40   \label{eq-teta}
41   \widehat{\Theta_t} \left(
42 \begin{array}{l}
43 \widehat{\mu_t} = \frac{1}{N_t} \displaystyle\sum_{x_k\in \Omega_t} v_k \\
44 \widehat{\sigma^2_t} = \frac{1}{N_t} \displaystyle\sum_{x_k\in \Omega_t} \left(v_k - \widehat{\mu_t}\right)^2 \\
45   \end{array}
46 \right.
47 \end{equation}
48
49 En insérant les expressions de \eqref{eq-teta} dans \eqref{eq-gl1}, il reste, à une constante près, le critère de vraisemblance généralisée suivant, noté GL, que l'on cherche à  optimiser en déterminant la géométrie de $\Gamma$ qui en maximise la valeur et épousera alors au mieux la forme du contour de la cible.
50 \begin{equation}
51   \label{eq-gl}
52   GL=\frac{1}{2}\left(N_tln\left(\widehat{\sigma_t^2}\right)+N_bln\left(\widehat{\sigma_b^2}\right)\right)
53 \end{equation}
54
55 \subsection{Optimisation des calculs\label{snake-formulation}}
56 La maximisation de GL est effectuée en employant une technique itérative où sa valeur doit être calculée à chaque déformation du contour $\Gamma$.
57 Si l'on se reporte à l'équation \eqref{eq-teta}, on voit que l'obtention de la valeur de GL nécessite, à chaque évaluation d'une géométrie donnée de $\Gamma$, le calcul des sommes  
58 \begin{align}
59 \label{eq-sommes1}
60   S_v(\Omega_t) &= \sum_{x_k\in \Omega_t} v_k & S_{v^2}(\Omega_t) &= \displaystyle\sum_{x_k\in \Omega_t} v_k^2 \nonumber\\
61   S_v(\Omega_b) &= \sum_{x_k\in \Omega_b} v_k & S_{v^2}(\Omega_b) &= \displaystyle\sum_{x_k\in \Omega_b} v_k^2 \nonumber\\
62 \end{align}
63
64 Considérons la région cible $\Omega_t$, les pixels de coordonées $(i,j)$ qui la composent, et généralisons l'écriture des sommes de \eqref{eq-sommes1} en 
65 \begin{equation}
66   \label{eq-sommes-gene}
67   S_g(\Omega_t) = \sum_{i=i_{min}}^{i=i_{max}}\sum_{j=j_{min}(i)}^{j=j_{max}(i)}g\left(v(i,j)\right)
68 \end{equation}
69 où $g$ représente la fonction de valeurs de niveaux de gris à sommer.
70
71 En posant 
72 \begin{equation}
73   \label{eq-cumuls1}
74   T_g(y,\tau) = \sum_{j=0}^{\tau}g\left(v(y,j)\right)
75 \end{equation}
76 L'équation \eqref{eq-sommes-gene} devient 
77 \begin{equation}
78   \label{eq-somme-cumuls2}
79   S_g(\Omega_t) = \sum_{i=i_{min}}^{i=i_{max}}\left[T_g(i,j_{max}(i))-T_g(i,j_{min}(i)-1)\right]
80 \end{equation}
81 qui représente  une sommation sur le contour $\Gamma$ que l'on peut écrire 
82 \begin{equation}
83   \label{eq-somme-contour}
84   S_f(\Omega_t) = \sum_{(i,j)\in \Gamma}C(i,j)\gamma(i,j)
85 \end{equation}
86 où $C(i,j)$ est un coefficient lié à la direction du contour au point $(i,j)$ et $\gamma(i,j)$ prend sa valeur selon les règles suivantes
87
88 \begin{equation}
89   \label{eq-coefC}
90   \gamma(i,j)=
91   \begin{cases}
92     T(i,j)  & \text{si $C(i,j)= 1 $}\\
93     T(i,j-1)& \text{si $C(i,j)= -1$}\\
94     0       & \text{si $C(i,j)= 0$}\\
95   \end{cases}
96 \end{equation}
97
98 La valeur de $C(i,j)$ est déterminée pour chaque pixel d'indice $l$ du contour en considérant les pixels d'indices $l-1$ et $l+1$ qui définissent les deux vecteurs $f_{in}$ et $f_{out}$ et leur code selon le codage de Freeman, comme l'illustre la figure \ref{fig-freeman}. La table \ref{tab-freeman} donne les valeurs de $C(i,j)$ selon les valeurs des codes de Freeman des vecteurs $f_{in}$ et $f_{out}$.
99 Il faut noter que les valeurs dans la table \ref{tab-freeman} diffèrent de celles proposées initialement dans \cite{ChesnaudRB99}. Cette modification a été proposée pour permettre de s'adapter à la  segmentation multi-cibles. 
100 \begin{figure}[htb]
101   \centering
102   \includegraphics[height=3cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/codage-freeman.png}
103   \caption{À gauche : détermination des vecteurs $f_{in}$ et $f_{out}$. À droite : code de Freeman d'un vecteur en fonction de sa direction, l'origine étant supposée au pixel central, en noir. }
104   \label{fig-freeman}
105 \end{figure}
106
107 \begin{table}[h]
108   \centering
109 \begin{tabular}[htb]{ccccccccc}
110       \toprule
111       &\multicolumn{8}{c}{$f_{out}$}\\
112       \cmidrule(r){2-9}
113       $f_{in}$& 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 \\
114       \midrule
115       0     &0&0&0&0&0&-1&-1&-1 \\
116       1     &1&1&1&1&1&0&0&0\\
117       2     &1&1&1&1&1&0&0&0\\
118       3     &1&1&1&1&1&0&0&0\\
119       4     &0&0&0&0&0&-1&-1&-1\\
120       5     &0&0&0&0&0&-1&-1&-1\\
121       6     &0&0&0&0&0&-1&-1&-1\\
122       7     &0&0&0&0&0&-1&-1&-1\\
123       \bottomrule
124 \end{tabular}
125    \caption{Valeur du coefficient $C(i,j)$ en fonction des valeurs des codes de Freeman des vecteurs $f_{in}$ et $f_{out}$.}
126       \label{tab-freeman}
127 \end{table}
128
129 L'intérêt de cette transformation est majeur :
130 \begin{itemize}
131 \item La sommation en deux dimensions sur la région $\Omega_t$ est ainsi réduite à une sommation à une dimension sur le contour $\Gamma$.
132 \item Les valeurs $T_g(i,j)$ peuvent être calculées préalablement à la phase de segmentation proprement dite. Pour le cas gaussien qui nous intéresse, cela revient à pré-calculer les trois images \textit{cumulées} $S_1$, $S_x$ et $S_{x^2}$ définies par
133   \begin{alignat}{4}
134     \label{eq-img-cumul}
135     S_1(i,j) &= \sum_{x=0}^jx & \quad \text{,}\quad S_x(i,j) &= \sum_{x=0}^jv(i,x) & \quad \text{et}&\quad & S_{x^2}(i,j) &= \sum_{x=0}^jv(i,x)^2 
136   \end{alignat}
137 \item Les valeurs du coefficient $C(i,j)$ se calculent très facilement durant la génération du contour $\Gamma$.
138 \end{itemize}
139
140
141 Par ailleurs, le choix d'un contour polygonal permet d'améliorer l'efficacité de l'algorithme. Lors de la phase de segmentation, le déplacement d'un sommet du polygone n'influe ainsi que sur les pixels des deux segments qui s'y rapportent, réduisant ainsi la quantité de calculs à effectuer à chaque nouvelle déformation du contour.  
142
143 \begin{upminfo}
144 L'approche décrite dans ce chapitre n'est valide que si les segments formant le polygone du contour ne se croisent pas. Il est donc nécessaire, lors de la convergence de la segmentation, d'empêcher ces croisements. Une solution simple a été proposée dans \cite{ChesnaudRB99}, que nous avons parallélisée et intégrée. 
145 \end{upminfo}
146
147 \subsection{Implémentation séquentielle\label{snake-cpu-impl}}
148 Un des inconvénients des algorithmes de type \textit{snake} est l'influence du contour initial sur la convergence de la segmentation. Pour pallier simplement ce défaut, une technique progressive est adoptée, en initialisant le contour avec peu de sommets (par exemple 4) puis en augmentant le nombre au fur et à mesure de la convergence. L'algorithme \ref{algo-snake-cpu1} décrit macroscopiquement la solution mise \oe uvre tandis que l'algorithme \ref{algo-snake-cpu2} en présente les détails.
149
150 \begin{algorithm}[h]
151 \label{algo-snake-cpu1}
152 \small
153 \caption{Principe mis en \oe uvre pour la convergence du snake polygonal}
154   Calculer les images cumulées\;
155   Initialiser le contour avec 4 sommets\;
156   \Repeter(\tcc*[f]{niveau contour}){aucun sommet ne peut être ajouté}{
157       \Repeter(\tcc*[f]{niveau sommet}){aucun sommet ne peut être déplacé}{
158         Déplacer chaque sommet autour de sa position actuelle.\;
159         Déplacer le sommet vers la position induisant le meilleur GL\;
160      }
161       Ajouter un sommet au milieu de chaque \textit{grand} segment\;
162   }
163 \end{algorithm}
164
165 \begin{algorithm}[H]
166 \caption{Détail de l'implémentation du snake polygonal} 
167 \small
168 \label{algo-snake-cpu2}
169    Lire l'image $\bar{v}$\;
170    Calculer les images cumulées $S_1$, $S_x$ $S_{x^2}$ \nllabel{li-img-cumul}\tcc*[r]{en parallèle via SSE2} 
171    $n \leftarrow 0$ \tcc*[r]{indice de boucle niveau contour}
172    $N_n \leftarrow 4$ \tcc*[r]{nombre de n\oe uds}
173    $\Gamma \leftarrow \{\Gamma_0,\Gamma_1,\Gamma_2,\Gamma_3\} $\;
174    $d \leftarrow d_{max}$ \tcc*[r]{pas de déplacement des n\oe uds}
175    $l_{min} = 32$ \tcc*[r]{longueur mini des segments sécables}
176    $\Gamma_i \leftarrow \Gamma_0$ \tcc*[r]{sommet courant}
177    $GL_{ref} \leftarrow GL(\Gamma, N_n, \bar{v}, S_y, S_{y^2})$ \tcc*[r]{voir à partir de ligne 18 pour le détail}
178   \Repeter(\tcc*[f]{niveau contour}){$N_{add}=0$}{
179     $N_{add}\leftarrow 0$\;
180     \Repeter(\tcc*[f]{niveau n\oe ud}){$N_{move}=0$}{
181       $N_{move}\leftarrow 0$\;
182       \Pour{$i=0$ à $i=N_n-1$}{
183         Calculer les positions $\{\Gamma_i^0, \dots, \Gamma_i^7\}$ \tcc*[r]{les 8 voisins de $\Gamma_i$ }
184         $GL_w \leftarrow GL_{ref} - $ la contribution des segments $\Gamma_{i-1}\Gamma_i$ et $\Gamma_{i}\Gamma_{i+1}$\; 
185         \Pour{$w=0$ à $w=7$}{
186           Discrétiser les segments $\Gamma_{i-1}\Gamma_i^w$ et $\Gamma_{i}^w\Gamma_{i+1}$\nllabel{li-bresen}\;
187           Lire dans $S_1$, $S_x$ et $S_{x^2}$ les contributions des pixels de $\Gamma_{i-1}\Gamma_i^w$ et $\Gamma_{i}^w\Gamma_{i+1}$\nllabel{li-contrib-seg-deb}\;
188           Calculer les directions et lire les codes de Freeman \;
189           Calculer $GL_w$ incluant les contributions de $\Gamma_{i-1}\Gamma_i^w$ et $\Gamma_{i}^w\Gamma_{i+1}$ \nllabel{li-contrib-seg-fin}\;
190           \Si{$GL_w > GL_{ref}$}{
191             $GL_{ref} \leftarrow GL_w$\;
192             $\Gamma_i \leftarrow \Gamma_i^w$\;
193             $N_{move} \leftarrow N_{move}+1$\;
194           }
195         }
196       }
197      $l \leftarrow l+1$\;
198    }
199    \PourCh{segment $\Gamma_i\Gamma_{i+1}$}{
200      \Si{$\|\Gamma_i\Gamma_{i+1}\| > l_{min}$}{
201        Ajouter un n\oe ud au milieu de $\Gamma_i$ et $\Gamma_{i+1}$\;
202        $N_{add} \leftarrow N_{add}+1$\;
203      }
204    }
205    $N_n \leftarrow N_n + N_{add}$\;
206    \lSi{$d > 1$}{ $d \leftarrow d/2$ } \lSinon{ $d \leftarrow 1$ }\;
207    $GL_{ref} \leftarrow GL(\Gamma, N_n, \bar{v}, S_y, S_{y^2})$ \;
208   }
209 \end{algorithm}
210
211 \pagebreak
212 Les différentes sommations nécessaires au calcul de la valeur du critère $GL$ sont effectuées en parallèle à l'aide du jeu d'instructions SSE2, qui permet de travailler avec des registres de grande capacité (128 bits) et d'envisager d'y ranger côte à côte les opérandes des trois sommes pour les effectuer simultanément. 
213 Si l'on cherche à traiter des images en niveaux de gris sont codés sur 16 bits, les sommes $S_1$, $S_X$ et $S_{X^2}$ vont utiliser :
214 \begin{itemize}
215 \item $N_c$ bits par opérande de chaque somme pour représenter les coordonnées des pixels.
216 \item $N_p$ bits pour traduire le nombre d'opérandes dans chaque somme. 
217 \item 16 bits par valeur de niveau de gris dans $S_X$.
218 \item 32 bits par valeur de niveau de gris au carré dans $S_{X^2}$.
219 \end{itemize}
220 Les trois sommes utilisent donc, par opérande, un total de $\left(3\left(N_c+N_p\right)+16+32\right)$ bits devant être contenu dans un registre de 128 bits, ce qui nous donne un maximum de 26 bits pour $N_c+N_p$. 
221 La longueur des segments pouvant être au maximum $\sqrt{2}$ fois supérieure au coté de l'image, on peut donc considérer qu'il est nécessaire d'avoir  $N_p = N_c+1$ pour ne pas générer de restriction sur la longueur des segments. Cela nous conduit donc à $N_c = 12$ et $N_p=13$ ($12+13 = 25 < 26$).  
222 La répartition retenue pour les données dans les registres SSE2 de 128 bits est alors la suivante :
223 \begin{itemize}
224 \item $N_c+N_p=25$ bits pour les opérandes des sommes de $S_1$.
225 \item $N_c+N_p+16=41$ bits pour les opérandes des sommes de $S_X$.
226 \item $N_c+N_p+32=57$ bits pour les opérandes des sommes de $S_{X^2}$.
227 \end{itemize}
228
229 \subsection{Performances}
230 Les images de 1024$\times$1024 pixels de la figure \ref{fig-snakecpu-cochon512} montrent l'évolution du contour lors de la segmentation d'une image photographique prise en faible éclairement et bruitée artificiellement par un bruit gaussien d'écart type 25. Les paramètres de la séquence sont fixés empiriquement aux valeurs $d_{max}=16, l_{min}=8$.
231 Les temps d'exécution indiqués sont mesurés sur Intel Xeon E5530-2.4GHz with 12Go RAM et sont les valeurs moyennes obtenues pour 10 exécutions.
232 \begin{figure}
233   \centering
234   \subfigure[Initialisation : 4 n\oe uds]{\includegraphics[height=3.5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/im000.png}}
235   \subfigure[Itération 1 : 8 n\oe uds 3~ms]{\includegraphics[height=3.5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/im001.png}}
236   \subfigure[Itération 2 : 16 n\oe uds 1~ms]{\includegraphics[height=3.5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/im002.png}}
237   \subfigure[Itération 3, 32 n\oe uds 1~ms]{\includegraphics[height=3.5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/im003.png}}\\
238   \subfigure[Itération 7 : 223 n\oe uds 3~ms]{\includegraphics[height=3.5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/im007.png}}
239   \subfigure[Itération 10 : 244 n\oe uds 3~ms]{\includegraphics[height=3.5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/im010.png}}
240   \subfigure[Itération 13 : 256 n\oe uds 3~ms]{\includegraphics[height=3.5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/im013.png}}
241   \subfigure[Itération 14 : 256 n\oe uds 3~ms]{\includegraphics[height=3.5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/im014.png}}
242   \caption{Évolution du contour lors de la segmentation d'une image de 512$^2$ pixels. La convergence est obtenue à l'itération 14 après 44~ms pour un total de  256 n\oe uds.}
243  \label{fig-snakecpu-cochon512}
244 \end{figure}
245
246 La dépendance vis à vis du contour initial, qui est un des principaux soucis liés au snake est ici fortement relativisée. La figure \ref{fig-snakecpu-compinit} montre le contour final segmentant l'image de test de la figure \ref{fig-snakecpu-cochon512} à partir d'un état initial très éloigné du précédent et, \textit{a priori}, très défavorable compte tenu du fait qu'il est loin de la cible et sans intersection avec elle. Toutefois, le contour final est très proche de celui obtenu à partir d'un état initial englobant la cible, malgré un n\oe ud qui s'est \og accroché \fg{} au bord de l'image. La convergence est également plus longue à obtenir dans ce cas avec 87~ms pour de 17 itérations et 273 n\oe uds. 
247
248 \begin{figure}
249   \centering
250   \subfigure[Initialisation 2 ]{\includegraphics[height=4.5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/snakecpu-init2.png}}
251   \subfigure[Contour final 2 : 273 n\oe uds 87~ms]{\includegraphics[height=4.5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/snakecpu-compinit2.png}}
252   \subfigure[Contour final 1 : 256 n\oe uds 44~ms]{\includegraphics[height=4.5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/im014.png}}
253   \caption{Influence du contour initial sur la segmentation. Le contour final 1 est celui de la figure \ref{fig-snakecpu-cochon512}.}
254   \label{fig-snakecpu-compinit}
255 \end{figure}
256
257 La dimension de l'image à traiter a également un effet sur le résultat et naturellement sur le temps de calcul. Si l'on conserve les mêmes paramètres d'optimisation que pour la segmentation de l'image 512$\times$512 pixels et un contour initial dont les cotés sont à une distance des bords équivalente à 10\% des cotés de l'image, le résultat sur une image identique de 4000$^2$4000 pixels est  obtenu en 1,3~s avec 1246 n\oe uds ; il est reproduit  à la figure \ref{fig-snakecpu-cochon4ka}. Le nombre de pixels appartenant à la région cible est tel que l'amplitude des déplacements autorisés pour chaque n\oe ud ($d$) peut se révéler trop faible vis-à-vis du seuil d'acceptation des mouvements. On observe que les zones à gradient élevé ne posent pas de problème et sont détourées de la même manière, alors que dans le bas de l'image où figure une zone de gradient faible (ombre), la cible se trouve maintenant quelque peu surévaluée en surface là ou elle était plutôt sous évaluée dans l'image en 512$\times$512 pixels. 
258 On parvient à un résultat très proche beaucoup plus rapidement en adaptant les paramètres à la taille de l'image, comme le montre par exemple la segmentation de la figure \ref{fig-snakecpu-cochon4kb}, effectuée avec $d_{max}=128$ et $l_{min}=32$ et qui converge vers un contour de 447 n\oe uds en moins de 0,7~s.
259 Au delà des 16 millions de pixels (4000$\times$4000 pixels), l'implémentation séquentielle est toujours possible mais doit se priver des instructions SSE. Nous avons, avec l'accord des auteurs, adapté leur code en ce sens et réalisé les mesures pour des tailles allant jusqu'à 150~MP. La table \ref{tab-snakecpu-speed-size} en synthétise les résultats en distinguant chaque fois le temps pris par les pré-calculs et celui nécessaire à la convergence de la segmentation. On constate que les deux étapes et donc le temps total varient linéairement avec la taille de l'image.
260
261 \begin{figure}
262   \centering
263   \subfigure[$d_{max}=16$ et $l_{min}=8$, 1246 n\oe uds en 1.3~s]{\label{fig-snakecpu-cochon4ka}\includegraphics[height=5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/snakecpu-cochon4k-t.png}}
264   \subfigure[$d_{max}=128$ et $l_{min}=32$, 447 n\oe uds en 0.7~s]{\label{fig-snakecpu-cochon4kb}\includegraphics[height=5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/snakecpu-cochon4k-128-32-t.png}}
265   \caption{Segmentation de l'image de test en 4000$\times$4000 pixels. Le tracé du contour a été artificiellement épaissi pour le rendre visible à l'échelle de l'impression.}
266   \label{fig-snakecpu-cochon4k}
267 \end{figure}
268
269 \begin{figure}[h]
270   \centering
271   \includegraphics[height=5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/cochon4kc-128-8-t.png}
272   \caption{Segmentation de l'image de test en 4000$\times$4000 pixels avec une cible de petite taille. Le contour initial est la transcription de celui utilisé à la figure \ref{fig-snakecpu-cochon512}. Le tracé du contour a été artificiellement épaissi pour le rendre visible à l'échelle de l'impression.}
273   \label{fig-snakecpu-cochon4kc3}
274 \end{figure}
275  
276
277
278 \begin{table}[h]
279   \centering
280   \begin{tabular}{rrrr}
281       \toprule
282       &\multicolumn{3}{c}{Taille de l'image}\\
283       &\multicolumn{3}{c}{(millions de pixels)}\\
284       \cmidrule(r){2-4}
285       & 15 & 100 & 150 \\
286       \midrule
287       Pré-calculs &0,13&0,91&1,4\\
288       Segmentation&0,46&3,17&4,3\\
289       {\bf Total} &0,51&4,08&5,7\\
290       \bottomrule
291 \end{tabular}
292    \caption{Performances (en secondes) de la segmentation par snake polygonal sur CPU en fonction de la taille de l'image à traiter. Les temps sont obtenus avec la même image de test dilatée et bruitée et un contour initial carré dont la distance aux bords est proportionnelle à la taille de l'image. Seule l'image en 15~MP a pu être traitée par une implémentation utilisant SSE2.}
293       \label{tab-snakecpu-speed-size}
294 \end{table}
295
296
297 Enfin, il faut aussi considérer les tailles relatives de la cible et de l'image. Ainsi, si on fait l'hypothèse d'une cible de petite taille \og noyée \fg{} dans une image de grandes dimensions, les résultats de la segmentation seront impactés en raison, cette fois, d'une moindre adaptation à la cible lors des toutes premières itérations, les plus grossières, où le nombre de n\oe uds est réduit et le pas de déplacement potentiellement grand vis à vis de la cible. Ce cas de figure est illustré par la segmentation reproduite à la figure \ref{fig-snakecpu-cochon4kc3} et qui met en évidence une qualité moindre par la confusion des zones les plus sombres de la cible avec le fond.
298
299
300
301 \section{Implémentation parallèle GPU du snake polygonal}
302 L'analyse de l'exécution du programme séquentiel révèle la prépondérance des blocs fonctionnels suivants, qui occupent à eux seuls 80\% du temps total d'exécution :
303 \begin{itemize}
304  \item Le calcul de la contribution des segments, pour environ 50\% (lignes \ref{li-contrib-seg-deb} à \ref{li-contrib-seg-fin} dans l'algorithme \ref{algo-snake-cpu2}) 
305   \item La génération des trois images cumulées, avant le début des itérations, pour environ 20\% (ligne \ref{li-img-cumul}).
306   \item La discrétisation des segments définis par les coordonnées de leurs extrémités, pour environ 7\% (ligne \ref{li-bresen}).
307 \end{itemize}
308 Cette proportion est globalement conservée lorsque la taille de l'image à traiter varie, comme le montre le graphique de la figure \ref{fig-snakecpu-chronos1} 
309
310 \begin{figure}
311   \centering
312   \includegraphics[width=0.5\linewidth, height=0.25\linewidth]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/cpu_profil.png}
313 \label{fig-snakecpu-chronos1}
314   \caption{Évolution du coût relatif des trois fonctions les plus consommatrices en temps de calcul en fonction de la taille de l'image à traiter.}
315 \end{figure} 
316
317 Si l'effort de parallélisation porte essentiellement sur ces fonctions coûteuses, l'ensemble du traitement est réalisé sur le GPU afin de réduire autant que possible les transferts entre le GPU et le système hôte qui, selon le volume concerné, sont susceptibles de grever considérablement la performance globale. L'hôte ne conserve que l'initiative du transfert initial et le contrôle de la boucle principale, ne nécessitant l'échange que d'un seul octet à chaque itération (représentant le nombre de nouveaux n\oe uds $N_{add}$).
318
319 Les traitements étant totalement indépendants, nous traitons séparément la parallélisation des pré-calculs et celle de la segmentation.
320
321 \subsection{Pré-calculs des images cumulées}   
322 Pour réduire la quantité de mémoire requise, nous avons choisi de ne pas générer l'image $S_1$ mais plutôt d'en calculer les valeurs à la volée. L'expression en est simple et le temps pris par les opérations élémentaires qu'elle met en jeu est largement compensé par le gain obtenu en économisant les accès mémoire qui auraient été nécessaires, ce qui n'est pas le cas des deux autres images $S_x$ et $S_x^2$ dont le calcul est quant à lui réalisé en appliquant une variante de la méthode des sommes préfixées (\textit{prefixsums}) décrite dans \cite{BlellochTR90} et qui permet d'évaluer les expressions de l'équation \eqref{eq-img-cumul}.
323
324 Les sommations se font au niveau de chaque ligne de l'image, que l'on décompose en $n$ blocs de $bs$ pixels où $bs$ correspond aussi au nombre de threads exécutés par chaque bloc de la grille de calcul. La valeur $bs$ étant obligatoirement une puissance de 2 supérieure à 32, le bloc de pixels d'indice $n-1$ doit éventuellement être complété par des valeurs nulles. Chaque bloc de thread réalise son traitement indépendemment des autres, mais l'ensemble des sommes de bloc étant requise pour le calcul des sommes globales, une synchronisation est nécessaire à deux endroits du calcul. Nous avons choisi d'assurer ces synchronisations en découpant le traitement en trois \textit{kernels} distincts, rendant par la même occasion le code plus concis :
325 \begin{itemize}
326 \item \texttt{compute\_block\_prefixes()} est le \textit{kernel} effectuant, en mémoire partagée, la \textit{prefixsum} inclusive de chaque bloc, puis qui en mémorise la sommes, \textit{i.e} le dernier élément, dans deux vecteurs $V_x$ et $V_x^2$ en mémoire globale. L'ensemble des prefixsums est également mémorisé en mémoire globale. La largeur de l'image n'étant pas nécessairement une puissance de 2, il est nécessaire de faire du remplissage avec des valeurs nulles dans le dernier bloc (indice $n-1$). 
327 \item \texttt{scan\_blocksums()} est le \textit{kernel} effectuant les prefixsum exclusifs des vecteurs $V_x$ et $V_x^2$. Les résultat demeurent respectivement dans $V_x$ et $V_x^2$.
328 \item \texttt{add\_sums2prefixes()} est le \textit{kernel} effectuant les additions de chaque élément d'indice $i$ des vecteurs $V_x$ (respectivement $V_x^2$) avec tous les éléments du prefixsum du bloc de même indice $i$. 
329 \end{itemize}
330
331 Les diagrammes de la figure \ref{fig-calcul-cumuls} donnent le détail des opérations effectuées par ces trois \textit{kernels} pour l'image cumulée $S_x$. La seconde image cumulée $S_x^2$ est obtenues exactement de la même manière en sommant non plus les valeurs $v_k$ mais $v^2_k$.
332
333 \begin{figure}
334   \centering
335   \subfigure[Détail des opérations effectuées par le \textit{kernel} \texttt{compute\_block\_prefixes()}. La valeur $bs$ correspond au nombre de pixels de chaque bloc, qui est aussi le nombre de threads exécuté par chaque bloc de la grille de calcul.]{\resizebox{0.9\linewidth}{!}{ \input{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/GPUcumuls.pdf_t}}}\vspace{1cm}
336 \subfigure[Détail des opérations effectuées par le \textit{kernel} \texttt{scan\_blocksums()}.]{\resizebox{0.9\linewidth}{!}{ \input{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/GPUscansomblocs.pdf_t}}}\vspace{1cm}
337 \subfigure[Détail des opérations effectuées par le \textit{kernel} \texttt{add\_sums2prefixes()}.]{\resizebox{0.9\linewidth}{!}{ \input{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/GPUaddsoms2cumuls.pdf_t}}}
338   \caption{Calcul des images cumulées $S_x$ et $S_x^2$ en trois étapes successives. a) cumul partiel bloc par bloc et mémorisation de la somme de chaque bloc. b) cumul sur le  vecteur des sommes partielles. c) ajout des sommes partielles à chaque élément des blocs cumulés.}
339 \label{fig-calcul-cumuls}
340 \end{figure}
341
342 Les gains de performance de cette implémentation GPU comparée à l'implémentation CPU/SSE2 sont ceux de la table \ref{tab-speedup-cumuls}, soit un GPU environ 7 fois plus rapide pour des images de taille 15 à 150 millions de pixels. L'influence de la taille d'image sur le gain est faible, mais on peut toutefois noter que plus l'image est grande plus le gain est important. 
343 Les accélérations constatées peuvent sembler faibles en regard de ce que l'on attend d'un GPU, mais il faut rappeler que ce type d'opération (les réductions) n'est pas véritablement adapté à leur architecture en raison d'une grande inter-dépendance des données d'une étape de calcul à l'autre. Sans une implémentation optimisée, cette opération s'exécuterait même plus lentement sur GPU que sur un CPU.     
344 On obtient des accélérations supérieures en rendant le calcul moins générique et en développant des versions spécifiques des trois \textit{kernels}, dédiées par exemple au traitement des images dont largeur est multiple de 256 pixels.
345 \begin{table}[h]
346   \centering
347   \begin{tabular}{rrrr}
348       \toprule
349       &\multicolumn{3}{c}{Taille de l'image}\\
350       &\multicolumn{3}{c}{(millions de pixels)}\\
351       \cmidrule(r){2-4}
352       & 15 & 100 & 150 \\
353       \midrule
354       temps CPU (s)      &0,13&0,91&1,4\\
355       temps GPU (s)      &0,02&0,13&0,2\\
356       {\bf Accélération} &{\bf 6,5} &{\bf 6,9} &{\bf 7,0}\\
357       \bottomrule
358 \end{tabular}
359    \caption{Accélération constatée, pour le calcul des images cumulées, de l'implémentation GPU (C2070) par rapport à l'implémentation CPU de référence.}
360       \label{tab-speedup-cumuls}
361 \end{table}
362
363
364 \subsection{Calcul des contributions des segments}
365
366 Le déplacement d'un des $N_n$ n\oe uds du contour $\Gamma$ vers l'une des 8 positions voisines permises, impose d'évaluer les contributions des 8 paires de segments associées, soit $16N_n$ segments pour la totalité du contour, que nous évaluons en parallèle au sein du \textit{kernel} \texttt{GPU\_compute\_segments\_contribs()}. Pour ce faire, chaque segment doit tout d'abord être discrétisé en une suite de pixels puis, en conservant la règle \textit{1 pixel par thread} la contribution de chaque pixel est déterminée avant de toutes les additionner pour obtenir la contribution du segment. 
367 Les pixels représentant les n\oe uds font l'objet d'un traitement spécifique impliquant les codes de Freeman, pour ne pas fausser les contributions globales (voir paragraphe \ref{snake-cpu-impl}).  
368
369 Pour optimiser l'exécution de ce kernel et réduire l'effet de la disparité des longueurs des segments, nous avons crée un motif régulier en mémoire, basé sur la longueur $npix_{max}$ du plus grand segment et avons complété les blocs associés aux segments de longueur inférieure à $npix_{max}$ avec des valeurs neutres pour l'opération réalisée, c'est-à-dire des valeurs nulles. 
370
371 Si $bs_{max}$ est la taille de bloc maximale admissible par le GPU, la taille $bs$ des blocs de threads/pixels employée pour le calcul des contributions des segments est alors déterminée de la façon suivante :
372 \[
373 bs=
374 \begin{cases}
375 2^p     & \text{ avec $2^{p-1} < npix_{max} \leq 2^p$ si $npix_{max}\in[33; bs_{max}]$}\\
376 32      & \text{ si $npix_{max} \leq 32$}\\
377 bs_{max} & \text{ si $npix_{max} > bs_{max}$}
378 \end{cases} 
379 \]
380  
381 Dans notre implémentation, les calculs sont faits en mémoire partagée et la quantité nécessaire limite la taille de bloc admissible. Nous limitons celle-ci à 256 sur C1060 et 512 sur C2050. Toutefois, les tests ont montré que sur ces deux versions de l'architecture, la taille maximale conduisant aux meilleures performances est de 256 threads par bloc. 
382
383 Le \textit{kernel} \texttt{GPU\_compute\_segments\_contribs()} calcule alors en parallèle pour tous les segments les coordonnées de tous les pixels qui les composent. Nous mettons pour cela en \oe uvre l'algorithme de Bresenham, \textit{i.e} la méthode du segment semi-ouvert, en distinguant les cas où
384 \begin{itemize}
385 \item la valeur absolue de la pente $k$ du segment à discrétiser est supérieure à $1$; on applique alors la méthode au segment \textit{horizontal} semi-ouvert et on obtient un pixel par ligne.
386 \item la valeur absolue de la pente $k$ du segment à discrétiser est inférieure ou égale à  $1$; on applique alors la méthode au segment \textit{vertical} semi-ouvert et on obtient un pixel par colonne.
387 \end{itemize}
388 Cette distinction nous permet de conserver la règle \textit{1 pixel par thread} importante pour la régularité des motifs d'accès en mémoire et aussi pour \textit{charger} au maximum le GPU.
389
390 \begin{figure}
391   \centering
392   \resizebox{0.8\linewidth}{8cm}{ \input{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/contribs_segments.pdf_t}}
393   \caption{Structuration des données en mémoire du GPU pour l'évaluation en parallèle de l'ensemble des évolutions possibles du contour.}
394 \label{fig-structure-segment}
395 \end{figure}
396
397
398 La figure \ref{fig-structure-segment} représente la structure décrite ci-dessus pour la représentation en mémoire des segments à évaluer. La première ligne montre le détail du premier segment, avec la correspondance \textit{1 pixel par thread} et le découpage en un nombre de blocs suffisant pour permettre de décrire le plus long des segments. 
399
400 La seconde ligne présente l'ordre dans lequel sont concaténés les 16 groupes de blocs-segment associés au déplacement d'un n\oe ud particulier. 
401
402 Les deux dernières lignes décrivent la concaténation des ensembles de 16 blocs-segment, avec la particularité de séparer la description des positions des n\oe uds d'indices pairs et ceux d'indices impairs. Cela permet de moins s'écarter de l'heuristique d'optimisation en vigueur dans la version séquentielle où les statistiques globales comme la valeur de critère $GL$ sont recalculées après chaque déplacement (figures \ref{fig-cycle-contribs-segments-a}, \ref{fig-cycle-contribs-segments-b} et \ref{fig-cycle-contribs-segments-c}) .
403
404 En version parallèle, si les \og meilleures \fg{}  positions de tous les n\oe uds sont calculées simultanément, le contour généré est constitué de segments qui n'ont pas été validés pendant la phase de déplacement des n\oe uds, comme l'illustre la figure \ref{fig-cycle-contribs-segments-e}. La valeur du critère $GL$ doit donc être calculée après coup sur les segments réels du nouveau contour. Dans l'absolu, nous ne sommes donc pas assurés d'améliorer réellement la valeur du critère par rapport au contour de l'itération précédente.
405 Pour limiter ce phénomène, qui pourrait provoquer des oscillations et empêcher la convergence, nous avons effectué les déplacements en alternant ceux des n\oe uds d'indices pairs et ceux d'indices impairs. Cela permet de régler le problème lorsque le nombre de n\oe uds du contour est pair. Comme le montrent les figures \ref{fig-cycle-contribs-segments-e} et \ref{fig-cycle-contribs-segments-e}, un segment du contour demeure non validé lorsque le nombre de n\oe uds est impair et nous impose toujours de recalculer, \textit{a posteriori}, la valeur du critère $GL$ pour s'assurer de l'amélioration apporté par les déplacements des n\oe uds.   
406
407   
408 \begin{figure}
409   \centering
410   \subfigure[Contour de référence.]{\label{fig-cycle-contribs-segments-a}\includegraphics[height=3cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/cycle-contribs-segments-cpu1.png}}\quad
411 \subfigure[Déplacement du n\oe ud $N_1$. Le critère est amélioré.]{\label{fig-cycle-contribs-segments-b}\includegraphics[height=3cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/cycle-contribs-segments-cpu2.png}}\quad
412 \subfigure[Déplacement du n\oe ud $N_2$. Le critère est amélioré.]{\label{fig-cycle-contribs-segments-c}\includegraphics[height=3cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/cycle-contribs-segments-cpu3.png}}\\
413 \subfigure[Déplacement en parallèle de tous les n\oe uds. Les segments du contour n'ont pas été validés. On doit recalculer le critère après les déplacements pour savoir s'il a été amélioré.]{\label{fig-cycle-contribs-segments-d}\includegraphics[height=3cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/cycle-contribs-segments-gpu1.png}}\quad
414 \subfigure[Déplacement en parallèle des n\oe uds impairs. Le critère est amélioré.]{\label{fig-cycle-contribs-segments-e}\includegraphics[height=3cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/cycle-contribs-segments-gpu2.png}}\quad
415 \subfigure[Déplacement en parallèle des n\oe uds pairs. Un seul segment n'a pas été évalué.]{\label{fig-cycle-contribs-segments-f}\includegraphics[height=3cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/cycle-contribs-segments-gpu3.png}}
416   \caption{Comparaison des cycles de déplacement des n\oe uds. Ligne du haut : version séquentielle. Ligne du bas : version parallèle. Les segments en rouge sont des segments du contour non évalués, alors que ceux en pointillés sont les paires ayant reçu les meilleures évaluations parmi les 8 déplacements possibles des n\oe uds correspondant.}
417 \label{fig-cycle-contribs-segments}
418 \end{figure}
419
420 La représentation en mémoire des segments conduit à avoir un certain nombre non prévisible de threads inactifs dans la grille, sans que cela soit préjudiciable aux performances car cela n'engendre pas de branches d'exécution divergentes, qui sont à proscrire sur GPU. 
421
422 Les calculs liés à l'évaluation des contributions des pixels sont réalisés en mémoire partagée. Seule une très petite quantité de données doit être stockée en mémoire globale. Il s'agit, pour chaque {\bf segment} :
423 \begin{itemize}
424 \item des coordonnées de son milieu. Cela permet l'ajout efficace de n\oe ud quand c'est possible.
425 \item les coordonnées des deux derniers pixels de chaque extrémité. Ils sont nécessaires pour calculer la dérivée aux extrémités et ainsi déterminer le code de Freeman des n\oe uds.      
426 \end{itemize}
427
428 Pour obtenir les contributions des segments, c'est-à-dire les sommes des contributions des leurs pixels, une première phase de réduction partielle est effectuée au niveau de chaque bloc.
429
430 Une synchronisation est alors nécessaire avant d'effectuer les sommes de l'ensemble des contributions partielles qui fournissent les contributions globales des segments. Le contour modifié est alors construit comme la suite des meilleures positions déterminées pour chaque n\oe ud, pour peu que ces nouvelles positions ne génèrent pas de croisement de segments. 
431
432 La solution retenue pour vérifier l'absence de croisement est celle de l'implémentation séquentielle, parallélisée simplement par paire de segments. Cela n'apporte pas de véritable gain de performance par rapport à la version CPU, mais, contraints de conserver les données en mémoire GPU pour limiter les transferts entre l'hôte et son périphérique, nous avons fait en sorte que cette fonctionnalité ne grève pas les performances globales.
433
434 Les valeurs obtenues après détermination du nouveau contour, calcul des statistiques globales et évaluation du critère $GL$, servent de référence pour les prochaines déformations du contour. Les techniques appliquées pour ces calculs sont de nouveau celles décrites au début ce paragraphe.  
435 Enfin, l'ajout des nouveaux n\oe uds se fait simplement,  pour les segments suffisamment grands, en utilisant les coordonnées des pixels milieux mémorisées lors de la discrétisation des segments. 
436
437
438 \subsubsection{Cas particulier des segments dont la pente $k$ vérifie $|k|\leq 1$}
439 Comme nous venons de le voir, les segments dont la pente $k$ vérifie $|k|\leq 1$ sont discrétisés à raison de \textit{1 pixel par colonne} et comportent donc le plus souvent plusieurs pixels sur une ligne donnée, comme le montrent les schémas de la figure \ref{fig-segment-k<1}. 
440
441 D'après la formulation générale du snake faite au paragraphe \ref{snake-formulation}, le coefficient $C(i,j)$ est à appliquer en chaque point du contour. La technique de discrétisation employée conduit à des coefficients $C(i,j)$ constants sur l'ensemble des pixels des segments dont la pente $k$ vérifie  $|k|> 1$, mais ce n'est pas le cas pour ceux dont la pente $k$ est inférieure ou égale à $1$. Les quatre cas, un par quadrant, qui peuvent se présenter sont représentés à la figure \ref{fig-segment-k<1}. 
442
443 D'un point de vue opérationnel, on constate en se reportant à la table \ref{tab-freeman}, que tout pixel dont les voisins immédiats sont sur la même ligne, à un coefficient $C(i,j)=0$ ($F_{in}=f_{out}=0$). Les deux pixels des extrémités, n'ayant qu'un voisin sur la même ligne, ont un coefficient qui dépend du quadrant :
444 \begin{itemize}
445 \item dans les quandrant  1 et 2
446   \begin{itemize}
447   \item le premier pixel d'une ligne a un coefficient $C(i,j)=1$.
448     \item le dernier pixel d'une ligne a un coefficient $C(i,j)=0$
449   \end{itemize}
450  \item dans les quandrant  3 et 4
451    \begin{itemize}
452    \item le dernier pixel d'une ligne à un coefficient $C(i,j)=-1$.
453      \item le premier pixel d'une ligne a un coefficient $C(i,j)=0$.
454    \end{itemize}
455 \end{itemize}
456
457 \begin{figure}
458   \centering
459   \subfigure[Quadrants 1 et 4]{\includegraphics[width=7cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/coeffs-pixels2.png}}\quad
460   \subfigure[Quadrants 2 et 3]{\includegraphics[width=7cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/coeffs-pixels1.png}}\\
461   \subfigure{\includegraphics[width=8cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/coeffs-pixels3.png}}
462   \caption{Détermination des coefficients $C(i,j)$ des pixels du contour.}
463 \label{fig-segment-k<1}
464 \end{figure}
465
466 Les accès en mémoire aux contributions des pixels de coefficient $C(i,j)=0$, dans les images cumulées, sont évités et une contribution nulle leur est automatiquement attribuée dès l'étape de discrétisation au sein du kernel \texttt{GPU\_compute\_segments\_contribs()}.
467
468
469 \subsection{Performances}
470 Dans l'hypothèse la plus contraignante d'images en niveaux de gris codés sur 16 bits, l'implémentation parallèle que nous venons de décrire utilise de manière permanente 20 octets par pixel de l'image d'entrée, qui se détaillent en
471 \begin{itemize}
472 \item l'image d'entrée pour 4 octets par pixel (1 entier).
473 \item l'image cumulée $S_x$ pour 8 octets par pixel (1 entier long)
474 \item l'image cumulée $S_x^2$ pour 8 octets par pixel (1 entier long)
475 \end{itemize}
476 auxquels il faut ajouter un maximum d'environ 50~Mo d'espace nécessaire à la mémorisation des variables temporaires des calculs et données diverses comme le contour lui-même (n\oe uds, milieux, Freemans, etc.).   
477
478 Sur un GPU de type C1060 disposant de 3~Go de mémoire, cela permet de traiter des images jusqu'à presque 150 millions de pixels.
479 Il est possible de réduire cette empreinte jusqu'à 13 octets par pixel, mais cela soulève la question de l'alignement des données en mémoire, sans objet si on emploie les types entier et entier long (32 et 64 bits) pour la représentation des données et qui permet de préserver les performances maximales des opérations et des accès aux données du GPU. On pourrait tout de même porter ainsi la limite de taille de l'image d'entrée à 230 millions de pixels.
480
481 La convergence de notre implémentation intervient en un nombre généralement plus réduit d'itérations vers un contour final qui diffère par essence de celui obtenu avec la solution de référence. Ces effets sont la conséquence déjà abordée de l'heuristique d'optimisation appliquée à l'implémentation parallèle qui conduit à la création de certains segments non validés au préalable (voir fig. \ref{fig-cycle-contribs-segments}).
482
483 Les comparaisons visuelles et de valeur du critère $GL$ qui peuvent être faites pour les images de taille inférieure à 4096$\times$4096 pixels nous renseignent toutefois sur la qualité de la segmentation obtenue. Pour les tailles au delà et jusqu'au maximum de 12000$\times$12000 pixels, le comportement est globalement conservé, mais on note qu'il n'est pas pertinent de permettre des tailles de segments trop petites vis-à-vis de la taille d'image. Les déplacements des n\oe uds ne générent alors plus de variations significatives des contributions correspondantes.
484
485 La figure \ref{fig-snakegpu-result} présente une segmentation effectuée sur une image de 100 millions de pixels. La table \ref{tab-snake-results} résume les performances obtenues pour différentes tailles de la même image.
486 \begin{table}[h]
487   \centering
488   \begin{tabular}{rrrrr}
489       \toprule
490       &&\multicolumn{3}{c}{Performances}\\
491       \cmidrule(r){3-5}
492       && CPU & GPU & CPU/GPU \\
493       \midrule
494                      & {\bf total}      &{\bf 0,51 s}&{\bf 0,06 s}&{\bf x8,5}\\
495       Image 15~MP    & images cumulées  &0,13 s&0,02 s&x6,5\\
496                      & segmentation     &0,46 s&0,04 s&x11,5\\
497       \midrule
498                      & {\bf total}      &{\bf 4,08 s}&{\bf 0,59 s}&{\bf x6,9}\\
499       Image 100~MP   & images cumulées  &0,91 s&0,13 s&x6,9\\
500                      & segmentation     &3,17 s&0,46 s&x6,9\\
501       \midrule
502                      & {\bf total}      &{\bf 5,70 s}&{\bf 0,79 s}&{\bf x7,2}\\
503       Image 150~MP   & images cumulées  &1,40 s&0,20 s&x7,0\\
504                      & segmentation     &4,30 s&0,59 s&x7,3\\
505
506       \bottomrule
507 \end{tabular}
508    \caption{Comparaison des temps d'exécution de l'implémentation GPU (C2070) par rapport à l'implémentation CPU de référence, appliqués à une même image dilatée (fig. \ref{fig-snakecpu-cochon512}) pour en adapter la taille.}
509       \label{tab-snake-results}
510 \end{table} 
511
512 \begin{figure}
513   \centering
514   {\includegraphics[height=5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/cochon_it5_points.png}}
515 %\quad
516 %\subfigure[3 itérations en 0,35~s]{\includegraphics[height=5cm]{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/Montserrat_3it.png}}
517   \caption{Segmentations d'une image de 100~MP en 0,59~s pour 5 itérations. Le contour initial conserve les proportions de celui de la figure \ref{fig-snakecpu-cochon512}. }
518 \label{fig-snakegpu-result}
519 \end{figure}
520
521 \subsection{Détermination intelligente du contour initial}
522 Nous avons déjà discuté de l'influence du contour initial sur le résultat de la segmentation, mais il faut ajouter que la durée d'exécution est aussi impactée par ce choix, dans des proportions qui peuvent être importantes selon la distance, la taille et dans une moindre mesure la forme de la cible.
523
524 Ces effets se mesurent lors de la première itération, celle qui va cerner grossièrement la cible avec un polygone à quatre cotés. Si le contour initial se trouve très éloigné, comme dans la situation de la figure \ref{fig-snakecpu-cochon4kc3}, notre choix maintenant habituel d'un rectangle près des bords de l'image s'avère peu adapté et conduit à une première itération très longue. Dans un tel cas, pour une image de 10000$\times$10000 pixels, si la cible est un carré de 1000$\times$1000 pixels dont le sommet du bas à droite se confond avec celui du contour et que l'on approche par pas de 64 pixels, on devra dans le meilleur des cas déplacer les 4 n\oe uds du contour 110 fois de suite avant de pouvoir passer à la deuxième itération. Un pas de 128 permet de réduire ces valeurs, mais l'expérience montre qu'au delà, l'approche initiale de la cible est trop grossière et les itérations suivantes en pâtissent pour un résultat souvent dégradé.
525 En revanche, si les proportions sont celles de la figure \ref{fig-snakecpu-cochon512}, seules 31 passes de déplacement des 4 n\oe uds initiaux sont nécessaires.
526
527 Pour optimiser l'initialisation, nous avons donc proposé de tirer parti du GPU pour évaluer une grande quantité de contours initiaux rectangulaires et réduire ainsi le coût de la première itération. Pour pouvoir employer la mémoire partagée comme tampon de données, il faut limiter le nombre de contours à évaluer. Nous avons donc effectué un échantillonnage spatial des images et déterminé le contour initial en deux temps, en mettant à profit la propriété qu'ont les segments horizontaux d'avoir une contribution nulle, comme on peut le vérifier en se reportant à la figure \ref{fig-freeman} et à la table \ref{tab-freeman}. Le principe mis en \oe uvre, illustré par la figure \ref{fig-smart-init} est le suivant :
528 \begin{enumerate}
529 \item on réalise un échantillonnage horizontal pour ne considérer que les colonnes d'indice $j=8k$.
530 \item on évalue alors tous les contours rectangulaires de diagonale $(0, j_L)-(J_H, H)$
531 \item on identifie le contour présentant le meilleur critère $GL$, ce qui détermine $j_L$ et $j_H$.
532 \item on fait de même en échantillonnant verticalement : les lignes d'indice $i=8t$ permettent de décrire tous les contours de diagonale $(i_L, j_L)-(i_H, j_H)$. Le meilleur contour est celui retenu pour l'initialisation de la segmentatation.  
533 \end{enumerate}
534
535 Le gain de  performance apporté par cette initialisation \og intelligente \fg{} est naturellement très variable selon la cible, mais dans des situations favorables comme celle de l'image de la figure \ref{fig-snakecpu-cochon4kc3}, on parvient à une accélération proche de 15 alors qu'elle n'est que d'environ 7 avec l'initialisation basique. Cette proportion est conservée pour les tailles supérieures et signifie que la phase de segmentation est tout de même effectuée 30 fois plus rapidement qu'avec l'implémentation CPU, grâce à une première itération optimisée.  
536
537 \begin{figure}
538   \centering
539   \subfigure[Détermination de $j_L$ et $j_H$.]{\resizebox{6cm}{!}{\input{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/smart_init1.pdf_t}}}\quad
540  \subfigure[Détermination de $i_L$ et $i_H$.]{\resizebox{6cm}{!}{\input{/home/zulu/Documents/these_gilles/THESE/Chapters/chapter3/img/smart_init2.pdf_t}}}
541 \label{fig-smart-init}
542   \caption{Détermination intelligente du contour initial en deux phases successives. a) La première étape repose sur un échantillonnage horizontal. b) La seconde étape repose sur un échantillonnage vertical.}
543 \end{figure}
544  
545 \subsection{Conclusion}
546 Nous avons conçu une implémentation parallèle de \textit{snake} polygonal orienté régions, ce qui n'avait pas encore été réalisé, n'ayant recensé à ce jour aucune publication y faisant référence. Elle a fait l'objet d'une publication et d'une communication à la conférence \textit{Computer and Information Technology} (voir \cite{6036776}).
547 Les objectifs étaient d'étendre les capacités de traitement de l'implémentation CPU de référence en terme de taille d'image en conservant des temps d'exécution acceptables ce qui, de l'avis des auteurs de la version CPU, impose de se situer \textit{a minima} sous la seconde pour pouvoir envisager l'intégration dans une application interactive.
548
549 Sur ce point, les performances de notre version sont satisfaisantes, puisque nous avons repoussé la limite de taille de 16 à 150 millions de pixels et parvenons à segmenter ces grandes images en moins d'une seconde. Le temps de calcul dépend très fortement du contenu de l'image et la segmentation est le plus souvent obtenue en un temps plus court.
550
551 L'emploi du GPU dans notre implémentation ne parvient pas à être optimal car, par essence, la répartition des pixels d'intérêt est mouvante et ne permet pas de construire des accès mémoire coalescents. Les opérations de type réduction sont également  nombreuses et ne sont pas les plus efficaces sur GPU. Dans notre situation, elles peuvent même représenter une perte de performances, car effectuées sur des vecteurs de tailles insuffisantes.  
552
553 S'il s'agit de parler d'accélération, notre implémentation divise les temps de traitement précédents par un facteur allant de 6 à 15 selon l'image et le contour initial adopté. Rappelons encore que l'implémentation CPU de référence n'est pas une implémentation naïve, mais une solution optimisée employant déjà les capacités de parallélisme des microprocesseurs modernes et affichant les performances les plus élevées dans ce domaine ; il n'était pas trivial d'en surpasser les performances, même avec un GPU.     
554
555 Par nécessité, notre solution s'écarte cependant quelque peu de l'algorithme original pour permettre les déplacements simultanés de l'ensemble des sommets du polygone. Ce faisant, la décroissance du critère n'est pas certaine à toutes les étapes de la segmentation et l'on observe cette conséquence, en particulier lors des dernière itérations lorsque le pas de déplacement ainsi que les variations du critère sont faibles. Ce comportement  provoque parfois la convergence prématurée de la segmentation, mais n'influe toutefois que sur quelques n\oe uds et le contour ainsi obtenu ne s'éloigne que très peu du contour obtenu par l'algorithme de référence.
556
557 La technique que nous avons proposée pour la détermination intelligente du contour initial permet d'augmenter encore les performances, surtout dans les grandes images lorsque la cible est petite vis-à-vis des dimensions de l'image. Il reste toutefois à concevoir une technique permettant de prévoir si cette recherche intelligente serait génératrice de gain de performance. 
558
559 L'analyse fine des séquences de segmentation montre enfin que les première étapes, qui mettent en \oe uvre les segments les plus longs, génèrent des grilles de calcul suffisamment chargées et homogènes pour présenter de bonnes performances. Les dernières étapes, en revanche, traitent un grand nombre de petits segments, générant beaucoup de trous dans la grille de calcul et induisant des performances moindres. 
560
561 Pour résumer, l'accélération globale obtenue est principalement déterminée par le calcul des images cumulées et des toutes premières étapes de déplacements. Une possibilité à explorer serait de construire une version hybride réalisant le début de la segmentation sur GPU, puis la terminant sur le CPU hôte. Ceci est envisageable en raison du très petit volume de données à transférer que constituent les paramètres du contour (2~ko pour 100 n\oe uds).
562
563
564  
565
566
567
568
569
570
571
572