Bons exemples: https://siera-estaca.fr/opencv-python/
13/01: 2hCM
14/01: 2hTD init openCV
17/01: 2h +2h utilisation via ssh + bases espace couleur
23/01: 2hCM + 2h TD (seuillage et op morphologiques)
29/01: 2hTD grains de riz
=====Cours=====
https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/conf.pdf
=====Matériel et logiciel utilisés=====
Nous allons utiliser une carte raspberry Pi à laquelle un module caméra et une carte d'extension SenseHat on été ajoutées.
Les logiciels utilisés sont une distribution Linux Raspbian à laquelle les paquets suivants ont été ajoutés:
sudo apt-get update && sudo apt-get upgrade
sudo apt-get install python3-opencv libjasper-dev libqtgui4 libqt4-test libtiff5 libgstreamer1.0-0 libqtcore4 libopenexr23 libatlas3-base libsz2 build-essential cmake pkg-config libjpeg-dev libtiff5-dev libjasper-dev libpng-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev libfontconfig1-dev libcairo2-dev libgdk-pixbuf2.0-dev libpango1.0-dev libgtk2.0-dev libgtk-3-dev libatlas-base-dev gfortran libhdf5-dev libhdf5-serial-dev libhdf5-103 libqtgui4 libqtwebkit4 libqt4-test python3-pyqt5 python3-dev nano python3-pyqt4 screen imagemagick gitk iotop lsof geeqie
Les paquets python suivants ont également été installés:
sudo python3 -m pip install imutils
sudo python3 -m pip install opencv-python
sudo python3 -m pip install matplotlib
sudo python3 -m pip install rubik_solver
sudo python3 -m pip install picamera[array]
====Séance 1: Cours Magistral=====
Image, types de caméras, signal numérique, stockage, bases algorithmiques, filtrage, notion de longueur d'onde...
====Séance 2: TP Prise en main librairie OpenCV=====
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
ancienne url: https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_tutorials.html
Nous utiliserons l'environnement idle3 pour développer et tester.
{{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/messi5.jpg}}
lien vers documentation openCV: https://docs.opencv.org/3.4.16/
====Chargement et sauvegarde d'images====
Solution:
#!/usr/bin/python3
import numpy as np
import cv2
import math
def afficherCercle(cu,cv,r,nbpoints,image):
for i in range(0,nbpoints):
angle=2*3.14*i/nbpoints
#print(angle)
u=int(cu+r*math.cos(angle))
v=int(cv+r*math.sin(angle))
img[v][u]=0
print('salut')
a=65
b=a+9
print(b)
#chargement d'une image non présente sur le disque
img = cv2.imread('messi5.jpg',0)
print(str(img))
#img vaut None si l'image n'existe pas
img = cv2.imread('robot.jpeg',0)
print(str(img))
#img ne vaut pas None si l'image existe
img[60][500]=0
v=60
for u in range(10,200):
img[v][u]=0
u=60
for v in range(10,200):
img[v][u]=0
u=80
for v in range(10,200,2):
img[v][u]=0
for r in range(10,100,10):
afficherCercle(200+r,300,r,500,img)
cv2.imwrite('robot_cercle.jpeg',img)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
pour rendre le programme exécutable depuis le terminal
chmod a+x test.py
Image avant traitement:
{{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/robot.jpeg}}
Image après traitement:
{{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/robot_cercle.jpeg}}
====Séance 3: TP Utilisation via SSH=====
====Utilisation en réseau====
Relever l'adresse IP de la carte raspberry pi, ouvrir un terminal et saisir:
ip addr ls |grep inet |grep eth0
Relever l'adresse IPV4 fournie:
inet 10.6.15.115/24 brd 10.6.15.255 scope global eth0
Depuis le PC étudiant, vérifier si la raspberry pi est joignable (CTRL+C pour quitter la commande)
ping 10.6.15.115
Si la carte répond, s'y connecter en Secure SHell avec export de l'affichage:
slogin -Y meteo@10.6.15.115
Puis utiliser le terminal pour faire ce que vous voulez sur la raspberry pi.
Afin de gagner du temps, faire un programme sur le PC pour automatiser la connexion au raspberry pi:
#!/bin/bash
slogin -Y meteo@10.6.15.115
Rendre le script exécutable:
chmod +x rpi.sh
Pour l'exécuter:
./rpi.sh
Même principe pour rendre exécutable un programme python3, en ajoutant au début du programme python:
#!/usr/bin/env python3
Pour éditer les programmes sur la raspberry pi:
idle3 nomduprogrammepython.py
====Séance 4: TD Opérations de base sur les images=====
Espaces colorimétriques et gamut: https://en.wikipedia.org/wiki/Color_spaces_with_RGB_primaries
Représentations des couleurs dans différents espaces colorimétriques (sRGB,CMYK, YUV,YCrCb,HSV):
sRGB:
https://fr.wikipedia.org/wiki/SRGB
https://en.wikipedia.org/wiki/Color_spaces_with_RGB_primaries
YUV:
https://fr.wikipedia.org/wiki/YUV
https://answers.opencv.org/question/56762/opencv-bgr-to-yuv-conversion/
décimation chromatique: https://en.wikipedia.org/wiki/YUV#Y%E2%80%B2UV420p_(and_Y%E2%80%B2V12_or_YV12)_to_RGB888_conversion
YCbCr:
https://fr.wikipedia.org/wiki/YCbCr
CMYK
https://gist.github.com/wyudong/9c392578c6247e7d1d28#file-rgb2cmyk-cpp-L2
HSV:
https://fr.wikipedia.org/wiki/Teinte_saturation_lumi%C3%A8re
https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both
Filtre de bayer pour capteur couleur: https://fr.wikipedia.org/wiki/Matrice_de_Bayer
Pour afficher les informations sur une images avec imageMagick
identify -verbose image.jpg
===Tuto OpenCV:===
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_core/py_basic_ops/py_basic_ops.html#basic-ops
Solution:
#!/usr/bin/python3
import cv2
import numpy as np
img = cv2.imread('messi5.jpg')
px = img[100,100]
print(px)
#[157 166 200]
#organisé en B,G,R
img[100,100]=[0,255,0]
#conversion de BGR vers YUV
img_out = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
#recopie de la luminance Y sur les 3 canaux BGR pour affichage en niveau de gris
for x in range (0,img.shape[0]):
for y in range (0,img.shape[1]):
img_out[x,y,1]=img_out[x,y,0]
img_out[x,y,2]=img_out[x,y,0]
#génération d'une imagette de 60x60 pixels (cropping)
ball = img[280:340, 330:390]
#recopie de l'imagette à différentes positions dans l'image
for b in range(0,4):
img_out[50+b*50: 50+b*50 +ball.shape[0] , 50+b*100: 50+b*100 +ball.shape[1]] = ball
cv2.imwrite('messi5_multi_ballon.jpg',img_out)
#affichage
cv2.imshow('image',img_out)
cv2.waitKey(0)
cv2.destroyAllWindows()
Image avant traitement:
{{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/messi5.jpg}}
Image après traitement:
{{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/messi5_multi_ballon.jpg}}
====Séance 5: Cours Magistral=====
Différentes contraintes pour choisir un système de perception: capteur, calculateur etc. Traitement des images.
====Séance 6: TD Seuillage et opérateurs morphologiques=====
====Séance 7: TD application de détection de grains de riz=====
Solution:
#B. Vandeportaele 29/01/2020
import numpy as np
import cv2
#########################################"
def mesureStats(cnt):
#voir https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contour_properties/py_contour_properties.html
M = cv2.moments(cnt)
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt,True)
#x,y,w,h = cv2.boundingRect(cnt)
#aspect_ratio = float(w)/h
rect = cv2.minAreaRect(cnt)
#print("rect:"+str(rect))
l=rect[1][1]
m=rect[1][0]
w=max(l,m)
h=min(l,m)
#print("w:"+str(w))
#print("h:"+str(h))
if h>0:
aspect_ratio = float(w)/h
else:
aspect_ratio = 10000000
area = cv2.contourArea(cnt)
hull = cv2.convexHull(cnt)
hull_area = cv2.contourArea(hull)
if hull_area>0:
solidity = float(area)/hull_area
else:
solidity=1000000
return area,perimeter,aspect_ratio,solidity
#########################################
def isGrainDeRiz(cnt):
area,perimeter,aspect_ratio,solidity=mesureStats(cnt)
#définir une condition permettant de segmenter les grains isolés par rapport aux amas
if aspect_ratio>1.9 and area<200 and area>20 and solidity>0.8:
return True
else:
return False
#########################################"
im = cv2.imread('riz.jpg')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
#seuil fixe -> nécessite un réglage manuel du seuil qui fait changer les résultats
#ret,thresh = cv2.threshold(imgray,127,255,0)
#le seuil qui semble optimal
#ret,thresh = cv2.threshold(imgray,160,255,0)
#seuillage otsu après filtre gaussien -> lisse trop
#blur = cv2.GaussianBlur(imgray,(5,5),0)
#ret3,thresh = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
#seuillage otsu
ret3,thresh = cv2.threshold(imgray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
#kernel = np.ones((5,5),np.uint8)
kernel = np.ones((3,3),np.uint8)
thresh = cv2.erode(thresh,kernel,iterations = 2)
thresh = cv2.dilate(thresh,kernel,iterations = 1)
#thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel,iterations = 1)
cv2.imshow('image1',thresh)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
#image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_TC89_L1)
#image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_TC89_KCOS)
cv2.imshow('image',imgray)
cv2.imshow('image',thresh)
img=im
#affichage de tous les contours en vert
#img = cv2.drawContours(im, contours, -1, (0,255,0), 1)
#affichage du contours 4 en rouge
#cnt = contours[4]
#img = cv2.drawContours(img, [cnt], 0, (0,0,255), 1)
#mesureStats(cnt)
#cnt = contours[25]
#img = cv2.drawContours(img, [cnt], 0, (255,0,0), 1)
#mesureStats(cnt)
Debug=False
#mettre à True pour afficher les étapes du traitement
#Debug=True
methode=1
if methode==1:
compteur=0
for i in range(0,len(contours)):
#print(i)
if isGrainDeRiz(contours[i]):
compteur=compteur+1
#affiche les grains de riz isolé en rouge plein
img = cv2.drawContours(img, [contours[i]], 0, (0,0,255), -1)
else:
#affiche les amas de grains de riz en bleu et uniquement les contours
img = cv2.drawContours(img, [contours[i]], 0, (255,0,0), 1)
if Debug:
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
print(str(compteur)+ " grains de riz isolés trouvés")
elif methode==2:
#autre approche basée comparaison de forme, plus simple à implémenter mais moins bons résultats
#https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contours_more_functions/py_contours_more_functions.html
compteur=0
for i in range(0,len(contours)):
ret = cv2.matchShapes(contours[0],contours[i],1,0.0)
if ret<0.8:
compteur=compteur+1
#affiche les grains de riz isolé en rouge plein
img = cv2.drawContours(img, [contours[i]], 0, (0,0,255), -1)
else:
#affiche les amas de grains de riz en bleu et uniquement les contours
img = cv2.drawContours(img, [contours[i]], 0, (255,0,0), 1)
if Debug:
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
print(str(compteur)+ " grains de riz isolés trouvés")
cv2.imwrite('riz_out.png',img)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Image avant traitement:
{{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/riz.jpg}}
Image après traitement:
{{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/riz_out.png}}
====Séance 8: Cours Magistral Modèle géométrique de la caméra=====
====Séance 9: TD Etalonnage géométrique de camera=====
====Séance 9: TP Vision dans le plan=====
====Séance 10: TP de 6h à l'AIP: Intégration de la vision sur l'application robotique=====
====Annexes====
Dimension 4: https://www.youtube.com/watch?v=n1hBuueqC7s&list=PLA7FEDF4FDFC667F1&index=3&ab_channel=MohamedAmineELAFRIT