After projecting 3D points to 2D, how to get back to 3D? - camera

Simple question: I used a translation and rotation matrix and camera intrinsics matrix to get a 3x4 matrix used to transform 3d points to 2d points (notated as Tform)
I transformed the point [10,-5,1] with the matrix by adding one to the end, and the new point is notated as newpoint.
Now I want to use the newpoint data to transform back to 3D space, where old_est should be equal to old.
I'm looking for the solution to plug into the XXX matrix in my code below
import numpy as np
Tform=np.array([[4000,0,-1600,-8000],[500,5000,868,-8000],[.5,0,.8,-8]])
old=np.array([10,-5,1,1])
newpoint=np.dot(Tform,old)
print(newpoint)
old_est=np.dot(XXX,np.append(newpoint,1))
print(old_est)

Add a 4th row to Tform with the values 0 0 0 1, i.e. the last row of an identity matrix:
>>> m = np.vstack(Tform, np.array([0,0,0,1]))
>>> m
array([[ 4.00e+03, 0.00e+00, -1.60e+03, -8.00e+03],
[ 5.00e+02, 5.00e+03, 8.68e+02, -8.00e+03],
[ 5.00e-01, 0.00e+00, 8.00e-01, -8.00e+00],
[ 0.00e+00, 0.00e+00, 0.00e+00, 1.00e+00]])
Note that you cannot use append because it also flattens the input arrays.
Observe that, when multiplied with old, the 4th component of the result is 1, i.e. the result is equal to np.append(newpoint, 1):
>>> np.dot(m, old)
array([ 3.0400e+04, -2.7132e+04, -2.2000e+00, 1.0000e+00])
----------
It follows that XXX is the inverse of this new matrix:
>>> XXX = np.linalg.inv(m)
>>> np.dot(XXX, np.append(newpoint, 1))
array([10., -5., 1., 1.])
-------------
And we get the components of old back.
Alternatively you can subtract the 4th column of Tform from newpoint and multiply the result with the inverse of the left 3x3 sub-matrix of Tform, but this is slightly fiddly so we might as well let numpy do more of the work :)

Related

Using numpy einsum to perform high dimensional subtraction broadcasting

I'm having troubles in using a broadcasting subtraction. My problem is the following. I have an array x of shape [L,N], where L is an integer and N is the number of variables of my problem.
I need to compute a [L,N,N] array where at each element l,i,j it contains x[l,i]-x[l,j].
If L=1 this is equivalent to run broadcasting on subtraction: x-x.T
For example here with L=1 and N=3:
import numpy as np
x = np.array([[0,2,4]])
x-x.T
However, if one increases the dimension L things become more complicated and enter the realm of the np.einsum function.
So I tried to recreate my example, in the case L=2, where I've replicated the two rows. What I'd expect is to get a 2x3x3 array with two 3x3 matrices with equal elements.
x = np.array([[0,2,4],[0,2,4]])
n = 3
k = 2
X = np.zeros([k,n,n])
for l in range(k):
for i in range(n):
for j in range(n):
X[l,i,j] = x[l,i]-x[l,j]
print(X)
which returns
[[[ 0. -2. -4.]
[ 2. 0. -2.]
[ 4. 2. 0.]]
[[ 0. -2. -4.]
[2. 0. -2.]
[ 4. 2. 0.]]]
But how to make this with numpy einsum? I can only obtain the product:
np.einsum('ki,kj->kij',x,-x)
Are there specific examples of numpy batched subtractions or additions with increased dimension?

why the difference between numpy matrix and numpy array when selecting element

I have a calculated matrix
from numpy import matrix
vec=matrix([[ 4.79263398e-01+0.j , -2.94883960e-14+0.34362808j,
5.91036823e-01+0.j , -2.06730654e-14+0.41959935j,
-3.20298698e-01+0.08635809j, -5.97136351e-02+0.22325523j],
[ 9.45394208e-14+0.34385164j, 4.78941900e-01+0.j ,
1.07732017e-13+0.41891016j, 5.91969770e-01+0.j ,
-6.06877417e-02-0.2250884j , 3.17803028e-01+0.08500215j],
[ 4.63795513e-01-0.00827114j, -1.15263719e-02+0.33287485j,
-2.78282097e-01-0.20137267j, -2.81970922e-01-0.1980647j ,
9.26109539e-02-0.38428445j, 5.12483437e-01+0.j ],
[ -1.15282610e-02+0.33275927j, 4.63961516e-01-0.00826978j,
-2.84077490e-01-0.19723838j, -2.79429184e-01-0.19984041j,
-4.42104809e-01+0.25708681j, -2.71973825e-01+0.28735795j],
[ 4.63795513e-01+0.00827114j, 1.15263719e-02+0.33287485j,
-2.78282097e-01+0.20137267j, 2.81970922e-01-0.1980647j ,
2.73235786e-01+0.28564581j, -4.44053596e-01-0.25584307j],
[ 1.15282610e-02+0.33275927j, 4.63961516e-01+0.00826978j,
2.84077490e-01-0.19723838j, -2.79429184e-01+0.19984041j,
5.11419878e-01+0.j , -9.22028113e-02-0.38476356j]])
I want to get 2nd row, 3rd column element
vec[1][2]
IndexError: index 1 is out of bounds for axis 0 with size 1
and slicing works well
vec[1,2]
(1.07732017e-13+0.41891015999999998j)
My first question why first way does not work in this case? it worked before when I used it.
Second question is: the result of slicing is an array, how to make it an complex value without bracket? My experience was using
vec[1,2][0]
but again it is not working here.
I tried to do everything on numpy array at begining, those methods that do not work on numpy matrix work on numpy array. Why there are such differences?
The key difference is that a matrix is always 2d, always. (This is supposed to be familiar to MATLAB users.)
In [85]: mat = np.matrix('1,2;3,4')
In [86]: mat
Out[86]:
matrix([[1, 2],
[3, 4]])
In [87]: mat.shape
Out[87]: (2, 2)
In [88]: mat[1]
Out[88]: matrix([[3, 4]])
In [89]: _.shape
Out[89]: (1, 2)
Selecting a row of mat returns a matrix - a 1 row one. It should be clear that it cannot be indexed again with [1].
Indexing with the tuple returns a scalar:
In [90]: mat[1,1]
Out[90]: 4
In [91]: type(_)
Out[91]: numpy.int32
As a general rule operations on a np.matrix returns a matrix or a scalar, not a np.ndarray.
The other key point is that mat[1][1] is not one numpy operation. It is two, a mat[1] followed by another [1]. Imagine yourself to be a Python interpreter without any special knowledge of numpy. How would you evaluate that expression?
Now for the complex question:
In [92]: mat = np.matrix('1+3j, 2;-2, 2+1j')
In [93]: mat
Out[93]:
matrix([[ 1.+3.j, 2.+0.j],
[-2.+0.j, 2.+1.j]])
In [94]: mat[1,1]
Out[94]: (2+1j)
In [95]: type(_)
Out[95]: numpy.complex128
As expected the tuple index has returned a scalar numpy element. () is just part of numpys way of displaying a complex number.
We can use item to extra python equivalent, but the display still uses ()
In [96]: __.item()
Out[96]: (2+1j)
In [97]: type(_)
Out[97]: complex
In [98]: 1+3j
Out[98]: (1+3j)
mat has A property that gives the array equivalent. But notice the shapes.
In [99]: mat.A # a 2d array
Out[99]:
array([[ 1.+3.j, 2.+0.j],
[-2.+0.j, 2.+1.j]])
In [100]: mat.A1 # a 1d array
Out[100]: array([ 1.+3.j, 2.+0.j, -2.+0.j, 2.+1.j])
In [101]: mat[1].A
Out[101]: array([[-2.+0.j, 2.+1.j]])
In [102]: mat[1].A1
Out[102]: array([-2.+0.j, 2.+1.j])
Sometimes this behavior of matrix is handy. For example np.sum acts like the array keepdims=True:
In [108]: np.sum(mat,1)
Out[108]:
matrix([[ 3.+3.j],
[ 0.+1.j]])
In [110]: np.sum(mat.A,1, keepdims=True)
Out[110]:
array([[ 3.+3.j],
[ 0.+1.j]])

Creating a new matrix from a tensor in tensorflow

Let's say I have a (7,7,3) image with 3 channels.
How can I create a matrix A using the image such that the each row consist of just the pairwise connections of the neighbouring pixel? I know this can be done easily in python with for loops but how can we do this within a tensorflow graph?
Example of matrix A:
[[pixel1 pixel2],
[pixel1 pixel3],
[pixel1 pixel4],
.....so on ]]
You can do this using some matrix algebra. To illustrate the idea, suppose you wanted to do this for a 1D vector.
You can stack the vector with a shifted version of itself to get pairs of neighbors
n = 5
a = tf.range(n)
left = tf.stack([a[1:], a[:n-1]])
left = tf.transpose(left)
By chopping off the tails and repeating for different offset you can get left neighbors and right neighbors
right = tf.stack([a[:n-1], a[1:]])
right = tf.transpose(right)
To ignore edge effects you can chop off the ends and stack again into rank-3 matrix
stacked_neighbors = tf.stack([left[:-1], right[1:]])
Now to interleave the neighbors we can use a trick with transpose and reshape.
stacked_neighbors = tf.transpose(stacked_neighbors, [1, 0, 2])
Since data storage is in row-major order, reshaping into less dimensions than original, reshape flattens excess dimensions on the left
stacked_neighbors = tf.reshape(stacked_neighbors, [6,2])

Confused about (x,y) order with RectBivariateSpline

I am getting confused about the argument order with RectBivariateSpline. I am reading a set of 2D data which has 343 values along the X axis and 373 values along the Y axis. The routine that reads the data returns it in the "correct" sense such that when I plot it it matplotlib I get a map that has the correct physical orientation. It also returns the X values in an array of 343 elements and the Y values in in array of 373 elements which makes sense.
The scipy documentation for RectBivariateSpline gives the arguments as:
scipy.interpolate.RectBivariateSpline(x, y, z)
However, when I execute
spln = scipy.interpolate.RectBivariateSpline(xval, yval, zval)
I get this error:
TypeError: x dimension of z must have same number of elements as x
I can remove the error by executing
spln = scipy.interpolate.RectBivariateSpline(yval, xval, zval)
but now the x and y values are the wrong way round (in a physical sense at least). Does this mean that the x argument to RectBivariateSpline refers to the first data dimension of the dataset rather than the physical x dimension? I am used to working with data in Fortran-style ordering, which probably is not helping.
In answer to hpaulj's comment, the shapes of the various arrays are:
xval (343,)
yval (373,)
zval (373, 343)
I think the issue is that I am getting confused between 'xy' and 'ij' ordering. Matpoltlib seems to be using 'xy' ordering so I guess I just need to be careful to transpose the ZVAL array when interpolating using scipy
Show us the values of xval.shape, yval.shape and zval.shape
Early in the RectBivariateSpline code it does:
x, y = ravel(x), ravel(y)
....
if not x.size == z.shape[0]:
raise TypeError('x dimension of z must have same number of '
'elements as x')
if not y.size == z.shape[1]:
raise TypeError('y dimension of z must have same number of '
'elements as y')
So the number of rows of z (1st dimension) must match the number of elements in x.
When you display a 2d array, rows are the 1st dimension, going down the page, columns the 2nd. But in a plot, we often expect the first axis, the x one to go across the page.
np.meshgrid lets you specify:
indexing : {'xy', 'ij'}, optional
Cartesian ('xy', default) or matrix ('ij') indexing of output.
See Notes for more details.
The difference between 'xy' indexing and 'ij' indexing might be confusing you. This Spline class is using the 'ij' kind.
An alternative to switching x and y is to use the transpose of z, z.T. Keep in mind that the interpolation points follow the same ordering rules.
Simple example
In [30]: x=np.arange(10)
In [31]: y=np.arange(15)
In [32]: z=x[:,None]*y[None,:]
In [33]: S=interpolate.RectBivariateSpline(x,y,z)
In [34]: S([1,2,3],[4,5,6])
Out[34]:
array([[ 4., 5., 6.],
[ 8., 10., 12.],
[ 12., 15., 18.]])
Contrast the xy v ij in meshgrid:
In [37]: np.meshgrid(x,y)[0].shape
Out[37]: (15, 10)
In [38]: np.meshgrid(x,y,indexing='ij')[0].shape
Out[38]: (10, 15)
z could have been constructed from the ij grids.
X,Y=np.meshgrid(x,y,indexing='ij')
Z = X*Y

Indexing the last dimension of a 3D array with a 2D integer array

I have one 3D data = NxMxD numpy array, and another 2D idx = NxM integer array which values are in the range of [0, D-1]. I want to perform basic updates to each data = NxM entry at the depth given by the idx array at that position.
For example, for N = M = D = 2:
data = np.zeros((2,2,2))
idx = np.array([[0,0],[1, 1]], int)
And I want to perform a simple operation like:
data[..., idx] += 1
My expected output would be:
>>> data
array([[[ 1., 0.],
[ 1., 0.]],
[[ 0., 1.],
[ 0., 1.]]])
idx indicates for each 2D coordinate which D should be updated. The above operation doesn't work.
I've found this approach in SO which solves the indexing problem by using:
data[np.arange(N)[:, None], np.arange(M)[None, :], idx] += 1
It works fine, but looks pretty horrible needing to manually index the whole matrices for what it seems a pretty simple operation (using one matrix as a index mask for the last channel).
Is there any better solution?
With numpy.ix_ it does not look so horrible but the underlying idea using fancy indexing is still the same
x = np.arange(N)
y = np.arange(M)
xx,yy = np.ix_(x,y)
data[xx,yy,idx] += 1
Note
The problem is that you want to change the values of data. If you just wanted to have the values according to idx you could do
out = np.choose(idx,data.transform(2,0,1))
However, this gives you a copy of the values of data and not a view which means that
out += 1
has no effect on your values in data.