appel et notation: https://docs.google.com/spreadsheets/d/1577tnArzUkMi6LHTgeE2kXAHJZNB4IIMzbCL-Mv3txQ/edit?gid=0#gid=0
pour travailler en double écran en C02, utiliser la prise vga et configurer en 1920*1080 à gauche de mon écran et utiliser vscode pour zoomer sur le code
page du module de l'année dernière: [[visionbut3-2023]]
Cours 2023 provisoire: https://bvdp.inetdoc.net/files/iut/cours_vision_but3_2023.pdf
=====Planning du module=====
====CM 4/11====
* initiation à la vision
* spectre électromagnétique
* comparaison caméras avec la vision humaine
* capteurs d'images passifs
====TD/TP 4/11====
* installation des outils
* prise en main OpenCV
* chargement et affichage image
* caractérisation d'une image bitmap: résolution, rapport d'aspect, format, poids, compression
* manipulation image: ROI
* tracé de textes, lignes, rectangles, croix
====TD/TP 6/11====
* espaces de couleurs RGBA, YUV, HSV, grey
* génération de dégradé par synthèse
* décimation chromatique
* segmentation par seuillage dans l'espace HSV
* effacement fond vert par opérateurs de masquage
SUPERS TUTOS: https://www.geeksforgeeks.org/opencv-python-tutorial/
=====Installations des outils pour travailler chez vous=====
===Visual Studio Code===
Installer Visual Studio Code: https://code.visualstudio.com/download
===Nomacs===
Logiciel pour visualiser les images, alternative à geeqie: https://nomacs.org/
version portable: https://github.com/nomacs/nomacs/releases/latest/download/nomacs-portable-win.zip
régler la langue en anglais
Cliquer sur Panels->Toolbars->Statusbar pour afficher les informations sur l'image en bas à droite.
Pour relever des coordonnées de pixels dans les images, lire en bas a gauche.
===Installation OpenCV===
Taper dans une invite de commande:
pip install numpy matplotlib opencv-python --upgrade
ne pas installer la version headless qui ne permet pas la visualisation des images
pip install opencv-contrib-python-headless
pip uninstall opencv-contrib-python-headless
=====Premier programme Python/OpenCV=====
Créer un dossier sur P:\etu-new\2024-2025\s5aii\s5aiiX\nom_prenom\tpvision et y télécharger le fichier suivant:
import numpy as np
import cv2
import math
print("version openCV:"+str( cv2.__version__ ))
Lancer Visual Studio Code:
Bureau->Applications locales geii->Visual Studio Code
Ouvrir le dossier que vous venez de créer:
File->Open Folder, choisir, puis cliquer sur sélectionner le dossier
Run->Start Debugging
si VSCode indique qu'il n'y a pas d'extension python; cliquer sur install coté de Python IntelliSense(Pylance) Microsoft
python3
Python 3.9.7
import cv2 as cv
cv.__version__
'4.8.1'
probleme: il faut caster en int les l20 de homographie.py et a completer sur wiki:
image=cv.line(image, (int(srcPoints[n][0]),int(srcPoints[n][1])),(int(srcPoints[(n+1)%4][0]),int(srcPoints[(n+1)%4][1])), color, thickness)
====Documentation OpenCV====
https://pypi.org/project/opencv-python/
lien vers documentation openCV: https://docs.opencv.org/4.8.0/
====TD1 Prise en main librairie OpenCV via Python=====
Nous allons commencer par prendre en main la librairie OpenCV via le langage Python:
https://opencv24-python-tutorials.readthedocs.io/en/latest/py_tutorials/py_tutorials.html
Image à télécharger dans votre dossier de projet
{{https://bvdp.inetdoc.net/files/iut/vision/img.jpg}}
=====Solution TD1=====
import numpy as np
import cv2
import math
print("version openCV:"+str( cv2.__version__ ))
couleur=True
if not couleur:
# Load an color image in grayscale
img = cv2.imread('img.jpg',cv2.IMREAD_GRAYSCALE)
if img is None:
print("probleme chargement image")
exit(-1)
print(img)
print("img.shape:" +str(img.shape))
# Draw a diagonal blue line with thickness of 5 px
cv2.line(img,(0,0),(img.shape[1]-1,img.shape[0]-1),255,5)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'BUT3',(10,500), font, 4,255,2,cv2.LINE_AA )
cv2.imwrite('imggray.png',img)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
# Load an color image in grayscale
img = cv2.imread('img.jpg',cv2.IMREAD_COLOR)
if img is None:
print("probleme chargement image")
exit(-1)
print(img)
print("img.shape:" +str(img.shape))
# Draw a diagonal blue line with thickness of 5 px
cv2.line(img,(0,0),(img.shape[1]-1,img.shape[0]-1),(0,0,255),5)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'BUT3',(10,500), font, 4,(0,255,0),2,cv2.LINE_AA )
px = img[200,100] # v,u
print("px:"+str(px))
img[200,100] = [255,0,255]
for up in range(0,255):
for vp in range(0,255):
img[200+vp,100+up] = [up,0,vp]
u0=692
v0=252
larg=60
haut=60
u1=434
v1=218
ROI=img[v0:v0+haut,u0:u0+larg]
cv2.imwrite('roi.png',ROI)
#copie directe
#img[v1:v1+haut,u1:u1+larg]=ROI
#copie pixel par pixel en faisant une symétrie par rapport à l'axe vertical (inefficace en python)
for u in range(0,larg-1):
for v in range(0,haut-1):
img[v1+v,u1+u]=ROI[v,larg-u-1]
for v1 in range(100,400,100):
u1=int(v1*1.5)+220
img[v1:v1+haut,u1:u1+larg]=ROI
cv2.imwrite('imgcolor.png',img)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
====Probleme fonction zoom pour imshow====
https://www.bing.com/search?pglt=2083&q=cv2.imshow+zoom&cvid=e7b4c8a9439b4c3190f67cd54b57fada&gs_lcrp=EgRlZGdlKgYIABBFGDkyBggAEEUYOdIBCDI1ODNqMGoxqAIAsAIA&FORM=ANNTA1&DAF0=1&PC=U531
https://stackoverflow.com/questions/50533775/zooming-functionality-in-opencv-imshow-in-windows
https://stackoverflow.com/questions/28595958/creating-trackbars-to-scroll-large-image-in-opencv-python/33293804#33293804
https://stackoverflow.com/questions/46637050/imshow-in-c-toolbar-disappeared
The toolbar is only present if you have OpenCV installed with a QT GUI backend (doesn't work with GTK). There are two ways to install with QT backend -
Build from source using cmake and specify WITH_QT=ON (this is the long and hard method).
The easier one and the one that worked for me - create a new virtual environment with Conda and type in conda install -c menpo opencv in terminal. Try running the code and if the issue still persists, type in the following command - pip install --upgrade pip followed by pip install opencv-contrib-python
=====TD2=====
import numpy as np
import cv2
import math
print("version openCV:"+str( cv2.__version__ ))
flags = [i for i in dir(cv2) if i.startswith('COLOR_')]
print("conversions supportées:"+ str(flags))
#exit(0)
#Load an color image in grayscale
img = cv2.imread('img.jpg',0)
print("img:" +str(img) )
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Create a black image
#img = np.zeros((512,512,3), np.uint8)
# Draw a diagonal blue line with thickness of 5 px
cv2.line(img,(0,0),(511,511),255,5,cv2.LINE_AA)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'BUT3',(10,500), font, 4,255,2,cv2.LINE_AA)
cv2.imwrite('imggray.png',img)
#load image in color
img = cv2.imread('img.jpg',1)
px = img[100,100]
print("px:"+str(px))
img[200,100] = [255,0,0]
px = img[200,100]
print("px:"+str(px))
for i in range(0,255):
for j in range(0,255):
img[200+j,100+i]=[i,j,0]
print("img.shape:"+str(img.shape))
print("img.dtype:"+str(img.dtype))
xo=692
yo=252
larg=60
haut=60
x1=434
y1=218
ROI = img[yo:yo+haut, xo:xo+larg]
for i in range(0,3):
img[y1+i*100:y1+haut+i*100, x1:x1+larg] = ROI
# Convert BGR to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of blue color in HSV
lower_blue = np.array([110,50,50])
upper_blue = np.array([130,255,255])
# Threshold the HSV image to get only blue colors
mask = cv2.inRange(hsv, lower_blue, upper_blue)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(img,img, mask= mask)
xgazon=350
ygazon=618
print("gazon:"+str(hsv[ygazon,xgazon]))
#gazon:[ 36 171 183]
lower_gazon = np.array([26,50,50])
upper_gazon= np.array([46,255,255])
# Threshold the HSV image to get only blue colors
mask = cv2.inRange(hsv, lower_gazon, upper_gazon)
# Bitwise-AND mask and original image
mask= cv2.bitwise_not( mask)
res = cv2.bitwise_and(img,img, mask= mask)
cv2.imwrite('imgres.png',res)
cv2.imshow('image res',res)
cv2.imwrite('imgcolor.png',img)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
====TD2 Espaces colorimétriques=====
Ressources: https://opencv24-python-tutorials.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_colorspaces/py_colorspaces.html#
Espace colorimétrique YUV: https://fr.wikipedia.org/wiki/YUV
Décimation chromatique en YUV: https://www.latelierducable.com/tv-televiseur/yuv-420-ycbcr-422-rgb-444-cest-quoi-le-chroma-subsampling/ et https://wiki.videolan.org/YUV/
Espace colorimétrique HSV (TSV en français): https://fr.wikipedia.org/wiki/Teinte_Saturation_Valeur
import numpy as np
import cv2
import math
print("version openCV:"+str( cv2.__version__ ))
flags = [i for i in dir(cv2) if i.startswith('COLOR_')]
print(flags)
couleur=True
if not couleur:
# Load an color image in grayscale
img = cv2.imread('img.jpg',cv2.IMREAD_GRAYSCALE)
if img is None:
print("probleme chargement image")
exit(-1)
print(img)
print("img.shape:" +str(img.shape))
# Draw a diagonal blue line with thickness of 5 px
cv2.line(img,(0,0),(img.shape[1]-1,img.shape[0]-1),255,5)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'BUT3',(10,500), font, 4,255,2,cv2.LINE_AA )
cv2.imwrite('imggray.png',img)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
# Load an color image in grayscale
img = cv2.imread('img.jpg',cv2.IMREAD_COLOR)
if img is None:
print("probleme chargement image")
exit(-1)
print(img)
print("img.shape:" +str(img.shape))
# Draw a diagonal blue line with thickness of 5 px
cv2.line(img,(0,0),(img.shape[1]-1,img.shape[0]-1),(0,0,255),5)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'BUT3',(10,500), font, 4,(0,255,0),2,cv2.LINE_AA )
px = img[200,100] # v,u
print("px:"+str(px))
img[200,100] = [255,0,255]
for up in range(0,255):
for vp in range(0,255):
img[200+vp,100+up] = [up,0,vp]
u0=692
v0=252
larg=60
haut=60
u1=434
v1=218
ROI=img[v0:v0+haut,u0:u0+larg]
cv2.imwrite('roi.png',ROI)
#copie directe
#img[v1:v1+haut,u1:u1+larg]=ROI
#copie pixel par pixel en faisant une symétrie par rapport à l'axe vertical (inefficace en python)
for u in range(0,larg-1):
for v in range(0,haut-1):
img[v1+v,u1+u]=ROI[v,larg-u-1]
for v1 in range(100,400,100):
u1=int(v1*1.5)+220
img[v1:v1+haut,u1:u1+larg]=ROI
cv2.imwrite('imgcolor.png',img)
# Convert BGR to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of green color in HSV
lower_green = np.array([26,50,50])
upper_green = np.array([46,255,225])
# Threshold the HSV image to get only green colors
mask = cv2.inRange(hsv, lower_green, upper_green)
mask=cv2.bitwise_not(mask)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(img,img, mask= mask)
cv2.imshow('image',res)
cv2.waitKey(0)
cv2.destroyAllWindows()
=====Détection et comptage d'objets=====
Image avant traitement:
{{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/riz.jpg}}
{{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/riz2.jpg}}
====Code AII1====
import numpy as np
import cv2
import math
from matplotlib import pyplot as plt
print("version openCV:"+str( cv2.__version__ ))
#niveau de gris
if True:
# Load an color image in grayscale
img = cv2.imread('riz2.jpg',0)
print("img.dtype : "+ str(img.dtype))
print("img.shape : "+ str(img.shape))
#le premier scalaire est la hauteur de l'image
if img is None:
print("Fichier image foireux")
exit()
print(img)
'''
kernel = np.ones((5,5),np.float32)/25
img = cv2.filter2D(img,-1,kernel)
img = cv2.filter2D(img,-1,kernel)
img = cv2.filter2D(img,-1,kernel)
'''
#kernel = np.ones((1,1),np.float32)/4
#img = cv2.filter2D(img,-1,kernel)
#plt.hist(img.ravel(),256,[0,256]); plt.show()
ret,thresh1 = cv2.threshold(img,200,255,cv2.THRESH_BINARY)
# Otsu's thresholding
ret2,thresh2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
print("ret2:"+str(ret2))
kernel = np.ones((3,3),np.uint8)
erosion = cv2.erode(thresh2,kernel,iterations = 2)
dilatation = cv2.dilate(erosion,kernel,iterations = 1)
#imgxor = cv2.bitwise_xor(thresh2,erosion)
imgxor = cv2.bitwise_xor(thresh2,dilatation)
imcolor = cv2.cvtColor(dilatation,cv2.COLOR_GRAY2BGR)
imcolor[4,2]=(255,0,0)
contours, hierarchy = cv2.findContours(dilatation,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
compteur=0
for data in contours:
print("The contours have this data "+str(data))
area = cv2.contourArea(data)
hull = cv2.convexHull(data)
hull_area = cv2.contourArea(hull)
solidity = float(area)/hull_area
(x,y),(MA,ma),angle = cv2.fitEllipse(data)
if MA=20 and area<=200 and solidity>0.8 and aspect>2:
#cv2.drawContours(imcolor,data,-1,(0,255,0),1)
cv2.line(imcolor,(x,y-2),(x,y+2),(0,255,0),1)
cv2.line(imcolor,(x-2,y),(x+2,y),(0,255,0),1)
else:
cv2.drawContours(imcolor,data,-1,(0,0,255),1)
compteur=compteur+1
cv2.imwrite('imggray.png',img)
cv2.imwrite('imgthresh.png',thresh2)
cv2.imwrite('imgerosion.png',erosion)
cv2.imwrite('imgdilatation.png',dilatation)
cv2.imwrite('imgcolor.png',imcolor)
cv2.imwrite('imgxor.png',imgxor)
cv2.imshow('image',imcolor)
cv2.waitKey(0)
cv2.destroyAllWindows()
=====Exercice à faire=====
Images de cartes:
{{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/cartes2.png}}
Images des symboles avec différentes rotations:
{{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/symboles.png}}
=====TD géométrie pour la vision=====
Solution pour le rendu de cube:
import cv2
import numpy as np
import math
print("==============================================================")
taillecube=1
listeM = np.float32([[0,0,0], [taillecube,0,0],[taillecube,taillecube,0], [0,taillecube,0],
[0,0,taillecube], [taillecube,0,taillecube],[taillecube,taillecube,taillecube], [0,taillecube,taillecube]])
print("listeM:"+str(listeM))
listeseg=[[0,1],[1,2],[2,3],[3,0],[4,5],[5,6],[6,7],[7,4],[0,4],[1,5],[2,6],[3,7]]
print("listeseg:"+str(listeseg))
print("==============================================================")
cRw=np.float32([[1,0,0],[0,-1,0],[0,0,-1]])
cTw=np.float32([[0],[0],[3]])
cRTw = np.hstack((cRw,cTw))
cRTw = np.vstack((cRTw,[0,0,0,1]))
print("cRTw:"+str(cRTw))
print("==============================================================")
eu=0.00001 #10um
ev=eu
#champ de vision en degré
fovhdegres=100
demifovh=(fovhdegres/2)*math.pi/180
alphau=400/math.tan(demifovh)
print("alphau:"+str(alphau))
f=alphau*eu
print("f:"+str(f)+" m")
alphav=alphau
demifovv=math.atan(300/alphav)
fovvdegres=demifovv*2*180/math.pi
print("fovvdegres:"+str(fovvdegres))
#=======================================
pu=400
pv=300
iCc=np.float32([[alphau,0,pu,0],[0,alphav,pv,0],[0,0,1,0]])
print("iCc:"+str(iCc))
iCw=iCc@cRTw
print("iCw:"+str(iCw))
#------------------
def projeterPoint(M):
print("M:"+str(M))
M = np.append(M, 1)
print("M:"+str(M))
m=iCw@M
#deshomogénéisation
mu=m[0]/m[2]
mv=m[1]/m[2]
#arrondi pour avoir des positions entières
mu=int(round(mu,0))
mv=int(round(mv,0))
print("mu:"+str(mu))
print("mv:"+str(mv))
return (mu,mv)
#------------------
M= np.float32([0,0,0])
m= projeterPoint(M)
# Create a black image
img = np.zeros((pv*2,pu*2), np.uint8)
for seg in listeseg:
print("seg:"+str(seg))
m1=projeterPoint(listeM[seg[0]])
m2=projeterPoint(listeM[seg[1]])
print("m1:"+str(m1))
print("m2:"+str(m2))
cv2.line(img,m1,m2,255,1,cv2.LINE_AA)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
=====TP reconstruction 3D monoculaire et par stéréovision=====
{{https://bvdp.inetdoc.net/files/iut/vision/imaobj1_rect.jpg}}
{{https://bvdp.inetdoc.net/files/iut/vision/imaobj2_rect.jpg}}
#/usr/bin/python3
import numpy as np
import cv2
import glob
filename1="imaobj1_rect.jpg"
filename2="imaobj2_rect.jpg"
img1 = cv2.imread(filename1)
if(len(img1.shape)<3):
img1 = cv2.cvtColor(img1,cv2.COLOR_GRAY2BGR)
img2 = cv2.imread(filename2)
if(len(img2.shape)<3):
img2 = cv2.cvtColor(img2,cv2.COLOR_GRAY2BGR)
h, w = img1.shape[:2]
print("h:"+str(h))
print("w:"+str(w))
#------------------
def projeterPoint(M,iCw):
#print("M:"+str(M))
M = np.append(M, 1)
#print("M:"+str(M))
m=iCw@M
#deshomogénéisation
mu=m[0]/m[2]
mv=m[1]/m[2]
#arrondi pour avoir des positions entières
mu=int(round(mu,0))
mv=int(round(mv,0))
#print("mu:"+str(mu))
#print("mv:"+str(mv))
return (mu,mv)
#------------------
alphau=2149.349810656287900
alphav=2146.276553276586100
pu=412.626457723086160
pv=355.723365602020240
rvec1 = np.float32([ -3.125480e-001 , -2.316874e+000 , 1.048207e-001 ])
tvec1 = 0.001 * np.float32([ [4.813190e+001] , [-1.258122e+002] , [1.066916e+003] ])
rvec2 = np.float32([ 6.592006e-001 , -2.262313e+000 , -3.064294e-001])
tvec2 = 0.001 * np.float32([ [2.557518e+001] , [-6.888417e+001] , [1.091155e+003] ])
matR1,jac1=cv2.Rodrigues(rvec1)
print("matR1:"+str(matR1))
matR2,jac2=cv2.Rodrigues(rvec2)
print("matR2:"+str(matR2))
#affichage comme TD synthese
cRT1w=np.hstack((matR1,tvec1))
cRT1w = np.vstack((cRT1w,[0,0,0,1]))
cRT2w=np.hstack((matR2,tvec2))
cRT2w = np.vstack((cRT2w,[0,0,0,1]))
print("cRTw: "+str(cRT1w))
#print("mtx: "+str(mtx))
iCc=np.float32([[alphau,0,pu,0],[0,alphav,pv,0],[0,0,1,0]])
print("iCc:"+str(iCc))
iC1w=iCc@cRT1w
print("iC1w:"+str(iC1w))
iC2w=iCc@cRT2w
print("iC2w:"+str(iC2w))
#---------------------------
#TODO: afficher un repère de 10cm de coté sur chaque image sur chaque image
#---------------------------
#liste des points pour les 2 image, mu puis mv
listem1 = np.int_([[490,96], [329,83] , [510,119] , [359,107],
[493,156],[339,151],[525,189],[373,180],[507,230],
[353,223],[534,253],[385,250],[514,302],[361,304],
[544,333],[393,336],[523,377],[369,383],
[400,252], # ce point n'est pas observé dans l'image
[254,264]])
listem2 = np.int_([[448,194], [290,184] , [450,233] , [297,228],
[420,249],[265,243],[427,297],[272,294],[391,311],
[241,308],[399,349],[250,354],[366,364],[212,367],
[371,408],[216,417],[338,416],[179,420],
[316,263], # ce point n'est pas observé dans l'image
[162,228]])
if listem2.shape[0]!=listem1.shape[0]:
print("les 2 listes de points doivent avoir le même nombre de points")
exit(-1)
listeM=np.zeros((listem2.shape[0],3), np.float32)
#valeur par defaut pour le point 3D reconstruit
X = np.zeros((3,1), np.float32)
Xz0 = np.zeros((3,1), np.float32)
for i,(mu,mv) in enumerate(listem1):
img1 = cv2.circle(img1,(mu,mv), 3,(255,0,255), -1)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img1,str(i),(mu+10,mv), font, 1,(255,0,255),2,cv2.LINE_AA)
#récupère le point correspondant dans l'image 2
mu2=listem2[i][0]
mv2=listem2[i][1]
img2 = cv2.circle(img2,(mu2,mv2), 3,(255,0,255), -1)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img2,str(i),(mu2+10,mv2), font, 1,(255,0,255),2,cv2.LINE_AA)
#---------------------------
#TODO: Remplir listeM[i][:] avec les coordonnées 3D du point i
#---------------------------
#image composée à partir des 2 images 1 et 2
imagecomposee = np.hstack((img1,img2))
filenameout="imaobj_rect-out.jpg"
cv2.imwrite(filenameout,imagecomposee)
cv2.imshow('dst',imagecomposee)
cv2.waitKey(-1)
cv2.destroyAllWindows()
#-----------------------------------------------
#matplot 3D
#https://www.geeksforgeeks.org/three-dimensional-plotting-in-python-using-matplotlib/
#mettre à True pour activer le rendu 3D après fermeture de la fenêtre opencv
if False:
# importing mplot3d toolkits
from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
# syntax for 3-D projection
ax = plt.axes(projection ='3d')
# defining axes
x = listeM[:,0]
y = listeM[:,1]
z = listeM[:,2]
ax.scatter(x, y, z)
ax.axis('equal')
# syntax for plotting
ax.set_title('3d Scatter plot geeks for geeks')
plt.show()