4

I am trying to add an image to the xy axis of a 3d chart in Matplotlib. There will be a bar chart over the top of this but currently I'm trying to figure out how to get just an image to appear. Here's my code, all it returns so far is blank 3d axes, although the limits are what I want. I've tried importing the image as a jpeg and png, and can get it to show on 2d axes just by plotting it. I've tried following the code in this question: Add background image to 3d plot, but am stuck on how to import an image from the directory instead of using get_sample_data.

'''''
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
img = mpimg.imread('ukmap.jpg')

fig=plt.figure()
ax1=fig.add_subplot(111,projection='3d')
ax1.imshow(img, zorder=1, extent = [-10, 1.77, 49.88, 58.7] , alpha = 0.8)
'''''
  • The line of code that adds the image in the linked duplicate is `ax.plot_surface(X1, Y1, -2.01, rstride=1, cstride=1, facecolors=arr)`. `arr` there is your `img`. You just need to generate the coordinates in the exact same way. – Andras Deak -- Слава Україні Nov 01 '18 at 23:34
  • They are defined right before the plotting call [in the answer](https://stackoverflow.com/a/37480529/5067311). They define a grid which tells `plot_surface` the x and y domain where the image will be plotted. You need something like `X1,Y1 = np.meshgrid(np.linspace(-10, 1.77, img.shape[0]), np.linspace(49.88, 58.7, img.shape[1]))`, where I took the numbers from your `extent` keyword argument to `imshow`. Does this help? – Andras Deak -- Слава Україні Nov 02 '18 at 00:07
  • Thanks that's really helpful, I've tried using your example and have tried with the code in the answer before, but keep getting the error: File "C:\Users\annac\Anaconda3\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 1609, in plot_surface if Z.ndim != 2: AttributeError: 'float' object has no attribute 'ndim' – Anna Humphreys Nov 02 '18 at 00:18
  • That's probably because a scalar `-2.01` value is passed in the original code, which might be unsupported format today. You'll have to pass the z value where you want to plot the image, I presume this is 0 for you. And make it an array the same size as `X1` and `Y1`: `ax.plot_surface(X1, Y1, np.zeros_like(X1), ...)` for instance. – Andras Deak -- Слава Україні Nov 02 '18 at 00:21
  • Thanks that makes sense, I'm now getting this error, is this to do with the size of the image I'm trying to use? File "C:\Users\annac\Anaconda3\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 1695, in plot_surface colset.append(fcolors[rs][cs]) IndexError: index 900 is out of bounds for axis 0 with size 900 – Anna Humphreys Nov 02 '18 at 00:33
  • OK, I fired up an example plot. The problem is that `meshgrid` outputs arrays with a transposed shape compared to `img`. So you need `ax.plot_surface(X1.T, Y1.T, np.zeros_like(X1.T), ...)`. And note that if you have a lot of pixels, plotting will take some time and memory. The `rstride=1, cstride=1` parameters tell pyplot that it should create the surface using every data point. Passing `rstride=2, cstride=2` would take every other data point along each dimension, etc. And now we have alternatively `rcount` and `ccount` which define the _number_ of maximum points used along a dimension. – Andras Deak -- Слава Україні Nov 02 '18 at 00:44
  • Oh, and if your image has `uint8` intensity values (ranging from 0 to 255) you'll have to pass `facecolors=img/255` because pyplot wants intensities ranging from 0 to 1. – Andras Deak -- Слава Україні Nov 02 '18 at 00:46
  • @AndrasDeak Thanks so much I've got it working now! I very much appreciate all of the help – Anna Humphreys Nov 02 '18 at 01:31
  • No problem, welcome to Stack Overflow :) – Andras Deak -- Слава Україні Nov 02 '18 at 01:34

0 Answers0