def __init__(self): # The vectorized coordinate functions self.X = vmap(self._X) # (N x 2) -> N self.Y = vmap(self._Y) # (N x 2) -> N self.Z = vmap(self._Z) # (N x 2) -> N # _T,_N,_B are unvectorized functions that yield the unit tangent, normal # and bi-normal vectors of the curve at t. self._T = lambda t: normalize_vector(jnp.array(jacfwd(self._f)(t))) self._N = lambda t: normalize_vector(jnp.array(jacfwd(self._T)(t))) self._B = lambda t: normalize_vector(jnp.cross(self._T(t), self._N(t))) # Vectorized TNB self.T = vmap(self._T) self.N = vmap(self._N) self.B = vmap(self._B)
def _XYZ(self, uv): """ The CMFs place a wavelength parameter into a space curve (the spectral locus) multiplying by intensity shifts to a position on a surface (the spectral cone) """ intensity, wavelength = uv x = intensity * CIEXfitSimple(wavelength) y = intensity * CIEYfitSimple(wavelength) z = intensity * CIEZfitSimple(wavelength) return self.XYZ_to_outputspace(jnp.array([x, y, z]))
def _XYZ(self, uv): # https://math.stackexchange.com/questions/461547/whats-the-equation-of-helix-surface theta, t = uv f = self.curve._f N = self.curve._N B = self.curve._B r = self.radius XYZ = jnp.array( f(t)) + r * N(t) * jnp.cos(theta) + r * B(t) * jnp.sin(theta) return XYZ
def _N(uv): dfdu, dfdv = jnp.array(jacfwd(self._f)(uv)).swapaxes(0, 1) n = jnp.cross(dfdu, dfdv) return n / jnp.linalg.norm(n)
def down_right_shifted_conv_transpose(inputs): out_h, out_w = np.multiply(np.array(inputs.shape[-3:-1]), np.array(strides or (1, 1))) inputs = ConvTranspose(out_chan, filter_shape, strides, 'VALID', **kwargs)(inputs) return inputs[:, :out_h, :out_w]
# Simple Analytic (Differentiable!) Approximations to the CIE XYZ # Color Matching Functions def CIEXfitSimple(wave): t1 = (wave - 595.8) / 33.33 t2 = (wave - 446.88) / 19.44 return 1.065 * jnp.exp(-0.5 * t1 * t1) + 0.366 * jnp.exp(-0.5 * t2 * t2) def CIEYfitSimple(wave): t1 = (jnp.log(wave) - jnp.log(556.3)) / 0.075 return 1.014 * jnp.exp(-0.5 * t1 * t1) def CIEZfitSimple(wave): t1 = (jnp.log(wave) - jnp.log(449.8)) / 0.051 return 1.839 * jnp.exp(-0.5 * t1 * t1) sRGB_primaries = \ jnp.array([ [ 0.41231515, 0.2126, 0.01932727], [ 0.3576 , 0.7152, 0.1192 ], [ 0.1805 , 0.0722, 0.95063333]]) def xyz_to_srgb(xyz): primary_inverse = jnp.linalg.inv(sRGB_primaries) return xyz.T.dot(primary_inverse) # visible wavelengths of light visual_range_nm = [400.0, 700.0]
def __init__(self, r0=[1, 1, 1], v=[1, 0.5, 0]): self.r0 = jnp.array(r0) self.v = jnp.array(v) super().__init__()