numpy 如何在matplotlib中为没有内部线条的掩码添加轮廓

hzbexzde  于 2023-06-06  发布在  其他
关注(0)|答案(1)|浏览(132)

对于一个给定的2D蒙版,我想画出它的轮廓,相邻网格单元之间没有内部线条。突出显示一个单元格非常简单:How to mark cells in matplotlib.pyplot.imshow (drawing cell borders)并突出显示许多单元格也很简单:Python matplotlib - add borders to grid plot based on value。但是,我不希望掩码内的内部边界被一条线分隔,因此上面的第二个链接不合适。
下面是生成掩码的示例代码:

import matplotlib.pyplot as plt
import numpy as np

N = 50
xbound = np.linspace(0, 10, N + 1)
ybound = np.linspace(0, 10, N + 1)
x = (xbound[:-1] + xbound[1:]) / 2
y = (ybound[:-1] + ybound[1:]) / 2

X, Y = np.meshgrid(x, y)
Z = np.exp(-((X - 2.5)**2 + (Y - 2.5)**2)) + np.exp(-2 * ((X - 7.5)**2 + (Y - 6.5)**2))
mask = Z > 0.2

plt.imshow(mask, origin='lower', extent=(0, 10, 0, 10))

生成图像的步骤:

我希望边界围绕每个像素化的黄色圆圈,并遵循上面显示的相同边缘(即平行于x/y轴)-我想强调的是,底层数据是网格化的。使用:

plt.contour(x, y, mask, levels=[0.5])

接近,但轮廓沿着楼梯边缘呈45°。
如果轮廓可以填充或显示使用制图加分。

kxeu7u2r

kxeu7u2r1#

这可以使用shapely形状和union操作来完成。这个想法是为掩码为真的每个单元格创建一个正方形(或矩形等),然后将相邻的正方形组合在一起。然后可以绘制这些几何图形,或使用这些几何图形创建matplotlib多边形。下图以红色显示轮廓(根据需要带有楼梯边缘):

import shapely.geometry
import shapely.ops

geoms = []
for yidx, xidx in zip(*np.where(mask)):
    geoms.append(shapely.geometry.box(xbound[xidx], ybound[yidx], xbound[xidx+1], ybound[yidx+1]))
full_geom = shapely.ops.unary_union(geoms)

for geom in full_geom.geoms:
    plt.plot(*geom.exterior.xy, linewidth=4, color='r')
plt.imshow(mask, origin='lower', extent=(0, 10, 0, 10))

很难用颜色/图案填充每个形状,因此可以使用matplotlib多边形:

from matplotlib.patches import Polygon

polygons = []
for geom in full_geom.geoms:
    p = Polygon(list(zip(*geom.exterior.xy)), closed=True, facecolor='none', edgecolor='r', linestyle='-', hatch='//', linewidth=4)
    polygons.append(p)

plt.figure()
axes = plt.gca()
for p in polygons:
    axes.add_patch(p)
plt.imshow(mask, origin='lower', extent=(0, 10, 0, 10))

最后,可以将原始shapely多边形直接添加到制图图中:

import cartopy.crs as ccrs

fig, ax = plt.subplots(subplot_kw={'projection': ccrs.PlateCarree()})
ax.add_geometries(
    full_geom.geoms,
    crs=ccrs.PlateCarree(),
    facecolor='white',
    edgecolor='red',
    linewidth=4,
    hatch='//',
)
ax.coastlines()
ax.gridlines(draw_labels=True, dms=True, x_inline=False, y_inline=False)
ax.set_xlim((0, 10))
ax.set_ylim((0, 10))

相关问题