import os, sys import numpy as np import random import time import json import matplotlib.pyplot as plt import tqdm import torch def generate_standard_elips(N_samples = 10, a= 1,b = 1): radius = 0.5 center = 0 N_samples1 = int(N_samples/2 - 1) N_samples2 = N_samples - N_samples1 x1 = np.random.uniform((center-radius)*a,(center+radius)*a, size = N_samples1) x1_ordered = np.sort(x1) y1 = center + b*np.sqrt(radius**2 - ((x1_ordered-center)/a)**2) x2 = np.random.uniform((center-radius)*a,(center+radius)*a, size = N_samples - N_samples1) x2_ordered = -np.sort(-x2) #the minus sign to sort descindingly y2 = center - b*np.sqrt(radius**2 - ((x2_ordered-center)/a)**2) x = np.concatenate([x1_ordered, x2_ordered], axis=0) y = np.concatenate([y1, y2], axis=0) return x, y def destandarize_point(d, dim=128): return dim*(d + 0.5) def To_pointcloud(x,y,z=0): N_points = x.shape[0] point_cloud = np.zeros([N_points,3]) point_cloud[:,0] = x point_cloud[:,1] = y if not z==0: point_cloud[:,2] = z return point_cloud def To_xyz(point_cloud): x = point_cloud[:,0] y = point_cloud[:,1] z = point_cloud[:,2] return x,y,z def random_rigid_transformation(dim=2): #dim = 4 rotation_x = 0 rotation_y = 0 rotation_z = random.uniform(0, 2)*np.pi translation_x = random.uniform(-1, 1)*dim translation_y = random.uniform(-1, 1)*dim translation_z = 0 reflection_x = random.sample([-1,1],1)[0] reflection_y = random.sample([-1,1],1)[0] reflection_z = 1 Rotx = np.array([[1,0,0], [0,np.cos(rotation_x),-np.sin(rotation_x)], [0,np.sin(rotation_x),np.cos(rotation_x)]]) Roty = np.array([[np.cos(rotation_y),0,np.sin(rotation_y)], [0,1,0], [-np.sin(rotation_y),0,np.cos(rotation_y)]]) Rotz = np.array([[np.cos(rotation_z),-np.sin(rotation_z),0], [np.sin(rotation_z),np.cos(rotation_z),0], [0,0,1]]) Rotation = np.matmul(Rotz, np.matmul(Roty,Rotx)) Reflection = np.array([[reflection_x,0,0],[0,reflection_y,0],[0,0,reflection_z]]) Translation = np.array([translation_x,translation_y,translation_z]) RefRotation = np.matmul(Reflection,Rotation) return RefRotation, Translation def rigid_2Dtransformation(prdction): #prediction = [rotationz, reflectionx, reflectiony, translationx, translationy] N_examples = prdction['rotation'].shape[0] Translation = prdction['translation'] Reflection = torch.zeros([N_examples,3,3])# Reflection[:,0,0] = prdction['reflection'][:,0] Reflection[:,1,1] = prdction['reflection'][:,1] Reflection[:,2,2] = 1. rotation_z = prdction['rotation'][:,2] Rotation = torch.zeros([N_examples,3,3])#np.repeat(np.eye(3)[None,:,:],N_examples, axis=0)) Rotation[:,0,0] = torch.cos(rotation_z) Rotation[:,1,1] = torch.cos(rotation_z) Rotation[:,0,1] = -torch.sin(rotation_z) Rotation[:,2,2] = 1. Rotation[:,1,0] = torch.sin(rotation_z) RefRotation = torch.matmul(Reflection,Rotation) return RefRotation, Translation def batch_hausdorff_prcnt_distance(batch_point_cloud1, point_cloud2, percentile = 0.95): assert point_cloud2.shape[0]==3 assert batch_point_cloud1.shape[1]==3 distances = torch.norm(batch_point_cloud1[:, :, None,:] - point_cloud2[None, :, :,None], dim=1) dists1 = torch.min(distances, dim=1).values dists2 = torch.min(distances, dim=2).values # Calculate the 95th percentile distance percentile_95 = torch.quantile(torch.cat([dists1, dists2],axis=1), percentile, interpolation='linear', dim=1) return percentile_95 def HDloss(prd, pointcloud_source_norm_torch,pointcloud_target_norm_torch, percentile = 0.95): A, b = rigid_2Dtransformation(prd) point_cloud_wrapped = torch.matmul(A, pointcloud_source_norm_torch.T) + b[:,:,None] loss = batch_hausdorff_prcnt_distance(point_cloud_wrapped, pointcloud_target_norm_torch.T, percentile) return loss def Mean_HDloss(prd, pointcloud_source_norm_torch, pointcloud_target_norm_torch, percentile = 0.95): loss = HDloss(prd, pointcloud_source_norm_torch, pointcloud_target_norm_torch, percentile = 0.95) return torch.mean(loss) def wrap_pointcloud(record, pointcloud_source): #normalize first PC1_mean = np.mean(pointcloud_source, axis=0) pointcloud_source_norm = pointcloud_source - PC1_mean pointcloud_source_norm_torch = torch.tensor(pointcloud_source_norm, requires_grad=False).to(torch.float32) # find Tx A, b = rigid_2Dtransformation(record) point_cloud_wrapped = torch.matmul(A, pointcloud_source_norm_torch.T) + b[:,:,None] return point_cloud_wrapped device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") def move_dict2device(dictionary,device): for key in list(dictionary.keys()): dictionary[key] = dictionary[key].to(device) return dictionary eps = 0.000001948 class Optimization_model(torch.nn.Module): def __init__(self): super(Optimization_model, self).__init__() self.alpha = torch.nn.Parameter(torch.tensor(0.5, requires_grad=True)) self.rotation = torch.nn.Parameter(torch.tensor([0.0, 0.0, 0.0], requires_grad=True)) self.translation = torch.nn.Parameter(torch.tensor([0.01,-0.01, 0.0], requires_grad=True)) self.reflection = torch.nn.Parameter(torch.sign(torch.tensor([0.01,-0.01, 1], requires_grad=True))) #self.rigid = torch.nn.Parameter(torch.tensor([0.0,1.0,1.0,0.1,0.1], requires_grad=True)) def forward(self, input_X_batch): predicted_rotation = self.alpha*self.rotation + (1-self.alpha)*input_X_batch['rotation'] predicted_translation = self.alpha*self.translation + (1-self.alpha)*input_X_batch['translation'] predicted_reflection = torch.sign(self.alpha*self.reflection + (1-self.alpha)*input_X_batch['reflection']+eps) return {'rotation':predicted_rotation, 'translation':predicted_translation, 'reflection':predicted_reflection} class Dataset(torch.utils.data.Dataset): def __init__(self, dataset_size, N_dim = 2): self.dataset_size = dataset_size self.N_dim = 2 def __len__(self): return int(self.dataset_size) def __getitem__(self, index): rotation = np.pi*(-1 + 2*torch.rand([3])) translation = -0.1 + 0.2*torch.rand([3]) reflection = torch.sign(torch.rand([3]) - 0.5) if self.N_dim == 2: rotation[0:2]=0 translation[2]=0 reflection[2]=1 random_solution = {'rotation':rotation, 'translation':translation, 'reflection':reflection} return random_solution def pil_to_numpy(im): im.load() # Unpack data e = Image._getencoder(im.mode, "raw", im.mode) e.setimage(im.im) # NumPy buffer for the result shape, typestr = Image._conv_type_shape(im) data = np.empty(shape, dtype=np.dtype(typestr)) mem = data.data.cast("B", (data.data.nbytes,)) bufsize, s, offset = 65536, 0, 0 while not s: l, s, d = e.encode(bufsize) mem[offset:offset + len(d)] = d offset += len(d) if s < 0: raise RuntimeError("encoder error %d in tobytes" % s) return data def load_image_pil_accelerated(image_path, dim=128): image = Image.open(image_path).convert("RGB") array = pil_to_numpy(image) tensor = torch.from_numpy(np.rollaxis(array,2,0)/255).to(torch.float32) tensor = torchvision.transforms.Resize((dim,dim))(tensor) return tensor def preprocess_image(image_path, dim = 128): img = torch.zeros([1,3,dim,dim]) img[0] = load_image_pil_accelerated(image_path, dim) return img