Search code examples
pythoninterpolationbezierspline

How to interpolate beautiful smooth curve path for the given x and y mouse coordinates


I am making a web application that records the user's screen and capture the mouse position every x ms and send both the data to the python backend.

In the backend, it receives all the mouse position data and the recorded video after the user stops the recording. Now, in the backend, I want to generate a beautiful interpolated curved smooth path based on those mouse positions and draw a custom mouse on top of each frame of the recorded video. The interpolation should be smooth and curved based on the available mouse coordinates data and it should generate the full path.

For example, I have 5 mouse position data in a recording of 5 seconds, now I want to draw a custom mouse on top of each frame of the recorded video based on those 5 mouse position data, but I want to smooth result, the custom mouse should be drawn in a smooth and curved way from the point of the next data point.

The movement should be beautiful and smooth. I read about Bézier Curves and spline interpolation. Which one would be good for such a situation? Ideally, the line between the curves should be curved.

Demo Image Demo Image 2

For example, I have a 5-second video, i.e. 5 sec * 30 (30 fps) = 150 frames, and I only have data of 20 mouse positions, now it had to do a beautiful smooth curve based on those mouse positions in a curve way and generate positions between two data points so that at last I would have a total of 150 mouse positions that when drawn a custom mouse based on those position on top of frames forms a curve and smooth movements journey.


Solution

  • Following this solution, you can perform a cubic spline interpolation between each of the points. With that, you can get a finer sampling of points to generate the curve you are looking for.

    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.interpolate import interp1d
    
    plt.close("all")
    
    rng = np.random.default_rng()
    
    N = 10
    x = rng.uniform(-5, 5, N)
    y = rng.uniform(-5, 5, N)
    xy = np.vstack((x.reshape(1, N), y.reshape(1, N)))
    
    t = np.linspace(0, 1, N)
    spline = interp1d(t, xy, kind="cubic")
    
    t_fine = np.linspace(0, 1, N**2)
    x_fine, y_fine = spline(t_fine)
    
    fig, ax = plt.subplots()
    ax.plot(x_fine, y_fine)
    ax.plot(x, y, ".")
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    

    enter image description here


    Edit:

    I've played around with this problem some more and also tested using splprep with splev, which doesn't require combining the data into a single array. I've included some comparison plots for the same randomly-generated points. You can decide which you prefer to use.

    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.interpolate import interp1d, splprep, splev
    
    plt.close("all")
    
    rng = np.random.default_rng()
    
    N = 10
    x = rng.uniform(-5, 5, N)
    y = rng.uniform(-5, 5, N)
    
    # method 1: interp1d
    xy = np.vstack((x.reshape(1, N), y.reshape(1, N)))
    
    t = np.linspace(0, 1, N)
    spline = interp1d(t, xy, kind="cubic")
    
    t_fine = np.linspace(0, 1, N**3)
    x_fine1, y_fine1 = spline(t_fine)
    
    # method 2: splprep
    tck, _ = splprep([x, y], s=0)
    x_fine2, y_fine2 = splev(t_fine, tck)
    
    # side-by-side plot
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10,5), sharex=True, sharey=True)
    ax1.plot(x_fine1, y_fine1)
    ax1.plot(x, y, ".")
    ax1.set_xlabel("x")
    ax1.set_ylabel("y")
    ax1.set_title("interp1d")
    
    ax2.plot(x_fine2, y_fine2)
    ax2.plot(x, y, ".")
    ax2.set_xlabel("x")
    ax2.set_ylabel("y")
    ax2.set_title("splprep")
    

    enter image description here

    enter image description here

    enter image description here