Добавить фоновое изображение в 3D-график

Эта тема затрагивалась здесь, но не было указано, как создать 3D-график и вставить изображение в плоскость (x,y) на указанной высоте z.

Итак, чтобы придумать простой и воспроизводимый случай, скажем, я создаю трехмерный график, подобный этому, с mplot3d:

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.winter,
                       linewidth=0, antialiased=True)
ax.set_zlim(-1.01, 1.01)

ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

plt.show()

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

На уровне z=min(z)-1, где -1 – это визуальное смещение во избежание наложения, я хочу вставить изображение, представляющее элементы, для которых кривая показывает определенное значение. Как это сделать?

В этом примере меня не волнует идеальное соответствие между элементом и его значением, поэтому, пожалуйста, не стесняйтесь загружать любое изображение, которое вам нравится. Кроме того, есть ли способ позволить этому изображению вращаться, если совпадение не устраивает?

ИЗМЕНИТЬ

Это наглядный пример чего-то подобного, сделанного для 3D-гистограммы. Серые фигуры на уровне z=0 — это элементы, для которых столбцы показывают определенное значение z. Источник. введите описание изображения  здесь


person FaCoffee    schedule 27.05.2016    source источник


Ответы (1)


Используйте plot_surface для рисования изображения с помощью аргумента facecolors.

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
from matplotlib._png import read_png
from matplotlib.cbook import get_sample_data

fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, .25)
Y = np.arange(-5, 5, .25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.winter,
                       linewidth=0, antialiased=True)

ax.set_zlim(-2.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

fn = get_sample_data("./lena.png", asfileobj=False)
arr = read_png(fn)
# 10 is equal length of x and y axises of your surface
stepX, stepY = 10. / arr.shape[0], 10. / arr.shape[1]

X1 = np.arange(-5, 5, stepX)
Y1 = np.arange(-5, 5, stepY)
X1, Y1 = np.meshgrid(X1, Y1)
# stride args allows to determine image quality 
# stride = 1 work slow
ax.plot_surface(X1, Y1, -2.01, rstride=1, cstride=1, facecolors=arr)

plt.show()

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

Если вам нужно добавить значения, используйте PathPatch:

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import mpl_toolkits.mplot3d.art3d as art3d
from matplotlib.text import TextPath
from matplotlib.transforms import Affine2D
from matplotlib.patches import PathPatch

def text3d(ax, xyz, s, zdir="z", size=None, angle=0, usetex=False, **kwargs):
    x, y, z = xyz
    if zdir == "y":
        xy1, z1 = (x, z), y
    elif zdir == "y":
        xy1, z1 = (y, z), x
    else:
        xy1, z1 = (x, y), z

    text_path = TextPath((0, 0), s, size=size, usetex=usetex)
    trans = Affine2D().rotate(angle).translate(xy1[0], xy1[1])

    p1 = PathPatch(trans.transform_path(text_path), **kwargs)
    ax.add_patch(p1)
    art3d.pathpatch_2d_to_3d(p1, z=z1, zdir=zdir)

# main
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, .25)
Y = np.arange(-5, 5, .25)

Xg, Yg = np.meshgrid(X, Y)
R = np.sqrt(Xg**2 + Yg**2)
Z = np.sin(R)
surf = ax.plot_surface(Xg, Yg, Z, rstride=1, cstride=1, cmap=cm.winter,
                       linewidth=0, antialiased=True)

ax.set_zlim(-2.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

# add pathces with values
for i,x in enumerate(X[::4]):
    for j,y in enumerate(Y[::4]):
        text3d(ax, (x, y, -2.01), "{0:.1f}".format(Z[i][j]), zdir="z", size=.5, ec="none", fc="k")

plt.show()

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

person Serenity    schedule 27.05.2016
comment
Изображения, добавленные к 3D-графикам, теряют некоторую яркость. Есть ли способ сохранить исходный уровень яркости? - person Honey Gourami; 24.10.2019