display: a browser-based graphics server

Overview

display: a browser-based graphics server

A very lightweight display server for Torch. Best used as a remote desktop paired with a terminal of your choice.

Use a Torch REPL (e.g., trepl) via SSH to control Torch and tell it to display stuff (images, plots, audio) to the server. The server then forwards the display data to (one or more) web clients.

Installation

Install for Torch via:

luarocks install https://raw.githubusercontent.com/szym/display/master/display-scm-0.rockspec

Install for Python (numpy required) via:

python setup.py install [--user]

NOTE: The Python client is not yet fully developed.

Quick Start

Launch the server:

th -ldisplay.start [port [hostname]]

Note, there is no authentication so don't use "as is" for sensitive data. By default, the server listens on localhost. Pass 0.0.0.0 to allow external connections on any interface:

th -ldisplay.start 8000 0.0.0.0

Then open http://(hostname):(port)/ in your browser to load the remote desktop.

To actually display stuff on the server, use the display package in a Torch script or REPL:

-- Generic stuff you'll need to make images anyway.
torch = require 'torch'
image = require 'image'

-- Load the display package
display = require 'display'

-- Tell the library, if you used a custom port or a remote server (default is 127.0.0.1).
display.configure({hostname='myremoteserver.com', port=1234})

-- Display a torch tensor as an image. The image is automatically normalized to be renderable.
local lena = image.lena()
display.image(lena)

-- Plot some random data.
display.plot(torch.cat(torch.linspace(0, 10, 10), torch.randn(10), 2))

See example.lua or example.py for a bigger example.

Usage

Each command creates a new window (pane) on the desktop that can be independently positioned, resized, maximized. It also returns the id of the window which can be passed as the win option to reuse the window for a subsequent command. This can be used to show current progress of your script:

for i = 1, num_iterations do
   -- do some work
   ...
   -- update results
   local state_win = display.image(current_state, {win=state_win, title='state at iteration ' .. i})
end

Another common option is title. The title bar can be double-clicked to maximize the window. The x button closes the window. The o button "disconnects" the window so that it will not be overwritten when the script reuses the window id. This is useful if you want to make a quick "copy" of the window to compare progress between iterations.

Images

display.image(tensor, options)

Displays the tensor as an image. The tensor is normalized (by a scalar offset and scaling factor) to be displayable. The image can be panned around and zoomed (with the scroll wheel or equivalent). Double-click the image to restore original size or fill the window.

If the tensor has 4 dimensions, it is considered to be a list of images -- sliced by first dimension. Same thing if it has 3 dimensions but the first dimension has size more than 3 (so they cannot be considered the RGB channels). This is equivalent to passing a list (Lua table) of tensors or the explicit images command. This is convenient when visualizing the trained filters of convolutional layer. Each image is normalized independently. When displaying a list of images, the option labels can be used to put a small label on each sub-image:

display.images({a, b, c, d}, {labels={'a', 'b', 'c', 'd'}})

Finally, the option width can be used to specify the initial size of the window in pixels.

Plotting

display.plot(data, options)

Creates a Dygraph plot which is most useful for visualizing time series. The graph can be zoomed in by selecting a range of X values or zoomed-out by double-clicking it.

The data should either be a 2-dimensional tensor where the each row is a data point and each column is a series, or a Lua table of tables. The first column is always taken as the X dimension. The command supports all the Dygraph options. Most importantly labels is taken as a list (Lua table) of series labels. Again the first label is for the X axis. You can name the Y axis with ylabel.

local config = {
  title = "Global accuracy/recall over time",
  labels = {"epoch", "accuracy", "recall"},
  ylabel = "ratio",
}

for t = 1, noEpoch do
  -- update model, compute data
  local accuracy, recall = train()
  -- update plot data
  table.insert(data, {t, accuracy, recall})
  -- display
  config.win = display.plot(data, config)
end

Other

display.audio(tensor_with_audio, options)

Development

Supported commands

  • pane: creates a new Pane of specified type; arguments are:
    • type: the registered type, e.g., image for ImagePane
    • win: identifier of the window to be reused (pick a random one if you want a new window)
    • title: title for the window title bar
    • content: passed to the Pane.setContent method

Built-in Pane types

image creates a zoomable element

  • src: URL for the element
  • width: initial width in pixels
  • labels: array of 3-element arrays [ x, y, text ], where x, y are the coordinates (0, 0) is top-left, (1, 1) is bottom-right; text is the label content

plot creates a Dygraph, all Dygraph options are supported

  • file: see Dygraph data formats for supported formats
  • labels: list of strings, first element is the X label

text places raw text in

element

audio places raw audio content in an element

Technical overview

The server is a trivial message forwarder:

POST /events -> EventSource('/events')

The Lua client sends JSON commands directly to the server. The browser script interprets these commands, e.g.

{ command: 'pane', type: 'image', content: { src: 'data:image/png;base64,....' }, title: 'lena' }

History

Originally forked from gfx.js.

The initial goal was to remain compatible with the torch/python API of gfx.js, but remove the term.js/tty.js/pty.js stuff which is served just fine by ssh.

Compared to gfx.js:

  • no terminal windows (no term.js)
  • dygraphs instead of nvd3 (have built in zoom and are perfect for time-series plots)
  • plots resize when windows are resized
  • images support zoom and pan
  • image lists are rendered as one image to speed up loading
  • windows remember their positions
  • implementation not relying on the filesystem, supports remote clients (sources)
Comments
  • display not working on localhost -- only showing json info, but not image

    display not working on localhost -- only showing json info, but not image

    In following the tutorial on the README, when the display function runs, but only is outputting the json: data: {"content":{"file":[[0,-1.2920932073243],[1.1111111111111,-1.0734784297415],[2.2222222222222,0.27885892990331],[3.3333333333333,-0.12175768971955],[4.4444444444444,0.16801618442082],[5.5555555555556,0.011533429745621],[6.6666666666667,0.83489397687172],[7.7777777777778,0.031249044780407],[8.8888888888889,0.23550898868986],[10,-0.6444625754049]]},"command":"pane","id":"pane_145713209407532286166836","type":"plot"}

    Is there something that's happening, and something I can do to resolve this? This same issue is also happening when running MazeBase from Facebook -- it shows the json, and not the actual images.

    opened by suryabhupa 25
  • dependency on nodejs

    dependency on nodejs

    Hi,

    as you saw, we are thinking of extending "display" a bit more, pimping it out with essential features, and making it a much more polished display backend that we can recommend to everyone in the Torch ecosystem.

    On that note, gfx.js, itorch and display suffer from having nodejs/ipython dependency, with quite a few users having trouble installing these.

    What are your views on making the json forwarder purely as a lua server?

    opened by soumith 16
  • Installation fails for Torch

    Installation fails for Torch

    Hello, I just started using Torch and was looking into using the display package. But everytime I try to install it, I get the following error:

    
    ttp_parser.c: In function ‘http_parser_parse_url’:
    http_parser.c:2093:18: error: this statement may fall through [-Werror=implicit-fallthrough=]
             found_at = 1;
             ~~~~~~~~~^~~
    http_parser.c:2096:7: note: here
           case s_req_server:
           ^~~~
    cc1: all warnings being treated as errors
    make[2]: *** [Makefile:35: http_parser.o] Error 1
    make[2]: Leaving directory '/tmp/luarocks_async-scm-1-4227/async/lhttp_parser/http-parser'
    make[1]: *** [Makefile:17: http-parser/http_parser.o] Error 2
    make[1]: Leaving directory '/tmp/luarocks_async-scm-1-4227/async/lhttp_parser'
    make: *** [Makefile:6: lhttp_parser/lhttp_parser.so] Error 2
    
    Error: Failed installing dependency: https://raw.githubusercontent.com/torch/rocks/master/async-scm-1.rockspec - Build error: Failed building.
    

    I am on Arch Linux 64-bit, kernel version 4.13.5-1

    I couldn't exactly find anything related to the error on the internet. Thanks in advance!

    opened by Morpheus3000 7
  • Long-running use of 'display' exhausts disk space in /tmp and kills process due to not removing temporary files

    Long-running use of 'display' exhausts disk space in /tmp and kills process due to not removing temporary files

    I was running dcgan.torch for 4 days and it had finally begun to learn to generate images which looked a little like the originals, and I was very excited. Then it crashed because it tried to write a file to /tmp and it had run out of space, due to thousands upon thousands of files named '/tmp/lua*', which turned out to be the visualizations in the local server hanging around for some reason. (I had several gigabytes of free space on / originally, but not enough, apparently.) After looking through dcgan.torch's source and seeing nothing that looked like it would be responsible for littering /tmp/ like that, I checked 'display' and it seems that this is deliberate:

    function M.image(img, opts)
      -- options:
      opts = opts or {}
      local win = opts.win or uid()      -- id of the window to be reused
    ...
      img = normalize(img, opts)
    
      -- I wish we could write to memory instead of on-disk file.
      local filename = os.tmpname() .. '.png'
      image.save(filename, img)
    
      local file = io.open(filename, 'rb')
      local imgdata = 'data:image/png;base64,' .. mime.b64(file:read('*all'))
      file:close()
    
      send({ command='image', id=win, src=imgdata, labels=opts._labels, width=opts.width, title=opts.title })
      return win
    end
    

    That is, each image displayed gets copied to /tmp/ along with another empty file and they never get deleted.

    I infer that the idea is that the HTML source code sent to the browser by the local server is including a image href of '/tmp/luaxxx.png' and this is what the browser is following and displaying, and the mention of asynchronous suggests that the rationale for leaving all images written to disk & taking up space in /tmp/ is that they cannot be removed immediately lest there be a race condition with the browser trying to read the image from disk to display it.

    I'm not a Lua programmer so I don't know what the fix here is, but perhaps the images could be sent inline in base64 encoding as a string so they need never be written in the first place? Or perhaps the call could be made synchronous and the display function clean up after itself once the browser finishes?

    In the mean time, a hack to work around it is to simply delete all reasonably old 'lua'-named intermediate files in a while loop:

    while true; do find /tmp/ -maxdepth 1 -type f -cmin +10 -name "lua*" -delete; sleep 10m; done
    
    opened by gwern 5
  • Error on display

    Error on display

    When I run and load the page all I see is a grey screen with the word Error in a small red rectangle in the upper right.

    The console displays this every time I run and then refresh the http://localhost:8000/ page:

    $ ~/.display/run.js &
    [1] 38756
    $ Listening on http://undefined:8000
    RangeError: msecs must be a non-negative finite number
    at Object.exports.enroll (timers.js:160:11)
    at Socket.setTimeout (net.js:329:12)
    at /Users/adamlevy/.display/server.js:14:16
    at Layer.handle [as handle_request] (/Users/adamlevy/.display/node_modules/express/lib/router/layer.js:82:5)
    at next (/Users/adamlevy/.display/node_modules/express/lib/router/route.js:110:13)
    at Route.dispatch (/Users/adamlevy/.display/node_modules/express/lib/router/route.js:91:3)
    at Layer.handle [as handle_request] (/Users/adamlevy/.display/node_modules/express/lib/router/layer.js:82:5)
    at /Users/adamlevy/.display/node_modules/express/lib/router/index.js:267:22
    at Function.proto.process_params (/Users/adamlevy/.display/node_modules/express/lib/router/index.js:321:12)
    at next (/Users/adamlevy/.display/node_modules/express/lib/router/index.js:261:10)
    
    opened by AdamSLevy 4
  • Can I redraw the last 10 windows?

    Can I redraw the last 10 windows?

    gfx had this ability to redraw windows you lost (let's say you closed the browser). gfx.redraw(10) redrew the last 10 windows.

    Does display still retain that functionality?

    opened by soumith 3
  • display.image Images no Longer Zoom

    display.image Images no Longer Zoom

    In the zoom function, self.width and self.height (in panes.js lines 451 and 452) are always 0 (so the image disappears when a user tries to zoom by scrolling). It doesn't seem to be due to an update in the display utility, but I'm wondering if anyone else has experienced this issue and has tracked down a root cause.

    opened by mathewhall 2
  • throwing error on display.image

    throwing error on display.image

    Hi, I'm running on an EC2 instance and trying to get a basic remote desktop going to view images. Following the readme, and in other attempts (such as displaying a basic jpg), I get the following error:

    .../ubuntu/torch/install/share/lua/5.1/display/init.lua:71: attempt to index local 'img' (a nil value)
    stack traceback:
    	.../ubuntu/torch/install/share/lua/5.1/display/init.lua:71: in function 'image'
    	stdin:1: in main chunk
    	[C]: ?
    
    

    I see in the file in question that the image module is marked 'broken.' Is there a workaround? I'm able to send plots and text just fine.

    Thanks,

    • michael
    opened by michaelByrne 2
  • doesn't seem to work within a non root path eg. http://example.com:8889/display/

    doesn't seem to work within a non root path eg. http://example.com:8889/display/

    I want to put display behind a proxy which serves other sites, so I can't use / as path for display, however it seems we can only configure the hostname and port, not the root path.

    opened by zig 2
  • Command line args not getting through

    Command line args not getting through

    I'm running this on a headless server so I ran the command like:

    $ th -ldisplay.start 8888 0.0.0.0
    server listening on http://127.0.0.1:8000
    

    I spotted that in start.lua the first line is

    local arg = {...}
    

    Doesn't this wipe out the command line arguments? When I commented that line out it seems to work fine:

    $ th -ldisplay.start 8888 0.0.0.0
    server listening on http://0.0.0.0:8888
    
    opened by lopsided 2
  • Error of missing async dependency during installation

    Error of missing async dependency during installation

    Hi,

    I am unable to install this package due to the following error: "Error: Could not satisfy dependency: async >= 1.0"

    I tried installing trepl, but I received the following error: "Failed searching manifest: Failed fetching manifest for https://raw.githubusercontent.com/torch/rocks/master - Failed downloading https://raw.githubusercontent.com/torch/rocks/master/manifest"

    Can anyone please help me with this?

    Thanks!

    opened by vasavig 1
  • Problem install

    Problem install

    luarocks install https://raw.githubusercontent.com/szym/display/master/display-scm-0.rockspec

    tle.pic.o -lm -ldl -lrt -Wl,-soname,libuv.so.0.10
    make[3]: se sale del directorio '/tmp/luarocks_async-scm-1-2406/async/luv/libuv'
    make[2]: se sale del directorio '/tmp/luarocks_async-scm-1-2406/async/luv/libuv'
    cc -shared -o luv.so temp/luv.o libuv/libuv.a temp/common.o -lm -lrt
    ar cr luv.a temp/luv.o temp/common.o
    /usr/bin/ld: temp/common.o:(.bss+0x0): definiciones múltiples de `luv_main_thread'; temp/luv.o:(.bss+0x0): primero se definió aquí
    ranlib luv.a
    collect2: error: ld devolvió el estado de salida 1
    make[1]: *** [Makefile:31: luv.so] Error 1
    make[1]: se sale del directorio '/tmp/luarocks_async-scm-1-2406/async/luv'
    make: *** [Makefile:9: luv/luv.so] Error 2
    
    
    
    opened by b4zz4 0
  • Is there a way to save graph out?

    Is there a way to save graph out?

    I plan to show the loss during training session. But it seems no download button on the plot window. Right click save as can only save one dot out from the grapha.

    Is there any alternative for this? image

    opened by ShuangjunLiu 0
  • How display an image in a fixed position?

    How display an image in a fixed position?

    Thanks for your sharing .when I use the display.image() I found the place where the images showed is not fixed ,I want display the images in particular positions to get a nice view ,but I don't know how to do .

    opened by ShowLang 0
  • Fix restoring default tensor type after call .image(...)

    Fix restoring default tensor type after call .image(...)

    Non-working example:

    torch.setdefaulttensortype('torch.FloatTensor')
    print(torch.getdefaulttensortype())
    display = require('display')
    display.image({torch.Tensor(1, 1, 1)})
    print(torch.getdefaulttensortype())
    

    Output:

    torch.FloatTensor
    torch.DoubleTensor
    

    After fix and correct output:

    torch.FloatTensor
    torch.FloatTensor
    
    opened by aropan 1
  • issues with python version for python2.7

    issues with python version for python2.7

    Hey, I've been using the lua version of this package and it's awesome, really great work! Now I'm trying out the python version, but am having this error:

    >>> import display
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/zhang/.local/lib/python2.7/site-packages/display/__init__.py", line 5, in <module>
        import urllib.request
    ImportError: No module named request
    

    Then I found out that here urllib.request is a package for python3 but I'm using python2.7, so I modified the following lines of __init__.py:

    # import urllib.request
    import urllib2
    
    # req = urllib.request.Request(URL, method='POST')
    req = urllib2.Request(URL)
    
    # resp = urllib.request.urlopen(req)
    resp = urllib2.urlopen(req)
    

    but then I get this error:

    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/me/.local/lib/python2.7/site-packages/display/__init__.py", line 76, in image
        'width': opts.get('width'),
      File "/home/me/.local/lib/python2.7/site-packages/display/__init__.py", line 38, in pane
        send(command='pane', type=panetype, id=win, title=title, content=content)
      File "/home/me/.local/lib/python2.7/site-packages/display/__init__.py", line 29, in send
        resp = urllib2.urlopen(req)
      File "/usr/lib/python2.7/urllib2.py", line 127, in urlopen
        return _opener.open(url, data, timeout)
      File "/usr/lib/python2.7/urllib2.py", line 404, in open
        response = self._open(req, data)
      File "/usr/lib/python2.7/urllib2.py", line 422, in _open
        '_open', req)
      File "/usr/lib/python2.7/urllib2.py", line 382, in _call_chain
        result = func(*args)
      File "/usr/lib/python2.7/urllib2.py", line 1214, in http_open
        return self.do_open(httplib.HTTPConnection, req)
      File "/usr/lib/python2.7/urllib2.py", line 1184, in do_open
        raise URLError(err)
    urllib2.URLError: <urlopen error [Errno 111] Connection refused>
    

    So I guess I cannot just simply replace urllib.request.Request(URL, method='POST') also just to urllib2.Request(URL), so can you give me some ideas as how to modify the code to work under python2.7?

    Thanks in advance!

    opened by jingweiz 2
Owner
Szymon Jakubczak
Szymon Jakubczak
DrawBot is a powerful, free application for macOS that invites you to write Python scripts to generate two-dimensional graphics

DrawBot is a powerful, free application for macOS that invites you to write Python scripts to generate two-dimensional graphics.

Frederik Berlaen 344 Jan 6, 2023
MyPaint is a simple drawing and painting program that works well with Wacom-style graphics tablets.

MyPaint A fast and dead-simple painting app for artists Features Infinite canvas Extremely configurable brushes Distraction-free fullscreen mode Exten

MyPaint 2.3k Jan 1, 2023
sK1 2.0 cross-platform vector graphics editor

sK1 2.0 sK1 2.0 is a cross-platform open source vector graphics editor similar to CorelDRAW, Adobe Illustrator, or Freehand. sK1 is oriented for prepr

sK1 Project 238 Dec 4, 2022
Turtle graphics || Python

turtle Turtle graphics || Python Rainbow (রংধনু) : Rainbow.using.Python.--.Python.Turtle.graphics.mp4 Human robot (মানব রোবট) : Draw.a.human.robot.usi

Jubair Ahmed Junjun 1 Oct 8, 2021
Python library for ascii graphics

Python library for ascii graphics

Anton 6 Oct 20, 2021
Python framework for creating and scaling up production of vector graphics assets.

Board Game Factory Contributors are welcome here! See the end of readme. This is a vector-graphics framework intended for creating and scaling up prod

Adam Volný 5 Jul 13, 2022
GIMP script to export bitmap as GRAPHICS 4 file (aka SCREEN 5)

gimpfu-msx-gr4.py GIMP script to export bitmap as GRAPHICS 4 file (aka SCREEN 5). GRAPHICS 4 specs are: 256x212 (or 256x192); 16 color palette (from 5

Pedro de Medeiros 4 Oct 17, 2022
Python script to generate vector graphics of an oriented lattice unit cell

unitcell Python script to generate vector graphics of an oriented lattice unit cell Examples unitcell --type hexagonal --eulers 12 23 34 --axes --crys

Philip Eisenlohr 2 Dec 10, 2021
Python binding to Skia Graphics Library

Skia python binding Python binding to Skia Graphics Library. Binding based on pybind11. Currently, the binding is under active development. Install Bi

Kota Yamaguchi 170 Jan 6, 2023
An agnostic Canvas API for the browser-less and insane

Apollo An agnostic Canvas API for the browser-less and mildly insane. Project Apollo is a Pythonic re-imagining of HTML Canvas element implementati

null 1 Jan 13, 2022
This Github Action automatically creates a GIF from a given web page to display on your project README

This Github Action automatically creates a GIF from a given web page to display on your project README

Pablo Lecolinet 28 Dec 15, 2022
This is an app that allows users to upload photos and display and store the photos in a file until the user deletes them.

Qt Photo App This is an app that allows users to upload photos and display and store the photos in a file until the user deletes them. Setup python3 -

Kathy Yang 5 Jan 22, 2022
Small wrapper around 3dmol.js and html2canvas for creating self-contained HTML files that display a 3D molecular representation.

Description Small wrapper around 3dmol.js and html2canvas for creating self-contained HTML files that display a 3D molecular representation. Double cl

David Meijer 1 Dec 2, 2021
Dynamic image server for web and print

Quru Image Server - dynamic imaging for web and print QIS is a high performance web server for creating and delivering dynamic images. It is ideal for

Quru 84 Jan 3, 2023
MaryJane is a simple MJPEG server written in Python.

MaryJane is a simple MJPEG server written in Python.

bootrino 152 Dec 13, 2022
A python based library to help you create unique generative images based on Rarity for your next NFT Project

Generative-NFT Generate Unique Images based on Rarity A python based library to help you create unique generative images based on Rarity for your next

Kartikay Bhutani 8 Sep 21, 2022
Computer art based on quadtrees.

Quads Computer art based on quadtrees. The program targets an input image. The input image is split into four quadrants. Each quadrant is assigned an

Michael Fogleman 1.1k Dec 23, 2022
The ctypes-based simple ImageMagick binding for Python

Wand Wand is a ctypes-based simple ImageMagick binding for Python, supporting 2.7, 3.3+, and PyPy. All functionalities of MagickWand API are implement

Eric McConville 1.2k Dec 30, 2022