[PYTHON] J'ai trouvé un moyen de créer un modèle 3D à partir d'une photo Partie 02 Chargement d'images et dessin de sommets

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.

Essayer

Procédure </ b>

  1. Charger l'image
  2. Prétraitement pour la partie 3
  3. Création de données de sommet
  4. Affichage des sommets

Le code ici et là est le dernier.

1. Charger l'image

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 Image latérale Image du haut

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]

2. Prétraitement pour la partie 3

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]


3. Création de données de sommet

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]

4. Affichage des sommets

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

Contrôle de fonctionnement

Enfin, vérifiez si le code fonctionne correctement.

1. Exécution

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. キャプチャ.PNG

prochain?

Je vais transférer l'image dans un tableau 3D pour montrer un objet épais (toujours un groupe de points).

Résumé du code

Si vous l'ajoutez après le code précédent, cela devrait fonctionner. (Le chemin du fichier doit être corrigé)

Edition de fonction

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

Code d'exécution

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

Recommended Posts