2010-07-23 6 views
12

Yo uso plot(x,y,'r') para trazar un círculo rojo. xey son matrices tales que cuando se emparejan como (x, y) y se trazan, todos los puntos forman una línea circular.Rellenar FUERA del polígono | Máscara de matriz donde las indicaciones están más allá de un límite circular?

fill(x,y,'r') traza un círculo rojo que está rellenado (o coloreado) en rojo.

¿Cómo puedo mantener el círculo blanco en el interior, pero llenar el círculo fuera de los límites del eje?

Intenté usar fill_between(x_array, y1_array, y2_array, where) pero después de jugar un poco con él no creo que funcione para mis matrices x, y. Pensé en fill_between() fuera del círculo, y dentro de un cuadrado que está definido por los límites del eje, pero no creo que fill_between() sea capaz ... Estoy seguro de que podría convertirlo en un tipo de problema integral con delta x y delta y yendo a cero, pero no estoy dispuesto a hacerlo.

Si alguien puede ver que me falta algo con fill_between(), por favor avíseme.

Todo lo que realmente necesito hacer es enmascarar números en una matriz 2d que se encuentran más allá de este límite del círculo creado con xey, de modo que cuando la matriz 2D se ve como un trazado de color o contorno, dentro del círculo estará la imagen, y afuera se verá blanco.

¿Se puede lograr esto mediante una técnica de enmascaramiento de la matriz 2D en su lugar? Me gusta al usar masked_where()? No he investigado todavía, pero lo haré.

¿Alguna idea? Gracias

Edit 1: Esto es lo que tengo permiso para mostrar que creo que explicará mi problema.

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

Respuesta

13

Todo lo que estoy realmente tener que hacer es máscara los números en una matriz 2D que son situada más allá de este límite del círculo creado con X e Y, de modo que cuando la matriz 2D es visto como una trama de color , o contorno, dentro del círculo será la imagen, y el exterior será blanco-ed out.

tiene dos opciones:

En primer lugar, se puede utilizar una variedad de máscaras para las imágenes. Esto es más complicado pero un poco más a prueba de fallas. Para enmascarar una matriz fuera de un círculo, genere un mapa de distancia desde el punto central y enmascare donde la distancia es mayor que el radio.

La opción más fácil es recortar las áreas fuera del parche con im.set_clip_path() después de trazar la imagen.

Ver this example from the matplotlib gallery. Desafortunadamente, con algunos ejes (ejes no cartesianos) esto puede ser un poco impreciso, en mi experiencia. En cualquier otro caso, debería funcionar perfectamente, sin embargo.

Editar: Por cierto, this is how to do what you originally asked: trazar un polígono con un agujero en el interior. Sin embargo, si solo quieres enmascarar una imagen, estás mejor con cualquiera de las dos opciones anteriores.

Edit2: Solo para dar un ejemplo rápido de ambas maneras ...

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

Edición 2: Trazado de un polígono máscara sobre la trama original: Aquí hay un poco más de detalle sobre la forma de trazar un polígono que enmascara todo fuera de ella sobre la trama actual. Aparentemente, no hay una mejor manera de recortar las curvas de nivel (que podría encontrar, de todos modos ...).

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

Thanks Joe. el set_clip_path parece mi mejor opción, pero si quisiera ir con la primera opción, ¿usaría el método 'MaskedArray()'? – AmyS

+0

@AmyS - Sí, agregué un ejemplo para mostrar ambas formas de hacer las cosas. ¡Espero eso ayude! –

+0

Gracias por los extras Joe. Interesante y útil, pero para la aplicación actual en la que estoy trabajando, mi límite está definido por dos vectores que cuando se emparejan a veces forman un círculo, a veces no. Yo uso 'pcolor()' porque mi eje está definido por 2 matrices de 2d que no siempre son cartesianas, y no puedo entender cómo manejar eso con 'imshow()'. Afortunadamente, 'set_clip_plath (patch)' es un attr. para 'pcolor()', pero no para 'contour()' o 'plot()' :(Si aún está interesado, eche un vistazo a Edit 1 arriba de mi pregunta y vea si puedo agregar masking parche a eje sin pcolor predefinido? – AmyS

2

Nota: Esta respuesta utiliza la sintaxis de MATLAB, ya que la cuestión fue originalmente marcada como tal. Sin embargo, incluso si está usando matplotlib en Python, el concepto debería ser el mismo, incluso si la sintaxis es ligeramente diferente.

Una opción que tienes es hacer un polígono que aparece tener un agujero en él, pero en realidad sólo tiene dos de sus bordes envolver alrededor de un espacio vacío y el tacto. Puede hacer esto creando un conjunto de coordenadas x y y que siguen el borde del círculo, luego rastrean desde el borde del círculo hasta el borde de un cuadrado delimitador, luego rastrean alrededor del borde de ese cuadrado y vuelven al borde del círculo a lo largo de la misma línea. He aquí un ejemplo con un círculo unitario y un cuadrado de 4 por 4 con centro en el origen:

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 

y aquí está la figura que usted debe ver:

alt text

Aviso de la línea a la derecha de unirse los bordes del círculo y el cuadrado. Aquí es donde dos bordes del polígono rojo se encuentran y se tocan. Si no desea que las líneas de borde sean visibles, puede cambiar su color para que coincida con el color de relleno del polígono:

set(hp,'EdgeColor','r'); 
+0

¿Seguro que MATLAB tiene una forma de trazar polígonos con agujeros directamente? Por otra parte, parece que tampoco puedo encontrarlo ... Así es como lo maneja matplotlib: http://matplotlib.sourceforge.net/examples/api/donut_demo.html –

+0

Gracias por las ideas chicos. Parece que la forma más conveniente es hacer un parche desde la ruta. Clase de directorio y trabajar desde allí como en el ejemplo anterior, o ejemplo dado en otra respuesta usando set_clip_path() – AmyS

Cuestiones relacionadas