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:
Vous devez spécifier manuellement les coordonnées des 4 coins de la face à traiter:
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 31×31 pixels:
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:
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 3×3 pixels:
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.
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)
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.
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 <tab_colors.scad>\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()
include <tab_colors.scad>
// 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();
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)
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 <tab_colors.scad>\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()
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
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)