Mesh generation for 2D label images

I have a segmented image in the form of a label image (stored as a numpy array). I am looking for free software that can create a conforming mesh from it. I checked iso2mesh but that one is intended for 3D and has some issues when using for 2D meshing. I also tried OOF2, but I cannot install it on a new Linux distribution. Are you aware of any software that I can use?

1 Like

Hey @ZCs,

would you mind sharing an example data set? I’m just wondering: Do you want to connect labelled dots in a 2D image?



Here is the label image in form of a numpy matrix:!ABk1FAbI!xI4CHriARNHMn0hkbLhzO8nWajR4vUVBp8ME9SuIglg (I couldn’t upload .npy file here).

Do you want to connect labelled dots in a 2D image?

What do you mean? What I want is a finite element mesh respecting the boundaries between the label clusters. Of course, it means that some contouring is used beforehand. Here is a sketch, containing part of a segmented image with two labels:
meshing.pdf (23.5 KB)

Hi, may be my answer for this question would help.

may be you need find_boundaries, find_contours, approximate_polygon, or insert some random inner point? then use scipy.spatial.Delaunay?

1 Like

So your method creates a triangulation for each label region approximated by a polygon? In that case, how do you enforce that the mesh nodes coincide along the internal boundary of region i and region j? Or more generally, how do you guarantee that the polygonal regions i and j have no gap in between?

I thikn they are two questions: polygonize, and meshlize


As i known gdal.polygonize can create polygon from labeled raster. (but it is a lib in geogeraphy, it is very heavy)
so we can try other method.

  1. find_boundaries with the mode=‘subpixel’, you can get a double sized image whit thin line.
  2. skeletonize, then use sknw to build the edge graph.
  3. if you like, you can use approximate_polygon to simplize the edge, even use scipy.interpolate’s functon to got a super smooth edge.
  4. you can use networkx to find the cycle from the graph, then combine the edges to polygons. (each cycle is a region, another choice is shapely.ops.polygonize)


In fact you can only build the tin from edge, or insert some point in. I did not know what you want, you need a all-grid tin? or with less point? or without long and thin tin?

I did not know if there is other specific lib for this question, but I thik it is not too difficult to write it follow the method upon.

It is done until step 2, i.e. I built the graph based on the skeleton. Are steps 3 and 4 built into ImagePy?

In fact you can only build the tin from edge

What do you mean by tin in this context?

there is nothing need interaction, so need not imagepy.

sknw can build graph from skeleton image. imagepy contains it in ipyalg, but it also could be used directly (you 'd better copy the from imagepy, because we had update something a few days ago, but not update in sknw repo yet).

you can use sknw build the edge graph, it is a networkx object. and you can use graph.edges[start][end][‘pts’] to get a edge’s coordinates.

step 3 is, do some filter to fit the coordinates. (simpelize or smooth)

step 4 is, for the graph is a networkx object, so you can look the networkx 's doc, I think there should be method to find cycle from the graph. Then np.vstack the coordinates form every edge. (you need to check the direction, some coordinate need a pts[::-1]), then you would got the polygon.

imagepy did not has such function now, but when we complete this code, it could be in imagepy easily.

tin means mesh here, in geogeraphy we say Triangulated Irregular Network.

I wrote a demo about step 1,2

from skimage.draw import circle
import numpy as np
import matplotlib.pyplot as plt
import sknw

img = np.zeros((100,100), dtype=np.uint8)

from skimage.segmentation import find_boundaries
from skimage.morphology import skeletonize
msk = find_boundaries(img, mode='subpixel')
# if you have region touch the image's border, you need clear the border, or pad the image, that make sure every region make a cycle, then could be a polygon later.
msk = skeletonize(msk)
g = sknw.build_sknw(msk, True)

# step 3: for i in g.edges: fit(g.edges[i]['pts']) ...
# step 4: for c in g.find_cycles(): np.vstack ...

for i in g.edges:
    pts = g.edges[i]['pts']
    plt.plot(*pts.T[::-1]/2, 'red')

I haven’t answered for a long time because I followed the approach OOF2 offers. However, that turned out to be a dead end. So now, I consider reconstructing the grains from the skeleton network.
Indeed, NetworkX can create the cycles in the graph that correspond to the grains. However, that returns all the cycles. And many of them do not correspond to a grain, rather a cluster of grains. For instance, consider the graph here. simple_cycles does not only return the four grains, but also e.g. A-D-F-E-C-A. Currently, I don’t know how I can discard the cycles I don’t need.