Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Shape interpolation using whole contours, as described in
import cv2
import numpy as np
import scipy.spatial.distance as distance
def get_interpolated_points(c1, c2, step, scale):
c1 = c1.reshape(c1.shape[0], c1.shape[-1])
c2 = c2.reshape(c2.shape[0], c2.shape[-1])
dif = c2 - c1
return np.array(c1 + dif*step/scale, dtype=np.int32)
def get_sorted_shape_list(img, height, simplified):
Returns the shapes sorted from the longest to the shortest. Skips
the shapes consising of just one point.
if 3 == len(img.shape):
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
thr = cv2.threshold(img, 0, 255,
cnts = cv2.findContours(thr, cv2.RETR_LIST,
if simplified:
cnts = [cv2.approxPolyDP(i, height/200, True) for i in cnts]
cnts.sort(key=lambda x: cv2.arcLength(x, True), reverse=True)
return [i for i in cnts if i.shape[0] > 1]
def extend_list(sh_list, count):
Duplicate items in the sh_list to make it count items long.
:param sh_list: A list to duplicate some items in.
:param count: The resulting list will have as many items as source_sh.
:returns: A list such as len(returned_list) == count
src_points_count = len(sh_list)
smaller = count // src_points_count
rep_schema = np.full([src_points_count], smaller)
rep_schema[0:src_points_count-(smaller+1)*src_points_count+count] = smaller+1
return np.repeat(sh_list, rep_schema, axis=0)
def enlarge_contour(c1, point_count):
Returns a contour that has point_count points.
:param c1: A contour that has at most point_count points. If c1 has
the same number or more of points as point_count, returns c1.
:param point_count:
:returns: A new contour that has all points from c1 and new
points obtained by linear interpolation between the points
with the biggest distance.
to_add = point_count - c1.shape[0]
if to_add <= 0:
return c1
c1 = c1.reshape((c1.shape[0], c1.shape[-1]))
dists = []
for (idx, i) in enumerate(c1[1:]):
dists.append((distance.euclidean(c1[idx], i), idx))
# interpolate between c1[i0], c1[i0+1]
add_idxs = []
add_items = []
for i in range(max(1, min(len(dists)//2, to_add))):
i0 = dists[i][1]
new_pt = c1[i0] + (c1[i0+1] - c1[i0])/2
c1 = np.insert(c1, add_idxs, add_items, 0)
return enlarge_contour(c1, point_count)
def determine_font_scale(img_size):
Returns about the biggest font scale to fit a single character
into the supplied img_size.
scale = 1.0
for i in range(10):
(sz, baseline) = cv2.getTextSize("M", cv2.FONT_HERSHEY_SIMPLEX, scale, 4)
if sz[1] + baseline < img_size[0]*0.9:
scale *= (img_size[0]*0.9)/(sz[1]+baseline)
elif sz[1] + baseline > img_size[0]*0.95:
scale *= (img_size[0]*0.9)/(sz[1]+baseline)
return (scale, baseline)
def gen_char(img_size, c, scale, baseline):
Creates an image containing a centered character c.
img = np.zeros(img_size, dtype=np.uint8)
(sz, _baseline) = cv2.getTextSize(c, cv2.FONT_HERSHEY_SIMPLEX,
scale, 4)
cv2.putText(img, c, ((img_size[1]-sz[0])//2, img_size[0] - baseline),
cv2.FONT_HERSHEY_SIMPLEX, scale, 40, cv2.LINE_AA)
return img
def interpolate_shapes(out_shape, i0, i1):
c1l = get_sorted_shape_list(i0, out_shape[1], simplified=SIMPLIFIED)
c2l = get_sorted_shape_list(i1, out_shape[1], simplified=SIMPLIFIED)
# make the shapes the same length
if len(c1l) < len(c2l):
c1l = extend_list(c1l, len(c2l))
elif len(c2l) < len(c1l):
c2l = extend_list(c2l, len(c1l))
pairs = []
# now we need all shapes to have the same length
for i in range(len(c1l)):
c1 = c1l[i]
c2 = c2l[i]
if len(c1) > len(c2):
c2 = enlarge_contour(c2, len(c1))
elif len(c2) > len(c1):
c1 = enlarge_contour(c1, len(c2))
pairs.append((c1, c2))
SCALE = 100
for j in range(SCALE + 1):
img = np.zeros((out_shape[0], out_shape[1], 3), dtype=np.uint8)
for (c1, c2) in pairs:
cf = get_interpolated_points(c1, c2, j, SCALE)
cv2.drawContours(img, [cf], 0, (255, 0, 255), 4)
cv2.imshow("A", img)
return img
if "__main__" == __name__:
img_size = (800, 800)
(scale, baseline) = determine_font_scale(img_size)
i0 = gen_char(img_size, '-', scale, baseline)
for i in "01467&890-ijklmno":
i = ord(i)
img = gen_char(img_size, chr(i), scale, baseline)
out_img = interpolate_shapes(img_size, i0, img)
i0 = img
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.