New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embedding or displaying local images #71

Closed
FaustineLi opened this Issue Jul 5, 2017 · 19 comments

Comments

Projects
None yet
9 participants
@FaustineLi
Copy link

FaustineLi commented Jul 5, 2017

I can't find a way to embed or display local images, either through html or in a plotly graph. This seems to be a limitation with the server can do (in the former case) or a lack of support (in the later case).

My intended use case is have a user point to a directory of images. I want to display the image that the user selects. The image is then classified using a model built in Keras.

Is there any way to display an image locally?

@chriddyp

This comment has been minimized.

Copy link
Member

chriddyp commented Jul 5, 2017

Great question @FaustineLi !

Serving general static content isn't supported out of the box with Dash (yet). Here are two workarounds for now:
1 - You can serve files by adding a static file route to the underlying dash server.
2 - You can serve files by starting a separate file server like Python's built-in file server ($ python -m SimpleHTTPServer 8000) or something like nginx or apache.
3 - You can upload images to a 3rd party image hosting site like Amazon S3

1 is the simplest way to go and works great for just running apps on your own machine. Here's a simple example that displays PNGs from my personal Desktop (mostly screenshots of Plotly!).

Note that if you deploy this app, you'll want to be careful that the static file serving route is only serving files from a single directory (otherwise, malicious users might be able to view any file on the server). This example includes a whitelist of files and only serves files from the Desktop.

import dash
import dash_core_components as dcc
import dash_html_components as html

import flask
import glob
import os

image_directory = '/Users/chriddyp/Desktop/'
list_of_images = [os.path.basename(x) for x in glob.glob('{}*.png'.format(image_directory))]
static_image_route = '/static/'

app = dash.Dash()

app.layout = html.Div([
    dcc.Dropdown(
        id='image-dropdown',
        options=[{'label': i, 'value': i} for i in list_of_images],
        value=list_of_images[0]
    ),
    html.Img(id='image')
])

@app.callback(
    dash.dependencies.Output('image', 'src'),
    [dash.dependencies.Input('image-dropdown', 'value')])
def update_image_src(value):
    return static_image_route + value

# Add a static image route that serves images from desktop
# Be *very* careful here - you don't want to serve arbitrary files
# from your computer or server
@app.server.route('{}<image_path>.png'.format(static_image_route))
def serve_image(image_path):
    image_name = '{}.png'.format(image_path)
    if image_name not in list_of_images:
        raise Exception('"{}" is excluded from the allowed static files'.format(image_path))
    return flask.send_from_directory(image_directory, image_name)

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

Here's what the app looks like:
image-serving

@chriddyp

This comment has been minimized.

Copy link
Member

chriddyp commented Jul 5, 2017

Closing this for now. Feel free to reopen if this solution doesn't work for you!

@chriddyp chriddyp closed this Jul 5, 2017

@FaustineLi

This comment has been minimized.

Copy link

FaustineLi commented Jul 6, 2017

Thanks! That definitely worked!

@chriddyp

This comment has been minimized.

Copy link
Member

chriddyp commented Jul 20, 2017

Another way to display images without adding an extra route is to base64 encode them:

import dash
import dash_html_components as html
import base64

app = dash.Dash()

image_filename = 'my-image.png' # replace with your own image
encoded_image = base64.b64encode(open(image_filename, 'rb').read())

app.layout = html.Div([
    html.Img(src='data:image/png;base64,{}'.format(encoded_image))
])

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

This comment has been minimized.

Copy link

shapiromatron commented Jul 28, 2017

Thanks, this was helpful. This is especially useful if you want to bundle your standard css resources in a relative path in a project. I feel like this might be a really common use-case that may be good to bake into the docs; if you're interested @chriddyp I could submit a PR.

I'm coming from the django-world; here's a minimal example for static serving any arbitrary resource from a /static/ path, using a pattern similar to django static files in debug mode (docs):

import dash
import flask

STATIC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')
app = dash.Dash()

@app.server.route('/static/<resource>')
def serve_static(resource):
    return flask.send_from_directory(STATIC_PATH, resource)

app.css.append_css({
    'external_url': '/static/site.css',
})

if __name__ == '__main__':
    app.run_server()
@zshujon

This comment has been minimized.

Copy link

zshujon commented Aug 8, 2017

I've the same problem with Video.
Please tell me what to do?

@chriddyp

This comment has been minimized.

Copy link
Member

chriddyp commented Aug 8, 2017

@zshujon - could you open an issue in the community forum? More folks are likely to help you out with implementation details there: https://community.plot.ly/c/dash

@cfeng2016

This comment has been minimized.

Copy link

cfeng2016 commented Aug 18, 2017

I found the base64 encoding approach works in general but doesn't work if I am working under virtualenv. Why and is there a work around? Thanks.

@chriddyp

This comment has been minimized.

Copy link
Member

chriddyp commented Aug 18, 2017

@cfeng2016 - Can you create a small reproducable example?

@cfeng2016

This comment has been minimized.

Copy link

cfeng2016 commented Aug 18, 2017

@chriddyp It looks like the problem is system/python dependent. My Mac with Python 3.6.2 under virtualenv has problem but Ubuntu with Python 2 does not (with or without virtualenv). I checked the html code. The problem came when it is

<img src="data:image/png;base64,b'......'" class=......>

Removing 'b'' parts solves the problem. So I think it's from the return of the function base64.b64encode()

@cfeng2016

This comment has been minimized.

Copy link

cfeng2016 commented Aug 20, 2017

@chriddyp - It looks like the Python version causes the difference. Using decode() function will fix it regardless of Python environment.

import dash
import dash_html_components as html
import base64

app = dash.Dash()
image_filename = 'my-image.png' # replace with your own image
encoded_image = base64.b64encode(open(image_filename, 'rb').read())
app.layout = html.Div([
    html.Img(src='data:image/png;base64,{}'.format(encoded_image.decode()))
])

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

This comment has been minimized.

Copy link

ianwaring commented Sep 25, 2017

I know it's a bit around the houses with Base64 versions - but how do I adjust the size of the image using html.Img() once I have it? Trying to find any documentation to see if there are methods I can invoke to alter the size! Any help, guidance etc would really help my sanity :-}

@chriddyp

This comment has been minimized.

Copy link
Member

chriddyp commented Sep 25, 2017

@ianwaring - try out the style parameter which sets inline css like:

html.Image(src='...', style={'width': '500px'})

I recommend messing around with these CSS inline styles by using Chrome's dev tools: https://developers.google.com/web/tools/chrome-devtools/inspect-styles/.

@eloiup

This comment has been minimized.

Copy link

eloiup commented Sep 29, 2017

hey chris,

the base64 solution worked great for me, but the image is too small. I used the style option within html.image, but i was wondering if there were other ways to edit it.

I have also looked into https://plot.ly/dash/dash-html-components for reference. But it doesnt have that many examples. For example, i wld like to change its position, but i am unsure to the proper argument for it.

Also, for some reason, the image is located above a few dropdown menus i built in the dash. I have no idea why, since i declare 'em previous to the image.

edit: for some reason the style={'width': '20%', 'display': 'inline-block'}) within the dropdowns were causing em to go below the image.
edit: tryied "align":"middle" on img style without success. i was able to manually set the paddings with "margin":"x px y px ..." but the auto command leaves the img attached to the left border instead of centralized

@chriddyp

This comment has been minimized.

Copy link
Member

chriddyp commented Sep 29, 2017

Hey @eloiup - Yeah, sounds like CSS issue. style translates to inline css, so that is the correct argument to modify its size and position. I recommend playing around with this live in the Chrome's CSS editor: https://developers.google.com/web/tools/chrome-devtools/inspect-styles/

@chaotic-enigma

This comment has been minimized.

Copy link

chaotic-enigma commented May 14, 2018

How do I Update single image as per the input given. I have tried, and it gives big chunk errors saying

RuntimeError: main thread is not in main loop.

My input is basically a date and the output image is a graph. I have provided input section in my dash app but when I give my input, no image displays except the error in my terminal.

I think for this case, we cannot use base64 method. Is there any Different method?

@ghost

This comment has been minimized.

Copy link

ghost commented Aug 19, 2018

I am trying to do something similar for Plotly 3D surface plots, but it would load the image. Do I need to make some changes in go.Layout()? I want the image on x-y quadrant.

@chriddyp

This comment has been minimized.

Copy link
Member

chriddyp commented Aug 20, 2018

I am trying to do something similar for Plotly 3D surface plots, but it would load the image

What do you mean by "load the image"? Do you mean, render the 3D surface plot as an image? If so, then that's a slightly different issue than the one being discussed here.

If you just want an image to be embedded inside the graph, then you can use the layout.images object, see https://plot.ly/python/images/. I'm not sure if that's suppored in 3D plots or not, but you could search through issues in https://github.com/plotly/plotly.js/issues/ if it isn't supported.

@dbjohnsonii

This comment has been minimized.

Copy link

dbjohnsonii commented Dec 27, 2018

Another way to display images without adding an extra route is to base64 encode them:

import dash
import dash_html_components as html
import base64

app = dash.Dash()

image_filename = 'my-image.png' # replace with your own image
encoded_image = base64.b64encode(open(image_filename, 'rb').read())

app.layout = html.Div([
    html.Img(src='data:image/png;base64,{}'.format(encoded_image))
])

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

Hello, I recognize that this string may be closed but I explored the 64base option with no success. I used the script below and it only returned the broken image icon. Has anything changed since this conversation in 2017? FYI I am using Python Version 3.7.0

import dash
import dash_html_components as html
import base64

app = dash.Dash()

image_filename = 'my-image.png'
encoded_image = base64.b64encode(open(image_filename, 'rb').read())

app.layout = html.Div([
html.Img(src='data:image/png;base64,{}'.format(encoded_image))
])

if name == 'main':
app.run_server(debug=True)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment