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()