tensorflow find minimum distance to real points - python

I have a Bx3 tensor, foo of B= batch size 3D points. Through some fanciness or another, I get another tensor, bar that is of shape Bx6x3, and where each of the B 6x3 matrices corresponds to a point in foo. That 6x3 matrix consists of 6 complex-valued 3D points. What I would like to do is, for each of my B points, find the closest real-valued point out of the 6 in bar to the corresponding point in foo, ending up with a new Bx3 min_bar consisting of the closest points in bar to points in foo.
In numpy, I can accomplish this feat with masked arrays:
foo = np.array([
[1,2,3],
[4,5,6],
[7,8,9]])
# here bar is only Bx2x3 for simplicity, but the solution generalizes
bar = np.array([
[[2,3,4],[1+0.1j,2+0.1j,3+0.1j]],
[[6,5,4],[4,5,7]],
[[1j,1j,1j],[0,0,0]],
])
#mask complex elements of bar
bar_with_masked_imag = np.ma.array(bar)
candidates = bar_with_masked_imag.imag == 0
bar_with_masked_imag.mask = ~candidates
dists = np.sum(bar_with_masked_imag**2, axis=1)
mindists = np.argmin(dists, axis=1)
foo_indices = np.arange(foo.shape[0])
min_bar = np.array(
bar_with_masked_imag[foo_indices,mindists,:],
dtype=float
)
print(min_bar)
#[[2. 3. 4.]
# [4. 5. 7.]
# [0. 0. 0.]]
However, tensorflow doesn't have masked arrays and such. How do I translate this into tensorflow?

Here is a way to do that:
import tensorflow as tf
import math
def solution_tf(foo, bar):
foo = tf.convert_to_tensor(foo)
bar = tf.convert_to_tensor(bar)
# Get real and imaginary parts
bar_r = tf.cast(tf.real(bar), foo.dtype)
bar_i = tf.imag(bar)
# Mask of all real-valued points
m = tf.reduce_all(tf.equal(bar_i, 0), axis=-1)
# Distance to every corresponding point
d = tf.reduce_sum(tf.squared_difference(tf.expand_dims(foo, 1), bar_r), axis=-1)
# Replace distances of complex points with infinity
d2 = tf.where(m, d, tf.fill(tf.shape(d), tf.constant(math.inf, d.dtype)))
# Find smallest distances
idx = tf.argmin(d2, axis=1)
# Get points with smallest distances
b = tf.range(tf.shape(foo, out_type=idx.dtype)[0])
return tf.gather_nd(bar_r, tf.stack([b, idx], axis=1))
# Test
with tf.Graph().as_default(), tf.Session() as sess:
foo = tf.constant([
[1,2,3],
[4,5,6],
[7,8,9]], dtype=tf.float32)
bar = tf.constant([
[[2,3,4],[1+0.1j,2+0.1j,3+0.1j]],
[[6,5,4],[4,5,7]],
[[1j,1j,1j],[0,0,0]]], dtype=tf.complex64)
sol_tf = solution_tf(foo, bar)
print(sess.run(sol_tf))
# [[2. 3. 4.]
# [4. 5. 7.]
# [0. 0. 0.]]

Related

scipy.stats.norm for array of values with different accuracy in different method

Generate two arrays:
np.random.seed(1)
x = np.random.rand(30, 2)
np.random.seed(2)
x_test = np.random.rand(5,2)
Caluclate scipy.stats.norm axis by axis:
gx0 = scipy.stats.norm(np.mean(x[:,0]), np.std(x[:,0])).pdf(x_test[:,0])
gx1 = scipy.stats.norm(np.mean(x[:,1]), np.std(x[:,1])).pdf(x_test[:,1])
and get:
gx0 = array([1.29928091, 1.1344507 , 1.30920536, 1.10709298, 1.26903949])
gx1 = array([0.29941644, 1.36808598, 1.13817727, 1.34149231, 0.95054596])
Calculate using NumPy broadcasting
gx = scipy.stats.norm(np.mean(x, axis = 0), np.std(x, axis = 0)).pdf(x_test)
and get:
gx = array([[1.29928091, 0.29941644],
[1.1344507 , 1.36808598],
[1.30920536, 1.13817727],
[1.10709298, 1.34149231],
[1.26903949, 0.95054596]])
gx[:,0] and gx0 look like the same, but subtracting one from another gx[:,0] - gx0 will get:
array([-4.44089210e-16, -2.22044605e-16, -4.44089210e-16, 0.00000000e+00,
0.00000000e+00])
Why is that?
Not sure why they calculate the answer to different precisions, but converting the input arrays to 128 bit floats solves the problem:
np.random.seed(1)
x = np.random.rand(30, 2).astype(np.float128)
np.random.seed(2)
x_test = np.random.rand(5,2).astype(np.float128)
...
print(gx[:,0] - gx0)
results in:
[0. 0. 0. 0. 0.]

How to generate 2d gaussian kernel using 2d convolution in python?

From my workout instruction:
A 2D Gaussian can be formed by convolution of a 1D Gaussian with its transpose.
Here is my 1d gaussian function:
def gauss1d(sigma, filter_length=11):
# INPUTS
# # sigma : sigma of gaussian distribution
# # filter_length : integer denoting the filter length
# OUTPUTS
# # gauss_filter : 1D gaussian filter without normalization
rng = range(-int(filter_length/2),int(filter_length/2)+1)
gauss_filter = [np.exp((-x**2) / (2*sigma**2)) for x in rng]
# The formula used above has been given in the instruction.
return np.array(gauss_filter)
And 2d convolution function which performs a 2D convolution between image and filt, image being a 2D image.
def myconv2(image, filt):
# INPUTS
# # image : 2D image, as numpy array of size mxn
# # filt : 1D or 2D filter of size kxl
# OUTPUTS
# img_filtered : 2D filtered image, of size (m+k-1)x(n+l-1)
m, n = image.shape
k, l = filt.shape
offsety = k // 2
offsetx = l // 2
img_filtered = np.zeros((m+k-1, n+l-1), "double")
image = np.pad(image, ((offsety,offsety),(offsetx, offsetx)), mode='constant')
for i in range(offsety, m+offsety):
for j in range(offsetx, n+offsetx):
box_vals = image[ i - offsety : i + offsety+1, j-offsetx: j+offsetx+1]
new_val = np.sum( filt * box_vals)
img_filtered[i][j] = np.sum(new_val)
return img_filtered
A simple presentation of how function works for 5x5 input image and 3x3 filter kernel:
With having following 1d gaussian and its transpose, I call myconv2 function :
sigma = 3
filter_length = 5
gauss = gauss1d(sigma, filter_length).reshape(1,filter_length)
guass
array([[0.18073067, 0.20897821, 0.22058223, 0.20897821, 0.18073067]])
gauss_t = np.transpose(gauss)
gauss_t
array([[0.18073067],
[0.20897821],
[0.22058223],
[0.20897821],
[0.18073067]])
myconv2(gauss, guass_t)
array([[0. , 0. , 0. , 0. , 0. ],
[0. , 0. , 0. , 0. , 0. ],
[0.03986597, 0.04609688, 0.04865652, 0.04609688, 0.03986597],
[0. , 0. , 0. , 0. , 0. ],
[0. , 0. , 0. , 0. , 0. ]])
As you can see its not actually a 2d gaussian kernel and some values are missing.
I don't know what I am missing and what should I consider in my code to reach the goal.
Thanks.
You could just do a matrix multiplication. The convolution should also work, just beware of the padding.
gaus2d = gauss.T # gauss
Your conv2d implementation does not seem to be right. I suggest you to implement a 'valid' convolution (or cross correlation):
simple_valid_cross_correlation(img, filt):
ih, iw = img.shape
fh, fw = filt.shape
result = np.zeros((ih - fh + 1, iw - fw + 1))
for i in range(result.shape[0]):
for j in range(result.shape[1]):
result[i, j] = np.sum(filt * img[i:i+fh, j:j+fw])
return result
gauss_pad = np.pad(gauss.T, ((0, 0), (gauss.shape[1]-1, gauss.shape[1]-1)))
gauss2d = simple_valid_cross_correlation(gauss_pad, gauss)
There is also scipy.signal.convolve2d if you don't want to implement your own conv. I think it may be faster

Could anyone explain the return of linalg.lstsq in details?

Though the linalg.lstsq document is offered. I still feel hard to understand since it is not quite detailed.
x : {(N,), (N, K)} ndarray
Least-squares solution. If b is two-dimensional, the solutions are in
the K columns of x.
residuals : {(1,), (K,), (0,)} ndarray
Sums of residuals; squared Euclidean 2-norm for each column in b -
a*x. If the rank of a is < N or M <= N, this is an empty array. If b
is 1-dimensional, this is a (1,) shape array. Otherwise the shape is
(K,).
rank : int
Rank of matrix a.
s : (min(M, N),) ndarray
Singular values of a.
I tried to observe the output. But I only figure out the rank is 2. For the rest, I don't get why it is so.
x = np.array([0, 1, 2, 3])
y = np.array([-1, 0.2, 0.9, 2.1])
A = np.vstack([x, np.ones(len(x))]).T
print(A)
print('-------------------------')
print(np.linalg.lstsq(A, y, rcond=None))
Gives
[[0. 1.]
[1. 1.]
[2. 1.]
[3. 1.]]
-------------------------
(array([ 1. , -0.95]), array([0.05]), 2, array([4.10003045, 1.09075677]))
I don't understand what the tuples, "(N, ), (N, K), (1,), (K,), (0,), (M, N)", represent in the document.
For example, np.linalg.lstsq(A, y, rcond=None)[0] would be array([ 1. , -0.95]) How does it relate to {(N,), (N, K)}?
Those tuples are the possible shapes of inputs and outputs.
In your example, A.shape = (4, 2) and y.shape = (4,).
Looking at the documentation, M = 4, N = 2, and we are dealing with the cases without K.
So the output's shapes should be x.shape = (N,) = (2,), residuals.shape = (1,), s.shape = (min(M, N),) = (2,).
Let's look at the outputs one at a time
>>> x, residuals, rank, s = np.linalg.lstsq(A, y, rcond=None)
x is the least square solution of A # x = y, so it minimises np.linalg.norm(A # x - y)**2:
>>> A.T # (A # x - y)
array([1.72084569e-15, 2.16493490e-15])
The other outputs are there to tell you how good this solution is and how susceptible it is to numerical errors.
residuals is the squared norm of the mis-match between A # x and y:
>>> np.linalg.norm(A # x - y)**2
0.04999999999999995
>>> residuals[0]
0.04999999999999971
rank is the rank of A:
np.linalg.matrix_rank(A)
2
>>> rank
2
s contains the singular values of A
>>> np.linalg.svd(A, compute_uv=False)
array([4.10003045, 1.09075677])
>>> s
array([4.10003045, 1.09075677])
Are you familiar with the mathematical concepts?

Calculate pixelwise distance of 3D tensor in tensorflow?

I am trying to create a 3d distance map (size: W * H * D) in tensorflow to be used in a loss function for training. I have a ground truth (binary volume of size W * H * D) that I will use to create the distance map, i.e. the value of each pixel of my distance map will be the minimum distance of that pixel to the positive valued (i.e pixel=1) shape in the ground truth.
Having issues with the 3d shape problem as L2.NORM reduce the axis to a 2D shape and making this problem fully differentiable. Any advice or pointers would be much appreciated.
If I understand correctly, you want to compute the distance from each position in the volume to the closest position of a given class. For simplicity, I will assume that the interesting class is labelled with 1, but hopefully you can adapt it to your case if it is different. The code is for TensorFlow 2.0, but should work the same for 1.x.
The simplest way to do this is to compute the distance between all the coordinates in the volume against every coordinate with a 1, and then pick the smallest distance from there. You can do that like this:
import tensorflow as tf
# Make input data
w, h, d = 10, 20, 30
w, h, d = 2, 3, 4
t = tf.random.stateless_uniform([w, h, d], (0, 0), 0, 2, tf.int32)
print(t.numpy())
# [[[0 1 0 0]
# [0 0 0 0]
# [1 1 0 1]]
#
# [[1 0 0 0]
# [0 0 0 0]
# [1 1 0 0]]]
# Make coordinates
coords = tf.meshgrid(tf.range(w), tf.range(h), tf.range(d), indexing='ij')
coords = tf.stack(coords, axis=-1)
# Find coordinates that are positive
m = t > 0
coords_pos = tf.boolean_mask(coords, m)
# Find every pairwise distance
vec_d = tf.reshape(coords, [-1, 1, 3]) - coords_pos
# You may choose a difference precision type here
dists = tf.linalg.norm(tf.dtypes.cast(vec_d, tf.float32), axis=-1)
# Find minimum distances
min_dists = tf.reduce_min(dists, axis=-1)
# Reshape
out = tf.reshape(min_dists, [w, h, d])
print(out.numpy().round(3))
# [[[1. 0. 1. 2. ]
# [1. 1. 1.414 1. ]
# [0. 0. 1. 0. ]]
#
# [[0. 1. 1.414 2.236]
# [1. 1. 1.414 1.414]
# [0. 0. 1. 1. ]]]
This may work well enough for you, although it may not be the most efficient solution. The smartest thing would be to search for the closest positive position in the neighboring area of each position, but that is complicated to do effectively, both in general and more so in a vectorized way in TensorFlow. There are however a couple of ways we can improve on the code above. On the one hand, we know that positions with a 1 will always have zero distance, so computing for those is unnecessary. On the other hand, if the 1 class in the 3D volume represents some kind of dense shape, then we could save some time if we only computed the distances against the surface of that shape. All other positive positions will have necessarily a greater distance to positions outside the shape. So we can do the same thing we were doing, but computing only distances from non-positive positions to positive surface positions. You can do that like this:
import tensorflow as tf
# Make input data
w, h, d = 10, 20, 30
w, h, d = 2, 3, 4
t = tf.dtypes.cast(tf.random.stateless_uniform([w, h, d], (0, 0)) > .15, tf.int32)
print(t.numpy())
# [[[1 1 1 1]
# [1 1 1 1]
# [1 1 0 0]]
#
# [[1 1 1 1]
# [1 1 1 1]
# [1 1 1 1]]]
# Find coordinates that are positive and on the surface
# (surrounded but at least one 0)
t_pad_z = tf.pad(t, [(1, 1), (1, 1), (1, 1)]) <= 0
m_pos = t > 0
m_surround_z = tf.zeros_like(m_pos)
# Go through the 6 surrounding positions
for i in range(3):
for s in [slice(None, -2), slice(2, None)]:
slices = tuple(slice(1, -1) if i != j else s for j in range(3))
m_surround_z |= t_pad_z.__getitem__(slices)
# Surface points are positive points surrounded by some zero
m_surf = m_pos & m_surround_z
coords_surf = tf.where(m_surf)
# Find coordinates that are zero
coords_z = tf.where(~m_pos)
# Find every pairwise distance
vec_d = tf.reshape(coords_z, [-1, 1, 3]) - coords_surf
dists = tf.linalg.norm(tf.dtypes.cast(vec_d, tf.float32), axis=-1)
# Find minimum distances
min_dists = tf.reduce_min(dists, axis=-1)
# Put minimum distances in output array
out = tf.scatter_nd(coords_z, min_dists, [w, h, d])
print(out.numpy().round(3))
# [[[0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 1. 1.]]
#
# [[0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]]]
EDIT: Here is one way in which you can divide the distance computations in chunks with a TensorFlow loop:
# Following from before
coords_surf = ...
coords_z = ...
CHUNK_SIZE = 1_000 # Choose chunk size
dtype = tf.float32
# If using TF 2.x you can know in advance the size of the tensor array
# (although the element shape will not be constant due to the last chunk)
num_z = tf.shape(coords_z)[0]
arr = tf.TensorArray(dtype, size=(num_z - 1) // CHUNK_SIZE + 1, element_shape=[None], infer_shape=False)
_, arr = tf.while_loop(lambda i, arr: i < num_z,
lambda i, arr: (i + CHUNK_SIZE, arr.write(i // CHUNK_SIZE,
tf.reduce_min(tf.linalg.norm(tf.dtypes.cast(
tf.reshape(coords_z[i:i + CHUNK_SIZE], [-1, 1, 3]) - coords_surf,
dtype), axis=-1), axis=-1))),
[tf.constant(0, tf.int32), arr])
min_dists = arr.concat()
out = tf.scatter_nd(coords_z, min_dists, [w, h, d])

Using homogeneous transforms (non-affine) with matplotlib patches

I have an application where I am using matplotlib to display elliptical regions on an image. To do this I'm using mpl.patches.Circle along with mp.patches.Affine2D to warp unit circles into an elliptical shape.
import numpy as np
import matplotlib as mpl
import pyplot as plt
invVR_mats = np.array([
[[ 7.80247545, 0. , 92.9254837 ],
[ -3.46026921, 10.85727882, 17.53866959],
[ 0. , 0. , 1. ]],
[[ 11.42656994, 0. , 76.86006927],
[ -3.26515651, 9.61946297, 24.79440498],
[ 0. , 0. , 1. ]],
[[ 10.40444851, 0. , 140.62428284],
[ -10.94557095, 10.59212685, 24.91024971],
[ 0. , 0. , 1. ]],])
invVR_aff2Ds = [mpl.transforms.Affine2D(invVR)
for invVR in invVR_mats]
ell_actors = [mpl.patches.Circle((0, 0), 1, transform=invVR)
for invVR in invVR_aff2Ds]
coll = mpl.collections.PatchCollection(ell_actors)
plt.figure()
ax = plt.gca()
ax.set_ylim(0, 100)
ax.set_xlim(0, 300)
ax.add_collection(coll)
There is a point in my application that the ellipses in one image are put in correspondence with ellipses from a second image using a homography matrix. So far I have been using it to warp points from image1 into image2.
I would like to get a visual idea of how these ellipses warp into image2. I can transform my affine matrices with this homography matrix, but the resulting matrix is no longer affine. (I believe it represents a general conic, either a circle, ellipse, hyperbola, or parabola)
from numpy.core.umath_tests import matrix_multiply
H = np.array([[ -0.70098, 0.12273, 5.18734],
[ 0.12444, -0.63474, 14.13995],
[ 0.00004, 0.00025, -0.64873]])
HinvVR_mats = matrix_multiply(H, invVR_mats)
print(HinvVR_mats)
#---------
np.array([
[[ -5.89405808e+00, 1.33251383e+00, -5.77990446e+01],
[ 3.16731132e+00, -6.89154916e+00, 1.45711021e+01],
[ -5.52968284e-04, 2.71431970e-03, -6.40628313e-01]],
[[ -8.41052966e+00, 1.18059669e+00, -4.56470140e+01],
[ 3.49444781e+00, -6.10585793e+00, 7.96641640e+00],
[ -3.59226330e-04, 2.40486574e-03, -6.39456996e-01]],
[[ -8.63666024e+00, 1.29997173e+00, -9.03302348e+01],
[ 8.24232128e+00, -6.72324660e+00, 1.58277039e+01],
[ -2.32021480e-03, 2.64803171e-03, -6.36877466e-01]]])
If I de-homogenize only the last column I can find the center of where the ellipse was projected, but I would like to see some shape information as well.
So far the best I've done is just de-homogenizing the last column and ignoring the values in [:, 2, 0] and [:, 2, 1]
HinvVR_mats = np.divide(HinvVR_mats , HinvVR_mats[:, None, None, 2, 2])
print(HinvVR_mats)
array([[[ 9.20043332e+00, -2.08001083e+00, 9.02224323e+01],
[ -4.94407015e+00, 1.07574845e+01, -2.27450173e+01],
[ 8.63165541e-04, -4.23696494e-03, 1.00000000e+00]],
[[ 1.31526118e+01, -1.84624877e+00, 7.13840248e+01],
[ -5.46471120e+00, 9.54850438e+00, -1.24580956e+01],
[ 5.61767769e-04, -3.76079354e-03, 1.00000000e+00]],
[[ 1.35609449e+01, -2.04116458e+00, 1.41832989e+02],
[ -1.29417694e+01, 1.05565779e+01, -2.48520394e+01],
[ 3.64311021e-03, -4.15783546e-03, 1.00000000e+00]]])
Is there a way I can tranform mpl.patches.Circle (or any other patch for that matter) using a non-affine matrix. The documentation seems to suggest it is possible, but I'm not seeing any way to go about it.
I have
I was able to solve this by looking at the tutorial posted by tcaswell
I had to create my own tranformation class though which looked like this
class HomographyTransform(mpl.transforms.Transform):
"""
References:
http://stackoverflow.com/questions/28401788/using-homogeneous-transforms-non-affine-with-matplotlib-patches?noredirect=1#comment45156353_28401788
http://matplotlib.org/users/transforms_tutorial.html
"""
input_dims = 2
output_dims = 2
is_separable = False
def __init__(self, H, axis=None, use_rmin=True):
mpl.transforms.Transform.__init__(self)
self._axis = axis
self._use_rmin = use_rmin
self.H = H
def transform_non_affine(self, input_xy):
"""
The input and output are Nx2 numpy arrays.
"""
import vtool as vt
_xys = input_xy.T
xyz = vt.add_homogenous_coordinate(_xys)
xyz_t = vt.matrix_multiply(self.H, xyz)
xy_t = vt.remove_homogenous_coordinate(xyz_t)
output_xy = xy_t.T
return output_xy
#transform_non_affine.__doc__ = mpl.transforms.Transform.transform_non_affine.__doc__
def transform_path_non_affine(self, path):
vertices = path.vertices
if len(vertices) == 2 and vertices[0, 0] == vertices[1, 0]:
return mpl.path.Path(self.transform(vertices), path.codes)
ipath = path.interpolated(path._interpolation_steps)
return mpl.path.Path(self.transform(ipath.vertices), ipath.codes)
#transform_path_non_affine.__doc__ = mpl.transforms.Transform.transform_path_non_affine.__doc__
The functions called by my own library vtool are:
def add_homogenous_coordinate(_xys):
assert _xys.shape[0] == 2
_zs = np.ones((1, _xys.shape[1]), dtype=_xys.dtype)
_xyzs = np.vstack((_xys, _zs))
return _xyzs
def remove_homogenous_coordinate(_xyzs):
assert _xyzs.shape[0] == 3
_xys = np.divide(_xyzs[0:2], _xyzs[None, 2])
return _xys
and matrix_multiply is the same matrix_multiply used earlier.
and my function to create the transform matrices currently looks like this:
def get_invVR_aff2Ds(kpts, H=None):
""" Returns matplotlib keypoint transformations (circle -> ellipse) """
#invVR_mats = ktool.get_invV_mats(kpts, with_trans=True, with_ori=True)
invVR_mats = ktool.get_invVR_mats3x3(kpts)
if H is None:
invVR_aff2Ds = [mpl.transforms.Affine2D(invVR)
for invVR in invVR_mats]
else:
# not actually affine
invVR_aff2Ds = [HomographyTransform(H.dot(invVR))
for invVR in invVR_mats]
return invVR_aff2Ds

Categories