Home > OS >  Displaying an image with dcc.Graph in Plotly-Dash
Displaying an image with dcc.Graph in Plotly-Dash

Time:12-01

I am trying to display an image with with dcc.Graph component in Plotly-Dash so I get its nice toolbars and built-in UI. However I am getting a bunch of errors. This is what my procedure is:

  1. Upload JPG image.
  2. Load image using np.array(Image.open(..)).
  3. Convert to figure with px.imshow().
  4. Pass this through dcc.Graph component and try to display it on page.

Below is my code:

import datetime
import dash
from dash.dependencies import Input, Output, State
from dash import dcc
from dash import html
import numpy as np
from PIL import Image
import plotly.express as px

app = dash.Dash(__name__)

app.layout = html.Div([

    html.Div(
        children=[
            dcc.Upload(
                id='upload-image',
                children=html.Div([
                    'Drag and Drop or ',
                    html.A('Select Files')
                ]),
                # Allow multiple files to be uploaded
                multiple=True)]),
    html.Div(
        children=[
            html.Div(id='output-image-upload'),
        ])
])

def parse_contents(contents, filename, date):
    img = np.array(Image.open(contents))
    fig = px.imshow(img)
    return html.Div([
        html.H5(filename),
        html.H6(datetime.datetime.fromtimestamp(date)),
        dcc.Graph(figure=fig)
    ])
        
@app.callback(Output('output-image-upload', 'children'),
              Input('upload-image', 'contents'),
              State('upload-image', 'filename'),
              State('upload-image', 'last_modified'))
def update_output(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
        children = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
        return children

if __name__ == '__main__':
    app.run_server(debug=True)

I get these errors below:

Callback error updating output-image-upload.children

Traceback (most recent call last):
  File "C:\Users\...\test.py", line 48, in update_output
    children = [
  File "C:\Users\...\test.py", line 49, in <listcomp>
    parse_contents(c, n, d) for c, n, d in
  File "C:\Users\...\test.py", line 34, in parse_contents
    img = np.array(Image.open(contents))
  File "C:\Users\...\AppData\Local\Programs\Python\Python38\Lib\site-packages\PIL\Image.py", line 2904, in open
    fp = builtins.open(filename, "rb")
FileNotFoundError: [Errno 2] No such file or directory: 'data:image/jpeg;base64,/9j/2wBDAAYEBQY...

CodePudding user response:

As explained in this answer you need to remove data:image/png;base64, from the image string. If you update your parse_contents function as follows your app should work:

def parse_contents(contents, filename, date):

    # Remove 'data:image/png;base64' from the image string,
    # see https://stackoverflow.com/a/26079673/11989081
    data = contents.replace('data:image/png;base64,', '')
    img = Image.open(io.BytesIO(base64.b64decode(data)))

    # Convert the image string to numpy array and create a
    # Plotly figure, see https://plotly.com/python/imshow/
    fig = px.imshow(np.array(img))

    # Hide the axes and the tooltips
    fig.update_layout(
        plot_bgcolor='white',
        paper_bgcolor='white',
        margin=dict(t=20, b=0, l=0, r=0),
        xaxis=dict(
            showgrid=False,
            showticklabels=False,
            linewidth=0
        ),
        yaxis=dict(
            showgrid=False,
            showticklabels=False,
            linewidth=0
        ),
        hovermode=False
    )

    return html.Div([
        html.H5(filename),
        html.H6(datetime.datetime.fromtimestamp(date)),
        dcc.Graph(
            figure=fig,
            config={'displayModeBar': True} # Always display the modebar
        )
    ])

Full code:

import io
import base64
import datetime
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import numpy as np
import plotly.express as px
from PIL import Image

app = dash.Dash(__name__)

app.layout = html.Div([
    html.Div(
        children=[
            dcc.Upload(
                id='upload-image',
                children=html.Div(['Drag and Drop or ', html.A('Select Files')]),
                multiple=True
            )
        ]
    ),
    html.Div(
        children=[
            html.Div(id='output-image-upload'),
        ]
    )
])

def parse_contents(contents, filename, date):

    # Remove 'data:image/png;base64' from the image string,
    # see https://stackoverflow.com/a/26079673/11989081
    data = contents.replace('data:image/png;base64,', '')
    img = Image.open(io.BytesIO(base64.b64decode(data)))

    # Convert the image string to numpy array and create a
    # Plotly figure, see https://plotly.com/python/imshow/
    fig = px.imshow(np.array(img))

    # Hide the axes and the tooltips
    fig.update_layout(
        plot_bgcolor='white',
        paper_bgcolor='white',
        margin=dict(t=20, b=0, l=0, r=0),
        xaxis=dict(
            showgrid=False,
            showticklabels=False,
            linewidth=0
        ),
        yaxis=dict(
            showgrid=False,
            showticklabels=False,
            linewidth=0
        ),
        hovermode=False
    )

    return html.Div([
        html.H5(filename),
        html.H6(datetime.datetime.fromtimestamp(date)),
        dcc.Graph(
            figure=fig,
            config={'displayModeBar': True} # Always display the modebar
        )
    ])

@app.callback(
    Output('output-image-upload', 'children'),
    [Input('upload-image', 'contents')],
    [State('upload-image', 'filename'),
     State('upload-image', 'last_modified')])
def update_output(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
        children = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)
        ]
        return children

if __name__ == '__main__':
    app.run_server(debug=True, host='127.0.0.1')
  • Related