how to generate rectangle from a plane equation ax+by+cz=d? - python

given a plane equation, how can you generate four points that comprise a rectangle? I only have the plane equation ax+by+cz=d.
I am following the approach listed here Find Corners of Rectangle, Given Plane equation, height and width
#generate horizontal vector U
temp_normal=np.array([a,b,c])
temp_vertical=np.array([0,0,1])
U=np.cross(temp_normal, temp_vertical)
# for corner 3 and 4
neg_U=np.multiply([-1.0, -1.0, -1.0], U)
#generate vertical vector W
W=np.cross(temp_normal,U)
#for corner 2 and 4
neg_W=np.multiply([-1.0, -1.0, -1.0], W)
#make the four corners
#C1 = P0 + (width / 2) * U + (height / 2) * W
C1=np.sum([centroid,np.multiply(U, width_array),np.multiply(W, height_array)], axis=0)
corner1=C1.tolist()
#C2 = P0 + (width / 2) * U - (height / 2) * W
C2=np.sum([centroid,np.multiply(U, width_array),np.multiply(neg_W, height_array)], axis=0)
corner2=C2.tolist()
#C3 = P0 - (width / 2) * U + (height / 2) * W
C3=np.sum([centroid,np.multiply(neg_U, width_array),np.multiply(W, height_array)], axis=0)
corner3=C3.tolist()
#C4 = P0 - (width / 2) * U - (height / 2) * W
C4=np.sum([centroid,np.multiply(neg_U, width_array),np.multiply(neg_W, height_array)], axis=0)
self.theLw.WriteLine("C4 is " +str(type(C4))+" "+str(C4.tolist()))
corner4=C4.tolist()
corners_list.append([corner1, corner2, corner3, corner4])

Find a vector inside that plane using the equation. Find a second one inside that plane, perpendicular to the first one, using the cross-product (of the first and a normal vector to the plane). Then add these vectors (with +- signs, 4 possibilities) to generate 4 corners.
Edit: to help you a bit more:
(a,b,c) is the vector normal to the plane;
(0,0,d/c), (0,d/b,0) and (d/a,0,0) are points belonging to the plane, i.e. for instance b1 = (0,d/b,-d/c) is a vector tangent to the plane;
The cross-product of two vectors returns a vector that is perpendicular to both. So the product b2 = (a,b,c) x (0,d/b,-d/c) is a vector tangent to the plane, perpendicular to the other one. With that, you have constructed a normal basis of the plane [b1,b2].
Start from a point, say (0,0,d/c), and add b1+b2, b1-b2, -b1+b2, -b1-b2 to have 4 corners.
Ok here is the answer:
import numpy as np
a = 2; b = 3; c = 4; d = 5
n = np.array([a,b,c])
x1 = np.array([0,0,d/c])
x2 = np.array([0,d/b,0])
def is_equal(n,m):
return n-m < 1e-10
def is_on_the_plane(v):
return is_equal(v[0]*a + v[1]*b + v[2]*c, d)
assert is_on_the_plane(x1)
assert is_on_the_plane(x2)
# Get the normal basis
b1 = x2 - x1
b2 = np.cross(n, b1)
c1 = x1 + b1 + b2
c2 = x1 + b1 - b2
c3 = x1 - b1 + b2
c4 = x1 - b1 - b2
assert is_on_the_plane(c1)
assert is_on_the_plane(c2)
assert is_on_the_plane(c3)
assert is_on_the_plane(c4)
assert is_equal(np.dot(c1-c3, c1-x2), 0)
assert is_equal(np.dot(c2-c1, c2-c4), 0)
# etc. :
# c3 c1
#
# x1
#
# c4 c2
It is actually a square, but you can surely find out how to make it a less specific rectangle.

Related

Optimizing gaussian heatmap generation

I have a set of 68 keypoints (size [68, 2]) that I am mapping to gaussian heatmaps. To do this, I have the following function:
def generate_gaussian(t, x, y, sigma=10):
"""
Generates a 2D Gaussian point at location x,y in tensor t.
x should be in range (-1, 1).
sigma is the standard deviation of the generated 2D Gaussian.
"""
h,w = t.shape
# Heatmap pixel per output pixel
mu_x = int(0.5 * (x + 1.) * w)
mu_y = int(0.5 * (y + 1.) * h)
tmp_size = sigma * 3
# Top-left
x1,y1 = int(mu_x - tmp_size), int(mu_y - tmp_size)
# Bottom right
x2, y2 = int(mu_x + tmp_size + 1), int(mu_y + tmp_size + 1)
if x1 >= w or y1 >= h or x2 < 0 or y2 < 0:
return t
size = 2 * tmp_size + 1
tx = np.arange(0, size, 1, np.float32)
ty = tx[:, np.newaxis]
x0 = y0 = size // 2
# The gaussian is not normalized, we want the center value to equal 1
g = torch.tensor(np.exp(- ((tx - x0) ** 2 + (ty - y0) ** 2) / (2 * sigma ** 2)))
# Determine the bounds of the source gaussian
g_x_min, g_x_max = max(0, -x1), min(x2, w) - x1
g_y_min, g_y_max = max(0, -y1), min(y2, h) - y1
# Image range
img_x_min, img_x_max = max(0, x1), min(x2, w)
img_y_min, img_y_max = max(0, y1), min(y2, h)
t[img_y_min:img_y_max, img_x_min:img_x_max] = \
g[g_y_min:g_y_max, g_x_min:g_x_max]
return t
def rescale(a, img_size):
# scale tensor to [-1, 1]
return 2 * a / img_size[0] - 1
My current code uses a for loop to compute the gaussian heatmap for each of the 68 keypoint coordinates, then stacks the resulting tensors to create a [68, H, W] tensor:
x_k1 = [generate_gaussian(torch.zeros(H, W), x, y) for x, y in rescale(kp1.numpy(), frame.shape)]
x_k1 = torch.stack(x_k1, dim=0)
However, this method is super slow. Is there some way that I can do this without a for loop?
Edit:
I tried #Cris Luengo's proposal to compute a 1D Gaussian:
def generate_gaussian1D(t, x, y, sigma=10):
h,w = t.shape
# Heatmap pixel per output pixel
mu_x = int(0.5 * (x + 1.) * w)
mu_y = int(0.5 * (y + 1.) * h)
tmp_size = sigma * 3
# Top-left
x1, y1 = int(mu_x - tmp_size), int(mu_y - tmp_size)
# Bottom right
x2, y2 = int(mu_x + tmp_size + 1), int(mu_y + tmp_size + 1)
if x1 >= w or y1 >= h or x2 < 0 or y2 < 0:
return t
size = 2 * tmp_size + 1
tx = np.arange(0, size, 1, np.float32)
ty = tx[:, np.newaxis]
x0 = y0 = size // 2
g = torch.tensor(np.exp(-np.power(tx - mu_x, 2.) / (2 * np.power(sigma, 2.))))
g = g * g[:, None]
g_x_min, g_x_max = max(0, -x1), min(x2, w) - x1
g_y_min, g_y_max = max(0, -y1), min(y2, h) - y1
img_x_min, img_x_max = max(0, x1), min(x2, w)
img_y_min, img_y_max = max(0, y1), min(y2, h)
t[img_y_min:img_y_max, img_x_min:img_x_max] = \
g[g_y_min:g_y_max, g_x_min:g_x_max]
return t
but my output ends up being an incomplete gaussian.
I'm not sure what I'm doing wrong. Any help would be appreciated.
You generate an NxN array g with a Gaussian centered on its center pixel. N is computed such that it extends by 3*sigma from that center pixel. This is the fastest way to build such an array:
tmp_size = sigma * 3
tx = np.arange(1, tmp_size + 1, 1, np.float32)
g = np.exp(-(tx**2) / (2 * sigma**2))
g = np.concatenate((np.flip(g), [1], g))
g = g * g[:, None]
What we're doing here is compute half a 1D Gaussian. We don't even bother computing the value of the Gaussian for the middle pixel, which we know will be 1. We then build the full 1D Gaussian by flipping our half-Gaussian and concatenating. Finally, the 2D Gaussian is built by the outer product of the 1D Gaussian with itself.
We could shave a bit of extra time by building a quarter of the 2D Gaussian, then concatenating four rotated copies of it. But the difference in computational cost is not very large, and this is much simpler. Note that np.exp is the most expensive operation here by far, so just minimizing how often we call it we significantly reduce the computational cost.
However, the best way to speed up the complete code is to compute the array g only once, rather than anew for each key point. Note how your sigma doesn't change, so all the arrays g that are computed are identical. If you compute it only once, it no longer matters which method you use to compute it, since this will be a minimal portion of the total program anyway.
You could, for example, have a global variable _gaussian to hold your array, and have your function compute it only the first time it is called. Or you could separate your function into two functions, one that constructs this array, and one that copies it into an image, and call them as follows:
g = create_gaussian(sigma=3)
x_k1 = [
copy_gaussian(torch.zeros(H, W), x, y, g)
for x, y in rescale(kp1.numpy(), frame.shape)
]
On the other hand, you're likely best off using existing functionality. For example, DIPlib has a function dip.DrawBandlimitedPoint() [disclosure: I'm an author] that adds a Gaussian blob to an image. Likely you'll find similar functions in other libraries.

Find coordinates of points given their distances

I have to find in Python the coordinates of the points A, B, C, D given their distances and the gradient of the line L (the one that passes through the center), which is parallel to segments AD and BC and orthogonal to segments AB and CD.
That is the code I wrote:
import numpy as np
# Gradient of the known line
l_gradient = 0.17
l_angle = np.arctan(l_gradient)
# Length of the segments
ad_distance = 1
ab_distance = 2
# Gradient and Intercept of lines AB and DC with the y axes
ab_gradient = dc_gradient = -1 / l_gradient # orthogonal to L
dc_intercept = (ad_distance / 2) / np.sin(l_angle) # Inverse formula of the right triangle
ab_intercept = - dc_intercept
# Gradient and Intercept of lines AD and BC with the y axes
ad_gradient = bc_gradient = l_gradient # parallel to L
ad_intercept = (ab_distance / 2) / np.cos(l_angle) # Inverse formula of the right triangle
bc_intercept = - ad_intercept
I think the easiest way to do this is first assume the gradient is 0. Then we have our points:
ad_distance = 1
ab_distance = 2
points = np.array([
[-ad_distance / 2, +ab_distance / 2], # A
[-ad_distance / 2, -ab_distance / 2], # B
[+ad_distance / 2, -ab_distance / 2], # C
[+ad_distance / 2, +ab_distance / 2], # D
])
Note that at the bottom we have a triangle with sides (x, l_gradient x, sqrt(1 + l_gradient^2) x). And remember cos(angle) = adjacent / hypot.
Thus we have:
l_gradient = 0.17
l_cos = 1 / np.sqrt(1 + l_gradient**2)
l_sin = l_gradient * l_cos
Now we can use those to construct a rotation matrix, and rotate our points into the correct positions:
l_rot = np.array([[l_cos , -l_sin], [l_sin, l_cos]])
points = (l_rot # points.T).T
No trigonometry functions required!

How can I go about finding points where there is a bend/cut in my data?

I have the following data points: There are 5 sublists in this list of data. What I am trying to do is find the points where there is a maximum amount of curvature.
for i in range(len(smallest_5)):
x = [x for x,y in smallest_5[i]]
y = [y for x,y in smallest_5[i]]
plt.scatter(x,y)
plt.savefig('bend'+str(count)+'.png')
plt.show()
I've used this code to plot the points.
sub_curvature = []
for i in range(len(smallest_5)):
a = np.array(smallest_5[i])
dx_dt = np.gradient(a[:,0])
dy_dt = np.gradient(a[:,1])
velocity = np.array([ [dx_dt[i], dy_dt[i]] for i in range(dx_dt.size)])
ds_dt = np.sqrt(dx_dt * dx_dt + dy_dt * dy_dt)
tangent = np.array([1/ds_dt] * 2).transpose() * velocity
tangent_x = tangent[:, 0]
tangent_y = tangent[:, 1]
deriv_tangent_x = np.gradient(tangent_x)
deriv_tangent_y = np.gradient(tangent_y)
dT_dt = np.array([ [deriv_tangent_x[i], deriv_tangent_y[i]] for i in range(deriv_tangent_x.size)])
length_dT_dt = np.sqrt(deriv_tangent_x * deriv_tangent_x + deriv_tangent_y * deriv_tangent_y)
normal = np.array([1/length_dT_dt] * 2).transpose() * dT_dt
d2s_dt2 = np.gradient(ds_dt)
d2x_dt2 = np.gradient(dx_dt)
d2y_dt2 = np.gradient(dy_dt)
curvature = np.abs(d2x_dt2 * dy_dt - dx_dt * d2y_dt2) / (dx_dt * dx_dt + dy_dt * dy_dt)**1.5
t_component = np.array([d2s_dt2] * 2).transpose()
n_component = np.array([curvature * ds_dt * ds_dt] * 2).transpose()
acceleration = t_component * tangent + n_component * normal
sub_curvature.append(curvature)
I used the code above to calculate the curvature of individual points on the data.
Above are some of the graphs I created using the data. As you can see, the first one has no real bend but the last two have a point where there is a large bend. How could I go about identifying this region? Is it correct to calculate the curvature for individual points or should I look at the curvature over a sliding window of points? Thank you!
If we assume "curvature" to mean circular curvature, then you'll need a sliding window over 3 points (since 3 points determine a circle).
For any three points (a,b,c) the curvature is 2 * |(a-b) x (b-c)| / (|a-b| * |b-c| * |c-b|).
We can get a-b and b-c from
ab = smallest_5[1:] - smallest_5[:-1]
and a-c from:
ac = smallest_5[2:] - smallest_5[:-2]
Then the squared curvature is:
curv_sq = 4 * (np.cross(ab[1:], ab[:-1])**2).sum() / ((ab[1:]**2).sum() * (ab[:-1]**2).sum() * (ac**2).sum())
Since we're just looking for a maximum curvature, we don't actually have to take the square root of that. We can find the index of the point with maximum curvature with
max_curv_index = np.argmax(curv_sq)
As an idea, you can find the minimum y which is not the first or the last value in the y-dimension of the array. For example:
s4 = np.array(smallest_5[4]).T # exctract a sub-array
min_y = np.agrmin(s4[1]) # gives 13
min_y == (0 or len(s4[1]-1) # gives False, so the minimum is in the middle of the curve
s0 = np.array(smallest_5[0]).T # exctract a sub-array
min_y = np.agrmin(s0[1]) # gives 16
min_y == (0 or len(s0[1]-1) # gives True, so the minimum is not in the middle of the curve

Rotation of 3d plane intoo a xy plane

I want to rotate a multiple points that are in the same 3D plane ax+by+cz=d,to a 2D plane xy. I was able to rotate the plane but not fully.
as you can see in this picture(the red points is circule in the xy plane, the yellow is the circule I want to rotate, and the red is the points are that I was able to rotate.
I tried to create the rotation matrix calculating the normal vector of the plane (a,b,c) and then calculating this rotation matrix! , by rotating the plane by the vector perpendicular to (a,b,c) and (0,0,1).
def grid2d_perplane(plane): # creates a 2d grid in a plane ax+by+cz=d by calculating a,b,c,d
p1 = copy.copy(plane[0])
p2 = copy.copy(plane[13])
p3 = copy.copy(plane[30])
# These two vectors are in the plane
v1 = p3 - p1
v2 = p2 - p1
# the cross product is a vector normal to the plane
cp = np.cross(v1, v2)
cp /= np.sqrt(cp[0]*cp[0] + cp[1]*cp[1] +cp[2]*cp[2])
a, b, c = cp
# This evaluates a * x3 + b * y3 + c * z3 which equals d
d = np.dot(cp, p3)
maxx = np.max(plane[:,0:1])
maxy = np.max(plane[:,1:2])
minx = np.min(plane[:,0:1])
miny = np.min(plane[:,1:2])
x = np.arange(minx*2,maxx*2,0.25)
y = np.arange(miny*2,maxy*2,0.25)
xx,yy= np.meshgrid(x,y)
zz = (d - a * xx - b * yy) / c
return xx, yy, zz, a , b , c, d
def transfor2d(p,a,b,c):
"The function will transform a set of points from a 3d plane to xy plane we have to keep the indexes intact for us to use later"
#TODO make it only rotation, and try to translate after wards
norm = np.sqrt(a**2+ b**2 +c **2)
cos = c /norm
theta = np.arccos(cos)
# if cos < 0:
# cos = -cos # this is a hack I have to test it again.
sen = np.sqrt((a**2+ b**2)/(norm**2))
theta1 = np.arcsin(sen)
print(theta,theta1)
u_one = b /norm
u_two = -a /norm
u_one_s = u_one**2
u_two_s = u_two**2
T11 = cos + (u_one_s*(1-cos))
T12 = ((u_one)*(u_two))*(1-cos)
T13 = u_two * sen
T21 = ((u_one)*(u_two))*(1-cos)
T22 = cos + (u_two_s*(1-cos))
T23 = -u_one * sen
T31 = -u_two * sen
T32 = u_one * sen
T33 = cos
matrix = np.zeros((3,3),np.float32)
matrix[0][0] = T11
matrix[0][1] = T12
matrix[0][2] = T13
matrix[1][0] = T21
matrix[1][1] = T22
matrix[1][2] = T23
matrix[2][0] = T31
matrix[2][1] = T32
matrix[2][2] = T33
m = matrix.dot(p)
return np.array([m[0],m[1],m[2]])
The expected results should to make the yellow points go to the red points. However the rotation matrix is not doing this. Can you see any errors that I'm making? I think that theoretically should work.

cartesian coordinates : generate coordinates of 4 points around another point

I'd like to place 4 points around a point on a sphere (cartesian coordinates: x y z), it doesn't matter how far these 4 points are from the center point (straight line distance or spherical distance) but I'd like these 4 points to be the same distance D from the center point (ideally the 5 points should have a + or x shape, so one north, one south, one east and one south).
I could do it by changing one variable (x, y or z) then keeping another the same and calculating the last variable based on the formula x * x + y * y + z * z = radius * radius but that didn't give good results. I could also maybe use the pythagorean theorem to get the distance between each of the 4 points and the center but I think there is a better formula that I don't know (and couldn't find by doing my research).
Thank you.
Some math
AFAIU your problem is that you have a sphere and a point on the sphere and you want to add 4 more points on the same sphere that would form a kind of a cross on the surface of the sphere around the target point.
I think it is easier to think about this problem in terms of vectors. You have a vector from the center of the sphere to your target point V of size R. All the point lying on the distance d from the target point form another sphere. The crossing of two sphere is a circle. Obviously this circle lies in a plane that is orthogonal to V. Solving a simple system of equations you can find that the distance from the target point to that plane is d^2/(2*R). So the vector from the center of the original sphere to the center of the circle:
Vc = V * (1 - d^2/(2*R^2))
and the radius of that circle is
Rc = sqrt(d^2 - (d^2/(2*R))**2)
So now to select 4 points, you need to select two orthogonal unit vectors lying in that plane D1 and D2. Then 4 points would be Vc + Rc*D1, Vc - Rc*D1, Vc + Rc*D2, and Vc - Rc*D2. To do this you may first select D1 fixing z =0 and switch x and y in Vc
D1 = (Vy/sqrt(Vx^2+Vy^2), -Vx/sqrt(Vx^2+Vy^2), 0)
and then find D2 as a result of cross-product of V and D1. This will work unless unless Vx = Vy = 0 (i.e. V goes along the z-axis) but in that case you can select
D1 = (1,0,0)
D2 = (0,1,0)
Some code
And here is some Python code that implements that math:
def cross_product(v1, v2):
return (v1[1] * v2[2] - v1[2] * v2[1],
v1[2] * v2[0] - v1[0] * v2[2],
v1[0] * v2[1] - v1[1] * v2[0])
def find_marks(sphereCenter, target, d):
lsc = list(sphereCenter)
lt0 = list(target)
lt1 = map(lambda c1, c0: (c1 - c0), lt0, lsc) # shift everything as if sphereCenter is (0,0,0)
rs2 = sum(map(lambda x: x ** 2, lt1)) # spehere radius**2
rs = rs2 ** 0.5
dv = d ** 2 / 2.0 / rs
dvf = d ** 2 / 2.0 / rs2
lcc = map(lambda c: c * (1 - dvf), lt1) # center of the circle in the orthogonal plane
rc = (d ** 2 - dv ** 2) ** 0.5 # orthogonal circle radius
relEps = 0.0001
absEps = relEps * rs
dir1 = (lt1[1], -lt1[0], 0) # select any direction orthogonal to the original vector
dl1 = (lt1[0] ** 2 + lt1[1] ** 2) ** 0.5
# if original vector is (0,0, z) then we've got dir1 = (0,0,0) but we can use (1,0,0) as our vector
if abs(dl1) < absEps:
dir1 = (rc, 0, 0)
dir2 = (0, rc, 0)
else:
dir1 = map(lambda c: rc * c / dl1, dir1)
dir2 = cross_product(lt1, dir1)
dl2 = sum(map(lambda c: c ** 2, dir2)) ** 0.5
dir2 = map(lambda c: rc * c / dl2, dir2)
p1 = map(lambda c0, c1, c2: c0 + c1 + c2, lsc, lcc, dir1)
p2 = map(lambda c0, c1, c2: c0 + c1 + c2, lsc, lcc, dir2)
p3 = map(lambda c0, c1, c2: c0 + c1 - c2, lsc, lcc, dir1)
p4 = map(lambda c0, c1, c2: c0 + c1 - c2, lsc, lcc, dir2)
return [tuple(p1), tuple(p2), tuple(p3), tuple(p4)]
For an extreme case
find_marks((0, 0, 0), (12, 5, 0), 13.0 * 2 ** 0.5)
i.e. for a circle of radius 13 with a center at (0,0,0), the target point lying on the big circle in the plane parallel to the xy-plane and d = sqrt(2)*R, the answer is
[(4.999999999999996, -12.000000000000004, 0.0),
(-5.329070518200751e-15, -2.220446049250313e-15, -13.0),
(-5.000000000000006, 12.0, 0.0),
(-5.329070518200751e-15, -2.220446049250313e-15, 13.0)]
So two points (2-nd and 4-th) are just two z-extremes and the other two are 90° rotations of the target point in the xy-plane which looks quite OK.
For a less extreme example:
find_marks((1, 2, 3), (13, 7, 3), 1)
which is the previous example with d reduced to 1 and with the original center moved to (1,2,3)
[(13.34882784191617, 6.06281317940119, 3.0),
(12.964497041420119, 6.985207100591716, 2.000739918710263),
(12.580166240924067, 7.907601021782242, 3.0),
(12.964497041420119, 6.985207100591716, 3.999260081289737)]
which also looks plausible

Categories