|
- # -*- coding: utf-8 -*-
- # @Time : 15/1/2019 9:41 PM
- # @Description :
- # @Author : li rui hui
- # @Email : ruihuili@gmail.com
- # @File : visu_utils.py
-
- '''
- Created on November 26, 2017
-
- @author: optas
- '''
-
- import numpy as np
- from numpy.linalg import norm
- import matplotlib.pylab as plt
- # plt.switch_backend('TkAgg')
- from mpl_toolkits.mplot3d import Axes3D
- # from open3d import *
- from Common.eulerangles import euler2mat
-
-
- def rand_rotation_matrix(deflection=1.0, seed=None):
- '''Creates a random rotation matrix.
-
- deflection: the magnitude of the rotation. For 0, no rotation; for 1, completely random
- rotation. Small deflection => small perturbation.
-
- DOI: http://www.realtimerendering.com/resources/GraphicsGems/gemsiii/rand_rotation.c
- http://blog.lostinmyterminal.com/python/2015/05/12/random-rotation-matrix.html
- '''
- if seed is not None:
- np.random.seed(seed)
-
- randnums = np.random.uniform(size=(3,))
-
- theta, phi, z = randnums
-
- theta = theta * 2.0 * deflection * np.pi # Rotation about the pole (Z).
- phi = phi * 2.0 * np.pi # For direction of pole deflection.
- z = z * 2.0 * deflection # For magnitude of pole deflection.
-
- # Compute a vector V used for distributing points over the sphere
- # via the reflection I - V Transpose(V). This formulation of V
- # will guarantee that if x[1] and x[2] are uniformly distributed,
- # the reflected points will be uniform on the sphere. Note that V
- # has length sqrt(2) to eliminate the 2 in the Householder matrix.
-
- r = np.sqrt(z)
- V = (
- np.sin(phi) * r,
- np.cos(phi) * r,
- np.sqrt(2.0 - z))
-
- st = np.sin(theta)
- ct = np.cos(theta)
-
- R = np.array(((ct, st, 0), (-st, ct, 0), (0, 0, 1)))
-
- # Construct the rotation matrix ( V Transpose(V) - I ) R.
- M = (np.outer(V, V) - np.eye(3)).dot(R)
- return M
-
-
- def iterate_in_chunks(l, n):
- '''Yield successive 'n'-sized chunks from iterable 'l'.
- Note: last chunk will be smaller than l if n doesn't divide l perfectly.
- '''
- for i in range(0, len(l), n):
- yield l[i:i + n]
-
-
- def add_gaussian_noise_to_pcloud(pcloud, mu=0, sigma=1):
- gnoise = np.random.normal(mu, sigma, pcloud.shape[0])
- gnoise = np.tile(gnoise, (3, 1)).T
- pcloud += gnoise
- return pcloud
-
-
- def apply_augmentations(batch, conf):
- if conf.gauss_augment is not None or conf.z_rotate:
- batch = batch.copy()
-
- if conf.gauss_augment is not None:
- mu = conf.gauss_augment['mu']
- sigma = conf.gauss_augment['sigma']
- batch += np.random.normal(mu, sigma, batch.shape)
-
- if conf.z_rotate:
- r_rotation = rand_rotation_matrix()
- r_rotation[0, 2] = 0
- r_rotation[2, 0] = 0
- r_rotation[1, 2] = 0
- r_rotation[2, 1] = 0
- r_rotation[2, 2] = 1
- batch = batch.dot(r_rotation)
- return batch
-
-
- def unit_cube_grid_point_cloud(resolution, clip_sphere=False):
- '''Returns the center coordinates of each cell of a 3D grid with resolution^3 cells,
- that is placed in the unit-cube.
- If clip_sphere it True it drops the "corner" cells that lie outside the unit-sphere.
- '''
- grid = np.ndarray((resolution, resolution, resolution, 3), np.float32)
- spacing = 1.0 / float(resolution - 1)
- for i in range(resolution):
- for j in range(resolution):
- for k in range(resolution):
- grid[i, j, k, 0] = i * spacing - 0.5
- grid[i, j, k, 1] = j * spacing - 0.5
- grid[i, j, k, 2] = k * spacing - 0.5
-
- if clip_sphere:
- grid = grid.reshape(-1, 3)
- grid = grid[norm(grid, axis=1) <= 0.5]
-
- return grid, spacing
-
-
- def plot_3d_point_cloud(x, y, z, show=True, show_axis=True, in_u_sphere=False, marker='.', s=8, alpha=.8,
- figsize=(5, 5), elev=10, azim=240, axis=None, title=None, *args, **kwargs):
- if axis is None:
- fig = plt.figure(figsize=figsize)
- ax = fig.add_subplot(111, projection='3d')
- else:
- ax = axis
- fig = axis
-
- if title is not None:
- plt.title(title)
-
- sc = ax.scatter(x, y, z, marker=marker, s=s, alpha=alpha, *args, **kwargs)
- ax.view_init(elev=elev, azim=azim)
-
- if in_u_sphere:
- ax.set_xlim3d(-0.5, 0.5)
- ax.set_ylim3d(-0.5, 0.5)
- ax.set_zlim3d(-0.5, 0.5)
- else:
- miv = 0.7 * np.min([np.min(x), np.min(y), np.min(z)]) # Multiply with 0.7 to squeeze free-space.
- mav = 0.7 * np.max([np.max(x), np.max(y), np.max(z)])
- ax.set_xlim(miv, mav)
- ax.set_ylim(miv, mav)
- ax.set_zlim(miv, mav)
- plt.tight_layout()
-
- if not show_axis:
- plt.axis('off')
-
- if 'c' in kwargs:
- plt.colorbar(sc)
-
- if show:
- plt.show()
-
- return fig
-
-
- def plot_pcd_three_views(filename, pcds, titles, suptitle='', sizes=None, cmap='Reds', zdir='y',
- xlim=(-0.4, 0.4), ylim=(-0.4, 0.4), zlim=(-0.4, 0.4)):
- if sizes is None:
- sizes = [0.2 for i in range(len(pcds))]
- fig = plt.figure(figsize=(len(pcds) * 3, 9))
- for i in range(3):
- elev = 30
- azim = -45 + 90 * i
- for j, (pcd, size) in enumerate(zip(pcds, sizes)):
- color = pcd[:, 0]
- ax = fig.add_subplot(3, len(pcds), i * len(pcds) + j + 1, projection='3d')
- ax.view_init(elev, azim)
- ax.scatter(pcd[:, 0], pcd[:, 1], pcd[:, 2], zdir=zdir, c=color, s=size, cmap=cmap, vmin=-1, vmax=0.5)
- ax.set_title(titles[j])
- ax.set_axis_off()
- ax.set_xlim(xlim)
- ax.set_ylim(ylim)
- ax.set_zlim(zlim)
- plt.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.9, wspace=0.1, hspace=0.1)
- plt.suptitle(suptitle)
- fig.savefig(filename)
- plt.close(fig)
-
-
- # ----------------------------------------
- # Simple Point cloud and Volume Renderers
- # ----------------------------------------
-
- def draw_point_cloud(input_points, canvasSize=500, space=240, diameter=10,
- xrot=0, yrot=0, zrot=0, switch_xyz=[0, 1, 2], normalize=True):
- """ Render point cloud to image with alpha channel.
- Input:
- points: Nx3 numpy array (+y is up direction)
- Output:
- gray image as numpy array of size canvasSizexcanvasSize
- """
- canvasSizeX = canvasSize
- canvasSizeY = canvasSize
-
- image = np.zeros((canvasSizeX, canvasSizeY))
- if input_points is None or input_points.shape[0] == 0:
- return image
-
- points = input_points[:, switch_xyz]
- M = euler2mat(zrot, yrot, xrot)
- points = (np.dot(M, points.transpose())).transpose()
-
- # Normalize the point cloud
- # We normalize scale to fit points in a unit sphere
- if normalize:
- centroid = np.mean(points, axis=0)
- points -= centroid
- furthest_distance = np.max(np.sqrt(np.sum(abs(points) ** 2, axis=-1)))
- points /= (furthest_distance + 1e-7)
-
- # Pre-compute the Gaussian disk
- radius = (diameter - 1) / 2.0
- disk = np.zeros((diameter, diameter))
- for i in range(diameter):
- for j in range(diameter):
- if (i - radius) * (i - radius) + (j - radius) * (j - radius) <= radius * radius:
- disk[i, j] = np.exp((-(i - radius) ** 2 - (j - radius) ** 2) / (radius ** 2))
- mask = np.argwhere(disk > 0)
- dx = mask[:, 0]
- dy = mask[:, 1]
- dv = disk[disk > 0]
-
- # Order points by z-buffer
- zorder = np.argsort(points[:, 2])
- points = points[zorder, :]
- points[:, 2] = (points[:, 2] - np.min(points[:, 2])) / (np.max(points[:, 2] - np.min(points[:, 2])) + 1e-7)
- max_depth = np.max(points[:, 2])
-
- for i in range(points.shape[0]):
- j = points.shape[0] - i - 1
- x = points[j, 0]
- y = points[j, 1]
- xc = canvasSizeX / 2 + (x * space)
- yc = canvasSizeY / 2 + (y * space)
- xc = int(np.round(xc))
- yc = int(np.round(yc))
-
- px = dx + xc
- py = dy + yc
- # image[px, py] = image[px, py] * 0.7 + dv * (max_depth - points[j, 2]) * 0.3
- image[px, py] = image[px, py] * 0.7 + dv * 0.3
-
- val = np.max(image)
- val = np.percentile(image, 99.9)
- image = image / val
- mask = image == 0
-
- image[image > 1.0] = 1.0
- image = 1.0 - image
- # image = np.expand_dims(image, axis=-1)
- # image = np.concatenate((image*0.3+0.7,np.ones_like(image), np.ones_like(image)), axis=2)
- # image = colors.hsv_to_rgb(image)
- image[mask] = 1.0
-
- return image
-
-
- def point_cloud_three_views(points, diameter=5):
- """ input points Nx3 numpy array (+y is up direction).
- return an numpy array gray image of size 500x1500. """
- # +y is up direction
- # xrot is azimuth
- # yrot is in-plane
- # zrot is elevation
- # img1 = draw_point_cloud(points, xrot=90/180.0*np.pi, yrot=0/180.0*np.pi, zrot=0/180.0*np.pi,diameter=diameter)
- # img2 = draw_point_cloud(points, xrot=180/180.0*np.pi, yrot=0/180.0*np.pi, zrot=0/180.0*np.pi,diameter=diameter)
- # img3 = draw_point_cloud(points, xrot=0/180.0*np.pi, yrot=-90/180.0*np.pi, zrot=0/180.0*np.pi,diameter=diameter)
- # image_large = np.concatenate([img1, img2, img3], 1)
-
- img1 = draw_point_cloud(points, zrot=110 / 180.0 * np.pi, xrot=135 / 180.0 * np.pi, yrot=0 / 180.0 * np.pi,
- diameter=diameter)
- img2 = draw_point_cloud(points, zrot=70 / 180.0 * np.pi, xrot=135 / 180.0 * np.pi, yrot=0 / 180.0 * np.pi,
- diameter=diameter)
- img3 = draw_point_cloud(points, zrot=180.0 / 180.0 * np.pi, xrot=90 / 180.0 * np.pi, yrot=0 / 180.0 * np.pi,
- diameter=diameter)
- image_large = np.concatenate([img1, img2, img3], axis=0)
-
- return image_large
-
-
- import random
-
-
- class PointPool:
- """ History of generated images
- Same logic as https://github.com/junyanz/CycleGAN/blob/master/util/image_pool.lua
- """
-
- def __init__(self, pool_size=20):
- self.pool_size = pool_size
- self.points = []
-
- def query(self, point):
- if self.pool_size == 0:
- return point
-
- if len(self.points) < self.pool_size:
- self.points.append(point)
- return point
- else:
- p = random.random()
- if p > 0.5:
- # use old image
- random_id = random.randrange(0, self.pool_size)
- tmp = self.points[random_id].copy()
- self.points[random_id] = point.copy()
- return tmp
- else:
- return point
|