Table des matières

Cours 2023 provisoire: https://bvdp.inetdoc.net/files/iut/cours_vision_but3_2023.pdf

Planning du module

CM 4/11

TD/TP 4/11

TD/TP 6/11

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

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:

test1.py
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

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

bvdp.inetdoc.net_files_iut_vision_img.jpg

Solution TD1

td1.py
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()

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

td2.py
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: bvdp.inetdoc.net_files_iut_tp_lpro_vision_riz.jpg

bvdp.inetdoc.net_files_iut_tp_lpro_vision_riz2.jpg

Code AII1

riz.py
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<ma:
            tmp=MA
            MA=ma
            ma=tmp
        aspect=MA/ma
 
        x=int(x)
        y=int(y)
 
 
        #if compteur%2==0:
        if area>=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:

Images des symboles avec différentes rotations:

TD géométrie pour la vision

Solution pour le rendu de cube:

synthese.py
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

bvdp.inetdoc.net_files_iut_vision_imaobj1_rect.jpg

bvdp.inetdoc.net_files_iut_vision_imaobj2_rect.jpg

tpstereo.py
#/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()