README.md

June 21, 2026 · View on GitHub

equilib

Processing Equirectangular Images with Python

PyPI version Python versions CI GitHub license Documentation
equilib

equilib is a library for processing equirectangular (360°) images in Python.

  • Pure Python, with numpy and torch as the only runtime dependencies.
  • Runs on CPU and CUDA tensors, with batched and mixed-precision processing.
  • Automatic input-type detection (numpy.ndarray or torch.Tensor).
  • Extras such as rotation-matrix creation and a customizable grid sampler.
  • Highly modular and extensible.

📖 Full documentation: https://haruishi43.github.io/equilib/

Installation

Prerequisites:

  • Python >=3.9
  • PyTorch >=2.8
pip install pyequilib

Transforms

equilib provides transforms between equirectangular, cubemap, and perspective images. Each transform ships both a class API and a func API.

TransformDescription
Cube2Equi / cube2equicubemap → equirectangular
Equi2Cube / equi2cubeequirectangular → cubemap
Equi2Equi / equi2equiequirectangular → equirectangular
Equi2Pers / equi2persequirectangular → perspective
Pers2Equi / pers2equiperspective → equirectangular

The class API instantiates a reusable object configured once; the func API takes the configuration on every call. The class API calls the func API internally, so there is no behavioral difference — both are extensible.

Inputs are channel-first (BxCxHxW or CxHxW); the output type matches the input. Common arguments shared across transforms:

  • rots: rotation as three angles pitch, yaw, roll in radians.
  • z_down (bool): use a z-axis-down coordinate system. Default False.
  • mode (str): interpolation mode. Default "bilinear".
  • clip_output (bool): clip values to the input range. Default True.

Basic usage

Example with Equi2Pers / equi2pers.

class API

import numpy as np
from PIL import Image
from equilib import Equi2Pers

# Input equirectangular image (channel-first: HWC -> CHW)
equi_img = np.asarray(Image.open("./some_image.jpg"))
equi_img = np.transpose(equi_img, (2, 0, 1))

rots = {
    "roll": 0.0,
    "pitch": np.pi / 4,  # rotate vertical
    "yaw": np.pi / 4,    # rotate horizontal
}

equi2pers = Equi2Pers(height=480, width=640, fov_x=90.0, mode="bilinear")
pers_img = equi2pers(equi=equi_img, rots=rots)

func API

import numpy as np
from PIL import Image
from equilib import equi2pers

equi_img = np.asarray(Image.open("./some_image.jpg"))
equi_img = np.transpose(equi_img, (2, 0, 1))

rots = {"roll": 0.0, "pitch": np.pi / 4, "yaw": np.pi / 4}

pers_img = equi2pers(
    equi=equi_img,
    rots=rots,
    height=480,
    width=640,
    fov_x=90.0,
    mode="bilinear",
)

See the documentation for every transform's arguments, or browse the examples under tests, benchmarks, and scripts.

Coordinate system

A right-handed XYZ global coordinate system: x-axis faces forward and z-axis faces up.

  • roll: counter-clockwise rotation about the x-axis
  • pitch: counter-clockwise rotation about the y-axis
  • yaw: counter-clockwise rotation about the z-axis

Pass z_down=True to flip the system so the z-axis faces down. See more in the coordinate system docs.

Grid sampling

To process equirectangular images quickly, equilib relies on grid sampling and implements its own numpy and torch backends to minimize dependencies and exploit cuda and batching:

  • The torch backend uses the built-in torch.nn.functional.grid_sample by default, with a customizable pure-torch implementation also available.
  • The numpy backend uses a pure-numpy implementation that is faster than scipy and more robust than cv2.remap. You can override it with scipy or cv2 via the override_func argument.

See the grid sampling docs and the benchmark scripts in benchmarks/.

Development

This project uses uv and Ruff. Image/video assets are stored with Git LFS (git lfs install once before cloning).

git clone https://github.com/haruishi43/equilib.git
cd equilib
uv sync --group dev      # create the venv and install package + dev tools

uv run pytest tests      # run tests
uv run ruff check .      # lint
uv run ruff format .     # format

Pull requests and issues are welcome. See CONTRIBUTING.md for the full workflow, including how releases are published.

Roadmap

  • Cache the rotation-invariant grid prep in the class API (all rotation-based transforms; bitwise-identical to the func path). Saves ~6–17% per call on the torch path and up to ~20% for equi2equi; negligible for the numpy equi2pers/equi2cube where the sampler dominates. See benchmarks/cache_benchmark.py.
  • Optional full-grid cache for the fixed-rotation (static camera) case
  • Speed up the pure-numpy grid_sample (the dominant cost on the numpy path, ~100 ms/call)
  • Try torch.compile on grid construction instead of a hand-written c++/cuda kernel
  • More accurate intrinsic matrix using vertical FOV for equi2pers
  • Multiprocessing support (slow on torch.distributed)
  • Compute the torch grid on-device to avoid the per-call CPU→GPU round-trip
  • Resolve the equi2cube z-axis orientation uncertainty and add coverage for the untested paths
  • Add GPU/CUDA coverage to CI (currently CPU-only due to costs)

Citation

If this project was helpful to your work, please cite it:

@software{pyequilib2021github,
  author = {Haruya Ishikawa},
  title = {PyEquilib: Processing Equirectangular Images with Python},
  url = {https://github.com/haruishi43/equilib},
  version = {0.6.0},
  year = {2021},
}

Acknowledgements