46

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!

Karel Macek
  • 1,119
  • 2
  • 11
  • 24
user1185675
  • 495
  • 1
  • 4
  • 4
  • In case you need the perpendicular vector, use `P12 = p2 - p1 P13 = p3 - p1 proj_13over12 = np.dot(P13, P12) * P12 / norm(P12) ** 2 perpendicular = P13 - proj_13over12` ref. [https://stackoverflow.com/questions/76644080/find-perpendicular-distance-vector-from-a-point-to-a-straight-line-in-3d-space] – beta green Jul 28 '23 at 12:22

9 Answers9

62

Try using the norm function from numpy.linalg

d = norm(np.cross(p2-p1, p1-p3))/norm(p2-p1)
DotPi
  • 3,977
  • 6
  • 33
  • 53
  • 28
    Should be `np.abs(np.cross(p2-p1, p1-p3)) / norm(p2-p1))`? – nn0p Apr 27 '18 at 18:34
  • 2
    unsupported operand type(s) for -: 'tuple' and 'tuple'? What's this? – Frank Sep 10 '18 at 07:21
  • 4
    you need to convert the points to numpy arrays. This can be done like so: `p1 = np.asarray(p1)` – brad Oct 15 '18 at 18:02
  • 4
    `abs((x2-x1)*(y1-y0) - (x1-x0)*(y2-y1)) / np.sqrt(np.square(x2-x1) + np.square(y2-y1))` I used it this way, and it gave me the same answer in python – id101112 Oct 23 '18 at 19:34
  • 3
    Does this assume the line extends to infinity, or the distance it generates become larger as points `P3` grow farther away from the endpoints of the line segment generated by `P1,P2`? – Cloud Feb 19 '19 at 15:49
  • brilliant. cross is correct. line does extend to infinity. this gives you |p1-p3|sin(alpha). – Gazihan Alankus Mar 03 '19 at 19:52
  • why abs ? @nn0p – ajayramesh Jun 14 '20 at 02:08
  • 2
    @ajayramesh You need abs if you have a vector of p1, p2 and p3; or one could say Nx2 arrays. Since we are working in 2D, there is no cross-product for 2D coordinates, a Z axis is appended with value 0. Numpy doc says for np.cross "In cases where both input vectors have dimension 2, the z-component of the cross product is returned.", therefore you will receive an Nx1 array of scalars, but mathematically you would expect a Nx3 array of vectors (x and y are equal to 0). If you use the np.norm, you will get the norm of the Nx1 array, but what you want is the norm of each vector in the Nx3 array. – codingmonkey87 Jul 27 '20 at 13:53
21

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.

  • could you explain how `norm` in the numerator is dangerous if `p3` is an array? – dinosaur Jan 17 '18 at 19:44
  • 2
    @dinosaur because `norm` will treat the 1D array returned by `np.cross` as one big vector and return its "size". – Martin Hardcastle Jan 17 '18 at 21:36
  • 2
    I only got a _signed_ distance after removing the first norm, that is, a distance that tells me if the point is at the right or left side of the line, according to some convention. – heltonbiker Jun 26 '18 at 22:38
  • missing imports for np and norm, see Szymon Szott's answer for complete code – David Nov 02 '22 at 13:02
14

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)
Szymon Szott
  • 141
  • 1
  • 2
5

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

5

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

ajayramesh
  • 3,576
  • 8
  • 50
  • 75
4
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.

id101112
  • 1,012
  • 2
  • 16
  • 28
1

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.

Song
  • 298
  • 5
  • 20
  • 2
    not clear which are the coordinates of the line and which are the coordinates of the point – Lorenzo Sciuto Sep 24 '19 at 10:36
  • @LorenzoSciuto These are coefficients for linear equations, but clarifications would help. – tsturzl Jan 13 '20 at 19:20
  • I have compared both the np.cross method as described above and this method. The polyfit works fine for both. However, when I compared the distance using np.cross and the formula you posted, there was a difference in results (very slightly). Since I was working with moderately large dataset, I summed the distance from point to the line. Imagine what, the "geekforGeeks" method gave the sum nearly zero. – Prasanta Bandyopadhyay Sep 08 '21 at 13:44
1

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 therefore have

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

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)
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
-3

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
azurys
  • 7
  • 1
  • 1
    Welcome to Stack Overflow. This question has been already answered several times, and while different approaches are encouraged, you should provide some background in the key differences your implemented solution has. Please edit your answer to explain your code and when this approach should be taken. Thanks for taking your time to answer, though! – Marc Sances Aug 21 '20 at 18:11