def test_assert_no_infs_or_nans_passthrough(self, value): """Checks that the assert is a passthrough when the flag is False.""" vector_input = (value, ) vector_output = asserts.assert_no_infs_or_nans(vector_input) self.assertIs(vector_input, vector_output)
def test_assert_no_infs_or_nans_raises_exception_for_nan(self, value): """Checks that the assert works for `Inf` or `NaN` values.""" vector_input = (value, ) with self.assertRaisesRegex( # pylint: disable=g-error-prone-assert-raises tf.errors.InvalidArgumentError, "Inf or NaN detected."): self.evaluate(asserts.assert_no_infs_or_nans(vector_input))
def safe_signed_div(a, b, eps=None, name='safe_signed_div'): """Calculates a/b safely. If the tf-graphics debug flag is set to `True`, this function adds assertions to the graph that check whether `abs(b + eps)` is greather than zero, and the division has no `NaN` or `Inf` values. Note: In the following, A1 to An are optional batch dimensions, which must be broadcast compatible. Args: a: A `float` or a tensor of shape `[A1, ..., An]`, which is the nominator. b: A `float` or a tensor of shape `[A1, ..., An]`, which is the denominator with non-negative values. eps: A small `float`, to be added to the denominator. If left `None`, its value is automatically selected using `b.dtype`. name: A name for this op. Defaults to 'safe_signed_div'. Raises: InvalidArgumentError: If tf-graphics debug flag is set and the division causes `NaN` or `Inf` values. Returns: A tensor of shape `[A1, ..., An]` containing the results of division. """ with tf.name_scope(name): a = tf.convert_to_tensor(value=a) b = tf.convert_to_tensor(value=b) if eps is None: eps = asserts.select_eps_for_division(b.dtype) eps = tf.convert_to_tensor(value=eps, dtype=b.dtype) return asserts.assert_no_infs_or_nans(a / (b + nonzero_sign(b) * eps))
def safe_unsigned_div(a, b, eps=None, name='safe_unsigned_div'): """Calculates a/b with b >= 0 safely. If the tfg debug flag TFG_ADD_ASSERTS_TO_GRAPH defined in tfg_flags.py is set to True, this function adds assertions to the graph that check whether b + eps is greather than zero, and the division has no NaN or Inf values. Args: a: A `float` or a tensor of shape `[A1, ..., An]`, which is the nominator. b: A `float` or a tensor of shape `[A1, ..., An]`, which is the denominator. eps: A small `float`, to be added to the denominator. If left as `None`, its value is automatically selected using `b.dtype`. name: A name for this op. Defaults to 'safe_unsigned_div'. Raises: InvalidArgumentError: If tf-graphics debug flag is set and the division causes `NaN` or `Inf` values. Returns: A tensor of shape `[A1, ..., An]` containing the results of division. """ with tf.name_scope(name): a = tf.convert_to_tensor(value=a) b = tf.convert_to_tensor(value=b) if eps is None: eps = asserts.select_eps_for_division(b.dtype) eps = tf.convert_to_tensor(value=eps, dtype=b.dtype) return asserts.assert_no_infs_or_nans(a / (b + eps))
def test_assert_no_infs_or_nans_passthrough(self): """Checks that the assert is a passthrough when the flag is False.""" vector_input = _pick_random_vector() vector_output = asserts.assert_no_infs_or_nans(vector_input) self.assertIs(vector_input, vector_output)
def build_matrices(image, size=3, eps=1e-5, name=None): """Generates the closed form matting Laplacian. Generates the closed form matting Laplacian as proposed by Levin et al. in "A Closed Form Solution to Natural Image Matting". This function also return the pseudo-inverse matrix allowing to retrieve the matting linear coefficient. Args: image: A tensor of shape `[B, H, W, C]`. size: An `int` representing the size of the patches used to enforce smoothness. eps: A small number of type `float` to regularize the problem. name: A name for this op. Defaults to "matting_build_matrices". Returns: A tensor of shape `[B, H - pad, W - pad, size^2, size^2]` containing the matting Laplacian matrices. A tensor of shape `[B, H - pad, W - pad, C + 1, size^2]` containing the pseudo-inverse matrices which can be used to retrieve the matting linear coefficients. The padding `pad` is equal to `size - 1`. Raises: ValueError: If `image` is not of rank 4. """ with tf.compat.v1.name_scope(name, "matting_build_matrices", [image]): image = tf.convert_to_tensor(value=image) eps = tf.constant(value=eps, dtype=image.dtype) shape.check_static(image, has_rank=4) if size % 2 == 0: raise ValueError("The patch size is expected to be an odd value.") pixels = size**2 channels = tf.shape(input=image)[-1] dtype = image.dtype # Extracts image patches. patches = _image_patches(image, size) batches = tf.shape(input=patches)[:-1] patches = tf.reshape(patches, shape=_shape(batches, pixels, channels)) # Creates the data matrix block. ones = tf.ones(shape=_shape(batches, pixels, 1), dtype=dtype) affine = tf.concat((patches, ones), axis=-1) # Creates the regularizer matrix block. diag = tf.sqrt(eps) * tf.eye( channels, batch_shape=(1, 1, 1), dtype=dtype) zeros = tf.zeros(shape=_shape((1, 1, 1), channels, 1), dtype=dtype) regularizer = tf.concat((diag, zeros), axis=-1) regularizer = tf.tile(regularizer, multiples=_shape(batches, 1, 1)) # Creates a matrix concatenating the data and regularizer blocks. mat = tf.concat((affine, regularizer), axis=-2) # Builds the pseudo inverse and the laplacian matrices. inverse = tf.linalg.inv(tf.matmul(mat, mat, transpose_a=True)) inverse = asserts.assert_no_infs_or_nans(inverse) pseudo_inverse = tf.matmul(inverse, affine, transpose_b=True) identity = tf.eye(num_rows=pixels, dtype=dtype) laplacian = identity - tf.matmul(affine, pseudo_inverse) return laplacian, pseudo_inverse
def _laplacian_matrix(image, size=3, eps=1e-5, name="matting_laplacian_matrix"): """Generates the closed form matting Laplacian matrices. Generates the closed form matting Laplacian as proposed by Levin et al. in "A Closed Form Solution to Natural Image Matting". Args: image: A tensor of shape `[B, H, W, C]`. size: An `int` representing the size of the patches used to enforce smoothness. eps: A small number of type `float` to regularize the problem. name: A name for this op. Defaults to "matting_laplacian_matrix". Returns: A tensor of shape `[B, H, W, size^2, size^2]` containing the matting Laplacian matrices. Raises: ValueError: If `image` is not of rank 4. """ with tf.name_scope(name): image = tf.convert_to_tensor(value=image) shape.check_static(image, has_rank=4) if size % 2 == 0: raise ValueError("The patch size is expected to be an odd value.") pixels = size**2 channels = tf.shape(input=image)[-1] dtype = image.dtype patches = tf.image.extract_patches(image, sizes=(1, size, size, 1), strides=(1, 1, 1, 1), rates=(1, 1, 1, 1), padding="VALID") batches = tf.shape(input=patches)[:-1] new_shape = tf.concat((batches, (pixels, channels)), axis=-1) patches = tf.reshape(patches, shape=new_shape) mean = tf.reduce_mean(input_tensor=patches, axis=-2, keepdims=True) demean = patches - mean covariance = tf.matmul(demean, demean, transpose_a=True) / pixels regularizer = (eps / pixels) * tf.eye(channels, dtype=dtype) covariance_inv = tf.linalg.inv(covariance + regularizer) covariance_inv = asserts.assert_no_infs_or_nans(covariance_inv) mat = tf.matmul(tf.matmul(demean, covariance_inv), demean, transpose_b=True) return tf.eye(pixels, dtype=dtype) - (1.0 + mat) / pixels
def safe_cospx_div_cosx(theta: type_alias.TensorLike, factor: type_alias.TensorLike, eps: Optional[type_alias.Float] = None, name: str = 'safe_cospx_div_cosx') -> tf.Tensor: """Calculates cos(factor * theta)/cos(theta) safely. The term `cos(factor * theta)/cos(theta)` has periodic edge cases with division by zero problems, and also zero / zero, e.g. when factor is equal to 1.0 and `theta` is `(n + 1/2)pi`. This function adds signed eps to the angles in both the nominator and the denominator to ensure safety, and returns the correct values in all edge cases. Note: In the following, A1 to An are optional batch dimensions, which must be broadcast compatible. Args: theta: A tensor of shape `[A1, ..., An]`, representing angles in radians. factor: A `float` or a tensor of shape `[A1, ..., An]`. eps: A `float`, used to perturb the angle. If left as `None`, its value is automatically determined from the `dtype` of `theta`. name: A name for this op. Defaults to 'safe_cospx_div_cosx'. Raises: InvalidArgumentError: If tf-graphics debug flag is set and division returns `NaN` or `Inf` values. Returns: A tensor of shape `[A1, ..., An]` containing the resulting values. """ with tf.name_scope(name): theta = tf.convert_to_tensor(value=theta) factor = tf.convert_to_tensor(value=factor, dtype=theta.dtype) if eps is None: eps = asserts.select_eps_for_division(theta.dtype) eps = tf.convert_to_tensor(value=eps, dtype=theta.dtype) # eps will be multiplied with factor next, which can make it zero. # Therefore we multiply eps with min(1/factor, 1e10), which can handle # factors as small as 1e-10 correctly, while preventing a division by zero. eps *= tf.clip_by_value(1.0 / factor, 1.0, 1e10) sign = nonzero_sign(0.5 * np.pi - (theta - 0.5 * np.pi) % np.pi) theta += sign * eps div = tf.cos(factor * theta) / tf.cos(theta) return asserts.assert_no_infs_or_nans(div)
def safe_sinpx_div_sinx(theta, factor, eps=None, name='safe_sinpx_div_sinx'): """Calculates sin(factor * theta)/sin(theta) safely. The term `sin(factor * theta)/sin(theta)` appears when calculating spherical interpolation weights, and it has periodic edge cases causing both zero / zero and division by zero problems. This function adds signed eps to the angles in both the nominator and the denominator to ensure safety, and returns the correct values estimated by l'Hopital rule in the case of zero / zero. Note: In the following, A1 to An are optional batch dimensions, which must be broadcast compatible. Args: theta: A tensor of shape `[A1, ..., An]` representing angles in radians. factor: A `float` or a tensor of shape `[A1, ..., An]`. eps: A `float`, used to perturb the angle. If left as `None`, its value is automatically determined from the `dtype` of `theta`. name: A name for this op. Defaults to 'safe_sinpx_div_sinx'. Raises: InvalidArgumentError: If tf-graphics debug flag is set and the division returns `NaN` or `Inf` values. Returns: A tensor of shape `[A1, ..., An]` containing the resulting values. """ with tf.name_scope(name): theta = tf.convert_to_tensor(value=theta) factor = tf.convert_to_tensor(value=factor, dtype=theta.dtype) if eps is None: eps = asserts.select_eps_for_division(theta.dtype) eps = tf.convert_to_tensor(value=eps, dtype=theta.dtype) # eps will be multiplied with factor next, which can make it zero. # Therefore we multiply eps with min(1/factor, 1e10), which can handle # factors as small as 1e-10 correctly, while preventing a division by zero. eps *= tf.clip_by_value(1.0 / factor, 1.0, 1e10) sign = nonzero_sign(0.5 * np.pi - theta % np.pi) theta += sign * eps div = tf.sin(factor * theta) / tf.sin(theta) return asserts.assert_no_infs_or_nans(div)
def laplacian_weights(image, size=3, eps=1e-5, name=None): """Generates the closed form matting Laplacian weights. Generates the closed form matting Laplacian weights as proposed by Levin et al. in "A Closed Form Solution to Natural Image Matting". Args: image: A tensor of shape `[B, H, W, C]`. size: An `int` representing the size of the patches used to enforce smoothness. eps: A small number of type `float` to regularize the problem. name: A name for this op. Defaults to "matting_laplacian_weights". Returns: A tensor of shape `[B, H, W, size^2, size^2]` containing the matting Laplacian weights . Raises: ValueError: If `image` is not of rank 4. """ with tf.compat.v1.name_scope(name, "matting_laplacian_weights", [image]): image = tf.convert_to_tensor(value=image) shape.check_static(image, has_rank=4) pixels = size**2 channels = tf.shape(input=image)[3] dtype = image.dtype patches = _image_patches(image, size) current_shape = tf.shape(input=patches) new_shape = tf.concat((current_shape[:-1], (pixels, channels)), axis=-1) patches = tf.reshape(patches, shape=new_shape) mean = tf.reduce_mean(input_tensor=patches, axis=-2, keepdims=True) demean = patches - mean covariance = tf.matmul(demean, demean, transpose_a=True) / pixels regularizer = (eps / pixels) * tf.eye(channels, dtype=dtype) covariance_inv = tf.linalg.inv(covariance + regularizer) covariance_inv = asserts.assert_no_infs_or_nans(covariance_inv) mat = _quadratic_form(covariance_inv, demean) return tf.eye(pixels, dtype=dtype) - (1.0 + mat) / pixels