简体   繁体   中英

How to go from a contour to an image mask in with Matplotlib

If I plot a 2D array and contour it, I can get the access to the segmentation map, via cs = plt.contour(...); cs.allsegs cs = plt.contour(...); cs.allsegs but it's parameterized as a line. I'd like a segmap boolean mask of what's interior to the line, so I can, say, quickly sum everything within that contour.

Many thanks!

I dont think there is a really easy way, mainly because you want to mix raster and vector data. Matplotlib paths fortunately have a way to check if a point is within the path, doing this for all pixels will make a mask, but i think this method can get very slow for large datasets.

import matplotlib.patches as patches
from matplotlib.nxutils import points_inside_poly
import matplotlib.pyplot as plt
import numpy as np

# generate some data
X, Y = np.meshgrid(np.arange(-3.0, 3.0, 0.025), np.arange(-3.0, 3.0, 0.025))
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)

fig, axs = plt.subplots(1,2, figsize=(12,6), subplot_kw={'xticks': [], 'yticks': [], 'frameon': False})

# create a normal contour plot
axs[0].set_title('Standard contour plot')
im = axs[0].imshow(Z, cmap=plt.cm.Greys_r)
cs = axs[0].contour(Z, np.arange(-3, 4, .5), linewidths=2, colors='red', linestyles='solid')

# get the path from 1 of the contour lines
verts = cs.collections[7].get_paths()[0]

# highlight the selected contour with yellow
axs[0].add_patch(patches.PathPatch(verts, facecolor='none', ec='yellow', lw=2, zorder=50))

# make a mask from it with the dimensions of Z
mask = verts.contains_points(list(np.ndindex(Z.shape)))
mask = mask.reshape(Z.shape).T

axs[1].set_title('Mask of everything within one contour line')
axs[1].imshow(mask, cmap=plt.cm.Greys_r, interpolation='none')

# get the sum of everything within the contour
# the mask is inverted because everything within the contour should not be masked
print np.ma.MaskedArray(Z, mask=~mask).sum()

Note that contour lines which 'leave' the plot at different edges by default wont make a path which follows these edges. These lines would need some additional processing.

在此输入图像描述

Another way, perhaps more intuitive, is the binary_fill_holes function from scipy.ndimage .

import numpy as np
import scipy


image = np.zeros((512, 512))
image[contour1[:, 0], contour1[:, 1]] = 1
masked_image = scipy.ndimage.morphology.binary_fill_holes(image)
```

Here is how to create a filled polygon from contours and create a binary mask using OpenCV

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

mask = np.zeros((10,10,3), dtype=np.uint8)
# polygon's coordinates
coords = np.array([[3,3],[3,6],[6,6],[6,3]])

cv2.drawContours(mask, [coords], contourIdx=-1, color=(1,1,1), thickness=-1)
bin_mask = mask[:,:,0].astype(np.float32)
plt.imshow(bin_mask, cmap='gray')
  • contourIdx=-1 - draw all contours

  • color=(1,1,1) - a number from 0 to 255 for each channel; since we generate a binary mask it is set to 1

  • thickness=-1 - fills the polygon

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM