web-dev-qa-db-fra.com

Intersections Numpy et Line

Comment pourrais-je utiliser numpy pour calculer l'intersection entre deux segments de ligne?

Dans le code, j'ai segment1 = ((x1, y1), (x2, y2)) et segment2 = ((x1, y1), (x2, y2)). Remarque le segment 1 n'est pas égal au segment2. Donc, dans mon code, j'ai également calculé la pente et l'ordonnée à l'origine, ce serait bien si cela pouvait être évité, mais je ne sais pas comment.

J'utilise la règle de Cramer avec une fonction que j'ai écrite en Python, mais j'aimerais trouver un moyen plus rapide de le faire.

21
Xavier

Volé directement à http://www.cs.mun.ca/~rod/2500/notes/numpy-arrays/numpy-arrays.html

#
# line segment intersection using vectors
# see Computer Graphics by F.S. Hill
#
from numpy import *
def perp( a ) :
    b = empty_like(a)
    b[0] = -a[1]
    b[1] = a[0]
    return b

# line segment a given by endpoints a1, a2
# line segment b given by endpoints b1, b2
# return 
def seg_intersect(a1,a2, b1,b2) :
    da = a2-a1
    db = b2-b1
    dp = a1-b1
    dap = perp(da)
    denom = dot( dap, db)
    num = dot( dap, dp )
    return (num / denom.astype(float))*db + b1

p1 = array( [0.0, 0.0] )
p2 = array( [1.0, 0.0] )

p3 = array( [4.0, -5.0] )
p4 = array( [4.0, 2.0] )

print seg_intersect( p1,p2, p3,p4)

p1 = array( [2.0, 2.0] )
p2 = array( [4.0, 3.0] )

p3 = array( [6.0, 0.0] )
p4 = array( [6.0, 3.0] )

print seg_intersect( p1,p2, p3,p4)
29
Hamish Grubijan
import numpy as np

def get_intersect(a1, a2, b1, b2):
    """ 
    Returns the point of intersection of the lines passing through a2,a1 and b2,b1.
    a1: [x, y] a point on the first line
    a2: [x, y] another point on the first line
    b1: [x, y] a point on the second line
    b2: [x, y] another point on the second line
    """
    s = np.vstack([a1,a2,b1,b2])        # s for stacked
    h = np.hstack((s, np.ones((4, 1)))) # h for homogeneous
    l1 = np.cross(h[0], h[1])           # get first line
    l2 = np.cross(h[2], h[3])           # get second line
    x, y, z = np.cross(l1, l2)          # point of intersection
    if z == 0:                          # lines are parallel
        return (float('inf'), float('inf'))
    return (x/z, y/z)

if __== "__main__":
    print get_intersect((0, 1), (0, 2), (1, 10), (1, 9))  # parallel  lines
    print get_intersect((0, 1), (0, 2), (1, 10), (2, 10)) # vertical and horizontal lines
    print get_intersect((0, 1), (1, 2), (0, 10), (1, 9))  # another line for fun

Explication

Notez que l'équation d'une ligne est ax+by+c=0. Donc si un point est sur cette ligne, alors c'est une solution pour (a,b,c).(x,y,1)=0 (. est le produit scalaire)

soit l1=(a1,b1,c1), l2=(a2,b2,c2) deux lignes et p1=(x1,y1,1), p2=(x2,y2,1) deux points.


Trouver la ligne passant par deux points:

soit t=p1xp2 (le produit croisé de deux points) un vecteur représentant une ligne.

Nous savons que p1 est sur la ligne t parce que t.p1 = (p1xp2).p1=0. Nous savons également que p2 est sur t car t.p2 = (p1xp2).p2=0. Donc, t doit être la ligne passant par p1 et p2

Cela signifie que nous pouvons obtenir la représentation vectorielle d'une ligne en prenant le produit croisé de deux points sur cette ligne .


Trouver le point d'intersection:

Soit maintenant r=l1xl2 (le produit croisé de deux lignes) un vecteur représentant un point.

Nous savons que r se trouve sur l1 car r.l1=(l1xl2).l1=0. Nous savons également que r se trouve sur l2 car r.l2=(l1xl2).l2=0. Donc, r doit être le point d'intersection des lignes l1 et l2

Fait intéressant, nous pouvons trouver le point d'intersection en prenant le produit croisé de deux lignes .

11
Norbu Tsering

C’est une réponse tardive, peut-être, mais c’est le premier résultat lorsque j’ai cherché Google 'intersections de lignes numpy'. Dans mon cas, il y a deux lignes dans un avion et je voulais obtenir rapidement toutes les intersections entre elles. La solution de Hamish serait lente - nécessitant une boucle imbriquée sur tous les segments de ligne. 

Voici comment le faire sans une boucle for (c'est assez rapide):

from numpy import where, dstack, diff, meshgrid

def find_intersections(A, B):

    # min, max and all for arrays
    amin = lambda x1, x2: where(x1<x2, x1, x2)
    amax = lambda x1, x2: where(x1>x2, x1, x2)
    aall = lambda abools: dstack(abools).all(axis=2)
    slope = lambda line: (lambda d: d[:,1]/d[:,0])(diff(line, axis=0))

    x11, x21 = meshgrid(A[:-1, 0], B[:-1, 0])
    x12, x22 = meshgrid(A[1:, 0], B[1:, 0])
    y11, y21 = meshgrid(A[:-1, 1], B[:-1, 1])
    y12, y22 = meshgrid(A[1:, 1], B[1:, 1])

    m1, m2 = meshgrid(slope(A), slope(B))
    m1inv, m2inv = 1/m1, 1/m2

    yi = (m1*(x21-x11-m2inv*y21) + y11)/(1 - m1*m2inv)
    xi = (yi - y21)*m2inv + x21

    xconds = (amin(x11, x12) < xi, xi <= amax(x11, x12), 
              amin(x21, x22) < xi, xi <= amax(x21, x22) )
    yconds = (amin(y11, y12) < yi, yi <= amax(y11, y12),
              amin(y21, y22) < yi, yi <= amax(y21, y22) )

    return xi[aall(xconds)], yi[aall(yconds)]

Ensuite, pour l'utiliser, spécifiez deux lignes en tant qu'arguments, où arg est une matrice à 2 colonnes, chaque ligne correspondant à un point (x, y): 

# example from matplotlib contour plots
Acs = contour(...)
Bsc = contour(...)

# A and B are the two lines, each is a 
# two column matrix
A = Acs.collections[0].get_paths()[0].vertices
B = Bcs.collections[0].get_paths()[0].vertices

# do it
x, y = find_intersections(A, B)

s'amuser

9
marmaduke

Ceci est une version de la réponse de @Hamish Grubijan qui fonctionne également pour plusieurs points dans chacun des arguments d'entrée, c.-à-d., a1, a2, b1, b2, peuvent être des tableaux de rangées 2D de points Nx2. La fonction perp est remplacée par un produit scalaire.

T = np.array([[0, -1], [1, 0]])
def line_intersect(a1, a2, b1, b2):
    da = np.atleast_2d(a2 - a1)
    db = np.atleast_2d(b2 - b1)
    dp = np.atleast_2d(a1 - b1)
    dap = np.dot(da, T)
    denom = np.sum(dap * db, axis=1)
    num = np.sum(dap * dp, axis=1)
    return np.atleast_2d(num / denom).T * db + b1
3
user1248490

Voici un one-liner (un peu forcé):

import numpy as np
from scipy.interpolate import interp1d

interp1d(segment1-segment2,np.arange(segment1.shape[0]))(0)

Interpoler la différence (par défaut, linéaire) et trouver un 0 de l'inverse.

À votre santé!

1
Andy Reagan

C’est ce que j’utilise pour trouver une intersection de ligne, cela fonctionne en ayant 2 points de chaque ligne ou juste un point et sa pente. Je résous fondamentalement le système d'équations linéaires.

def line_intersect(p0, p1, m0=None, m1=None, q0=None, q1=None):
    ''' intersect 2 lines given 2 points and (either associated slopes or one extra point)
    Inputs:
        p0 - first point of first line [x,y]
        p1 - fist point of second line [x,y]
        m0 - slope of first line
        m1 - slope of second line
        q0 - second point of first line [x,y]
        q1 - second point of second line [x,y]
    '''
    if m0 is  None:
        if q0 is None:
            raise ValueError('either m0 or q0 is needed')
        dy = q0[1] - p0[1]
        dx = q0[0] - p0[0]
        lhs0 = [-dy, dx]
        rhs0 = p0[1] * dx - dy * p0[0]
    else:
        lhs0 = [-m0, 1]
        rhs0 = p0[1] - m0 * p0[0]

    if m1 is  None:
        if q1 is None:
            raise ValueError('either m1 or q1 is needed')
        dy = q1[1] - p1[1]
        dx = q1[0] - p1[0]
        lhs1 = [-dy, dx]
        rhs1 = p1[1] * dx - dy * p1[0]
    else:
        lhs1 = [-m1, 1]
        rhs1 = p1[1] - m1 * p1[0]

    a = np.array([lhs0, 
                  lhs1])

    b = np.array([rhs0, 
                  rhs1])
    try:
        px = np.linalg.solve(a, b)
    except:
        px = np.array([np.nan, np.nan])

    return px
0
dashesy