smoother boundaries between components of segmented image

I’d like to use boundaries between components of a segmented image to create a mesh for finite elements, as described in Mesh for Images for three materials and meshes with multiple regions from 2D images?.

The goal is smooth boundaries that roughly approximate the edges of the segmented components: the exact component boundaries, specified by individual pixels are finer than justified by the blur or noise in the original image.

To create a mesh, the boundaries must not have any gaps. One approach is using the "Contours" property of the components. But these boundaries be very jagged, which leads to unnecessarily fine meshes.

Other approaches are extracting boundaries from ImageMesh of each component, or from a contour plot. Both methods give smoother boundaries, but can have gaps or duplicates.

Is there a way to get boundaries without gaps, as given by the Contours property, but smoother, as given by the ImageMesh or contour plot approaches?

The boundaries should be a form suitable for meshing, e.g., line segments.


Create an image:

img = Blur[Rasterize[Graphics[{Gray, Rectangle[], Blue, Annulus[]},      PlotRangePadding -> None], RasterSize -> 200], 5] 

example image for component boundaries

Segment the image (clustering components is an example, the actual segmentation could come from other methods):

components = ClusteringComponents[img, 3]; 

Method 1: use Cluster property of the segments

boundaries =    ComponentMeasurements[components, "Contours", All,     "ComponentAssociation"];  Show[Values[   Graphics[#, PlotRange -> {{0, 200}, {0, 200}},       PlotRangePadding -> 10] & /@ boundaries]] 

boundaries from Contours property

The boundaries between components are jagged. Also, each such boundary appears twice, once for each of the two neighboring components. Since the two boundaries use exactly the same points, that duplication doesn’t cause a problem with meshing.

Method 2: use ImageMesh boundary of each segment

regions =    Table[ImageMesh[     Image[components /. Thread[DeleteCases[Range[3], i] -> 0]]], {i,3}];  Graphics[{EdgeForm[Black],Riffle[{Red, LightBlue, Green}, regions]},   PlotRangePadding -> None] 

ImageMesh of components

The boundaries of these regions are smoother than those from the components "Contours" property. But there are slight gaps between the regions, so their boundaries don’t quite match, as seen in this detail:

detail of ImageMesh of components

Method 3: use a contour plot

Create a contour plot on a blurred version of the components, with contour values selected to be between the gray levels specified for the components.

imgC = ColorConvert[    Blur[Colorize[components,       ColorRules -> {1 -> White, 2 -> Black, 3 -> Gray}], 5],     "Grayscale"];  ListContourPlot[Reverse@ImageData@imgC, Contours -> {0.4, 0.8},   ContourStyle -> {Blue, Darker@Green}, BaseStyle -> Thick,   ContourShading -> None,   Frame -> None, DataRange -> {{0, 200}, {0, 200}},  (* add outer boundary *)  Epilog -> Line[{{0, 0}, {200, 0}, {200, 200}, {0, 200}, {0, 0}}]  ] 

boundaries from ListContourPlot

The boundaries are smooth, but with two slightly different boundaries between some of the components.