如何在matplotlib中为没有内部线的蒙版添加轮廓线。

huangapple go评论68阅读模式
英文:

How to add an outline to a mask with no internal lines in matplotlib

问题

对于给定的二维掩码,我想要绘制其轮廓,但不要在相邻的网格单元之间绘制内部线条。突出显示一个单元格很简单:链接,而突出显示多个单元格也很简单:链接。然而,我不希望掩码内部的边界由一条线分隔,所以上面的第二个链接不适用。

以下是生成掩码的示例代码:

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

生成的图像如下所示:

如何在matplotlib中为没有内部线的蒙版添加轮廓线。

我想要边界围绕每个像素化的黄色圆圈,并沿着上面显示的相同边缘(即平行于x/y轴) - 我想强调底层数据是网格化的。使用以下代码可以接近:

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

但是这个轮廓位于楼梯边缘上的45度角。

如果能够填充轮廓或使用cartopy显示轮廓将获得额外的奖励分数。

英文:

For a given 2D mask, I would like to draw its outline with no internal lines between adjacent grid cells. Highlighting one cell is straightforward: <https://stackoverflow.com/questions/56654952/how-to-mark-cells-in-matplotlib-pyplot-imshow-drawing-cell-borders> and highlighting many cells is also straightforward: <https://stackoverflow.com/questions/51432498/python-matplotlib-add-borders-to-grid-plot-based-on-value>. However, I do not want internal boundaries within the mask to be separated by a line, so the second link above is not suitable.

Here is example code to generate a mask:

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 &gt; 0.2

plt.imshow(mask, origin=&#39;lower&#39;, extent=(0, 10, 0, 10))

Which generates the image:

如何在matplotlib中为没有内部线的蒙版添加轮廓线。

I want the boundary to be around each of the pixelated yellow circles, and to follow the same edges as show above (i.e. be parallel to the x/y-axis) - I want to emphasize that the underlying data is gridded. Using:

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

comes close, but the contour is at 45° along the staircase edges.

Bonus points if the outline can be filled or shown using cartopy.

答案1

得分: 1

这可以使用shapely的形状和联合操作来完成。思路是为每个掩码为真的单元格创建一个正方形(或矩形等),然后将相邻的正方形组合在一起。然后可以绘制这些几何图形或用它们创建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中为没有内部线的蒙版添加轮廓线。

要填充每个形状的颜色/图案可能会很困难,因此可以使用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))

如何在matplotlib中为没有内部线的蒙版添加轮廓线。

最后,原始的shapely多边形可以直接添加到cartopy地图中:

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

如何在matplotlib中为没有内部线的蒙版添加轮廓线。

英文:

This can be done using shapely shapes and the union operation. The idea is to create a square (or rectangle etc.) for each cell where the mask is true, then combine contiguous squares together. These geometries can then be plotted or used to create matplotlib polygons. The following shows the outline in red (with the staircase edge as required):

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=&#39;r&#39;)
plt.imshow(mask, origin=&#39;lower&#39;, extent=(0, 10, 0, 10))

如何在matplotlib中为没有内部线的蒙版添加轮廓线。

It would be hard to fill each shape with a colour/pattern, so instead matplotlib polygons can be used:

from matplotlib.patches import Polygon

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

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

如何在matplotlib中为没有内部线的蒙版添加轮廓线。

Finally, the raw shapely polygons can be added directly to a cartopy map:

import cartopy.crs as ccrs

fig, ax = plt.subplots(subplot_kw={&#39;projection&#39;: ccrs.PlateCarree()})
ax.add_geometries(
    full_geom.geoms,
    crs=ccrs.PlateCarree(),
    facecolor=&#39;white&#39;,
    edgecolor=&#39;red&#39;,
    linewidth=4,
    hatch=&#39;//&#39;,
)
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))

如何在matplotlib中为没有内部线的蒙版添加轮廓线。

huangapple
  • 本文由 发表于 2023年5月29日 19:33:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76356969.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定