fbpixel
Reconnaissance d’objets avec Yolo et OpenCV

Reconnaissance d’objets avec Yolo et OpenCV

Nous allons voir dans ce tutoriel comment faire de la reconnaissance d’objet avec Yolo et OpenCV en utilisant un réseau de neurones pré-entrainé grâce au deep learning.

Nous avons vu dans un précédent tutoriel comment reconnaitre des formes simples avec la vision par ordinateur. Cette méthode ne fonctionne que pour certaines formes simples prédéfinies. Si vous souhaitez reconnaitre une plus grande variété d’objets, le plus simple est d’utiliser l’intelligence artificielle.

Matériel

  • Un ordinateur avec une installation de Python3
  • Une caméra

Principe

L’intelligence artificielle est un domaine de l’informatique dans lequel le programme apprend par lui-même à effectuer certaines tâches. Notamment de la reconnaissance visuelle. Dans ce tutoriel, nous allons utiliser un réseau de neurones entrainé pour reconnaitre des formes particulières.

Il faut de nombreuse données pour pouvoir entrainer correctement un réseau de neurone. Il a été démontré que l’apprentissage était plus rapide sur un réseau de neurones entrainé pour autre chose. Par exemple, un réseau de neurones entrainé pour reconnaitre les chiens s’entrainera plus facilement à reconnaitre les chats.

Ultralytics propose les modèles YOLO qui sont simples d’utilisation et assez efficace pour la détection, la classification et le suivit

Pour chaque version, il existe différents modèle du plus léger et moins performant au plus performant mais plus lourd

YOLOv5nYOLOv5sYOLOv5mYOLOv5lYOLOv5x
YOLOv8nYOLOv8sYOLOv8mYOLOv8lYOLOv8x
(nano, small, medium, large and extra large)

Configuration de Python

Si ce n’est pas le cas, vous pouvez télécharger et installer Python 3

Vous pouvez ensuite installer les librairies nécessaires imutils, OpenCV, ultralytics

python3 -m pip install imutils opencv-python ultralytics

Récupérer un modèle pré-entrainé

Il est possible de récupérer un modèle pré-entrainé à partir du script pyhton

# load the pre-trained YOLOv8n model
model = YOLO("yolov8n.pt")

Script Python pour la reconnaissance d’Objet

Tout d’abord, nous créons un flux vidéo (vs) à l’aide de la librairie imutils qui va récupérer les images de la caméra.

vs = VideoStream(src=0, resolution=(1600, 1200)).start()

Nous initialisons un réseau de neurones avec les paramètres du modèle à l’aide de la librairie YOLO (yolov5, v8, etc.).

model = YOLO("yolov8n.pt")

Nous allons, ensuite, créer une boucle qui à chaque itération va lire l’image de la caméra et la passer en entrée du réseau de neurone pour faire la détection et reconnaissance d’objet.

while True:
	# start time to compute the fps
	start = datetime.datetime.now()

	#ret, frame = video_cap.read()
	#frame = vs.read(); ret=True
	
	
	# if there are no more frames to process, break out of the loop
	if not ret:
		break

	# run the YOLO model on the frame
	detections = model(frame)[0]

Enfin, le code affiche sur l’image la boite de détection, la probabilité de reconnaissance ainsi que la position

	# loop over the detections
	for box in detections.boxes:
		#extract the label name
		label=model.names.get(box.cls.item())
		
		# extract the confidence (i.e., probability) associated with the detection
		data=box.data.tolist()[0]
		confidence = data[4]

		# filter out weak detections
		if float(confidence) < CONFIDENCE_THRESHOLD:
			continue

		# if the confidence is greater than the minimum confidence
		xmin, ymin, xmax, ymax = int(data[0]), int(data[1]), int(data[2]), int(data[3])
		cv2.rectangle(frame, (xmin, ymin) , (xmax, ymax), GREEN, 2)

		#draw confidence and label
		y = ymin - 15 if ymin - 15 > 15 else ymin + 15
		cv2.putText(frame, "{} {:.1f}%".format(label,float(confidence*100)), (xmin, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, GREEN, 2)

Code complet de reconnaissance d’objet avec OpenCV et YOLO

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#

import datetime
from ultralytics import YOLO
import cv2
from imutils.video import VideoStream
#from helper import create_video_writer


# define some constants
CONFIDENCE_THRESHOLD = 0.8
GREEN = (0, 255, 0)



# load the pre-trained YOLOv8n model
model = YOLO("yolov8n.pt")
#model = YOLO("yolov5n.pt")
"""
#detect on image
frame= cv2.imread('./datasets/two-boats.jpg');ret= True #from image file
detections = model(frame)[0]
# loop over the detections
#for data in detections.boxes.data.tolist():
for box in detections.boxes:
	#extract the label name
	label=model.names.get(box.cls.item())
		
	# extract the confidence (i.e., probability) associated with the detection
	data=box.data.tolist()[0]
	confidence = data[4]

	# filter out weak detections by ensuring the
	# confidence is greater than the minimum confidence
	if float(confidence) < CONFIDENCE_THRESHOLD:
		continue

	# if the confidence is greater than the minimum confidence,
	# draw the bounding box on the frame
	xmin, ymin, xmax, ymax = int(data[0]), int(data[1]), int(data[2]), int(data[3])
	cv2.rectangle(frame, (xmin, ymin) , (xmax, ymax), GREEN, 2)

	#draw confidence and label
	y = ymin - 15 if ymin - 15 > 15 else ymin + 15
	cv2.putText(frame, "{} {:.1f}%".format(label,float(confidence*100)), (xmin, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, GREEN, 2)

# show the frame to our screen
cv2.imshow("Img", frame)
"""
#detect on video
# initialize the video capture object
#vs = VideoStream(src=0, resolution=(1600, 1200)).start()
video_cap = cv2.VideoCapture("datasets\\Splash - 23011.mp4")
# initialize the video writer object
#writer = create_video_writer(video_cap, "output.mp4")

while True:
	# start time to compute the fps
	start = datetime.datetime.now()

	#ret, frame = video_cap.read()
	#frame = vs.read(); ret=True
	
	
	# if there are no more frames to process, break out of the loop
	if not ret:
		break

	# run the YOLO model on the frame
	detections = model(frame)[0]
	
	# loop over the detections
	#for data in detections.boxes.data.tolist():
	for box in detections.boxes:
		#extract the label name
		label=model.names.get(box.cls.item())
		
		# extract the confidence (i.e., probability) associated with the detection
		data=box.data.tolist()[0]
		confidence = data[4]

		# filter out weak detections by ensuring the
		# confidence is greater than the minimum confidence
		if float(confidence) < CONFIDENCE_THRESHOLD:
			continue

		# if the confidence is greater than the minimum confidence,
		# draw the bounding box on the frame
		xmin, ymin, xmax, ymax = int(data[0]), int(data[1]), int(data[2]), int(data[3])
		cv2.rectangle(frame, (xmin, ymin) , (xmax, ymax), GREEN, 2)

		#draw confidence and label
		y = ymin - 15 if ymin - 15 > 15 else ymin + 15
		cv2.putText(frame, "{} {:.1f}%".format(label,float(confidence*100)), (xmin, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, GREEN, 2)
		#cv2.circle(frame, (int(X)-15, int(Y)), 1, GREEN, 2)
		#cv2.putText(frame, poslbl, (int(X), int(Y)),cv2.FONT_HERSHEY_SIMPLEX, 0.5, GREEN, 2)

	# end time to compute the fps
	end = datetime.datetime.now()
	# show the time it took to process 1 frame
	total = (end - start).total_seconds()
	print(f"Time to process 1 frame: {total * 1000:.0f} milliseconds")

	# calculate the frame per second and draw it on the frame
	fps = f"FPS: {1 / total:.2f}"
	cv2.putText(frame, fps, (50, 50),
				cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 4)

	# show the frame to our screen
	cv2.imshow("Frame", frame)
	#writer.write(frame)
	if cv2.waitKey(1) == ord("q"):
		break

video_cap.release()
vs.stop()
#writer.release()
cv2.destroyAllWindows()

Sources d’image pour la détection d’objet

Vous pouvez utiliser ce script avec différentes sources d’image. Pour cela, il faut légèrement adapter le code précédent afin de modifier la variable “img” contenant l’image à analyser.

  • La webcam de votre ordinateur
vs = VideoStream(src=0, resolution=(1600, 1200)).start()
while True:
	frame = vs.read()

Le stream vidéo doit être arrêté à la fin du script avec vs.stop()

vc = cv2.VideoCapture('rtsp://user:password@ipaddress:rtspPort')
while True:
	ret, frame=vc.read() #from ip cam

Veillez à arrêter la capture vidéo à la fin du script avec vc.release()

  • La Picam du Raspberry Pi
vs = VideoStream(usePiCamera=True, resolution=(1600, 1200)).start()
while True:
	frame = vs.read()

Pensez à arrêter le stream à la fin du script avec vs.stop()

  • Un fichier vidéo
vc = cv2.VideoCapture('./datasets/Splash - 23011.mp4') #from video
while True:
	ret, frame=vc.read() #from video
  • Un fichier image
frame= cv2.imread('./datasets/two-boats.jpg') 

Résultats

Pour cet exemple nous envoyons en entrée du réseau de neurones une image de deux bateaux qui sont reconnus correctement. Pour obtenir des résultats légèrement différents, vous pouvez modifier le paramètre confidence pour éviter les faux positifs.

Vous pouvez tester ce code avec votre webcam ou avec des photos, par exemple, pour voir les performances du modèle et de la reconnaissance d’objet.

Une fois que votre script fonctionne, vous pouvez entrainer votre modèle pour qu’il puisse détecter d’autres objets.

Sources

Reconnaissance de texte avec Python

Reconnaissance de texte avec Python

Nous allons voir dans ce tutoriel comment faire de la reconnaissance de texte à partir d’une image avec Python et Tesseract. Tesseract est un outil permettant de reconnaitre des caractères, et donc du texte, contenus dans une image (OCR, Optical Characters Recognition).

Installation de Tesseract

  • Sous Linux

Pour installer tesseract, entrez les commandes suivantes dans un terminal

sudo apt install tesseract-ocr
sudo apt install libtesseract-dev

  • Sous Windows

vous pouvez télécharger et exécuter l’installateur correspondant à votre OS

Une fois l’installation terminée, ajoutez C:\Program Files\Tesseract-OCR à votre Variable d’environnement Path

Vous pouvez maintenant exécuter tesseract et tester le résultat avec la commande suivante

tesseract <path_to_image> <path_to_result_file> -l <language>

ex:

tesseract test.png result -l fra

Tesseract va reconnaitre le texte contenu dans l’image test.png et écrire le texte brut dans le fichier result.txt

N.B.: Tesseract peut avoir du mal avec la ponctuation et l’alignement du texte

Reconnaissance de texte avec Pytesseract

Vous pouvez ensuite installer le paquet pytesseract

pip install pytesseract

‘L’intérêt d’utiliser Python, et OpenCV particulièrement, est que vous pouvez traiter les images et implémenter l’outil dans un logiciel plus important. Voici une liste de quelques avantages:

  • détection de texte dans une vidéo
  • Traitement et filtrage des images dans le cas de caractères obstrués, par exemple
  • Détecter du texte à partir d’un fichier PDF
  • Écrire les résultats dans un fichier word ou excel

Dans le script suivant, nous chargeons l’image avec OpenCV et nous traçons des rectangles autour du texte détecté. Les données des positions s’obtiennent avec la fonction image_to_data. Il est possible d’obtenir le texte brut avec la fonction image_to_string

from PIL import Image
import pytesseract
from pytesseract import Output
import cv2
 
source = 'test.png'
img = cv2.imread(source)
text=pytesseract.image_to_string(img)
print(text)

d = pytesseract.image_to_data(img, output_type=Output.DICT)
 
NbBox = len(d['level'])
print ("Number of boxes: {}".format(NbBox))

for i in range(NbBox):
	(x, y, w, h) = (d['left'][i], d['top'][i], d['width'][i], d['height'][i])
	# display rectangle
	cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
 
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Le script fonctionne aussi sur des photos de document

Bonus: Reconnaissance de texte avec Python à partir d’un fichier PDF

Installation de la librairie pdf2image

pip install pdf2image

L’utilisation de pdf2image nécessite l’installation de poppler

Assez simple sur Linux

sudo apt-get install poppler-utils

Sous Windows

  • Téléchargez le dossier zip
  • Extraire les fichiers où vous souhaitez (C:/Users/ADMIN/Documents , par exemple)
  • Ajouter le dossier bin à la variable d’environnement Path (C:\Users\ADMIN\Documents\poppler\Library\bin)
  • tester avec la commande pdftoppm -h

Script pour récupérer le texte d’un PDF

from pdf2image import convert_from_path, convert_from_bytes
from PIL import Image
import pytesseract
from pytesseract import Output

images = convert_from_path('invoice.pdf')

# get text
print("Number of pages: {}".format(len(images)))
for i,img in enumerate(images):
    print ("Page N°{}\n".format(i+1))
    print(pytesseract.image_to_string(img))

Script pour afficher les rectangles sur un PDF

from pdf2image import convert_from_path, convert_from_bytes
from PIL import Image
import pytesseract
from pytesseract import Output
import cv2
import numpy

images = convert_from_path('invoice.pdf')
for i,source in enumerate(images):
	print ("Page N°{}\n".format(i+1))
	
	#convert PIL to opencv
	pil_image = source.convert('RGB') 
	open_cv_image = numpy.array(pil_image) 
	# Convert RGB to BGR 
	img = open_cv_image[:, :, ::-1].copy() 
	#img = cv2.imread(source)

	d = pytesseract.image_to_data(img, output_type=Output.DICT)
	 
	NbBox = len(d['level'])
	print ("Number of boxes: {}".format(NbBox))

	for j in range(NbBox):
		(x, y, w, h) = (d['left'][j], d['top'][j], d['width'][j], d['height'][j])
		# display rectangle
		cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
	 
	cv2.imshow('img', img)
	cv2.waitKey(0)
	cv2.destroyAllWindows()

Applications

  • Lire des documents scannés
  • Faire de la reconnaissance de texte en temps réel à partir d’une vidéo

Sources

Afficher une Image d’OpenCV dans une interface PyQt

Afficher une Image d’OpenCV dans une interface PyQt

Pour certaines applications, il vous sera peut-être utile d’embarquer OpenCV dans une interface PyQt. Nous allons voir dans ce tutoriel comment intégrer et gérer correctement une vidéo capturée par OpenCV dans une application PyQt.

N.B.: Nous utilisons Pyside mais la conversion vers PyQt se fait assez simplement.

Pré-requis:

  • Installation de Python
  • Installation OpenCV (pip install opencv-python)
  • PySide ou PyQt (pip install pyside6 ou pip install PyQt5)

Code pour capturer une vidéo avec OpenCV

Voici le code de base pour afficher la vidéo d’une webcam avec openCV

import sys
import cv2

def main(args):

	cap = cv2.VideoCapture(0) #default camera

	while(True):
		ret, frame = cap.read()
		if ret:
			frame=cv2.resize(frame, (800, 600)) 
			cv2.imshow("Video",frame)
			
		if cv2.waitKey(1) & 0xFF == ord('q'): #click q to stop capturing
			break

	cap.release()
	cv2.destroyAllWindows()
	return 0

if __name__ == '__main__':
    
    sys.exit(main(sys.argv))

Pour l’intégrer dans une application PyQt, nous allons créer un objet QThread qui sera chargé de lire la vidéo sans bloquer l’application.

  • La fonction run est la fonction contenant le code openCV qui va tourner en boucle à l’appel de la fonction QThread.start
  • La fonction stop permet d’arrêter proprement le thread
  • Le signal changePixmap permet de signaler à l’application qu’une nouvelle image est disponible
class Thread(QThread):
	changePixmap = pyqtSignal(QImage)

	def run(self):
		self.isRunning=True
		cap = cv2.VideoCapture(0)
		while self.isRunning:
			ret, frame = cap.read()
			if ret:
				rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
				h, w, ch = rgbImage.shape
				bytesPerLine = ch * w
				convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
				p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
				self.changePixmap.emit(p)
				
	def stop(self):
		self.isRunning=False
		self.quit()
		self.terminate()

Création de l’application PyQt

Pour l’application, nous allons créer un QLabel dans un simple QWidget qui va contenir l’image de la vidéo et instancier le QThread. La vidéo se mettra à jour automatiquement à l’aide de la fonction setImage qui est appelée à la réception du signal changePixmap.

  • fonction setImage
	@pyqtSlot(QImage)
	def setImage(self, image):
		#update image	
		self.label.setPixmap(QPixmap.fromImage(image))
  • signal changePixmap
		self.th.changePixmap.connect(self.setImage)
class VideoContainer(QWidget):
	def __init__(self):
		super().__init__()
		self.title = 'PySide Video'
		self.left = 100
		self.top = 100
		self.fwidth = 640
		self.fheight = 480
		self.initUI()

	@pyqtSlot(QImage)
	def setImage(self, image):
		#update image	
		self.label.setPixmap(QPixmap.fromImage(image)) 
	
	def initUI(self):
		self.setWindowTitle(self.title)
		self.setGeometry(self.left, self.top, self.fwidth, self.fheight)
		self.resize(1200, 800)
		
		# create a label
		self.label = QLabel(self)		
		self.label.resize(640, 480)
		self.th = Thread(self)
		self.th.changePixmap.connect(self.setImage)
		self.th.start()
		self.show()

Code complet pour afficher une vidéo dans une fenêtre PyQt

import cv2
import sys
#from PyQt5.QtWidgets import  QWidget, QLabel, QApplication
#from PyQt5.QtCore import QThread, Qt, pyqtSignal, pyqtSlot
#from PyQt5.QtGui import QImage, QPixmap

from PySide6.QtWidgets import  QWidget, QLabel, QApplication
from PySide6.QtCore import QThread, Qt, Signal, Slot
from PySide6.QtGui import QImage, QPixmap
pyqtSignal = Signal
pyqtSlot = Slot

class Thread(QThread):
	changePixmap = pyqtSignal(QImage)

	def run(self):
		self.isRunning=True
		cap = cv2.VideoCapture(0)
		while self.isRunning:
			ret, frame = cap.read()
			if ret:
				# https://stackoverflow.com/a/55468544/6622587
				rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
				h, w, ch = rgbImage.shape
				bytesPerLine = ch * w
				convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
				p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
				self.changePixmap.emit(p)
				
	def stop(self):
		self.isRunning=False
		self.quit()
		self.terminate()

class VideoContainer(QWidget):
	def __init__(self):
		super().__init__()
		self.title = 'Video'
		self.left = 100
		self.top = 100
		self.fwidth = 640
		self.fheight = 480
		self.initUI()

	@pyqtSlot(QImage)
	def setImage(self, image):
		#update image	
		self.label.setPixmap(QPixmap.fromImage(image)) 
	
	def initUI(self):
		self.setWindowTitle(self.title)
		self.setGeometry(self.left, self.top, self.fwidth, self.fheight)
		self.resize(1200, 800)
		
		# create a label
		self.label = QLabel(self)		
		self.label.resize(640, 480)
		self.th = Thread(self)
		self.th.changePixmap.connect(self.setImage)
		self.th.start()
		self.show()

if __name__ == '__main__':
	
		app = QApplication(sys.argv)
		ex = VideoContainer()
		sys.exit(app.exec())

Une fenêtre “Video” apparait contenant l’image provenant de la webcam.

Vous pouvez adapter ce code simple pour intégrer une vidéo à un interface graphique qui permet de modifier les option vidéo ou travailler avec des filtres, par exemple.

Bonus:amélioration de l’interface fermeture/dimension

Le code fonctionne bien et peut être suffisant mais quelques problèmes sont présents avec cette implémentation:

  • On ne peut pas fermer l’application avec Ctrl+C (KeyboardInterrupt)
  • Lorsqu’on ferme la fenêtre le Qthread ne s’arrête pas
  • Si on redimensionne la fenêtre la taille de la vidéo ne bouge pas

Pour fermer l’application avec Ctrl+C, il est possible d’utiliser le signal d’interruption pour cela il suffit de rajouter le code suivant avant l’appel de l’application (il existe des méthodes plus propres)

import signal #close signal with Ctrl+C
signal.signal(signal.SIGINT, signal.SIG_DFL)

Pour terminer le QThread à la fermeture de la fenêtre, on peut utiliser le signal aboutToQuit de l’application pour appeler la fonction stop du QThread

app.aboutToQuit.connect(ex.th.stop) #stop qthread when closing window

Enfin, pour redimensionner la vidéo avec le fenêtre à chaque rafraichissement, nous utilisons la taille de la fenêtre pour calculer la taille de l’image et la position du label pour qu’il soit centré et que la vidéo garde ses proportions.

	@pyqtSlot(QImage)
	def setImage(self, image):
		#resize image with window and center
		imWidth=self.width()-2*self.padding
		imHeight=self.height()-2*self.padding
		image = image.scaled(imWidth, imHeight, Qt.KeepAspectRatio) # remove Qt.KeepAspectRatio if not needed
		self.label.resize(image.width(), image.height()) #(640, 480)
		self.label.move((self.width()-image.width())/2, (self.height()-image.height())/2)
			
		#update image	
		self.label.setPixmap(QPixmap.fromImage(image)) 

Voici le code complet avec amélioration

import cv2
import sys
#from PyQt5.QtWidgets import  QWidget, QLabel, QApplication
#from PyQt5.QtCore import QThread, Qt, pyqtSignal, pyqtSlot
#from PyQt5.QtGui import QImage, QPixmap

from PySide6.QtWidgets import  QWidget, QLabel, QApplication
from PySide6.QtCore import QThread, Qt, Signal, Slot
from PySide6.QtGui import QImage, QPixmap
pyqtSignal = Signal
pyqtSlot = Slot

class Thread(QThread):
	changePixmap = pyqtSignal(QImage)

	def run(self):
		self.isRunning=True
		cap = cv2.VideoCapture(0)
		while self.isRunning:
			ret, frame = cap.read()
			if ret:
				# https://stackoverflow.com/a/55468544/6622587
				rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
				h, w, ch = rgbImage.shape
				bytesPerLine = ch * w
				convertToQtFormat = QImage(rgbImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
				p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
				self.changePixmap.emit(p)
				
	def stop(self):
		self.isRunning=False
		self.quit()
		self.terminate()

class VideoContainer(QWidget):
	def __init__(self):
		super().__init__()
		self.title = 'PySide Video'
		self.left = 100
		self.top = 100
		self.fwidth = 640
		self.fheight = 480
		self.padding = 10
		self.initUI()

	@pyqtSlot(QImage)
	def setImage(self, image):
		#resize image with window and center
		imWidth=self.width()-2*self.padding
		imHeight=self.height()-2*self.padding
		image = image.scaled(imWidth, imHeight, Qt.KeepAspectRatio) # remove Qt.KeepAspectRatio if not needed
		self.label.resize(image.width(), image.height()) #(640, 480)
		self.label.move((self.width()-image.width())/2, (self.height()-image.height())/2)
			
		#update image	
		self.label.setPixmap(QPixmap.fromImage(image)) 
		
	def initUI(self):
		self.setWindowTitle(self.title)
		self.setGeometry(self.left, self.top, self.fwidth, self.fheight)
		self.resize(1200, 800)
		
		# create a label
		self.label = QLabel(self)		
		self.label.resize(self.width()-2*self.padding,self.height()-2*self.padding) #(640, 480)
		self.th = Thread(self)
		self.th.changePixmap.connect(self.setImage)
		self.th.start()
		self.show()

import signal #close signal with Ctrl+C
signal.signal(signal.SIGINT, signal.SIG_DFL)

if __name__ == '__main__':
	
		app = QApplication(sys.argv)
		ex = VideoContainer()
		app.aboutToQuit.connect(ex.th.stop) #stop qthread when closing window
		
		sys.exit(app.exec())

Sources

Créer votre banque d’image avec Python

Créer votre banque d’image avec Python

Pour entrainer un réseau de neurone à la détection et à la reconnaissance d’objet, il faut une banque d’image sur laquelle travailler. Nous allons voir comment télécharger un grand nombre d’images à partir de Google avec Python. Pour pouvoir entrainer un réseau de neurones, il est nécessaire d’avoir un grand nombre de données. Plus il y a de données, meilleur sera l’entrainement. Dans notre cas nous souhaitons entrainer un réseau de neurones à reconnaitre un objet en particulier. Pour cela, nous créons un script Python qui va venir télécharger les fichiers sur internet et les placer dans un dossier.

Configurer Python3

Téléchargez les librairies Selenium et OpenCV (optional)

python3 -m pip install selenium
python3 -m pip install opencv-python

Téléchargez le fichier geckodriver, décompressez-le et copier le .EXE à l’endroit que vous voulez. Notez bien le chemin d’accès du fichier geckodriver.exe

N.B.: Nous n’utilisons la librairie OpenCV seulement pour vérifier que OpenCV peut ouvrir et utiliser les images afin de ne pas encombrer le dossier inutilement

Script Python de téléchargement d’image

Ce script lance une recherche sur Google Image et enregistre les images trouvées dans le dossier spécifié pour la banque d’images.

N.B.: N’oubliez pas de préciser le chemin du fichier geckodriver GECKOPATH, le chemin vers le dossier de destination et les mot-clés pour la recherche Google.

import sys
import os
import time 

#Imports Packages
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import TimeoutException,WebDriverException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

import cv2

########################################################################
GECKOPATH = "PATH-TO-GECKODRIVER"
parent_dir = "PATH-TO-FOLDER"
search='coffee mug'
########################################################################

# path 
folderName=search.replace(" ","_")
directory = os.path.join(parent_dir, folderName,'img') 
   
# Create the directory 
try: 
	if not os.path.exists(directory):
		os.makedirs(directory) #os.mkdir(directory)
except OSError as error: 
	print("ERROR : {}".format(error)) 


sys.path.append(GECKOPATH)  
#Opens up web driver and goes to Google Images
browser = webdriver.Firefox()#Firefox(firefox_binary=binary)

#load google image
browser.get('https://www.google.ca/imghp?hl=en')

delay = 10 # seconds
try:
	btnId="L2AGLb"
	myElem = WebDriverWait(browser, delay).until(EC.presence_of_element_located((By.ID , btnId))) #id info-address-place-wrapper 
	#elm=browser.find_element_by_id(btnId)
	elm=browser.find_element(By.ID,btnId)
	elm.click()
	print("Popup is passed!")
except TimeoutException as e:
	print("Loading took too much time!")

time.sleep(30) #loading

# get and fill search bar
#box = browser.find_element_by_xpath('//*[@id="sbtc"]/div/div[2]/input')
#box = browser.find_element(By.XPATH,'//*[@id="sbtc"]/div/div[2]/input')
box = browser.find_element(By.TAG_NAME, "textarea")
box.send_keys(search)
box.send_keys(Keys.ENTER)
print("key enter is pressed")
time.sleep(10) #loading

#Will keep scrolling down the webpage until it cannot scroll no more
last_height = browser.execute_script('return document.body.scrollHeight')
while True:
	browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
	time.sleep(20)
	new_height = browser.execute_script('return document.body.scrollHeight')
	try:
		#browser.find_element_by_xpath('//*[@id="islmp"]/div/div/div/div/div[5]/input').click()
		browser.find_element(By.XPATH,'//*[@id="islmp"]/div/div/div/div/div[5]/input').click()
		
		time.sleep(10)
	except:
		print("button not found")
		pass
		
	if new_height == last_height:
		break
	last_height = new_height

#find all images on page
imgList=[]
try:
	#imgs = browser.find_elements(By.TAG_NAME, "img")
	#imgs = browser.find_elements(By.CLASS_NAME, "rg_i")
	imgs = browser.find_elements(By.XPATH,"//img[contains(@class,'rg_i')]")
	print("found {} images".format(len(imgs)))
	for i,img in enumerate(imgs):
		#src=img.get_attribute("src") # get source of image
		#urllib.request.urlretrieve(str(src),directory+"\{}.png".format(i)) # download source
		img.screenshot(directory+'\{}.png'.format(i)) # or screenshot
		imgList.append(directory+'\{}.png'.format(i))	
except:
	print("imagenot found")
	pass
		
browser.quit()
 
#Test images with OpenCV
for img in imgList:
	try:   
		cv2.imread(img)
	except Exception as e:
		os.remove(img)
		print("remove {}".format(img))

N.B.: update avec nouvelle version de selenium find_element_by_id(thisId) –> find_element(By.Id,thisId)

BONUS: Gestion d’une popup

Dans le code j’ai rajouté une commande permettant de gérer la popup qui apparait à l’ouverture de la page internet. Il va attendre que le bouton avec le bon identifiant soit chargé avant d’appuyer dessus.

delay = 10 # seconds
try:
	btnId="L2AGLb"
	myElem = WebDriverWait(browser, delay).until(EC.presence_of_element_located((By.ID , btnId))) #id info-address-place-wrapper 
	elm=browser.find_element_by_id(btnId)
	elm.click()
	print("Popup is passed!")
except TimeoutException as e:
	print("Loading took too much time!")

Résultat

Le script va parcourir les résultats de Google et faire des captures d’écran de chaque image pour les placer dans notre base de données et former une banque d’images.

Vous avez désormais une banque d’image que vous pouvez utiliser pour de la reconnaissance visuelle par exemple ou faire du traitement d’image.

Applications

  • Développer des algorithmes de traitement d’image
  • Entrainer des réseaux de neurones à la détection et reconnaissance d’objet

Sources

Reconnaissance de forme et de couleur avec Python

Reconnaissance de forme et de couleur avec Python

La libraire OpenCV est utilisée pour faire du traitement d’image notamment de la reconnaissance de forme et de couleur. La librairie possèdent des fonction d’acquisition et des algorithmes de traitement d’image qui permettent notamment de faire de la reconnaissance d’image assez simplement sans avoir à passer par de l’intelligence artificielle. C’est ce que nous allons voir dans ce tutoriel.

Ce tutoriel peut être appliqué à n’importe quel ordinateur possédant une installation de Python avec OpenCV et une Caméra. Notamment le Raspberry Pi.

Matériel

  • Ordinateur avec une installation de python3

Préparation de l’environnement de travail

Pour réaliser le script de reconnaissance de forme, nous installons les modules OpenCV, numpy et imutils pour manipuler et traiter les images.

pip3 install opencv-python numpy imutils

La détection des couleurs se fait à l’aide des modules webcolors et scipy (KDTree)

pip3 install webcolors scipy

Afin de tester et valider l’algorithme, nous créons une image contenant des objets de formes et de couleurs différentes. Vous pouvez créer votre propre image avec Paint ou utiliser celle-ci:

Principe de fonctionnement

Dans le code suivant, nous allons créer un classe détection de forme qui va nous permettre de sélectionner une forme en fonction du nombre de contours trouvé. Puis nous allons définir une fonction permettant de retrouver le nom de la couleur en fonction de son code RGB. Enfin nous allons utiliser OpenCV pour charger, filtrer et masquer l’image afin de détecter les formes et les couleurs contenues dans l’image.

Code complet de reconnaissance de forme simple et de couleur

Vous devez créer le fichier python ObjectDetection.py dans le même dossier que l’image que vous souhaitez analyser.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# import the necessary packages
import cv2
import numpy as np
import imutils

#colors
from webcolors import rgb_to_name,CSS3_HEX_TO_NAMES,hex_to_rgb #python3 -m pip install webcolors
from scipy.spatial import KDTree

def convert_rgb_to_names(rgb_tuple):
    # a dictionary of all the hex and their respective names in css3
    css3_db = CSS3_HEX_TO_NAMES#css3_hex_to_names
    names = []
    rgb_values = []    
    for color_hex, color_name in css3_db.items():
        names.append(color_name)
        rgb_values.append(hex_to_rgb(color_hex))
    
    kdt_db = KDTree(rgb_values)    
    distance, index = kdt_db.query(rgb_tuple)
    return names[index]
    
class ShapeDetector:
	def __init__(self):
		pass
	def detect(self, c):
		# initialize the shape name and approximate the contour
		shape = "unidentified"
		peri = cv2.arcLength(c, True)
		approx = cv2.approxPolyDP(c, 0.04 * peri, True)

		# if the shape is a triangle, it will have 3 vertices
		if len(approx) == 3:
			shape = "triangle"
		# if the shape has 4 vertices, it is either a square or
		# a rectangle
		elif len(approx) == 4:
			# compute the bounding box of the contour and use the
			# bounding box to compute the aspect ratio
			(x, y, w, h) = cv2.boundingRect(approx)
			ar = w / float(h)
			# a square will have an aspect ratio that is approximately
			# equal to one, otherwise, the shape is a rectangle
			shape = "square" if ar >= 0.95 and ar <= 1.05 else "rectangle"
		# if the shape is a pentagon, it will have 5 vertices
		elif len(approx) == 5:
			shape = "pentagon"
		elif len(approx) == 6:
			shape = "hexagon"
		elif len(approx) == 10 or len(approx) == 12:
			shape = "star"
		# otherwise, we assume the shape is a circle
		else:
			shape = "circle"
		# return the name of the shape
		return shape	

if __name__ == '__main__':
	# load the image and resize it to a smaller factor so that
	# the shapes can be approximated better
	image = cv2.imread('python_shapes_detection_base.PNG')
	resized = imutils.resize(image, width=300)
	ratio = image.shape[0] / float(resized.shape[0])
	
	# convert the resized image to grayscale, blur it slightly,
	# and threshold it
	gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
	blurred = cv2.GaussianBlur(gray, (5, 5), 0)
	thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY)[1]
	
	# find contours in the thresholded image and initialize the
	# shape detector
	cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
		cv2.CHAIN_APPROX_SIMPLE)
	cnts = imutils.grab_contours(cnts)
	sd = ShapeDetector()

	# loop over the contours
	for c in cnts:
		# compute the center of the contour
		M = cv2.moments(c)
		cX = int((M["m10"] / M["m00"]) * ratio)
		cY = int((M["m01"] / M["m00"]) * ratio)  
		
		#detect shape from contour
		shape = sd.detect(c)
		
		# resize the contour
		c = c.astype("float")
		c *= ratio
		c = c.astype("int")
		cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
		
		#draw contour with mask
		mask = np.zeros(image.shape[:2], np.uint8)
		cv2.drawContours(mask, [c], -1, 255, -1)
		img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
		
		#Convert to RGB and get color name
		imgRGB = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
		mean=cv2.mean(imgRGB, mask=mask)[:3]
		named_color = convert_rgb_to_names(mean)
		
		#get complementary color for text
		mean2 = (255-mean[0],255-mean[1],255-mean[2])
		
		#display shape name and color
		objLbl=shape+" {}".format(named_color)
		textSize = cv2.getTextSize(objLbl,cv2.FONT_HERSHEY_SIMPLEX,0.5,2)[0]
		cv2.putText(image, objLbl, (int(cX-textSize[0]/2),int(cY+textSize[1]/2)), cv2.FONT_HERSHEY_SIMPLEX,0.5,mean2, 2)
		
		#show image
		cv2.imshow("Image", image)
		#cv2.waitKey(0)
	cv2.waitKey(0)		

Résultat

Pour lancer le script, vous pouvez soit l’exécuter à partir de votre éditeur de code (comme Geany) ou bien lancer la commande suivante dans un terminal de commande ouvert dans votre dossier de travail.

$ python3 ObjectDetection.py

Une fois le code exécuté, l’image va s’afficher avec chacune des formes entourées de vert et un texte au centre de la forme contenant le nom et la couleur de la forme.

N.B.: Cet algorithme ne fonctionnera pas pour toute les formes. Pour détecter d’autres formes vous devez soit adapter la fonction detect de la classe ShapeDetector pour identifier tous les cas possibles soit passer par de l’intelligence artificielle.

Applications

Sources