Построение вогнутой формы (фокус линзы) с использованием mplot3d

В настоящее время я пытаюсь визуализировать фокусную форму линзы, используя matplotlib, в частности набор инструментов mplot3d. Я получил данные путем подгонки эллипсов к набору изображений микроскопа с разными фокусными расстояниями, такими как большой major и малый minor радиус, а также угол поворота ang указанных эллипсов. Исходя из этого, я создаю массивы x, y и z, содержащие координаты эллипсов, подобных этому.

i = 100
omega = np.linspace(0, 2 * np.pi, i, endpoint=True)

x = [major * np.cos(omega) * np.cos(np.deg2rad(ang + 90)) - minor * np.sin(omega) * np.sin(np.deg2rad(ang + 90)) for major, minor, ang in zip(maj_avg, min_avg, ang_avg)]
y = [major * np.cos(omega) * np.sin(np.deg2rad(ang + 90)) + minor * np.sin(omega) * np.cos(np.deg2rad(ang + 90)) for major, minor, ang in zip(maj_avg, min_avg, ang_avg)]
z = [np.full(i, zi) for zi in zs]

Если я теперь нарисую отдельные эллипсы в трехмерном пространстве, все будет работать, как задумано.

fig = plt.figure(figsize=(16, 12))
ax = fig.add_subplot(111, projection='3d')
for x_arr, y_arr, z_arr in zip(x, y, z):
    ax.plot(x_arr, y_arr, z_arr)

plt.show()

Линии графика эллипсов

Что я пытаюсь сделать, так это создать график поверхности из этого набора данных, который показывает фокусную форму линзы. До сих пор я пробовал plot_surface вместе с meshgrid/griddata вот так:

xi = np.arange(-300, 300, 0.1)
yi = np.arange(-300, 300, 0.1)

xgrid, ygrid = np.meshgrid(xi, yi)
zgrid = griddata(np.ravel(x), np.ravel(y), np.ravel(z), xi, yi, interp='linear')

fig = plt.figure(figsize=(16, 12))
ax = fig.add_subplot(111, projection='3d')

surf = ax.plot_surface(xgrid, ygrid, zgrid)
plt.show()

Поверхностный график набора данных с использованием griddata

А также plot_trisurf дает такие же неудовлетворительные результаты:

triang = mtri.Triangulation(np.ravel(x), np.ravel(y))

fig = plt.figure(figsize=(16, 12))
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(triang, np.ravel(z), cmap=plt.cm.CMRmap)
plt.show()

Поверхностный график набора данных с использованием trisurf

Может ли кто-нибудь предложить способ правильно отобразить область высоких значений z моего набора данных на поверхностных графиках?


person Christoph    schedule 08.02.2018    source источник
comment
Если у вас есть такая проблема, сначала создайте минимально воспроизводимый пример. Здесь проблема в том, что вы пытаетесь интерполировать параметрическую кривую на сетку. Есть ли причина для этого? Почему бы не взять сами точки данных?   -  person ImportanceOfBeingErnest    schedule 08.02.2018
comment
Может ли ваша проблема заключаться в том, что из-за интерполяции в пограничной области есть значения NaN? См., например, здесь.   -  person Mr. T    schedule 08.02.2018
comment
@Piinthesky Нет, проблема просто в том, что при попытке интерполировать небиективную, несуъективную функцию в сетке вы неизбежно получите полный беспорядок.   -  person ImportanceOfBeingErnest    schedule 08.02.2018


Ответы (1)


Проблема в том, что вы пытаетесь интерполировать параметрическую кривую на сетке. Поскольку фигура, которую нужно нарисовать, не является ни биективной, ни субъективной, вы получаете полный беспорядок.

введите здесь описание изображения

Вместо того, чтобы пытаться интерполировать точки, вы можете напрямую нанести их на график.

X = np.array(x)
Y = np.array(y)
Z = np.array(z)
ax.plot_surface(X,Y,Z, cmap="RdYlBu")
plt.show()

введите здесь описание изображения

Полный пример для воспроизведения:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

maj_avg = 50*(np.linspace(0,1,20)-0.6)**2+50
min_avg = 60*(np.linspace(0,1,20)-0.7)**2+60
ang_avg = np.linspace(0,90,20)
zs = np.arange(0,40,2)

i = 100
omega = np.linspace(0, 2 * np.pi, i, endpoint=True)

x = [major * np.cos(omega) * np.cos(np.deg2rad(ang + 90)) \
     - minor * np.sin(omega) * np.sin(np.deg2rad(ang + 90)) \
     for major, minor, ang in zip(maj_avg, min_avg, ang_avg)]
y = [major * np.cos(omega) * np.sin(np.deg2rad(ang + 90)) \
     + minor * np.sin(omega) * np.cos(np.deg2rad(ang + 90)) \
     for major, minor, ang in zip(maj_avg, min_avg, ang_avg)]
z = [np.full(i, zi) for zi in zs]


fig = plt.figure(figsize=(16, 12))
ax = fig.add_subplot(111, projection='3d')
#for x_arr, y_arr, z_arr in zip(x, y, z):
#    ax.plot(x_arr, y_arr, z_arr)

X = np.array(x)
Y = np.array(y)
Z = np.array(z)
ax.plot_surface(X,Y,Z, cmap="RdYlBu")

plt.show()
person ImportanceOfBeingErnest    schedule 08.02.2018
comment
Спасибо, это решает проблему. Думаю, я не пробовал, потому что в документации это говорит, что X, Y и Z должны быть предоставлены в виде 2D-массива, который, как я предполагал, я получу только с использованием данных сетки. - person Christoph; 09.02.2018