=====SAE Vision Industrielle pour les étudiants Formation Initiale=====
====Manipulation Robotique RubiksCube====
https://www.youtube.com/embed/mFfiV3QRtU8
trouvé sur le net:
https://www.youtube.com/watch?v=WJRhB39BxWQ
https://medium.com/@diegomesamarrero/rubiks-cube-in-python-using-numpy-and-opencv-a8abfa2d8216
https://www.academia.edu/83769592/Solving_Rubiks_Cube_Using_Open_CV
====Partie 1) Lecture d'une face du Rubik's Cube à une position connue====
Dans un premier temps, vous devrez développer l'application réalisant la lecture d'UNE face du Rubik's cube, le cube étant à une position connue et maîtrisée.
Les étapes du traitement sont données ici:
L'image de test à traiter est:
Image 0: {{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/rubiks.jpg}}
Vous devez spécifier manuellement les coordonnées des 4 coins de la face à traiter:
Image 1: {{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/rubiksrect_contour.png}}
Vous devez ensuite générer une image rectifiée (par application de l'homographie) de la face du cube avec une résolution de 31x31 pixels:
Image 2: {{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/rubiksrect0.png?320}}
31 pixels= 3*9 + 1+1+1+1
ou
31 pixels= 3*7 + 2+3+3+2
pix_deb=2+10*i
pix_fin=8+10*i
Vous devez ensuite déterminer pour chacun des pixels de l'image quelle est la couleur (parmi les 6 couleurs apprises) la plus proche et sinon utiliser un autre indice pour indiquer que le pixel n'est d'aucune des 6 couleurs connues:
Image 3: {{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/rubiksrect0bis_label.png?320}}
Vous devez finalement effectuer un vote pour chacune des cases du rubiks cube en utilisant les différentes valeurs calculées précédemment, et générer une image de 3x3 pixels:
Image 4: {{https://bvdp.inetdoc.net/files/iut/tp_lpro_vision/rubiksrect_3X3_0_label_upsize320.png?320}}
Vous devrez fournir en sortie l'information des 9 cases lues pour la face du cube traitée.
Votre programme devra également fournir un booléen indiquant si la face du cube a pu être correctement lue (chacune des cases ayant une apparence correcte).
Votre programme devra tenir compte du fait que les cases centrales Noires ou Blanches sont souvent dotée d'un logo et ne sont donc pas uniforme.
====Partie 2) Lecture d'une face du Rubik's Cube à une position INconnue====
Une fois la première partie terminée, il est temps de l'intégrer pour permettre de lire le cube, celui ci n'étant plus positionné de manière maîtrisée. Une à trois faces du cube peuvent être visibles par la caméra selon les positions et orientations relatives. L'objectif est ici de trouver les 4 coins de chacune des faces visible pour les lire avec le travail fait à la partie 1.
Pour cela, vous pourrez chercher à détecter des pixels candidats, situés à l'intersections de segments de droites, elles-mêmes détectées dans l'images. Vous pourrez utiliser un algorithme appelé RANSAC (RANdom SAmple Consensus)
====Partie 3) Estimation de la pose du Rubik's Cube====
Vous devrez ici, à partir des 4 coins de la face supérieure du cube, calculer sa pose (position et orientation par rapport à la caméra, puis par rapport au robot).
Pour cela, vous devrez avoir étalonné la caméra (estimé les paramètres intrinsèques et extrinsèques de son modèle).
Vous devrez ensuite utiliser l'algorithme Perspective N Points (PnP) pour calculer la pose du cube à partir de l'observation des 4 coins de sa face supérieure.
=====Images synthétiques=====
====Fichier pour générer les facettes du cube====
import os
import cv2
import numpy as np
#tabcolors_python= [[0,2,6,5,5,1,3,2,1],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0] ]
tabcolors_python= [[5,2,2,4,1,1,6,5,6],[5,2,2,4,1,1,6,5,6],[3,4,4,2,3,5,6,5,6],[5,2,2,4,1,1,6,5,6],[5,2,2,4,1,1,6,5,6],[5,2,2,4,1,1,6,5,6] ]
def create_tab_colors():
with open('tab_colors.scad', 'w') as f:
f.write('// fichier de définition du cube, a inclure avec:\n')
f.write('// include \n')
f.write('// Definition des couleurs\n')
f.write('color_black = [0, 0, 0]; // indice 0 \n')
f.write('color_white = [1, 1, 1]; // indice 1\n')
f.write('color_red = [1, 0, 0]; // indice 2\n')
f.write('color_green = [0, 1, 0]; // indice 3\n')
f.write('color_orange = [1, 0.5, 0]; // indice 4\n')
f.write('color_yellow = [1, 1, 0]; // indice 5\n')
f.write('color_blue = [0, 0, 1]; // indice 6\n')
f.write('tab_indices_color=[color_black,color_white,color_red,color_green,color_orange,color_yellow,color_blue];\n')
f.write('tabcolors= [')
for nface in range(6):
f.write('[')
for ncarre in range(9):
#f.write(str(nface))
f.write(str(tabcolors_python[nface][ncarre]))
if ncarre!=8:
f.write(',')
else:
f.write(']')
if nface!=5:
f.write(',')
f.write('];')
f.close()
create_tab_colors()
====Fichier modèle 3D du cube====
include
// Dimensions du Rubik's Cube
cube_size = 26;
interieur = 90;
interstice = 3;
// Fonction pour créer un carré
module colored_square(color) {
color(color) {
square([cube_size, cube_size], true);
}
}
// Fonction pour créer une face du Rubik's cube
module face(colors) {
for (r = [0:2]) {
for (c = [0:2]) {
translate([r * (cube_size + interstice), c * (cube_size + interstice), 0]) {
colored_square(colors[r][c]);
}
// Ajout de l'interstice
translate([r * (cube_size + interstice) + interstice, c * (cube_size + interstice) + interstice, 0]) {
}
}
}
}
// Assemblage du Rubik's Cube
module rubiks_cube() {
translate([-28, -28, 1]) face([[
tab_indices_color[tabcolors [0][0]], tab_indices_color[tabcolors [0][1]], tab_indices_color[tabcolors [0][2]]], [tab_indices_color[tabcolors [0][3]], tab_indices_color[tabcolors [0][4]], tab_indices_color[tabcolors [0][5]]], [tab_indices_color[tabcolors [0][6]], tab_indices_color[tabcolors [0][7]], tab_indices_color[tabcolors [0][8]]]]);
////////////////////////////////////////////////////
//pas bon
translate([-28, 46, 17]) rotate([90,0,0]) face( [[tab_indices_color[tabcolors [1][0]], tab_indices_color[tabcolors [1][1]], tab_indices_color[tabcolors [1][2]]], [tab_indices_color[tabcolors [1][3]], tab_indices_color[tabcolors [1][4]], tab_indices_color[tabcolors [1][5]]], [tab_indices_color[tabcolors [1][6]], tab_indices_color[tabcolors [1][7]], tab_indices_color[tabcolors [1][8]]]]);
////////////////////////////////////////////////////
//pas bon
translate([46, -28, 17])rotate([0,-90,0])face([[tab_indices_color[tabcolors [2][0]], tab_indices_color[tabcolors [2][1]], tab_indices_color[tabcolors [2][2]]], [tab_indices_color[tabcolors [2][3]], tab_indices_color[tabcolors [2][4]], tab_indices_color[tabcolors [2][5]]], [tab_indices_color[tabcolors [2][6]], tab_indices_color[tabcolors [2][7]], tab_indices_color[tabcolors [2][8]]]]);
////////////////////////////////////////////////////
translate([-28, -44, 17]) rotate([90,0,0]) face([[tab_indices_color[tabcolors [3][0]], tab_indices_color[tabcolors [3][1]], tab_indices_color[tabcolors [3][2]]], [tab_indices_color[tabcolors [3][3]], tab_indices_color[tabcolors [3][4]], tab_indices_color[tabcolors [3][5]]], [tab_indices_color[tabcolors [3][6]], tab_indices_color[tabcolors [3][7]], tab_indices_color[tabcolors [3][8]]]]);
////////////////////////////////////////////////////
translate([-44, -28, 17])rotate([0,-90,0]) face([[tab_indices_color[tabcolors [4][0]], tab_indices_color[tabcolors [4][1]], tab_indices_color[tabcolors [4][2]]], [tab_indices_color[tabcolors [4][3]], tab_indices_color[tabcolors [4][4]], tab_indices_color[tabcolors [4][5]]], [tab_indices_color[tabcolors [4][6]], tab_indices_color[tabcolors [4][7]], tab_indices_color[tabcolors [4][8]]]]);
////////////////////////////////////////////////////
//pas bon
translate([-28, -28,91]) face( [[tab_indices_color[tabcolors [5][0]], tab_indices_color[tabcolors [5][1]], tab_indices_color[tabcolors [5][2]]], [tab_indices_color[tabcolors [5][3]], tab_indices_color[tabcolors [5][4]], tab_indices_color[tabcolors [5][5]]], [tab_indices_color[tabcolors [5][6]], tab_indices_color[tabcolors [5][7]], tab_indices_color[tabcolors [5][8]]]]);
////////////////////////////////////////////////////
}
module black_cube(color) {
color([0, 0, 0]) {
translate([1,1,46]) cube([interieur, interieur, interieur],center = true);
}
}
// Appel de la fonction pour générer le cube noir
black_cube();
rubiks_cube();
====Fichier pour générer une image====
import os
import cv2
import numpy as np
filename_img="img"
#executable_openscad="openscad"
executable_openscad="D:\openscad-2021.01\openscad.exe"
chaine=f'{executable_openscad} -o {filename_img}.png --imgsize=1024,768 --camera=230,340,500,0,0,0 --projection=p rubiks.scad'
os.system(chaine)
=====Code pour générer des images aléatoires=====
import os
import cv2
import numpy as np
import time
import random
#tabcolors_python= [[0,2,6,5,5,1,3,2,1],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0] ]
tabcolors_python= [[5,2,2,4,1,1,6,5,6],[5,2,2,4,1,1,6,5,6],[3,4,4,2,3,5,6,5,6],[5,2,3,5,1,1,6,5,6],[5,2,3,4,1,1,6,5,6],[5,2,2,4,2,1,5,5,6] ]
def create_image():
def create_tab_colors():
with open('tab_colors.scad', 'w') as f:
f.write('// fichier de définition du cube, a inclure avec:\n')
f.write('// include \n')
f.write('// Definition des couleurs\n')
f.write('color_black = [0, 0, 0]; // indice 0 \n')
f.write('color_white = [1, 1, 1]; // indice 1\n')
f.write('color_red = [1, 0, 0]; // indice 2\n')
f.write('color_green = [0, 1, 0]; // indice 3\n')
f.write('color_orange = [1, 0.5, 0]; // indice 4\n')
f.write('color_yellow = [1, 1, 0]; // indice 5\n')
f.write('color_blue = [0, 0, 1]; // indice 6\n')
f.write('tab_indices_color=[color_black,color_white,color_red,color_green,color_orange,color_yellow,color_blue];\n')
f.write('tabcolors= [')
for nface in range(6):
f.write('[')
for ncarre in range(9):
#f.write(str(nface))
f.write(str(tabcolors_python[nface][ncarre]))
if ncarre!=8:
f.write(',')
else:
f.write(']')
if nface!=5:
f.write(',')
f.write('];')
f.close()
create_tab_colors()
time.sleep(2)
filename_img="img"
for j in range(9):
filename_img=filename_img+str(tabcolors_python[5][j])
#executable_openscad="openscad"
executable_openscad="D:\OpenSCAD-2021.01-x86-64\openscad-2021.01\openscad.exe"
chaine=f'{executable_openscad} -o {filename_img}.png --imgsize=1024,768 --camera=230,340,500,0,0,0 --projection=p rubiks.scad'
os.system(chaine)
######################################################
for i in range(7):
print (str(i))
#tabcolors_python[5][0]=i
for j in range(9):
tabcolors_python[5][j]=random.randint(0, 7)
create_image()
=====Images Réelles=====
{{https://bvdp.inetdoc.net/files/iut/vision_BUT3/vue_rubiks.png}}
Pleins d'images à utiliser: https://bvdp.inetdoc.net/files/iut/vision_BUT3/
Archive zip des images: https://bvdp.inetdoc.net/files/iut/vision_BUT3/images_rubiks.zip
=====Codage HSV en openCV=====
Teinte en opencv va de 0 à 180 (code des 2 degrés)
import numpy as np
import cv2
import math
import os
if __name__ == '__main__':
im=np.zeros((1,1,3),np.uint8)
im[:,:,:]=(74,188,41)
im[:,:,:]=(255,255,255)
im[:,:,:]=(255,0,0)
im[:,:,:]=(0,255,0)
im[:,:,:]=(0,0,255)
hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
print(hsv)
exit(0)