Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Shape interpolation, as described in
import cv2
import numpy as np
import scipy.spatial.distance as distance
def get_interpolated_points(c1, c2, step, scale):
Performs a linear interpolation between c1 and c2. Draws a line between
members of c1 and c2 on the same index, divides the line between the
points into "scale" sections and returns the step-th point.
:param c1: A list of points (may be a contour from cv2.getContours).
:param c2: A list of the same length (number of points) as c1.
:param step: The step of the interpolation on the scale from (0 to scale>.
:param scale: The length of the scale.
:returns: A list of points interpolated between c1 and c2.
get_interpolated_points(np.array([[0,0], [10,0]]),
np.array([[10,0], [0,0]]), 1, 10) --> [[1, 0], [9, 0]]
get_interpolated_points(np.array([[0,0], [10,0]]),
np.array([[10,0], [0,0]]), 9, 10) --> [[9, 0], [1, 0]]
get_interpolated_points(np.array([[0,0], [10,0]]),
np.array([[10,0], [0,0]]), 10, 10) --> [[10, 0], [0, 0]]
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_contour(img, height):
if 3 == len(img.shape):
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
thr = cv2.threshold(img, 0, 255,
cnts = cv2.findContours(thr, cv2.RETR_TREE,
ret = None
for (idx, i) in enumerate(sorted(cnts, key=cv2.contourArea, reverse=1)):
ret = cv2.approxPolyDP(i, height/600, True)
return ret
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(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(img_size, i0, i1):
out_shape = img_size
c1 = get_contour(i0, out_shape[1])
c2 = get_contour(i1, out_shape[1])
c1 = c1.reshape(c1.shape[0], c1.shape[-1])
c2 = c2.reshape(c2.shape[0], c2.shape[-1])
if c1.shape[0] < c2.shape[0]:
c1 = enlarge_contour(c1, c2.shape[0])
c2 = enlarge_contour(c2, c1.shape[0])
c_fin = c2
SCALE = 50
for i in range(SCALE + 1):
c2 = get_interpolated_points(c1, c_fin, i, SCALE)
img = np.zeros((out_shape[0], out_shape[1]), dtype=np.uint8)
cv2.drawContours(img, [c2], -1, 255, 50)
cv2.imshow("A", img)
if "__main__" == __name__:
img_size = (600, 600)
(scale, baseline) = determine_font_scale(img_size)
i0 = gen_char(img_size, 'A', scale, baseline)
i1 = gen_char(img_size, 'O', scale, baseline)
interpolate_shapes(img_size, i0, i1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment