This project is an early work in progress, NOT READY FOR WIDE SHARING yet.
Very lightweight and cross-platform image viewer, inspired by the good old
xv. The project was born after I found myself still
trying to build the 1999 xv shareware in 2021 as none of the more recent
alternatives were as efficient.
The computer vision community is the main target audience, and zv has unique
features to navigate large collection of images (e.g. machine learning datasets
or results) and easily compare multiple images with synchronized zooms and
pointers to inspect pixel-level differences.
It also has a standalone C and Python API to be used as an alternative to OpenCV
imshow.
Goals:
-
Be the default tool for computer vision practitioners to quickly inspect images.
-
Small, statically-linked desktop binary that can be easily distributed.
-
Linux, macOS and Windows support.
-
Lightweight and fast to load, lazy loading of images so it can open thousands of them.
-
Easily compare multiple images at the pixel level, e.g to inspect the output of image processing algorithms.
-
Support only a small set of the most useful manipulation routines and annotations.
-
xv-like keyboard shortcuts for the main commands.
-
Python-API and standalone C-API to also use it as an in-app image viewer / logger.
-
Client-server mode to visualize images on a remote server (e.g. machine learning server).
Non-goals:
-
Become a photo viewer app with library management, etc.
-
Become a fully-featured image manipulation program (GIMP).
-
Become a fully-featured scientific image viewer (ImageJ, napari)
Grid Layout (2x2) to visualize 4 images at a time, with synchronized zoom and multiple cursors.

-
Pre-pre-alpha. I use it on a daily basis, but it has lots of rough edges and nothing is stabilized yet. The code is still prototype quality.
-
Only tested on Linux and macOS so far, but it should be straightforward to build on Windows later on as all the dependencies are cross-platform.
Single-command install for the latest release:
curl -fsSL https://raw.githubusercontent.com/nburrus/zv/main/scripts/install.sh | bashInstall a specific version:
curl -fsSL https://raw.githubusercontent.com/nburrus/zv/main/scripts/install.sh | bash -s -- --version v0.1.1By default, the installer places zv and zv-client in ~/.local/bin. Override that with:
curl -fsSL https://raw.githubusercontent.com/nburrus/zv/main/scripts/install.sh | bash -s -- --install-dir ~/binCurrently supported installer targets:
- macOS arm64
- Linux x86_64
Manual install is also straightforward: download the matching release tarball, extract it, and copy zv and zv-client into a directory on your PATH, for example ~/.local/bin.
On Linux, zv is distributed as a single binary, but it still relies on system graphics and windowing libraries such as OpenGL and X11.
To uninstall:
rm -f ~/.local/bin/zv
rm -f ~/.local/bin/zv-clientCreating a zv viewer directly from Python.
import zv
import numpy as np
app = zv.App()
app.initialize()
viewer = app.getViewer()
blue_im = np.zeros((256,256,4), dtype=np.uint8)
blue_im[:,:,3] = 255
viewer.addImage ("All Blue", blue_im)
viewer.addImageFromFile ("myimage.png")
viewer.setLayout(1,2) # one row, two columns
while app.numViewers > 0:
app.updateOnce(1.0 / 30.0)
import numpy as np
from zv.log import zvlog
zvlog.start () # will create an instance as a subprocess
# Alternative: connect to an existing server.
# zvlog.start (('localhost', 4207))
zvlog.image("random1", np.random.default_rng().random(size=(256,256,3), dtype=np.float32))
zvlog.waitUntilWindowsAreClosed()
A similar logging API is available in C/C++, without external dependencies, but the zv binary needs to be in the PATH.
If you want to browse images that live on a remote machine while keeping the GUI on your local machine, use:
zv-clienton the remote machine- either
zvorzv-proxyon the local machine ssh -Rto expose the local proxy port back to the remote machine
If a single forwarded session is enough, you can connect the remote client directly to a local zv server.
In the common case, the defaults already line up:
zvlistens on127.0.0.1:4207zv-clientconnects to127.0.0.1:4207
So in practice you usually only need:
- On your local machine:
zv- From your local machine:
ssh -R 4207:127.0.0.1:4207 user@remote-host- On the remote machine:
zv-client /path/to/image1.png /path/to/image2.jpgIn this setup:
zv-clientconnects to127.0.0.1:4207on the remote machinessh -Rforwards that connection to127.0.0.1:4207on your local machine- the local
zvprocess receives the images and displays them - if you need to customize this,
zvexposes--interface,--port, and--require-server, andzv-clientexposes--hostand--port
If you want each remote client connection to create its own local zv instance automatically, insert zv-proxy between the SSH tunnel and zv.
Again, the defaults are set up so the minimal version is usually enough:
- On your local machine:
uv run python/zv-proxy.py- Reuse the same
ssh -Randzv-clientsteps as above.
End-to-end flow:
zv-clientconnects to127.0.0.1:4207on the remote machinessh -Rforwards that connection to127.0.0.1:4207on your local machinezv-proxyaccepts the connection, starts a localzv -p <ephemeral-port> --require-server, and proxies the traffic- the images are then displayed by a local
zvwindow on your machine - if you need to customize this,
zv-proxyexposes--listen-host,--listen-port,--zv-host, and--zv-cmd, whilezv-clientstill exposes--hostand--port
Notes:
zv-proxylives atpython/zv-proxy.pyin this repository and currently expectsuv run.- If
zvis not on your localPATH, replace--zv-cmd "zv"with the full path to the binary. - The proxy handles multiple client connections and spawns one separate local
zvprocess per connection.
Standard cmake. No external dependency should be required as everything is included in the repo. Example command line:
mkdir build && cd build && cmake .. && make
There are no external dependencies to install as they are all snapshotted in the repository. But here is the list to give credits:
- GLFW: desktop windows and GL context creation.
- Dear ImGui: immediate mode GUI.
- stb_image: image loading and saving.
- Clip: copy/paste images from clipboard.
- cppuserprefs: storage of user preferences.
- gl3w: tiny OpenGL loader.
- ImGuiFileDialog: pure ImGui file dialogs.
- nanobind: generate Python bindings.
A lot of the visualization code was adapted from DaltonLens.