Rotating Point Cloud Data in Bird's-Eye-View Using Rotation Matrix - python

I am trying to rotate 3d point cloud data to theta degree using rotation matrix.
The shape of point cloud data that is indicated as 'xyz' in code is (64,2048,3), so there are 131,072 points including x,y,z.
I have tried with 2d rotation matrix (since I want to rotate in bird's eye view).
The 2d rotation matrix that I used is:
And this is my code for the rotation:
def rotation (xyz):
original_x = []
original_y = []
for i in range(64):
for j in range(2048):
original_x.append(xyz[i][j][0])
original_y.append(xyz[i][j][1])
original = np.matrix.transpose(np.array([original_x,original_y]))
rotation_2d = [[np.cos(theta), -np.sin(theta)],[np.sin(theta),np.cos(theta)]]
rotated_points = np.matmul(original,rotation_2d)
return rotated_points
The result looks successful from bird's eye view, but not successful from the front view, as it is very messy so it is not possible to understand the data.
I have also tried with 3d rotation matrix with [x,y,z] that is :
However, the result was exactly the same when 2d rotation matrix was used.
What could be the possible reason? Is there any mistake in the rotation method?

Here's a function that applies the rotation you're after and a demonstration of this code.
Note that the rotation is applied clockwise. For a counterclockwise rotation, use the transpose of the rotation matrix.
import numpy as np
import matplotlib.pyplot as plt
from scipy.linalg import block_diag
def rotation(xyz):
before = xyz.reshape((-1,3))
rot_3d = block_diag([[np.cos(theta), -np.sin(theta)],[np.sin(theta),np.cos(theta)]],1)
after = before # rot_3d
return after
theta = 3*np.pi/2
xyz_test = np.random.rand(8,16)
xyz_test = np.stack([xyz_test]*3,axis = -1)
xyz_test += np.random.randn(8,16,3)*.1
original = xyz_test.reshape([8*16,3])
rotated = rotation(xyz_test)
# plt.plot(1,2,3,projection = '3d')
fig,ax = plt.subplots(figsize = (10,10), subplot_kw = {"projection":"3d"})
ax.plot3D(*original.T, 'x', ms = 20)
ax.plot3D(*rotated.T, 'x', ms = 20)
Sample resulting plot (of cloud and rotated cloud):

Related

Why does OpenCV projectPoints return the same coordinates every time?

Like I said in this post I am trying to project object points onto a camera with a known world location if that camera was in a certain orientation. I know that the camera is in this certain orientation when its looking down the y axis with the z axis above and the x axis to the right. So I think its a -90 degree rotation about the x axis to transform from the OpenCV default to this orientation.
The major changes I have made from the previous post are creating and labeling the transformation matrices.
For some reason every single object point that I pass into cv::projectPoints() projects onto the same 2d point.
import cv2
import transformations
import numpy as np
import math
# #Christoph Rackwitz
def matrix_to_rtvec(matrix):
"Convert 4x4 matrix to rotation vector and translation vector"
(rvec, jac) = cv2.Rodrigues(matrix[:3, :3])
tvec = matrix[:3, 3]
return rvec, tvec
cam_matrix = np.array([[1561.9015217711233, 0, 944.3790845611046], [0, 1557.8348925840205, 538.3374859400157], [0, 0, 1]])
distortion = np.array([-0.2136432557736835, 0.20055112514542725, 0.00054631323043295, -0.00067835485282051, -0.07781645541334031])
objects = np.array([[89.43900677430054, -32.842383325319595, 67.31985743900492], [103.57096890138145, -26.55406643523704, 49.86023609673168], [0, 0, 0]])
cam = np.array([-14.45194, 34.59882, 19.11343])
# create cam (input) to world (output) transformation matrix
rt = transformations.rotation_matrix(math.radians(-90), [1,0,0]) # -90 degrees about x axis
trans = transformations.translation_matrix([-cam[0], -cam[1], -cam[2]])
T_world_cam = transformations.concatenate_matrices(rt, trans)
for i, _ in enumerate(objects):
# create object (input) to world (output) transformation matrix
T_world_object = transformations.translation_matrix([-objects[i][0], -objects[i][1], -objects[i][2]])
# create object (input) to cam (output) transformation matrix
T_cam_object = np.linalg.inv(T_world_cam) # T_world_object
# break down into components
rVec, tVec = matrix_to_rtvec(T_cam_object)
projected, jac = cv2.projectPoints(objects[i], rVec, tVec, cam_matrix, distortion)
print(projected)
Output:
[[[ 2457.06284386 -3080.15125722]]]
[[[ 2457.06284386 -3080.15125722]]]
[[[ 2457.06284386 -3080.15125722]]]

Rotating a reshaped image as a matrix operation

I have a gray scale image that I want to rotate. However, I need to do optimization on it. Therefore, I cannot use pillow or opencv.
I want to reshape this image using python with numpy.reshape into an one dimensional vector (where I use the default settings C-style reshape).
And thereafter, I want to rotate this image around a point using matrix multiplication and addition, i.e. it should be something like
rotated_image_vector = A # vector + b # (or the equivalent in homogenious coordinates).
After this operation I want to reshape the outcome back to two dimensions and have the rotated image.
It would be best if it would as well use linear interpolation between the pixels that do not fit exactly to an other pixel.
The mathematical theory tells it is possible, and I believe there is a very elegant solution to this problem, but I do not see how to create this matrix. Did anyone already have this problem or sees an immediate solution?
Thanks a lot,
Eike
I like your approach but there is a slight misconception in it. What you want to transform are not the pixel values themselves but the coordinates. So you don't reshape your image but rather do a np.indices on it to obtain coordinates to each pixel. For those a rotation around a point looks like
rotation_matrix#(coordinates-fixed_point)+fixed_point
except that I have to transpose a bit to get the dimensions to align. The cove below is a slight adoption of my code in this answer.
As an example I am going to use the Wikipedia-logo-v2 by Nohat. It is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
First I read in the picture, swap x and y axis to not get mad and rotate the coordinates as described above.
import numpy as np
import matplotlib.pyplot as plt
import itertools
image = plt.imread('wikipedia.jpg')
image = np.swapaxes(image,0,1)/255
fixed_point = np.array(image.shape[:2], dtype='float')/2
points = np.moveaxis(np.indices(image.shape[:2]),0,-1).reshape(-1,2)
a = 2*np.pi/8
A = np.array([[np.cos(a),-np.sin(a)],[np.sin(a),np.cos(a)]])
rotated_coordinates = (A#(points-fixed_point.reshape(1,2)).T).T+fixed_point.reshape(1,2)
Now I set up a little class to interpolate between the pixels that do not fit exactly to an other pixel. And finally I swap the axis back and plot it.
class Image_knn():
def fit(self, image):
self.image = image.astype('float')
def predict(self, x, y):
image = self.image
weights_x = [(1-(x % 1)).reshape(*x.shape,1), (x % 1).reshape(*x.shape,1)]
weights_y = [(1-(y % 1)).reshape(*x.shape,1), (y % 1).reshape(*x.shape,1)]
start_x = np.floor(x)
start_y = np.floor(y)
return sum([image[np.clip(np.floor(start_x + x), 0, image.shape[0]-1).astype('int'),
np.clip(np.floor(start_y + y), 0, image.shape[1]-1).astype('int')] * weights_x[x]*weights_y[y]
for x,y in itertools.product(range(2),range(2))])
image_model = Image_knn()
image_model.fit(image)
transformed_image = image_model.predict(*rotated_coordinates.T).reshape(*image.shape)
plt.imshow(np.swapaxes(transformed_image,0,1))
And I get a result like this
Possible Issue
The artifact in the bottom left that looks like one needs to clean the screen comes from the following problem: When we rotate it can happen that we don't have enough pixels to paint the lower left. What we do by default in image_knn is to clip the coordinates to an area where we have information. That means when we ask image knn for pixels coming from outside the image it gives us the pixels at the boundary of the image. This looks good if there is a background but if an object touches the edge of the picture it looks odd like here. Just something to keep in mind when using this.
Thank you for your answer!
But actually it is not a misconception that you could let this roation be represented by a matrix multiplication with the reshaped vector.
I used your code to generate such a matrix (its surely not the most efficient way but it works, most likely you see a more efficient implementation immediately XD. You see I really need it as a matix multiplication :-D).
What I basically did is to generate the representation matrix of the linear transformation, by computing how every of the 100*100 basis images (i.e. the image with zeros everywhere und a one) is mapped by your transformation.
import sys
import numpy as np
import matplotlib.pyplot as plt
import itertools
angle = 2*np.pi/6
image_expl = plt.imread('wikipedia.jpg')
image_expl = image_expl[:,:,0]
plt.imshow(image_expl)
plt.title("Image")
plt.show()
image_shape = image_expl.shape
pixel_number = image_shape[0]*image_shape[1]
rot_mat = np.zeros((pixel_number,pixel_number))
for i in range(pixel_number):
vector = np.zeros(pixel_number)
vector[i] = 1
image = vector.reshape(*image_shape)
fixed_point = np.array(image.shape, dtype='float')/2
points = np.moveaxis(np.indices(image.shape),0,-1).reshape(-1,2)
a = -angle
A = np.array([[np.cos(a),-np.sin(a)],[np.sin(a),np.cos(a)]])
rotated_coordinates = (A#(points-fixed_point.reshape(1,2)).T).T+fixed_point.reshape(1,2)
x,y = rotated_coordinates.T
image = image.astype('float')
weights_x = [(1-(x % 1)).reshape(*x.shape), (x % 1).reshape(*x.shape)]
weights_y = [(1-(y % 1)).reshape(*x.shape), (y % 1).reshape(*x.shape)]
start_x = np.floor(x)
start_y = np.floor(y)
transformed_image_returned = sum([image[np.clip(np.floor(start_x + x), 0, image.shape[0]-1).astype('int'),
np.clip(np.floor(start_y + y), 0, image.shape[1]-1).astype('int')] * weights_x[x]*weights_y[y]
for x,y in itertools.product(range(2),range(2))])
rot_mat[:,i] = transformed_image_returned
if i%100 == 0: print(int(100*i/pixel_number), "% finisched")
plt.imshow((rot_mat # image_expl.reshape(-1)).reshape(image_shape))
Thank you again :-)

How to apply vector field to image in 2D with python?

I generate two Gaussian Random Fields with the library FyeldGenerator. If they are plotted, it's like this:
from FyeldGenerator import generate_field
import matplotlib.pyplot as plt
import numpy as np
# Helper that generates power-law power spectrum
def Pkgen(n):
def Pk(k):
return np.power(k, -n)
return Pk
# Draw samples from a normal distribution
def distrib(shape):
a = np.random.normal(loc=0, scale=1, size=shape)
b = np.random.normal(loc=0, scale=1, size=shape)
return a + 1j * b
shape = (512, 512)
field_x = generate_field(distrib, Pkgen(3), shape)
field_y = generate_field(distrib, Pkgen(3), shape)
plt.imshow(field_x, cmap='seismic')
plt.show()
plt.imshow(field_y, cmap='seismic')
Then I plot with matplotlib with quiver the vector field.
Now I would like to apply on an image of the same size of the random fields, the vector field. I would like the pixel in a point (i,j) to move in the direction (in 2D), which is shown in the quiver function. Is there anyway to do it ?
This is an example of this problem done on matlab, but in 3D :
Link of stackoverflow : Applying a vector field to image in matlab
I think that what you are looking for is the remap function in opencv.
As described in the link, the function remaps the values based on the indexing arrays mapx and mapy:
𝚍𝚜𝚝(x,y)=𝚜𝚛𝚌(mapx(x,y),mapy(x,y))
If I understood properly what you would like to do you first need to create the base indexes for the mapx and mapy:
mapx_base, mapy_base = np.meshgrid(np.arange(shape[0]), np.arange(shape[1]))
Then deform the image indexes with your vector field. Here I multiply to increase the deformation.
mapx = mapx_base + field_x*30
mapy = mapy_base + field_y*30
Finally resample your image
img = cv2.imread('apple.jpg', 0).astype(np.float32)
deformed_apple = cv2.remap(img, mapx.astype(np.float32), mapy.astype(np.float32), cv2.INTER_LINEAR)
Of course, the field needs to be smoothed if you would like to have a less noisy deformation.
Hope this helps!

Healpy: From Data to Healpix map

I have a data grid where the rows represent theta (0, pi) and the columns represent phi (0, 2*pi) and where f(theta,phi) is the density of dark matter at that location. I wanted to calculate the power spectrum for this and have decided to use healpy.
What I can not understand is how to format my data for healpy to use. If someone could provide code (in python for obvious reasons) or point me to a tutorial, that would be great! I have tried my hand at doing it with the following code:
#grid dimensions are Nrows*Ncols (subject to change)
theta = np.linspace(0, np.pi, num=grid.shape[0])[:, None]
phi = np.linspace(0, 2*np.pi, num=grid.shape[1])
nside = 512
print "Pixel area: %.2f square degrees" % hp.nside2pixarea(nside, degrees=True)
pix = hp.ang2pix(nside, theta, phi)
healpix_map = np.zeros(hp.nside2npix(nside), dtype=np.double)
healpix_map[pix] = grid
But, when I try to execute the code to do the power spectrum. Specifically, :
cl = hp.anafast(healpix_map[pix], lmax=1024)
I get this error:
TypeError: bad number of pixels
If anyone could point me to a good tutorial or help edit my code that would be great.
More specifications:
my data is in a 2d np array and I can change the numRows/numCols if I need to.
Edit:
I have solved this problem by first changing the args of anafast to healpix_map.
I also improved the spacing by making my Nrows*Ncols=12*nside*nside.
But, my power spectrum is still giving errors. If anyone has links to good documentation/tutorial on how to calculate the power spectrum (condition of theta/phi args), that would be incredibly helpful.
There you go, hope it's what you're looking for. Feel free to comment with questions :)
import healpy as hp
import numpy as np
import matplotlib.pyplot as plt
# Set the number of sources and the coordinates for the input
nsources = int(1.e4)
nside = 16
npix = hp.nside2npix(nside)
# Coordinates and the density field f
thetas = np.random.random(nsources) * np.pi
phis = np.random.random(nsources) * np.pi * 2.
fs = np.random.randn(nsources)
# Go from HEALPix coordinates to indices
indices = hp.ang2pix(nside, thetas, phis)
# Initate the map and fill it with the values
hpxmap = np.zeros(npix, dtype=np.float)
for i in range(nsources):
hpxmap[indices[i]] += fs[i]
# Inspect the map
hp.mollview(hpxmap)
Since the map above contains nothing but noise, the power spectrum should just contain shot noise, i.e. be flat.
# Get the power spectrum
Cl = hp.anafast(hpxmap)
plt.figure()
plt.plot(Cl)
There is a faster way to do the map initialization using numpy.add.at, following this answer.
This is several times faster on my machine as compared to the first section of Daniel's excellent answer:
import healpy as hp
import numpy as np
import matplotlib.pyplot as plt
# Set the number of sources and the coordinates for the input
nsources = int(1e7)
nside = 64
npix = hp.nside2npix(nside)
# Coordinates and the density field f
thetas = np.random.uniform(0, np.pi, nsources)
phis = np.random.uniform(0, 2*np.pi, nsources)
fs = np.random.randn(nsources)
# Go from HEALPix coordinates to indices
indices = hp.ang2pix(nside, thetas, phis)
# Baseline, from Daniel Lenz's answer:
# time: ~5 s
hpxmap1 = np.zeros(npix, dtype=np.float)
for i in range(nsources):
hpxmap1[indices[i]] += fs[i]
# Using numpy.add.at
# time: ~0.6 ms
hpxmap2 = np.zeros(npix, dtype=np.float)
np.add.at(hpxmap2, indices, fs)

Fast 2D rigid body transformations in numpy/scipy

I want to apply rigid body transformations to a large set of 2D image matrices. Ideally, I'd like to be able to just supply an affine transformation matrix specifying both the translation and rotation, apply this in one go, then do cubic spline interpolation on the output.
Unfortunately it seems that affine_transform in scipy.ndimage.interpolation doesn't do translation. I know I could use a combination of shift and rotate, but this is kind of messy and in involves interpolating the output multiple times.
I've also tried using the generic geometric_transformation like this:
import numpy as np
from scipy.ndimage.interpolation import geometric_transformation
# make the affine matrix
def maketmat(xshift,yshift,rotation,dimin=(0,0)):
# centre on the origin
in2orig = np.identity(3)
in2orig[:2,2] = -dimin[0]/2.,-dimin[1]/2.
# rotate about the origin
theta = np.deg2rad(rotation)
rotmat = np.identity(3)
rotmat[:2,:2] = [np.cos(theta),np.sin(theta)],[-np.sin(theta),np.cos(theta)]
# translate to new position
orig2out = np.identity(3)
orig2out[:2,2] = xshift,yshift
# the final affine matrix is just the product
tmat = np.dot(orig2out,np.dot(rotmat,in2orig))
# function that maps output space to input space
def out2in(outcoords,affinemat):
outcoords = np.asarray(outcoords)
outcoords = np.concatenate((outcoords,(1.,)))
incoords = np.dot(affinemat,outcoords)
incoords = tuple(incoords[0:2])
return incoords
def rbtransform(source,xshift,yshift,rotation,outdims):
# source --> target
forward = maketmat(xshift,yshift,rotation,source.shape)
# target --> source
backward = np.linalg.inv(forward)
# now we can use geometric_transform to do the interpolation etc.
tformed = geometric_transform(source,out2in,output_shape=outdims,extra_arguments=(backward,))
return tformed
This works, but it's horribly slow, since it's essentially looping over pixel coordinates! What's a good way to do this?
Can you use the scikit image?
If this is the case, you could try to apply an homography. An homography cab used to represent both translation and rotation at the same time through a 3x3 matrix.
You can use the skimage.transform.fast_homography function.
import numpy as np
import scipy
import skimage.transform
im = scipy.misc.lena()
H = np.asarray([[1, 0, 10], [0, 1, 20], [0, 0, 1]])
skimage.transform.fast_homography(im, H)
The transform took about 30 ms on my old Core 2 Duo.
About homography : http://en.wikipedia.org/wiki/Homography
I think affine_transform does do translation --- there's the offset parameter.

Categories

Resources