If I plot a 2D array and contour it, I can get the access to the segmentation map, via 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!
If (as done in my answer) the contour is drawn in (255) or (255,255,255), the contour is drawn in white and the resulting image is a mask for the contour. Choosing thickness = -1 performs drawing a filled conour, which results in a mask of the whole contour area instead of the contour outline.
Create a contour plot of the image using imcontour . Grayscale image, specified as an m -by- n matrix. Number of contour levels, specified as a numeric scalar. Value of contour levels, specified as a numeric vector with length greater than or equal to two. Use V = [v v] to compute a single contour at level v.
The basic algorithm for removing contours from an image goes something like this: Step 1: Detect and find contours in your image. Step 2: Loop over contours individually. Step 3: Determine if the contour is “bad” and should be removed according to some criterion. Step 4: Accumulate a mask of “bad” contours to be removed.
A short tutorial on plotting images with Matplotlib. First, let's start IPython. It is a most excellent enhancement to the standard Python prompt, and it ties in especially well with Matplotlib. Start IPython either directly at a shell, or with the Jupyter Notebook (where IPython as a running kernel).
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)
```
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With