|
|
|
|
|
|
|
|
|
|
|
|
|
import PIL.Image |
|
import os |
|
os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1" |
|
import cv2 |
|
import numpy as np |
|
from dust3r.utils.geometry import colmap_to_opencv_intrinsics, opencv_to_colmap_intrinsics |
|
try: |
|
lanczos = PIL.Image.Resampling.LANCZOS |
|
bicubic = PIL.Image.Resampling.BICUBIC |
|
except AttributeError: |
|
lanczos = PIL.Image.LANCZOS |
|
bicubic = PIL.Image.BICUBIC |
|
|
|
|
|
class ImageList: |
|
""" Convenience class to aply the same operation to a whole set of images. |
|
""" |
|
|
|
def __init__(self, images): |
|
if not isinstance(images, (tuple, list, set)): |
|
images = [images] |
|
self.images = [] |
|
for image in images: |
|
if not isinstance(image, PIL.Image.Image): |
|
image = PIL.Image.fromarray(image) |
|
self.images.append(image) |
|
|
|
def __len__(self): |
|
return len(self.images) |
|
|
|
def to_pil(self): |
|
return tuple(self.images) if len(self.images) > 1 else self.images[0] |
|
|
|
@property |
|
def size(self): |
|
sizes = [im.size for im in self.images] |
|
assert all(sizes[0] == s for s in sizes) |
|
return sizes[0] |
|
|
|
def resize(self, *args, **kwargs): |
|
return ImageList(self._dispatch('resize', *args, **kwargs)) |
|
|
|
def crop(self, *args, **kwargs): |
|
return ImageList(self._dispatch('crop', *args, **kwargs)) |
|
|
|
def _dispatch(self, func, *args, **kwargs): |
|
return [getattr(im, func)(*args, **kwargs) for im in self.images] |
|
|
|
|
|
def rescale_image_depthmap(image, depthmap, pred_depth, camera_intrinsics, output_resolution, force=True): |
|
""" Jointly rescale a (image, depthmap) |
|
so that (out_width, out_height) >= output_res |
|
""" |
|
image = ImageList(image) |
|
input_resolution = np.array(image.size) |
|
output_resolution = np.array(output_resolution) |
|
if depthmap is not None: |
|
|
|
assert tuple(depthmap.shape[:2]) == image.size[::-1] |
|
if pred_depth is not None: |
|
|
|
assert tuple(pred_depth.shape[:2]) == image.size[::-1] |
|
|
|
assert output_resolution.shape == (2,) |
|
scale_final = max(output_resolution / image.size) + 1e-8 |
|
if scale_final >= 1 and not force: |
|
return (image.to_pil(), depthmap, pred_depth, camera_intrinsics) |
|
output_resolution = np.floor(input_resolution * scale_final).astype(int) |
|
output_resolution = list(output_resolution) |
|
|
|
image = image.resize(output_resolution, resample=lanczos if scale_final < 1 else bicubic) |
|
if depthmap is not None: |
|
depthmap = cv2.resize(depthmap, output_resolution, fx=scale_final, |
|
fy=scale_final, interpolation=cv2.INTER_NEAREST) |
|
if pred_depth is not None: |
|
pred_depth = cv2.resize(pred_depth, output_resolution, fx=scale_final, |
|
fy=scale_final, interpolation=cv2.INTER_NEAREST) |
|
|
|
|
|
camera_intrinsics = camera_matrix_of_crop( |
|
camera_intrinsics, input_resolution, output_resolution, scaling=scale_final) |
|
|
|
return image.to_pil(), depthmap, pred_depth, camera_intrinsics |
|
|
|
|
|
def camera_matrix_of_crop(input_camera_matrix, input_resolution, output_resolution, scaling=1, offset_factor=0.5, offset=None): |
|
|
|
margins = np.asarray(input_resolution) * scaling - output_resolution |
|
assert np.all(margins >= 0.0) |
|
if offset is None: |
|
offset = offset_factor * margins |
|
|
|
|
|
output_camera_matrix_colmap = opencv_to_colmap_intrinsics(input_camera_matrix) |
|
output_camera_matrix_colmap[:2, :] *= scaling |
|
output_camera_matrix_colmap[:2, 2] -= offset |
|
output_camera_matrix = colmap_to_opencv_intrinsics(output_camera_matrix_colmap) |
|
|
|
return output_camera_matrix |
|
|
|
|
|
def crop_image_depthmap(image, depthmap, pred_depth, camera_intrinsics, crop_bbox): |
|
""" |
|
Return a crop of the input view. |
|
""" |
|
image = ImageList(image) |
|
l, t, r, b = crop_bbox |
|
|
|
image = image.crop((l, t, r, b)) |
|
depthmap = depthmap[t:b, l:r] |
|
pred_depth = pred_depth[t:b, l:r, :] |
|
camera_intrinsics = camera_intrinsics.copy() |
|
camera_intrinsics[0, 2] -= l |
|
camera_intrinsics[1, 2] -= t |
|
|
|
return image.to_pil(), depthmap, pred_depth, camera_intrinsics |
|
|
|
|
|
def bbox_from_intrinsics_in_out(input_camera_matrix, output_camera_matrix, output_resolution): |
|
out_width, out_height = output_resolution |
|
l, t = np.int32(np.round(input_camera_matrix[:2, 2] - output_camera_matrix[:2, 2])) |
|
crop_bbox = (l, t, l + out_width, t + out_height) |
|
return crop_bbox |
|
|
|
def center_crop_image_depthmap(image, depthmap, pred_depth, camera_intrinsics, crop_scale): |
|
""" |
|
Jointly center-crop an image and its depthmap, and adjust the camera intrinsics accordingly. |
|
|
|
Parameters: |
|
- image: PIL.Image or similar, the input image. |
|
- depthmap: np.ndarray, the corresponding depth map. |
|
- camera_intrinsics: np.ndarray, the 3x3 camera intrinsics matrix. |
|
- crop_scale: float between 0 and 1, the fraction of the image to keep. |
|
|
|
Returns: |
|
- cropped_image: PIL.Image, the center-cropped image. |
|
- cropped_depthmap: np.ndarray, the center-cropped depth map. |
|
- adjusted_intrinsics: np.ndarray, the adjusted camera intrinsics matrix. |
|
""" |
|
|
|
assert 0 < crop_scale <= 1, "crop_scale must be between 0 and 1" |
|
|
|
|
|
image = ImageList(image) |
|
input_resolution = np.array(image.size) |
|
if depthmap is not None: |
|
|
|
assert depthmap.shape[:2] == tuple(image.size[::-1]), "Depthmap size must match image size" |
|
if pred_depth is not None: |
|
|
|
assert pred_depth.shape[:2] == tuple(image.size[::-1]), "pred_depth size must match image size" |
|
|
|
output_resolution = np.floor(input_resolution * crop_scale).astype(int) |
|
|
|
crop_scale = output_resolution / input_resolution |
|
|
|
|
|
margins = input_resolution - output_resolution |
|
offset = margins / 2 |
|
|
|
|
|
l, t = offset.astype(int) |
|
r = l + output_resolution[0] |
|
b = t + output_resolution[1] |
|
crop_bbox = (l, t, r, b) |
|
|
|
|
|
image = image.crop(crop_bbox) |
|
if depthmap is not None: |
|
depthmap = depthmap[t:b, l:r] |
|
if pred_depth is not None: |
|
pred_depth = pred_depth[t:b, l:r, :] |
|
|
|
adjusted_intrinsics = camera_intrinsics.copy() |
|
|
|
|
|
|
|
|
|
|
|
|
|
adjusted_intrinsics[0, 2] -= l |
|
adjusted_intrinsics[1, 2] -= t |
|
|
|
return image.to_pil(), depthmap, pred_depth, adjusted_intrinsics |
|
|