How to compute an affine transformation of multiple points? - python

I have a 3d numpy array of point (484,3,1) and a 2d transformation matrix (3,3). I want to compute the transformation for all 484 points.
I have tried to reshape the arrays and compute the dot product, but I am struggling to get it to output a (484,3,1) shaped array where all the points are transformed.
points = np.random.randint(0, 979, (484,3,1))
transformation = array([[0.94117647, 0. , 0. ],
[0. , 0.94117647, 0. ],
[0. , 0. , 1. ]])
points.shape = (484,3,1)
transformation = (3,3)
transformation.dot(points).shape = (3,484,1)
I would like this to be as optimized as possible. Any advice would be greatly appreciated.

Just do a reshape to (484,3) dimensions and use the np.matmul (np.dot is also possible but since you are looking for a matrix multiplication matmul is prefered according to the documentation) product
np.matmul(points.reshape(484,-1), transformation).reshape(484,3,-1)
resulting shape is the same of course given by the last reshaping: (484,3,1)

Related

How to make a affine transform matrix to a perspective transform matrix?

From code
rotation = cv2.getRotationMatrix2D((0, 0), 47.65, 1.0)
I got a rotation transform matrix like:
[[ 0.67365771 0.7390435 0. ]
[-0.7390435 0.67365771 0. ]]
Since rotation is a special case of affine transform, I think this is a valid affine transform matrix, am I right?
Since affine transform is a special case of perspective transform, I also think this matrix will be a valid perspective transform matrix, if I make some modification based on it.
So I tried to add 1 more row to make it shape as 3 x 3.
newrow = numpy.array([numpy.array([1, 1, 1])]) # [[0 0 0]]
rotation3 = numpy.append(rotation, newrow, axis=0)
print(rotation3):
[[ 0.67365771 0.7390435 0. ]
[-0.7390435 0.67365771 0. ]
[ 1. 1. 1. ]]
But rotation3 does not seem to work properly as a perspective matrix, here is how I tested it:
rotated_points = cv2.perspectiveTransform(points, rotation3)
rotated_points does not look like a rotaion of points
Is [1, 1, 1] the correct row 3, should I also change row 1 and 2? and how can I do it?
Basically you are right, the affine transform is a special case of the perspective transform.
The perspective transform of a identity matrix results in no change to the output:
(identity 3x3 matrix)
[1,0,0]
[0,1,0]
[0,0,1]
So if you want a affine transformation matrix to grow to a perspective one you want to add the last line of this identity matrix.
Your example would look like:
[ 0.67365771 0.7390435 0. ]
[-0.7390435 0.67365771 0. ]
[ 0. 0. 1. ]
Applying the above perspective mat has the same effect as if you would apply a affine transform with:
[ 0.67365771 0.7390435 0. ]
[-0.7390435 0.67365771 0. ]
--> have a look at
affie transformation wikipedia
Opencv generate identity matrix
identity matrix wikipedia

TensorFlow outer product of two 2-rank tensors

Suppose I have a rank two tensor, [[a,b],[c,d]](in general a m-by-n matrix).
I want to expand every element by a 2-2 identity matrix(outer product) and result in
[[a, 0, b, 0],[0,a,0,b],[c,0,d,0],[0,c,0,d]].
What is the most efficient way to implement it in tensorflow?
This operation appears a lot in the framework.
I would like to do this in 2-step process.
If I have a m-by-n matrix, and a 2-2 identity matrix.
First I will enlarge (repeat) the matrix to "2m-2n matrix"
and then multiply by the enlarged identity matrix (2m-2n matrix). As following shows.
import tensorflow as tf
#Process 1 repeat the tensor in 2D.
#e.g. [1,2,3,4] --> [[1,1,2,2],[1,1,2,2],[3,3,4,4],[3,3,4,4]]
# assuming m x n idenity matrix e.g. [1,2,3],[4,5,6]] , m=2,n=3
id_matrix_size=2 # size of identity matrix (e.g. 2x2 3x3 ...)
m=2
n=3
mytensor=tf.constant([[1,2,3],[4,5,6] ],dtype = tf.float32)
# similar to np.repeat in x-dimension.
flatten_mytensor=tf.reshape(mytensor,[-1,1])
a=tf.tile(flatten_mytensor, [1, id_matrix_size])
b=tf.reshape( a, [m,-1])
# tile in y-dimension
c=tf.tile(b,[1,id_matrix_size])
d=tf.reshape(c,[id_matrix_size*m,id_matrix_size*n])
#Process 2 tile identity matrix in 2D.
identity_matrix=tf.eye(id_matrix_size) # identity matrix
identity_matrix_2D= tf.tile(identity_matrix,[m,n])
#Process 3 elementwise multiply
output = tf.multiply(d,identity_matrix_2D )
with tf.Session() as sess:
print(sess.run(output) )
#output :
#[[1. 0. 2. 0. 3. 0.]
# [0. 1. 0. 2. 0. 3.]
# [4. 0. 5. 0. 6. 0.]
# [0. 4. 0. 5. 0. 6.]]
And also, a def is more convenient if lots of implements are needed.
def Expand_tensor(mytensor,id_matrix_size):
m=mytensor.shape[0]
n=mytensor.shape[1]
# similar to np.repeat in x-dimension.
flatten_mytensor=tf.reshape(mytensor,[-1,1])
a=tf.tile(flatten_mytensor, [1, id_matrix_size])
b=tf.reshape( a, [m,-1])
# tile in y-dimension
c=tf.tile(b,[1,id_matrix_size])
d=tf.reshape(c,[id_matrix_size*m,id_matrix_size*n])
# tile identity matrix in 2D.
identity_matrix=tf.eye(id_matrix_size) # identity matrix
identity_matrix_2D= tf.tile(identity_matrix,[m,n])
#elementwise multiply
output = tf.multiply(d,identity_matrix_2D )
return output
mytensor=tf.constant([[1,2,3],[4,5,6] ],dtype = tf.float32)
with tf.Session() as sess:
print(sess.run(Expand_tensor(mytensor,2)) )

Avoid using for loop. Python 3

I have an array of shape (3,2):
import numpy as np
arr = np.array([[0.,0.],[0.25,-0.125],[0.5,-0.125]])
I was trying to build a matrix (matrix) of dimensions (6,2), with the results of the outer product of the elements i,i of arr and arr.T. At the moment I am using a for loop such as:
size = np.shape(arr)
matrix = np.zeros((size[0]*size[1],size[1]))
for i in range(np.shape(arr)[0]):
prod = np.outer(arr[i],arr[i].T)
matrix[size[1]*i:size[1]+size[1]*i,:] = prod
Resulting:
matrix =array([[ 0. , 0. ],
[ 0. , 0. ],
[ 0.0625 , -0.03125 ],
[-0.03125 , 0.015625],
[ 0.25 , -0.0625 ],
[-0.0625 , 0.015625]])
Is there any way to build this matrix without using a for loop (e.g. broadcasting)?
Extend arrays to 3D with None/np.newaxis keeping the first axis aligned, while letting the second axis getting pair-wise multiplied, perform multiplication leveraging broadcasting and reshape to 2D -
matrix = (arr[:,None,:]*arr[:,:,None]).reshape(-1,arr.shape[1])
We can also use np.einsum -
matrix = np.einsum('ij,ik->ijk',arr,arr).reshape(-1,arr.shape[1])
einsum string representation might be more intuitive as it lets us visualize three things :
Axes that are aligned (axis=0 here).
Axes that are getting summed up (none here).
Axes that are kept i.e. element-wise multiplied (axis=1 here).

How to blur 3D array of points, while maintaining their original values? (Python)

I have a sparse 3D array of values. I am trying to turn each "point" into a fuzzy "sphere", by applying a Gaussian filter to the array.
I would like the original value at the point (x,y,z) to remain the same. I just want to create falloff values around this point... But applying the Gaussian filter changes the original (x,y,z) value as well.
I am currently doing this:
dataCube = scipy.ndimage.filters.gaussian_filter(dataCube, 3, truncate=8)
Is there a way for me to normalize this, or do something so that my original values are still in this new dataCube? I am not necessarily tied to using a Gaussian filter, if that is not the best approach.
You can do this using a convolution with a kernel that has 1 as its central value, and a width smaller than the spacing between your data points.
1-d example:
import numpy as np
import scipy.signal
data = np.array([0,0,0,0,0,5,0,0,0,0,0])
kernel = np.array([0.5,1,0.5])
scipy.signal.convolve(data, kernel, mode="same")
gives
array([ 0. , 0. , 0. , 0. , 2.5, 5. , 2.5, 0. , 0. , 0. , 0. ])
Note that fftconvolve might be much faster for large arrays. You also have to specify what should happen at the boundaries of your array.
Update: 3-d example
import numpy as np
from scipy import signal
# first build the smoothing kernel
sigma = 1.0 # width of kernel
x = np.arange(-3,4,1) # coordinate arrays -- make sure they contain 0!
y = np.arange(-3,4,1)
z = np.arange(-3,4,1)
xx, yy, zz = np.meshgrid(x,y,z)
kernel = np.exp(-(xx**2 + yy**2 + zz**2)/(2*sigma**2))
# apply to sample data
data = np.zeros((11,11,11))
data[5,5,5] = 5.
filtered = signal.convolve(data, kernel, mode="same")
# check output
print filtered[:,5,5]
gives
[ 0. 0. 0.05554498 0.67667642 3.0326533 5. 3.0326533
0.67667642 0.05554498 0. 0. ]

Reshape numpy (n,) vector to (n,1) vector

So it is easier for me to think about vectors as column vectors when I need to do some linear algebra. Thus I prefer shapes like (n,1).
Is there significant memory usage difference between shapes (n,) and (n,1)?
What is preferred way?
And how to reshape (n,) vector into (n,1) vector. Somehow b.reshape((n,1)) doesn't do the trick.
a = np.random.random((10,1))
b = np.ones((10,))
b.reshape((10,1))
print(a)
print(b)
[[ 0.76336295]
[ 0.71643237]
[ 0.37312894]
[ 0.33668241]
[ 0.55551975]
[ 0.20055153]
[ 0.01636735]
[ 0.5724694 ]
[ 0.96887004]
[ 0.58609882]]
[ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
More simpler way with python syntax sugar is to use
b.reshape(-1,1)
where the system will automatically compute the correct shape instead "-1"
ndarray.reshape() returns a new view, or a copy (depends on the new shape). It does not modify the array in place.
b.reshape((10, 1))
as such is effectively no-operation, since the created view/copy is not assigned to anything. The "fix" is simple:
b_new = b.reshape((10, 1))
The amount of memory used should not differ at all between the 2 shapes. Numpy arrays use the concept of strides and so the dimensions (10,) and (10, 1) can both use the same buffer; the amounts to jump to next row and column just change.

Categories