orthogonal projection with numpy

后端 未结 3 2094
情话喂你
情话喂你 2020-12-14 22:50

I have a list of 3D-points for which I calculate a plane by numpy.linalg.lstsq - method. But Now I want to do a orthogonal projection for each point into this plane, but I c

3条回答
  •  佛祖请我去吃肉
    2020-12-14 23:30

    You are doing a very poor use of np.lstsq, since you are feeding it a precomputed 3x3 matrix, instead of letting it do the job. I would do it like this:

    import numpy as np
    
    def calc_plane(x, y, z):
        a = np.column_stack((x, y, np.ones_like(x)))
        return np.linalg.lstsq(a, z)[0]
    
    >>> x = np.random.rand(1000)
    >>> y = np.random.rand(1000)
    >>> z = 4*x + 5*y + 7 + np.random.rand(1000)*.1
    >>> calc_plane(x, y, z)
    array([ 3.99795126,  5.00233364,  7.05007326])
    

    It is actually more convenient to use a formula for your plane that doesn't depend on the coefficient of z not being zero, i.e. use a*x + b*y + c*z = 1. You can similarly compute a, b and c doing:

    def calc_plane_bis(x, y, z):
        a = np.column_stack((x, y, z))
        return np.linalg.lstsq(a, np.ones_like(x))[0]
    >>> calc_plane_bis(x, y, z)
    array([-0.56732299, -0.70949543,  0.14185393])
    

    To project points onto a plane, using my alternative equation, the vector (a, b, c) is perpendicular to the plane. It is easy to check that the point (a, b, c) / (a**2+b**2+c**2) is on the plane, so projection can be done by referencing all points to that point on the plane, projecting the points onto the normal vector, subtract that projection from the points, then referencing them back to the origin. You could do that as follows:

    def project_points(x, y, z, a, b, c):
        """
        Projects the points with coordinates x, y, z onto the plane
        defined by a*x + b*y + c*z = 1
        """
        vector_norm = a*a + b*b + c*c
        normal_vector = np.array([a, b, c]) / np.sqrt(vector_norm)
        point_in_plane = np.array([a, b, c]) / vector_norm
    
        points = np.column_stack((x, y, z))
        points_from_point_in_plane = points - point_in_plane
        proj_onto_normal_vector = np.dot(points_from_point_in_plane,
                                         normal_vector)
        proj_onto_plane = (points_from_point_in_plane -
                           proj_onto_normal_vector[:, None]*normal_vector)
    
        return point_in_plane + proj_onto_plane
    

    So now you can do something like:

    >>> project_points(x, y, z, *calc_plane_bis(x, y, z))
    array([[  0.13138012,   0.76009389,  11.37555123],
           [  0.71096929,   0.68711773,  13.32843506],
           [  0.14889398,   0.74404116,  11.36534936],
           ..., 
           [  0.85975642,   0.4827624 ,  12.90197969],
           [  0.48364383,   0.2963717 ,  10.46636903],
           [  0.81596472,   0.45273681,  12.57679188]])
    

提交回复
热议问题