Python VTK "normalize" 点云

Python VTK "normalize" a point cloud

我已经做了很多搜索,但还没有找到答案。我目前正在处理农田的一些数据。我有多个字段的 PLY 文件,我已使用 Python 和 VTK 成功读入、过滤和可视化这些文件。我的主要目标是最终对单个作物地块进行分割和 运行 分析。

但是为了让这个任务更容易,我首先想 "Normalize" 我的点云,这样所有的图基本上都是 "on the same level"。从我附上的图片中,您可以看到点土块从一个角向对面倾斜。所以我想要展平图像,以便地面点都在同一平面/水平面上。并相应调整点数的重置。

Point Cloud

我还包含了我的代码来展示我是如何走到这一步的。如果有人对我如何实现对一架飞机的标准化有任何建议,我将不胜感激。遗憾的是,我不能包含我的数据,因为它与工作相关。

谢谢。 乔什

import vtk
from vtk.util import numpy_support
import numpy as np

filename = 'File.ply'

# Reader
r = vtk.vtkPLYReader()
r.SetFileName(filename)

# Filters
vgf = vtk.vtkVertexGlyphFilter()
vgf.SetInputConnection(r.GetOutputPort())

# Elevation
pc = r.GetOutput()
bounds = pc.GetBounds()
#print(bounds)
minz = bounds[4]
maxz = bounds[5]
#print(bounds[4], bounds[5])
evgf = vtk.vtkElevationFilter()
evgf.SetInputConnection(vgf.GetOutputPort())
evgf.SetLowPoint(0, 0, minz)
evgf.SetHighPoint(0, 0, maxz)
#pc.GetNumberOfPoints()

# Look up table
lut = vtk.vtkLookupTable()
lut.SetHueRange(0.667, 0)
lut.SetSaturationRange(1, 1)
lut.SetValueRange(1, 1)
lut.Build

# Renderer
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(evgf.GetOutputPort())
mapper.SetLookupTable(lut)

actor = vtk.vtkActor()
actor.SetMapper(mapper)

renderer = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(renderer)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)

renderer.AddActor(actor)
renderer.SetBackground(0, 0, 0) 

renWin.Render()
iren.Start()

我曾经解决过类似的问题。在下面找到我当时使用的一些代码。它使用两个函数 fitPlanefindTransformFromVectors,您可以用自己的实现替换它们。

请注意,有多种方法可以通过一组点来拟合平面。 discusses compares scipy.optimize.minimize with scipy.linalg.lstsq. In ,建议使用PCA或RANSAC等方法。您可能想使用 sklearnnumpy 或其他模块提供的方法。我的解决方案简单地(非稳健地)计算普通最小二乘回归。

import vtk
import numpy as np
# Convert vtk to numpy arrays
from vtk.util.numpy_support import vtk_to_numpy as vtk2np

# Create a random point cloud.
center = [3.0, 2.0, 1.0]
source = vtk.vtkPointSource()
source.SetCenter(center)
source.SetNumberOfPoints(50)
source.SetRadius(1.)
source.Update()
source = source.GetOutput()
# Extract the points from the point cloud.
points = vtk2np(source.GetPoints().GetData())
points = points.transpose()

# Fit a plane. nRegression contains the normal vector of the
# regression surface.
nRegression = fitPlane(points)
# Compute a transform that maps the source center to the origin and
# plane normal to the z-axis.
trafo = findTransformFromVectors(originFrom=center,
                                 axisFrom=nRegression.transpose(),
                                 originTo=(0,0,0),
                                 axisTo=(0.,0.,1.))

# Apply transform to source.
sourceTransformed = vtk.vtkTransformFilter()
sourceTransformed.SetInputData(source)
sourceTransformed.SetTransform(trafo)
sourceTransformed.Update()

# Visualize output...

这里是我对 fitPlanefindTransformFromVectors 的实现:

# The following code has been written by normanius under the CC BY-SA 4.0 
# license.
# License:    https://creativecommons.org/licenses/by-sa/4.0/
# Author:     normanius: https://whosebug.com/users/3388962/normanius
# Date:       October 2018
# Reference:  

def fitPlane(X, tolerance=1e-10):
    '''
    Estimate the plane normal by means of ordinary least dsquares.
    Requirement: points X span the full column rank. If the points lie in a
    perfect plane, the regression problem is ill-conditioned!

    Formulas:
        a = (XX^T)^(-1)*X*z
    Surface normal:
        n = [a[0], a[1], -1]
        n = n/norm(n)
    Plane intercept:
        c = a[2]/norm(n)

    NOTE: The condition number for the pseudo-inverse improves if the
          formulation is changed to homogenous notation.
    Formulas (homogenous):
        a = (XX^T)^(-1)*[1,1,1]^T
        n = a[:-1]
        n = n/norm(n)
        c = a[-1]/norm(n)

    Arguments:
        X:          A matrix with n rows and 3 columns
        tolerance:  Minimal condition number accepted. If the condition
                    number is lower, the algorithm returns None.

    Returns:
        If the computation was successful, a numpy array of length three is
        returned that represents the estimated plane normal. On failure,
        None is returned.
    '''
    X = np.asarray(X)
    d,N = X.shape
    X = np.vstack([X,np.ones([1,N])])
    z = np.ones([d+1,1])
    XXT = np.dot(X, np.transpose(X)) # XXT=X*X^T
    if np.linalg.det(XXT) < 1e-10:
        # The test covers the case where n<3
        return None
    n = np.dot(np.linalg.inv(XXT), z)
    intercept = n[-1]
    n = n[:-1]
    scale = np.linalg.norm(n)
    n /= scale
    intercept /= scale
    return n

def findTransformFromVectors(originFrom=None, axisFrom=None,
                             originTo=None, axisTo=None,
                             origin=None,
                             scale=1):
    '''
    Compute a transformation that maps originFrom and axisFrom to originTo
    and axisTo respectively. If scale is set to 'auto', the scale will be
    determined such that the axes will also match in length:
        scale = norm(axisTo)/norm(axisFrom)

    Arguments:  originFrom:     sequences with 3 elements, or None
                axisFrom:       sequences with 3 elements, or None
                originTo:       sequences with 3 elements, or None
                axisTo:         sequences with 3 elements, or None
                origin:         sequences with 3 elements, or None,
                                overrides originFrom and originTo if set
                scale:          - scalar (isotropic scaling)
                                - sequence with 3 elements (anisotropic scaling),
                                - 'auto' (sets scale such that input axes match
                                  in length after transforming axisFrom)
                                - None (no scaling)

    Align two axes alone, assuming that we sit on (0,0,0)
        findTransformFromVectors(axisFrom=a0, axisTo=a1)
    Align two axes in one point (all calls are equivalent):
        findTransformFromVectors(origin=o, axisFrom=a0, axisTo=a1)
        findTransformFromVectors(originFrom=o, axisFrom=a0, axisTo=a1)
        findTransformFromVectors(axisFrom=a0, originTo=o, axisTo=a1)
    Move between two points:
        findTransformFromVectors(orgin=o0, originTo=o1)
    Move from one position to the other and align axes:
        findTransformFromVectors(orgin=o0, axisFrom=a0, originTo=o1, axisTo=a1)
    '''
    # Prelude with trickle-down logic.
    # Infer the origins if an information is not set.
    if origin is not None:
        # Check for ambiguous input.
        assert(originFrom is None and originTo is None)
        originFrom = origin
        originTo = origin
    if originFrom is None:
        originFrom = originTo
    if originTo is None:
        originTo = originFrom
    if originTo is None:
        # We arrive here only if no origin information was set.
        originTo = [0.,0.,0.]
        originFrom = [0.,0.,0.]
    originFrom = np.asarray(originFrom)
    originTo = np.asarray(originTo)
    # Check if any rotation will be involved.
    axisFrom = np.asarray(axisFrom)
    axisTo = np.asarray(axisTo)
    axisFromL2 = np.linalg.norm(axisFrom)
    axisToL2 = np.linalg.norm(axisTo)
    if axisFrom is None or axisTo is None or axisFromL2==0 or axisToL2==0:
        rotate = False
    else:
        rotate = not np.array_equal(axisFrom, axisTo)
    # Scale.
    if scale is None:
        scale = 1.
    if scale == 'auto':
        scale = axisToL2/axisFromL2 if axisFromL2!=0. else 1.
    if np.isscalar(scale):
        scale = scale*np.ones(3)
    if rotate:
        rAxis = np.cross(axisFrom.ravel(), axisTo.ravel())  # rotation axis
        angle = np.dot(axisFrom, axisTo) / axisFromL2 / axisToL2
        angle = np.arccos(angle)

    # Here we finally compute the transform.
    trafo = vtk.vtkTransform()
    trafo.Translate(originTo)
    if rotate:
        trafo.RotateWXYZ(angle / np.pi * 180, rAxis[0], rAxis[1], rAxis[2])
    trafo.Scale(scale[0],scale[1],scale[2])
    trafo.Translate(-originFrom)
    return trafo