簡體   English   中英

在 3D 球體上插入非均勻分布的點

[英]Interpolating non-uniformly distributed points on a 3D sphere

我在單位球面上有幾個點,它們根據https://www.cmu.edu/biolphys/deserno/pdf/sphere_equi.pdf 中描述的算法分布(並在下面的代碼中實現)。 在這些點中的每一個上,我都有一個值,在我的特定情況下,它表示 1 減去一個小錯誤。 如果這很重要,錯誤在[0, 0.1] ,所以我的值在[0.9, 1]

可悲的是,計算錯誤是一個代價高昂的過程,我無法根據需要計算盡可能多的點。 不過,我希望我的情節看起來像我在繪制“連續”的東西。 所以我想為我的數據擬合一個插值函數,以便能夠根據需要采樣盡可能多的點。

經過一些研究,我發現scipy.interpolate.SmoothSphereBivariateSpline似乎完全符合我的要求。 但我不能讓它正常工作。

問題:我可以用什么來插值(樣條、線性插值,目前什么都可以)我在單位球體上的數據? 答案可以是“您誤用了scipy.interpolation ,這是執行此操作的正確方法”或“此其他功能更適合您的問題”。

安裝numpyscipy應該可執行的示例代碼:

import typing as ty

import numpy
import scipy.interpolate


def get_equidistant_points(N: int) -> ty.List[numpy.ndarray]:
    """Generate approximately n points evenly distributed accros the 3-d sphere.

    This function tries to find approximately n points (might be a little less
    or more) that are evenly distributed accros the 3-dimensional unit sphere.

    The algorithm used is described in
    https://www.cmu.edu/biolphys/deserno/pdf/sphere_equi.pdf.
    """
    # Unit sphere
    r = 1

    points: ty.List[numpy.ndarray] = list()

    a = 4 * numpy.pi * r ** 2 / N
    d = numpy.sqrt(a)
    m_v = int(numpy.round(numpy.pi / d))
    d_v = numpy.pi / m_v
    d_phi = a / d_v

    for m in range(m_v):
        v = numpy.pi * (m + 0.5) / m_v
        m_phi = int(numpy.round(2 * numpy.pi * numpy.sin(v) / d_phi))
        for n in range(m_phi):
            phi = 2 * numpy.pi * n / m_phi
            points.append(
                numpy.array(
                    [
                        numpy.sin(v) * numpy.cos(phi),
                        numpy.sin(v) * numpy.sin(phi),
                        numpy.cos(v),
                    ]
                )
            )
    return points


def cartesian2spherical(x: float, y: float, z: float) -> numpy.ndarray:
    r = numpy.linalg.norm([x, y, z])
    theta = numpy.arccos(z / r)
    phi = numpy.arctan2(y, x)
    return numpy.array([r, theta, phi])


n = 100
points = get_equidistant_points(n)
# Random here, but costly in real life.
errors = numpy.random.rand(len(points)) / 10

# Change everything to spherical to use the interpolator from scipy.
ideal_spherical_points = numpy.array([cartesian2spherical(*point) for point in points])
r_interp = 1 - errors
theta_interp = ideal_spherical_points[:, 1]
phi_interp = ideal_spherical_points[:, 2]
# Change phi coordinate from [-pi, pi] to [0, 2pi] to please scipy.
phi_interp[phi_interp < 0] += 2 * numpy.pi

# Create the interpolator.
interpolator = scipy.interpolate.SmoothSphereBivariateSpline(
    theta_interp, phi_interp, r_interp
)

# Creating the finer theta and phi values for the final plot
theta = numpy.linspace(0, numpy.pi, 100, endpoint=True)
phi = numpy.linspace(0, numpy.pi * 2, 100, endpoint=True)

# Creating the coordinate grid for the unit sphere.
X = numpy.outer(numpy.sin(theta), numpy.cos(phi))
Y = numpy.outer(numpy.sin(theta), numpy.sin(phi))
Z = numpy.outer(numpy.cos(theta), numpy.ones(100))

thetas, phis = numpy.meshgrid(theta, phi)
heatmap = interpolator(thetas, phis)

上面代碼的問題:

  • 使用原樣的代碼,我有一個
    ValueError: The required storage space exceeds the available storage space: nxest or nyest too small, or s too small. The weighted least-squares spline corresponds to the current set of knots.
    在初始化interpolator實例時引發。
  • 上面的問題似乎是說我應該更改scipy.interpolate.SmoothSphereBivariateSpline參數上的s值。 我測試了s不同值,范圍從0.0001100000 ,上面的代碼總是引發,上述異常或:
     ValueError: Error code returned by bispev: 10

編輯:我在這里包括我的發現。 它們不能真正被視為解決方案,這就是為什么我正在編輯而不是作為答案發布。

通過更多的研究,我發現了這個問題Using Radial Basis Functions to Interpolate a Function on a Sphere 作者和我有完全相同的問題,並使用了不同的插值器: scipy.interpolate.Rbf 我通過替換插值器和繪圖更改了上面的代碼:

# Create the interpolator.
interpolator = scipy.interpolate.Rbf(theta_interp, phi_interp, r_interp)

# Creating the finer theta and phi values for the final plot
plot_points = 100
theta = numpy.linspace(0, numpy.pi, plot_points, endpoint=True)
phi = numpy.linspace(0, numpy.pi * 2, plot_points, endpoint=True)

# Creating the coordinate grid for the unit sphere.
X = numpy.outer(numpy.sin(theta), numpy.cos(phi))
Y = numpy.outer(numpy.sin(theta), numpy.sin(phi))
Z = numpy.outer(numpy.cos(theta), numpy.ones(plot_points))

thetas, phis = numpy.meshgrid(theta, phi)
heatmap = interpolator(thetas, phis)


import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import cm

colormap = cm.inferno
normaliser = mpl.colors.Normalize(vmin=numpy.min(heatmap), vmax=1)
scalar_mappable = cm.ScalarMappable(cmap=colormap, norm=normaliser)
scalar_mappable.set_array([])

fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.plot_surface(
    X,
    Y,
    Z,
    facecolors=colormap(normaliser(heatmap)),
    alpha=0.7,
    cmap=colormap,
)
plt.colorbar(scalar_mappable)
plt.show()

此代碼運行順利並給出以下結果: 在此處輸入圖片說明

除了在不連續的一行上,插值似乎還可以,就像在引導我上這門課的問題中一樣。 答案之一給出了使用不同距離的想法,更適合球面坐標:Haversine 距離。

def haversine(x1, x2):
    theta1, phi1 = x1
    theta2, phi2 = x2
    return 2 * numpy.arcsin(
        numpy.sqrt(
            numpy.sin((theta2 - theta1) / 2) ** 2
            + numpy.cos(theta1) * numpy.cos(theta2) * numpy.sin((phi2 - phi1) / 2) ** 2
        )
    )


# Create the interpolator.
interpolator = scipy.interpolate.Rbf(theta_interp, phi_interp, r_interp, norm=haversine)

執行時會發出警告:

LinAlgWarning: Ill-conditioned matrix (rcond=1.33262e-19): result may not be accurate.
  self.nodes = linalg.solve(self.A, self.di)

並且結果完全不是預期的:插值函數的值可能會上升到-1 ,這顯然是錯誤的。

您可以使用笛卡爾坐標而不是球坐標。

Rbf使用的默認范數參數 ( 'euclidean' ) 就足夠了

# interpolation
x, y, z = numpy.array(points).T
interpolator = scipy.interpolate.Rbf(x, y, z, r_interp)

# predict
heatmap = interpolator(X, Y, Z)

結果如下:

ax.plot_surface(
    X, Y, Z,
    rstride=1, cstride=1, 
    # or rcount=50, ccount=50,
    facecolors=colormap(normaliser(heatmap)),
    cmap=colormap,
    alpha=0.7, shade=False
)
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')

如果需要,您還可以使用余弦距離(范數參數):

def cosine(XA, XB):
    if XA.ndim == 1:
        XA = numpy.expand_dims(XA, axis=0)
    if XB.ndim == 1:
        XB = numpy.expand_dims(XB, axis=0)
    return scipy.spatial.distance.cosine(XA, XB)

余弦距離

為了更好地看到差異,我堆疊了兩個圖像,減去它們並反轉圖層。 在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM