fbpixel
Étiquettes : ,

Nous allons voir dans ce tutoriel comment implémenter un algorithme de détection de contour avec Python et OpenCV. La détection de contour est un exercice primordiale pour la vision assistée par ordinateur et la reconnaissance d’objet.

Installation et configuration de Python

Si ce n’est pas déjà fait, installer Python et configurer vos variables d’environnement

Installer la librairie opencv

python -m pip install opencv-python

N.B.: Il peut être intéressant de créer un environnement virtuel car les algorithmes n’utilisent pas les mêmes librairies d’OpenCV. L’important est de n’en choisir qu’une seule. opencv-python, opencv-python-contrib, opencv-python-headless et opencv-python-headless-contrib.

La première officielle contient les modules principaux, la deuxième a le plus de fonctionnalités, les deux autres sont des versions sans interface graphique.

d’autres modules sont très utiles comme numpy et matplotlib

python -m pip install numpy matplotlib

Pour importer ces modules dans un script Python

import cv2
import numpy as np
import matplotlib.pyplot as plt

Dans un précédent tutoriel, nous avons vu les fonctions de bases du traitement d’images avec OpenCV. Nous allons voir dans ce tutoriel, comment mettre en place les étapes d’une détection de contour à partir d’une image et, surtout, détecter les contours de l’objet désiré dans l’image.

Ouvrir et afficher l’image

Dans notre programme principal, nous ouvrons, dimensionnons et affichons l’image. Nous placerons l’algorithme de détection de contour dans la fonction contourDetection().

def contourDetection():
	#detection algorithm
	pass

def main(args):
	
	## Open and resize image	
	base_img = cv2.imread( fileDir+fileName , cv2.IMREAD_UNCHANGED)
	h,w,c = base_img.shape	
	width = 512 # specify new width
	scale_f = width/w
	height = h*scale_f
	base_img = cv2.resize(base_img, (int(width),int(height)))
	img = base_img.copy()

	cv2.imshow('orig', img)
	
	
	## Process image
	contourDetection()	
	
	## wait ESC or q
	key = cv2.waitKey(0) & 0x0FF # wait indefinitely and get input
	if key == 27 or key == ord('q'): #key is ESC or q
		cv2.destroyAllWindows()
	
	return 0


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

Algorithme simple de détection

Dans cet exemple, nous utilisons l’algorithme de base

  • passage en niveau de gris
  • floutage de l’image pour éliminer le bruit
  • détection des bords dans l’image Canny()
  • détection des contours findContours()
def contourDetection(img):
	
	# Conversion to grayscale 
	gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 

	# Blurr
	blur = cv2.GaussianBlur(gray, (5,5), 0)
	
	# Find Canny edges 
	edged = cv2.Canny(blur, 30, 200) 
	cv2.imshow('Canny', edged) 

	# Finding Contours 
	contours, hierarchy = cv2.findContours(edged, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) 
	#cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE
	#cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE
	print("Number of Contours found = " + str(len(contours))) 
	
	# Draw all contours (i.e -1)
	cv2.drawContours(img, contours, -1, (0, 255, 0), 3) 
	
	cv2.namedWindow('Contours', cv2.WINDOW_NORMAL)  
	cv2.imshow('Contours', img)
	
python-opencv-contour-detection-sple Détection de contour avec OpenCV et Python

L’algorithme Canny est défini par quatre paramètres:

  • un seuil1 pour le premier seuil d’hystérésis
  • le seuil2 pour le deuxième
  • la taille d’ouverture pour le filtre de Sobel
  • l’utilisation d’une plus grande précision sur le gradient

L’algorithme de récupération des contours permet juste de hiérarchiser et filtrer les rebords:

Méthode de récupération du contour est basé sur la hiérarchie des contours. A savoir si un contour est enfant ou parent par rapport à un autre (enfant à l’intérieur du parent)

  • cv2.RETR_LIST pas de hiérarchie
  • cv2.RETR_EXTERNAL avec cette méthode les contours enfants ne sont pas conservé
  • cv2.RETR_CCOMP propose deux niveaux de hierarchie, les limites extérieures des objets et les trous dans les objets
  • cv2.RETR_TREE retourne une arborescence pour chaque contour

Méthode d’approximation du contour

  • cv2.CHAIN_APPROX_NONE pas d’approximation. Enregistre tous les points du contour
  • cv2.CHAIN_APPROX_SIMPLE supprime les points proches (ex: un rectangle aura un contour de 4 points)

Ce code fonctionne correctement sur des images simples ou avec un contraste bien défini. Pour des images plus complexes comme des photos, tous les bords seront tracés, il faut ajouter des fonctionnalités à l’algorithme afin de filtrer ce qui est indésirable.

Paramétrer la fonction de détection de contour

Une bonne pratique est de tester les paramètres de la fonction et pour cela rien de mieux qu’une visualisation en temps réel. Nous mettons en place des curseurs afin de faire varier les paramètres et tracer le résultat.

# Conversion to grayscale 
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 

# Blurr
blurr_size = 5
blur = cv2.GaussianBlur(gray, (blurr_size,blurr_size), 0)

# Find Canny edges 
#cv2.Canny( image,threshold1, threshold2, apertureSize = 3, L2gradient = false )
low_thresh = 30
ratio =3.0
edged = cv2.Canny(blur, low_thresh, low_thresh*ratio )
cv2.imshow('Canny', edged)  

def change_blur(value):
	global blurr_size, low_thresh, ratio 
	if value%2 == 1:
		blurr_size = value
	else:
		blurr_size = value+1
	blur = cv2.GaussianBlur(gray, (blurr_size,blurr_size), 0)
	edged = cv2.Canny(blur, low_thresh, low_thresh*ratio )
	cv2.imshow('Canny', edged)	
	
def change_ratio(value):
	global blurr_size, low_thresh, ratio 
	ratio = (3.0-2.0)*value/100+2.0 # lower to upper ratio [2 ; 3]
	blur = cv2.GaussianBlur(gray, (blurr_size,blurr_size), 0)
	edged = cv2.Canny(blur, low_thresh, low_thresh*ratio )
	cv2.imshow('Canny', edged)		

def change_thresh(value):
	global blurr_size, low_thresh, ratio 
	low_thresh = value#
	blur = cv2.GaussianBlur(gray, (blurr_size,blurr_size), 0)
	edged = cv2.Canny(blur, low_thresh, low_thresh*ratio )
	cv2.imshow('Canny', edged)	

cv2.createTrackbar('blurr', 'Canny', 5, 30, change_blur)
cv2.createTrackbar('ratio', 'Canny', 100, 100, change_ratio)
cv2.createTrackbar('thresh', 'Canny', 30, 200, change_thresh)
python-opencv-canny-edge-trackbar Détection de contour avec OpenCV et Python

Fonction de seuillage

La fonction de seuillage cv2.threshold permet de sélectionner les pixels dont le niveau de gris est à un certain seuil. Cela permet de filtrer les zones plus ou moins claires d’une image. La fonction prend comme paramètres:

  • l’image img
  • le seuil considérer thresh [0-255]
  • la valeur maximale à donner au pixel au dessus du seuil maxval [0-255]
  • la méthode du threshold
    • cv2.THRESH_BINARY en dessous du seuil, 0; au dessus, maxval
    • cv2.THRESH_BINARY_INV en dessous du seuil, maxval; au dessus, 0
    • cv2.THRESH_TRUNC en dessous du seuil, srcl; au dessus, thresh
    • cv2.THRESH_TOZERO en dessous du seuil, 0; au dessus, src
    • cv2.THRESH_TOZERO_INV en dessous du seuil, src; au dessus, 0
# Thresh
	thresh_min = 180
	thresh_max = 255
	ret, binary = cv2.threshold(blur, thresh, maxval, cv2.THRESH_TOZERO)

Filtrage

Il existe d’autres filtres dans OpenCV très utiliser qui vous permettront d’améliorer les résultats de la détection de contours. Vous pouvez les utiliser et visualiser leurs effets à l’aide de curseur afin de choisir un réglage optimal.

## bilateral blurr
bilateral = cv2.bilateralFilter(img, 15, 75, 75) 
blur = bilateral

## dilate
dil_size = 1
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(dil_size,dil_size))
dilated = cv2.dilate(blur, kernel)

Recadrer l’image

Si vous avez du mal à trouver le contour d’un objet en particulier

Vous pouvez sélectionner une partie de l’image

y=100
x=100
h=50
w=50
sub_img = img[y:y+h, x:x+w]

Supprimer l’arrière plan

Vous pouvez également utiliser un algorithme pour retirer l’arrière plan

pip install rembg
from rembg import remove
import cv2

base_img = cv2.imread( "input.png" )
img_bg = rembg.remove(img,alpha_matting=False, alpha_matting_erode_size=5)
#cv2.imwrite("img_bg.png", img_bg)
cv2.imshow('rembg', img_bg)
	

Applications

Sources