In [1]:
%reload_ext XTIPython
In [2]:
%matplotlib inline
import matplotlib.pyplot as plt
In [3]:
# I will use the marching cubes from measure as per:
# https://stackoverflow.com/questions/6030098/how-to-display-a-3d-plot-of-a-3d-array-isosurface-in-matplotlib-mplot3d-or-simil
from skimage import measure
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
In [4]:
%imaris_screenshot
Out[4]:
In [5]:
%imaris_pull surfaces
Out[5]:
In [6]:
s = surfaces['Surfaces 2']
In [7]:
from matplotlib.pyplot import cm
# As per https://stackoverflow.com/questions/4971269/how-to-pick-a-new-color-for-each-plotted-line-within-a-figure-in-matplotlib
color=iter(cm.rainbow(np.linspace(0,1,20)))
# New figure and new 3-D subplot
fig = plt.figure(figsize=(20,15))
ax = fig.add_subplot(111, projection='3d')
nSurfaces = len(s.GetIds())
for i in range(nSurfaces):
#print("Processing surface %d/%d"%(i+1,nSurfaces))
sl = s.GetSurfaceDataLayout(i)
#inverting Y to have the same orientation as in Imaris
arr = np.swapaxes(np.array(s.GetSurfaceData(i).GetDataShorts())[0,0,...],0,2)[:,:-1,:]
vx = (sl.mExtendMaxX-sl.mExtendMinX)/(sl.mSizeX-1)
vy = (sl.mExtendMaxY-sl.mExtendMinY)/(sl.mSizeY-1)
vz = (sl.mExtendMaxZ-sl.mExtendMinZ)/(sl.mSizeZ-1)
verts, faces, normals, values = measure.marching_cubes_lewiner(arr, 0, spacing=(vz, vy, vx))
c = next(color)
ax.plot_trisurf(verts[:, 2]+sl.mExtendMinX, verts[:,1]+sl.mExtendMinY, faces, verts[:, 0]+sl.mExtendMinZ,
color=c,lw=1)
#Same orientation as Imaris reset view
ax.view_init(90, -90)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
plt.show()
Returns the layout of the surface, that is the : public and the size of the return value of GetData().
!! Due to the sub-pixel resolution of the surfaces, the actual minimum and maximum coordinates of the vertices of the triangulated representation of the surface are typically included and not equal to the : public of the layout !!
In [ ]:
sl = s.GetSurfaceDataLayout(0)
print(sl)
Returns a dataset of type UInt16 containing signed int16 values. Values below zero are outside the surface, above zero is inside. A sub-voxel precise surface can be reconstructed by linearly interpolating the values around zero.
!! data values are placed on the border as well, voxel size is (mExtendMax - mExtendMin) / (mSize - 1) !!
In [ ]:
arr = np.swapaxes(np.array(s.GetSurfaceData(0).GetDataShorts())[0,0,...],0,2)
print(arr.shape)
vx = (sl.mExtendMaxX-sl.mExtendMinX)/(sl.mSizeX-1)
vy = (sl.mExtendMaxY-sl.mExtendMinY)/(sl.mSizeY-1)
vz = (sl.mExtendMaxZ-sl.mExtendMinZ)/(sl.mSizeZ-1)
print(vx,vy,vz)
In [ ]:
normals = np.swapaxes(np.array(s.GetSurfaceNormals(0).GetDataShorts())[0,0,...],0,2)
print(normals.shape)
In [ ]:
verts, faces, normals, values = measure.marching_cubes_lewiner(arr, 0, spacing=(vz, vy, vx))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(verts[:, 2]+sl.mExtendMinX, verts[:,1]+sl.mExtendMinY, faces, verts[:, 0]+sl.mExtendMinZ,
lw=1,
antialiased=True)
plt.show()