finding rotated image coordinates - python

I'm using python and I have some images on my canvas that are rotated by different angles.
What I want to do is to get their coordinates while I know their previous position, axis and the angle they were rotated by.
It would also be enough if I can find coordinates of just one point after rotation

To get a coordinate after it has been rotated, I would use numpy and a rotation matrix to derive its position:
import numpy as np
# define parameters
pixel_column = 10
pixel_row = 20
center_column = 50
center_row = 50
# angle goes counter-clockwise!
rotation_angle_deg = 90
def get_rotation_matrix(angle_deg: float):
theta = np.radians(angle_deg)
cos_theta, sin_theta = np.cos(theta), np.sin(theta)
rotation = np.array(((cos_theta, -sin_theta), (sin_theta, cos_theta)))
return rotation
def rotate_coordinate(coordinate: np.array, angle_deg: float) -> np.array:
rotation = get_rotation_matrix(angle_deg)
rotated = rotation.dot(coordinate)
return rotated
def get_new_coordinate(original_coordinate: np.array, center_of_rotation: np.array, angle_deg: float):
delta = original_coordinate - center_of_rotation
delta_rotated = rotate_coordinate(delta, angle_deg)
new_coordinate = center_of_rotation + delta_rotated
return new_coordinate
# calculate new rotated coordinate
rotated_coordinate = get_new_coordinate(
np.array((pixel_row, pixel_column)),
np.array((center_row, center_column)),
rotation_angle_deg
)
new_pixel_row, new_pixel_column = [int(val) for val in rotated_coordinate]
# show as text
print(f"""
After rotating the image at an angle of {rotation_angle_deg}° around ({center_row}, {center_column}),
the pixel located previously at ({pixel_row}, {pixel_column}) is now at ({new_pixel_row}, {new_pixel_column}).
""")

Related

How can you find the relative spherical coordinates of an object after changing the origin?

I have an algorithm question. I am currently working on a script that generates images of an object from various angles inside of Unreal engine and pairs these images with the coordinates of the object. The way it works is that I have the object at the origin, and I generate random spherical coordinates to place my camera at. I then rotate my camera to face the object and do an extra rotation so that the object can lie anywhere in my camera's FOV. I now want to consider my camera as the origin and find the spherical coordinates of the object relative to the graph.
Currently, I am trying to derive the coordinates as in the code below. I start by noting that the radial distance between the object and the camera is the same regardless of which one is the origin. Then, I use the fact that the angles between my camera and my object are determined entirely by the extra rotation at the end of my camera placement. Finally, I try to find a rotation that will orient the object the same way as in the image based on the angular coordinates of the camera (This is done because I want to encode information about points on the object besides the center. For example, I am currently using a 1 meter cube as a placeholder object, and I want to keep track of the coordinates of the corners. I chose to use rotations because I can use them to make a rotation matrix and use it to convert my coordinates). Below is the code I use to do this (the AirSim library is used here, but all you need to know is airsim.Pose() takes in a Euclidean position coordinate and a Quaternion rotation as arguments to position my camera).
PRECISION_ANGLE = 4 # Fractions of a degree used in generating random pitch, roll, and yaw values
PRECISION_METER = 100 # Fractions of a meter used in generating random distance values
RADIUS_MAX = 20 # Maximum distance from the obstacle to be expected
#TODO: Replace minimum distace with a test for detecting if the camera is inside the obstacle
RADIUS_MIN = 3 # Minimum distance from the obstacle to be expected. Set this value large enough so that the camera will not spawn inside the object
# Camera details should match settings.json
IMAGE_HEIGHT = 144
IMAGE_WIDTH = 256
FOV = 90
# TODO: Vertical FOV rounds down for generating random integers. Some pictures will not be created
VERT_FOV = FOV * IMAGE_HEIGHT // IMAGE_WIDTH
def polarToCartesian(r, theta, phi):
return [
r * math.sin(theta) * math.cos(phi),
r * math.sin(theta) * math.sin(phi),
r * math.cos(theta)]
while 1:
# generate a random position for our camera
r = random.randint(RADIUS_MIN * PRECISION_METER, RADIUS_MAX * PRECISION_METER) / PRECISION_METER
phi = random.randint(0, 360 * PRECISION_ANGLE) / PRECISION_ANGLE
theta = random.randint(0, 180 * PRECISION_ANGLE) / PRECISION_ANGLE
# Convert polar coordinates to cartesian for AirSim
pos = polarToCartesian(r, math.radians(theta), math.radians(phi))
# Generate a random offset for the camera angle
pitch = random.randint(0, VERT_FOV * PRECISION_ANGLE) / PRECISION_ANGLE - VERT_FOV / 2
# TODO: Rotating the drone causes the obstacle to be removed from the image because the camera is not square
#roll = random.randint(0, 360 * PRECISION_ANGLE) / PRECISION_ANGLE
roll = 0
yaw = random.randint(0, FOV * PRECISION_ANGLE) / PRECISION_ANGLE - FOV/2
# Calculate coordinates of the center of the obstacle relative to the drone's new position and orientation
obs_r = r
obs_phi = yaw
obs_theta = 90 - pitch
# Convert polar coordinates to cartesian for AirSim
obs_pos = polarToCartesian(obs_r, math.radians(obs_theta), math.radians(obs_phi))
# Record rotational transformation on obstacle for calculating coordinates of key locations relative to the center
obs_phi_offset = -phi
obs_theta_offset = 270 - theta
# Move the camera to our calculated position
camera_pose = airsim.Pose(airsim.Vector3r(pos[0], pos[1], pos[2]), airsim.to_quaternion(math.radians(90 - theta + pitch), math.radians(roll), math.radians(phi + 180 + yaw))) #radians
Is this algorithm implemented correctly? What other ways could I find the coordinates of my object? Should I be doing something in Unreal Engine to get my coordinates instead of doing this algorithmically (though it needs to be fast)?
A translation of the origin by Vector3(i,j,k) is simply the translation of the original output.
camera_pose = airsim.Pose(airsim.Vector3r(pos[0] + i, pos[1] + j, pos[2] + k), airsim.to_quaternion(math.radians(90 - theta + pitch), math.radians(roll), math.radians(phi + 180 + yaw))) #radians

SimpleITK 3Deuler angles rotation volumetric data

I want to rotate my volumetric CT data using the euler angles x, y and z. For this, I use SimpleITK. I have read the question from Dr. Jessop - simpleitk-rotation-of-volumetric-data-e-g-mri and I think I have the same problem that my orientation/direction is not an identity matrix. The direction namely is:
0.08716564279125966, 0.0, -0.9961938319005929, 0.9961938319005927, 6.633444000000004e-17, 0.08716564279125968, 0.0, -1.0, 6.12303124808918e-17
However, the solution Dr. Jessop has found is by using an axis-angle orientation so that he can rotate around the z axis only. I want to rotate around all axes by using Euler angles. How can I achieve this?
P.S. I would have commented on Dr.Jessops question to ask it, but I don't have enough reputation points for this.
The code of dr. Jessop:
# This function is from https://github.com/rock-learning/pytransform3d/blob/7589e083a50597a75b12d745ebacaa7cc056cfbd/pytransform3d/rotations.py#L302
def matrix_from_axis_angle(a):
""" Compute rotation matrix from axis-angle.
This is called exponential map or Rodrigues' formula.
Parameters
----------
a : array-like, shape (4,)
Axis of rotation and rotation angle: (x, y, z, angle)
Returns
-------
R : array-like, shape (3, 3)
Rotation matrix
"""
ux, uy, uz, theta = a
c = np.cos(theta)
s = np.sin(theta)
ci = 1.0 - c
R = np.array([[ci * ux * ux + c,
ci * ux * uy - uz * s,
ci * ux * uz + uy * s],
[ci * uy * ux + uz * s,
ci * uy * uy + c,
ci * uy * uz - ux * s],
[ci * uz * ux - uy * s,
ci * uz * uy + ux * s,
ci * uz * uz + c],
])
# This is equivalent to
# R = (np.eye(3) * np.cos(theta) +
# (1.0 - np.cos(theta)) * a[:3, np.newaxis].dot(a[np.newaxis, :3]) +
# cross_product_matrix(a[:3]) * np.sin(theta))
return R
def resample(image, transform):
"""
This function resamples (updates) an image using a specified transform
:param image: The sitk image we are trying to transform
:param transform: An sitk transform (ex. resizing, rotation, etc.
:return: The transformed sitk image
"""
reference_image = image
interpolator = sitk.sitkLinear
default_value = 0
return sitk.Resample(image, reference_image, transform,
interpolator, default_value)
def get_center(img):
"""
This function returns the physical center point of a 3d sitk image
:param img: The sitk image we are trying to find the center of
:return: The physical center point of the image
"""
width, height, depth = img.GetSize()
return img.TransformIndexToPhysicalPoint((int(np.ceil(width/2)),
int(np.ceil(height/2)),
int(np.ceil(depth/2))))
def rotation3d(image, theta_z, show=False):
"""
This function rotates an image across each of the x, y, z axes by theta_x, theta_y, and
theta_z degrees
respectively
:param image: An sitk MRI image
:param theta_x: The amount of degrees the user wants the image rotated around the x axis
:param theta_y: The amount of degrees the user wants the image rotated around the y axis
:param theta_z: The amount of degrees the user wants the image rotated around the z axis
:param show: Boolean, whether or not the user wants to see the result of the rotation
:return: The rotated image
"""
theta_z = np.deg2rad(theta_z)
euler_transform = sitk.Euler3DTransform()
print(euler_transform.GetMatrix())
image_center = get_center(image)
euler_transform.SetCenter(image_center)
direction = image.GetDirection()
print(direction)
axis_angle = (direction[2], direction[5], direction[8], theta_z)
np_rot_mat = matrix_from_axis_angle(axis_angle)
euler_transform.SetMatrix(np_rot_mat.flatten().tolist())
resampled_image = resample(image, euler_transform)
if show:
slice_num = int(input("Enter the index of the slice you would like to see"))
plt.imshow(sitk.GetArrayFromImage(resampled_image)[slice_num])
plt.show()
return resampled_image
To get the rotationmatrix from the euler angle method, this code could be used:
def matrix_from_euler_xyz(e):
"""Compute rotation matrix from xyz Euler angles.
Intrinsic rotations are used to create the transformation matrix
from three concatenated rotations.
The xyz convention is usually used in physics and chemistry.
Parameters
----------
e : array-like, shape (3,)
Angles for rotation around x-, y'-, and z''-axes (intrinsic rotations)
Returns
-------
R : array-like, shape (3, 3)
Rotation matrix
"""
alpha, beta, gamma = e
# We use intrinsic rotations
Qx = matrix_from_angle(0, alpha)
Qy = matrix_from_angle(1, beta)
Qz = matrix_from_angle(2, gamma)
R = Qx.dot(Qy).dot(Qz)
return R
However, the orientation should still be incorporated. Does anyone know how to do this?
This can be performed using SimpleITK using VersorRigid3DTransform and passing it into the resampleitkfilter, which I believe is what this code is doing in a way.
I would suggest using scipy.transform.rotation module to pass in a versor into the rigid3Dtransform then set your direction from the input to output accordingly.
I 'think' this will work even with your off-unit directions but as always with 3D affine transforms this might be missing something that brighter mathematicians can critique:
r = R.from_euler('zyx', [90, 45, 30], degrees=True)
orientation = r.as_quat()
transform = sitk.VersorRigid3DTransform()
translation = sitk.TranslationTransform(3, (x,y,z))
transform.SetTranslation(translation.GetOffset())
#get rot_center from your center of image function as tuple (x,y,z)
rotation = sitk.VersorTransform([orientation[0], orientation[1], orientation[2], orientation[3]],rot_center)
transform.SetRotation(rotation.GetVersor())
transform.SetCenter(rotation.GetCenter())
size = img.GetSize()
bounds = list()
for x in [0, size[0]]:
for y in [0, size[1]]:
for z in [0, size[2]]:
bounds.append(img.TransformIndexToPhysicalPoint((x, y, z)))
# get the physical position of the bounds given transform
trans_bounds = list()
for b in bounds:
trans_bounds.append(transform.TransformPoint((b[0], b[1], b[2])))
trans_bounds = np.array(trans_bounds)
xmin = np.min(trans_bounds.T[0])
xmax = np.max(trans_bounds.T[0])
ymin = np.min(trans_bounds.T[1])
ymax = np.max(trans_bounds.T[1])
zmin = np.min(trans_bounds.T[2])
zmax = np.max(trans_bounds.T[2])
output_origin = (xmin, ymin, zmin)
res_all = (0.5, 0.5, 0.5)
res = res_all[0]
output_size = np.array(
[int(np.round((xmax - xmin) / res)), int(np.round((ymax - ymin) / res)), int(np.round((zmax - zmin) / res))])
output_size = output_size.astype(int)
resampleFilter = sitk.ResampleImageFilter()
resampleFilter.SetTransform(transform.GetInverse())
resampleFilter.SetInterpolator(sitk.sitkLinear)
resampleFilter.SetSize(output_size.tolist())
resampleFilter.SetOutputOrigin(output_origin)
resampleFilter.SetOutputSpacing(res_all)
resampleFilter.SetOutputDirection(img.GetDirection())
resampleFilter.SetDefaultPixelValue(0.0)
I solved it (with the help of zivy from the itk discourse
My answer is this:
# The function which is used to rotate (and make the 3D image isotropic) using euler angles
def rotation3d(image, theta_x, theta_y, theta_z, output_spacing = None, background_value=0.0):
"""
This function rotates an image across each of the x, y, z axes by theta_x, theta_y, and theta_z degrees
respectively (euler ZXY orientation) and resamples it to be isotropic.
:param image: An sitk 3D image
:param theta_x: The amount of degrees the user wants the image rotated around the x axis
:param theta_y: The amount of degrees the user wants the image rotated around the y axis
:param theta_z: The amount of degrees the user wants the image rotated around the z axis
:param output_spacing: Scalar denoting the isotropic output image spacing. If None, then use the smallest
spacing from original image.
:return: The rotated image
"""
euler_transform = sitk.Euler3DTransform (image.TransformContinuousIndexToPhysicalPoint([(sz-1)/2.0 for sz in image.GetSize()]),
np.deg2rad(theta_x),
np.deg2rad(theta_y),
np.deg2rad(theta_z))
# compute the resampling grid for the transformed image
max_indexes = [sz-1 for sz in image.GetSize()]
extreme_indexes = list(itertools.product(*(list(zip([0]*image.GetDimension(),max_indexes)))))
extreme_points_transformed = [euler_transform.TransformPoint(image.TransformContinuousIndexToPhysicalPoint(p)) for p in extreme_indexes]
output_min_coordinates = np.min(extreme_points_transformed, axis=0)
output_max_coordinates = np.max(extreme_points_transformed, axis=0)
# isotropic ouput spacing
if output_spacing is None:
output_spacing = min(image.GetSpacing())
output_spacing = [output_spacing]*image.GetDimension()
output_origin = output_min_coordinates
output_size = [int(((omx-omn)/ospc)+0.5) for ospc, omn, omx in zip(output_spacing, output_min_coordinates, output_max_coordinates)]
output_direction = [1,0,0,0,1,0,0,0,1]
output_pixeltype = image.GetPixelIDValue()
return sitk.Resample(image,
output_size,
euler_transform.GetInverse(),
sitk.sitkLinear,
output_origin,
output_spacing,
output_direction,
background_value,
output_pixeltype)

NumPy FFT producing off centre output

TL;DR: NumPy FFT creates non uniform output when output is wanted to be uniform. I want the output to be a uniform corona.
I am trying to eventually run a Gerchberg-Saxton phase retrieval algorithm. I have been trying to make sure that I understand how FFT works in NumPy. I have used fftshift to create the correct looking output but the image does not have uniform intensity afterwards.
My input image is a circle, output should be a coronagraph looking thing from the circle aperture. I am trying to reproduce the results detailed in https://www.osapublishing.org/optica/fulltext.cfm?uri=optica-2-2-147&id=311836#articleSupplMat.
My algorithm to produce the error:
Initial image, f
FT(f)
x exp ( i phase_mask)
IFT(FT(f)x exp( i phase_mask)
Happy to clear anything up.
import numpy as np
import matplotlib.pyplot as plt
#Create 'pixels' for circle
pixels = 400
edge = np.linspace(-10, 10, pixels)
xv, yv = np.meshgrid(edge, edge)
def circle(x, y, r):
'''
x, y : dimensions of grid to place circle on
r : radius
Function defines aperture
'''
x0 = 0
y0 = 0
return np.select([((x-x0)**2+(y-y0)**2)>=r**2,
((x-x0)**2+(y-y0)**2)<r**2],
[0,
1.])
#Create input and output images
radius = 4
input_img = circle(xv, yv, radius)
constraint_img = xcircle(xv, yv, radius)
img = input_img
constraint = 1 - img
max_iter = 10
re,im = np.mgrid[-1:1:400j, -1:1:400j] #Creates grid of values, 400=pixels
mask = 2*np.angle(re + 1j*im) #Gets angle from centre of grid
mask_i = mask
#Initial focal plane field, F. Initial image f.
f = np.sqrt(img)
F = np.fft.fftshift(np.fft.fft2(f)) * np.exp(mask * 1j) #Focal plane field
F_1 = F
am_f = np.abs(F_1) #Initial amplitude
g = np.fft.ifft2(F)
mask = np.angle(F/(F_1+1e-18)) #Final phase mask
recovery = (np.fft.ifft2(F*np.exp(-1j * mask)))
im3 = plt.imshow(np.abs(g)**2, cmap='gray')
plt.title('Recovered image')
plt.tight_layout()
plt.show()
plt.imshow(mask_i)
plt.colorbar()
plt.show()
Your issue is in this bit of code:
pixels = 400
edge = np.linspace(-10, 10, pixels)
as well as this one:
re,im = np.mgrid[-1:1:400j, -1:1:400j]
Because you use fftshift*, you need the origin to be at pixels//2. However, you don't sample the origin at all, it is in between two samples.
* You should really be using ifftshift instead, which moves the origin from pixels//2 to 0. fftshift moves the origin from 0 to pixels//2. For an even number of samples, these two do the same thing though.
To properly sample the origin, create edge as follows:
edge = np.linspace(-10, 10, pixels, endpoint=False)
We now see that edge[pixels//2] is equal to 0.
For np.mgrid there's no equivalent option. You will have to do this manually by creating one more sample, then deleting the last sample:
re,im = np.mgrid[-1:1:401j, -1:1:401j] #Creates grid of values, 400=pixels
mask = 2*np.angle(re + 1j*im) #Gets angle from centre of grid
mask = mask[:-1, :-1]
With these two changes, you will see a symmetric output.

How to generate regular points on cylindrical surface

I am a beginner in Python and I have to work on a project using Numpy.
I need to generate some points (e.g. one million) on one part of the surface of a cylinder. These points should be regularly distributed on a subregion of the surface defined by a given angle. How could I go about doing this?
My input parameters are:
position of the center of cylinder (e.g. [0,0,0] )
the orientation of cylinder
length of cylinder
radius of cylinder
angle (this defines the part of cylinder which the points should be distributed on it.) for alpha = 360, the whole surface
delta_l is the distance between each two points in the length direction
delta_alpha is the distance between each two points in the alpha (rotation) direction
My output parameters :
an array containing the coordinates of all points
Could anyone help me, or give me a hint about how to do this?
Many thanks
This is taken from a previous project of mine:
def make_cylinder(radius, length, nlength, alpha, nalpha, center, orientation):
#Create the length array
I = np.linspace(0, length, nlength)
#Create alpha array avoid duplication of endpoints
#Conditional should be changed to meet your requirements
if int(alpha) == 360:
A = np.linspace(0, alpha, num=nalpha, endpoint=False)/180*np.pi
else:
A = np.linspace(0, alpha, num=nalpha)/180*np.pi
#Calculate X and Y
X = radius * np.cos(A)
Y = radius * np.sin(A)
#Tile/repeat indices so all unique pairs are present
pz = np.tile(I, nalpha)
px = np.repeat(X, nlength)
py = np.repeat(Y, nlength)
points = np.vstack(( pz, px, py )).T
#Shift to center
shift = np.array(center) - np.mean(points, axis=0)
points += shift
#Orient tube to new vector
#Grabbed from an old unutbu answer
def rotation_matrix(axis,theta):
a = np.cos(theta/2)
b,c,d = -axis*np.sin(theta/2)
return np.array([[a*a+b*b-c*c-d*d, 2*(b*c-a*d), 2*(b*d+a*c)],
[2*(b*c+a*d), a*a+c*c-b*b-d*d, 2*(c*d-a*b)],
[2*(b*d-a*c), 2*(c*d+a*b), a*a+d*d-b*b-c*c]])
ovec = orientation / np.linalg.norm(orientation)
cylvec = np.array([1,0,0])
if np.allclose(cylvec, ovec):
return points
#Get orthogonal axis and rotation
oaxis = np.cross(ovec, cylvec)
rot = np.arccos(np.dot(ovec, cylvec))
R = rotation_matrix(oaxis, rot)
return points.dot(R)
Plotted points for:
points = make_cylinder(3, 5, 5, 360, 10, [0,2,0], [1,0,0])
The rotation part is quick and dirty- you should likely double check it. Euler-Rodrigues formula thanks to unutbu.

Image Rotation using Python

My problem is the following:
I have two points in an image, I get the angle between these two points and rotate the image by this angle. I need to get the new position of this points in the image, but when I try to rotate those points using a rotation matrix with the same angle the points do not concur, what's wrong in the following code?
def rotMat(angle):
return asarray([[cos(angle), -sin(angle)],[sin(angle),cos(angle)]])
for i in batch2:
figure(1)
filename = "../imagens/" + i[-1]
outputFile = "./output/" + i[-1]
x1 = float(i[22]) # x coordinate of first point
y1 = float(i[23]) # y coordinate of first point
x2 = float(i[34]) # x coordinate of second point
y2 = float(i[35]) # y coordinate of second point
# angle of rotation
angle = arctan((y1-y2)/(x1-x2))
im = imread(filename)
im = ndimage.rotate(im, angle*180/pi, reshape=False)
imshow(im)
p1 = asarray([x1,y1])
p2 = asarray([x2,y2])
# Rotating the points
# [512,680] is the center of the image
p1n = (p1-[512,680]).dot(rotMat(angle)) + [512,680]
p2n = (p2-[512,680]).dot(rotMat(angle)) + [512,680]
print p1n, p2n
plot(p1n[0],p1n[1],'d')
plot(p2n[0],p2n[1],'d')
savefig(outputFile)
clf()
I don't understand 100 % what you are doing. But, did you consider that the y-axis in an image runs from 0 at the top to positive values for lower points. Therefore, the direction is opposite compared to the usual mathmetical definition. You defined rotMat in the usual way, but you have to adopt it to the changed y-axis in the image definition that runs in the oposite direction.

Categories

Resources