Skip to content

haruishi43/equilib

Repository files navigation

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.

Transform Description
Cube2Equi / cube2equi cubemap → equirectangular
Equi2Cube / equi2cube equirectangular → cubemap
Equi2Equi / equi2equi equirectangular → equirectangular
Equi2Pers / equi2pers equirectangular → perspective
Pers2Equi / pers2equi perspective → 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

About

🌎→🗾Equirectangular (360/panoramic) image processing library for Python with minimal dependencies only using Numpy and PyTorch

Topics

Resources

License

Contributing

Stars

215 stars

Watchers

1 watching

Forks

Contributors

Languages