Skip to main content

Render images in the terminal via a VQ-VAE codebook turned into a glyph font

Project description

TUI-Image

Show an image in your terminal as colored text, using a pretrained image tokenizer's codebook as a custom 4096-glyph font.

Left: original. Right: rendered in the terminal (using TokImg Mono font).

photo ui bear

The idea

  1. A pretrained VQ-VAE (LlamaGen, MIT licensed) has 16384 little 16×16 image patches. We keep the 4096 most shape-diverse as a custom font (TokImg Mono).
  2. Each glyph is just a binary shape; its two colors come from the image — so a shape and its color-swapped mirror are the same, and duplicates are dropped.
  3. To draw an image: split it into a grid of terminal cells — each cell is a tall rectangle (~1:2) and for each cell pick the glyph whose shape fits best, drawn (stretched to fill the cell) in that cell's two main colors.
  4. The grid of colored glyphs approximates the image.

The VQ-VAE is used only once, to build the font. Rendering is pure NumPy on CPU — tuimg never imports torch and needs no GPU/VRAM.

A handful of Unicode block/box characters (█ ▀ ▌ ▖ ▒ ─ ╱ …) are mixed into the alphabet too: the terminal draws those procedurally, so they render seam-free and need no font. They win a cell whenever they fit about as well as the best glyph, trading a seam away at ~no fidelity cost.


Install

The font is prebuilt and bundled, so no GPU is needed to use it. Install it as a tuimg command that's always on your PATH:

git clone https://github.com/volotat/tuimg 
cd tuimg                  
pip install .             # or:  pip install --user .
tuimg --install-font      # copies the bundled font to ~/.fonts (+ refreshes cache)
tuimg path/to/image.jpg

Or run from the cloned repo without installing:

pip install numpy Pillow                                        # only runtime deps
cp tuimg/assets/tokimg.ttf ~/.fonts/ && fc-cache -f             # install the font
python -m tuimg samples/photo.jpg

Rebuilding the font from the model is optional and the only step that needs a GPU (~3 min): pip install ".[build]" then make assets.

Why you don't need to select the font: our glyphs live in plane-16 PUA (U+100000+), a Unicode range no normal font defines. Your terminal can't find them in your current font, so it automatically falls back to TokImg just for those characters — your own font keeps drawing all your normal text. Installing the font is enough.

If the tiles don't appear (a few terminals don't fall back for these codepoints): either set your terminal font to "TokImg Mono" (it includes normal ASCII, so your shell still looks normal — fc-match "TokImg Mono" should print tokimg.ttf), or skip the font entirely with --mode block / --save *.png. Tip: install to ~/.fonts, which fontconfig scans even when XDG_DATA_HOME is redirected by a snap/flatpak sandbox.

Use

python -m tuimg samples/photo.jpg                      # color (default)
python -m tuimg samples/photo.jpg --save pic.png       # save the stitched picture
python -m tuimg samples/photo.jpg --no-color --save pic.txt  # plain text, opens anywhere
python -m tuimg samples/ui.png   --mode block          # colored blocks, NO font needed
python -m tuimg --demo                                 # render all sample images

--save FILE saves the perfectly-stitched picture when FILE ends in .png/.jpg (no font needed), otherwise the rendered text. --no-color drops all ANSI color codes and renders a 1-bit brightness halftone of plain glyph characters — clean to open in any editor (it suits a light/white background by default; add --invert for a dark terminal, and --mono-threshold T to shift the brightness pivot — raise it for more ink in a bright image, lower it for a dark one). --blocks matches using only the Unicode block/box characters (no codebook font) — a baseline to see how much the font actually adds. Other flags: --width N, --cell-aspect R (if the image looks stretched; auto-detected when possible), --debug. Run python -m tuimg -h for all.

Terminal rendering limitations (and why they can't be "fixed" here)

What you actually see in the terminal is not pixel-perfect, and this is a limit of the medium, not a bug in this project:

how it really looks in the terminal

A terminal lays out discrete text glyphs, one per cell — it was never built to tile glyphs into a seamless image. Two consequences are unavoidable from inside the application:

  • Hairline seams between cells. A cell's size in device pixels is almost always fractional, so the terminal snaps each cell to an integer pixel boundary and rasterises every glyph independently. Edges that should meet don't share pixels, leaving ~1-px seams. We mitigate this with glyph "ink bleed" (overfilling right/bottom) and by preferring seam-free block characters, but it can't be eliminated — the rounding happens in the terminal, below us.
  • No antialiasing control. Whether glyph edges are soft or hard is the terminal/font-renderer's decision.

Why can't we get the flawless alignment that box-drawing (─ │ █ ▀) has? Because terminals special-case those ranges and draw them procedurally, filling the exact cell rectangle and bypassing the font. Arbitrary custom shapes — the whole premise of a glyph alphabet — go through normal font rasterisation, where the snapping is inherent. You can have a rich shaped alphabet or box-drawing-grade alignment, not both. For a pixel-perfect result, export the picture with --save out.png (rasterised seam-free) or use --mode block.

Within those constraints, this project is about as good as terminal image printing gets with a predefined character set: instead of a fixed handful of ASCII/box glyphs, it builds a 4096-glyph alphabet straight from a learned image tokenizer, selects it to match real-image statistics, mixes in the seam-free block characters, and fits each cell's two colors to the block — squeezing the most detail the two-colors-per-cell ceiling allows.

License

MIT. The vendored model code (tuimg/vendor/llamagen_vq.py) and the downloaded weights are from LlamaGen, also MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tuimg-0.1.1.tar.gz (491.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tuimg-0.1.1-py3-none-any.whl (507.3 kB view details)

Uploaded Python 3

File details

Details for the file tuimg-0.1.1.tar.gz.

File metadata

  • Download URL: tuimg-0.1.1.tar.gz
  • Upload date:
  • Size: 491.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tuimg-0.1.1.tar.gz
Algorithm Hash digest
SHA256 31a11dd254f7646a7b9c3728937c891a9c0f9dbb02b406340c57c3a95dcc4cbc
MD5 0a28cf540b6f177de220996939a78106
BLAKE2b-256 35b270bd7809409726bdeb17be74d30bc8bbd0d04db8c74b55047d9f99d54543

See more details on using hashes here.

Provenance

The following attestation bundles were made for tuimg-0.1.1.tar.gz:

Publisher: release.yml on volotat/tuimg

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tuimg-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: tuimg-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 507.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tuimg-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6b6d36d4894f70e6355af14fcd01c00fcab33733d69acdac254dabc60e6be8f4
MD5 ab8b44117813e9f6df6059e3fe50b2d6
BLAKE2b-256 296c66bd51e8b17e12a054a2194de669b16fe7c7467bdb350e16b1c19e26b760

See more details on using hashes here.

Provenance

The following attestation bundles were made for tuimg-0.1.1-py3-none-any.whl:

Publisher: release.yml on volotat/tuimg

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page