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:
- Upload JPG image.
- Load image using
np.array(Image.open(..))
. - Convert to figure with
px.imshow()
. - 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')