def on_cone_rotation(theta_image, normal_image, s): theta = theta_image.as_vector() nprime = normal_image.as_vector(keep_channels=True) # cross product and break in to row vectors C = np.cross(nprime, s) C = normalise_vector(C) u = C[:, 0] v = C[:, 1] w = C[:, 2] # expects |nprime| = |sec| = 1 # represents intensity and can never be < 0 d = np.squeeze(np.inner(nprime, s) / (row_norm(nprime) * row_norm(s))) d = np.nan_to_num(d) d[d < 0.0] = 0.0 beta = np.arccos(d) # flip beta and theta so that it rotates along the correct axis alpha = beta - theta c = np.cos(alpha) cprime = 1.0 - c s = np.sin(alpha) # setup structures N = nprime.shape[0] phi = np.zeros([N, 3, 3]) phi[:, 0, 0] = c + u ** 2 * cprime phi[:, 0, 1] = -w * s + u * v * cprime phi[:, 0, 2] = v * s + u * w * cprime phi[:, 1, 0] = w * s + u * v * cprime phi[:, 1, 1] = c + v ** 2 * cprime phi[:, 1, 2] = -u * s + v * w * cprime phi[:, 2, 0] = -v * s + u * w * cprime phi[:, 2, 1] = u * s + v * w * cprime phi[:, 2, 2] = c + w ** 2 * cprime n = np.einsum('kjl, klm -> kj', phi, nprime[..., None]) # Normalize the result ?? n = normalise_vector(n) return normal_image.from_vector(n)
def expmap(self, tangent_vectors): # If we've been passed a single vector to map, then add the extra axis # Number of sample first if len(tangent_vectors.shape) < 3: tangent_vectors = tangent_vectors[None, ...] # Expmap v1 = tangent_vectors[..., 0] v2 = tangent_vectors[..., 1] normv = row_norm(tangent_vectors) exp = np.concatenate([(v1 * np.sin(normv) / normv)[..., None], (v2 * np.sin(normv) / normv)[..., None], np.cos(normv)[..., None]], axis=2) near_zero_ind = normv < np.spacing(1) exp[near_zero_ind, :] = [0.0, 0.0, 1.0] # Rotate back to geodesic mean from north pole # Apply inverse rotation matrix due to data ordering ns = np.einsum('fvi, ijv -> fvj', exp, self.rotation_matrices) return ns