From: Greg Burri Date: Sun, 7 Jun 2015 08:03:06 +0000 (+0200) Subject: Avancement rapport + mesures. X-Git-Url: https://git.euphorik.ch/?a=commitdiff_plain;h=16eb554b60c2c029a8c6c8ac8722c36abb163216;p=malaria.git Avancement rapport + mesures. --- diff --git a/rapport/main.tex b/rapport/main.tex index 59d88bd..5658794 100644 --- a/rapport/main.tex +++ b/rapport/main.tex @@ -22,6 +22,8 @@ \usepackage{epstopdf} \usepackage{fancyhdr} \usepackage{courier} +\usepackage{pifont} +\usepackage{csquotes} \lstset{basicstyle=\footnotesize\ttfamily,breaklines=true} \lstset{framextopmargin=50pt,frame=bottomline} @@ -43,19 +45,15 @@ %emph=[2]{word1,word2}, emphstyle=[2]{style}, } -% \title{Détection de cellules sanguines infectées par la malaria} -% \author{G.Burri} +\renewcommand{\appendixtocname}{Annexes} \pagestyle{fancy} \fancyfoot[CE,CO]{} \fancyfoot[LE,RO]{\thepage} - \begin{document} -\nocite{*} - -% \maketitle +\nocite{*} % Pour inclure toute la bibliographie (et pas seulement celle citée). \begin{titlepage} \begin{center} @@ -136,8 +134,7 @@ Lausanne, le \begin{center} \textbf{Résumé} \end{center} -bla bla bla - +Cet article a pour but la description et la réalisation d'un méthode automatique permettant le calcul de la parasitémie de sang infecté par la malaria à partir d'images agrandies cent fois. \vspace{5cm} @@ -145,7 +142,7 @@ bla bla bla \begin{center} \textbf{Mots-clefs} \end{center} -"malaria", "paludisme" +\og{}malaria\fg{}, \og{}paludisme\fg{}, \og{}opérations morphologiques\fg{}, \og{}taux d'infection\fg{} \newpage @@ -159,28 +156,34 @@ bla bla bla \section{Introduction} -Le but de ce projet est de dénombrer de manière automatisée les globules rouges infectés par la malaria. Le nombre total de globules rouges est également recensé afin de pouvoir établir un taux d'infection. Cela est réalisé à partir d'images microscopiques fournies par Dr. Guy Prod'hom du laboratoire de parasitologie du CHUV à Lausanne. +Le but de ce projet est de dénombrer de manière automatisée les globules rouges infectés par la malaria. Le nombre total de globules rouges est également recensé afin de pouvoir établir un taux d'infection. Cela est réalisé à partir d'images microscopiques fournies par Dr. Guy Prod'hom du laboratoire de parasitologie du \emph{CHUV} à Lausanne. + +Pour ce faire, nous nous basons sur l'article \emph{Analysis of infected blood cell images using morphological operators}\cite{DiRuberto-Analysis-infected-blood-morphological} qui décrit une approche principalement basée sur des opérations morphologiques afin d'établir la parasitémie d'images d'échantillons de sang infecté par la malaria. -Pour ce faire, nous nous basons sur l'article~\cite{DiRuberto-Analysis-infected-blood-morphological} qui décrit une approche principalement basée sur des opérations morphologiques afin d'établir la parasitémie d'images d'échantillons de sang infecté par la malaria. +L'ensemble du processus décrit ci après est implémenté sous \emph{MATLAB}, le code est fournit en annexe. Des résultats de sa mise en oeuvre sur plusieurs images de sang infecté sont présentés en fin d'article. + + : expliquer est montrer à quoi ressemble un parasite \section{Approche générale} -L'image de départ correspond à une photographie de sang infecté agrandit 100 fois. Un colorant est utilisé afin de faire resortir les parasites avec une teinte spéciale. +Le processus complet est montré par la figure \ref{fig:processusComplet}. Les différents chiffres correspondent aux étapes décrites ci après. + +L'image de départ correspond à une photographie de sang infecté agrandie 100 fois. Un colorant est utilisé afin de faire ressortir les parasites avec une teinte particulière. -La première étape consiste à détecter les éléments colorés comprenant les parasites, les globules blanc et les plaquettes. Les parasites peuvent se trouver à l'extérieure de cellules, par exemple groupés sous la forme de schizontes. +La première étape (\ding{192}) consiste à détecter les éléments colorés comprenant les parasites, les globules blanc et les plaquettes. Les parasites peuvent se trouver à l'extérieure de cellules, par exemple groupés sous la forme de schizontes. -La deuxième et troisième étape consiste à chercher et à extraire les globules blancs, respectivement les schizontes. +Les deuxième et troisième étapes (\ding{193} et \ding{194}) consistent à chercher et à extraire les globules blancs, respectivement les schizontes. -La quatrième étape va segmenter les globules rouges. +La quatrième étape (\ding{195}) va segmenter les globules rouges. -La cinquième étape consiste à reconstruire les globules rouges parasités à partir des parasites marqués de la première étape et de la ségmentation de la quatrième étape. +La cinquième étape (\ding{196}) consiste à reconstruire les globules rouges parasités à partir des parasites marqués de la première étape (\ding{192}) et de la ségmentation de la quatrième étape (\ding{195}) . La sixième et dernière étape va compter le nombre de globules rouges total et de globules rouges infectés afin de calculer le rapport entre ces deux valeurs. -\begin{figure} +\begin{figure}[htbp] \centering \includegraphics[width=1\linewidth]{img/schema_processus.eps} - \caption{Processus complet} + \caption{Le processus complet.} \label{fig:processusComplet} \end{figure} @@ -191,7 +194,9 @@ La sixième et dernière étape va compter le nombre de globules rouges total et \section{Détail du processus} -Le processus est actuellement réalisé sur des images réduites de 40 \% à l'aide d'une interpolation bicubique par rapport à la taille originale afin d'augmenter la vitesse de l'ensemble du processus. La taille des images traitées après réduction est de 1167 x 1556. Voir la figure \ref{fig:imgRGB}. +Le processus est actuellement réalisé sur des images réduites de 40 \% à l'aide d'une interpolation bicubique par rapport à la taille originale afin d'augmenter la vitesse de l'ensemble du processus. La taille des images traitées après réduction est de 1167 x 1556. Voir la figure \ref{fig:imgRGB}. + +La qualité du résultat final n'est pas beaucoup affectée par diminution de la taille des images, sur l'ensemble des douze images présentées à la section~\ref{}, nous obtenons un taux d'erreur moyen de 17.9~\% pour les images originales contre 18.2~\% pour les images réduites. Le taux mesuré de référence est de 16.9 \%. Le temps moyen est de 485~s pour les images originales contre 78~s pour les images réduites. \begin{figure}[htbp] \centering @@ -200,20 +205,20 @@ Le processus est actuellement réalisé sur des images réduites de 40 \% à l'a \label{fig:imgRGB} \end{figure} -Tout au long de cette description nous lions certain résultats d'opération à des noms de variables issue du code \emph{MATLAB} (fourni en annexe). Ceci afin de pouvoir facilement retrouver le détail de ces opérations dans le code et de facilité le suivit de l'enchaînement des opérations. +Tout au long de cette description nous lions certains résultats d'opération à des noms de variables issue du code \emph{MATLAB} (fourni en annexe). Ceci afin de pouvoir facilement retrouver le détail de ces opérations dans le code et de facilité le suivit de l'enchaînement des opérations. Caractériser les images en entrée + mettre une image en figure (sachant que l'ensemble du processus y est très sensible). - faire des mesures précises sur des images plus grandes afin de voir s'il y a beaucoup d'imprécisions liées à la réduction. + faire des mesures précises sur des images plus grandes afin de voir s'il y a beaucoup d'imprécisions liées à la réduction, également mettre en évidence le temps de calcul ? \subsection{Détection des parasites} -Cette opération a pour but de marquer les éléments colorés qui correspondent aux parasites, schizontes et globules blancs. Nous utilisons ici les composantes "teinte" et "saturation" de l'image. +Cette opération a pour but de marquer les éléments colorés qui correspondent aux parasites, schizontes et globules blancs. Nous utilisons ici les composantes \emph{teinte} et \emph{saturation} de l'image. \subsubsection{Filtrage préliminaire} -Il est nécessaire pour la suite du traitement d'avoir les intensités les plus élevés pour les éléments coloriés pour les deux composantes teinte et valeur. Pour se faire nous allons utiliser le négatif des deux composantes. De plus la composante teinte est translatée de $100 / 255$ afin que les différents éléments de l'image, à savoir le fond, les cellules et les parties colorées, n'aient pas des valeurs extremes, voir l'histogramme montré par la figure \ref{fig:histogram_imgH}. $imgRGB \rightarrow (imgH, imgS)$. +Il est nécessaire pour la suite du traitement d'avoir les intensités les plus élevés pour les éléments colorés pour les deux composantes teinte et valeur. Pour se faire nous allons utiliser le négatif des deux composantes. De plus la composante \emph{teinte} est translatée de $100 / 255$ (141°) afin que les différents éléments de l'image, à savoir le fond, les cellules et les parties colorées, ne soient pas proche des valeurs extremes, voir l'histogramme montré par la figure \ref{fig:histogram_imgH}. $imgRGB \rightarrow (imgH, imgS)$. -Les histogrammes des deux images $imgH$ et $imgS$ sont montrés par les figures \ref{fig:histogram_imgH} et \ref{fig:histogram_imgS}. L'histogramme de la teinte montre un premier sommet vers 80 correspondant au fond, un deuxième sommet vers 88 correspondant aux cellules., les éléments colorés se trouvent aux environs de 120. L'histogramme de la saturation montre un premier sommet vers 128 correspondant aux cellules, un deuxième sommet vers 150 correspondant au fond, les éléments colorés se trouvent aux environs de 200. +Les histogrammes des deux images $imgH$ et $imgS$ sont montrés par les figures \ref{fig:histogram_imgH} et \ref{fig:histogram_imgS}. L'histogramme de la teinte montre un premier sommet vers 80 correspondant au fond et un deuxième sommet vers 88 correspondant aux cellules, les éléments colorés se trouvent aux environs de 120. L'histogramme de la saturation montre un premier sommet vers 128 correspondant aux cellules et un deuxième sommet vers 150 correspondant au fond, les éléments colorés se trouvent aux environs de 200. \begin{figure}[htbp] \centering @@ -248,7 +253,7 @@ Afin de gommer le bruit et de rendre le fond plus homogène et plus sombre, un f \subsubsection{Granulométrie} -L'objectif ici est de déterminer le rayon moyenne des globules rouges (souvent abrégé \emph{RBC} pour \emph{red blood cell}). Pour ce faire nous allons réaliser une succession de fermetures à l'aide d'un élément structurant de forme octogonale sur la composante \emph{saturation} ($imgFiltered\{2\}$). Nous évitons ici un élément structurant ayant la forme d'un disque pour des raisons de performance, une fermeture avec ce dernier demandant d'effectuer beaucoup plus de calculs. Une ouverture par aire de 1000 est appliquée au préalable sur la composante \emph{saturation} afin de niveler les cellules. +L'objectif ici est de déterminer le rayon moyenne des globules rouges (souvent abrégé \emph{RBC} pour \emph{red blood cell}). Pour ce faire, nous réalisons une succession de fermetures à l'aide d'un élément structurant de forme octogonale sur la composante \emph{saturation} ($imgFiltered\{2\}$). Nous évitons ici un élément structurant ayant la forme d'un disque pour des raisons de performance, une fermeture avec ce dernier demandant d'effectuer beaucoup plus de calculs. Une ouverture par aire de 1000 est appliquée au préalable sur la composante \emph{saturation} afin de niveler l'intensité des cellules. \begin{sloppypar} % Pour éviter que certaines formules inline débordent dans la marge. Nous partons d'un rayon initial de un pour arriver à un rayon maximal de $width(imgRGB) / 35$. La rayon maximal est définit en fonction de la largeur de l'image ce qui permet de ne pas être dépendant de sa résolution. À chaque itération une fermeture est effectuée suivi du calcul du volume relatif de l'image : $volume(imgClosed) \rightarrow A, 1 - A / volImg \rightarrow N$ où $volImg$ est le volume de $imgFiltered\{2\}$. La différence de volume est calculé pour chaque itération $i$ comme suit : $N_{i + 1} - N_i$. @@ -307,11 +312,11 @@ Nous allons ensuite seuiller les deux composantes puis combiner les résultats a {\mu}S &= \frac{\displaystyle\sum_{p \in MHS} S(p)}{sum(MHS)} & où~S &= imgFiltered\{2\} & \end{flalign} -La teinte et la saturation sont seuillés à l'aide ${\mu}H$, respectivement ${\mu}S$. Nous obtenons alors les images $TH$ et $TS$ que nous combinons comme suit pour obtenir $THS$. La figure \ref{fig:THS} montre $THS$ en blanc imprimé sur l'image d'entrée. +La teinte et la saturation sont seuillés à l'aide ${\mu}H$, respectivement ${\mu}S$. Nous obtenons alors les images $TH$ et $TS$ que nous combinons comme suit pour obtenir $THS$. L'ouverture par aire permet d'éliminer les toutes petites traces qui représentent dans la majorité des cas du bruit. La figure \ref{fig:THS} montre $THS$ en blanc imprimé sur l'image d'entrée. {\setlength{\abovedisplayskip}{0pt} \begin{flalign} - THS &= TH \cap TS & + THS &= areaOpen(TH \cap TS, 10) & \end{flalign} \begin{figure}[htbp] @@ -324,7 +329,7 @@ La teinte et la saturation sont seuillés à l'aide ${\mu}H$, respectivement ${\ \subsection{Détection des globules blancs} -Cette opération utilise l'image de marquage des éléments colorés ($THS$) ainsi que la taille des globules rouges ($RBCRadius$). Les globules blancs sont tout d'abord marqués à l'aide d'une érosion utilisant un élément structurant octogonal dont la taille correspond à 50 \% de celle des globules rouges ($0.5 * RBCRadius$). Puis reconstruction par dilatation des éléments colorés en utilisant le marqueur précédent permet de ne garder que les globules blanc. +Cette opération utilise l'image de marquage des éléments colorés ($THS$) ainsi que la taille des globules rouges ($RBCRadius$). Les globules blancs sont tout d'abord marqués à l'aide d'une érosion utilisant un élément structurant octogonal dont la taille correspond à 50 \% de celle des globules rouges ($0.5 \cdot RBCRadius$). Puis reconstruction par dilatation des éléments colorés en utilisant le marqueur précédent permet de ne garder que les globules blanc. Finalement l'on lisse le résultat en réalisant une fermeture suivit d'une ouverture afin de fermer les petits trous qu'il pourrait rester. Le résultat est appelé $WBC$ et est montré par la figure \ref{fig:WBC}. @@ -338,7 +343,7 @@ Finalement l'on lisse le résultat en réalisant une fermeture suivit d'une ouve \subsection{Détection des schizontes} -Pour détecter les schizontes, nous utilisons l'image de marquage des éléments colorés ($THS$) ainsi que la taille des globules rouges ($RBCRadius$). Nous allons ici plus loin que l'article et créons un graphe non-orienté dont les arcs représentent une \emph{distance de Hausdorff} égale ou plus petite au diamètre des globules rouges entre deux sous-ensembles. Nous cherchons alors les sous-ensembles connexes de plus de $n$ éléments et les marquons comme étant des schizontes. Dans notre cas $n$ est égal à 6. Il est important de ne pas utiliser un nombre trop bas car des cellules poly-infectées pourraient être considérées comme des schizontes, la figure \ref{fig:poly-et-schizonte} illustre cela. +Pour détecter les schizontes, nous utilisons l'image de marquage des éléments colorés ($THS$) ainsi que la taille des globules rouges ($RBCRadius$). Nous allons ici plus loin que l'article et créons un graphe non-orienté dont les arcs représentent une \emph{distance de Hausdorff} égale ou plus petite à 1.3 fois le rayon des globules rouges entre deux sous-ensembles. Nous cherchons alors les sous-ensembles connexes de plus de $n$ éléments et les marquons comme étant des schizontes. Dans notre cas $n$ est égal à 6. Il est important de ne pas utiliser un nombre trop bas car des cellules poly-infectées pourraient être considérées comme des schizontes, la figure \ref{fig:poly-et-schizonte} illustre cela. \begin{figure}[htbp] \centering @@ -359,41 +364,182 @@ Pour détecter les schizontes, nous utilisons l'image de marquage des éléments Pour savoir si deux sous-ensembles, $A$ et $B$, sont à une \emph{distance de Hausdorff} égale ou plus petite que $k$, il faut que $B \subseteq A \oplus SE_k$ et que $A \subseteq B \oplus SE_k$ où $SE_k$ est un élément structurant ayant la forme d'un disque de diamètre $k$. +\subsection{Segmentation des globules rouges} + +Le but ici est de séparer les globules rouges, nous utilisons l'image initiale ($imgRGB$), le rayon des globules rouges ($RBCRadius$), les globules blancs ($WBC$) et les schizontes ($schizonts$). Pour ce faire nous utilisons dans un premier temps une segmentation par \emph{watersheds} à l'aide d'une transformée de distance. Nous séparons ensuite les segments en deux groupes, un groupe dont chaque élément représente une unique cellule et un autre groupe dont chaque élément représente un composé de cellules. Les cellules des éléments composés sont ensuite séparées, le résultat est unies aux cellules uniques et les éléments touchant les bords sont enlevé afin d'obtenir le résultat final. + +Durant le processus, nous avons besoin de définir le rayon minimum d'un globule rouge ainsi que son aire. Le rayon minimum est définit comme étant 75~\% du rayon nominal comme montré ci dessous. + +{\setlength{\abovedisplayskip}{0pt} +\begin{flalign} + RBCRadiusMin &= \lfloor 0.75 \cdot RBCRadius \rfloor &\\ + RBCRadiusMinArea &= \pi \cdot RBCRadiusMin^2 & +\end{flalign} + + +\subsubsection{Filtrage préliminaire} + +Nous utilisons ici la composantes verte de l'image à laquelle nous appliquons une ouverture par aire avec une zone de taille $RBCRadiusMinArea$. Les globules blancs ($WBC$) ainsi que les schizontes ($schizonts$) sont \og~soustraits~\fg de l'image, le résultat est nommé $grayFlat$ comme montré par la figure~\ref{fig:segmentation-grayflat}. + +{\setlength{\abovedisplayskip}{0pt} +\begin{flalign} + grayFlat &= areaOpen(green(imgRGB)) \setminus WBC \setminus schizonts +\end{flalign} + +La figure~\ref{fig:segmentation-histogramme-grayflat} montre l'histogramme de $grayFlat$. Un \emph{seuillage d'Otsu} lui est appliqué suivit d'une ouverture par un disque de rayon $RBCRadiusMin$ afin d'adoucir les contours et d'enlever tout les éléments plus petits qu'un globules rouge. + +\begin{figure}[htbp] + \centering + \includegraphics[width=0.8\linewidth]{img/segmentation_input_grayflat.jpg} + \caption{Composante verte dont l'intensité des cellules a été homogénéisée à l'aide d'une ouverture par aire, les globules blancs et les schizontes ont été peints en blanc ($grayFlat$)} + \label{fig:segmentation-grayflat} +\end{figure} + +\begin{figure}[htbp] + \centering + \includegraphics[width=0.8\linewidth]{img/histogram_segmentation_grayFlat.eps} + \caption{Histogramme de la composante verte de $imgRGB$ après l'application d'une ouverture par aire et de la \og{}suppression\fg{} des globules blancs et des schizontes} + \label{fig:segmentation-histogramme-grayflat} +\end{figure} + + +\subsubsection{Calcul des distances et \emph{watersheds}} + +Après le seuillage des globules rouge certaines régions comprennent deux ou plusieurs globules rouges qu'il faut séparer. Pour se faire nous appliquons une transformé de distance (\texttt{mmdist}) qui permet de donner une distance à chaque pixel blanc. Cette distance correspond à la plus courte distance euclidienne entre le pixel blanc et un pixel noir. Nous cherchons ensuite les maximaux régionaux de l'image de distance en utilisant un élément structurant octogonal dont le rayon est la moitié de $RBCRadius$. Une dilaté d'un disque de rayon $RBCRadius / 7$ est alors appliqué aux maximaux pour fusionner d'éventuels zones très proches. L'algorithme \emph{watersheds} est appliqué au négatif des distances en utilisant les maximaux dilatés comme marqueurs. Finalement l'intersection entre le négatif des \emph{watersheds} et de $thresholdOpened$ correspond au résultat. La figure~\ref{fig:segmentation-watershed} montre les étapes de ce processus. + +\begin{figure}[htbp] + \centering + \begin{subfigure}[t]{0.3\textwidth} + \includegraphics[width=\linewidth]{img/segmentation_watershed_1.jpg} + \caption{Image binaire initiale ($thresholdOpened$)} + \label{fig:segmentation-watershed-1} + \end{subfigure} + ~ + \begin{subfigure}[t]{0.3\textwidth} + \includegraphics[width=\linewidth]{img/segmentation_watershed_2.jpg} + \caption{Superposition de trois images : les distances, les maximaux et sa dilaté} + \label{fig:segmentation-watershed-3} + \end{subfigure} + ~ + \begin{subfigure}[t]{0.3\textwidth} + \includegraphics[width=\linewidth]{img/segmentation_watershed_3.jpg} + \caption{Image après découpe de l'image initiale avec le résultat de l'algorithme \emph{watersheds}} + \label{fig:segmentation-watershed-3} + \end{subfigure} + \caption{Wastershed par transformé de distance} + \label{fig:segmentation-watershed} +\end{figure} + + + + +%\begin{framed} +%$i$ : l'image seuillée +%\begin{enumerate} +% \item d <- +%\end{enumerate} +%\end{framed} + +\subsubsection{Identification et traitement des cellules composés} + +Afin d'identifier les zones composées de plusieurs cellules nous calculons le rapport grand-axe / petit-axe pour chacune des zones, si le rapport d'une zone est plus grand ou égal à 1.3 alors la zone est définit comme étant composée. -\subsection{Ségmentation des globules rouges} +La figure~\ref{fig:segmentation-composites} montre le processus de séparation d'une zone composée de plusieurs cellules, ce processus est décrit ci après. -\subsubsection{Calcul des Distances et Watersheds} +Une ouverture à l'aide d'un disque de rayon $RBCRadiusMin$ est appliquée à $gray$ en utilisant les zones composées comme masque. Le résultat est montrée par la figure \ref{fig:segmentation-composites-3}. Un gradient morphologique suivit d'une binarisation est réaliser pour obtenir l'image de la figure \ref{fig:segmentation-composites-4}. Les trous d'une aire plus petite ou égale à la moitié de $RBCRadiusMinArea$ sont remplis avec une ouverture par aire puis un amincissement est appliqué. Nous remplissons ensuite les trous de l'amincissement est mettons à zéro les pixels marqués par l'amincissement. Nous érodons le résultat afin d'éviter que les cellules composées ne touchent les cellules uniques lors de l'union qui aura lieu après. Finalement une ouverture par aire de valeur $RBCRadiusMinArea / 5$ est effectué pour enlever d'éventuels artefacts comme montré par la figure~\ref{fig:soustraction-thinning}. -Après le seuillage des globules rouge certaines régions comprenent deux ou plusieurs globules rouges qu'il faut séparer. L'article propose une calssification en fonction du rapport grand axe/petit axe de ces zones, les zones dont le rapport est supperieur à 1.3 sont traitées comme des amas de cellules. +% L'exemple provient de 1401063467-0007-schizonte.png (x: 900, y: 50). +\begin{figure}[htbp] + \centering + \begin{subfigure}[t]{0.3\textwidth} + \includegraphics[width=\linewidth]{img/segmentation_composites_1.jpg} + \caption{Composante verte ($gray$)} + \label{fig:segmentation-composites-1} + \end{subfigure} + + \begin{subfigure}[t]{0.3\textwidth} + \includegraphics[width=\linewidth]{img/segmentation_composites_2.jpg} + \caption{Image binaire des éléments composés, obtenue à partir de $grayFlat$} + \label{fig:segmentation-composites-2} + \end{subfigure} + ~ + \begin{subfigure}[t]{0.3\textwidth} + \includegraphics[width=\linewidth]{img/segmentation_composites_3.jpg} + \caption{Ouverture de \emph{(a)} en utilisant \emph{(b)} comme masque} + \label{fig:segmentation-composites-3} + \end{subfigure} + ~ + \begin{subfigure}[t]{0.3\textwidth} + \includegraphics[width=\linewidth]{img/segmentation_composites_4.jpg} + \caption{Gradient morphologique} + \label{fig:segmentation-composites-4} + \end{subfigure} + + \begin{subfigure}[t]{0.3\textwidth} + \includegraphics[width=\linewidth]{img/segmentation_composites_5.jpg} + \caption{Remplissage des trous} + \label{fig:segmentation-composites-5} + \end{subfigure} + ~ + \begin{subfigure}[t]{0.3\textwidth} + \includegraphics[width=\linewidth]{img/segmentation_composites_6.jpg} + \caption{Amincissement} + \label{fig:segmentation-composites-6} + \end{subfigure} + ~ + \begin{subfigure}[t]{0.3\textwidth} + \includegraphics[width=\linewidth]{img/segmentation_composites_7.jpg} + \caption{Fermeture par aire puis mise à zéro des pixels marqué par \emph{(f)}} + \label{fig:segmentation-composites-7} + \end{subfigure} + \caption{Processus de séparation des cellules composées} + \label{fig:segmentation-composites} +\end{figure} -Dans un premier temps nous utilisons une approche plus simple, sans nécessité de classification des amas, basée sur une transformée de distance puis l'application de l'algorithme \emph{watersheds}. Ce processus est détaillé ci-après. +\begin{figure}[htbp] + \centering + \includegraphics[width=0.8\linewidth]{img/splitting_composites.jpg} + \caption{Résultat de l'amincissement soustrait de son remplissage. De petits artefacts, ici marqués en jaune, restent présents. S'ils ne sont pas enlevés, ceux ci seront comptés comme des cellules à part entière lors de la labellisation finale} + \label{fig:soustraction-thinning} +\end{figure} -\begin{framed} -$i$ : l'image seuillée -\begin{enumerate} - \item d <- -\end{enumerate} -\end{framed} +\subsubsection{Résultat final} + +Les deux résultats précédents, c'est à dire les cellules uniques et les zones composées de plusieurs cellules sont unies. Les zones touchant les bords sont supprimé afin d'obtenir les résultat final, son gradient imprimé sur l'image initiale est montré par la figure~\ref{fig:segmentation_finale}. Le résultat est nommé $redCells$. \begin{figure}[htbp] \centering - \includegraphics[width=\linewidth]{img/splitting_composites.jpg} - \caption{Application } - \label{fig:pouet} + \includegraphics[width=0.8\linewidth]{img/segmentation_finale.jpg} + \caption{Le gradient du résultat de la segmentation des globules rouges imprimé sur l'image initiale ($redCells$)} + \label{fig:segmentation_finale} \end{figure} \subsection{Identification des globules rouges infectés} - +À partir du résultat de la segmentation des globules rouges obtenue à la section~\ref{} et des éléments colorés obtenus à la section~\ref{} nous pouvons reconstruire les cellules infectés par une succession de dilatation des éléments colorés puis de masquage par les globules rouges. Ceci est réalisé à l'aide de la fonction \texttt{mminfrec}. Le résultat est nommé $infectedRedCells$. + +La figure~\ref{fig:globules_rouges_infectes} montre les globules rouges infectés. + +\begin{figure}[htbp] + \centering + \includegraphics[width=0.8\linewidth]{img/globules_rouges_infectes.jpg} + \caption{Le gradient des globules rouges imprimé sur l'image originale ($infectedRedCells$)} + \label{fig:globules_rouges_infectes} +\end{figure} + \subsection{Calcul du taux d'infection} +Les deux images $redCells$ et $infectedRedCells$ sont labellisées afin d'obtenir le nombre totale de globules rouges ($nbRedCells$) et le nombre de globules rouges infecté ($nbInfectedRedCells$). Le pourcentage d'infection est le rapport de ces deux valeurs : $100 \cdot nbInfectedRedCells / nbRedCells$. + + \section{Résultats} \subsection{Qualité des images en entrée} + \subsection{Méthode} @@ -403,109 +549,96 @@ Finalement, nous calculons les moyennes et les écarts types des erreurs, et des \subsection{Échantillon n°1412151257} -\subsubsection{\texttt{1412151257-0001}} - -\begin{itemize} - \item Taux de cellules infectées : 19.1~\% (22 / 115) - \item Temps de calcul : 89 secondes - \item Dénombrement des cellules saines à l'oeil : 94 - \item Dénombrement des cellules infectées à l'oeil : 22 - \item Taux de cellules infectées réel : 19.0~\% (22 / (94 + 22)) - \item Erreur : +0.9~\% -\end{itemize} - -\subsubsection{\texttt{1412151257-0004}} - -\begin{itemize} - \item Taux de cellules infectées : 12.6~\% (15/119) - \item Temps de calcul : 106 secondes - \item Dénombrement des cellules saines à l'oeil : 104 - \item Dénombrement des cellules infectées à l'oeil : 19 - \item Taux de cellules infectées réel : 15.4 \% (19 / (104 + 19)) - \item Erreur : -18.4~\% -\end{itemize} - -Cette erreur importante est liée à beaucoup de parasite manqué lors de la recherche des minimums locaux sur la composante saturation. - -\subsubsection{\texttt{1412151257-0007}} - -\begin{itemize} - \item Taux de cellules infectées : 23.1~\% (34/147) - \item Temps de calcul : 109 secondes - \item Dénombrement des cellules saines à l'oeil : 106 - \item Dénombrement des cellules infectées à l'oeil : 33 - \item Taux de cellules infectées réel : 23.7~\% (33 / (106 + 33)) - \item Erreur : -2.6~\% -\end{itemize} - -\subsubsection{\texttt{1412151257-0013}} - -\begin{itemize} - \item Taux de cellules infectées : 13.7~\% (22 / 161) - \item Temps de calcul : 90 secondes - \item Dénombrement des cellules saines à l'oeil : 128 - \item Dénombrement des cellules infectées à l'oeil : 22 - \item Taux de cellules infectées réel : 14.7~\% (22 / (128 + 22)) - \item Erreur : -6.8~\% -\end{itemize} - -\subsubsection{\texttt{1412151257-0014}} - -\begin{itemize} - \item Taux de cellules infectées : 13.2~\% (18 / 136) - \item Temps de calcul : 78 secondes - \item Dénombrement des cellules saines à l'oeil : 114 - \item Dénombrement des cellules infectées à l'oeil : 18 - \item Taux de cellules infectées réel : 13.6~\% (18 / (114 + 18)) - \item Erreur : -2.9~\% -\end{itemize} - -\subsubsection{Statistiques} - -\begin{itemize} - \item Taux de cellules infectées moyen détecté : 16.4~\% - \item Écart type sur le taux de cellules infectées moyen détecté : 4.6~\% - \item Taux de cellules infectées moyen réel : 17.3~\% - \item Écart type sur le taux de cellules infectées moyen réel : 4.1~\% - \item Erreur moyenne : -6.0~\% - \item Écart type sur l'erreur : 7.5~\% -\end{itemize} +Les tableaux \ref{tab:resultat60} et \ref{tab:resultat100} montrent les résultats du processus appliqué à des images réduite de 40~\%, respectivement à des images originales. + +\begin{table}[htbp] +\setlength{\tabcolsep}{3pt} +\centering +\begin{tabular}{ r || r | r | r || r | r | r || r || r} + & \multicolumn{3}{ c| }{Mesuré} & \multicolumn{3}{ c| }{Détecté} & & \\ \cline{2-8} + N° & Total & Infectés & Taux & Total & Infectés & Taux & Erreur & t~[s] \\ \cline{1-9} + 0013 & 150 & 22 & 14.7~\% & 153 & 23 & 15.0~\% & 2.5~\% & 68 \\ + 0014 & 132 & 18 & 13.6~\% & 131 & 18 & 13.7~\% & 0.8~\% & 65 \\ + 0036 & 133 & 20 & 15.0~\% & 135 & 21 & 15.6~\% & 3.4~\% & 70 \\ + 0038 & 116 & 19 & 16.4~\% & 117 & 19 & 16.2~\% & -0.9~\% & 68 \\ + 0039 & 136 & 26 & 19.1~\% & 137 & 27 & 19.7~\% & 3.1~\% & 79 \\ + 0040 & 128 & 19 & 14.8~\% & 128 & 18 & 14.1~\% & -5.3~\% & 78 \\ + 0041 & 120 & 26 & 21.7~\% & 121 & 27 & 22.3~\% & 3.0~\% & 89 \\ + 0042 & 135 & 26 & 19.3~\% & 144 & 30 & 20.8~\% & 8.2~\% & 82 \\ + 0043 & 128 & 20 & 15.6~\% & 132 & 27 & 20.5~\% & 30.9~\% & 80 \\ + 0044 & 133 & 25 & 18.8~\% & 135 & 25 & 18.5~\% & -1.5~\% & 86 \\ + 0045 & 136 & 22 & 16.2~\% & 136 & 28 & 20.6~\% & 27.3~\% & 84 \\ + 0046 & 127 & 29 & 22.8~\% & 128 & 27 & 21.1~\% & -7.6~\% & 85 +\end{tabular} +\caption{Résultats sur les images réduite de 40~\%} +\label{tab:resultats60} +\end{table} + + +\begin{table}[htbp] +\setlength{\tabcolsep}{3pt} +\centering +\begin{tabular}{ r || r | r | r || r | r | r || r || r} + & \multicolumn{3}{ c| }{Mesuré} & \multicolumn{3}{ c| }{Détecté} & & \\ \cline{2-8} + N° & Total & Infectés & Taux & Total & Infectés & Taux & Erreur & t~[s] \\ \cline{1-9} + 0013 & 150 & 22 & 14.7~\% & 153 & 22 & 14.4~\% & -2.0~\% & 429 \\ + 0014 & 132 & 18 & 13.6~\% & 136 & 19 & 14.0~\% & 2.5~\% & 462 \\ + 0036 & 133 & 20 & 15.0~\% & 135 & 21 & 15.6~\% & 3.4~\% & 431 \\ + 0038 & 116 & 19 & 16.4~\% & 118 & 19 & 16.1~\% & -1.7~\% & 447 \\ + 0039 & 136 & 26 & 19.1~\% & 136 & 27 & 19.9~\% & 3.8~\% & 463 \\ + 0040 & 128 & 19 & 14.8~\% & 131 & 18 & 13.7~\% & -7.4~\% & 458 \\ + 0041 & 120 & 26 & 21.7~\% & 122 & 27 & 22.1~\% & 2.1~\% & 526 \\ + 0042 & 135 & 26 & 19.3~\% & 142 & 27 & 19.0~\% & -1.3~\% & 483 \\ + 0043 & 128 & 20 & 15.6~\% & 128 & 25 & 19.5~\% & 25.0~\% & 515 \\ + 0044 & 133 & 25 & 18.8~\% & 137 & 25 & 18.2~\% & -2.9~\% & 505 \\ + 0045 & 136 & 22 & 16.2~\% & 144 & 30 & 20.8~\% & 28.8~\% & 538 \\ + 0046 & 127 & 29 & 22.8~\% & 126 & 27 & 21.4~\% & -6.2~\% & 557 +\end{tabular} +\caption{Résultats sur les images originales (non-réduites)} +\label{tab:resultats100} +\end{table} + +Le tableau ci dessous sont les résultat du processus appliqué aux images originales. -\begin{figure}[htbp] - \centering - %\includegraphics[width=\linewidth]{img/1412151257-Gamma-1-0001.png} - \caption{Image originale \texttt{1412151257-Gamma-1-0001.png}} - \label{fig:asd} -\end{figure} -\begin{figure}[htbp] - \centering - %\includegraphics[width=\linewidth]{img/1412151257-Gamma-1-0001---infected-cells.png} - \caption{Détection des globules rouges sains et parasités} - \label{fig:dsa} -\end{figure} + : Analyses les résultats \section{Conclusion} -Travaux futurs ? +Travaux futurs ? \bibliographystyle{plain} \bibliography{main} - +\newpage \begin{appendices} \section{Code MATLAB} \label{app:matlab} -\subsection{} - +\subsection{\texttt{Main.m}} \lstinputlisting{../src/Main.m} -\end{appendices} +\subsection{\texttt{DetectionOfParasites.m}} +\lstinputlisting{../src/DetectionOfParasites.m} + +\subsection{\texttt{DetectionOfWhiteCells.m}} +\lstinputlisting{../src/DetectionOfWhiteCells.m} + +\subsection{\texttt{DetectionOfSchizonts.m}} +\lstinputlisting{../src/DetectionOfSchizonts.m} +\subsection{\texttt{SegmentationOfRedCells.m}} +\lstinputlisting{../src/SegmentationOfRedCells.m} +\subsection{\texttt{WatershedsByDistanceTransform.m}} +\lstinputlisting{../src/WatershedsByDistanceTransform.m} +\subsection{\texttt{IdentificationOfInfectedRedCells.m}} +\lstinputlisting{../src/IdentificationOfInfectedRedCells.m} + + +\end{appendices} \end{document} diff --git "a/rapport/r\303\251sultats.ods" "b/rapport/r\303\251sultats.ods" index e8bb831..d17604d 100644 Binary files "a/rapport/r\303\251sultats.ods" and "b/rapport/r\303\251sultats.ods" differ diff --git a/src/DetectionOfParasites.m b/src/DetectionOfParasites.m index fca70a0..1c5f892 100644 --- a/src/DetectionOfParasites.m +++ b/src/DetectionOfParasites.m @@ -4,7 +4,7 @@ % Outputs: % THS: Parasites of all types and WBC (mask). % RBCRadius: Typical red cell radius [px]. -function [THS, RBCRadius, nucleiRadius] = DetectionOfParasites(imgRGB) +function [THS, RBCRadius, nucleiRadius] = DetectionOfParasites(imgRGB, invertSaturation) %% Extracting of H, S and V components imgHSV = rgb2hsv(imgRGB); @@ -16,6 +16,9 @@ function [THS, RBCRadius, nucleiRadius] = DetectionOfParasites(imgRGB) imwrite(imgH, '../output/imgH.png') imgS = uint8(255 * imgHSV(:, :, 2)); + if invertSaturation + imgS = 255 - imgS; + end imwrite(imgS, '../output/imgS.png') % We keep the value component even if we do not use it. @@ -78,17 +81,21 @@ function [THS, RBCRadius, nucleiRadius] = DetectionOfParasites(imgRGB) M{i} = mmregmax(imgFiltered{i}, regionalExtremumSE); end - nucleiSE = mmsedisk(nucleiRadius, '2D', 'OCTAGON'); + nucleiSE = mmsedisk(nucleiRadius, '2D', 'EUCLIDEAN'); MHS = mmdil(M{1}, nucleiSE) & mmdil(M{2}, nucleiSE); - volumeMHS = funVolume(MHS); + % Compute the mean intensity of pixel marked by MHS from + % hue (imgFiltered{1}) and saturation (imgFiltered{2}). + volumeMHS = funVolume(MHS); muH = funVolume(uint8(MHS) .* imgFiltered{1}) / volumeMHS; muS = funVolume(uint8(MHS) .* imgFiltered{2}) / volumeMHS; TH = im2bw(imgFiltered{1}, double(muH) / 255); - TS = im2bw(imgFiltered{2}, double(muS) / 255); + TS = im2bw(imgFiltered{2}, double(muS) / 255); - THS = TH & TS; + % TH and TS are combined with an intersect. + % We remove very little islands depending of the RBC area. + THS = mmareaopen(TH & TS, RBCRadius^2 / 130); imwrite(M{1}, '../output/MH.png') imwrite(M{2}, '../output/MS.png') diff --git a/src/DetectionOfSchizonts.m b/src/DetectionOfSchizonts.m index 178457d..7f58117 100644 --- a/src/DetectionOfSchizonts.m +++ b/src/DetectionOfSchizonts.m @@ -4,15 +4,15 @@ function [schizonts] = DetectionOfSchizonts(THS, RBCRadius) schizontsConnectivityMin = 6; % Minimum cluster size when detecting schizonts. - redCellsSE = mmsedisk(2 * RBCRadius, '2D', 'OCTAGON'); + redCellsSE = mmsedisk(1.3 * RBCRadius, '2D', 'OCTAGON'); schizonts = false(size(THS)); %% Extract all sets. - % We choose a SE which is a 3x3 square to connect pixel which + % We choose a SE which is a 3x3 square to connect pixels which % are diagonally each others. labelizedTHS = mmlabel(THS, mmsebox(1)); - + nbSets = mmstats(labelizedTHS, 'max'); if nbSets > 500 ME = MException('DetectionOfSchizonts:toManySets', 'The number of set is too high: %d', nbSets); diff --git a/src/DetectionOfWhiteCells.m b/src/DetectionOfWhiteCells.m index 923baf8..8b326c5 100644 --- a/src/DetectionOfWhiteCells.m +++ b/src/DetectionOfWhiteCells.m @@ -1,15 +1,16 @@ % Inputs: % RBCRadius: The typical red cell radius [px]. -function [WBCSmoothed] = DetectionOfWhiteCells(THS, RBCRadius) +function [WBCSmoothed, THSWithoutWBC] = DetectionOfWhiteCells(THS, RBCRadius) % We use a radius near the smallest red blood cell size (50% of the radius) redCellsSE = mmsedisk(0.5 * RBCRadius, '2D', 'OCTAGON'); % The white cells may not be correctly marked in the case they have a very special shape (oblong). WBCMarker = mmero(THS, redCellsSE); % Erosion to keep only fragments of white cells. WBC = mminfrec(WBCMarker, THS); % Reconstruction by dilation. - - % Morphological smoothing (Hole filling). -% WBCSmoothed = mmclohole(WBC); + + % We remove the white cells from THS. + % The while cell is dilated to also remove some little + THSWithoutWBC = THS & ~mmdil(WBC, mmsedisk(0.2 * RBCRadius)); smoothingSE = mmsedisk(0.1 * RBCRadius, '2D', 'OCTAGON'); WBCSmoothed = mmopen(mmclose(WBC, smoothingSE), smoothingSE); diff --git a/src/IdentificationOfInfectedRedCells.m b/src/IdentificationOfInfectedRedCells.m index 294d691..ac0c731 100644 --- a/src/IdentificationOfInfectedRedCells.m +++ b/src/IdentificationOfInfectedRedCells.m @@ -1,7 +1,6 @@ function [infectedRedCells] = IdentificationOfInfectedRedCells(redCells, parasites) - infectedRedCells = mminfrec(parasites, redCells); - + infectedRedCells = mminfrec(parasites, redCells); WriteImageGradient(infectedRedCells, '../output/infected red cells.png', [50, 50, 255]); end \ No newline at end of file diff --git a/src/Main.m b/src/Main.m index c9caa4c..bae031e 100644 --- a/src/Main.m +++ b/src/Main.m @@ -1,100 +1,81 @@ -tic +%% Parameters -mkdir('../output'); % Just in case the 'output' directory doesn't exist. -delete('../output/*'); +defaultDir = '../imgs_corrMK/'; +if exist('imagePath', 'var') + defaultPath = imagePath; +else + defaultPath = defaultDir; +end + +% imagePathInput = ... +% inputdlg('Enter the path to the input image: ', 'Sample', ... +% [1 100], {defaultPath}); + +imagePathInput = { + [defaultDir '13.05.2015/1412151257-100x-Teinte30-Saturation3-0013.tif'] + [defaultDir '13.05.2015/1412151257-100x-Teinte30-Saturation3-0014.tif'] + [defaultDir '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0036.png'] + [defaultDir '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0038.png'] + [defaultDir '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0039.png'] + [defaultDir '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0040.png'] + [defaultDir '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0041.png'] + [defaultDir '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0042.png'] + [defaultDir '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0043.png'] + [defaultDir '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0044.png'] + [defaultDir '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0045.png'] + [defaultDir '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0046.png'] + }; + + +scaleFactor = 1; +invertSaturation = true; + +%% Main +for k = 1:length(imagePathInput) + imagePath = imagePathInput{k}; + + mkdir('../output'); % Just in case the 'output' directory doesn't exist. + delete('../output/*'); -%% Parameters + fprintf('Image path: %s\n', imagePath); -% Image contenant un schizonte. -imagePath = '1401063467/1401063467-0001-schizonte.png'; - -% imagePath = '1412151257/1412151257-Gamma-1-0001.png'; -% imagePath = '1412151257/1412151257-Gamma-1-0004.png'; -% imagePath = '1412151257/1412151257-Gamma-1-0007.png'; - -%% 13 mai 2015 -% prefix = '13.05.2015/1412151257-100x-'; - -% imagePath = [prefix 'Teinte30-Saturation3-0013.tif']; - -% Good result. -% imagePath = [prefix 'Teinte30-Saturation3-0014.tif']; - -% Bad result. -% imagePath = [prefix ... -% '13.05.2015/1412151257-100x-Teinte0-Saturation0-0015.tif'; -% imagePath = '13.05.2015/1412151257-100x-0010.tif'; - - -%% 22 mai 2015 -% prefix = '22.05.2015/1412151257-100x-avecfiltre-lum8-'; -% imagePath = [prefix ... -% '676ms-Teinte0-Saturation0- SatMachine150-0036.png']; -% imagePath = [prefix ... -% '676ms-Teinte0-Saturation0- SatMachine150-0037.png']; % Blurred. -% imagePath = [prefix ... -% '676ms-Teinte0-Saturation0- SatMachine150-0038.png']; -% imagePath = '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0039.png'; -% imagePath = '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0040.png'; -% imagePath = '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0041.png'; -% imagePath = '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0042.png'; -% imagePath = [prefix ... -% '676ms-Teinte0-Saturation0- SatMachine150-0043.png']; -% imagePath = '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0044.png'; -% imagePath = '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0045.png'; -% imagePath = '22.05.2015/1412151257-100x-avecfiltre-lum8-676ms-Teinte0-Saturation0- SatMachine150-0046.png'; - -% imagePath = '22.05.2015/1410100927-100x-avecfiltre-lum8-267ms-Teinte0-Saturation0- SatMachine150-0047.png'; -% imagePath = '22.05.2015/1410100927-100x-avecfiltre-lum8-267ms-Teinte0-Saturation0- SatMachine150-0048.png'; -% imagePath = '22.05.2015/1410100927-100x-avecfiltre-lum8-267ms-Teinte0-Saturation0- SatMachine150-0049.png'; -% imagePath = '22.05.2015/1410051390-100x-avecfiltre-lum8-172ms-Teinte0-Saturation0- SatMachine150-0050.png'; -% imagePath = '22.05.2015/1410051390-100x-avecfiltre-lum8-172ms-Teinte0-Saturation0- SatMachine150-0051.png'; -% imagePath = '22.05.2015/1410051390-100x-avecfiltre-lum8-172ms-Teinte0-Saturation0- SatMachine150-0052.png'; -% imagePath = '22.05.2015/1410051390-100x-avecfiltre-lum8-172ms-Teinte0-Saturation0- SatMachine150-0053.png'; % Bad saturation. -% imagePath = '22.05.2015/1410051390-100x-avecfiltre-lum8-172ms-Teinte0-Saturation0- SatMachine150-0054.png'; -% imagePath = '22.05.2015/1410051390-100x-avecfiltre-lum8-172ms-Teinte0-Saturation0- SatMachine150-0055.png'; -% imagePath = '22.05.2015/1410051390-100x-avecfiltre-lum8-172ms-Teinte0-Saturation0- SatMachine150-0056.png'; - -scaleFactor = 0.6; - -%% Main -fprintf('Image path: %s\n', imagePath); + tic -% Load the image and its ground truth. -imgRGB = loadImg(imagePath); + % Load the image and its ground truth. + imgRGB = loadImg(imagePath); -% Resample the image and its ground truth. -imgRGBResampled = imresize(imgRGB, scaleFactor); + % Resample the image and its ground truth. + imgRGBResampled = imresize(imgRGB, scaleFactor); -imwrite(imgRGBResampled, '../output/imgRGB.png') + imwrite(imgRGBResampled, '../output/imgRGB.png') -disp('1) Detection of parasites ...') -[THS, RBCRadius, nucleiRadius] = DetectionOfParasites(imgRGBResampled); + disp('1) Detection of parasites ...') + [THS, RBCRadius, nucleiRadius] = DetectionOfParasites(imgRGBResampled, invertSaturation); -disp('2) Detection of white cells ...') -[WBC] = DetectionOfWhiteCells(THS, RBCRadius); -THSWithoutWBC = THS & ~WBC; % We remove the white cells from THS. + disp('2) Detection of white cells ...') + [WBC, THSWithoutWBC] = DetectionOfWhiteCells(THS, RBCRadius); -disp('3) Detection of schizonts ...') -[schizonts] = DetectionOfSchizonts(THSWithoutWBC, RBCRadius); -THS(schizonts) = 0; % We remove the white cells from THS. -THSWithoutWBCandSchizonts = THSWithoutWBC & ~schizonts; % We remove the shizonts from THS. + disp('3) Detection of schizonts ...') + [schizonts] = DetectionOfSchizonts(THSWithoutWBC, RBCRadius); + THS(schizonts) = 0; % We remove the white cells from THS. + THSWithoutWBCandSchizonts = THSWithoutWBC & ~schizonts; % We remove the shizonts from THS. -disp('4) Segmentation of red cells ...') -[redCells] = SegmentationOfRedCells(imgRGBResampled, RBCRadius, WBC, schizonts); + disp('4) Segmentation of red cells ...') + [redCells] = SegmentationOfRedCells(imgRGBResampled, RBCRadius, WBC, schizonts); -disp('5) Finding infected red cells ...') -[infectedRedCells] = IdentificationOfInfectedRedCells(redCells, THSWithoutWBCandSchizonts); + disp('5) Finding infected red cells ...') + [infectedRedCells] = IdentificationOfInfectedRedCells(redCells, THSWithoutWBCandSchizonts); -disp('6) Counting red cells and infected red cells ...') + disp('6) Counting red cells and infected red cells ...') -imwrite(mmlabel(redCells), '../output/red cells segmentation - individual labeled.png'); + imwrite(mmlabel(redCells), '../output/red cells segmentation - individual labeled.png'); -nbRedCells = mmstats(mmlabel(redCells), 'max'); -nbInfectedRedCells = mmstats(mmlabel(infectedRedCells), 'max'); -infectionPercentage = 100 * nbInfectedRedCells / nbRedCells; -fprintf('Percentage of infected cell: %.1f%% (%d/%d)\n', infectionPercentage, nbInfectedRedCells, nbRedCells) + nbRedCells = mmstats(mmlabel(redCells), 'max'); + nbInfectedRedCells = mmstats(mmlabel(infectedRedCells), 'max'); + infectionPercentage = 100 * nbInfectedRedCells / nbRedCells; + fprintf('Percentage of infected cell: %.1f%% (%d/%d)\n', infectionPercentage, nbInfectedRedCells, nbRedCells) -disp('Finished') -toc \ No newline at end of file + disp('Finished') + toc +end \ No newline at end of file diff --git a/src/SegmentationOfRedCells.m b/src/SegmentationOfRedCells.m index 65c24ba..0fd2285 100644 --- a/src/SegmentationOfRedCells.m +++ b/src/SegmentationOfRedCells.m @@ -1,58 +1,45 @@ function [segmentedCells] = SegmentationOfRedCells(imgRGB, RBCRadius, WBC, schizonts) - RBCRadiusMin = uint32(RBCRadius * 0.75); % 75%. + RBCRadiusMin = uint32(0.75 * RBCRadius); % 75%. RBCRadiusMinArea = pi * RBCRadiusMin ^ 2; - gray = imgRGB(:,:,2); + gray = imgRGB(:,:,2); % Green component. imwrite(gray, '../output/red cells segmentation - gray.png') - grayFlat = mmareaopen(gray, 2 * RBCRadiusMinArea, mmsebox(1)); % The gray image is better than the green component alone. + grayFlat = mmareaopen(gray, RBCRadiusMinArea, mmsebox(1)); grayFlat(WBC) = 255; grayFlat(schizonts) = 255; + imwrite(grayFlat, '../output/red cells segmentation - gray flat.png') % Thresholding using the Otsu's method. - % TODO : montrer l'histogramme threshold = ~im2bw(grayFlat, graythresh(grayFlat)); - % We remove all objects with a smaller area than a red blood cell. - threshold = mmareaopen(threshold, RBCRadiusMinArea); - - % We set the background to 0 by using the previous threshold. - redCellsFiltered = grayFlat; - redCellsFiltered(~threshold) = 0; - - imwrite(redCellsFiltered, '../output/red cells segmentation - input.png') - - %% Segmentation + % Uncomment to see the histogramm of 'grayFlat'. + % histogram(grayFlat, 'BinLimits', [40, 150], 'FaceColor', 'black', 'EdgeColor', 'black', 'FaceAlpha', 1); + % We remove all objects with a smaller area than a red blood cell. + imwrite(threshold, '../output/red cells segmentation - input.png') + % To smooth the borders and to remove little elements. % Here the paper use a non-flat ('NON-FLAT') but it's useless in our case. - opened = mmopen(redCellsFiltered, mmsedisk(RBCRadiusMin, '2D', 'EUCLIDEAN', 'FLAT')); - imwrite(opened, '../output/red cells segmentation - opened.png') + thresholdOpened = mmopen(threshold, mmsedisk(RBCRadiusMin, '2D', 'EUCLIDEAN', 'FLAT')); + imwrite(thresholdOpened, '../output/red cells segmentation - opened.png'); + + %% Segmentation - Watersheds - openedThreshold = im2bw(opened, 0.02); - imwrite(openedThreshold, '../output/red cells segmentation - opened - threshold.png') - - %% Don't know the meaning. (end of the right column of page 139). -% openedGradient = mmgradm(opened); -% imwrite(openedGradient, '../output/red cells segmentation - opened - gradient.png') - -% openedGradientThreshold = im2bw(openedGradient, 0.02); -% imwrite(openedGradientThreshold, '../output/red cells segmentation - opened - gradient - threshold.png') - -% holesClosed = mmareaclose(openedGradientThreshold, 10 * cMinArea); -% imwrite(holesClosed, '../output/red cells segmentation - holes closed.png') - % Watershed by distance transform. - watershed = WatershedsByDistanceTransform(openedThreshold); + watershed = WatershedsByDistanceTransform(thresholdOpened, RBCRadius); imwrite(watershed, '../output/red cells segmentation - watershed.png') + % An area opening to remove the little artifacts. + watershedSingles = mmareaopen(watershed, RBCRadiusMinArea / 5); + + + %% Segmentation - Composites + watershedLabeled = mmlabel(watershed); axisProperties = regionprops(watershedLabeled, 'MajorAxisLength', 'MinorAxisLength'); - % The area opening remove the little artifacts. - watershedSingles = mmareaopen(watershed, RBCRadiusMinArea / 5); - % Compute the ratio of the major axis over the minor axis and keep % cells with a ration greater than or equal to 1.3 as single. % The others will be processed as composite cells. @@ -65,10 +52,10 @@ function [segmentedCells] = SegmentationOfRedCells(imgRGB, RBCRadius, WBC, schiz imwrite(watershedSingles, '../output/red cells segmentation - watershed singles.png') % We remove the single cells from the initial image to obtain the composite cells. - composites = and(openedThreshold, ~watershedSingles); + composites = and(thresholdOpened, ~watershedSingles); imwrite(composites, '../output/red cells segmentation - composites.png') - compositesOpened = grayFlat; + compositesOpened = gray; compositesOpened(~composites) = 0; compositesOpened = mmopen(compositesOpened, mmsedisk(RBCRadiusMin, '2D', 'EUCLIDEAN', 'FLAT')); Reading git-diff-tree failed