2010-07-23 8 views
12

Ich verwende plot(x,y,'r'), um einen roten Kreis zu zeichnen. x und y sind Arrays, bei denen, wenn sie als (x, y) geplottet werden, alle Punkte eine Kreislinie bilden.Füllen Sie OUTSIDE des Polygons | Maskenarray, bei dem die Indices jenseits einer kreisförmigen Grenze liegen?

fill(x,y,'r') zeichnet einen roten Kreis, der rot ausgefüllt (oder eingefärbt) ist.

Wie kann ich den Kreis weiß auf der Innenseite halten, aber außerhalb des Kreises zu den Achsengrenzen ausfüllen?

Ich schaute in Verwendung fill_between(x_array, y1_array, y2_array, where), aber nach ein wenig damit spielen Ich glaube nicht, dass das für meine x, y-Arrays funktioniert. Ich dachte an fill_between() außerhalb des Kreises, und in einem Quadrat, das durch die Achsengrenzen definiert ist, aber ich glaube nicht fill_between() ist in der Lage ... Ich bin sicher, dass ich es zu einem integralen Problem mit Delta X und Delta y machen könnte auf Null gehen, aber ich bin nicht dazu bereit.

Wenn jemand sehen kann, dass ich etwas mit fill_between() vermisse, lass es mich wissen.

Alles, was ich wirklich tun muss, ist Zahlen in einem 2D-Array auszublenden, die jenseits dieser Grenze des Kreises mit x und y erstellt werden, so dass, wenn das 2D-Array als Farbdiagramm oder Kontur angesehen wird, innerhalb des Kreises wird das Bild sein, und außen wird weiß werden.

Kann dies stattdessen durch eine Maskierungstechnik des 2D-Arrays erreicht werden? Wie mit masked_where()? Ich habe es noch nicht untersucht, werde es aber tun.

Irgendwelche Ideen? Danke

Edit 1: Hier ist, was ich habe Erlaubnis zu zeigen, dass ich denke, wird mein Problem zu erklären.

from pylab import * 
from matplotlib.path import Path 
from matplotlib.patches import PathPatch 

f=Figure() 
a=f.add_subplot(111) 

# x,y,z are 2d arrays 

# sometimes i plot a color plot 
# im = a.pcolor(x,y,z) 
a.pcolor(x,y,z) 

# sometimes i plot a contour 
a.contour(x,y,z) 

# sometimes i plot both using a.hold(True) 

# here is the masking part. 
# sometimes i just want to see the boundary drawn without masking 
# sometimes i want to see the boundary drawn with masking inside of the boundary 
# sometimes i want to see the boundary drawn with masking outside of the boundary 

# depending on the vectors that define x_bound and y_bound, sometimes the boundary 
# is a circle, sometimes it is not. 

path=Path(vpath) 
patch=PathPatch(path,facecolor='none') 
a.add_patch(patch) # just plots boundary if anything has been previously plotted on a 
if ('I want to mask inside'): 
    patch.set_facecolor('white') # masks(whitens) inside if pcolor is currently on a, 
    # but if contour is on a, the contour part is not whitened out. 
else: # i want to mask outside 
    im.set_clip_path(patch) # masks outside only when im = a.pcolor(x,y,z) 
    # the following commands don't update any masking but they don't produce errors? 
    # patch.set_clip_on(True) 
    # a.set_clip_on(True) 
    # a.set_clip_path(patch) 

a.show() 

Antwort

13

Alles, was ich wirklich zu tun bin brauchen ist Maske aus Zahlen in einem 2D-Array, das jenseits dieser Grenze des mit x und y erstellt Kreis angeordnet ist, so dass , wenn der 2D-Array ist als eine Farbe Plot oder Kontur, innerhalb des Kreises wird das Bild, und außerhalb wird weiß-ed out sein.

Sie haben zwei Möglichkeiten:

Zuerst Sie eine maskierte Array für die Bilder verwenden könnten. Dies ist komplizierter, aber ein wenig mehr ausfallsicher. Um ein Array außerhalb eines Kreises zu maskieren, erstellen Sie eine Entfernungskarte vom Mittelpunkt und maskieren Sie, wo die Entfernung größer als der Radius ist.

Die einfachere Option besteht darin, die Bereiche außerhalb des Patches mit im.set_clip_path() zu beschneiden, nachdem Sie das Bild geplottet haben.

Siehe this example from the matplotlib gallery. Leider kann dies bei einigen Achsen (nicht kartesischen Achsen) nach meiner Erfahrung ein wenig störend sein. In jedem anderen Fall sollte es jedoch perfekt funktionieren.

Edit: Übrigens, this is how to do what you originally asked: Zeichnen Sie ein Polygon mit einem Loch im Inneren. Wenn Sie jedoch nur ein Bild maskieren möchten, sind Sie mit beiden oben genannten Optionen besser bedient.

Edit2: Nur um ein kurzes Beispiel für beide Wege zu geben ...

import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.patches as patches 

def main(): 
    # Generate some random data 
    nx, ny = 100, 100 
    data = np.random.random((ny,nx)) 

    # Define a circle in the center of the data with a radius of 20 pixels 
    radius = 20 
    center_x = nx // 2 
    center_y = ny // 2 

    plot_masked(data, center_x, center_y, radius) 
    plot_clipped(data, center_x, center_y, radius) 
    plt.show() 

def plot_masked(data, center_x, center_y, radius): 
    """Plots the image masked outside of a circle using masked arrays""" 
    # Calculate the distance from the center of the circle 
    ny, nx = data.shape 
    ix, iy = np.meshgrid(np.arange(nx), np.arange(ny)) 
    distance = np.sqrt((ix - center_x)**2 + (iy - center_y)**2) 

    # Mask portions of the data array outside of the circle 
    data = np.ma.masked_where(distance > radius, data) 

    # Plot 
    plt.figure() 
    plt.imshow(data) 
    plt.title('Masked Array') 

def plot_clipped(data, center_x, center_y, radius): 
    """Plots the image clipped outside of a circle by using a clip path""" 
    fig = plt.figure() 
    ax = fig.add_subplot(111) 

    # Make a circle 
    circ = patches.Circle((center_x, center_y), radius, facecolor='none') 
    ax.add_patch(circ) # Plot the outline 

    # Plot the clipped image 
    im = ax.imshow(data, clip_path=circ, clip_on=True) 

    plt.title('Clipped Array') 

main() 

enter image description here enter image description here

Edit 2: Grafische Darstellung eine Maske Polygon über das ursprüngliche Grundstück: Hier ist etwas detaillierter, wie ein Polygon zu zeichnen, die Masken alles außerhalb davon über den aktuellen Plot. Anscheinend gibt es keinen besseren Weg, um Kontur-Plots zu schneiden (Das könnte ich sowieso finden ...).

import numpy as np 
import matplotlib.pyplot as plt 

def main(): 
    # Contour some regular (fake) data 
    grid = np.arange(100).reshape((10,10)) 
    plt.contourf(grid) 

    # Verticies of the clipping polygon in counter-clockwise order 
    # (A triange, in this case) 
    poly_verts = [(2, 2), (5, 2.5), (6, 8), (2, 2)] 

    mask_outside_polygon(poly_verts) 

    plt.show() 

def mask_outside_polygon(poly_verts, ax=None): 
    """ 
    Plots a mask on the specified axis ("ax", defaults to plt.gca()) such that 
    all areas outside of the polygon specified by "poly_verts" are masked. 

    "poly_verts" must be a list of tuples of the verticies in the polygon in 
    counter-clockwise order. 

    Returns the matplotlib.patches.PathPatch instance plotted on the figure. 
    """ 
    import matplotlib.patches as mpatches 
    import matplotlib.path as mpath 

    if ax is None: 
     ax = plt.gca() 

    # Get current plot limits 
    xlim = ax.get_xlim() 
    ylim = ax.get_ylim() 

    # Verticies of the plot boundaries in clockwise order 
    bound_verts = [(xlim[0], ylim[0]), (xlim[0], ylim[1]), 
        (xlim[1], ylim[1]), (xlim[1], ylim[0]), 
        (xlim[0], ylim[0])] 

    # A series of codes (1 and 2) to tell matplotlib whether to draw a line or 
    # move the "pen" (So that there's no connecting line) 
    bound_codes = [mpath.Path.MOVETO] + (len(bound_verts) - 1) * [mpath.Path.LINETO] 
    poly_codes = [mpath.Path.MOVETO] + (len(poly_verts) - 1) * [mpath.Path.LINETO] 

    # Plot the masking patch 
    path = mpath.Path(bound_verts + poly_verts, bound_codes + poly_codes) 
    patch = mpatches.PathPatch(path, facecolor='white', edgecolor='none') 
    patch = ax.add_patch(patch) 

    # Reset the plot limits to their original extents 
    ax.set_xlim(xlim) 
    ax.set_ylim(ylim) 

    return patch 

if __name__ == '__main__': 
    main() 

Clipped contour plot

+0

Danke Joe. der set_clip_path scheint meine beste Option zu sein, aber wenn ich mit der ersten Option gehen wollte, würde ich 'MaskedArray()' Methode verwenden? – AmyS

+0

@AmyS - Ja, ich habe ein Beispiel hinzugefügt, um beide Möglichkeiten zu zeigen, Dinge zu tun. Ich hoffe es hilft! –

+0

Danke für die Extras Joe. Intersting und hilfreich, aber für die aktuelle App, an der ich arbeite, wird meine Grenze durch zwei Vektoren definiert, die manchmal paarweise einen Kreis bilden, manchmal nicht. Ich benutze 'pcolor()', weil meine Achse durch 2 2D-Arrays definiert ist, die nicht immer kartesisch sind, und ich kann nicht herausfinden, wie ich das mit 'imshow()' behandeln soll. Glücklicherweise ist 'set_clip_path (patch)' ein attr. für 'pcolor()', aber nicht für 'contour()' oder 'plot()' :(Wenn Sie noch interessiert sind, würden Sie sich Edit 1 oben in meiner Frage ansehen und sehen, ob ich Maskierung hinzufügen kann patch to axis ohne vordefiniertes pcolor? – AmyS

2

Hinweis: Diese Antwort verwendet MATLAB-Syntax, da die Frage ursprünglich als solche markiert wurde. Auch wenn Sie Matplotlib in Python verwenden, sollte das Konzept jedoch gleich sein, auch wenn die Syntax etwas anders ist.

Eine Option, die Sie haben, ist ein Polygon zu machen, die ein Loch in ihm zu haben scheint, aber wirklich hat nur zwei seiner Ränder um einen leeren Raum Einwickeln und berührend. Dazu können Sie eine Reihe von x und y Koordinaten erstellen, die um den Rand des Kreises herum verlaufen, dann von der Kreiskante zum Rand eines Begrenzungsquadrats und dann um den Rand dieses Quadrats herum und zurück zur Kreiskante auf der gleichen Linie. Hier ist ein Beispiel mit einem Einheitskreis und ein 4 x 4 Quadrat am Ursprung zentriert:

theta = linspace(0,2*pi,100);  %# A vector of 100 angles from 0 to 2*pi 
xCircle = cos(theta);    %# x coordinates for circle 
yCircle = sin(theta);    %# y coordinates for circle 
xSquare = [2 2 -2 -2 2 2];   %# x coordinates for square 
ySquare = [0 -2 -2 2 2 0];   %# y coordinates for square 
hp = fill([xCircle xSquare],... %# Plot the filled polygon 
      [yCircle ySquare],'r'); 
axis equal       %# Make axes tick marks equal in size 

Und hier ist die Zahl sollten Sie sehen:

alt text

Beachten Sie die Linie auf der rechten Verbindungs die Kanten von Kreis und Quadrat. Hier treffen und berühren sich zwei Kanten des roten Polygons. Wenn Sie nicht möchten, dass die Kantenlinien sichtbar sind, können Sie ihre Farbe so ändern, dass sie der Füllfarbe für das Polygon entspricht:

+0

Sicherlich MATLAB hat eine Möglichkeit, Polygone mit Löchern direkt zu zeichnen? Dann wieder, ich kann es auch nicht finden ... Hier ist, wie Matplotlib es behandelt: http://matplotlib.sourceforge.net/examples/api/donut_demo.html –

+0

Danke für die Ideen Jungs. Es scheint der bequemste Weg zu sein, einen Patch aus der path.Path-Klasse zu erstellen und von dort aus zu arbeiten, wie im obigen Beispiel, oder in einer anderen Antwort mit set_clip_path() angegeben. – AmyS

Verwandte Themen