Files
kornia--kornia/benchmarks/geometry/pointcloud.py
T
wehub-resource-sync 3a2c66702c
Tests on CPU (scheduled) / check-skip (push) Has been cancelled
Tests on CPU (scheduled) / pre-tests (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-ubuntu (float32) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-ubuntu (float64) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float64, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float64, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float64, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float64, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.13, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.13, float64, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.11, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.11, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.12, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.12, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.13, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / coverage (push) Has been cancelled
Tests on CPU (scheduled) / typing (push) Has been cancelled
Tests on CPU (scheduled) / tutorials (push) Has been cancelled
Tests on CPU (scheduled) / docs (push) Has been cancelled
Lint / TOML Format (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:49:27 +08:00

283 lines
12 KiB
Python

# LICENSE HEADER MANAGED BY add-license-header
#
# Copyright 2018 Kornia Team
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Benchmark for kornia.geometry point cloud operations on CPU and CUDA.
Covers:
- transform_points
- project_points / unproject_points
- convert_points_to/from_homogeneous
- depth_to_3d, depth_to_3d_v2 (uncached vs cached grid)
- depth_to_normals
- warp_frame_depth
Usage:
python benchmarks/geometry/pointcloud.py # CPU only
python benchmarks/geometry/pointcloud.py --cuda # CPU + CUDA
python benchmarks/geometry/pointcloud.py --compile # include torch.compile variants
python benchmarks/geometry/pointcloud.py --cuda --compile
"""
from __future__ import annotations
import argparse
import datetime
import functools
import platform
import shutil
import subprocess
import time
import torch
from kornia.geometry.camera import project_points, unproject_points
from kornia.geometry.conversions import convert_points_from_homogeneous, convert_points_to_homogeneous
from kornia.geometry.depth import depth_to_3d, depth_to_3d_v2, depth_to_normals, unproject_meshgrid, warp_frame_depth
from kornia.geometry.linalg import transform_points
# ─────────────────────────────────────────────────────────────────────────────
# Helpers
# ─────────────────────────────────────────────────────────────────────────────
def _sync(device: str) -> None:
if device == "cuda":
torch.cuda.synchronize()
def bench(fn, *args, warmup: int = 5, reps: int = 20, device: str = "cpu", label: str = "") -> float:
"""Return mean wall-clock time in milliseconds."""
for _ in range(warmup):
fn(*args)
_sync(device)
t0 = time.perf_counter()
for _ in range(reps):
fn(*args)
_sync(device)
ms = (time.perf_counter() - t0) / reps * 1000
print(f" {label:<66s}: {ms:8.3f} ms")
return ms
def _print_env() -> None:
date = datetime.datetime.now(tz=datetime.UTC).strftime("%Y-%m-%d %H:%M:%S UTC")
git = shutil.which("git") or "git"
try:
commit = subprocess.check_output([git, "rev-parse", "--short", "HEAD"], text=True).strip()
except Exception:
commit = "unknown"
cpu = platform.processor() or platform.machine()
print(f" date : {date}")
print(f" commit : {commit}")
print(f" cpu : {cpu}")
if torch.cuda.is_available():
print(f" gpu : {torch.cuda.get_device_name(0)}")
# ─────────────────────────────────────────────────────────────────────────────
# transform_points
# ─────────────────────────────────────────────────────────────────────────────
def bench_transform_points(device: str, dtype: torch.dtype = torch.float32, compile_: bool = False) -> None:
print(f"\n--- transform_points device={device} dtype={dtype} ---")
fn = transform_points
fn_c = torch.compile(fn) if compile_ else None
configs = [
("B=1 N=1K single transform", 1, 1_000, True),
("B=8 N=10K single transform", 8, 10_000, True),
("B=32 N=100K single transform", 32, 100_000, True),
("B=1 N=1K per-sample transform", 1, 1_000, False),
("B=8 N=10K per-sample transform", 8, 10_000, False),
("B=32 N=100K per-sample transform", 32, 100_000, False),
]
for label, B, N, single_T in configs:
pts = torch.randn(B, N, 3, device=device, dtype=dtype)
T = torch.eye(4, device=device, dtype=dtype).unsqueeze(0)
if not single_T:
T = T.expand(B, -1, -1).contiguous()
bench(fn, T, pts, device=device, label=label)
if compile_:
bench(fn_c, T, pts, device=device, label=f"{label} (compiled)")
# ─────────────────────────────────────────────────────────────────────────────
# project / unproject
# ─────────────────────────────────────────────────────────────────────────────
def bench_project_unproject(device: str, dtype: torch.dtype = torch.float32, compile_: bool = False) -> None:
print(f"\n--- project_points / unproject_points device={device} dtype={dtype} ---")
K_base = torch.eye(3, device=device, dtype=dtype)
K_base[0, 0] = K_base[1, 1] = 500.0
K_base[0, 2] = K_base[1, 2] = 320.0
proj_fn = project_points
unproj_fn = unproject_points
if compile_:
proj_fn = torch.compile(proj_fn)
unproj_fn = torch.compile(unproj_fn)
configs = [
("B=1 N=1K ", 1, 1_000),
("B=8 N=10K ", 8, 10_000),
("B=32 N=100K", 32, 100_000),
]
for label, B, N in configs:
K_b = K_base.unsqueeze(0).expand(B, -1, -1)
pts3 = torch.rand(B, N, 3, device=device, dtype=dtype).add_(0.5)
pts2 = torch.rand(B, N, 2, device=device, dtype=dtype).mul_(640.0)
depth = torch.ones(B, N, 1, device=device, dtype=dtype)
bench(proj_fn, pts3, K_b, device=device, label=f"{label} project_points")
bench(unproj_fn, pts2, depth, K_b, device=device, label=f"{label} unproject_points")
# ─────────────────────────────────────────────────────────────────────────────
# homogeneous conversions
# ─────────────────────────────────────────────────────────────────────────────
def bench_homogeneous_conversions(device: str, dtype: torch.dtype = torch.float32) -> None:
print(f"\n--- homogeneous conversions device={device} dtype={dtype} ---")
for label, shape, fn in [
("N=1M 3-D → homogeneous", (1_000_000, 3), convert_points_to_homogeneous),
("N=1M 4-D → euclidean", (1_000_000, 4), convert_points_from_homogeneous),
("B=32 N=100K 3-D → homogeneous", (32, 100_000, 3), convert_points_to_homogeneous),
("B=32 N=100K 4-D → euclidean", (32, 100_000, 4), convert_points_from_homogeneous),
]:
x = torch.randn(*shape, device=device, dtype=dtype)
bench(fn, x, device=device, label=label)
# ─────────────────────────────────────────────────────────────────────────────
# depth_to_3d / depth_to_3d_v2 / depth_to_normals
# ─────────────────────────────────────────────────────────────────────────────
def bench_depth_functions(device: str, dtype: torch.dtype = torch.float32, compile_: bool = False) -> None:
print(f"\n--- depth_to_3d / depth_to_3d_v2 / depth_to_normals device={device} dtype={dtype} ---")
K_base = torch.eye(3, device=device, dtype=dtype)
K_base[0, 0] = K_base[1, 1] = 500.0
K_base[0, 2] = K_base[1, 2] = 320.0
configs = [
("B=1 H=64 W=64 ", 1, 64, 64),
("B=1 H=256 W=256 ", 1, 256, 256),
("B=4 H=480 W=640 ", 4, 480, 640),
("B=1 H=720 W=1280", 1, 720, 1280),
]
for label, B, H, W in configs:
K_b = K_base.unsqueeze(0).expand(B, -1, -1).contiguous()
depth4 = torch.rand(B, 1, H, W, device=device, dtype=dtype).add_(0.1)
depth3 = depth4.squeeze(1)
bench(depth_to_3d, depth4, K_b, device=device, label=f"{label} depth_to_3d")
bench(depth_to_3d_v2, depth3, K_b, device=device, label=f"{label} depth_to_3d_v2 (no cache)")
grid = unproject_meshgrid(H, W, K_b, device=device, dtype=dtype)
bench(
functools.partial(depth_to_3d_v2, xyz_grid=grid),
depth3,
K_b,
device=device,
label=f"{label} depth_to_3d_v2 (cached grid)",
)
bench(depth_to_normals, depth4, K_b, device=device, label=f"{label} depth_to_normals")
if compile_:
fn_c = torch.compile(depth_to_3d_v2)
bench(
functools.partial(fn_c, xyz_grid=grid),
depth3,
K_b,
device=device,
label=f"{label} depth_to_3d_v2 (cached + compiled)",
)
# ─────────────────────────────────────────────────────────────────────────────
# warp_frame_depth
# ─────────────────────────────────────────────────────────────────────────────
def bench_warp_frame_depth(device: str, dtype: torch.dtype = torch.float32) -> None:
print(f"\n--- warp_frame_depth device={device} dtype={dtype} ---")
K_base = torch.eye(3, device=device, dtype=dtype)
K_base[0, 0] = K_base[1, 1] = 500.0
K_base[0, 2] = K_base[1, 2] = 320.0
configs = [
("B=1 C=3 H=256 W=256 ", 1, 3, 256, 256),
("B=4 C=3 H=480 W=640 ", 4, 3, 480, 640),
("B=1 C=3 H=720 W=1280", 1, 3, 720, 1280),
]
for label, B, C, H, W in configs:
K_b = K_base.unsqueeze(0).expand(B, -1, -1).contiguous()
depth = torch.rand(B, 1, H, W, device=device, dtype=dtype).add_(0.1)
image = torch.rand(B, C, H, W, device=device, dtype=dtype)
T = torch.eye(4, device=device, dtype=dtype).unsqueeze(0).expand(B, -1, -1).contiguous()
bench(warp_frame_depth, image, depth, T, K_b, device=device, label=label)
# ─────────────────────────────────────────────────────────────────────────────
# Entry point
# ─────────────────────────────────────────────────────────────────────────────
def run_all(device: str, compile_: bool = False) -> None:
sep = "=" * 72
print(f"\n{sep}")
print(f" DEVICE : {device.upper()}")
print(f" compile: {compile_}")
print(sep)
bench_transform_points(device, compile_=compile_)
bench_project_unproject(device, compile_=compile_)
bench_homogeneous_conversions(device)
bench_depth_functions(device, compile_=compile_)
bench_warp_frame_depth(device)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("--cuda", action="store_true", help="also run on CUDA")
parser.add_argument("--compile", action="store_true", dest="compile_", help="include torch.compile variants")
args = parser.parse_args()
_print_env()
run_all("cpu", compile_=args.compile_)
if args.cuda:
if torch.cuda.is_available():
run_all("cuda", compile_=args.compile_)
else:
print("\nWarning: --cuda requested but CUDA is not available.")