user1185675
user1185675

Reputation: 501

Distance between point and a line (from two points)

I'm using Python+Numpy (can maybe also use Scipy) and have three 2D points

(P1, P2, P3); 

I am trying to get the distance from P3 perpendicular to a line drawn between P1 and P2. Let P1=(x1,y1), P2=(x2,y2) and P3=(x3,y3)

In vector notation this would be pretty easy, but I'm fairly new to python/numpy and can't get anythng that works (or even close).

Any tips appreciated, thanks!

Upvotes: 47

Views: 111429

Answers (10)

Mad Physicist
Mad Physicist

Reputation: 114230

Cross products are helpful for the 2D case, but they do not generalize well to other dimensions. Dot products do however. The dot product of two orthogonal vectors is zero in any space, which you can use to come up with a simple solution.

Let's say you have P4 on the same line as P1-P2. You could parametrize it with parameter t such that

P4 = P1 + t * (P2 - P1)

The goal is to find P4 such that

(P3 - P4) . (P2 - P1) == 0

Expanding P4 in terms of t and simplifying:

(P3 - P1 - t * (P2 - P1)) . (P2 - P1) == 0
(P3 - P1) . (P2 - P1) == t * ||P2 - P1||^2
t = (P3 - P1) . (P2 - P1) / ||P2 - P1||^2

You can now plug t back into the original definition of P4 to solve for the length ||P3 - P4||

D = ||P3 - P4||
  = ||P3 - P1 - t * (P2 - P1)||
  = ||(P3 - P1) - (P3 - P1) . (P2 - P1) / ||P2 - P1||^2 * (P2 - P1)||

Conceptually, this is the norm of the vector (P3 - P1) with the component along (P2 - P1) subtracted off.

I've written a function in my library of utility routines called haggis. You can use haggis.math.segment_distance to compute the distance to the entire line (not just the bounded line segment) like this:

d = haggis.math.segment_distance(P3, P1, P2, segment=False)

Upvotes: 1

Claude de Rijke-Thomas
Claude de Rijke-Thomas

Reputation: 101

For anyone who would like this operation to be performed as fast as possible (with the compromise of readability) in 2D (~8x faster than otherwise on Python 3.12), I would recommend:

from math import sqrt
p1_0, p2_0, p3_0, p1_1, p2_1, p3_1 = p1[0], p2[0], p3[0], p1[1], p2[1], p3[1]
p2_0_minus_p1_0, p2_1_minus_p1_1, p3_0_minus_p1_0, p3_1_minus_p1_1 = p2_0-p1_0, p2_1-p1_1, p3_0-p1_0, p3_1-p1_1
closest_distance = (p2_0_minus_p1_0*p3_1_minus_p1_1 - p3_0_minus_p1_0*p2_1_minus_p1_1)/sqrt(p2_0_minus_p1_0*p2_0_minus_p1_0 + p2_1_minus_p1_1*p2_1_minus_p1_1)

Note that this will give negative distances if the point is on the right-hand side of the line (this can be amended by adding the abs() function around the numerator). The code calculates the closest distance between the point and the infinite straight line that extends beyond p1 and p2. Also math.sqrt() is much faster than np.sqrt() because it refers to the sqrt() function written in C.

Upvotes: 0

azurys
azurys

Reputation: 7

3D distance should use np.dot def threeD_corres(points_3_d,pre_points_3_d,points_camera):

  for j in  range (0,len(pre_points_3_d)):
      vec1 = list(map(lambda x:x[0]- x[1],zip(pre_points_3_d[j], points_camera)))
      vec2 = list(map(lambda x:x[0]- x[1],zip(pre_points_3_d[j], points_3_d[j])))
      vec3 =  list(map(lambda x:x[0]- x[1],zip(points_3_d[j], points_camera)))
      distance = np.abs(np.dot(vec1_1,vec2_2))/np.linalg.norm(vec3)

      print("#########distance:\n",distance)
  return  distance

Upvotes: -3

ajayramesh
ajayramesh

Reputation: 3774

Based on the accepted answer

Test with below line equation -

Find the perpendicular distance from the point (5, 6) to the line −2x + 3y + 4 = 0

import numpy as np
norm = np.linalg.norm

p1 = np.array([0,-4/3])
p2 = np.array([2, 0])

p3 = np.array([5, 6])
d = np.abs(norm(np.cross(p2-p1, p1-p3)))/norm(p2-p1)
# output d = 3.328201177351375

Upvotes: 5

Song
Song

Reputation: 328

Shortest Distance from Point to a Line

This is the code I got from https://www.geeksforgeeks.org:

import math 

# Function to find distance 
def shortest_distance(x1, y1, a, b, c):    
    d = abs((a * x1 + b * y1 + c)) / (math.sqrt(a * a + b * b)) 
    print("Perpendicular distance is", d)

Now you have to find A, B, C, x, and y.

import numpy as np
closest = []
x = (x ,y)
y = (x, y)
coef = np.polyfit(x, y, 1)
A = coef[0]
B = coef[1]
C = A*x[0] + B*x[1]

Now you can plug in the values:

shortest_dis = shortest_distance(x, y, A, B, C)

The full code may look like this:

import math
import numpy as np

def shortest_distance(x1, y1, a, b, c):    
    d = abs((a * x1 + b * y1 + c)) / (math.sqrt(a * a + b * b)) 
    print("Perpendicular distance is", d)

closest = []
x = (x ,y)
y = (x, y)
coef = np.polyfit(x, y, 1)
A = coef[0]
B = coef[1]
C = A*x[0] + B*x[1]
shortest_dis = shortest_distance(x, y, A, B, C)

Please let me know if any of this is unclear.

Upvotes: 1

Aydar Akhmetzyanov
Aydar Akhmetzyanov

Reputation: 81

To find distance to line from point if you have slope and intercept you can use formula from wiki https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line Python:

def distance(point,coef):
    return abs((coef[0]*point[0])-point[1]+coef[1])/math.sqrt((coef[0]*coef[0])+1)

coef is a tuple with slope and intercept

Upvotes: 6

id101112
id101112

Reputation: 1042

abs((x2-x1)*(y1-y0) - (x1-x0)*(y2-y1)) / np.sqrt(np.square(x2-x1) + np.square(y2-y1))

Can be used directly through the formula, just have to plug in the values and boom it will work.

Upvotes: 4

Szymon Szott
Szymon Szott

Reputation: 141

For the above-mentioned answers to work, the points need to be numpy arrays, here's a working example:

import numpy as np
p1=np.array([0,0])
p2=np.array([10,10])
p3=np.array([5,7])
d=np.cross(p2-p1,p3-p1)/np.linalg.norm(p2-p1)

Upvotes: 14

Martin Hardcastle
Martin Hardcastle

Reputation: 311

np.cross returns the z-coordinate of the cross product only for 2D vectors. So the first norm in the accepted answer is not needed, and is actually dangerous if p3 is an array of vectors rather than a single vector. Best just to use

d=np.cross(p2-p1,p3-p1)/norm(p2-p1)

which for an array of points p3 will give you an array of distances from the line.

Upvotes: 21

DotPi
DotPi

Reputation: 4117

Try using the norm function from numpy.linalg

d = norm(np.cross(p2-p1, p1-p3))/norm(p2-p1)

Upvotes: 64

Related Questions