Table des matières Python

Segmentation par sélection de couleurs

1. Introduction

La segmentation d'une image par sélection de couleurs consiste à extraire des plages de pixels ayant une certaine couleur. Cela permet de repérer des objets en fonction de leur couleur.

Ce document montre comment effectuer cette segmentation avec OpenCV. La représentation des couleurs RVB et la conversion en TSL sont expliquées dans Espace des couleurs RVB.

import cv2 as cv
from matplotlib.pyplot import *
import numpy
import math
            

2. Couches RVB

On traite la photographie suivante, qui comporte des billes colorées posées sur un plan en bois. OpenCV range les couches dans l'ordre BGR (Blue,Green,Red). Pour afficher l'image avec Matplotlib, il faut les mettre dans l'ordre RGB.

img1 = cv.imread("../../../../simul/image/billes.png")
img2 = cv.cvtColor(img1,cv.COLOR_BGR2RGB)
figure(figsize=(4,4))
imshow(img2)
            
figAfigA.pdf

On effectue la séparation des couches pour les afficher et les traiter :

red,green,blue = cv.split(img2)
figure(figsize=(12,4))
subplot(131)
imshow(red,cmap=cm.gray)
subplot(132)
imshow(green,cmap=cm.gray)
subplot(133)
imshow(blue,cmap=cm.gray)
            
figBfigB.pdf

La couche rouge permet de discriminer les billes bleues et vertes, qui apparaissent très sombres car elles comportent très peu de rouge. Pour segmenter l'image, il faut effectuer un seuillage :

seuil=110.0
ret,seg_red = cv.threshold(red,seuil,255.0,cv.THRESH_BINARY_INV)
figure(figsize=(4,4))
imshow(seg_red,cmap=cm.gray)
             
figCfigC.pdf

On peut de la même manière sélectionner les billes jaunes et rouges, qui sont très claires sur la couche rouge :

seuil=200.0
ret,seg_red = cv.threshold(red,seuil,255.0,cv.THRESH_BINARY)
figure(figsize=(4,4))
imshow(seg_red,cmap=cm.gray)
             
figDfigD.pdf

La bille rouge n'est pas bien sélectionnée car une partie a une luminosité proche du fond sur la couche rouge. Voila ce qui arrive si on abaisse le seuil :

seuil=190.0
ret,seg_red = cv.threshold(red,seuil,255.0,cv.THRESH_BINARY)
figure(figsize=(4,4))
imshow(seg_red,cmap=cm.gray)
             
figEfigE.pdf

Un partie du fond apparaît (le fond comporte beaucoup de rouge). D'autre part, cette méthode ne permet pas de distinguer le jaune du rouge, ni le vert du bleu. On pourrait prendre en compte les informations des autres couches pour le faire, mais la méthode deviendrait trop complexe. Nous allons voir que la sélection de couleurs est beaucoup plus simple avec les couches TSL.

3. Couches TSL

La conversion d'une image RVB en image TSL est expliquée dans Espace des couleurs RVB. T est la teinte, qui est un angle en degrés, S est la saturation, et L la luminance. Le format correspondant dans openCV est HSV (Hue,Saturation,Value).

hsv = cv.cvtColor(img1,cv.COLOR_BGR2HSV)
hue,sat,val = cv.split(hsv)
figure(figsize=(12,4))
subplot(131)
imshow(hue,cmap=cm.gray)
subplot(132)
imshow(sat,cmap=cm.gray)
subplot(133)
imshow(val,cmap=cm.gray)
             
figFfigF.pdf

La couche T permet de sélectionner des pixels on fonction de leur teinte. Par exemple, pour sélectionner les pixels jaunes, il faut sélectionner les pixels dont la teinte est dans une plage située à environ 45 degrés. Dans OpenCV, les valeurs de teinte sont en faite comprises entre 0 et 180, de manière à pouvoir les représenter sur une image en niveaux de gris. Il faut donc diviser les angles par deux.

lower = numpy.array([36/2],dtype=numpy.uint8)
upper = numpy.array([60/2],dtype=numpy.uint8)
seg_hue = cv.inRange(hue,lower,upper)
figure(figsize=(4,4))
imshow(seg_hue,cmap=cm.gray)
             
figGfigG.pdf

La bille jaune est très bien isolée, mais il y a aussi un contour sur deux billes vertes. L'examen de la couche S (saturation) montre que ces contours sont sans doute beaucoup moins saturés que le jaune de la bille. Nous allons donc faire une sélection directement sur l'image HSV, en choisissant aussi une plage de saturation :

lower = numpy.array([36/2,180,0],dtype=numpy.uint8)
upper = numpy.array([60/2,255,255],dtype=numpy.uint8)
seg = cv.inRange(hsv,lower,upper)
figure(figsize=(4,4))
imshow(seg,cmap=cm.gray)
              
figHfigH.pdf

Une fois la bille jaune isolée, on peut obtenir sa position en calculant le barycentre des pixels blancs.

La teinte verte se situe vers 90 degrés. On peut sélectionner une bille verte :

lower = numpy.array([70/2,0,0],dtype=numpy.uint8)
upper = numpy.array([100/2,255,255],dtype=numpy.uint8)
seg = cv.inRange(hsv,lower,upper)
figure(figsize=(4,4))
imshow(seg,cmap=cm.gray)
              
figIfigI.pdf

La teinte rouge se situe vers 0 degré :

lower = numpy.array([0,190,0],dtype=numpy.uint8)
upper = numpy.array([15,255,255],dtype=numpy.uint8)
seg = cv.inRange(hsv,lower,upper)
figure(figsize=(4,4))
imshow(seg,cmap=cm.gray)
              
figJfigJ.pdf
Creative Commons LicenseTextes et figures sont mis à disposition sous contrat Creative Commons.