Background image plotly animation

Hi there,

I am trying to add an individual background image for each time step of a plotly animation.
So I have a plot (like here: Intro to animations in Python) and would like to set one image per time step as a background. Is this possible somhow?

Thanks in advance!

Hi @Vale1,
To run the code below download the images i1.png-i5.png
from https://github.com/empet/Datasets/tree/master/Gnomonic-Projs. There are 15 frames in which i1-i5 are repeating 3 times.
(see {k%5+1} in the for loop).

 import plotly.graph_objects as go
import numpy as np
import base64

fig=go.Figure(go.Scatter(x=np.linspace(0,3, 50), y=np.random.rand(50), mode="lines",
                        line_color="white", line_width=2))
fig.update_layout(width=700, height=450)

#used images i1-i5.png  three times; https://github.com/empet/Datasets/tree/master/Gnomonic-Projs
#set a local image as a background
img1 = base64.b64encode(open("images/i1.png", 'rb').read())

fig.update_layout(
                images= [dict(
                    source='data:image/png;base64,{}'.format(img1.decode()),
                    xref="paper", yref="paper",
                    x=0, y=1,
                    sizex=1, sizey=1,
                    xanchor="left",
                    yanchor="top",
                    sizing="stretch",
                    layer="below")])

frames=[]

for k in range(1,15):
    img = base64.b64encode(open(f"images/i{k%5 +1}.png", 'rb').read())
    frames.append(go.Frame(layout=dict(images=[dict(source='data:image/png;base64,{}'.format(img.decode()))])))
    
fig.update(frames=frames)

fig.update_layout(updatemenus = [dict(
        buttons = [
            dict(
                args = [None, {"frame": {"duration": 100, "redraw": True},
                                "fromcurrent": True}],
                label = "Play",
                method = "animate"
                ),
            dict(
                 args = [[None], {"frame": {"duration": 0, "redraw": False},
                                  "mode": "immediate",
                                  "transition": {"duration": 0}}],
                label = "Pause",
                method = "animate"
                )
        ],
        direction = "left",
        pad = {"r": 10, "t": 87},
        showactive = False,
        type = "buttons",
        x = 0.1,
        xanchor = "right",
        y = 0,
        yanchor = "top"
    )] ) 

If you want to remove the grid lines just add to fig.update_layout, defined above, more two settings:

fig.update_layout(xaxis_showgrid=False, yaxis_showgrid=False)

Thank you very much for your answer @empet !
Sorry I should have been a little more precise with my explanation what I’m looking for.
I have a scatter plot that shows data that changes over time. (an animation as in the above linke)
I think the bigger picture makes it easier to understand:
I’m detecting objects in a scenario that changes over time (hence, the pictures represent this (discretized!) timeflow which is why I would like to have it as an animation, so you can iterate over the different timesteps) so for each timestep I have one image that I’d like to display in the background and a few datapoints that refer to the position of the detected stuff in this image/for this specific time step.
As I want to make additional information accessible by clicking on the each datapoint I can’t just save the points on the image and display the result… (I’m working with dash which allows this interactivity)
So for the moment I have this figure which plots the datapoints for each timestep and that allows me to have access additional information for each datapoint thanks to dash.

        html.Div([
            dcc.Dropdown(
                df['value'].unique(),                    # just an unimportant filter  
                'all',
                id='crossfilter-xaxis-column',
            )
        ],


@app.callback(
    Output('crossfilter-indicator-scatter', 'figure'),
    Input('crossfilter-time_step--slider', 'value'))
def update_graph( time_step_value):
    df = df[df['time_step'] == time_step_value]

    fig = px.scatter(df,                         
        x=df['x_position_m'],
        y=df['y_position_m'],
        hover_name = df['trajectory_number'],
        color="value", color_continuous_scale='rdylgn_r'
        )

    fig.update_traces(customdata=df['trajectory_number'])

    fig.update_xaxes(title=xaxis_column_name, type='linear' if xaxis_type == 'Linear' else 'log')

    fig.update_yaxes(title=yaxis_column_name, type='linear' if yaxis_type == 'Linear' else 'log')

    fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')


return fig

So I’d like to add the background to this figure somehow…

@Vale1 In short, you want each frame to change simultaneously both x and y in the scatter trace, and layout image. Compared to the previous example you should now modify the initial definition for the scatter plot, add xaxis_range and yaxis_range in the layout, so that during the animation to avoid generating points that are not displayed:

fig=go.Figure(go.Scatter(x=-2+4*np.random.rand(50), y=3*np.random.rand(50), mode="markers",
                        marker_color="white", marker_size=6))
fig.update_layout(width=700, height=450, xaxis_range=[-2.1, 2.1], yaxis_range=[-0.1, 3.1], autorange=False)

Also change the definition of the frames as follows:"

frames=[]

for k in range(1,15):
    img = base64.b64encode(open(f"images/i{k%5 +1}.png", 'rb').read())
    frames.append(go.Frame(data=[go.Scatter(x=-2+4*np.random.rand(50), 
                                            y=3*np.random.rand(50))],
                           layout=dict(images=[dict(source='data:image/png;base64,{}'.format(img.decode()))])))

and adjust the frame duration to 400-500.