Détection de panneaux de signalisation

La détection de panneau de signalisation peut se faire par différentes approches qui peuvent être combinées. Cette problématique se rapproche fortement de la détection de logo.

De mon point de vue, la méthode la plus simple et la plus générique est d’utiliser les points d’intérêt. Cependant les autres informations telles que la couleur ou la forme des contours peuvent aider à reconnaitre les formes.

Prenons l’exemple de la détection d’un panneau STOP.

stop-sign-modelstop-sign

1. Couleur

Une première étape (optionnelle) consiste à filtrer les pixels rouge de l’image. Pour faire cela on utilise l’espace de couleur HSV. La valeur H (teinte ou « hue ») contient essentiellement l’information de couleur, il faut donc faire un filtre passe bande en coupant au bonnes valeurs pour laisser passer uniquement la teinte rouge. Il faudra également filtrer S (saturation), en effet toutes les valeurs de moins de 10 se rapportent généralement au blanc.

      private static Image<Gray, Byte> GetRedPixelMask(Image<Bgr, byte> image)
      {
         using (Image<Hsv, Byte> hsv = image.Convert<Hsv, Byte>())
         {
            Image<Gray, Byte>[] channels = hsv.Split();

            try
            {
               //channels[0] is the mask for hue less than 20 or larger than 160
               CvInvoke.cvInRangeS(channels[0], new MCvScalar(20), new MCvScalar(160), channels[0]);
               channels[0]._Not();

               //channels[1] is the mask for satuation of at least 10, this is mainly used to filter out white pixels
               channels[1]._ThresholdBinary(new Gray(10), new Gray(255.0));

               CvInvoke.cvAnd(channels[0], channels[1], channels[0], IntPtr.Zero);
            }
            finally
            {
               channels[1].Dispose();
               channels[2].Dispose();
            }
            return channels[0];
         }
      }

2. Forme

Une seconde étape (optionnelle aussi) consiste à détecter les contours des objets présents dans l’image puis à comparer ces contours avec ceux de l’image modèle.

Dans la fonction suivante, on applique un filtre Gaussien, on filtre les pixels rouges, on fait une fermeture (dilatation + erosion), puis Canny. Tout les contours extraits sont passés à une autre fonction qui va les examiner.

      public void DetectStopSign(Image<Bgr, byte> img, List<Image<Gray, Byte>> stopSignList, List boxList)
      {
         Image<Bgr, Byte> smoothImg = img.SmoothGaussian(5, 5, 1.5, 1.5);
         Image<Gray, Byte> smoothedRedMask = GetRedPixelMask(smoothImg);

         //Use Dilate followed by Erode to eliminate small gaps in some countour.
         smoothedRedMask._Dilate(1);
         smoothedRedMask._Erode(1);

         using (Image<Gray, Byte> canny = smoothedRedMask.Canny(100, 50))
         using (MemStorage stor = new MemStorage())
         {
            Contour contours = canny.FindContours(
               Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
               Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_TREE,
               stor);
            FindStopSign(img, stopSignList, boxList, contours);
         }
      }

La fonction suivante passe en revu tout les contours. La première étape consiste à polygonaliser le contour, cela permet de le simplifier en filtrant ces imperfections. Ensuite, si les contours sont trop petits, on les ignore. On essaie de matcher le contours avec une forme octogonale, le matching est fait avec les moments de Hu et donne en sortie une valeur. Plus cette valeur est proche de 0, plus cela veux dire que les formes sont similaire. Si la valeur est supérieur à 0.1 on considère que les formes sont trop différentes pour pouvoir être semblables. Si la valeur est inférieur à 0.1 alors on extrait l’image correspondant à la boite englobante du contour pour ensuite extraire les points d’intérêts.

 

private void FindStopSign(Image<Bgr, byte> img, List<Image<Gray, Byte>> stopSignList, List boxList, Contour contours)
      {
         for (; contours != null; contours = contours.HNext)
         {
            contours.ApproxPoly(contours.Perimeter * 0.02, 0, contours.Storage);
            if (contours.Area > 200)
            {
               double ratio = CvInvoke.cvMatchShapes(_octagon, contours, Emgu.CV.CvEnum.CONTOURS_MATCH_TYPE.CV_CONTOURS_MATCH_I3, 0);

               if (ratio > 0.1) //not a good match of contour shape
               {
                  Contour child = contours.VNext;
                  if (child != null)
                     FindStopSign(img, stopSignList, boxList, child);
                  continue;
               }

               Rectangle box = contours.BoundingRectangle;

               Image<Gray, Byte> candidate;
               using (Image<Bgr, Byte> tmp = img.Copy(box))
                  candidate = tmp.Convert<Gray, byte>();

               //set the value of pixels not in the contour region to zero
               using (Image<Gray, Byte> mask = new Image<Gray, byte>(box.Size))
               {
                  mask.Draw(contours, new Gray(255), new Gray(255), 0, -1, new Point(-box.X, -box.Y));

                  double mean = CvInvoke.cvAvg(candidate, mask).v0;
                  candidate._ThresholdBinary(new Gray(mean), new Gray(255.0));
                  candidate._Not();
                  mask._Not();
                  candidate.SetValue(0, mask);
               }

               ImageFeature[] features = _detector.DetectFeatures(candidate, null);

               Features2DTracker.MatchedImageFeature[] matchedFeatures = _tracker.MatchFeature(features, 2);

               int goodMatchCount = 0;
               foreach (Features2DTracker.MatchedImageFeature ms in matchedFeatures)
                  if (ms.SimilarFeatures[0].Distance < 0.5) goodMatchCount++;  
              if (goodMatchCount >= 10)
               {
                  boxList.Add(box);
                  stopSignList.Add(candidate);
               }
            }
         }
      }

3. Points d’intérêt

Les points d’intérêts sont préalablement extrait de l’image modèle du panneau stop :

      public StopSignDetector(Image<Bgr, Byte> stopSignModel)
      {
         _detector = new SURFDetector(500, false);
         using (Image<Gray, Byte> redMask = GetRedPixelMask(stopSignModel))
         {
            _tracker = new Features2DTracker(_detector.DetectFeatures(redMask, null));  
         }
         _octagonStorage = new MemStorage();
         _octagon = new Contour(_octagonStorage);
         _octagon.PushMulti(new Point[] { 
            new Point(1, 0),
            new Point(2, 0),
            new Point(3, 1),
            new Point(3, 2),
            new Point(2, 3),
            new Point(1, 3),
            new Point(0, 2),
            new Point(0, 1)},
            Emgu.CV.CvEnum.BACK_OR_FRONT.FRONT);
      }

Il sont ensuite comparé au points d’intérêt extraits sur l’image testée, après le test de couleur et de forme (qui sont optionnels)  :

 

ImageFeature[] features = _detector.DetectFeatures(candidate, null);

               Features2DTracker.MatchedImageFeature[] matchedFeatures = _tracker.MatchFeature(features, 2);

               int goodMatchCount = 0;
               foreach (Features2DTracker.MatchedImageFeature ms in matchedFeatures)
                  if (ms.SimilarFeatures[0].Distance < 0.5) goodMatchCount++;                if (goodMatchCount >= 10)
               {
                  boxList.Add(box);
                  stopSignList.Add(candidate);
               }

C’est la fonction MatchFeature qui permet de faire le matching entre deux ensembles de points d’intérêt. La fonction renvoi un tableau de matchedFeatures, contenant les points matchés. Tout les points matchés ne sont pas forcément bon, il faut donc faire un dernier filtre testant la distance entre un point et son matching. Si la distance est suffisament basse, alors le matching est considéré comme bon. Si plus de 10 bon matching sont obtenus alors on considère que le panneau a été reconnu.

 

Pour plus d’information sur l’extraction des points d’intérêt je conseille la lecture d’articles discutant de SIFT ou SURF, pour la mise en correspondance FLANN. Il faut également lire la documentation d’OpenCV.

Le code source et les images de ce billet sont extraits des exemples de codes donnés par EMGU CV.

 

Reconnaissance d’images de document

Mise en correspondance de deux images de document

La problématique de la reconnaissance de d’image de document peut s’avérer complexe car elle nécessite dans certain cas d’être robuste en translation, en rotation et en zoom. Il peut également arriver que les documents soient dégradé (bruit, taches, coupures, etc).

Les techniques basées sur l’utilisation de points d’intérêt telles que SIFT ou SURF sont couramment utilisées dans le domaine des images naturel. J’ai travaillé sur une extension de cette méthode afin de reconnaitre rapidement des modèles de document donnés par un utilisateur tel qu’une carte d’identité, un passeport, un ticket de train, etc.

La méthode est simple et extensible à de nombreux autres images de document, elle se décompose en 4 principales étapes :

  1. Extraction de points d’intérêt. (SURF)
  2. Description des points. (SURF)
  3. Mise en correspondance des points de l’image recherché avec ceux de l’image requête. (FLANN)
  4. Estimation d’une transformation à 4 paramètres. (RANSAC)

Les choix technologiques entre parenthèses seront dans le futur changés par de nouveaux algorithmes plus performants ou plus adaptés au contexte.

Le détail de la technique peut être trouvé dans la publication faite à CIFED 2012: Reconnaissance et Extraction de Pièces d’identité.

Détection de visage

Détection de visages (1)

La détection de visage est très utilisé dans l’analyse de vidéo et d’image naturel. Elle peut également être utilisé dans la reconnaissance de document d’identité.

La méthode la plus connue est celle mise au point par Viola et Jones en 2001, permettant de détecter la présence de visage sur une image en temps réel.

Viola et Jones

La technique utilise les caractéristiques pseudo-Haar. Cette technique consiste à définir des zones rectangulaires et adjacentes. On calcul ensuite la somme des intensités des pixels de l’image dans ces zones. La différence entre les rectangles noirs et blanc donne la caractéristique pseudo-Haar.

« ]

Exemples de fenêtres utilisées pour le calcul des caractéristiques pseudo-Haar [1

Pour déterminer la présence d’un visage sur une image, Viola et Jones utilisent une technique de classification supervisée. Un certain nombre d’images d’apprentissage sont nécessaires. Les auteurs utilisent une classification par boosting. Cette classification est également utilisé pour sélectionner les  caractéristiques intéressantes.

Pour aller plus loin

L’algorithme est implémenté dans la bibliothèque OpenCV et peut être utilisé facilement.

Cette même méthode peut être utilisé pour la reconnaissance de véhicules ou encore de piéton [2].

Bibliographie

[1] Méthode de Viola et Jones (Wikipedia).

[1] Detecting pedestrians using patterns of motion and appearance, Viola, P. and Jones, M.J. and Snow, D., International Journal of Computer Vision, volume 63, number 2, pages 153–161, 2005.

EMGU CV

Emgu cv est un wrapper d’openCV en C#.

On y retrouve la quasi-totalité des fonctionnalités d’openCV permettant d’effectuer toutes les tâches basique d’analyse et de traitement d’image. On y trouve également des outils de machine learning comme les classifieurs SVM,  Réseaux de neurones, Naive Bayes, Decision Tree…

Depuis la version 2.3, on y trouve en plus d’openCV, l’OCR Tesseract.

Des exemples déjà programmés sont disponible avec la bibliothèques comme la détection de visages, SVM, détection de mouvement, détection de piéton, reconnaissance de panneaux, utilisation de SURF, lecteur de plaque d’immatriculation.

http://www.emgu.com/wiki/index.php/Main_Page

SIFT et SURF

Mise en correspondance de points d'intérêts SIFT - image Wikipédia

La détection de points d’intérêt dans les images est de plus en plus utilisé afin de facilité de nombreuses tâches : reconnaissance d’objet, assemblage d’images, modélisation 3D, la recherche d’image par le contenu, le tracking video, etc. Les points clés extraits d’une image permettent de caractériser cette image. En comparant les points clés d’une image et ceux d’une autre image, on peut alors déduire si des informations communes sont présentes dans ces deux images.

Création d'un panorama en assemblant 2 à 2 des images à l'aide de SIFT - image Wikipédia

Paru en 1999, le descripteur SIFT [1] est le plus couramment utilisé pour l’extraction de points clés. Ce qui a fait le succès de ce descripteur, c’est qu’il est peu sensible au changement d’intensité, de mise à l’échelle et de rotation, ce qui fait de lui un descripteur très robuste. Il est basé sur les différences de gaussiennes. L’article Wikipédia est très bien détaillé d’un point de vue scientifique.

Un peu plus récemment (2006 et 2008), une variante appelé SURF [2] est apparue. Le principal avantage de cette technique est que l’extraction des points d’intérêts se fait plus rapidement, notamment grâce à l’utilisation d’images intégrales. Les points d’intérêt ne sont pas extraits exactement de la même manière que SIFT et ne sont pas non plus caractérisé de la même manière. Une étude comparative [3] met en évidence les avantages et inconvénient de SIFT, PCA-SIFT et SURF grâce au tableau suivant :

Méthode Temps échelle Rotation Flou Illumination Affine
SIFT normal meilleur meilleur meilleur normal bon
PCA-SIFT bon normal bon normal bon bon
SURF meilleur bon normal bon meilleur bon

J’utilise actuellement dans mes travaux de thèse l’implémentation OpenCV de SURF pour faire de la détection de logo, de carte d’identité et plus généralement de sous-images dans les images.

Références :

[1] David G. Lowe, « Object recognition from local scale-invariant features »,  Proceedings of the International Conference on Computer Vision, vol. 2, 1999, p. 1150–1157.

[2] Herbert Bay, Tinne Tuytelaars et Luc Van Gool, « SURF: Speeded Up Robust Features », Proceedings of the European Conference on Computer Vision, 2006, p. 404-417.

[3] Juan L., Gwun O., « A comparison of sift, pca-sift and surf », International Journal of Image Processing (IJIP), 2010.