|
|
import pytest |
|
|
import torch |
|
|
from torch.autograd import gradcheck |
|
|
|
|
|
import kornia |
|
|
from kornia.testing import assert_close, tensor_to_gradcheck_var |
|
|
|
|
|
|
|
|
class TestCam2Pixel: |
|
|
def _create_intrinsics(self, batch_size, fx, fy, cx, cy, device, dtype): |
|
|
temp = torch.eye(4, device=device, dtype=dtype) |
|
|
temp[0, 0], temp[0, 2] = fx, cx |
|
|
temp[1, 1], temp[1, 2] = fy, cy |
|
|
intrinsics = temp.expand(batch_size, -1, -1) |
|
|
return intrinsics |
|
|
|
|
|
def _create_intrinsics_inv(self, batch_size, fx, fy, cx, cy, device, dtype): |
|
|
temp = torch.eye(4, device=device, dtype=dtype) |
|
|
temp[0, 0], temp[0, 2] = 1 / fx, -cx / fx |
|
|
temp[1, 1], temp[1, 2] = 1 / fy, -cy / fy |
|
|
intrinsics_inv = temp.expand(batch_size, -1, -1) |
|
|
return intrinsics_inv |
|
|
|
|
|
def _get_samples(self, shape, low, high, device, dtype): |
|
|
"""Return a tensor having the given shape and whose values are in the range [low, high)""" |
|
|
return ((high - low) * torch.rand(shape, device=device, dtype=dtype)) + low |
|
|
|
|
|
@pytest.mark.parametrize("batch_size", (1,)) |
|
|
def test_smoke(self, batch_size, device, dtype): |
|
|
H, W = 250, 500 |
|
|
fx, fy = W, H |
|
|
cx, cy = W / 2, H / 2 |
|
|
eps = 1e-12 |
|
|
seed = 77 |
|
|
low, high = -500, 500 |
|
|
|
|
|
intrinsics = self._create_intrinsics(batch_size, fx, fy, cx, cy, device=device, dtype=dtype) |
|
|
|
|
|
|
|
|
|
|
|
proj_mat = intrinsics |
|
|
|
|
|
torch.manual_seed(seed) |
|
|
cam_coords_src = self._get_samples((batch_size, H, W, 3), low, high, device, dtype) |
|
|
|
|
|
pixel_coords_dst = kornia.geometry.camera.cam2pixel( |
|
|
cam_coords_src=cam_coords_src, dst_proj_src=proj_mat, eps=eps |
|
|
) |
|
|
assert pixel_coords_dst.shape == (batch_size, H, W, 2) |
|
|
|
|
|
@pytest.mark.parametrize("batch_size", (1, 2, 5)) |
|
|
def test_consistency(self, batch_size, device, dtype): |
|
|
H, W = 250, 500 |
|
|
fx, fy = W, H |
|
|
cx, cy = W / 2, H / 2 |
|
|
eps = 1e-12 |
|
|
seed = 77 |
|
|
low, high = -500, 500 |
|
|
|
|
|
intrinsics = self._create_intrinsics(batch_size, fx, fy, cx, cy, device=device, dtype=dtype) |
|
|
intrinsics_inv = self._create_intrinsics_inv(batch_size, fx, fy, cx, cy, device=device, dtype=dtype) |
|
|
|
|
|
|
|
|
|
|
|
proj_mat = intrinsics |
|
|
|
|
|
torch.manual_seed(seed) |
|
|
cam_coords_input = self._get_samples((batch_size, H, W, 3), low, high, device, dtype) |
|
|
|
|
|
pixel_coords_output = kornia.geometry.camera.cam2pixel( |
|
|
cam_coords_src=cam_coords_input, dst_proj_src=proj_mat, eps=eps |
|
|
) |
|
|
|
|
|
last_ch = torch.ones((batch_size, H, W, 1), device=device, dtype=dtype) |
|
|
pixel_coords_concat = torch.cat([pixel_coords_output, last_ch], axis=-1) |
|
|
|
|
|
depth = cam_coords_input[..., 2:3].permute(0, 3, 1, 2).contiguous() |
|
|
cam_coords_output = kornia.geometry.camera.pixel2cam( |
|
|
depth=depth, intrinsics_inv=intrinsics_inv, pixel_coords=pixel_coords_concat |
|
|
) |
|
|
|
|
|
assert_close(cam_coords_output, cam_coords_input, atol=1e-4, rtol=1e-4) |
|
|
|
|
|
@pytest.mark.parametrize("batch_size", (1,)) |
|
|
def test_gradcheck(self, batch_size, device, dtype): |
|
|
H, W = 10, 20 |
|
|
fx, fy = W, H |
|
|
cx, cy = W / 2, H / 2 |
|
|
eps = 1e-12 |
|
|
seed = 77 |
|
|
low, high = -500, 500 |
|
|
atol, rtol = 1e-5, 1e-3 |
|
|
|
|
|
|
|
|
if (device.type == "cuda") and (dtype == torch.float64): |
|
|
atol, rtol = 1e-4, 1e-2 |
|
|
|
|
|
|
|
|
intrinsics = self._create_intrinsics(batch_size, fx, fy, cx, cy, device=device, dtype=dtype).contiguous() |
|
|
|
|
|
|
|
|
|
|
|
proj_mat = intrinsics |
|
|
|
|
|
torch.manual_seed(seed) |
|
|
cam_coords_src = self._get_samples((batch_size, H, W, 3), low, high, device, dtype) |
|
|
|
|
|
cam_coords_src = tensor_to_gradcheck_var(cam_coords_src) |
|
|
proj_mat = tensor_to_gradcheck_var(proj_mat) |
|
|
|
|
|
assert gradcheck( |
|
|
kornia.geometry.camera.cam2pixel, |
|
|
(cam_coords_src, proj_mat, eps), |
|
|
raise_exception=True, |
|
|
atol=atol, |
|
|
rtol=rtol, |
|
|
) |
|
|
|
|
|
|
|
|
class TestPixel2Cam: |
|
|
def _create_intrinsics(self, batch_size, fx, fy, cx, cy, device, dtype): |
|
|
temp = torch.eye(4, device=device, dtype=dtype) |
|
|
temp[0, 0], temp[0, 2] = fx, cx |
|
|
temp[1, 1], temp[1, 2] = fy, cy |
|
|
intrinsics = temp.expand(batch_size, -1, -1) |
|
|
return intrinsics |
|
|
|
|
|
def _create_intrinsics_inv(self, batch_size, fx, fy, cx, cy, device, dtype): |
|
|
temp = torch.eye(4, device=device, dtype=dtype) |
|
|
temp[0, 0], temp[0, 2] = 1 / fx, -cx / fx |
|
|
temp[1, 1], temp[1, 2] = 1 / fy, -cy / fy |
|
|
intrinsics_inv = temp.expand(batch_size, -1, -1) |
|
|
return intrinsics_inv |
|
|
|
|
|
def _get_samples(self, shape, low, high, device, dtype): |
|
|
"""Return a tensor having the given shape and whose values are in the range [low, high)""" |
|
|
return ((high - low) * torch.rand(shape, device=device, dtype=dtype)) + low |
|
|
|
|
|
@pytest.mark.parametrize("batch_size", (1, 2, 5)) |
|
|
def test_smoke(self, batch_size, device, dtype): |
|
|
H, W = 250, 500 |
|
|
fx, fy = W, H |
|
|
cx, cy = W / 2, H / 2 |
|
|
seed = 77 |
|
|
low_1, high_1 = -500, 500 |
|
|
low_2, high_2 = -(max(W, H) * 3), (max(W, H) * 3) |
|
|
|
|
|
torch.manual_seed(seed) |
|
|
depth = self._get_samples((batch_size, 1, H, W), low_1, high_1, device, dtype) |
|
|
pixel_coords = self._get_samples((batch_size, H, W, 2), low_2, high_2, device, dtype) |
|
|
|
|
|
last_ch = torch.ones((batch_size, H, W, 1), device=device, dtype=dtype) |
|
|
pixel_coords_input = torch.cat([pixel_coords, last_ch], axis=-1) |
|
|
|
|
|
intrinsics_inv = self._create_intrinsics_inv(batch_size, fx, fy, cx, cy, device=device, dtype=dtype) |
|
|
|
|
|
output = kornia.geometry.camera.pixel2cam( |
|
|
depth=depth, intrinsics_inv=intrinsics_inv, pixel_coords=pixel_coords_input |
|
|
) |
|
|
|
|
|
assert output.shape == (batch_size, H, W, 3) |
|
|
|
|
|
@pytest.mark.parametrize("batch_size", (1, 2, 5)) |
|
|
def test_consistency(self, batch_size, device, dtype): |
|
|
H, W = 250, 500 |
|
|
fx, fy = W, H |
|
|
cx, cy = W / 2, H / 2 |
|
|
eps = 1e-12 |
|
|
seed = 77 |
|
|
low_1, high_1 = -500, 500 |
|
|
low_2, high_2 = -(max(W, H) * 3), (max(W, H) * 3) |
|
|
|
|
|
torch.manual_seed(seed) |
|
|
depth = self._get_samples((batch_size, 1, H, W), low_1, high_1, device, dtype) |
|
|
pixel_coords = self._get_samples((batch_size, H, W, 2), low_2, high_2, device, dtype) |
|
|
|
|
|
last_ch = torch.ones((batch_size, H, W, 1), device=device, dtype=dtype) |
|
|
pixel_coords_input = torch.cat([pixel_coords, last_ch], axis=-1) |
|
|
|
|
|
intrinsics = self._create_intrinsics(batch_size, fx, fy, cx, cy, device=device, dtype=dtype) |
|
|
intrinsics_inv = self._create_intrinsics_inv(batch_size, fx, fy, cx, cy, device=device, dtype=dtype) |
|
|
|
|
|
cam_coords = kornia.geometry.camera.pixel2cam( |
|
|
depth=depth, intrinsics_inv=intrinsics_inv, pixel_coords=pixel_coords_input |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
proj_mat = intrinsics |
|
|
pixel_coords_output = kornia.geometry.camera.cam2pixel( |
|
|
cam_coords_src=cam_coords, dst_proj_src=proj_mat, eps=eps |
|
|
) |
|
|
pixel_coords_concat = torch.cat([pixel_coords_output, last_ch], axis=-1) |
|
|
|
|
|
assert_close(pixel_coords_concat, pixel_coords_input, atol=1e-4, rtol=1e-4) |
|
|
|
|
|
@pytest.mark.parametrize("batch_size", (1,)) |
|
|
def test_gradcheck(self, batch_size, device, dtype): |
|
|
H, W = 10, 20 |
|
|
fx, fy = W, H |
|
|
cx, cy = W / 2, H / 2 |
|
|
seed = 77 |
|
|
low_1, high_1 = -500, 500 |
|
|
low_2, high_2 = -(max(W, H) * 3), (max(W, H) * 3) |
|
|
|
|
|
torch.manual_seed(seed) |
|
|
depth = self._get_samples((batch_size, 1, H, W), low_1, high_1, device, dtype) |
|
|
pixel_coords = self._get_samples((batch_size, H, W, 2), low_2, high_2, device, dtype) |
|
|
|
|
|
last_ch = torch.ones((batch_size, H, W, 1), device=device, dtype=dtype) |
|
|
pixel_coords_input = torch.cat([pixel_coords, last_ch], axis=-1) |
|
|
|
|
|
|
|
|
intrinsics_inv = self._create_intrinsics_inv( |
|
|
batch_size, fx, fy, cx, cy, device=device, dtype=dtype |
|
|
).contiguous() |
|
|
|
|
|
depth = tensor_to_gradcheck_var(depth) |
|
|
intrinsics_inv = tensor_to_gradcheck_var(intrinsics_inv) |
|
|
pixel_coords_input = tensor_to_gradcheck_var(pixel_coords_input) |
|
|
|
|
|
assert gradcheck( |
|
|
kornia.geometry.camera.pixel2cam, (depth, intrinsics_inv, pixel_coords_input), raise_exception=True |
|
|
) |
|
|
|
|
|
|
|
|
class TestPinholeCamera: |
|
|
def _create_intrinsics(self, batch_size, fx, fy, cx, cy, device, dtype): |
|
|
intrinsics = torch.eye(4, device=device, dtype=dtype) |
|
|
intrinsics[..., 0, 0] = fx |
|
|
intrinsics[..., 1, 1] = fy |
|
|
intrinsics[..., 0, 2] = cx |
|
|
intrinsics[..., 1, 2] = cy |
|
|
return intrinsics.expand(batch_size, -1, -1) |
|
|
|
|
|
def _create_extrinsics(self, batch_size, tx, ty, tz, device, dtype): |
|
|
extrinsics = torch.eye(4, device=device, dtype=dtype) |
|
|
extrinsics[..., 0, -1] = tx |
|
|
extrinsics[..., 1, -1] = ty |
|
|
extrinsics[..., 2, -1] = tz |
|
|
return extrinsics.expand(batch_size, -1, -1) |
|
|
|
|
|
def test_smoke(self, device, dtype): |
|
|
intrinsics = torch.eye(4, device=device, dtype=dtype)[None] |
|
|
extrinsics = torch.eye(4, device=device, dtype=dtype)[None] |
|
|
height = torch.ones(1, device=device, dtype=dtype) |
|
|
width = torch.ones(1, device=device, dtype=dtype) |
|
|
pinhole = kornia.geometry.camera.PinholeCamera(intrinsics, extrinsics, height, width) |
|
|
assert isinstance(pinhole, kornia.geometry.camera.PinholeCamera) |
|
|
|
|
|
def test_pinhole_camera_attributes(self, device, dtype): |
|
|
batch_size = 1 |
|
|
height, width = 4, 6 |
|
|
fx, fy, cx, cy = 1, 2, width / 2, height / 2 |
|
|
tx, ty, tz = 1, 2, 3 |
|
|
|
|
|
intrinsics = self._create_intrinsics(batch_size, fx, fy, cx, cy, device=device, dtype=dtype) |
|
|
extrinsics = self._create_extrinsics(batch_size, tx, ty, tz, device=device, dtype=dtype) |
|
|
height = torch.ones(batch_size, device=device, dtype=dtype) * height |
|
|
width = torch.ones(batch_size, device=device, dtype=dtype) * width |
|
|
|
|
|
pinhole = kornia.geometry.camera.PinholeCamera(intrinsics, extrinsics, height, width) |
|
|
|
|
|
assert pinhole.batch_size == batch_size |
|
|
assert pinhole.fx.item() == fx |
|
|
assert pinhole.fy.item() == fy |
|
|
assert pinhole.cx.item() == cx |
|
|
assert pinhole.cy.item() == cy |
|
|
assert pinhole.tx.item() == tx |
|
|
assert pinhole.ty.item() == ty |
|
|
assert pinhole.tz.item() == tz |
|
|
assert pinhole.height.item() == height |
|
|
assert pinhole.width.item() == width |
|
|
assert pinhole.rt_matrix.shape == (batch_size, 3, 4) |
|
|
assert pinhole.camera_matrix.shape == (batch_size, 3, 3) |
|
|
assert pinhole.rotation_matrix.shape == (batch_size, 3, 3) |
|
|
assert pinhole.translation_vector.shape == (batch_size, 3, 1) |
|
|
|
|
|
def test_pinhole_camera_translation_setters(self, device, dtype): |
|
|
batch_size = 1 |
|
|
height, width = 4, 6 |
|
|
fx, fy, cx, cy = 1, 2, width / 2, height / 2 |
|
|
tx, ty, tz = 1, 2, 3 |
|
|
|
|
|
intrinsics = self._create_intrinsics(batch_size, fx, fy, cx, cy, device=device, dtype=dtype) |
|
|
extrinsics = self._create_extrinsics(batch_size, tx, ty, tz, device=device, dtype=dtype) |
|
|
height = torch.ones(batch_size, device=device, dtype=dtype) * height |
|
|
width = torch.ones(batch_size, device=device, dtype=dtype) * width |
|
|
|
|
|
pinhole = kornia.geometry.camera.PinholeCamera(intrinsics, extrinsics, height, width) |
|
|
|
|
|
assert pinhole.tx.item() == tx |
|
|
assert pinhole.ty.item() == ty |
|
|
assert pinhole.tz.item() == tz |
|
|
|
|
|
|
|
|
pinhole.tx += 3.0 |
|
|
pinhole.ty += 2.0 |
|
|
pinhole.tz += 1.0 |
|
|
|
|
|
assert pinhole.tx.item() == tx + 3.0 |
|
|
assert pinhole.ty.item() == ty + 2.0 |
|
|
assert pinhole.tz.item() == tz + 1.0 |
|
|
|
|
|
|
|
|
pinhole.tx = 0.0 |
|
|
pinhole.ty = 0.0 |
|
|
pinhole.tz = 0.0 |
|
|
|
|
|
assert pinhole.tx.item() == 0.0 |
|
|
assert pinhole.ty.item() == 0.0 |
|
|
assert pinhole.tz.item() == 0.0 |
|
|
|
|
|
def test_pinhole_camera_attributes_batch2(self, device, dtype): |
|
|
batch_size = 2 |
|
|
height, width = 4, 6 |
|
|
fx, fy, cx, cy = 1, 2, width / 2, height / 2 |
|
|
tx, ty, tz = 1, 2, 3 |
|
|
|
|
|
intrinsics = self._create_intrinsics(batch_size, fx, fy, cx, cy, device=device, dtype=dtype) |
|
|
extrinsics = self._create_extrinsics(batch_size, tx, ty, tz, device=device, dtype=dtype) |
|
|
height = torch.ones(batch_size, device=device, dtype=dtype) * height |
|
|
width = torch.ones(batch_size, device=device, dtype=dtype) * width |
|
|
|
|
|
pinhole = kornia.geometry.camera.PinholeCamera(intrinsics, extrinsics, height, width) |
|
|
|
|
|
assert pinhole.batch_size == batch_size |
|
|
assert pinhole.fx.shape[0] == batch_size |
|
|
assert pinhole.fy.shape[0] == batch_size |
|
|
assert pinhole.cx.shape[0] == batch_size |
|
|
assert pinhole.cy.shape[0] == batch_size |
|
|
assert pinhole.tx.shape[0] == batch_size |
|
|
assert pinhole.ty.shape[0] == batch_size |
|
|
assert pinhole.tz.shape[0] == batch_size |
|
|
assert pinhole.height.shape[0] == batch_size |
|
|
assert pinhole.width.shape[0] == batch_size |
|
|
assert pinhole.rt_matrix.shape == (batch_size, 3, 4) |
|
|
assert pinhole.camera_matrix.shape == (batch_size, 3, 3) |
|
|
assert pinhole.rotation_matrix.shape == (batch_size, 3, 3) |
|
|
assert pinhole.translation_vector.shape == (batch_size, 3, 1) |
|
|
|
|
|
def test_pinhole_camera_scale(self, device, dtype): |
|
|
batch_size = 2 |
|
|
height, width = 4, 6 |
|
|
fx, fy, cx, cy = 1, 2, width / 2, height / 2 |
|
|
tx, ty, tz = 1, 2, 3 |
|
|
scale_val = 2.0 |
|
|
|
|
|
intrinsics = self._create_intrinsics(batch_size, fx, fy, cx, cy, device=device, dtype=dtype) |
|
|
extrinsics = self._create_extrinsics(batch_size, tx, ty, tz, device=device, dtype=dtype) |
|
|
height = torch.ones(batch_size, device=device, dtype=dtype) * height |
|
|
width = torch.ones(batch_size, device=device, dtype=dtype) * width |
|
|
scale_factor = torch.ones(batch_size, device=device, dtype=dtype) * scale_val |
|
|
|
|
|
pinhole = kornia.geometry.camera.PinholeCamera(intrinsics, extrinsics, height, width) |
|
|
pinhole_scale = pinhole.scale(scale_factor) |
|
|
|
|
|
assert_close( |
|
|
pinhole_scale.intrinsics[..., 0, 0], pinhole.intrinsics[..., 0, 0] * scale_val, atol=1e-4, rtol=1e-4 |
|
|
) |
|
|
assert_close( |
|
|
pinhole_scale.intrinsics[..., 1, 1], pinhole.intrinsics[..., 1, 1] * scale_val, atol=1e-4, rtol=1e-4 |
|
|
) |
|
|
assert_close( |
|
|
pinhole_scale.intrinsics[..., 0, 2], pinhole.intrinsics[..., 0, 2] * scale_val, atol=1e-4, rtol=1e-4 |
|
|
) |
|
|
assert_close( |
|
|
pinhole_scale.intrinsics[..., 1, 2], pinhole.intrinsics[..., 1, 2] * scale_val, atol=1e-4, rtol=1e-4 |
|
|
) |
|
|
assert_close(pinhole_scale.height, pinhole.height * scale_val, atol=1e-4, rtol=1e-4) |
|
|
assert_close(pinhole_scale.width, pinhole.width * scale_val, atol=1e-4, rtol=1e-4) |
|
|
|
|
|
def test_pinhole_camera_scale_inplace(self, device, dtype): |
|
|
batch_size = 2 |
|
|
height, width = 4, 6 |
|
|
fx, fy, cx, cy = 1, 2, width / 2, height / 2 |
|
|
tx, ty, tz = 1, 2, 3 |
|
|
scale_val = 2.0 |
|
|
|
|
|
intrinsics = self._create_intrinsics(batch_size, fx, fy, cx, cy, device=device, dtype=dtype) |
|
|
extrinsics = self._create_extrinsics(batch_size, tx, ty, tz, device=device, dtype=dtype) |
|
|
height = torch.ones(batch_size, device=device, dtype=dtype) * height |
|
|
width = torch.ones(batch_size, device=device, dtype=dtype) * width |
|
|
scale_factor = torch.ones(batch_size, device=device, dtype=dtype) * scale_val |
|
|
|
|
|
pinhole = kornia.geometry.camera.PinholeCamera(intrinsics, extrinsics, height, width) |
|
|
pinhole_scale = pinhole.clone() |
|
|
pinhole_scale.scale_(scale_factor) |
|
|
|
|
|
assert_close( |
|
|
pinhole_scale.intrinsics[..., 0, 0], pinhole.intrinsics[..., 0, 0] * scale_val, atol=1e-4, rtol=1e-4 |
|
|
) |
|
|
assert_close( |
|
|
pinhole_scale.intrinsics[..., 1, 1], pinhole.intrinsics[..., 1, 1] * scale_val, atol=1e-4, rtol=1e-4 |
|
|
) |
|
|
assert_close( |
|
|
pinhole_scale.intrinsics[..., 0, 2], pinhole.intrinsics[..., 0, 2] * scale_val, atol=1e-4, rtol=1e-4 |
|
|
) |
|
|
assert_close( |
|
|
pinhole_scale.intrinsics[..., 1, 2], pinhole.intrinsics[..., 1, 2] * scale_val, atol=1e-4, rtol=1e-4 |
|
|
) |
|
|
assert_close(pinhole_scale.height, pinhole.height * scale_val, atol=1e-4, rtol=1e-4) |
|
|
assert_close(pinhole_scale.width, pinhole.width * scale_val, atol=1e-4, rtol=1e-4) |
|
|
|