Site icon AranaCorp

Filtrer les couleurs avec OpenCV et Python

Lors de traitement d’image, il peut être intéressant de filtrer une couleur pour la modifier, la garder ou la supprimer. Nous allons voir dans ce tutoriel comment sélectionner et filtrer une zone de couleur avec OpenCV et Python.

Traitement d’image

Pour ce tutoriel, nous utilisons une image avec différentes formes et couleurs.

import cv2
import numpy as np

fileName=  "python_shapes_detection_base.png"
img = cv2.imread( fileName , cv2.IMREAD_UNCHANGED)

win_name = "original"
cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
cv2.imshow(win_name, img)

cv2.waitKey()

Codes couleur RGB, HSV et Hexadecimal

Pour traiter les images, nous utilisons le module OpenCV. Il existe différentes façons de filtrer les images et les couleurs avec OpenCV en fonction des codes couleur utilisés. Pour convertir les couleurs de RGB vers HSV, nous utiliserons le module colorsys.

# format conversion
import colorsys

def rgb_to_hex(pixel):
	""" convert color from BGR to Hexa format """
	#rgb normal: range (0-255, 0-255, 0.255)
	#red=pixel[0]
	#green=pixel[1]
	#blue=pixel[2]

	#bgr normal: range (0-255, 0-255, 0.255)
	blue=pixel[0]
	green=pixel[1]
	red=pixel[2]
	return '#{:02x}{:02x}{:02x}'.format(red, green, blue)


def convert_rgb_to_hsv(pixel):
	""" convert color from BGR to HSV format """
	#rgb normal: range (0-255, 0-255, 0.255)
	#red=pixel[0]
	#green=pixel[1]
	#blue=pixel[2]

	#bgr normal: range (0-255, 0-255, 0.255)
	blue=pixel[0]
	green=pixel[1]
	red=pixel[2]

	#get rgb percentage: range (0-1, 0-1, 0-1 )
	red_percentage= red / float(255)
	green_percentage= green/ float(255)
	blue_percentage=blue / float(255)

	#get hsv percentage: range (0-1, 0-1, 0-1)
	color_hsv_percentage=colorsys.rgb_to_hsv(red_percentage, green_percentage, blue_percentage)

	#get opencv hsv: range (0-179, 0-255, 0-255)
	color_h=round(179*color_hsv_percentage[0])#round(360*color_hsv_percentage[0])
	color_s=round(255*color_hsv_percentage[1])
	color_v=round(255*color_hsv_percentage[2])
	color_hsv=[color_h, color_s, color_v]

	#print('color_hsv: ', color_hsv)
	return color_hsv

N.B: le format de couleur des pixels dans OpenCV est BGR

Sélection de pixel dans OpenCV

Pour savoir quel valeur de couleur filtrer sur notre image, nous allons ajouter un écouteur d’évènement pour pouvoir sélectionner un pixel avec la souris et afficher les couleurs RGB, HSV et Hexa correspondante

def click_event(event, x, y, flags, params):
	""" add a click event to display BGR and HSV color on image when click on partciular pixel
	event - event type
	x - event x position
	y - event y position
	flags - 
	params - (windows name, img object)
	
	usage : 	cv2.setMouseCallback(win_name, click_event, [win_name,img])
	"""
	global rgb_to_filter,hsv_to_filter
	if event == cv2.EVENT_LBUTTONDOWN:
		img = params[1]
		imgdisp = img.copy()
		#print(f'Coordinates: ({x}, {y})')
		rgb = imgdisp[y,x]
		hsv = convert_rgb_to_hsv(rgb)
		hexc = rgb_to_hex(rgb)

		print('rgb: {}\nhsv : {}'.format(rgb,hsv))
		font = cv2.FONT_HERSHEY_PLAIN
		cv2.putText(imgdisp, 'rgb: {}'.format(rgb), (x, y), font, 1, (255, 0, 0), 1)
		cv2.putText(imgdisp, 'hsv : {}'.format(hsv), (x, y+15), font, 1, (255, 0, 0), 1)
		cv2.putText(imgdisp, 'hex : {}'.format(hexc), (x, y+30), font, 1, (255, 0, 0), 1)
		

		cv2.imshow(params[0], imgdisp)
		#cv2.destroyWindow(params[0])
		rgb_to_filter = rgb
		hsv_to_filter = convert_rgb_to_hsv(rgb)

Nous pouvons ensuite lier la fonction de gestion de l’évènement à la fenêtre

cv2.setMouseCallback(win_name, click_event, [win_name,img])

Filtrer les couleurs avec OpenCV

Pour filtrer les couleurs, il existe différentes manières selon le code couleur utilisé et ce que vous souhaitez faire exactement.

Pour le format RGB, il est possible de traiter les pixels un par un. Dans cet exemple, nous filtrons les couleurs proches (th) de la couleur sélectionnée (color)

def colorThresh(img,color,keep=True,th=3, ncolor = [0,0,0,0]):
	"""remove or keep specific color from image and replace with ncolor"""

	imgfilt = img.copy()
	#data format
	if isinstance(img,np.ndarray):#opencv
		h,w,c = img.shape
		#pixel_list = [img[y,x].tolist() for y in range(0, h) for x in range(0,w)]
		for y in range(0, h):
			for x in range(0,w):
				pixel = img[y,x]
				if keep:
					if abs(color[0]-pixel[0])<th and abs(color[1]-pixel[1])<th and abs(color[2]-pixel[2])<th:
						imgfilt[y,x] = pixel
					else:
						imgfilt[y,x] = ncolor

				else:
					if abs(color[0]-pixel[0])<th and abs(color[1]-pixel[1])<th and abs(color[2]-pixel[2])<th:
						imgfilt[y,x] = ncolor
					else:
						imgfilt[y,x] = pixel



	return imgfilt

Pour le format HSV, il existe une fonction plus directe et plus rapide en utilisant un mask construit avec inRange à partir d’un seuil de couleur bas et d’un seuil de couleur haut.

lower = np.maximum(np.array(hsv_to_filter) - 2 , np.array([0, 0, 0]))#np.array([10, 100, 100])
upper = np.minimum(np.array(hsv_to_filter) + 2 , np.array([255, 255, 255]))#np.array([20, 255, 255])
imghsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(imghsv, lower, upper)
result = cv2.bitwise_and(img, img, mask=mask) #keep color
#result = cv2.bitwise_and(img, img, mask=255-mask) #remove color

N.B: les deux fonctions précédentes donnent le même résultat dans cet exemple, même si la méthode HSV est beaucoup plus rapide. Vous pouvez adapter ces fonctions selon votre besoin

Voici le résultat du code complet que vous trouverez dans la section suivante. Il vous permet de sélectionner un pixel de l’image et de conserver la forme de couleur correspondante ou la supprimer.

Code complet pour la sélection et le filtrage de couleur

Ce code vous permettra de sélectionner une couleur, avec une souris ou autre, pour la filtrer. Vous pouvez travailler sur les fonctions données pour travailler sur les pixels ou sur des champs de couleur HSV.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  imgProc.py
#
#  imgprocessing utility functions to filter, remove pixel of certain color
import cv2
import numpy as np
import math


def colorThresh(img,color,keep=True,th=3, ncolor = [0,0,0,0]):
	"""remove specific color from image and replace with ncolor"""
	#img = img.convert("RGBA")
	imgfilt = img.copy()
	#data format
	if isinstance(img,np.ndarray):#opencv
		h,w,c = img.shape
		#pixel_list = [img[y,x].tolist() for y in range(0, h) for x in range(0,w)]
		for y in range(0, h):
			for x in range(0,w):
				pixel = img[y,x]
				if keep:
					#print(((color[0]-pixel[0])**2+(color[1]-pixel[1])**2+(color[2]-pixel[2])**2))
					#print(color[0]-pixel[0])
					#print(color[1]-pixel[1])
					#print(color[2]-pixel[2])
					
					
					if abs(color[0]-pixel[0])<th and abs(color[1]-pixel[1])<th and abs(color[2]-pixel[2])<th:
						imgfilt[y,x] = pixel
					else:
						imgfilt[y,x] = ncolor

				else:
					#print(((color[0]-pixel[0])**2+(color[1]-pixel[1])**2+(color[2]-pixel[2])**2))
					if abs(color[0]-pixel[0])<th and abs(color[1]-pixel[1])<th and abs(color[2]-pixel[2])<th:
						imgfilt[y,x] = ncolor
					else:
						imgfilt[y,x] = pixel


	return imgfilt

# format conversion
import colorsys

def rgb_to_hex(pixel):
	""" convert color from BGR to Hexa format """
	#rgb normal: range (0-255, 0-255, 0.255)
	#red=pixel[0]
	#green=pixel[1]
	#blue=pixel[2]

	#bgr normal: range (0-255, 0-255, 0.255)
	blue=pixel[0]
	green=pixel[1]
	red=pixel[2]
	return '#{:02x}{:02x}{:02x}'.format(red, green, blue)

def hex_to_rgb(hexc):
	rgb = []
	for i in (0, 2, 4):
		decimal = int(hexc[i:i+2], 16)
		rgb.append(decimal)
  
	return tuple(rgb)
  
def convert_rgb_to_hsv(pixel):
	""" convert color from BGR to HSV format """
	#rgb normal: range (0-255, 0-255, 0.255)
	#red=pixel[0]
	#green=pixel[1]
	#blue=pixel[2]

	#bgr normal: range (0-255, 0-255, 0.255)
	blue=pixel[0]
	green=pixel[1]
	red=pixel[2]

	#get rgb percentage: range (0-1, 0-1, 0-1 )
	red_percentage= red / float(255)
	green_percentage= green/ float(255)
	blue_percentage=blue / float(255)

	#get hsv percentage: range (0-1, 0-1, 0-1)
	color_hsv_percentage=colorsys.rgb_to_hsv(red_percentage, green_percentage, blue_percentage)

	#get opencv hsv: range (0-179, 0-255, 0-255)
	color_h=round(179*color_hsv_percentage[0])#round(360*color_hsv_percentage[0])
	color_s=round(255*color_hsv_percentage[1])
	color_v=round(255*color_hsv_percentage[2])
	color_hsv=[color_h, color_s, color_v]

	#print('color_hsv: ', color_hsv)
	return color_hsv

#### opencv utils

def click_event(event, x, y, flags, params):
	""" add a click event to display BGR and HSV color on image when click on partciular pixel
	event - event type
	x - event x position
	y - event y position
	flags - 
	params - (windows name, img object)
	
	usage : 	cv2.setMouseCallback(win_name, click_event, [win_name,img])
	"""
	global rgb_to_filter,hsv_to_filter
	if event == cv2.EVENT_LBUTTONDOWN:
		img = params[1]
		imgdisp = img.copy()
		#print(f'Coordinates: ({x}, {y})')
		rgb = imgdisp[y,x]
		hsv = convert_rgb_to_hsv(rgb)
		hexc = rgb_to_hex(rgb)

		print('rgb: {}\nhsv : {}'.format(rgb,hsv))
		font = cv2.FONT_HERSHEY_PLAIN
		cv2.putText(imgdisp, 'rgb: {}'.format(rgb), (x, y), font, 1, (255, 0, 0), 1)
		cv2.putText(imgdisp, 'hsv : {}'.format(hsv), (x, y+15), font, 1, (255, 0, 0), 1)
		cv2.putText(imgdisp, 'hex : {}'.format(hexc), (x, y+30), font, 1, (255, 0, 0), 1)
		

		cv2.imshow(params[0], imgdisp)
		#cv2.destroyWindow(params[0])
		rgb_to_filter = rgb
		hsv_to_filter = convert_rgb_to_hsv(rgb)

def main(args):
	global rgb_to_filter,hsv_to_filter
	fileName=  "./img/python_shapes_detection_base.png"
	img = cv2.imread( fileName , cv2.IMREAD_UNCHANGED)
	h,w,c = img.shape
	print("CV2 Image height: ", h," width: ", w)


	rgb_to_filter = [255,255,255]
	hsv_to_filter = [0,0,0]
	#show image with opencv
	scaleFactor = 0.1
	win_name = "original"
	cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
	cv2.imshow(win_name, img)
	cv2.setMouseCallback(win_name, click_event, [win_name,img])
	#cv2.resizeWindow(win_name, int(h*scaleFactor), int(w*scaleFactor))
	cv2.waitKey()
	
	#filter hsv color
	imghsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)


	lower = np.maximum(np.array(hsv_to_filter) - 2 , np.array([0, 0, 0]))
	upper = np.minimum(np.array(hsv_to_filter) + 2 , np.array([255, 255, 255]))
	print("hsv lower:",lower)
	print("hsv upper:",upper)
	
	mask = cv2.inRange(imghsv, lower, upper)
	result = cv2.bitwise_and(img, img, mask=mask)
	cv2.imshow('result', result)
	cv2.waitKey()

	#filter rgb color
	filtered_color = rgb_to_filter #[75, 19, 143, 255]
	print("rgb to filter",filtered_color)
	res = colorThresh(img,filtered_color,keep=True,th=30,ncolor = [0,0,0,0]) #remove all except color and replace by black
	#res = colorThresh(img,filtered_color,keep=False,th=30,ncolor = [0,0,0,0]) #remove color and replace by black
	cv2.imshow('res', res)
	cv2.setMouseCallback('res', click_event, ['res', res])
	cv2.waitKey()

	cv2.destroyAllWindows()
	return 0

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

Applications

Sources

Quitter la version mobile