In [0]:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
|
Given a limited number of pre-defined points and associated values, interpolation allows to predict new data points within the range of the pre-defined points.
In the example below, the left plot shows samples represented as blue dots. Assuming that these samples come from a smooth function, one has many options available to find plausible values between these dots. A first option is to build a piece-wise linear function which links any pair of neighbouring points with a line, as can be observed in the central plot. Another widely used option is to fit a polynomial to these samples. The right plot illustrates a cubic polynomial fitted to the samples.
This notebook illustrates how to use Tensorflow Graphics to perform B-Spline and Slerp interpolation.
Note: The easiest way to use this tutorial is as a Colab notebook, which allows you to dive in with no setup.
In [0]:
!pip install tensorflow_graphics
Now that Tensorflow Graphics is installed, let's import everything needed to run the demo contained in this notebook.
In [0]:
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow_graphics.math.interpolation import bspline
from tensorflow_graphics.math.interpolation import slerp
tf.compat.v1.enable_eager_execution()
Lerp is a widely used interpolation technique allowing to linearly interpolate between points. The piece-wise linear interpolation described at the beginning of this Colab effectively consists of pieces of linear interpolants. But what about if our data lives on a circle or at the surface of a sphere? In that case, Lerp would not provide a good way to interpolate, but fortunately, Slerp would! Slerp stands for spherical linear interpolation and has been introduced in the context of interpolating quaternions, which is a rotation formalism. We refer the interested reader to the wikipedia page for Slerp for further details.
The following demo allows to define two vectors, each starting from the center of a circle and ending on the circle. These define the vectors we want to interpolate between, and the slider 'percent' controls the extent to which each vector influences the interpolated vector. Note that the resulting vector also ends on the circle.
In [0]:
#@title Slerp - Vectors will be normalized first { vertical-output: true, run: "auto"}
vector_1_x = -0.56 #@param { type: "slider", min: -1.0, max: 1.0, step: 0.01}
vector_1_y = -0.39 #@param { type: "slider", min: -1.0, max: 1.0, step: 0.01}
vector_2_x = 0.47 #@param { type: "slider", min: -1.0, max: 1.0, step: 0.01}
vector_2_y = 0.74 #@param { type: "slider", min: -1.0, max: 1.0, step: 0.01}
percent = 0.7 #@param { type: "slider", min: 0.0, max: 1.0, step: 0.01}
vector_1 = tf.constant((vector_1_x, vector_1_y), dtype=tf.float32)
vector_2 = tf.constant((vector_2_x, vector_2_y), dtype=tf.float32)
vector_1 = tf.nn.l2_normalize(vector_1)
vector_2 = tf.nn.l2_normalize(vector_2)
vector_3 = slerp.interpolate(
vector_1, vector_2, percent, method=slerp.InterpolationType.VECTOR)
v1 = vector_1.numpy()
v2 = vector_2.numpy()
v3 = vector_3.numpy()
plt.figure(figsize=(10, 10))
circle = plt.Circle((0, 0), 1.0, color='g', fill=False)
ax = plt.gca()
ax.add_artist(circle)
plt.arrow(
0.0, 0.0, v1[0], v1[1], width=0.001, color='k', length_includes_head=True)
plt.arrow(
0.0, 0.0, v2[0], v2[1], width=0.001, color='b', length_includes_head=True)
plt.arrow(
0.0, 0.0, v3[0], v3[1], width=0.001, color='r', length_includes_head=True)
plt.axis((-1.1, 1.1, -1.1, 1.1))
plt.show()
As opposed to other interpolation techniques that have a global support (e.g. bezier curve, global polynomial fit), B-Spline are piece-wise polynomial functions that provide local control. The control comes from the position of points called 'knots'. The following demo illustrates how the position of these knots locally affect the interpolated curve.
We refer the interested reader to the wikipedia page for B-Spline for further details.
In [0]:
#@title B-Spline Interpolation { vertical-output: true, run: "auto"}
num_knots = 5
cyclical = True #@param { type: "boolean" }
degree = 3 #@param { type: "slider", min: 1, max: 4, step: 1}
knot_1_x = -2.5 #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_1_y = -1 #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_2_x = -1.5 #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_2_y = 2 #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_3_x = 0 #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_3_y = -3 #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_4_x = 1.5 #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_4_y = 3 #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_5_x = 3 #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
knot_5_y = 0 #@param { type: "slider", min: -3.0, max: 3.0, step: 0.5}
max_pos = num_knots if cyclical else num_knots - degree
knots = tf.constant(((knot_1_x, knot_2_x, knot_3_x, knot_4_x, knot_5_x),
(knot_1_y, knot_2_y, knot_3_y, knot_4_y, knot_5_y)))
positions = tf.expand_dims(
tf.range(start=0.0, limit=max_pos, delta=0.01, dtype=knots.dtype), axis=-1)
spline = bspline.interpolate(knots, positions, degree, cyclical)
spline = tf.squeeze(spline, axis=1)
plt.figure(figsize=(10, 10))
plt.plot(spline[:, 0], spline[:, 1], 'r')
plt.plot(knots[0, :], knots[1, :], 'b.')
plt.axis((-3.5, 3.5, -3.5, 3.5))
plt.show()