2010-09-06 9 views
20

Ich muss ein numpliges 2D-Array erstellen, das eine Binärmaske eines Polygons darstellt und Standard-Python-Pakete verwendet.SciPy 2D-Polygonmaske erstellen

  • Eingang: Polygon-Scheitelpunkte, Bildabmessungen
  • Ausgabe: binäre Maske von Polygon (numpy 2D-Array)

(größeren Kontext: Ich möchte die Distanz verwandeln dieses Polygons erhalten scipy verwenden. ndimage.morphology.distance_transform_edt.)

Kann mir jemand zeigen, wie man das macht?

Antwort

39

Die Antwort stellt sich heraus, ganz einfach:

import numpy 
from PIL import Image, ImageDraw 

# polygon = [(x1,y1),(x2,y2),...] or [x1,y1,x2,y2,...] 
# width = ? 
# height = ? 

img = Image.new('L', (width, height), 0) 
ImageDraw.Draw(img).polygon(polygon, outline=1, fill=1) 
mask = numpy.array(img) 
+0

Ich benutze den Bildmodus 'L', nicht '1', weil Numpy-1.5.0/PIL-1.1.7 das 'numpy.array nicht unterstützt (img) 'Konvertierung für Bivalue-Bilder. Der obere Teil des Arrays enthält 8 kleine Unterbilder 1/8 der erwarteten Maskengröße, wobei die restlichen 7/8 des Arrays mit Müll gefüllt sind. Vielleicht entpackt die Konvertierung die Binärdaten nicht richtig? –

+1

Ich denke, dass diese Methode nur mit Integer-Koordinaten funktioniert (d. H. Die Gitterkoordinaten). Wenn die Eckpunktkoordinaten Floats sind, funktioniert die andere Lösung weiterhin. –

+0

von: @jmetz "Nur zur Info: Ich habe einen einfachen Timing-Test gemacht und der PIL-Ansatz ist ~ 70 mal schneller als die Matplotlib-Version !!!" – Jakobovski

2

Sie könnten versuchen, Pythons Image Library, PIL zu verwenden. Zuerst initialisierst du die Leinwand. Dann erstellen Sie ein Zeichenobjekt, und Sie fangen an, Linien zu erstellen. Dies setzt voraus, dass sich das Polygon in R^2 befindet und dass die Scheitelpunktliste für die Eingabe in der richtigen Reihenfolge ist.

Input = [(x1, y1), (x2, y2), ..., (xn, yn)], (Breite, Höhe)

from PIL import Image, ImageDraw 

img = Image.new('L', (width, height), 0) # The Zero is to Specify Background Color 
draw = ImageDraw.Draw(img) 

for vertex in range(len(vertexlist)): 
    startpoint = vertexlist[vertex] 
    try: endpoint = vertexlist[vertex+1] 
    except IndexError: endpoint = vertexlist[0] 
    # The exception means We have reached the end and need to complete the polygon 
    draw.line((startpoint[0], startpoint[1], endpoint[0], endpoint[1]), fill=1) 

# If you want the result as a single list 
# You can make a two dimensional list or dictionary by iterating over the height and width variable 
list(img.getdata()) 

# If you want the result as an actual Image 
img.save('polgon.jpg', 'JPEG') 

Ist das, was Sie suchen, Oder hast du etwas anderes gefragt?

+1

Dank Anil, das ist im Grunde, was ich suchte. Es ist besser, wenn Sie die ImageDraw.polygon-Methode (ImageDraw.Draw (img) .polygon (Scheitelpunkte, Umriss = 1, Füllung = 1)) verwenden, und ich nutzte die Funktion numpy.reshape, um effizient ein 2D-Array aus den Bilddaten zu erhalten (import numpy, M = numpy.reshape (Liste (img.getdata()), (Höhe, Breite))). Ich akzeptiere Ihre Antwort, wenn Sie sie bearbeiten, um diese Dinge zu berücksichtigen. –

22

Als eine etwas direktere Alternative zu @ Anils Antwort hat matplotlib matplotlib.nxutils.points_inside_poly, die verwendet werden kann, um schnell ein beliebiges Polygon zu rastern. Z.B.

import numpy as np 
from matplotlib.nxutils import points_inside_poly 

nx, ny = 10, 10 
poly_verts = [(1,1), (5,1), (5,9),(3,2),(1,1)] 

# Create vertex coordinates for each grid cell... 
# (<0,0> is at the top left of the grid in this system) 
x, y = np.meshgrid(np.arange(nx), np.arange(ny)) 
x, y = x.flatten(), y.flatten() 

points = np.vstack((x,y)).T 

grid = points_inside_poly(points, poly_verts) 
grid = grid.reshape((ny,nx)) 

print grid 

Welche (a boolean numpy Array) ergibt:

[[False False False False False False False False False False] 
[False True True True True False False False False False] 
[False False False True True False False False False False] 
[False False False False True False False False False False] 
[False False False False True False False False False False] 
[False False False False True False False False False False] 
[False False False False False False False False False False] 
[False False False False False False False False False False] 
[False False False False False False False False False False] 
[False False False False False False False False False False]] 

Sie sollten in der Lage sein zu passieren grid einem der scipy.ndimage.morphology Funktionen ganz gut.

+0

Ich vermied es, points_inside_poly zu verwenden, da es mit einer Liste von Koordinaten arbeitet, anstatt direkt auf einem binären Bild zu arbeiten. Aus diesem Grund und weil PIL die Hardwarebeschleunigung zum Rendern meines Polygons verwenden kann, scheint mir die Lösung von Anil effizienter zu sein. –

+1

@Issac - Fair genug. Soweit ich weiß, verwendet PIL keine Hardwarebeschleunigung jeglicher Art, obwohl ... (Hat sich das in letzter Zeit geändert?) Wenn Sie PIL verwenden, müssen Sie auch nicht 'M = numpy.reshape (list (img .getdata()), (Höhe, Breite))) 'wie Sie in Ihrem Kommentar oben erwähnen. 'numpy.array (img)' macht genau das selbe, viel effizienter. –

+1

Weit draußen! Danke, dass Sie auf die Funktion numpy.array (img) hingewiesen haben. Und wahr, PIL verwendet wahrscheinlich immer noch keine Hardwarebeschleunigung. –

8

Ein Update auf Joes Kommentar. Die Matplotlib-API wurde seit der Veröffentlichung des Kommentars geändert, und Sie müssen jetzt eine Methode verwenden, die von einem Submodul matplotlib.path bereitgestellt wird.

Arbeitscode ist unten.

import numpy as np 
from matplotlib.path import Path 

nx, ny = 10, 10 
poly_verts = [(1,1), (5,1), (5,9),(3,2),(1,1)] 

# Create vertex coordinates for each grid cell... 
# (<0,0> is at the top left of the grid in this system) 
x, y = np.meshgrid(np.arange(nx), np.arange(ny)) 
x, y = x.flatten(), y.flatten() 

points = np.vstack((x,y)).T 

path = Path(poly_verts) 
grid = path.contains_points(points) 
grid = grid.reshape((ny,nx)) 

print grid