Domo est Ksuke. Dans la partie 02, où j'ai trouvé un moyen de créer un modèle 3D à partir d'une photo, je vais lire l'image et dessiner les sommets. Cliquez ici pour la partie 1 https://qiita.com/Ksuke/items/7b3df37399cecd34d036
* Attention * </ b> Cet article ne donne que la fin de ce que j'ai trouvé et essayé, donc cela pourrait se terminer avec du matériel abrupt ou Bad End.
Procédure </ b>
Le code ici et là est le dernier.
Prenez une photo de l'avant, du côté ou du dessus d'un objet. Remplissez ensuite l'arrière-plan de blanc pur pour que les dimensions verticales et horizontales soient identiques. (Ceci est pour faciliter le traitement après la séparation d'arrière-plan, etc. Je veux vraiment le traiter avec un programme, mais ce n'est pas le sujet principal de cette fois, donc je vais l'omettre.) Quand je l'ai fait avec ma propre tasse, ça ressemblait à ça. Après cela, je ferai diverses choses avec cette image.
Image avant th> | Image latérale th> | Image du haut th> |
---|---|---|
Dans un dossier quelque part, décidez de la taille pour gérer l'image et exécutez le code suivant avec Blender pour le charger.
Chargement d'image
#Rugosité du modèle 3D(Taille de l'image)Spécifier
imgSize = 100
#Chemin absolu du dossier contenant l'image
basePath = "Tout!"
#Chemin relatif depuis le dossier contenant l'image
imgPaths = ["/front.jpg ","/side.jpg ","/top.jpg "]
#Lecture d'image dans chaque direction,Réduire à imgSize
imgs = [cv2.resize(cv2.imread(basePath+imgPath),(imgSize,imgSize)) for imgPath in imgPaths]
Préparez une fonction pour séparer l'arrière-plan de l'image. Cette fois, il sera séparé en blanc et autres, mais veuillez le réécrire si nécessaire.
Séparation d'arrière-plan
#Méthode de séparation de l'arrière-plan
#Comme ce n'est pas le sujet principal, en gros, j'ai décidé que l'image était un fond blanc et juger si elle est blanche ou non
#Le RVB de chaque pixel est moyenné, et s'il est égal ou supérieur à 230, il s'agit de l'arrière-plan.
def sepBack(img):
#Remplacez les pixels blancs par 0 et les pixels non blancs par 1, pour créer une image binaire avec un arrière-plan de 0.
sepBackImg = np.where(230<=img.mean(axis=2),0,1)
#Renvoie une image binaire
return sepBackImg
#Créer une image avec l'arrière-plan et le sujet séparés de l'image chargée
sepBackImgs = [sepBack(img) for img in imgs]
Ensuite, préparez une fonction qui renvoie les coordonnées de l'endroit où la valeur est 1 à partir du tableau binaire.
Récupération de coordonnées
#Méthode pour calculer les coordonnées à partir d'un tableau binaire
#Recherche et renvoie les coordonnées du tableau binaire où la valeur est 1.
def binary2coords(binary):
#Trouvez les coordonnées avec une valeur de 1
coords = np.where(binary==1)
#Formaté car la valeur n'est pas darray et il est difficile à utiliser
coords = np.stack([*coords],axis=1)
#Renvoie un tableau de coordonnées
return coords
#Créez une liste de coordonnées de la position du sujet à partir de l'image avec l'arrière-plan séparé
#Puisqu'il s'agit d'un problème si les coordonnées sont bidimensionnelles lors de l'affichage en 3D, ajoutez une dimension à l'image puis prenez les coordonnées
imgCoords = [binary2coords(sepBackImg[:,:,None]) for sepBackImg in sepBackImgs]
Enfin, préparez une fonction qui affiche les sommets sous forme d'objet. Préparez des arguments pour pouvoir ajouter des informations sur les côtés et les visages à l'avenir.
Création d'objets
#Enregistrer les sommets en tant qu'objets
def addObj(coords=[],edges=[],faces=[],offset=[],name=""):
#Ajouter un décalage si le décalage a été spécifié
if len(offset)!=0:coords = coords+offset
#Créer un nouveau maillage
me = bpy.data.meshes.new("{}Mesh".format(name))
#Créer un objet avec un maillage
ob = bpy.data.objects.new("{}Object".format(name), me)
#Alignez la position de l'objet avec la position du curseur 3D
ob.location = bpy.context.scene.cursor.location
#Lier des objets à la scène
bpy.context.scene.collection.objects.link(ob)
#Remplissez les sommets, les côtés et les faces du maillage
me.from_pydata(coords,edges,faces)
#Changer l'objet de modification pour qu'il soit actif
bpy.context.view_layer.objects.active = ob
#Mettre à jour le maillage avec de nouvelles données
me.update(calc_edges=True)
#Renvoie le maillage créé
return me,ob
#Décaler la largeur des sommets de chaque image
offsets = [[-50,-150,0],[-50,-50,0],[-50,50,0]]
#Nom lors de l'enregistrement des sommets de chaque image en tant qu'objet
names = ['frontImg','sideImg','topImg']
#Dessiner des sommets
[addObj(coords=imgCoord,name = name,offset=offset) for imgCoord,name,offset in zip(imgCoords,names,offsets)]
Enfin, vérifiez si le code fonctionne correctement.
Ajoutez le code résumé ci-dessous sous le code précédent et exécutez-le avec blender. Succès si 3 objets comme celui-ci (actuellement des groupes de points) apparaissent.
Je vais transférer l'image dans un tableau 3D pour montrer un objet épais (toujours un groupe de points).
Si vous l'ajoutez après le code précédent, cela devrait fonctionner. (Le chemin du fichier doit être corrigé)
Résumé du code(Edition de fonction)
#Méthode de séparation de l'arrière-plan
#Comme ce n'est pas le sujet principal, je vais en gros décider que l'image est un fond blanc ici et juger si elle est blanche ou non
#Le RVB de chaque pixel est moyenné, et s'il est égal ou supérieur à 230, il s'agit de l'arrière-plan.
def sepBack(img):
#Remplacez les pixels blancs par 0 et les pixels non blancs par 1, pour créer une image binaire avec un arrière-plan de 0.
sepBackImg = np.where(230<=img.mean(axis=2),0,1)
#Renvoie une image binaire
return sepBackImg
#Méthode pour calculer les coordonnées à partir d'un tableau binaire
#Recherche et renvoie les coordonnées du tableau binaire où la valeur est 1.
def binary2coords(binary):
#Trouvez les coordonnées avec une valeur de 1
coords = np.where(binary==1)
#Formaté car la valeur n'est pas darray et il est difficile à utiliser
coords = np.stack([*coords],axis=1)
#Renvoie une liste de coordonnées
return coords
#Enregistrer les sommets en tant qu'objets
def addObj(coords=[],edges=[],faces=[],offset=[],name=""):
#Ajouter un décalage si le décalage a été spécifié
if len(offset)!=0:coords = coords+offset
#Créer un nouveau maillage
me = bpy.data.meshes.new("{}Mesh".format(name))
#Créer un objet avec un maillage
ob = bpy.data.objects.new("{}Object".format(name), me)
#Alignez la position de l'objet avec la position du curseur 3D
ob.location = bpy.context.scene.cursor.location
#Lier des objets à la scène
bpy.context.scene.collection.objects.link(ob)
#Remplissez les sommets, les côtés et les faces du maillage
me.from_pydata(coords,edges,faces)
#Changer l'objet de modification pour qu'il soit actif
bpy.context.view_layer.objects.active = ob
#Mettre à jour le maillage avec de nouvelles données
me.update(calc_edges=True)
#Renvoie le maillage créé
return me,ob
Résumé du code(Code d'exécution)
#Supprimer tous les objets existants
for item in bpy.data.objects:
bpy.data.objects.remove(item)
#Rugosité du modèle 3D(Taille de l'image)Spécifier
imgSize = 100
#Chemin absolu du dossier contenant l'image
basePath = "Tout!"
#Chemin relatif depuis le dossier contenant l'image
imgPaths = ["/front.jpg ","/side.jpg ","/top.jpg "]
#Lecture d'image dans chaque direction,Réduire à imgSize
imgs = [cv2.resize(cv2.imread(basePath+imgPath),(imgSize,imgSize)) for imgPath in imgPaths]
#Créer une image avec l'arrière-plan et le sujet séparés de l'image chargée
sepBackImgs = [sepBack(img) for img in imgs]
print("step02:image read and preprocessing is success.\n")
#Pour l'affichage de confirmation ci-dessous(Cela n'a rien à voir avec le flux principal, donc il disparaîtra probablement au prochain tour)
#Créez une liste de coordonnées de la position du sujet à partir de l'image avec l'arrière-plan séparé
imgCoords = [binary2coords(sepBackImg[:,:,None]) for sepBackImg in sepBackImgs]
#Décaler la largeur des sommets de chaque image
offsets = [[-50,-150,0],[-50,-50,0],[-50,50,0]]
#Nom lors de l'enregistrement des sommets de chaque image en tant qu'objet
names = ['frontImg','sideImg','topImg']
#Dessiner des sommets
[addObj(coords=imgCoord,name = name,offset=offset) for imgCoord,name,offset in zip(imgCoords,names,offsets)]