diff --git a/py360convert/__main__.py b/py360convert/__main__.py index 603b379..c1b609f 100644 --- a/py360convert/__main__.py +++ b/py360convert/__main__.py @@ -1,11 +1,13 @@ import argparse import sys from pathlib import Path +from typing import get_args import numpy as np from PIL import Image import py360convert +from py360convert.utils import InterpolationMode def _assert_height_width(args): @@ -46,7 +48,7 @@ def main(): c2e_parser.add_argument("--height", "-h", type=int, required=True, help="Output image height in pixels.") c2e_parser.add_argument("--width", "-w", type=int, required=True, help="Output image width in pixels.") c2e_parser.add_argument( - "--mode", "-m", default="bilinear", choices=["bilinear", "nearest"], help="Resampling method." + "--mode", "-m", default="bilinear", choices=get_args(InterpolationMode), help="Resampling method." ) e2c_parser = subparsers.add_parser("e2c", help="Convert equirectangular to cubemap.", add_help=False) @@ -58,7 +60,7 @@ def main(): e2c_parser.add_argument("--width", "-w", type=int, help="Output image width in pixels.") e2c_parser.add_argument("--size", "-s", type=int, help="Side length of each cube face. Overrides height/width.") e2c_parser.add_argument( - "--mode", "-m", default="bilinear", choices=["bilinear", "nearest"], help="Resampling method." + "--mode", "-m", default="bilinear", choices=get_args(InterpolationMode), help="Resampling method." ) e2p_parser = subparsers.add_parser("e2p", help="Convert equirectangular to perspective.", add_help=False) @@ -88,7 +90,7 @@ def main(): help="Roll camera degrees. Positive values rotate counterclockwise; negative values rotate clockwise.", ) e2p_parser.add_argument( - "--mode", "-m", default="bilinear", choices=["bilinear", "nearest"], help="Resampling method." + "--mode", "-m", default="bilinear", choices=get_args(InterpolationMode), help="Resampling method." ) args = parser.parse_args() diff --git a/py360convert/c2e.py b/py360convert/c2e.py index 2eccfca..7c1ab16 100644 --- a/py360convert/c2e.py +++ b/py360convert/c2e.py @@ -12,6 +12,7 @@ cube_list2h, equirect_facetype, equirect_uvgrid, + mode_to_order, sample_cubefaces, ) @@ -72,12 +73,7 @@ def c2e( np.ndarray Equirectangular image. """ - if mode == "bilinear": - order = 1 - elif mode == "nearest": - order = 0 - else: - raise ValueError(f'Unknown mode "{mode}".') + order = mode_to_order(mode) if cube_format == "horizon": if not isinstance(cubemap, np.ndarray): diff --git a/py360convert/e2c.py b/py360convert/e2c.py index 1473753..d291b39 100644 --- a/py360convert/e2c.py +++ b/py360convert/e2c.py @@ -10,6 +10,7 @@ cube_h2dice, cube_h2dict, cube_h2list, + mode_to_order, sample_equirec, uv2coor, xyz2uv, @@ -77,12 +78,7 @@ def e2c( squeeze = False h, w = e_img.shape[:2] - if mode == "bilinear": - order = 1 - elif mode == "nearest": - order = 0 - else: - raise ValueError(f'Unknown mode: "{mode}".') + order = mode_to_order(mode) xyz = xyzcube(face_w) uv = xyz2uv(xyz) diff --git a/py360convert/e2p.py b/py360convert/e2p.py index 0eec00b..d619129 100644 --- a/py360convert/e2p.py +++ b/py360convert/e2p.py @@ -7,6 +7,7 @@ from .utils import ( DType, InterpolationMode, + mode_to_order, sample_equirec, uv2coor, xyz2uv, @@ -64,12 +65,7 @@ def e2p( in_rot = np.deg2rad(in_rot_deg) - if mode == "bilinear": - order = 1 - elif mode == "nearest": - order = 0 - else: - raise ValueError(f'Unknown mode: "{mode}".') + order = mode_to_order(mode) u = -u_deg * np.pi / 180 v = v_deg * np.pi / 180 diff --git a/py360convert/utils.py b/py360convert/utils.py index a0e79dc..e28fd77 100644 --- a/py360convert/utils.py +++ b/py360convert/utils.py @@ -5,11 +5,57 @@ from numpy.typing import NDArray from scipy.ndimage import map_coordinates +_mode_to_order = { + "nearest": 0, + "linear": 1, + "bilinear": 1, + "biquadratic": 2, + "quadratic": 2, + "quad": 2, + "bicubic": 3, + "cubic": 3, + "biquartic": 4, + "quartic": 4, + "biquintic": 5, + "quintic": 5, +} + CubeFormat = Literal["horizon", "list", "dict", "dice"] -InterpolationMode = Literal["bilinear", "nearest"] +InterpolationMode = Literal[ + "nearest", + "linear", + "bilinear", + "biquadratic", + "quadratic", + "quad", + "bicubic", + "cubic", + "biquartic", + "quartic", + "biquintic", + "quintic", +] DType = TypeVar("DType", bound=np.generic, covariant=True) +def mode_to_order(mode: InterpolationMode) -> int: + """Convert a human-friendly interpolation string to integer equivalent. + + Parameters + ---------- + mode: str + Human-friendly interpolation string. + + Returns + ------- + The order of the spline interpolation + """ + try: + return _mode_to_order[mode.lower()] + except KeyError: + raise ValueError(f'Unknown mode "{mode}".') from None + + def xyzcube(face_w: int) -> NDArray[np.float32]: """ Return the xyz coordinates of the unit cube in [F R B L U D] format.