Détection de bords en temps réel à l'aide de FPGA

Présentation


Notre projet met en œuvre un système de détection des contours en temps réel basé sur la capture de trames d'image à partir d'une caméra OV7670 et leur diffusion sur un moniteur VGA après application d'un filtre en niveaux de gris et d'un opérateur Sobel. Notre conception est basée sur une carte FPGA Cyclone IV qui nous permet d'optimiser les performances à l'aide des puissantes fonctionnalités du matériel de bas niveau et des calculs parallèles, ce qui est important pour répondre aux exigences du système en temps réel.


Nous avons utilisé la carte de développement ZEOWAA FPGA qui est basée sur le cyclone IV (EP4CE6E22C8N). Nous avons également utilisé Quartus Prime Lite Edition comme environnement de développement et Verilog HDL comme langage de programmation. De plus, nous avons utilisé l'interface VGA intégrée pour piloter le moniteur VGA et GPIO (General Pins for Input and Output) pour connecter le matériel externe à notre carte.


Carte de développement FPGA ZEOWAA


L'architecture


Notre conception est divisée en 3 parties principales:


  1. Lecture des pixels de données de la caméra.
  2. Implémentation de notre algorithme de détection des contours (convertisseur en niveaux de gris et opérateur Sobel).
  3. Affichage de l'image finale en s'interfaçant avec un moniteur VGA.

En outre, il existe un stockage mémoire intermédiaire entre la lecture / écriture des données et l'exploitation de ces données. Dans ce but, nous avons implémenté deux tampons qui fonctionnent comme un espace temporaire pour les pixels avant leur utilisation.


L'architecture implémentée


Notez qu'après avoir pris le pixel de la caméra, nous ne l'avons pas stocké directement dans la mémoire tampon intermédiaire. Au lieu de cela, nous l'avons converti en niveaux de gris puis nous l'avons stocké dans le tampon. En effet, le stockage des pixels en niveaux de gris 8 bits nécessite moins de mémoire que le stockage des pixels colorés qui sont 16 bits. De plus, nous avons un autre tampon qui stocke les données après avoir appliqué l'opérateur Sobel pour les rendre prêtes à être affichées sur le moniteur.


Voici les détails de l'implémentation de notre architecture:


Appareil photo

Nous avons utilisé la caméra OV7670 qui est l'un des modules de caméra les moins chers que nous ayons trouvés. En outre, cette caméra peut fonctionner sur 3,3 V et n'a pas besoin de protocoles de communication difficiles comme I2c ou SPI pour extraire les données de l'image. Il ne nécessite qu'une interface SCCB similaire à l'interface I2c pour définir la configuration de la caméra en termes de format de couleur (RGB565, RGB555, YUV, YCbCr 4: 2: 2), de résolution (VGA, QVGA, QQVGA, CIF, QCIF) et de nombreux autres paramètres.


Module caméra OV7670


La vidéo se compose d'images qui sont modifiées à un rythme spécifique. Une image est une image composée de lignes et de colonnes de pixels où chaque pixel est représenté par des valeurs de couleur. Dans ce projet, nous avons utilisé la configuration par défaut de la caméra où la taille du cadre est la résolution VGA 640 x 480 (0,3 mégapixels), et le format de couleur du pixel est RGB565 (5 bits pour le rouge, 6 bits pour le bleu, 5 bits pour le vert ) et le taux de changement des images est de 30 ips.


Ci-dessous, les connexions de la caméra au FPGA en utilisant le GPIO qui existe dans la carte de développement:


Broche dans l'appareil photobroche dans le FPGALa descriptionBroche dans l'appareil photobroche dans le FPGALa description
3,3 V3,3 VAlimentation (+)GNDGNDNiveau d'alimentation au sol (-)
SdiocGNDHorloge SCCBSDIODGNDDonnées SCCB
VSYNCP31Synchronisation verticaleHrefP55Synchronisation horizontale
PCLKP23Horloge pixelXclkP54Horloge du système d'entrée (25 MHz)
D7P468e bit de donnéesD6P447e bit de données
D5P436e bit de donnéesD4P425e bit de données
D3P394e bit de donnéesD2P383e bit de données
D1P342e bit de donnéesD0P331er bit de données
RESET (Active Low)3,3 VRéinitialiser la brochePWDNGNDBroche de mise hors tension

Notez que nous n'avons pas utilisé l'interface SCCB pour la configuration. Donc, nous avons mis leurs fils correspondants au sol pour éviter tout signal flottant pouvant affecter les données.


Pour fournir l'horloge à 25 MHz pour la caméra, nous avons utilisé une boucle à verrouillage de phase (PLL) qui est un système de contrôle de fréquence en boucle fermée pour fournir l'horloge nécessaire à partir des 50 MHz fournis par la carte. Pour implémenter la PLL, nous avons utilisé l'outil de catalogue IP interne dans le logiciel Quartus.


Cette caméra utilise un signal de synchronisation verticale (VSYNC) pour contrôler le processus d'envoi de la trame et le signal de synchronisation horizontale (HREF) pour contrôler l'envoi de chaque ligne de la trame. Cette caméra utilise seulement 8 lignes de données (D0-D7) pour transférer les bits qui représentent les valeurs de couleur du pixel alors que la caméra divise la valeur de pixel RVB 16 bits en 2 parties (8 bits) et les envoie séparément.


Les figures ci-dessous de la fiche technique du module de caméra OV7670 illustrent les signaux de synchronisation verticale et horizontale.


Synchronisation de trame VGA


Calendrier horizontal


Diagramme de synchronisation de sortie RGB565


Convertisseur de niveaux de gris

Pour produire une image en niveaux de gris à partir de son image colorée d'origine, de nombreux facteurs doivent être pris en considération, car l'image peut perdre le contraste, la netteté, l'ombre et la structure. De plus, l'image doit conserver la luminance relative de l'espace colorimétrique. Plusieurs techniques linéaires et non linéaires sont utilisées pour convertir l'image couleur en niveaux de gris. En conséquence, pour atteindre notre objectif, nous avons utilisé la conversion colorimétrique (préservant la luminance perceptuelle) en niveaux de gris représentée dans l'équation suivante:



Pour améliorer les performances en termes de calculs, il est plus rapide d'utiliser l'opérateur de décalage. Par conséquent, l'équation ci-dessus peut être réduite comme suit:



Par conséquent, après avoir capturé une valeur de pixel (565 RVB) à partir de la caméra, elle peut être immédiatement convertie en une valeur de pixel en niveaux de gris de 8 bits en appliquant la formule de conversion. L'image en niveaux de gris est plus facile à stocker dans la mémoire et assez rapide pour servir les fonctionnalités de notre système en temps réel car sa complexité est approximativement logarithmique et FPGA peut la rendre encore plus rapide en accédant à la mémoire en parallèle. Après cela, l'image stockée est prête à implémenter l'algorithme de détection de bord.


Mémoire intermédiaire (le tampon)

Nous avons 2 tampons, le premier est utilisé pour stocker les pixels après les avoir convertis en niveaux de gris et sa taille (8 bits x 150 x 150) et le second est utilisé pour stocker les pixels après l'application de l'opérateur Sobel et le seuil de la valeur de sortie et sa taille (1 bit x 150 x 150). Malheureusement, les tampons 150 x 150 ne stockent pas l'image entière de la caméra mais n'en stockent qu'une partie.


Nous avons choisi la taille de nos tampons comme 150 x 150 en raison de la limitation de la mémoire du cyclone IV car il ne dispose que de 276,480 Kbit tandis que nos deux tampons prennent 202,500 Kbit (150 x 150 x 9), ce qui équivaut à 73,24% de la mémoire d'origine de le cyclone IV et le reste de la mémoire sont utilisés pour stocker l'algorithme et l'architecture. De plus, nous avons essayé (170 x 170) comme taille pour nos tampons qui prend 94,07% de la mémoire ce qui ne laisse pas assez d'espace pour implémenter l'algorithme.


Nos tampons sont de véritables RAM à double port qui peuvent lire et écrire simultanément dans différents cycles d'horloge. Ici, nous avons créé notre implémentation au lieu d'utiliser l'outil de catalogue IP dans le logiciel Quartus pour avoir plus de flexibilité dans l'implémentation. De plus, nous avons intégré les deux tampons dans un seul module au lieu d'avoir des modules différents.


Opérateur Sobel

Nous avons utilisé un premier opérateur de détection de bord dérivé qui est un opérateur de gradient de zone matricielle qui détermine le changement de luminance entre différents pixels. Pour être plus précis, car il s'agit d'une méthode simple et efficace en termes d'utilisation de la mémoire et de complexité temporelle, nous avons utilisé l'opérateur de gradient Sobel qui utilise un noyau 3x3 centré sur un pixel choisi pour représenter la force du bord. L'opérateur Sobel est la magnitude du gradient calculée par:


Équation G


Où Gx et Gy peuvent être représentés à l'aide de masques de convolution:


Matrices de convolution Gx et Gy


Notez que les pixels les plus proches du centre du masque ont plus de poids. De plus, G x et G y peuvent être calculés comme suit:


Équations Gx et Gy


Où p i est le pixel correspondant dans le tableau suivant, et la valeur de p i est une valeur en niveaux de gris de 8 bits:


matrice de pixels


Il est courant d'approximer la magnitude du gradient de l'opérateur Sobel par des valeurs absolues:


l'équation


Cette approximation est plus facile à mettre en œuvre et plus rapide à calculer, ce qui sert à nouveau nos fonctionnalités en termes de temps et de mémoire.


Voici le schéma de principe de l'opérateur Sobel qui prend 9 pixels (8 bits) en entrée et produit une valeur de pixel (8 bits):


Noyau Sobel


Et voici le schéma fonctionnel détaillé de la mise en œuvre de l'opérateur Sobel.


Noyau Sobel détaillé


Moniteur VGA

Notre carte de développement a une interface VGA intégrée qui a la capacité d'afficher seulement 8 couleurs sur le moniteur VGA car elle n'a que 3 bits pour contrôler les couleurs via un bit pour le rouge, un pour le vert et un pour le bleu. Cela a rendu notre débogage plus difficile car il nous empêche d'afficher l'image de la caméra directement sur le moniteur. Nous avons donc utilisé un seuil pour convertir les pixels en valeur 1 bit afin qu'il soit possible d'afficher l'image.


L'interface VGA fonctionne comme l'appareil photo car elle fonctionne pixel par pixel du coin supérieur gauche au coin inférieur droit. En utilisant la synchronisation verticale et horizontale, nous pouvons synchroniser les signaux qui contrôlent le flux de pixels.


Le signal de synchronisation verticale est utilisé pour représenter l'indice de la ligne tandis que le signal de synchronisation horizontale est utilisé pour représenter l'indice de la colonne. De plus, les deux signaux utilisent le porche avant, l'impulsion de synchronisation et le porche arrière comme signaux de synchronisation pour séparer l'ancienne ligne de la nouvelle ligne dans le signal de synchronisation horizontale, et l'ancienne trame de la nouvelle trame dans le signal de synchronisation verticale.


Diagramme de synchronisation du signal VGA


Nous avons utilisé l'interface de signal VGA standard (640 x 480 @ 60 MHz). Toutes les spécifications standard du signal sont décrites ici .


Test


Avant de tout assembler et de tester le système en temps réel. Nous avons d'abord dû tester chaque partie séparément. Au début, nous avons vérifié les valeurs et les signaux provenant de la caméra en affichant certaines valeurs de pixels. Ensuite, avec l'aide d'OpenCV utilisant le langage de programmation Python, nous avons pu appliquer le filtre Sobel sur plusieurs images pour comparer les résultats avec notre algorithme et vérifier l'exactitude de notre logique. De plus, nous avons testé nos tampons et notre pilote VGA en affichant plusieurs images statiques sur le moniteur VGA après avoir appliqué l'opérateur Sobel et le seuillage. De plus, en modifiant la valeur du seuil, la précision de l'image est affectée.


Le code python que nous avons utilisé:


# This code is made to test the accuracy of our algorithm on FPGA import cv2 #import opencv library f = open("sample.txt",'w') # Open file to write on it the static image initialization lines img = cv2.imread('us.jpg') # Read the image which has our faces and its size 150x150 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #convert to grayscale sobelx = cv2.Sobel(gray,cv2.CV_64F,1,0,ksize=3) #x-axis sobel operator sobely = cv2.Sobel(gray,cv2.CV_64F,0,1,ksize=3) #y-axis sobel operator abs_grad_x = cv2.convertScaleAbs(sobelx) abs_grad_y = cv2.convertScaleAbs(sobely) grad = abs_grad_x + abs_grad_y for i in range(0,150): for x in range(0,150): #read the pixels of the grayscaled image and Store them into file with specific format to initialize the buffer in FPGA code f.write("data_a[{:d}]<=8'd{:d};\n".format(i*150+x,gray[i][x])) #apply threshold to be exactly like the code on FPGA if(grad[i][x] < 100): grad[i][x] = 255 else: grad[i][x] = 0 cv2.imshow("rgb", img) #Show the real img cv2.imshow("gray",gray) #Show the grayscale img cv2.imshow("sobel",grad)#Show the result img cv2.waitKey(0) #Stop the img to see it 

Résultats


Grâce à notre implémentation, nous avons obtenu un système de détection de bord en temps réel qui produit une image 150x150 après avoir appliqué le filtre en niveaux de gris et l'opérateur Sobel. Le système implémenté fournit 30 fps. La caméra fonctionne sur une horloge de 25 MHz et le système, en général, respecte les délais en temps réel sans retard notable. De plus, la valeur de seuil peut affecter la quantité de détails et le bruit dans l'image finale.


Voici une comparaison entre l'opérateur Sobel sur FPGA et l'opérateur sobel OpenCV:


Comparaison


Voici une vidéo illustrative des résultats:


Vidéo du projet


Voici le lien du dépôt sur Github qui contient tous les codes sources.


Améliorations futures


Comme nous utilisons FPGA Cyclone IV, nous sommes limités à sa capacité de mémoire et au nombre de portes logiques. Par conséquent, comme amélioration future, nous pouvons utiliser une source de mémoire externe ou nous pouvons implémenter notre travail sur une autre carte afin que nous puissions afficher tous les pixels de l'image reçue de la caméra.


De plus, bien que l'opérateur Sobel soit rapide et simple à mettre en œuvre, il est sensible au bruit. Pour éliminer le bruit produit, nous pouvons utiliser un filtre de bruit comme le filtre médian non linéaire qui fonctionne parfaitement avec notre système si nous avions suffisamment de mémoire pour implémenter un troisième tampon. Cela produira une image plus lisse avec des fonctionnalités nettes supprimées.


En conséquence, nous avons utilisé l'interface VGA intégrée du FPGA qui ne peut produire qu'une image 3 bits. Ainsi, nous n'avons pas pu afficher l'image en niveaux de gris car elle a besoin de 8 bits pour être affichée. Par conséquent, la mise en œuvre d'une autre interface ou l'utilisation d'une carte plus puissante améliorera la flexibilité d'affichage de l'image.


Conclusion


Nous avons pu utiliser nos connaissances et notre compréhension des concepts cruciaux des systèmes embarqués comme machines à états, parallélisme des calculs et interface matérielle-logicielle pour créer une application de détection de bord efficace qui répond à nos objectifs.


Remerciements


Ce projet est construit par une équipe composée de deux étudiants: Hussein Youness et Hany Hamed en première année de baccalauréat en informatique à l' Université Innopolis en Russie.


Ce projet fait partie du cours d' architecture informatique automne 2018 à l' Université Innopolis .


Source: https://habr.com/ru/post/fr431326/


All Articles