| import argparse |
| import os |
| import sys |
|
|
| import cv2 |
| import numpy as np |
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| import torch.optim as optim |
|
|
| import kornia as tgm |
|
|
|
|
| def load_data(root_path, sequence_name, frame_id): |
| |
| file_name = 'frame_%04d' % (frame_id) |
| image_file = os.path.join(root_path, 'clean', sequence_name, file_name + '.png') |
| depth_file = os.path.join(root_path, 'depth', sequence_name, file_name + '.dpt') |
| camera_file = os.path.join(root_path, 'camdata_left', sequence_name, file_name + '.cam') |
| |
| image_tensor = load_image(image_file) |
| depth = load_depth(depth_file) |
| |
| height, width = image_tensor.shape[-2:] |
| intrinsics, extrinsics = load_camera_data(camera_file) |
| camera = tgm.utils.create_pinhole(intrinsics, extrinsics, height, width) |
| return image_tensor, depth, camera |
|
|
|
|
| def load_depth(file_name): |
| """Load the depth using the sintel SDK and converts to torch.Tensor.""" |
| if not os.path.isfile(file_name): |
| raise AssertionError(f"Invalid file {file_name}") |
| import sintel_io |
|
|
| depth = sintel_io.depth_read(file_name) |
| return torch.from_numpy(depth).view(1, 1, *depth.shape).float() |
|
|
|
|
| def load_camera_data(file_name): |
| """Load the camera data using the sintel SDK and converts to torch.Tensor.""" |
| if not os.path.isfile(file_name): |
| raise AssertionError(f"Invalid file {file_name}") |
| import sintel_io |
|
|
| intrinsic, extrinsic = sintel_io.cam_read(file_name) |
| return intrinsic, extrinsic |
|
|
|
|
| def load_image(file_name): |
| """Load the image with OpenCV and converts to torch.Tensor.""" |
| if not os.path.isfile(file_name): |
| raise AssertionError(f"Invalid file {file_name}") |
|
|
| |
| img = cv2.imread(file_name, cv2.IMREAD_COLOR) |
|
|
| |
| tensor = tgm.utils.image_to_tensor(img).float() / 255.0 |
| return tensor.view(1, *tensor.shape) |
|
|
|
|
| def clip_and_convert_tensor(tensor): |
| """Convert the input torch.Tensor to OpenCV image,clip it to be between. |
| |
| [0, 255] and convert it to unit |
| """ |
| img = tgm.utils.tensor_to_image(255.0 * tensor) |
| img_cliped = np.clip(img, 0, 255) |
| img = img_cliped.astype('uint8') |
| return img |
|
|
|
|
| class InvDepth(nn.Module): |
| def __init__(self, height, width, min_depth=0.50, max_depth=25.0): |
| super().__init__() |
| self._min_range = 1.0 / max_depth |
| self._max_range = 1.0 / min_depth |
|
|
| self.w = nn.Parameter(self._init_weights(height, width)) |
|
|
| def _init_weights(self, height, width): |
| r1 = self._min_range |
| r2 = self._min_range + (self._max_range - self._min_range) * 0.1 |
| w_init = (r1 - r2) * torch.rand(1, 1, height, width) + r2 |
| return w_init |
|
|
| def forward(self): |
| return self.w.clamp(min=self._min_range, max=self._max_range) |
|
|
|
|
| def DepthRegressionApp(): |
| |
| parser = argparse.ArgumentParser(description='Depth Regression with photometric loss.') |
| parser.add_argument('--input-dir', type=str, required=True, help='the path to the directory with the input data.') |
| parser.add_argument('--output-dir', type=str, required=True, help='the path to output the results.') |
| parser.add_argument( |
| '--num-iterations', type=int, default=1000, metavar='N', help='number of training iterations (default: 1000)' |
| ) |
| parser.add_argument('--sequence-name', type=str, default='alley_1', help='the name of the sequence.') |
| parser.add_argument('--frame-ref-id', type=int, default=1, help='the id for the reference image in the sequence.') |
| parser.add_argument('--frame-i-id', type=int, default=2, help='the id for the image i in the sequence.') |
| |
| parser.add_argument('--lr', type=float, default=1e-3, metavar='LR', help='learning rate (default: 1e-3)') |
| |
| parser.add_argument('--cuda', action='store_true', default=False, help='enables CUDA training') |
| parser.add_argument('--seed', type=int, default=666, metavar='S', help='random seed (default: 666)') |
| parser.add_argument( |
| '--log-interval', |
| type=int, |
| default=10, |
| metavar='N', |
| help='how many batches to wait before logging training status', |
| ) |
| parser.add_argument( |
| '--log-interval-vis', |
| type=int, |
| default=100, |
| metavar='N', |
| help='how many batches to wait before visual logging training status', |
| ) |
| args = parser.parse_args() |
|
|
| |
| use_cuda = args.cuda and torch.cuda.is_available() |
| device = torch.device('cuda' if use_cuda else 'cpu') |
|
|
| torch.manual_seed(args.seed) |
|
|
| |
| root_path = os.path.abspath(args.input_dir) |
| sys.path.append(os.path.join(root_path, 'sdk/python')) |
|
|
| |
| root_dir = os.path.join(root_path, 'training') |
| img_ref, _, cam_ref = load_data(root_dir, args.sequence_name, args.frame_ref_id) |
| img_i, _, cam_i = load_data(root_dir, args.sequence_name, args.frame_i_id) |
|
|
| |
| warper = tgm.DepthWarper(cam_i) |
| warper.compute_homographies(cam_ref) |
|
|
| |
| height, width = img_ref.shape[-2:] |
| inv_depth_ref = InvDepth(height, width).to(device) |
|
|
| |
| optimizer = optim.Adam(inv_depth_ref.parameters(), lr=args.lr) |
|
|
| |
| img_ref, img_i = img_ref.to(device), img_i.to(device) |
|
|
| |
|
|
| for iter_idx in range(args.num_iterations): |
| |
| img_i_to_ref = warper(inv_depth_ref(), img_i) |
|
|
| |
| loss = F.l1_loss(img_i_to_ref, img_ref, reduction='none') |
| loss = torch.mean(loss) |
|
|
| |
| optimizer.zero_grad() |
| loss.backward() |
| optimizer.step() |
|
|
| if iter_idx % args.log_interval == 0 or iter_idx == args.num_iterations - 1: |
| print(f'Train iteration: {iter_idx}/{args.num_iterations}\tLoss: {loss.item():.6}') |
|
|
| if iter_idx % args.log_interval_vis == 0: |
| |
| img_i_to_ref = warper(inv_depth_ref(), img_i) |
| img_both_vis = 0.5 * (img_i_to_ref + img_ref) |
|
|
| img_both_vis = clip_and_convert_tensor(img_both_vis) |
| img_i_to_ref_vis = clip_and_convert_tensor(img_i_to_ref) |
| inv_depth_ref_vis = tgm.utils.tensor_to_image( |
| inv_depth_ref() / (inv_depth_ref().max() + 1e-6) |
| ).squeeze() |
| inv_depth_ref_vis = np.clip(255.0 * inv_depth_ref_vis, 0, 255) |
| inv_depth_ref_vis = inv_depth_ref_vis.astype('uint8') |
|
|
| |
| def file_name(output_dir, file_name, iter_idx): |
| return os.path.join(output_dir, f"{file_name}_{iter_idx}.png") |
|
|
| cv2.imwrite(file_name(args.output_dir, "warped", iter_idx), img_i_to_ref_vis) |
| cv2.imwrite(file_name(args.output_dir, "warped_both", iter_idx), img_both_vis) |
| cv2.imwrite(file_name(args.output_dir, "inv_depth_ref", iter_idx), inv_depth_ref_vis) |
|
|
|
|
| if __name__ == "__main__": |
| DepthRegressionApp() |
|
|