[PYTHON] Ich habe mir eine Möglichkeit ausgedacht, ein 3D-Modell aus einem Foto zu erstellen. Teil 02 Laden von Bildern und Zeichnen von Scheitelpunkten

Domo ist Ksuke. In Teil 02, in dem ich eine Möglichkeit gefunden habe, ein 3D-Modell aus einem Foto zu erstellen, werde ich das Bild lesen und die Eckpunkte zeichnen. Klicken Sie hier für Teil 1 https://qiita.com/Ksuke/items/7b3df37399cecd34d036

* Achtung * </ b> Dieser Artikel gibt nur das Ende dessen an, was ich mir ausgedacht und versucht habe, sodass es zu abruptem Material oder schlechtem Ende kommen kann.

Versuchen

Vorgehensweise </ b>

  1. Laden Sie das Bild
  2. Vorbehandlung für Teil 3
  3. Erstellen von Scheitelpunktdaten
  4. Anzeige der Eckpunkte

Der Code hier und da ist der letzte.

1. Laden Sie das Bild

Machen Sie ein Foto von der Vorderseite, Seite oder Oberseite eines Objekts. Füllen Sie dann den Hintergrund mit reinem Weiß, um die vertikalen und horizontalen Abmessungen gleich zu machen. (Dies soll die Verarbeitung nach der Hintergrundtrennung usw. erleichtern. Ich möchte es wirklich mit einem Programm verarbeiten, aber es ist nicht das Hauptthema dieser Zeit, daher werde ich es weglassen.) Als ich es mit meiner eigenen Tasse gemacht habe, sah es so aus. Danach werde ich verschiedene Dinge mit diesem Bild machen.

Frontbild Seitenbild Oberes Bild

Entscheiden Sie in einem Ordner irgendwo die Größe für das Bild und führen Sie den folgenden Code mit Blender aus, um es zu laden.

Bild wird geladen


#Rauheit des 3D-Modells(Bildgröße)Angeben
imgSize = 100
   
#Absoluter Pfad des Ordners, der das Bild enthält
basePath = "Irgendein!"

#Relativer Pfad aus dem Ordner, der das Bild enthält
imgPaths = ["/front.jpg ","/side.jpg ","/top.jpg "]

#Bildlesung aus jeder Richtung,Auf imgSize reduzieren
imgs = [cv2.resize(cv2.imread(basePath+imgPath),(imgSize,imgSize)) for imgPath in imgPaths]

2. Vorbehandlung für Teil 3

Bereiten Sie eine Funktion vor, um den Hintergrund vom Bild zu trennen. Dieses Mal wird es in Weiß und andere getrennt, aber bitte schreiben Sie es bei Bedarf neu.

Hintergrundtrennung


#Methode zur Hintergrundtrennung
#Da dies ungefähr nicht das Hauptthema ist, habe ich entschieden, dass das Bild ein weißer Hintergrund ist, und beurteilt, ob es weiß ist oder nicht
#Das RGB jedes Pixels wird gemittelt, und wenn es 230 oder mehr beträgt, ist es der Hintergrund.
def sepBack(img):

    #Ersetzen Sie weiße Pixel durch 0 und nicht weiße Pixel durch 1, um ein Binärbild mit einem Hintergrund von 0 zu erstellen.
    sepBackImg = np.where(230<=img.mean(axis=2),0,1)
    
    #Gibt ein Binärbild zurück
    return sepBackImg
    

#Erstellen Sie ein Bild, bei dem Hintergrund und Motiv vom geladenen Bild getrennt sind
sepBackImgs = [sepBack(img) for img in imgs]


3. Erstellen von Scheitelpunktdaten

Bereiten Sie als Nächstes eine Funktion vor, die die Koordinaten der Stelle, an der der Wert 1 ist, aus dem binären Array zurückgibt.

Koordinatenabruf


#Methode zur Berechnung von Koordinaten aus einem binären Array
#Findet die Koordinaten des binären Arrays und gibt sie zurück, wobei der Wert 1 ist.
def binary2coords(binary):
    #Finden Sie die Koordinaten mit einem Wert von 1
    coords = np.where(binary==1)
    
    #Formatiert, weil der Wert nicht darray ist und schwer zu verwenden ist
    coords = np.stack([*coords],axis=1)
    
    #Gibt einen Darray von Koordinaten zurück
    return coords
    

#Erstellen Sie aus dem Bild eine Liste mit Koordinaten der Position des Motivs, wobei der Hintergrund getrennt ist
#Da es ein Problem ist, wenn die Koordinaten bei der Anzeige in 3D zweidimensional sind, fügen Sie dem Bild eine Dimension hinzu und nehmen Sie dann die Koordinaten
imgCoords = [binary2coords(sepBackImg[:,:,None]) for sepBackImg in sepBackImgs]

4. Anzeige der Eckpunkte

Bereiten Sie abschließend eine Funktion vor, die die Scheitelpunkte als Objekt anzeigt. Bereiten Sie Argumente vor, damit Sie in Zukunft Informationen zu Seiten und Gesichtern hinzufügen können.

Objekterstellung



#Registrieren Sie Scheitelpunkte als Objekte
def addObj(coords=[],edges=[],faces=[],offset=[],name=""):
    
    #Versatz hinzufügen, wenn Versatz angegeben wurde
    if len(offset)!=0:coords = coords+offset
        
    #Erstellen Sie ein neues Netz
    me = bpy.data.meshes.new("{}Mesh".format(name))

    #Erstellen Sie ein Objekt mit einem Netz
    ob = bpy.data.objects.new("{}Object".format(name), me)
    
    #Richten Sie die Position des Objekts an der Position des 3D-Cursors aus
    ob.location = bpy.context.scene.cursor.location
    
    #Verknüpfen Sie Objekte mit der Szene
    bpy.context.scene.collection.objects.link(ob)
    
    #Füllen Sie die Oberseiten, Seiten und Flächen des Netzes
    me.from_pydata(coords,edges,faces)
    
    #Ändern Sie das Änderungsobjekt so, dass es aktiv ist
    bpy.context.view_layer.objects.active = ob
        
    #Aktualisieren Sie das Netz mit neuen Daten
    me.update(calc_edges=True)
    
    #Gibt das erstellte Netz zurück
    return me,ob


#Verschieben Sie die Breite der Scheitelpunkte jedes Bildes
offsets = [[-50,-150,0],[-50,-50,0],[-50,50,0]]

#Name beim Registrieren der Eckpunkte jedes Bildes als Objekt
names = ['frontImg','sideImg','topImg']

#Scheitelpunkte zeichnen
[addObj(coords=imgCoord,name = name,offset=offset) for imgCoord,name,offset in zip(imgCoords,names,offsets)]

Funktionsprüfung

Überprüfen Sie abschließend, ob der Code ordnungsgemäß funktioniert.

1. Ausführung

Fügen Sie den unten unter dem vorherigen Code zusammengefassten Code hinzu und führen Sie ihn mit dem Mixer aus. Erfolg, wenn 3 Objekte wie dieses (derzeit Punktgruppen) angezeigt werden. キャプチャ.PNG

Nächster?

Ich werde das Bild in ein 3D-Array übertragen, um ein dickes Objekt (immer noch eine Gruppe von Punkten) anzuzeigen.

Codeübersicht

Wenn Sie es nach dem vorherigen Code hinzufügen, sollte es funktionieren. (Dateipfad muss korrigiert werden)

Funktionsausgabe

Codeübersicht(Funktionsausgabe)




#Methode zur Hintergrundtrennung
#Da dies nicht das Hauptthema ist, werde ich grob entscheiden, dass das Bild hier einen weißen Hintergrund hat, und beurteilen, ob es weiß ist oder nicht
#Das RGB jedes Pixels wird gemittelt, und wenn es 230 oder mehr beträgt, ist es der Hintergrund.
def sepBack(img):

    #Ersetzen Sie weiße Pixel durch 0 und nicht weiße Pixel durch 1, um ein Binärbild mit einem Hintergrund von 0 zu erstellen.
    sepBackImg = np.where(230<=img.mean(axis=2),0,1)
    
    #Gibt ein Binärbild zurück
    return sepBackImg
    

#Methode zur Berechnung von Koordinaten aus einem binären Array
#Findet die Koordinaten des binären Arrays und gibt sie zurück, wobei der Wert 1 ist.
def binary2coords(binary):
    #Finden Sie die Koordinaten mit einem Wert von 1
    coords = np.where(binary==1)
    
    #Formatiert, weil der Wert nicht darray ist und schwer zu verwenden ist
    coords = np.stack([*coords],axis=1)
    
    #Gibt eine Liste von Koordinaten zurück
    return coords
    

#Registrieren Sie Scheitelpunkte als Objekte
def addObj(coords=[],edges=[],faces=[],offset=[],name=""):
    
    #Versatz hinzufügen, wenn Versatz angegeben wurde
    if len(offset)!=0:coords = coords+offset
        
    #Erstellen Sie ein neues Netz
    me = bpy.data.meshes.new("{}Mesh".format(name))

    #Erstellen Sie ein Objekt mit einem Netz
    ob = bpy.data.objects.new("{}Object".format(name), me)
    
    #Richten Sie die Position des Objekts an der Position des 3D-Cursors aus
    ob.location = bpy.context.scene.cursor.location
    
    #Verknüpfen Sie Objekte mit der Szene
    bpy.context.scene.collection.objects.link(ob)
    
    #Füllen Sie die Oberseiten, Seiten und Flächen des Netzes
    me.from_pydata(coords,edges,faces)
    
    #Ändern Sie das Änderungsobjekt so, dass es aktiv ist
    bpy.context.view_layer.objects.active = ob
        
    #Aktualisieren Sie das Netz mit neuen Daten
    me.update(calc_edges=True)
    
    #Gibt das erstellte Netz zurück
    return me,ob

Ausführungscode

Codeübersicht(Ausführungscode)



#Löschen Sie alle vorhandenen Objekte
for item in bpy.data.objects:
   bpy.data.objects.remove(item)

#Rauheit des 3D-Modells(Bildgröße)Angeben
imgSize = 100

#Absoluter Pfad des Ordners, der das Bild enthält
basePath = "Irgendein!"

#Relativer Pfad aus dem Ordner, der das Bild enthält
imgPaths = ["/front.jpg ","/side.jpg ","/top.jpg "]

#Bildlesung aus jeder Richtung,Auf imgSize reduzieren
imgs = [cv2.resize(cv2.imread(basePath+imgPath),(imgSize,imgSize)) for imgPath in imgPaths]
    
#Erstellen Sie ein Bild, bei dem Hintergrund und Motiv vom geladenen Bild getrennt sind
sepBackImgs = [sepBack(img) for img in imgs]

print("step02:image read and preprocessing is success.\n")



#Zur Bestätigung unten anzeigen(Es hat nichts mit dem Hauptfluss zu tun, daher wird es wahrscheinlich in der nächsten Runde verschwinden)

#Erstellen Sie aus dem Bild eine Liste mit Koordinaten der Position des Motivs, wobei der Hintergrund getrennt ist
imgCoords = [binary2coords(sepBackImg[:,:,None]) for sepBackImg in sepBackImgs]

#Verschieben Sie die Breite der Scheitelpunkte jedes Bildes
offsets = [[-50,-150,0],[-50,-50,0],[-50,50,0]]

#Name beim Registrieren der Eckpunkte jedes Bildes als Objekt
names = ['frontImg','sideImg','topImg']

#Scheitelpunkte zeichnen
[addObj(coords=imgCoord,name = name,offset=offset) for imgCoord,name,offset in zip(imgCoords,names,offsets)]

Recommended Posts