Interpolate continuous curve from noisy data

Viewed 213

I am trying to estimate/interpolate a curve from noisy data like the circle in the example. My data consists of more than circles but this should be a good starting point for solving the other structures as well.

I have a noisy binary image and I am trying to fit a continuous curve/skeleton to it (each pixel has 2 neighbours, except maybe start and end pixel, if the shape is not circular). I had some success fitting the x,y coordinates separately, using the distance to a starting point as x values and the coordinates as y value and then interpolating distances in small steps. Then I checked if the coordinates were all connected. In some extreme cases the new interpolated points are not connected and I have to use smaller steps for the interpolation. This often also leads to pixels with more than 2 neighbours and other weird artifacts.

Is there an easier way to fit these values to a curve and to get a continuous curve as a result?

import numpy as np
from skimage import draw
from matplotlib import pyplot as plt
image = np.zeros((200,200), dtype=np.uint8)

coords = np.array(draw.circle_perimeter(100,100,50))

noise = np.random.normal(0,2,coords.shape).astype(np.int64)

coords += noise
image[coords[0], coords[1]] = 1

plt.imshow(image, cmap="gray")

plt.show()

noisy_circle

2 Answers

To fit data, you need a model. There are any number of ways of fitting a circle. The one I've had the most success with is Ian Coope's linearized solution. The paper is available here: https://ir.canterbury.ac.nz/handle/10092/11104

I've made a python implementation of it in a linearized fitting library called scikit-guess. The function is skg.nsphere_fit. Given your (2, n) array coords, you would use it like this:

from skg import nsphere_fit

radius, center = nsphere_fit(coords, axis=0)

To plot over your image, you can use matplotlib.patches.Circle:

from matplotlib.patches import Circle

fig, ax = plt.subplots()
ax.imshow(image, cmap='gray')
ax.add_patch(Circle(center[::-1], radius, edgecolor='red', facecolor='none'))

You need to reverse center because your input coordinates are (row, col), while Circle expects (x, y), which is (col, row).

enter image description here

To fit a different model, you would need a different method. For arbitrary models, you might want to look into scipy.optimize and lmfit.

Related