fbpixel
Étiquettes : ,

Le module open-source OpenCV est un outil puissant de traitement d’image contenant des algorithmes de vision par ordinateur. Ce module est codé nativement en C++ mais une API est disponible sur Python. On l’utilise pour le traitement d’image, photo comme vidéo et l’entraînement d’intelligence artificielle de reconnaissance visuelle.

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

Nous allons voir dans ce tutoriel, les fonctions de base de traitement d’image dans OpenCV

Ouvrir, copier, afficher et enregistrer une image

fileDir="./img/"
outDir="./res/"
fileName=  "myimg.png"
img = cv2.imread( fileDir+fileName )

imgout = img.copy()
cv2.imwrite(outDir+"imout.png", imgout)

cv2.imshow('img',img)

cv2.waitKey()
cv2.destroyAllWindow()

N.B.: il est préférable d’utiliser des fichiers PNG pour avoir accès au canal alpha qui détermine la transparence.

Travailler sur les pixels

Les fichiers images sont généralement défini comme 3 matrice de la taille de l’image contenant les couleurs des pixels (Bleu, vert, rouge). La taille de l’image est défini comme le nombre de pixel en hauteur, le nombre de pixel en largeur et la profondeur (nombre de couleur). OpenCV ouvre généralement les images avec 3 channels BGR. Les images en niveau de gris n’ont qu’un seul canal

h,w,c = img.shape #height, width, color channels
for y in range(0, h):
	for x in range(0,w):
		b,g,r = img[y,x] #pixel
B, G, R = cv2.split(img) #split img into color channels

Créer une image

Vous aurez certainement besoin de créer des images pour créer des masques, par exemple. Pour cela, il vous suffit de remplir un np.array dont chaque élément correspond à un pixel

# Create a black image
img = np.zeros((512,512,3), np.uint8)

Dessiner sur une image

Il est possible de dessiner sur une image pour créer des masques, pour afficher des informations ou pour entourer des éléments, par exemple. Pour tracer des formes, il vous faudra maîtriser les coordonnées des pixels de l’image.

#cv2.line(image, pt1, pt2, color, thickness)
cv2.line(img,(256,0),(256,512),(255,255,255),2)

#cv2.rectangle(image, pt1, pt2, color, thickness)
cv2.rectangle(img,(300,60),(500,360),(255,255,255),-1)

#cv2.circle(image, ptcenter, radius, color, thickness)
cv2.circle(img,(400,210), 63, (0,0,0), -1)

#cv2.ellipse(image, center, axes, angle, startAngle, endAngle, color, thickness)
cv2.ellipse(img,(120,256),(100,50),90,0,360,(255,255,255),-1)

#cv2.polylines(image, pts, isClosed, color, thickness)
pts = np.array([[120,356],[130,306],[100,206],[120,156]], np.int32)
pts = pts.reshape((-1,1,2))
cv2.polylines(img,[pts],False,(0,0,0),6)

#cv2.putText(image, text, pt, font, fontScale, color, thickness, lineType)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'AranaCorp',(10,30), font, 1,255,2,cv2.LINE_AA)

# thickness = -1 for filled shape
# color = (b,g,r) or g for gray scale or to write on first channel
python-opencv-drawings Traitement d'image avec OpenCV et Python

Interaction utilisateur

Puisque la fonction cv2.imshow n’est pas bloquante, il faut rajouter la fonction waitKey(time_ms) pour bloquer le code avant de rendre la main ou avant que toutes les fenêtres OpenCV soient fermées.

#wait before end of program
key = cv2.waitKey(0) & 0x0FF # wait indefinitely and get input
if key == 27 or key == ord('q'): #key is ESC or q
    cv2.destroyAllWindow()
    sys.exit(0)

Il est aussi possible avec la fonction waitKey de capture les entrées clavier de l’utilisateur

# to perform several tasks according to key
while True:
    key = cv2.waitKey(30) & 0x0FF # wait 30ms and get input
    if key == 27 or key==ord('q'): #key is ESC or q
        print('end program')
        break;
    if key ==ord('a'):
        print("process img")

Modifier la taille d’une image

#specify height and width
scaled_img = cv2.resize(img, (600,300)) 

#specify scale factor and interpolation method
scaled_img = cv2.resize(img, None, fx= 0.5, fy= 0.5, interpolation= cv2.INTER_LINEAR) 
#methods cv2.INTER_LINEAR INTER_AREA, INTER_CUBIC

# specify scale factor
h,w,c = img.shape
scale_f = 0.5 
height = int(h*scale_f)
width = int(w*scale_f)
scaled_img = cv2.resize(img, (width,height))

# specify new width
h,w,c = img.shape
width = 700 
scale_f = width/w
height = int(h*scale_f)
scaled_img = cv2.resize(img, (width,height))

# use shape of img1
scaled_img = cv2.resize(img, img1.shape[1::-1])

Concaténer plusieurs images

Il est possible de concaténer des images:

  • verticalement si elles sont de même largeur (width)
  • horizontalement si elles sont de même hauteur (height)
im_v = cv2.vconcat([img1, img2]) 

im_h = cv2.hconcat([img1, img2]) 

Transformation géométrique d’une image

h,w,c = img.shape

#flip
fliph = cv2.flip(img, 0) #horizontally
flipr = cv2.flip(img, 1) #vertically

# Rotation
center = (h/2, w/2)
angle = 30
scale = 1
rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)
rotated_image = cv2.warpAffine(img, rotation_matrix, (w, h))

# Translation matrix
tx = 100
ty = 70
translation_matrix = np.array([[1, 0, tx], [0, 1, ty]], dtype=np.float32)
translated_image = cv2.warpAffine(img, translation_matrix, (w,h))


# Shearing matrix
shearX = -0.15
shearY = 0
shear_matrix = np.array([[1, shearX, 0],  [0, 1, shearY]], dtype=np.float32)
sheared_image = cv2.warpAffine(img, shear_matrix, (w, h))

Il est possible de combiner les matrices de transformation en convertissant les matrices en coordonnées homogènes et en appliquant un produit scalaire sur les matrices (A@B <=> np.dot(A,B))

# convert matrix to homogeneous coordinates
rot_hom = np.append(rotation_matrix, [[0, 0, 1]], axis=0) 
tr_hom = np.append(translation_matrix, [[0, 0, 1]], axis=0)
sh_hom = np.append(shear_matrix, [[0, 0, 1]], axis=0)
rt_mat = rot_hom@tr_hom@sh_hom # shear then translated then rotated
transform_matrix = rt_mat[:2,:]

#combined transofrm
transformed_image = cv2.warpAffine(img, transform_matrix, (w, h))

N.B. : attention à l’ordre d’application des matrices, shear S, puis translation T puis rotation T :Tr = R @ T @ S

python-opencv-image-transformation Traitement d'image avec OpenCV et Python

Opérations arithmétiques et logiques sur les images

Vous pouvez combiner des images et des masques avec des opérateurs arithmétiques et logique. N’hésitez pas à vous entraîner en créant un masque et son inverse pour comprendre et jouer avec ces opérateurs.

#isum = cv2.add(img1,img2) #add images
wsum = cv2.addWeighted(img1, 0.2, img2, 0.5, 0)  #add images with weight
sub = cv2.subtract(img1,img2) #substract images

res_and = cv2.bitwise_and(img1, img2, mask = None) # AND operator
res_or = cv2.bitwise_or(img1, img2, mask = None)  # OR operator
res_xor = cv2.bitwise_xor(img1, img2, mask = None) # XOR operator
res_not = cv2.bitwise_not(img1, mask = None)  #NOT operator

res_mask = cv2.bitwise_and(img1, img1, mask = img2) # apply mask

N.B.: pour les opérations les images img1 et img2 doivent être de même taille et de même profondeur

python-opencv-masking Traitement d'image avec OpenCV et Python

Conversion de couleur

img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # gray scale conversion 1 channel
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # HSV conversion
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # RGB conversion

Filtrer une image

Il existe différentes techniques pour filtrer une image notamment par couleur, par masque ou par niveau.

# contrast and brightness
alpha = 1.5 # Contrast control (1.0-3.0)
beta = 30 # Brightness control (0-100)
contrast = cv2.convertScaleAbs(img, alpha=alpha, beta=beta)
cv2.imshow('contrast',contrast)

# niveau
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 
ret, thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY) 
# cv2.THRESH_BINARY_INV,cv2.THRESH_TRUNC,cv2.THRESH_TOZERO,cv2.THRESH_TOZERO_INV
cv2.imshow('thresh',thresh)

Il existe également des algorithmes pour filtrer des images en appliquant à un pixel des calculs sur les pixels voisins. Ces filtres peuvent permettre d’améliorer les images ou de les préparer à l’entraînement de modèle de reconnaissance visuelle.

  • floutage gaussien: utilisé pour réduire le bruit et les détails
  • floutage median: utilisé dans certains cas pour réduire le bruit tout en conservant les contours
  • floutage bilatéral: filtre de bruit permettant de conserver les contours
# Gaussian Blur 
Gaussian = cv2.GaussianBlur(image, (7, 7), 0) 
cv2.imshow('Gaussian Blurring', Gaussian) 

# Median Blur 
median = cv2.medianBlur(image, 5) 
cv2.imshow('Median Blurring', median) 

# Bilateral Blur 
bilateral = cv2.bilateralFilter(image, 9, 75, 75) 
cv2.imshow('Bilateral Blurring', bilateral) 

cv2.waitKey(0) 

N.B.: vous pouvez flouter vos masques pour donner des effets intéressant

python-opencv-image-blurring Traitement d'image avec OpenCV et Python

Analyse de couleur d’une image

Il peut être intéressant avant de travailler sur une image de connaître la distribution en terme de couleur pour savoir sur quel fonction travailler

def displayImgHist():
	#compute histograms
	crange = [10,250]
	#cv2.calcHist(images, channel, mask, histSize, ranges[, hist[, accumulate]])
	histb = cv2.calcHist([img],[0],None,[256],crange)
	histg = cv2.calcHist([img],[1],None,[256],crange)
	histr = cv2.calcHist([img],[2],None,[256],crange)

	#plot subplots
	imgrgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #matplotlib in RGB

	fig = plt.figure("Image Color Analysis",figsize=(10, 8))
	(col1fig, col2fig) = fig.subfigures(1, 2)
	col1_ax = col1fig.add_subplot()
	col2_axs = col2fig.subplots(4,1, sharex=True)
	col1_ax.imshow(imgrgb)
	col2_axs[0].plot(histb,'b')
	col2_axs[1].plot(histg,'g')
	col2_axs[2].plot(histr,'r')
	col2_axs[3].hist(img.ravel(),256,crange)

	plt.show()
		
python-opencv-image-analysis Traitement d'image avec OpenCV et Python

Gestion d’évènement

Nous avons vu que nous pouvions capter les entrées claviers.

while True:
    key = cv2.waitKey(30) & 0x0FF # wait 30ms and get input
    if key == 27 or key==ord('q'): #key is ESC or q
        print('end program')
        break;
    if key ==ord('a'):
        print("process img")

Nous pouvons également détecter la souris ou des widget pour rendre l’image interactive

def click_event(event, x, y, flags, params):
	if event == cv2.EVENT_LBUTTONDOWN:
		rgb = img[y,x]
		print('[{x}, {y}] : rgb: ({})'.format(rgb))
cv2.setMouseCallback(win_name, click_event, [win_name])

Exemple de zoom à l’aide de la molette de la souris

base_img = cv2.imread( fileName )
img = base_img.copy()

zoom = 1
min_zoom = 1
max_zoom = 5


def zoom_scroll(event, x, y, flags, param):
    #global base_img, zoom, min_zoom, max_zoom
    global zoom
    window_name = param[0]
    base_img =  param[1]
    if event == cv2.EVENT_MOUSEWHEEL:
        if flags > 0:
            zoom *= 1.1
            zoom = min(zoom, max_zoom)
        else:
            zoom /= 1.1
            zoom = max(zoom, min_zoom)

        img = base_img.copy()

        # Calculate zoomed-in image size
        new_width = round(img.shape[1] / zoom)
        new_height = round(img.shape[0] / zoom)

        # Calculate offset
        x_offset = round(x - (x / zoom))
        y_offset = round(y - (y / zoom))

        # Crop image
        img = img[
            y_offset : y_offset + new_height,
            x_offset : x_offset + new_width,
        ]

        # Stretch image to full size
        img = cv2.resize(img, (base_img.shape[1], base_img.shape[0]))
        cv2.imshow(window_name, img)

cv2.imshow('orig', img)
cv2.setMouseCallback('orig', zoom_scroll, ['orig',base_img])

Un widget pratique est le trackbar permettant de faire évoluer une variable comme la valeur d’un filtre

alpha = 1
beta = 0
def change_contrast(value):
	global alpha
	alpha = (3.0-1.0)*value/100+1. # Contrast control (1.0-3.0)
	contrast = cv2.convertScaleAbs(img, alpha=alpha, beta=beta)
	cv2.imshow('orig',contrast)		

def change_brightness(value):
	global beta
	beta = value # Brightness control (0-100)
	contrast = cv2.convertScaleAbs(img, alpha=alpha, beta=beta)
	cv2.imshow('orig',contrast)		
			
cv2.createTrackbar('contrast', 'orig', 0, 100, change_contrast)
cv2.createTrackbar('bright', 'orig', 0, 100, change_brightness)
python-opencv-trackbar Traitement d'image avec OpenCV et Python

Format Vidéo

Le module OpenCV peut également gérer des images en temps réel provenant d’un fichier vidéo ou d’une caméra. Vous pouvez donc utiliser ce tutoriel pour le traitement d’une vidéo image par image.

import cv2
cap = cv2.VideoCapture('myvideo.mp4') #cv2.VideoCapture(0) or url

while(cap.isOpened()):
    ret, frame = cap.read()
    if ret == True:
        cv2.imshow('Frame', frame)
        
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break
    else:
        break

cap.release()
cv2.destroyAllWindows()

Applications

Sources