13

I have a 3d plot of lines generated by matplotlib. I want to overlay an image at a specific xy (or yz, xz) slice. How do I do that using python? Thanks.

I have a simple 3d plot code as:

fig = plt.figure(1),<br>
ax = Axes3D(fig)<br>
ax.plot(f[:,0], f[:,1], f[:,2], color='r')

I also have an image "Im" (a 2d array), so I need something like:

ax.overlay(Im, slice='xy', sliceNo=10)
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
ahmethungari
  • 2,089
  • 4
  • 19
  • 21

3 Answers3

40

I did a 3d surface plot overlay on top of a background image once:

3d surface plot on top of background image

If this is similar to what you want, I could try to make a working example out of it.

Alternatively, if you just want to display an image in 3d space, you can use a surface plot:

from pylab import *
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.cbook import get_sample_data
from matplotlib._png import read_png
fn = get_sample_data("lena.png", asfileobj=False)
img = read_png(fn)
x, y = ogrid[0:img.shape[0], 0:img.shape[1]]
ax = gca(projection='3d')
ax.plot_surface(x, y, 10, rstride=5, cstride=5, facecolors=img)
show()

Of course, the stride values can be decreased to 1 for better image quality, but then drawing will take loooong =)

Resulting image from above code:

enter image description here

Edit Nov 2020:

Since it seems to be of interest, here's the code I used to generate the first image (which is the minority charge carrier decay in a multicrystalline silicon wafer after photo-excitation):

bg_img = Image.open(datadir + "DSC_1495_dark.jpg")
bg_img = bg_img.crop((0, 0, 4000, 2848))
dpi = pl.rcParams['figure.dpi']
figsize = float(bg_img.size[0]) / dpi, float(bg_img.size[1]) / dpi

fig = pl.figure(figsize=figsize)
ax = pl.axes([0, 0, 1, 1], frameon=False)
ax.set_axis_off()
im = pl.imshow(bg_img)

ax = pl.axes([0.01, -0.005, 1.01, 1], projection='3d')
data = (loadtxt(datadir + "pl-image.txt")[14:950, 14:950] - 30) / 270
height, width = data.shape
bin = 1
print data.min(), data.max()
X = arange(data.shape[1])
Y = arange(data.shape[0])
tau = data[:, data.shape[1] // 2][:, None]
T = 5.0
t = linspace(0, T, data.shape[1])[None, :]
f = 1 / (1 + exp(-T / (2 * tau)))
Z = where(t < T / 2, 1 - f * exp(-t / tau), f * exp(-(t - T / 2) / tau))
X, Y = meshgrid(X, Y)
colors = rbow(data)
colors[:, :, -1] = 0.6
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=colors,
        linewidth=0, antialiased=True, shade=True)
ax.set_xlim3d(0, data.shape[0] + 36.0 / bin)
ax.set_ylim3d(18.0 / bin, data.shape[0] + 30.0 / bin)
ax.set_zlim3d(-0.8, 1.1)
ax.grid(False)
ax.view_init(38, -55.5)
ax.dist = 9.4
for a in (ax.w_xaxis, ax.w_yaxis, ax.w_zaxis):
    for t in a.get_ticklines() + a.get_ticklabels():
        t.set_visible(False)
    a.line.set_visible(False)
    a.pane.set_visible(False)
pl.savefig(picdir + "3d-plot.png", transparent=True)
torfbolt
  • 1,101
  • 12
  • 8
  • 1
    the second example combined with 3d plot of curves is what I wanted. thanks a lot. I think, I can plot curves over image by simply using hold(True) option... – ahmethungari Mar 25 '13 at 13:01
  • Thanks it's helpful for me. But, I have one question, is there any way to make an image be placed under 3d data plot? In [here](http://stackoverflow.com/questions/22959472), an image keeps floating over `bar3d`. – Jeon Apr 09 '14 at 10:29
  • @torfbolt I would like to know more about the first example. I have an image which I want to stay on the x, y plane (z=0). On top of that image is going to be my 3D curve. How to do it? – FaCoffee May 27 '16 at 07:59
  • how can i replace lena.png image with one images in my folder? plus, i get the error for lena.png as well: No such file or directory: u'/usr/share/matplotlib/mpl-data/sample_data/lena.png' – Dario Z. Nov 06 '17 at 16:12
  • 1
    Could you please share how to make a 3d plot in shown in example 1. I need a similar overlay plot. – gaj Jan 22 '18 at 09:21
  • First example is amazing!! – jtlz2 Mar 29 '19 at 07:16
  • 1
    I have an updated version stacking a few random images in 3d [here](https://gist.github.com/prl900/915fdbcaf977b882e3ac0a82fc7276bd) in case it's useful for someone else. (Also solves the problem of dark image in the response's example) – prl900 Mar 19 '20 at 00:55
3

My solution using Opencv is a bit closer to the original solution.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.cbook import get_sample_data
import cv2

# get the file path
fn = get_sample_data("grace_hopper.jpg", asfileobj=False)

# Read the image with Opencv
img = cv2.imread(fn)
# Change the color from BGR to RGB
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# Orgird to store data
x, y = np.ogrid[0:img.shape[0], 0:img.shape[1]]
# In Python3 matplotlib assumes rgbdata in range 0.0 to 1.0
img = img / 255
fig = plt.figure()
# gca do not work thus use figure objects inbuilt function.
ax = fig.add_subplot(projection='3d')

# Plot data
ax.plot_surface(x, y, np.atleast_2d(0), rstride=10, cstride=10, facecolors=img)

# fig.savefig("results.png")
fig.show()  # use for interactive plot, otherwise use plt.show()

enter image description here

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
-1
"""you can try this program,"""
from pylab import *
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.cbook import get_sample_data
from matplotlib._png import read_png
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

fig = plt.figure()
ax = fig.gca(projection='3d')

# 在 matlab 命令行窗口直接输入 peaks 可以得到其表达式的 matlab 形式:
fn = get_sample_data("F:/demo3d/pic.png", asfileobj=False)
img = read_png(fn)
xx, yy = ogrid[0:img.shape[0], 0:img.shape[1]]
X = xx
Y = yy
Z1 = -5*np.ones(X.shape)
Z = np.cos(xx/10) * np.cos(xx/10) + np.sin(yy/10) * np.sin(yy/10)

# Plot the 3D surface
ax.plot_surface(X, Y, Z1, rstride=1, cstride=1, facecolors=img, shade=False)
surf = ax.plot_surface(X, Y, Z, cmap=cm.RdYlGn_r, linewidth=0, antialiased=False)

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# plt.axis('off')
plt.savefig('png', dpi=1000)
plt.show()

[enter image description here][1]