Disclaimer: I am in no way an expert in statistics, so much of the details is beyond me. This is just an explanation of my attempt to solve the problem I had.


Recently, I was working with some cool stuff in image processing. I had to extract some shapes after binarizing some images. The final task was to smoothen the contours extracted from the shapes to give it a better feel.

After researching around a bit, the task was clear. All I had to do was resample the points in the contours at regular intervals and draw a spline through the control points. But opencv had no native function to do this. So I had to resort to numpy. Now, another problem in numpy was the data representation. Though opencv uses numpy internally, you have to jump through a couple of hoops to get everything running along smoothly.

Without wasting further time, here’s the code -

Get the contours from the binary image-

import cv2

ret,thresh_img = cv2.threshold(
			img,
			127,
			255,
			cv2.THRESH_BINARY_INV)
contours, hierarchy = cv2.findContours(thresh_img,
			cv2.RETR_TREE,
			cv2.CHAIN_APPROX_SIMPLE)

Now comes the numpy code to smoothen each contour-

import numpy
import cv2
from scipy.interpolate import splprep, splev

smoothened = []
for contour in contours:
    x,y = contour.T
    # Convert from numpy arrays to normal arrays
    x = x.tolist()[0]
    y = y.tolist()[0]
    # https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.interpolate.splprep.html
    tck, u = splprep([x,y], u=None, s=1.0, per=1)
    # https://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.linspace.html
    u_new = numpy.linspace(u.min(), u.max(), 25)
    # https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.interpolate.splev.html
    x_new, y_new = splev(u_new, tck, der=0)
    # Convert it back to numpy format for opencv to be able to display it
    res_array = [[[int(i[0]), int(i[1])]] for i in zip(x_new,y_new)]
    smoothened.append(numpy.asarray(res_array, dtype=numpy.int32))

# Overlay the smoothed contours on the original image
cv2.drawContours(original_img, smoothened, -1, (255,255,255), 2)

P.S.: Credit has to be given to this SO answer which served as the starting point.

As you can see, data conversion is required to pass to splprep. And then again, when you are appending to the list to overlay on the image.

Hope you found it useful. If you have a better way to achieve the same result, please do not hesitate to let me know in the comments !


Agniva De Sarker

My journal on Software, Bugs, Tips & Tricks.