[PYTHON] Ich habe mir eine Möglichkeit ausgedacht, aus einem Foto ein 3D-Modell zu erstellen. Teil 04 Generieren von Polygonen

Domo ist Ksuke. Als ich 04 eine Methode zum Erstellen eines 3D-Modells aus einem Foto entwickelte, werde ich Polygone generieren. Klicken Sie hier für Teil 3 https://qiita.com/Ksuke/items/8b7f2dc840126753b4e9

* 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. Holen Sie sich die Punkte an der Grenze der Punktgruppe
  2. Holen Sie sich die Punkte innerhalb der Grenze der Punktgruppe
  3. Polygongenerierung

~~ Der Code hier und da ist der am Ende zusammengefasste. ~~ Es gibt nur eine Quelle, aber wie üblich wird sie auch am Ende veröffentlicht.

1. Holen Sie sich die Punkte an der Grenze der Punktgruppe

Im Moment wird das Objekt durch eine Gruppe von Punkten dargestellt, aber die Punkte sind auch innerhalb des Objekts verteilt. Da wir die inneren Punkte nicht benötigen, extrahieren wir nur die Punkte an der Grenze. Um festzustellen, ob jeder Punkt eine Grenze ist, zählen Sie die Anzahl der Punkte in 6 benachbarten Richtungen (oben, unten, rechts, links, vorne und hinten) jeder Koordinate. Die Punkte, an denen die Anzahl benachbarter Punkte nicht 0 ist, werden als Grenzpunkte extrahiert.

Holen Sie sich die Punkte an der Grenze der Punktgruppe



#Eine Funktion, die eine Reihe von Punkten an der Grenze zurückgibt
def genBorderPointSpace(imgProjectSpace):

    #6 Richtungen im Raum(Hoch, runter, links, rechts, vorne und hinten)Erstellen Sie eine Karte mit der Anzahl der Punkte um jeden Punkt, indem Sie eine Verarbeitung ausführen, die dem Additionsfilter von entspricht
    locationPointMap = np.stack([
        imgProjectSpace[2:,1:-1,1:-1],
        imgProjectSpace[:-2,1:-1,1:-1],
        imgProjectSpace[1:-1,2:,1:-1],
        imgProjectSpace[1:-1,:-2,1:-1],
        imgProjectSpace[1:-1,1:-1,2:],
        imgProjectSpace[1:-1,1:-1,:-2],
    ]).sum(axis=0,dtype=np.int8)
    
    #Stellen Sie die Größe des Speicherplatzes wieder her
    locationPointMap = np.insert(locationPointMap, (0,-1), 0, axis=0)
    locationPointMap = np.insert(locationPointMap, (0,-1), 0, axis=1)
    locationPointMap = np.insert(locationPointMap, (0,-1), 0, axis=2)

    #Aus der Karte der Anzahl der umgebenden Punkte für jeden Punkt ergibt sich, dass die Anzahl der umgebenden Punkte weder 0 noch 6 ist.(Es gibt Punkte und keine Punkte)Erstellen Sie einen Raum mit Punkten
    borderPointSpace = np.where((0<locationPointMap)&(locationPointMap<6)&(imgProjectSpace==1),1,0).astype(np.int8)
    
    #Gibt ein Leerzeichen zurück, das Punkte hinterlässt, die nicht von Punkten umgeben sind
    return borderPointSpace

#Nehmen Sie aus der Gruppe der Punkte im überlappenden Raum die Punkte an der Grenze heraus, die mit der Außenseite in Kontakt steht.
borderPointSpace = genBorderPointSpace(imgProjectSpace)

#Extrahieren Sie die Koordinaten eines Punktes aus dem Raum
borderCoords = binary2coords(borderPointSpace)

2. Holen Sie sich die Punkte innerhalb der Grenze der Punktgruppe

Es reicht aus, das Polygon nur am Punkt des Begrenzungsteils anzuzeigen, aber zur späteren automatischen Erzeugung des Polygons wird auch der Punkt innerhalb des Grenzpunkts erfasst.

Holen Sie sich Punkte innerhalb der Grenze einer Punktgruppe



#Eine Funktion, die eine Reihe von Punkten direkt innerhalb der Grenze zurückgibt
def genInsidePointSpace(imgProjectSpace,borderPointSpace):

    #Erstellen Sie einen Raum, in dem Punkte in der Nähe der Grenze verbleiben(Verlassen Sie den Begrenzungsteil nicht)
    nearBorderPointSpace = np.stack([
        borderPointSpace[2:,1:-1,1:-1],
        borderPointSpace[:-2,1:-1,1:-1],
        borderPointSpace[1:-1,2:,1:-1],
        borderPointSpace[1:-1,:-2,1:-1],
        borderPointSpace[1:-1,1:-1,2:],
        borderPointSpace[1:-1,1:-1,:-2],
        borderPointSpace[1:-1,1:-1,1:-1]*-6
    ]).sum(axis=0,dtype=np.int8)
    
    #Rückgabegröße
    nearBorderPointSpace = np.insert(nearBorderPointSpace, (0,-1), 0, axis=0)
    nearBorderPointSpace = np.insert(nearBorderPointSpace, (0,-1), 0, axis=1)
    nearBorderPointSpace = np.insert(nearBorderPointSpace, (0,-1), 0, axis=2)

    #Erstellen Sie einen Raum nahe der Grenze, der die Punkte innerhalb der ursprünglichen Punktgruppe belässt
    insidePointSpace = np.where(((0<nearBorderPointSpace)&(imgProjectSpace==1)),1,0)
    
    return insidePointSpace


#Nehmen Sie aus der Gruppe der Punkte im überlappenden Raum die Punkte unmittelbar innerhalb des Begrenzungsteils heraus
insidePointSpace = genInsidePointSpace(imgProjectSpace,borderPointSpace)

#Extrahieren Sie die Koordinaten eines Punktes aus dem Raum
insideCoords = binary2coords(insidePointSpace)

3. Polygongenerierung

Nachdem Sie zwei Arten von Punkten erhalten haben, ist es Zeit, Polygone zu generieren. Es ist schwieriger zu erklären, was dieser Prozess tut als andere Prozesse, daher erkläre ich ihn mit einem zweidimensionalen Bild (es hat hier viel Zeit in Anspruch genommen ...).

Der Zweck dieses Prozesses besteht darin, den Punkt der Grenze des Objekts erneut zu bestätigen, um die Oberfläche zu bestimmen, die das Objekt umgibt. Auf einem zweidimensionalen Bild wird die den Bereich umgebende Linie (Abb. 2) aus dem Punkt an der Grenze des Objekts bestimmt (Abb. 1).

delaunay-explanation00.png
Abbildung 1.Grenzpunkte

delaunay-explanation01.png
Figur 2.Die gewünschte Grenzlinie am Ende

Dazu habe ich eine Methode namens Delaunay verwendet. Dies dient dazu, den durch die Punkte dargestellten Bereich durch ein Dreieck zu teilen. Abbildung 3 unten zeigt das Ergebnis des Versuchs von Delaunay mit den roten Punkten im Bild. Wie Sie sehen können, wird die Linie an der gewünschten Grenze gezeichnet, aber die Linie wird auch an den anderen Teilen gezeichnet.

delaunay-explanation02.png
Figur 3.Dreiecksteilungsergebnis von Punkten an der Grenze

Nachdem ich mir über das Problem Gedanken gemacht hatte, beschloss ich, die Punkte innerhalb des Begrenzungsteils zu dem Punkt innerhalb des Begrenzungsteils hinzuzufügen, um Delaunay durchzuführen. Ein Punkt innerhalb der Grenze ist ein Punkt, der nahe am Punkt an der Grenze und innerhalb des Bereichs des Objekts liegt. Dies ist der hinzugefügte blaue Punkt in Abbildung 4 unten.

delaunay-explanation03.png
Figur 4.Grenzpunkte und Punkte innerhalb der Grenze

Was passiert, wenn Delaunay einschließlich der Punkte innerhalb dieses Begrenzungsteils durchgeführt wird, ist in Abb. 5 unten dargestellt.

delaunay-explanation04.png
Abbildung 5.Dreiecksteilung ergibt sich aus Punkten an der Grenze und Punkten innerhalb der Grenze

Auf den ersten Blick mögen die roten Punkte etwas komplizierter erscheinen als bei Delaunay. Auf diese Weise können Sie die geteilten Dreiecke in drei Typen einteilen. Wie zu klassifizieren ・ Ein Dreieck, das nur aus den Punkten an der Grenze besteht (hellrot gestrichen) ・ Ein Dreieck, das nur aus den Punkten innerhalb der Grenze besteht (hellblau gestrichen)

  • Ein Dreieck, das sowohl aus den Punkten an der Grenze als auch aus den Punkten innerhalb der Grenze besteht (hellviolett lackiert). Es ist genauso wie das. Wenn Sie sie tatsächlich separat malen, sieht es wie in Abbildung 6 aus.
delaunay-explanation05.png
Abbildung 6.Ergebnis der dreieckigen Teilungsklassifizierung

Wenn Sie sich die drei Arten von Dreiecken ansehen, werden Sie feststellen, dass alle gewünschten Grenzen am Ende im hellvioletten Dreieck enthalten sind. Mit Ausnahme der hellvioletten Dreiecke ist das Ergebnis wie in Abb. 7 dargestellt.

delaunay-explanation06.png
Abbildung 7.Dreieck einschließlich Grenze

Nachdem die Anzahl der Dreiecke bisher abgenommen hat, konzentrieren wir uns auf die Linien. Sie konzentrieren sich auf die Linien und können auch in drei Typen eingeteilt werden. Wie zu klassifizieren ・ Verbindungslinie zwischen den Punkten an der Grenze (hellrot gestrichen) ・ Eine Linie, die die Punkte innerhalb der Grenze verbindet (hellblau gestrichen) -Eine Linie, die die Punkte an der Grenze und die Punkte innerhalb der Grenze verbindet (hellviolett gemalt) Es ist genauso wie das. Wenn Sie sie tatsächlich separat malen, sieht es wie in Abbildung 8 aus.

delaunay-explanation07.png
Abbildung 8.Ergebnis der Linienklassifizierung

Wenn Sie an diesen Punkt kommen, ist der Rest so, wie Sie es gesehen haben. Die hellrot gestrichenen Linien entsprechen genau der endgültigen gewünschten Grenzlinie in Abb. 2, oder? Deshalb sieht es bis auf die hellrote Linie wie in Abbildung 9 aus.

delaunay-explanation08.png
Abbildung 9.Grenzlinie, die erhalten werden konnte

Auf diese Weise können Sie die umgebende Linie (oder die Fläche, wenn es sich um ein dreidimensionales Punktgruppenobjekt handelt) aus der Punktgruppe abrufen. Hier ist die Implementierung dieses Prozesses für 3D-Punktgruppen.

Polygongenerierung




#Eine Funktion, die die Fläche eines Polygons aus einem Satz von Koordinaten bestimmt
def genPolygon(borderCoords,insideCoords):
    
    #Kombinieren Sie zwei Arten von Eckpunkten(Entspricht der Verarbeitung in Abbildung 4)
    coords = np.concatenate([borderCoords,insideCoords])

    #Generieren Sie eine Karte mit dem Typ für jeden Scheitelpunkt(0 ist Grenze und 1 ist drinnen)
    coordStat = np.zeros((len(coords)),dtype=np.uint8)
    coordStat[len(borderCoords):len(borderCoords)+len(insideCoords)] = 1
    
    #Generieren Sie aus der Apex-Gruppe eine dreieckige Pyramidengruppe, die aus 4 Punkten besteht(Entspricht der Verarbeitung in Abb. 5)
    triPyramids = Delaunay(coords).simplices
    
    #Überprüfen Sie, wie viele Innenscheitelpunkte für jede dreieckige Pyramide enthalten sind(Entspricht der Verarbeitung in Abb. 6)
    triPyramidsStat = coordStat[triPyramids].sum(axis=1)
    
    #Von den dreieckigen Pyramiden werden diejenigen extrahiert und als gültige dreieckige Pyramide verwendet, die sowohl den inneren Scheitelpunkt als auch den Randscheitelpunkt enthalten.(Entspricht der Verarbeitung in Abbildung 7)
    #(Hier werden die dreieckige Pyramide im Randraum außerhalb des Objekts, die nur aus den Randscheitelpunkten besteht, und die dreieckige Pyramide im Randraum innerhalb des Objekts, die nur aus den inneren Scheitelpunkten besteht, entfernt.)
    effectiveTriPyramids = triPyramids[np.where((triPyramidsStat!=0)&(triPyramidsStat!=4))[0]]
    
    #Kandidatengesicht für Objekt(3 Index der Eckpunkte)Liste zu setzen(Entfernen Sie den Überschuss später)
    faces = []
        
    #Extrahieren Sie eine gültige dreieckige Pyramidenfläche als Kandidatenfläche für das Objekt(Entspricht dem Teil, der sich auf die Linie in der Erläuterung des Bildes des Artikels konzentriert)
    for coordIndexs in effectiveTriPyramids:
        faces.append([coordIndexs[0],coordIndexs[1],coordIndexs[2]])
        faces.append([coordIndexs[0],coordIndexs[1],coordIndexs[3]])
        faces.append([coordIndexs[0],coordIndexs[2],coordIndexs[3]])
        faces.append([coordIndexs[1],coordIndexs[2],coordIndexs[3]])
        
    #Sortieren Sie den Index des Scheitelpunkts der Kandidatenfläche des Objekts und entfernen Sie die Duplizierung
    faces = np.array(faces)
    faces.sort(axis=1)
    faces = np.unique(faces,axis=0)
    
    #Überprüfen Sie, wie viele Innenscheitelpunkte für jede Fläche enthalten sind(Entspricht der Verarbeitung in Abbildung 8)
    faceStat = coordStat[faces].sum(axis=1)
    
    #Nehmen Sie ein Gesicht heraus, das keine inneren Eckpunkte enthält(Entspricht der Verarbeitung in Abbildung 9)
    faces = faces[np.where(faceStat==0)]
        
    #Gibt Scheitelpunkte und Flächen zurück
    return borderCoords,faces


#Polygon aus zwei Arten von Koordinaten(Um genau zu sein, die Definition des Polygons und der Fläche, die die Eckpunkte verbindet)Generieren Sie a
coords,faces = genPolygon(borderCoords,insideCoords)

#Das Format wurde geändert, um Gesichter an den Mixer zu übergeben
faces = [[face[0],face[1],face[2],face[0]] for face in faces]

Funktionsprüfung

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

1. Alles zusammen

Ich habe meine Bemühungen bei der Polygongenerierung erschöpft, daher fasse ich die Bewegungen zusammen. .. .. Führen Sie den folgenden Code im Mixer aus

Für dynamische 1



#Zeichnen Sie Eckpunkte und Polygone
addObj(coords=coords,faces=faces,name = "porigon",offset=[-125,-50,-50])
addObj(coords=borderCoords,name = "borderCoords",offset=[-50,-85,-50])
addObj(coords=insideCoords,name = "insideCoords",offset=[-50,-15,-50])

Wenn ein solches Objekt angezeigt wird, ist es erfolgreich. Wenn Sie sich die beiden unteren Objekte genau ansehen, ist das rechte etwas kleiner (da es sich um einen Punkt innerhalb der Grenze handelt). キャプチャ.PNG

Nächster?

Schließlich werden die Polygone angezeigt, aber die Anzahl der Polygone ist wahnsinnig groß ... Daher werden wir die Anzahl der Polygone mithilfe der Blender-Funktion reduzieren.

Nachtrag 2020/9/25 Teil 5 wurde veröffentlicht. https://qiita.com/Ksuke/items/6595323e79892acf9a7a

Codeübersicht

Wenn Sie es nach dem vorherigen Code hinzufügen, sollte es funktionieren.

Funktionsausgabe

Codeübersicht(Funktionsausgabe)


#Eine Funktion, die eine Reihe von Punkten an der Grenze zurückgibt
def genBorderPointSpace(imgProjectSpace):

    #6 Richtungen im Raum(Hoch, runter, links, rechts, vorne und hinten)Erstellen Sie eine Karte mit der Anzahl der Punkte um jeden Punkt, indem Sie eine Verarbeitung ausführen, die dem Additionsfilter von entspricht
    locationPointMap = np.stack([
        imgProjectSpace[2:,1:-1,1:-1],
        imgProjectSpace[:-2,1:-1,1:-1],
        imgProjectSpace[1:-1,2:,1:-1],
        imgProjectSpace[1:-1,:-2,1:-1],
        imgProjectSpace[1:-1,1:-1,2:],
        imgProjectSpace[1:-1,1:-1,:-2],
    ]).sum(axis=0,dtype=np.int8)
    
    #Stellen Sie die Größe des Speicherplatzes wieder her
    locationPointMap = np.insert(locationPointMap, (0,-1), 0, axis=0)
    locationPointMap = np.insert(locationPointMap, (0,-1), 0, axis=1)
    locationPointMap = np.insert(locationPointMap, (0,-1), 0, axis=2)

    #Aus der Karte der Anzahl der umgebenden Punkte für jeden Punkt ergibt sich, dass die Anzahl der umgebenden Punkte weder 0 noch 6 ist.(Es gibt Punkte und keine Punkte)Erstellen Sie einen Raum mit Punkten
    borderPointSpace = np.where((0<locationPointMap)&(locationPointMap<6)&(imgProjectSpace==1),1,0).astype(np.int8)
    
    #Gibt ein Leerzeichen zurück, das Punkte hinterlässt, die nicht von Punkten umgeben sind
    return borderPointSpace
    
#Eine Funktion, die eine Reihe von Punkten direkt innerhalb der Grenze zurückgibt
def genInsidePointSpace(imgProjectSpace,borderPointSpace):

    #Erstellen Sie einen Raum, in dem Punkte in der Nähe der Grenze verbleiben(Verlassen Sie den Begrenzungsteil nicht)
    nearBorderPointSpace = np.stack([
        borderPointSpace[2:,1:-1,1:-1],
        borderPointSpace[:-2,1:-1,1:-1],
        borderPointSpace[1:-1,2:,1:-1],
        borderPointSpace[1:-1,:-2,1:-1],
        borderPointSpace[1:-1,1:-1,2:],
        borderPointSpace[1:-1,1:-1,:-2],
        borderPointSpace[1:-1,1:-1,1:-1]*-6
    ]).sum(axis=0,dtype=np.int8)
    
    #Rückgabegröße
    nearBorderPointSpace = np.insert(nearBorderPointSpace, (0,-1), 0, axis=0)
    nearBorderPointSpace = np.insert(nearBorderPointSpace, (0,-1), 0, axis=1)
    nearBorderPointSpace = np.insert(nearBorderPointSpace, (0,-1), 0, axis=2)

    #Erstellen Sie einen Raum nahe der Grenze, der die Punkte innerhalb der ursprünglichen Punktgruppe belässt
    insidePointSpace = np.where(((0<nearBorderPointSpace)&(imgProjectSpace==1)),1,0)
    
    return insidePointSpace
    
    #Eine Funktion, die die Fläche eines Polygons aus einem Satz von Koordinaten bestimmt
def genPolygon(borderCoords,insideCoords):
    
    #Kombinieren Sie zwei Arten von Eckpunkten(Entspricht der Verarbeitung in Abbildung 4)
    coords = np.concatenate([borderCoords,insideCoords])

    #Generieren Sie eine Karte mit dem Typ für jeden Scheitelpunkt(0 ist Grenze und 1 ist drinnen)
    coordStat = np.zeros((len(coords)),dtype=np.uint8)
    coordStat[len(borderCoords):len(borderCoords)+len(insideCoords)] = 1
    
    #Generieren Sie aus der Apex-Gruppe eine dreieckige Pyramidengruppe, die aus 4 Punkten besteht(Entspricht der Verarbeitung in Abb. 5)
    triPyramids = Delaunay(coords).simplices
    
    #Überprüfen Sie, wie viele Innenscheitelpunkte für jede dreieckige Pyramide enthalten sind(Entspricht der Verarbeitung in Abb. 6)
    triPyramidsStat = coordStat[triPyramids].sum(axis=1)
    
    #Von den dreieckigen Pyramiden werden diejenigen extrahiert und als gültige dreieckige Pyramide verwendet, die sowohl den inneren Scheitelpunkt als auch den Randscheitelpunkt enthalten.(Entspricht der Verarbeitung in Abbildung 7)
    #(Hier werden die dreieckige Pyramide im Randraum außerhalb des Objekts, die nur aus den Randscheitelpunkten besteht, und die dreieckige Pyramide im Randraum innerhalb des Objekts, die nur aus den inneren Scheitelpunkten besteht, entfernt.)
    effectiveTriPyramids = triPyramids[np.where((triPyramidsStat!=0)&(triPyramidsStat!=4))[0]]
    
    #Kandidatengesicht für Objekt(3 Index der Eckpunkte)Liste zu setzen(Entfernen Sie den Überschuss später)
    faces = []
        
    #Extrahieren Sie eine gültige dreieckige Pyramidenfläche als Kandidatenfläche für das Objekt(Entspricht dem Teil, der sich auf die Linie in der Erläuterung des Bildes des Artikels konzentriert)
    for coordIndexs in effectiveTriPyramids:
        faces.append([coordIndexs[0],coordIndexs[1],coordIndexs[2]])
        faces.append([coordIndexs[0],coordIndexs[1],coordIndexs[3]])
        faces.append([coordIndexs[0],coordIndexs[2],coordIndexs[3]])
        faces.append([coordIndexs[1],coordIndexs[2],coordIndexs[3]])
        
    #Sortieren Sie den Index des Scheitelpunkts der Kandidatenfläche des Objekts und entfernen Sie die Duplizierung
    faces = np.array(faces)
    faces.sort(axis=1)
    faces = np.unique(faces,axis=0)
    
    #Überprüfen Sie, wie viele Innenscheitelpunkte für jede Fläche enthalten sind(Entspricht der Verarbeitung in Abbildung 8)
    faceStat = coordStat[faces].sum(axis=1)
    
    #Nehmen Sie ein Gesicht heraus, das keine inneren Eckpunkte enthält(Entspricht der Verarbeitung in Abbildung 9)
    faces = faces[np.where(faceStat==0)]
        
    #Gibt Scheitelpunkte und Flächen zurück
    return borderCoords,faces

Ausführungscode

Codeübersicht(Ausführungscode)



#Nehmen Sie aus der Gruppe der Punkte im überlappenden Raum die Punkte an der Grenze heraus, die mit der Außenseite in Kontakt steht.
borderPointSpace = genBorderPointSpace(imgProjectSpace)

#Nehmen Sie aus der Gruppe der Punkte im überlappenden Raum die Punkte unmittelbar innerhalb des Begrenzungsteils heraus
insidePointSpace = genInsidePointSpace(imgProjectSpace,borderPointSpace)

#Extrahieren Sie die Koordinaten eines Punktes aus dem Raum
borderCoords = binary2coords(borderPointSpace)
insideCoords = binary2coords(insidePointSpace)

#Polygon aus zwei Arten von Koordinaten(Um genau zu sein, die Definition des Polygons und der Fläche, die die Eckpunkte verbindet)Generieren Sie a
coords,faces = genPolygon(borderCoords,insideCoords)

#Das Format wurde geändert, um Gesichter an den Mixer zu übergeben
faces = [[face[0],face[1],face[2],face[0]] for face in faces]

print("step04:porigon generate 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)

    
#Scheitelpunkte zeichnen
addObj(coords=coords,faces=faces,name = "porigon",offset=[-125,-50,-50])
addObj(coords=borderCoords,name = "borderCoords",offset=[-50,-85,-50])
addObj(coords=insideCoords,name = "insideCoords",offset=[-50,-15,-50])

Recommended Posts