def test_both_algs_same_result_donut(): # Performing this test on data that does not have ambiguities n = 48 a, b = 2.5/n, -1.25 vol = np.empty((n, n, n), 'float32') for iz in range(vol.shape[0]): for iy in range(vol.shape[1]): for ix in range(vol.shape[2]): # Double-torii formula by Thomas Lewiner z, y, x = float(iz)*a+b, float(iy)*a+b, float(ix)*a+b vol[iz,iy,ix] = ( ( (8*x)**2 + (8*y-2)**2 + (8*z)**2 + 16 - 1.85*1.85 ) * ( (8*x)**2 + (8*y-2)**2 + (8*z)**2 + 16 - 1.85*1.85 ) - 64 * ( (8*x)**2 + (8*y-2)**2 ) ) * ( ( (8*x)**2 + ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2 + 16 - 1.85*1.85 ) * ( (8*x)**2 + ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2 + 16 - 1.85*1.85 ) - 64 * ( ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2 ) ) + 1025 vertices1, faces1 = marching_cubes_classic(vol, 0)[:2] vertices2, faces2 = marching_cubes_lewiner(vol, 0)[:2] vertices3, faces3 = marching_cubes_lewiner(vol, 0, use_classic=True)[:2] # Old and new alg are different assert not _same_mesh(vertices1, faces1, vertices2, faces2) # New classic and new Lewiner are different assert not _same_mesh(vertices2, faces2, vertices3, faces3)
def test_both_algs_same_result_ellipse(): # Performing this test on data that does not have ambiguities sphere_small = ellipsoid(1, 1, 1, levelset=True) vertices1, faces1 = marching_cubes_classic(sphere_small, 0)[:2] vertices2, faces2 = marching_cubes_lewiner(sphere_small, 0, allow_degenerate=False)[:2] vertices3, faces3 = marching_cubes_lewiner(sphere_small, 0, allow_degenerate=False, use_classic=True)[:2] # Order is different, best we can do is test equal shape and same vertices present assert _same_mesh(vertices1, faces1, vertices2, faces2) assert _same_mesh(vertices1, faces1, vertices3, faces3)
def main(select=3, **kwargs): """Script main function. select: int 1: Medical data 2: Blocky data, different every time 3: Two donuts 4: Ellipsoid """ import visvis as vv # noqa: delay import visvis and GUI libraries # Create test volume if select == 1: vol = vv.volread('stent') isovalue = kwargs.pop('level', 800) elif select == 2: vol = vv.aVolume(20, 128) isovalue = kwargs.pop('level', 0.2) elif select == 3: with timer('computing donuts'): vol = donuts() isovalue = kwargs.pop('level', 0.0) # Uncommenting the line below will yield different results for # classic MC # vol *= -1 elif select == 4: vol = ellipsoid(4, 3, 2, levelset=True) isovalue = kwargs.pop('level', 0.0) else: raise ValueError('invalid selection') # Get surface meshes with timer('finding surface lewiner'): vertices1, faces1 = marching_cubes_lewiner(vol, isovalue, **kwargs)[:2] with timer('finding surface classic'): vertices2, faces2 = marching_cubes_classic(vol, isovalue, **kwargs) # Show vv.figure(1) vv.clf() a1 = vv.subplot(121) vv.title('Lewiner') m1 = vv.mesh(np.fliplr(vertices1), faces1) a2 = vv.subplot(122) vv.title('Classic') m2 = vv.mesh(np.fliplr(vertices2), faces2) a1.camera = a2.camera # visvis uses right-hand rule, gradient_direction param uses left-hand rule m1.cullFaces = m2.cullFaces = 'front' # None, front or back vv.use().Run()
def test_marching_cubes_anisotropic(): spacing = (1., 10 / 6., 16 / 6.) ellipsoid_anisotropic = ellipsoid(6, 10, 16, spacing=spacing, levelset=True) _, surf = ellipsoid_stats(6, 10, 16) # Classic verts, faces = marching_cubes_classic(ellipsoid_anisotropic, 0., spacing=spacing) surf_calc = mesh_surface_area(verts, faces) # Test within 1.5% tolerance for anisotropic. Will always underestimate. assert surf > surf_calc and surf_calc > surf * 0.985 # Lewiner verts, faces = marching_cubes_lewiner(ellipsoid_anisotropic, 0., spacing=spacing)[:2] surf_calc = mesh_surface_area(verts, faces) # Test within 1.5% tolerance for anisotropic. Will always underestimate. assert surf > surf_calc and surf_calc > surf * 0.985 # Test spacing together with allow_degenerate=False marching_cubes_lewiner(ellipsoid_anisotropic, 0, spacing=spacing, allow_degenerate=False)
def test_marching_cubes_isotropic(): ellipsoid_isotropic = ellipsoid(6, 10, 16, levelset=True) _, surf = ellipsoid_stats(6, 10, 16) # Classic verts, faces = marching_cubes_classic(ellipsoid_isotropic, 0.) surf_calc = mesh_surface_area(verts, faces) # Test within 1% tolerance for isotropic. Will always underestimate. assert surf > surf_calc and surf_calc > surf * 0.99 # Lewiner verts, faces = marching_cubes_lewiner(ellipsoid_isotropic, 0.)[:2] surf_calc = mesh_surface_area(verts, faces) # Test within 1% tolerance for isotropic. Will always underestimate. assert surf > surf_calc and surf_calc > surf * 0.99
def test_invalid_input(): # Classic with testing.raises(ValueError): marching_cubes_classic(np.zeros((2, 2, 1)), 0) with testing.raises(ValueError): marching_cubes_classic(np.zeros((2, 2, 1)), 1) with testing.raises(ValueError): marching_cubes_classic(np.ones((3, 3, 3)), 1, spacing=(1, 2)) with testing.raises(ValueError): marching_cubes_classic(np.zeros((20, 20)), 0) # Lewiner with testing.raises(ValueError): marching_cubes_lewiner(np.zeros((2, 2, 1)), 0) with testing.raises(ValueError): marching_cubes_lewiner(np.zeros((2, 2, 1)), 1) with testing.raises(ValueError): marching_cubes_lewiner(np.ones((3, 3, 3)), 1, spacing=(1, 2)) with testing.raises(ValueError): marching_cubes_lewiner(np.zeros((20, 20)), 0)
def plot_isosurface(filename=None, vlsvobj=None, filedir=None, step=None, outputdir=None, nooverwrite=None, # surf_var=None, surf_op=None, surf_level=None, color_var=None, color_op=None, surf_step=1, # title=None, cbtitle=None, draw=None, usesci=None, # boxm=[],boxre=[], colormap=None, run=None,wmark=None, nocb=None, unit=None, thick=1.0,scale=1.0, vmin=None, vmax=None, lin=None, symlog=None, ): ''' Plots a coloured plot with axes and a colour bar. :kword filename: path to .vlsv file to use for input. Assumes a bulk file. :kword vlsvobj: Optionally provide a python vlsvfile object instead :kword filedir: Optionally provide directory where files are located and use step for bulk file name :kword step: output step index, used for constructing output (and possibly input) filename :kword outputdir: path to directory where output files are created (default: $HOME/Plots/) If directory does not exist, it will be created. If the string does not end in a forward slash, the final parti will be used as a perfix for the files. :kword nooverwrite: Set to only perform actions if the target output file does not yet exist :kword surf_var: Variable to read for defining surface :kword surf_op: Operator to use for variable to read surface :kword surf_level: Level at which to define surface :kword surf_step: Vertex stepping for surface generation: larger value returns coarser surface :kword color_var: Variable to read for coloring surface :kword color_op: Operator to use for variable to color surface ('x', 'y', 'z'; if None and color_var is a vector, the magnitude is taken) :kword boxm: zoom box extents [x0,x1,y0,y1] in metres (default and truncate to: whole simulation box) :kword boxre: zoom box extents [x0,x1,y0,y1] in Earth radii (default and truncate to: whole simulation box) :kword colormap: colour scale for plot, use e.g. hot_desaturated, jet, viridis, plasma, inferno, magma, parula, nipy_spectral, RdBu, bwr :kword run: run identifier, used for constructing output filename :kword title: string to use as plot title instead of time :kword cbtitle: string to use as colorbar title instead of map name :kword unit: Plot axes using 10^{unit} m (default: Earth radius R_E) :kwird usesci: Use scientific notation for colorbar ticks? (default: 1) :kword vmin,vmax: min and max values for colour scale and colour bar. If no values are given, min and max values for whole plot (non-zero rho regions only) are used. :kword lin: Flag for using linear colour scaling instead of log :kword symlog: Use logarithmic scaling, but linear when abs(value) is below the value given to symlog. Allows symmetric quasi-logarithmic plots of e.g. transverse field components. A given of 0 translates to a threshold of max(abs(vmin),abs(vmax)) * 1.e-2. :kword wmark: If set to non-zero, will plot a Vlasiator watermark in the top left corner. :kword draw: Set to nonzero in order to draw image on-screen instead of saving to file (requires x-windowing) :kword scale: Scale text size (default=1.0) :kword thick: line and axis thickness, default=1.0 :kword nocb: Set to suppress drawing of colourbar :returns: Outputs an image to a file or to the screen. .. code-block:: python # Example usage: ''' # Verify the location of this watermark image watermarkimage=os.path.join(os.path.dirname(__file__), 'logo_color.png') # watermarkimage=os.path.expandvars('$HOME/appl_taito/analysator/pyPlot/logo_color.png') outputprefix = '' if outputdir==None: outputdir=os.path.expandvars('$HOME/Plots/') outputprefixind = outputdir.rfind('/') if outputprefixind >= 0: outputprefix = outputdir[outputprefixind+1:] outputdir = outputdir[:outputprefixind+1] if not os.path.exists(outputdir): os.makedirs(outputdir) # Input file or object if filename!=None: f=pt.vlsvfile.VlsvReader(filename) elif vlsvobj!=None: f=vlsvobj elif ((filedir!=None) and (step!=None)): filename = filedir+'bulk.'+str(step).rjust(7,'0')+'.vlsv' f=pt.vlsvfile.VlsvReader(filename) else: print("Error, needs a .vlsv file name, python object, or directory and step") return # Scientific notation for colorbar ticks? if usesci==None: usesci=1 if colormap==None: # Default values colormap="hot_desaturated" if color_op!=None: colormap="bwr" cmapuse=matplotlib.cm.get_cmap(name=colormap) fontsize=8*scale # Most text fontsize2=10*scale # Time title fontsize3=5*scale # Colour bar ticks # Plot title with time timeval=None timeval=f.read_parameter("time") if timeval==None: timeval=f.read_parameter("t") if timeval==None: print "Unknown time format encountered" # Plot title with time if title==None: if timeval == None: print "Unknown time format encountered" plot_title = '' else: #plot_title = "t="+str(np.int(timeval))+' s' plot_title = "t="+'{:4.2f}'.format(timeval)+' s' else: plot_title = title # step, used for file name if step!=None: stepstr = '_'+str(step).rjust(7,'0') else: stepstr = '' # If run name isn't given, just put "plot" in the output file name if run==None: run='plot' # Verify validity of operator surf_opstr='' color_opstr='' if color_op!=None: if color_op!='x' and color_op!='y' and color_op!='z': print("Unknown operator "+color_op+", defaulting to None/magnitude for a vector.") color_op=None else: # For components, always use linear scale, unless symlog is set color_opstr='_'+color_op if symlog==None: lin=1 # Verify validity of operator if surf_op!=None: if surf_op!='x' and surf_op!='y' and surf_op!='z': print("Unknown operator "+surf_op) surf_op=None else: surf_opstr='_'+surf_op # Output file name surf_varstr=surf_var.replace("/","_") if color_var!=None: color_varstr=color_var.replace("/","_") else: color_varstr="solid" savefigname = outputdir+outputprefix+run+"_isosurface_"+surf_varstr+surf_opstr+"-"+color_varstr+color_opstr+stepstr+".png" # Check if target file already exists and overwriting is disabled if (nooverwrite!=None and os.path.exists(savefigname)): # Also check that file is not empty if os.stat(savefigname).st_size > 0: return else: print("Found existing file "+savefigname+" of size zero. Re-rendering.") Re = 6.371e+6 # Earth radius in m #read in mesh size and cells in ordinary space [xsize, ysize, zsize] = f.get_spatial_mesh_size() [xmin, ymin, zmin, xmax, ymax, zmax] = f.get_spatial_mesh_extent() cellsize = (xmax-xmin)/xsize cellids = f.read_variable("CellID") # xsize = f.read_parameter("xcells_ini") # ysize = f.read_parameter("ycells_ini") # zsize = f.read_parameter("zcells_ini") # xmin = f.read_parameter("xmin") # xmax = f.read_parameter("xmax") # ymin = f.read_parameter("ymin") # ymax = f.read_parameter("ymax") # zmin = f.read_parameter("zmin") # zmax = f.read_parameter("zmax") if (xsize==1) or (ysize==1) or (zsize==1): print("Error: isosurface plotting requires 3D spatial domain!") return simext=[xmin,xmax,ymin,ymax,zmin,zmax] sizes=[xsize,ysize,zsize] # Select window to draw if len(boxm)==6: boxcoords=boxm elif len(boxre)==6: boxcoords=[i*Re for i in boxre] else: boxcoords=simext # If box extents were provided manually, truncate to simulation extents boxcoords[0] = max(boxcoords[0],simext[0]) boxcoords[1] = min(boxcoords[1],simext[1]) boxcoords[2] = max(boxcoords[2],simext[2]) boxcoords[3] = min(boxcoords[3],simext[3]) boxcoords[4] = max(boxcoords[4],simext[4]) boxcoords[5] = min(boxcoords[5],simext[5]) # Axes and units (default R_E) if unit!=None: # Use m or km or other if unit==0: unitstr = r'm' if unit==3: unitstr = r'km' else: unitstr = r'$10^{'+str(int(unit))+'}$ m' unit = np.power(10,int(unit)) else: unitstr = r'$\mathrm{R}_{\mathrm{E}}$' unit = Re # Scale data extent and plot box simext_org = simext simext=[i/unit for i in simext] boxcoords=[i/unit for i in boxcoords] if color_op==None and color_var!=None: color_op='pass' cb_title = color_var color_data = f.read_variable(color_var,cellids=[1,2]) # If value was vector value, take magnitude if np.size(color_data) != 2: cb_title = r"$|"+color_var+"|$" elif color_op!=None and color_var!=None: cb_title = r" $"+color_var+"_"+color_op+"$" else: # color_var==None cb_title = "" nocb=1 if f.check_variable(surf_var)!=True: print("Error, surface variable "+surf_var+" not found!") return if surf_op==None: surf_data = f.read_variable(surf_var) # If value was vector value, take magnitude if np.ndim(surf_data) != 1: surf_data = np.linalg.norm(np.asarray(surf_data),axis=-1) else: surf_data = f.read_variable(surf_var,operator=surf_op) if np.ndim(surf_data)!=1: print("Error reading surface variable "+surf_var+"! Exiting.") return -1 # Reshape data to ordered 3D arrays for plotting #color_data = color_data[cellids.argsort()].reshape([sizes[2],sizes[1],sizes[0]]) surf_data = surf_data[cellids.argsort()].reshape([sizes[2],sizes[1],sizes[0]]) # The data we have now is Z,Y,X # Rotate data so it's X,Y,Z surf_data = np.swapaxes(surf_data,0,2) if surf_level==None: surf_level = 0.5*(np.amin(surf_data)+np.amax(surf_data)) print("Minimum found surface value "+str(np.amin(surf_data))+" surface level "+str(surf_level)+" max "+str(np.amax(surf_data))) # Select ploitting back-end based on on-screen plotting or direct to file without requiring x-windowing if draw!=None: plt.switch_backend('TkAgg') else: plt.switch_backend('Agg') # skimage.measure.marching_cubes_lewiner(volume, level=None, spacing=(1.0, 1.0, 1.0), # gradient_direction='descent', step_size=1, # allow_degenerate=True, use_classic=False) # #Generate mask for only visible section of data # # Apparently the marching cubes method does not ignore masked values, so this doesn't directly help. # [Xmesh,Ymesh, Zmesh] = scipy.meshgrid(np.linspace(simext[0],simext[1],num=sizes[0]),np.linspace(simext[2],simext[3],num=sizes[1]),np.linspace(simext[4],simext[5],num=sizes[2])) # print("simext",simext) # print("sizes",sizes) # print("boxcoords", boxcoords) # maskgrid = np.ma.masked_where(Xmesh<(boxcoords[0]), Xmesh) # maskgrid = np.ma.masked_where(Xmesh>(boxcoords[1]), maskgrid) # maskgrid = np.ma.masked_where(Ymesh<(boxcoords[2]), maskgrid) # maskgrid = np.ma.masked_where(Ymesh>(boxcoords[3]), maskgrid) # maskgrid = np.ma.masked_where(Zmesh<(boxcoords[4]), maskgrid) # maskgrid = np.ma.masked_where(Zmesh>(boxcoords[5]), maskgrid) # surf_data = np.ma.asarray(surf_data) # surf_data.mask = maskgrid.mask # print(surf_data.count()) surf_spacing = ((xmax-xmin)/(xsize*unit), (ymax-ymin)/(ysize*unit), (zmax-zmin)/(zsize*unit)) verts, faces, normals, arrays = measure.marching_cubes_lewiner(surf_data, level=surf_level, spacing=surf_spacing, gradient_direction='descent', step_size=surf_step, allow_degenerate=True, use_classic=False) # offset with respect to simulation domain corner #print("simext",simext) verts[:,0] = verts[:,0] + simext[0] verts[:,1] = verts[:,1] + simext[2] verts[:,2] = verts[:,2] + simext[4] # # Crop surface to box area # verts = [] # faces = [] # for i in np.arange(len(verts_nocrop[:,0])): # if ((verts_nocrop[i,0] > boxcoords[0]) and (verts_nocrop[i,0] < boxcoords[1]) and # (verts_nocrop[i,1] > boxcoords[2]) and (verts_nocrop[i,1] < boxcoords[3]) and # (verts_nocrop[i,2] > boxcoords[4]) and (verts_nocrop[i,2] < boxcoords[5])): # verts.append(verts_nocrop[i,:]) # faces.append(faces_nocrop[i,:]) # This is now incorrect # else: # dropped = dropped+1 # verts = np.asarray(verts) # faces = np.asarray(faces) # Next find color variable values at vertices if color_var != None: nverts = len(verts[:,0]) print("Extracting color values for "+str(nverts)+" vertices and "+str(len(faces[:,0]))+" faces.") all_coords = np.empty((nverts, 3)) for i in np.arange(nverts): # # due to mesh generation, some coordinates may be outside simulation domain # WARNING this means it might be doing wrong things in the periodic dimension of 2.9D runs. coords = verts[i,:]*unit coords[0] = max(coords[0],simext_org[0]+0.1*cellsize) coords[0] = min(coords[0],simext_org[1]-cellsize) coords[1] = max(coords[1],simext_org[2]+0.1*cellsize) coords[1] = min(coords[1],simext_org[3]-cellsize) coords[2] = max(coords[2],simext_org[4]+0.1*cellsize) coords[2] = min(coords[2],simext_org[5]-cellsize) all_coords[i] = coords # Use interpolated values, WARNING periodic y (2.9 polar) hard-coded here /!\ color_data = f.read_interpolated_variable(color_var, all_coords, operator=color_op, periodic=["False", "True", "False"]) # Make sure color data is 1-dimensional (e.g. magnitude of E instead of 3 components) if np.ndim(color_data)!=1: color_data=np.linalg.norm(color_data, axis=-1) if color_var==None: # dummy norm print("No surface color given, using dummy setup") norm = BoundaryNorm([0,1], ncolors=cmapuse.N, clip=True) vminuse=0 vmaxuse=1 else: # If automatic range finding is required, find min and max of array # Performs range-finding on a masked array to work even if array contains invalid values color_data = np.ma.masked_invalid(color_data) if vmin!=None: vminuse=vmin else: vminuse=np.ma.amin(color_data) if vmax!=None: vmaxuse=vmax else: vmaxuse=np.ma.amax(color_data) # If vminuse and vmaxuse are extracted from data, different signs, and close to each other, adjust to be symmetric # e.g. to plot transverse field components if vmin==None and vmax==None: if (vminuse*vmaxuse < 0) and (abs(abs(vminuse)-abs(vmaxuse))/abs(vminuse) < 0.4 ) and (abs(abs(vminuse)-abs(vmaxuse))/abs(vmaxuse) < 0.4 ): absval = max(abs(vminuse),abs(vmaxuse)) if vminuse < 0: vminuse = -absval vmaxuse = absval else: vminuse = absval vmaxuse = -absval # Check that lower bound is valid for logarithmic plots if (vminuse <= 0) and (lin==None) and (symlog==None): # Drop negative and zero values vminuse = np.ma.amin(np.ma.masked_less_equal(color_data,0)) # If symlog scaling is set: if symlog!=None: if symlog>0: linthresh = symlog else: linthresh = max(abs(vminuse),abs(vmaxuse))*1.e-2 # Lin or log colour scaling, defaults to log if lin==None: # Special SymLogNorm case if symlog!=None: #norm = SymLogNorm(linthresh=linthresh, linscale = 0.3, vmin=vminuse, vmax=vmaxuse, ncolors=cmapuse.N, clip=True) norm = SymLogNorm(linthresh=linthresh, linscale = 0.3, vmin=vminuse, vmax=vmaxuse, clip=True) maxlog=int(np.ceil(np.log10(vmaxuse))) minlog=int(np.ceil(np.log10(-vminuse))) logthresh=int(np.floor(np.log10(linthresh))) logstep=1 ticks=([-(10**x) for x in range(logthresh, minlog+1, logstep)][::-1] +[0.0] +[(10**x) for x in range(logthresh, maxlog+1, logstep)] ) else: norm = LogNorm(vmin=vminuse,vmax=vmaxuse) ticks = LogLocator(base=10,subs=range(10)) # where to show labels else: # Linear levels = MaxNLocator(nbins=255).tick_values(vminuse,vmaxuse) norm = BoundaryNorm(levels, ncolors=cmapuse.N, clip=True) ticks = np.linspace(vminuse,vmaxuse,num=7) print("Selected color range: "+str(vminuse)+" to "+str(vmaxuse)) # Create 300 dpi image of suitable size fig = plt.figure(figsize=[4.0,4.0],dpi=300) #ax1 = fig.gca(projection='3d') ax1 = fig.add_subplot(111, projection='3d') # Generate virtual bounding box to get equal aspect maxrange = np.array([boxcoords[1]-boxcoords[0], boxcoords[3]-boxcoords[2], boxcoords[5]-boxcoords[4]]).max() / 2.0 midvals = np.array([boxcoords[1]+boxcoords[0], boxcoords[3]+boxcoords[2], boxcoords[5]+boxcoords[4]]) / 2.0 # Three options: # 2.9D ecliptic, 2.9D polar, or 3D if ysize < 0.2*xsize: # 2.9D polar, perform rotation generatedsurface = ax1.plot_trisurf(verts[:,2], verts[:,0], verts[:,1], triangles=faces, cmap=cmapuse, norm=norm, vmin=vminuse, vmax=vmaxuse, lw=0, shade=False, edgecolors=None, antialiased=False) ax1.set_xlabel("z ["+unitstr+"]", fontsize=fontsize3) ax1.set_ylabel("x ["+unitstr+"]", fontsize=fontsize3) ax1.set_zlabel("y ["+unitstr+"]", fontsize=fontsize3) # Set camera angle ax1.view_init(elev=30., azim=90) # Set virtual bounding box ax1.set_xlim([midvals[2]-maxrange, midvals[2]+maxrange]) ax1.set_ylim([midvals[0]-maxrange, midvals[0]+maxrange]) ax1.set_zlim([midvals[1]-maxrange, midvals[1]+maxrange]) ax1.tick_params(labelsize=fontsize3)#,width=1.5,length=3) else: # 3D or 2.9D ecliptic, leave as is generatedsurface = ax1.plot_trisurf(verts[:,0], verts[:,1], verts[:,2], triangles=faces, cmap=cmapuse, norm=norm, vmin=vminuse, vmax=vmaxuse, lw=0.2, shade=False, edgecolors=None) ax1.set_xlabel("x ["+unitstr+"]", fontsize=fontsize3) ax1.set_ylabel("y ["+unitstr+"]", fontsize=fontsize3) ax1.set_zlabel("z ["+unitstr+"]", fontsize=fontsize3) # Set camera angle ax1.view_init(elev=30., azim=0) # Set virtual bounding box ax1.set_xlim([midvals[0]-maxrange, midvals[0]+maxrange]) ax1.set_ylim([midvals[1]-maxrange, midvals[1]+maxrange]) ax1.set_zlim([midvals[2]-maxrange, midvals[2]+maxrange]) ax1.tick_params(labelsize=fontsize3)#,width=1.5,length=3) # Setting per-triangle colours for plot_trisurf needs to be done # as a separate set_array call. if color_var != None: # Find face-averaged colors # (simply setting the array to color_data failed for some reason) colors = np.mean(color_data[faces], axis=1) generatedsurface.set_array(colors) # Title and plot limits if len(plot_title)!=0: ax1.set_title(plot_title,fontsize=fontsize2,fontweight='bold') # ax1.set_aspect('equal') #<<<--- this does not work for 3D plots! # for axis in ['top','bottom','left','right']: # ax1.spines[axis].set_linewidth(thick) # ax1.xaxis.set_tick_params(width=thick,length=3) # ax1.yaxis.set_tick_params(width=thick,length=3) # #ax1.xaxis.set_tick_params(which='minor',width=3,length=5) # #ax1.yaxis.set_tick_params(which='minor',width=3,length=5) if cbtitle==None: cb_title_use = cb_title else: cb_title_use = cbtitle if nocb==None: # First draw colorbar if usesci==0: cb = fig.colorbar(generatedsurface,ticks=ticks, drawedges=False, fraction=0.023, pad=0.02) else: cb = fig.colorbar(generatedsurface,ticks=ticks,format=mtick.FuncFormatter(fmt),drawedges=False, fraction=0.046, pad=0.04) if len(cb_title_use)!=0: cb.ax.set_title(cb_title_use,fontsize=fontsize2,fontweight='bold') cb.ax.tick_params(labelsize=fontsize3)#,width=1.5,length=3) cb.outline.set_linewidth(thick) # if too many subticks: if lin==None and usesci!=0 and symlog==None: # Note: if usesci==0, only tick labels at powers of 10 are shown anyway. # For non-square pictures, adjust tick count nlabels = len(cb.ax.yaxis.get_ticklabels()) / ratio valids = ['1','2','3','4','5','6','7','8','9'] if nlabels > 10: valids = ['1','2','3','4','5','6','8'] if nlabels > 19: valids = ['1','2','5'] if nlabels > 28: valids = ['1'] # for label in cb.ax.yaxis.get_ticklabels()[::labelincrement]: for label in cb.ax.yaxis.get_ticklabels(): # labels will be in format $x.0\times10^{y}$ if not label.get_text()[1] in valids: label.set_visible(False) # Add Vlasiator watermark if wmark!=None: wm = plt.imread(get_sample_data(watermarkimage)) newax = fig.add_axes([0.01, 0.90, 0.3, 0.08], anchor='NW', zorder=-1) newax.imshow(wm) newax.axis('off') plt.tight_layout() savefig_pad=0.1 # The default is 0.1 bbox_inches=None # Save output or draw on-screen if draw==None: # Note: generated title can cause strange PNG header problems # in rare cases. This problem is under investigation, but is related to the exact generated # title string. This try-catch attempts to simplify the time string until output succedes. try: plt.savefig(savefigname,dpi=300, bbox_inches=bbox_inches, pad_inches=savefig_pad) savechange=0 except: savechange=1 plot_title = "t="+'{:4.1f}'.format(timeval)+' s ' ax1.set_title(plot_title,fontsize=fontsize2,fontweight='bold') try: plt.savefig(savefigname,dpi=300, bbox_inches=bbox_inches, pad_inches=savefig_pad) except: plot_title = "t="+str(np.int(timeval))+' s ' ax1.set_title(plot_title,fontsize=fontsize2,fontweight='bold') try: plt.savefig(savefigname,dpi=300, bbox_inches=bbox_inches, pad_inches=savefig_pad) except: plot_title = "" ax1.set_title(plot_title,fontsize=fontsize2,fontweight='bold') try: plt.savefig(savefigname,dpi=300, bbox_inches=bbox_inches, pad_inches=savefig_pad) except: print "Error:", sys.exc_info() print("Error with attempting to save figure, sometimes due to matplotlib LaTeX integration.") print("Usually removing the title should work, but this time even that failed.") savechange = -1 if savechange>0: print("Due to rendering error, replaced image title with "+plot_title) if savechange>=0: print(savefigname+"\n") else: plt.draw() plt.show()
print(' Number of pixels in full stomatal regions: ' + \ str(np.sum(full_stomata_regions_mask))) print(' Total number of airspace pixels: ' + str(np.sum(airspace_stack))) if np.sum(full_stomata_regions_mask) < 10000: print('***NO SINGLE STOMA REGIONS - too small high magnification stack?***') # If there are no single stomata regions, we still compute the values at the airspace edge. edge_and_full_stomata_mask = mesophyll_edge else: print('***EXTRACTING DATA FROM SINGLE STOMA REGIONS***') SA_single_region = np.empty(len(regions_full_in_center)) Pore_volume_single_region = np.copy(SA_single_region) for regions in tqdm(np.arange(len(regions_full_in_center))): regions_bool = stomata_regions == regions_full_in_center[regions] ias_vert_faces = marching_cubes_lewiner(regions_bool) ias_SA = mesh_surface_area(ias_vert_faces[0], ias_vert_faces[1]) SA_single_region[regions] = ias_SA * (px_edge_rescaled**2) Pore_volume_single_region[regions] = np.sum(regions_bool) * (px_edge_rescaled**3) stoma_export_col_fix = int(np.floor(255/max(regions_all))) single_stoma_data = {"stoma_nb": regions_full_in_center, "stoma_color_value": regions_full_in_center*stoma_export_col_fix, "SA_single_region_um2": SA_single_region, "Pore_volume_single_region_um3": Pore_volume_single_region } # Select only the values at the edge of the airspace and within the full stomata # Will have to find a way to include a larger zone of stomata edge_and_full_stomata_mask = mesophyll_edge & full_stomata_regions_mask
def create_mesh(stat_map,threshold, one=True, positive_only=False, negative_only=False ): """ Runs Marching Cube algorithm for iso-surface extraction of 3D Volume data at a given threshold. Parameters ---------- stat_map: str path to NIfTI-file threshold: int or float level for iso-surface extraction one: bool,optional for writing the .obj-file, specify if faces values should start at 1 or at 0. Different visualization programs use different conventions. positive_only: bool, optional If true, negative values will be ignored. If false, two meshes will be generated once for a positive cluster and one for a negative cluster, using -threshold as iso-surface level. negative_only: bool, optional Create Mesh only for negative cluster. Returns -------- output_path: str path to .obj file output_path_neg: str or none path to .obj file generated from the negative cluster. None if no .obj file is generated. """ ##TODO: stat_map also possibly already a Nifit1Image, adjust img= nibabel.load(stat_map) img_data = img.get_fdata() if numpy.max(numpy.isinf(img_data)): print("Warning: Infinite values detected; plotting at 0.") img_data = numpy.nan_to_num(img_data) neg = False if negative_only: img_data[img_data > 0] = 0 #all negative values if (numpy.max(img_data)<= 0) or negative_only: img_data = numpy.absolute(img_data) neg = True #run marching cube verts, faces, normals, values = measure.marching_cubes_lewiner(img_data,threshold) #save mesh as .obj filename = os.path.basename(stat_map) filename_prefix = filename.split(".")[0] path = '/tmp/' output_file = filename_prefix + '_pos_mesh.obj' if neg: output_file = filename_prefix + '_neg_mesh.obj' output_path = os.path.join(path,output_file) write_obj(output_path,verts,faces,normals,values,affine = img.affine,one=one) #create mesh for negative clusters if present if numpy.min(img_data) < 0 and positive_only == False and neg == False: img_data[img_data > 0] = 0 img_data = numpy.absolute(img_data) verts, faces, normals, values = measure.marching_cubes_lewiner(img_data,threshold) output_file_neg = filename_prefix + "neg_mesh.obj" output_path_neg = os.path.join(path,output_file_neg) write_obj(output_path_neg,verts,faces,normals,values,affine = img.affine,one=one) else: output_path_neg = None return output_path,output_path_neg
mesh.set_edgecolor(edge_color) ax.add_collection3d(mesh) ax.scatter(positions[0], positions[1], positions[2], color = rgba_colors, marker='s', edgecolors='none') ax.set_xlim3d(0, cube.shape[0]) ax.set_ylim3d(0, cube.shape[0]) ax.set_zlim3d(0, cube.shape[0]) ax.view_init(elev=20., azim=frame) print("Saving", folder + str(frame).zfill(3) + file_name) if not os.path.exists(folder): os.makedirs(folder) name = folder + str(frame).zfill(3) + file_name plt.savefig(name, bbox_inches = "tight") print("frame", frame, "done") return name file_name = "test" folder = "./temp_" + file_name + "/" # Use marching cubes to obtain the surface mesh of these ellipsoids print("marching cubes") verts, faces, normals, values = measure.marching_cubes_lewiner(data_t1[0,:,:,:], 0, step_size = 4) print("init pool") pool = mp.Pool(21) print("calculate frames") results = [pool.apply_async(create_frame, args=(x, verts, faces, non_zero_positions, rgba_colors, file_name, folder)) for x in range(360)] output = [p.get() for p in results] print(output)
def get_surface_trace(points, decoder, latent, resolution, mc_value, is_uniform, verbose, save_ply, connected=False): trace = [] meshexport = None if (is_uniform): grid = get_grid_uniform(resolution) else: if not points is None: grid = get_grid(points[:, -3:], resolution) else: grid = get_grid(None, resolution) z = [] for i, pnts in enumerate(torch.split(grid['grid_points'], 100000, dim=0)): if (verbose): print('{0}'.format(i / (grid['grid_points'].shape[0] // 100000) * 100)) if (not latent is None): pnts = torch.cat([latent.expand(pnts.shape[0], -1), pnts], dim=1) z.append(decoder(pnts).detach().cpu().numpy()) z = np.concatenate(z, axis=0) if (not (np.min(z) > mc_value or np.max(z) < mc_value)): import trimesh z = z.astype(np.float64) verts, faces, normals, values = measure.marching_cubes_lewiner( volume=z.reshape(grid['xyz'][1].shape[0], grid['xyz'][0].shape[0], grid['xyz'][2].shape[0]).transpose([1, 0, 2]), level=mc_value, spacing=(grid['xyz'][0][2] - grid['xyz'][0][1], grid['xyz'][0][2] - grid['xyz'][0][1], grid['xyz'][0][2] - grid['xyz'][0][1])) verts = verts + np.array( [grid['xyz'][0][0], grid['xyz'][1][0], grid['xyz'][2][0]]) if (save_ply): meshexport = trimesh.Trimesh(verts, faces, normals, vertex_colors=values) if connected: connected_comp = meshexport.split(only_watertight=False) max_area = 0 max_comp = None for comp in connected_comp: if comp.area > max_area: max_area = comp.area max_comp = comp meshexport = max_comp def tri_indices(simplices): return ([triplet[c] for triplet in simplices] for c in range(3)) I, J, K = tri_indices(faces) trace.append( go.Mesh3d(x=verts[:, 0], y=verts[:, 1], z=verts[:, 2], i=I, j=J, k=K, name='', color='orange', opacity=0.5)) return {"mesh_trace": trace, "mesh_export": meshexport}
def main(args0, args1, args2, args3): parser = argparse.ArgumentParser( description= """This program uses ray casting method to detect overhang problem""") parser.add_argument( "-args0", type=str, default= (("\\\\samba.cs.ucalgary.ca\\fatemeh.yazdanbakhsh\Documents\Data_Sets\Kowther\Specimen2501L\Specimen2501L\Segmentations\FacialNerve.nrrd" )), help="facial nerve") # parser.add_argument("-args0", type = str, default = "U:\Documents\Data_Sets\Calgary\TBone-2015\TBoneCBCT-2015-10\L3016_modified_19_nov", help = "dicome image address") parser.add_argument( "-args1", type=str, default= ("\\\\samba.cs.ucalgary.ca\\fatemeh.yazdanbakhsh\Documents\Data_Sets\Kowther\Specimen2501L\Specimen2501L\Segmentations\SigmoidSinus.nrrd" ), help="address of sigmoid sinus mask") parser.add_argument( "-args2", type=str, default= (('\\\\samba.cs.ucalgary.ca\\fatemeh.yazdanbakhsh\Documents\Data_Sets\Kowther\Specimen2501L\dissected_27_feb_2019' )), help="dissected image address") parser.add_argument( "-args3", type=str, default= ('\\\\samba.cs.ucalgary.ca\\fatemeh.yazdanbakhsh\Documents\Data_Sets\Kowther\Specimen2501L\Specimen2501L\\2501L_reduced' ), help="intact image address") parser.add_argument("-args4", type=int, default=1000, help="low") parser.add_argument("-args5", type=int, default=4000, help="high") # Get your arguments args = parser.parse_args() args.args0 = args0 args.args1 = args1 args.args2 = args2 args.args3 = args3 low = args.args4 high = args.args5 ################################################Reading Dissected Volume########################################################################## # read the original volume ext = os.path.splitext(args.args2)[1] m_string = args.args2 if (ext == ".nii" or ext == ".nrrd"): input_volume = sitk.ReadImage(m_string) else: input_volume = RIM.dicom_series_reader(m_string) spacing = input_volume.GetSpacing() origin = input_volume.GetOrigin() try: dissected_matrix = sitk.GetArrayFromImage(input_volume) except: dissected_matrix = itk.GetArrayFromImage(input_volume) w1 = dissected_matrix.shape[2] h1 = dissected_matrix.shape[1] d1 = dissected_matrix.shape[0] #############################Reading Intact Volume################################ ext = os.path.splitext(str((args.args3)))[1] m_string3 = str((args.args3)) if (ext == ".nii" or ext == ".nrrd" or ext == ".nhdr"): intact_volume = sitk.ReadImage(m_string3) intact_array = sitk.GetArrayFromImage(intact_volume) else: intact_volume = RIM.dicom_series_reader(m_string3) intact_array = itk.GetArrayFromImage(intact_volume) # intact_volume=RIM.dicom_series_reader(str(unicode('\\\\samba.cs.ucalgary.ca\\fatemeh.yazdanbakhsh\Documents\Data_Sets\Calgary\TBone-2015\TBoneCBCT-2015-10\L2963L','utf-8'))) ####################################################################################################### # #do binary threshoulding on the original image PixelType = itk.ctype('signed short') Dimension = 3 try: thresholdFilter = sitk.BinaryThresholdImageFilter() input_volume_thr = thresholdFilter.Execute(input_volume, low, high, 255, 0) except: print(0) try: ImageType_threshold = itk.Image[PixelType, Dimension] thresholdFilter = itk.BinaryThresholdImageFilter[ ImageType_threshold, ImageType_threshold].New() # input_volume=thresholdFilter.Execute(input_volume,low,high,0,255) thresholdFilter.SetInput((input_volume)) thresholdFilter.SetLowerThreshold(low) thresholdFilter.SetUpperThreshold(high) thresholdFilter.SetOutsideValue(0) thresholdFilter.SetInsideValue(255) thresholdFilter.Update() input_volume_thr = thresholdFilter.GetOutput() except: print(0) ##################################################### #do binary threshoulding on the intact image try: thresholdFilter = sitk.BinaryThresholdImageFilter() intact_volume2_thr = thresholdFilter.Execute(intact_volume, low, high, 255, 0) except: print(0) # try: PixelType = itk.ctype('signed short') Dimension = 3 ImageType_threshold = itk.Image[PixelType, Dimension] thresholdFilter = itk.BinaryThresholdImageFilter[ ImageType_threshold, ImageType_threshold].New() thresholdFilter.SetInput(intact_volume) thresholdFilter.SetLowerThreshold(low) thresholdFilter.SetUpperThreshold(high) thresholdFilter.SetOutsideValue(0) thresholdFilter.SetInsideValue(255) thresholdFilter.Update() intact_volume2_thr = thresholdFilter.GetOutput() # except: print(0) #intact_array=itk.GetArrayFromImage(intact_volume2) try: intact_array_thr = sitk.GetArrayFromImage(intact_volume2_thr) except: print(0) try: intact_array_thr = itk.GetArrayFromImage(intact_volume2_thr) except: print(0) ##################################################### try: dissected_array_thr = sitk.GetArrayFromImage(input_volume_thr) except: print(0) try: dissected_array_thr = itk.GetArrayFromImage(input_volume_thr) except: print(0) ####################################################################################################### # read nrrd or segmented volume of sigmoid sinus to be used as mask ext = os.path.splitext(args.args1)[1] m_string2 = args.args1 if (ext == ".nii" or ext == ".nrrd"): sigmoid_volume = sitk.ReadImage(m_string2) else: sigmoid_volume = RIM.dicom_series_reader(m_string2) # nrrd_volume=sitk.ReadImage(m_string2) spacing = sigmoid_volume.GetSpacing() sigmoid_volume = sitk.GetArrayFromImage(sigmoid_volume) sigmoid_volume = np.ndarray.transpose(np.ndarray.transpose(sigmoid_volume)) # read facialnerve ext = os.path.splitext(args.args0)[1] m_string = args.args0 if (ext == ".nii" or ext == ".nrrd"): facial_volume = sitk.ReadImage(m_string) else: facial_volume = RIM.dicom_series_reader(m_string) facial_volume = sitk.GetArrayFromImage(facial_volume) facial_volume = np.ndarray.transpose(np.ndarray.transpose(facial_volume)) ##########################chebyshev distance########################### # allDist = squareform( pdist2( set1, set2 ) ); # [minDist nni] = min( allDist, [], 2 ); ###########python version################# # # X = sigmoid_volume # Y = facial_volume # # allDist=cdist(X, Y) # distancee=min(allDist) mesh = sigmoid_volume pts = facial_volume # squared_distances,face_indices,closest_point=pymesh.distance_to_mesh(mesh, pts, engine='auto') # print(squared_distances) # print(face_indices) # print(closest_point) ####################################################################### # quality=computeQualityMeasures(facial_volume,sigmoid_volume) # print(quality) a = np.ones((3, 3, 3)) for i in range(10, 50): b = morph.binary_dilation(sigmoid_volume, a, i) c = morph.binary_dilation(facial_volume, a, i) d = np.logical_and(b, c) d[np.where((d == [True]))] = [1.0] if np.sum(d) > 10000: print(np.sum(d)) print(i) break print(np.where((d == [1.0]))) sum_image = b + c #############visualizing the extended sigmoid sinus and facial nerve verts, faces, normals, values = marching_cubes_lewiner( sum_image, 0, spacing) # mesh=mlab.triangular_mesh([vert[0] for vert in verts], # [vert[1] for vert in verts], # [vert[2] for vert in verts], # faces) # fig = mlab.figure(1) # mlab.show() #################calculate the bounding box of the intersection point d[np.where((d == [True]))] = [1.0] rmin3, rmax3, cmin3, cmax3, zmin3, zmax3 = bbox2_3D(d) original_sum = sigmoid_volume + facial_volume original_sum[np.where((original_sum == [True]))] = [1.0] ##################extracting region of interest segmented_area = dissected_array_thr[rmin3:rmax3, cmin3:cmax3, zmin3:zmax3] print(segmented_area.shape) verts, faces, normals, values = marching_cubes_lewiner( segmented_area, 0, spacing) # mesh=mlab.triangular_mesh([vert[0] for vert in verts], # [vert[1] for vert in verts], # [vert[2] for vert in verts], # faces) # mlab.title("part of dissected") # fig = mlab.figure("part of dissected") segmented_area = intact_array_thr[rmin3:rmax3, cmin3:cmax3, zmin3:zmax3] print(segmented_area.shape) verts, faces, normals, values = marching_cubes_lewiner( segmented_area, 0, spacing) # mesh=mlab.triangular_mesh([vert[0] for vert in verts], # [vert[1] for vert in verts], # [vert[2] for vert in verts], # faces) # mlab.title("part of intact") # fig = mlab.figure("part of intact") segmented_area = dissected_array_thr[rmin3:rmax3, cmin3:cmax3, zmin3:zmax3] print(segmented_area.shape) verts, faces, normals, values = marching_cubes_lewiner( dissected_array_thr, 0, spacing) # mesh=mlab.triangular_mesh([vert[0] for vert in verts], # [vert[1] for vert in verts], # [vert[2] for vert in verts], # faces) # mlab.title('complete image') # fig=mlab.figure('complete image') # mlab.show() ##############################results#################################### subtracted_array = intact_array_thr - dissected_array_thr subtracted_region = subtracted_array[rmin3:rmax3, cmin3:cmax3, zmin3:zmax3] num1 = np.sum(subtracted_region) print(num1) if (num1 > 0): print("digastric ridge is identified") else: print("digastric ridge is not identified") print("rmin") print(rmin3) print("rmax") print(rmax3) print("cmin") print(cmin3) print("cmax") print(cmax3) print("zmin") print(zmin3) print("zmax") print(zmax3) return rmin3, rmax3, cmin3, cmax3, zmin3, zmax3
def createMesh(vox, step=1, threshold=0.5): vox = np.pad(vox, step) verts, faces, normals, values = sm.marching_cubes_lewiner(vox, 0.5, step_size=step) return verts, faces
# Generate a level set about zero of two identical ellipsoids in 3D # ellip_base = ellipsoid(6, 10, 16, levelset=True) # ellip_double = np.concatenate((ellip_base[:-1, ...], # ellip_base[2:, ...]), axis=0) # print(ellip_double.dtype) # print(ellip_double.shape) # nrrd.write('mcubes.nrrd', ellip_double) # Use marching cubes to obtain the surface mesh of these ellipsoids # verts, faces, normals, values = measure.marching_cubes_lewiner(ellip_double, 0) data_path = "/home/xiang/mnt/Data/aorta_seg_data/coronary/lungmask_raw/1.2.156.112605.14038013507713.181219050016.3.PAohbDhK1TP7IgRc69IM1h4riK7TNA.nrrd" # data_path = "/data2/home/zhouxiangyong/Data/aorta_seg_data/coronary/lungmask_raw/1.2.156.112605.14038013507713.181219050016.3.PAohbDhK1TP7IgRc69IM1h4riK7TNA.nrrd" mask, _ = nrrd.read(data_path) verts, faces, normals, values = measure.marching_cubes_lewiner(mask, 0) print(verts.shape) print(faces.shape) print(normals.shape) print(values.shape) print(verts[faces].shape) X = verts[:, 0] Y = verts[:, 1] Z = verts[:, 2] mlab.triangular_mesh(X, Y, Z, faces, color=(1, 0.5, 1), opacity=1.0) mlab.show() # sys.exit(0) # Display resulting triangular mesh using Matplotlib. This can also be done # with mayavi (see skimage.measure.marching_cubes_lewiner docstring). # fig = plt.figure(figsize=(10, 10))
def main_normal(myvolume, spacing, verts2, faces2): # verts, faces = skimage.measure.marching_cubes(volume, level, spacing=(1,1,1)) verts, faces, normals, values = marching_cubes_lewiner( myvolume, 400.0, spacing) mesh = mlab.triangular_mesh([vert[0] for vert in verts], [vert[1] for vert in verts], [vert[2] for vert in verts], faces) mesh.mlab_source.dataset.cell_data.scalars = np.zeros(faces.size) mesh.actor.mapper.scalar_visibility = True mlab.gcf().scene.parallel_projection = True # cell_data = mesh.mlab_source.dataset.cell_data # cell_data = mesh.mlab_source.dataset # s = mlab.pipeline.triangular_mesh_source(mesh.mlab_source.points[:,0],mesh.mlab_source.points[:,1],mesh.mlab_source.points[:,2],mesh.mlab_source.triangles) # s.data.cell_data.scalars =np.ones(mesh.mlab_source.triangles.size) # Your data here. # surf = mlab.pipeline.surface(s) # surf.contour.filled_contours = True mesh.mlab_source.update() # mlab.show() mesh_external = mesh ########################these two lines give you information about celles try to color cells tomorrow####################################### # result=read_trimesh(mesh,myvolume) faces2.append(faces) verts2.append(verts) # A first plot in 3D fig = mlab.figure(1) # for f in faces: # if faces[f,0]==vertex # face_index=verts.index(vertex) cursor3d = mlab.points3d(0., 0., 0., mode='axes', color=(0, 0, 0), scale_factor=0.5) mlab.title( 'Click on the volume to determine 3 points(consider right hand rule)') ################################################################################ # Some logic to select 'mesh' and the data index when picking. def picker_callback2(picker_obj): picked = picker_obj.actors if mesh.actor.actor._vtk_obj in [o._vtk_obj for o in picked]: point_id = index_changed2.pop() index_to_change2 = np.where(point_id == (faces2[0].transpose())[:]) ##################################################################################mayavi puck surface point python no depth for i in range(0, index_to_change2[1].size): mesh.mlab_source.dataset.cell_data.scalars[int( index_to_change2[1][i])] = 0 mesh.mlab_source.dataset.cell_data.scalars.name = 'Cell data' mesh2 = mlab.pipeline.set_active_attribute( mesh, cell_scalars='Cell data') mlab.pipeline.surface(mesh2) ################################################################################### def picker_callback(picker_obj): # picker_obj.tolerance=1 picked = picker_obj.actors # picker_obj.GetActore() if mesh.actor.actor._vtk_obj in [o._vtk_obj for o in picked]: # m.mlab_source.points is the points array underlying the vtk # dataset. GetPointId return the index in this array. # x_, y_ = np.lib.index_tricks.unravel_index(picker_obj.point_id, s.shape) x_2, y_2, z_2 = picker_obj.pick_position # mesh.mlab_source.points(picker_obj.point_id) # xxx=np.asarray(np.where(verts2==np.asarray(picker_obj.pick_position,dtype=int)),dtype=int) # yyy=np.where(faces2==xxx.transpose(1,0)) # xxx=np.where(verts2[0][0][i]==np.asarray(picker_obj.pick_position[0],dtype=int) and verts2[0][1][i]==np.asarray(picker_obj.pick_position[1],dtype=int) and verts2[0][2][i]==np.asarray(picker_obj.pick_position[2],dtype=int)) # np.where(picker_obj.pick_position[0]==(verts2[0].transpose())[:][0] and picker_obj.pick_position[1]==(verts2[0].transpose())[:][1] and picker_obj.pick_position[2]==(verts2[0].transpose())[:][2]) # a=np.zeros(faces2[0].shape[0],dtype=bool) # b=np.zeros(faces2[0].shape[0],dtype=bool) # c=np.zeros(faces2[0].shape[0],dtype=bool) # a[np.where(picker_obj.point_id==(faces2[0].transpose())[:][0])]=True # b[np.where(picker_obj.point_id==(faces2[0].transpose())[:][1])]=True # c[np.where(picker_obj.point_id==(faces2[0].transpose())[:][2])]=True # index_to_change=np.where(np.logical_or(a,np.logical_or(b,c))==True) index_to_change2 = np.where( picker_obj.point_id == (faces2[0].transpose())[:]) ##################################################################################mayavi puck surface point python no depth for i in range(0, index_to_change2[1].size): mesh.mlab_source.dataset.cell_data.scalars[int( index_to_change2[1][i])] = 255 mesh.mlab_source.dataset.cell_data.scalars.name = 'Cell data' # mesh.module_manager.scalar_lut_manager=1 # mesh.mlab_source.update() # mesh.mlab_source. mesh2 = mlab.pipeline.set_active_attribute( mesh, cell_scalars='Cell data') mlab.pipeline.surface(mesh2) # mesh.mlab_source.update() # wx.Yield() ################################################################################### index_changed2.append(picker_obj.point_id) # x_2, y_2, z_2 = picker_obj.mapper_position X_2.append(x_2 / spacing[0]) Y_2.append(y_2 / spacing[1]) Z_2.append(z_2 / spacing[2]) print("Data indices: %f, %f, %f" % (x_2, y_2, z_2)) print("point ID: %f" % (picker_obj.point_id)) index_changed.append((picker_obj.pick_position)) print("cell ID: %f" % (picker_obj.cell_id)) # index_changed.append(int(picker_obj.cell_id)) picker_obj = fig.on_mouse_pick(picker_callback, type='cell') fig.on_mouse_pick(picker_callback2, type='cell', button='Right') # picker_obj.tolerance=0.0005 mlab.show() ############################################################################ p1 = glm.vec3(X_2[0], Y_2[0], Z_2[0]) p2 = glm.vec3(X_2[1], Y_2[1], Z_2[1]) p3 = glm.vec3(X_2[2], Y_2[2], Z_2[2]) # These two vectors are in the plane v1 = glm.vec3(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z) v2 = glm.vec3(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z) # the cross product is a vector normal to the plane The_Normal = glm.normalize(Cross(v1, v2)) The_Normal2 = The_Normal The_Normal2.x = The_Normal.y The_Normal2.y = (The_Normal.x) The_Normal2.z = (The_Normal.z) # The_Normal3=glm.triangleNormal(p1,p2,p3) print("Normal is: ") print((The_Normal2)) # print(The_Normal3) return ((The_Normal2), p1, p2, p3, index_changed)
((8 * x)**2 + ((8 * y - 2) + 4) * ((8 * y - 2) + 4) + (8 * z)**2 + 16 - 1.85 * 1.85) - 64 * (((8 * y - 2) + 4) * ((8 * y - 2) + 4) + (8 * z)**2)) + 1025 # Uncommenting the line below will yield different results for classic MC #vol = -vol elif SELECT == 4: vol = ellipsoid(4, 3, 2, levelset=True) isovalue = 0 # Get surface meshes t0 = time.time() vertices1, faces1, _ = marching_cubes_lewiner(vol, isovalue, gradient_direction=gradient_dir, use_classic=False) print('finding surface lewiner took %1.0f ms' % (1000 * (time.time() - t0))) t0 = time.time() vertices2, faces2, _ = marching_cubes_classic(vol, isovalue, gradient_direction=gradient_dir) print('finding surface classic took %1.0f ms' % (1000 * (time.time() - t0))) # Show vv.figure(1) vv.clf() a1 = vv.subplot(121) m1 = vv.mesh(np.fliplr(vertices1), faces1) a2 = vv.subplot(122)
def convertHatchedImageToSTL(imageurl): image = cv2.imread(imageurl) '''' # generate copy of original image im1 = image.copy() # decrease contrast to emboss lines ob = preprocess(im1) # decrease contrast to emboss lines im1 = ob.ChangeContrast(0.5) # blur to emboss edges im1 = ob.Blur() # define kernel for dilate kernel = np.ones((5, 5), np.uint8) # morphology operations for separating outer lines opening = cv2.morphologyEx(im1, cv2.MORPH_OPEN, kernel) # detect edges for dilation edges = cv2.Canny(opening, 100, 150) # img = np.array(image, dtype=np.uint8) gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Perform 'closing' morphological operation on the image kernel = np.ones((1, 1), np.uint8) gray_image = cv2.morphologyEx(gray_image, cv2.MORPH_CLOSE, kernel) # Figure out the scaling parameter according to original size and then scale scale = prop.get_scaling_factor() gray_image = cv2.resize(gray_image, (0, 0), fx=scale, fy=scale) # Normalize all pixels to assume values from 0 to 1 gray_image = gray_image / 255.0 gray_image = np.subtract(1.0, gray_image) # Find the threshold to separate foreground from background using OTSU's thresholding method threshold = threshold_otsu(gray_image) (rows, cols) = gray_image.shape ''' textons = Textons(image, 3, 10, 1) tex = textons.textons() show = np.zeros_like(image) colors = np.unique(tex) show[tex == colors[1]] = [0, 0, 255] show[tex == colors[2]] = [0, 255, 255] show = cv2.medianBlur(show, 5) cv2.imwrite('temp.jpg', show) distinct_colors = color_extractor.get_top_colors_hsv(show, 10) ui.assign_height_to_colors(distinct_colors, 'temp.jpg') hsv_image = color_converter.get_hsv_from_bgr_image(show) gray_image = convert_from_colored_to_gray(hsv_image, distinct_colors) gray_image = cv2.medianBlur(gray_image, 3) # Perform 'closing' morphological operation on the image kernel = np.ones((1, 1), np.uint8) gray_image = cv2.morphologyEx(gray_image, cv2.MORPH_CLOSE, kernel) # Figure out the scaling parameter according to original size and then scale scale = prop.get_scaling_factor() gray_image = cv2.resize(gray_image, (0, 0), fx=scale, fy=scale) # Find the threshold to separate foreground from background using OTSU's thresholding method threshold = threshold_otsu(gray_image) (rows, cols) = gray_image.shape ''' Create a 3D voxel data from the image The top-most (#1) and bottom-most (#13) layer will contain all zeros The middle 10 layers (#3 to #12) contain the same pixel values as the gray scale image There is an additional layer(#2) for the base of the model ''' layers = 13 rows += 2 cols += 2 voxel = np.zeros((rows, cols, layers)) voxel[:, :, 1] = np.ones((rows, cols)).astype('float32') # making the boundary voxel values to be zero, for the marching cubes algorithm to work correctly voxel[0, :, :] = np.zeros((cols, layers)).astype('float32') voxel[(rows - 1), :, :] = np.zeros((cols, layers)).astype('float32') voxel[:, 0, :] = np.zeros((rows, layers)).astype('float32') voxel[:, (cols - 1), :] = np.zeros((rows, layers)).astype('float32') ''' Create the middle 10 layers from the image Based on the pixel values the layers are created to assign different heights to different regions in the image ''' for level in range(1, 10): level_threshold = level * 0.1 for j in range(0, rows - 2): for k in range(0, cols - 2): pixel_value = gray_image[j][k] if pixel_value > level_threshold: voxel[j + 1][k + 1][level + 1] = pixel_value ''' Run the marching cubes algorithm to extract surface mesh from 3D volume. Params: volume : (M, N, P) array of doubles Input data volume to find isosurfaces. Will be cast to `np.float64`. level : float Contour value to search for isosurfaces in `volume`. If not given or None, the average of the min and max of vol is used. spacing : length-3 tuple of floats Voxel spacing in spatial dimensions corresponding to numpy array indexing dimensions (M, N, P) as in `volume`. gradient_direction : string Controls if the mesh was generated from an isosurface with gradient descent toward objects of interest (the default), or the opposite. The two options are: * descent : Object was greater than exterior * ascent : Exterior was greater than object ''' verts, faces, normals, values = measure.marching_cubes_lewiner( volume=voxel, level=threshold, spacing=(1., 1., 1.), gradient_direction='descent') # Export the mesh as stl mymesh = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype)) for i, f in enumerate(faces): for j in range(3): mymesh.vectors[i][j] = verts[f[j], :] file_utils.save_mesh_to_file(imageurl, mymesh)
if __name__ == "__main__": args = parse() checkArgs(args) print "1 - Converting .mesh to binary .xyz" print "- 1.1 - Opening the mesh file" mesh = msh.Mesh(args.input) print "- 1.2 - Converting to binary point data" binaryData, totalScale = ptsToXYZCubes(mesh.verts, args.resolution) print "2 - Creating the filled volume" print "- 2.1 - Space carving" newData = spaceCarve(binaryData) newData = nd.binary_closing(newData, structure=nd.generate_binary_structure(3, 3), iterations=3) print "- 2.2 - Marching cubes" verts, faces, _, _ = mea.marching_cubes_lewiner(volume=newData, level=0.5) recon = msh.Mesh() recon.verts = np.insert(np.array(verts), 3, 0, axis=1) recon.tris = np.insert(np.array(faces), 3, 0, axis=1) recon.computeBBox() print "- 2.3 - Writing the scaled reconstructed .mesh file" recon.fitTo(mesh, keepRatio=False) recon.write(args.output) del binaryData, newData, recon if args.remesh: print "2.4 - Remeshing the hull" os.system("mmgs_O3 " + args.output + " -nr -hausd " + str(np.max(mesh.dims) / 100) + " -o " + args.output)
yInd = np.logical_and(yId >= 0, yId < imgH - 0.5) imInd = np.logical_and(xInd, yInd) xImId = np.round(xId[imInd]).astype(np.int32) yImId = np.round(yId[imInd]).astype(np.int32) maskInd = mask[yImId * imgW + xImId] volumeInd = imInd.copy() volumeInd[imInd == 1] = maskInd volume[volumeInd == 0] = 1 print('Occupied voxel: %d' % np.sum((volume > 0).astype(np.float32))) verts, faces, normals, _ = measure.marching_cubes_lewiner(volume, 0) print('Vertices Num: %d' % verts.shape[0]) print('Normals Num: %d' % normals.shape[0]) print('Faces Num: %d' % faces.shape[0]) axisLen = float(resolution - 1) / 2.0 verts = (verts - axisLen) / axisLen * 1.7 mesh = trm.Trimesh(vertices=verts, vertex_normals=normals, faces=faces) if checkMode == True: print('Export mesh ', imgId, ' cId:', camDict[imgId]['cId']) mesh.export( os.path.join( checkFolder, os.path.basename( meshName.replace('.ply', '_{}.ply'.format(imgId)))))
def main(): # Extract data from command line input path = sys.argv[0] argfiles = sys.argv[1] path_to_argfile_folder = '/'.join( path.split('/')[:-1]) + '/argfile_folder/' # print(path_to_argfile_folder) # define some things j = 0 permission = 0 filenames = [] for z in argfiles.split(','): z.strip() z = z.replace('\n', '') filenames.append(z) # TESTING for i in range( 0, len(filenames) ): # optional but nice catch for incorrect filepath or filename entry if os.path.exists(path_to_argfile_folder + filenames[i]) == False: print( "\nSome of the information you entered is incorrect. Try again.\n" ) permission = 1 # TESTING while j < len(filenames) and permission == 0: print('\nWorking on scan: ' + str(j + 1) + ' of ' + str(len(filenames)) + '\n') #read input file and define lots of stuff list_of_lines = openAndReadFile(path_to_argfile_folder + filenames[j]) # print(list_of_lines) # comment out once built # define parameters using list_of_lines path_to_sample, binary_postfix, px_edge, to_resize, reuse_raw_binary, trim_slices, trim_column_L, trim_column_R, color_values, base_folder_name = define_params_traits( list_of_lines) # If some parameters have been defined in the command line, grab them. if len(sys.argv) > 2: for ii in range(2, len(sys.argv)): exec(sys.argv[ii]) # Pixel dimmension vx_volume = px_edge**3 # TESTING # print(vx_volume) # print(color_values) # TESTING # Define the different tissue values epid_value, bg_value, mesophyll_value, ias_value, vein_value, bs_value = [ int(x) for x in color_values.split(',') ] # TESTING # print(epid_value, bg_value, mesophyll_value) # TESTING # Load segmented image # Set directory of functions in order to import MLmicroCTfunctions # path_to_script = '/'.join(path.split('/')[:-1]) + '/' # os.chdir(path_to_script) sample_path_split = path_to_sample.split('/') # TESTING # print(sample_path_split) # TESTING # If input path to sample is of length 1, i.e. only the sample name, # create the folder names based on default file naming. if len(sample_path_split) == 1: sample_name = path_to_sample folder_name = '/MLresults/' raw_ML_prediction_name = sample_name + 'fullstack_prediction.tif' else: sample_name = sample_path_split[-3] folder_name = sample_path_split[-2] + '/' raw_ML_prediction_name = sample_path_split[-1] filepath = base_folder_name + sample_name + '/' binary_filename = sample_name + binary_postfix # raw_ML_prediction_name = sample_name + 'fullstack_prediction.tif' print('') print('#################') print('# STARTING WITH #') print('#################') print(' ' + sample_name) # # Check if the file has already been processed -- Just in case! if os.path.isfile(filepath + sample_name + 'RESULTS.txt'): print('') print('This file has already been processed!') print('') assert False if os.path.isfile(base_folder_name + sample_name + '/' + sample_name + 'SEGMENTED.tif'): print('###LOADING POST-PROCESSED SEGMENTED STACK###') large_segmented_stack = io.imread(base_folder_name + sample_name + '/' + sample_name + 'SEGMENTED.tif') else: # Load the ML segmented stack raw_pred_stack = io.imread(filepath + folder_name + raw_ML_prediction_name) uniq100th = np.unique(raw_pred_stack[100]) if np.any(uniq100th < 0): raw_pred_stack = np.where(raw_pred_stack < 0, raw_pred_stack + 256, raw_pred_stack) print(np.unique(raw_pred_stack[100])) else: print(uniq100th) # Trim at the edges -- The ML does a bad job there if trim_slices == 0: if trim_column_L == 0: if trim_column_R == 0: raw_pred_stack = raw_pred_stack else: if trim_column_L == 0: if trim_column_R == 0: raw_pred_stack = raw_pred_stack = raw_pred_stack[ trim_slices:-trim_slices, :, :] else: raw_pred_stack = raw_pred_stack[ trim_slices:-trim_slices, :, trim_column_L:-trim_column_R] # # ################### # EPIDERMIS ################### print('') print('### EPIDERMIS ###') print('') # Label all of the epidermis regions unique_epidermis_volumes = label(raw_pred_stack == epid_value, connectivity=1) props_of_unique_epidermis = regionprops(unique_epidermis_volumes) # io.imshow(unique_epidermis_volumes[100]) # Find the size and properties of the epidermis regions epidermis_area = np.zeros(len(props_of_unique_epidermis)) epidermis_label = np.zeros(len(props_of_unique_epidermis)) epidermis_centroid = np.zeros([len(props_of_unique_epidermis), 3]) for regions in np.arange(len(props_of_unique_epidermis)): epidermis_area[regions] = props_of_unique_epidermis[ regions].area epidermis_label[regions] = props_of_unique_epidermis[ regions].label epidermis_centroid[regions] = props_of_unique_epidermis[ regions].centroid # Find the two largest epidermis ordered_epidermis = np.argsort(epidermis_area) print( 'The two largest values below should be in the same order of magnitude' ) print((epidermis_area[ordered_epidermis[-4:]])) if epidermis_area[ordered_epidermis[-1]] > ( 10 * epidermis_area[ordered_epidermis[-2]]): print('ERROR: Both epidermis might be connected!') print('Trying to remove the dangling epidermis pixels') no_dangling = delete_dangling_epidermis( (unique_epidermis_volumes == ordered_epidermis[-1] + 1), True, False) # Label all of the epidermis regions unique_epidermis_volumes = label(raw_pred_stack == epid_value, connectivity=1) props_of_unique_epidermis = regionprops( unique_epidermis_volumes) # io.imshow(unique_epidermis_volumes[100]) # Find the size and properties of the epidermis regions epidermis_area = np.zeros(len(props_of_unique_epidermis)) epidermis_label = np.zeros(len(props_of_unique_epidermis)) epidermis_centroid = np.zeros( [len(props_of_unique_epidermis), 3]) for regions in np.arange(len(props_of_unique_epidermis)): epidermis_area[regions] = props_of_unique_epidermis[ regions].area epidermis_label[regions] = props_of_unique_epidermis[ regions].label epidermis_centroid[regions] = props_of_unique_epidermis[ regions].centroid # Find the two largest epidermis ordered_epidermis = np.argsort(epidermis_area) print( 'The two largest values below should be in the same order of magnitude' ) print((epidermis_area[ordered_epidermis[-4:]])) if epidermis_area[ordered_epidermis[-1]] > ( 10 * epidermis_area[ordered_epidermis[-2]]): print('#########################################') print('#########################################') print('ERROR: Both epidermis are still connected!') print('' + sample_name) print('#########################################') print('#########################################') assert False print("") print( 'The center of the epidermis should be more or less the same on the' ) print('1st and 3rd columns for the two largest values.') print((epidermis_centroid[ordered_epidermis[-2:]])) print("") two_largest_epidermis = ( unique_epidermis_volumes == ordered_epidermis[-1] + 1) | (unique_epidermis_volumes == ordered_epidermis[-2] + 1) #Check if it's correct #io.imsave(filepath + folder_name + 'test_epidermis.tif', # img_as_ubyte(two_largest_epidermis)) # io.imshow(two_largest_epidermis[100]) # Get the values again: makes it cleaner unique_epidermis_volumes = label(two_largest_epidermis, connectivity=1) props_of_unique_epidermis = regionprops(unique_epidermis_volumes) epidermis_area = np.zeros(len(props_of_unique_epidermis)) epidermis_label = np.zeros(len(props_of_unique_epidermis)) epidermis_centroid = np.zeros([len(props_of_unique_epidermis), 3]) for regions in np.arange(len(props_of_unique_epidermis)): epidermis_area[regions] = props_of_unique_epidermis[ regions].area epidermis_label[regions] = props_of_unique_epidermis[ regions].label epidermis_centroid[regions] = props_of_unique_epidermis[ regions].centroid ## io.imshow(unique_epidermis_volumes[100]) # Transform the array to 8-bit: no need for the extra precision as there are only 3 values unique_epidermis_volumes = np.array(unique_epidermis_volumes, dtype='uint8') # Find the fvalues of each epidermis: assumes adaxial epidermis is at the top of the image adaxial_epidermis_value = unique_epidermis_volumes[100, :, 100][( unique_epidermis_volumes[100, :, 100] != 0).argmax()] abaxial_epidermis_value = int( np.arange(start=1, stop=3)[ np.arange(start=1, stop=3) != adaxial_epidermis_value]) # Compute volume epidermis_adaxial_volume = epidermis_area[adaxial_epidermis_value - 1] * (px_edge * (px_edge * 2)**2) epidermis_abaxial_volume = epidermis_area[abaxial_epidermis_value - 1] * (px_edge * (px_edge * 2)**2) # Tichkness return a 2D array, i.e. the thcikness of each column epidermis_abaxial_thickness = np.sum( (unique_epidermis_volumes == abaxial_epidermis_value), axis=1) * (px_edge * 2) epidermis_adaxial_thickness = np.sum( (unique_epidermis_volumes == adaxial_epidermis_value), axis=1) * (px_edge * 2) del props_of_unique_epidermis gc.collect() ################### ## VEINS ################### print('### VEINS ###') # Get the veins volumes unique_vein_volumes = label(raw_pred_stack == vein_value, connectivity=1) props_of_unique_veins = regionprops(unique_vein_volumes) # io.imshow(unique_vein_volumes[100]) veins_area = np.zeros(len(props_of_unique_veins)) veins_label = np.zeros(len(props_of_unique_veins)) veins_centroid = np.zeros([len(props_of_unique_veins), 3]) for regions in np.arange(len(props_of_unique_veins)): veins_area[regions] = props_of_unique_veins[regions].area veins_label[regions] = props_of_unique_veins[regions].label veins_centroid[regions] = props_of_unique_veins[ regions].centroid # Find the largest veins ordered_veins = np.argsort(veins_area) #veins_area[ordered_veins[-80:]] #veins_area[ordered_veins[:1000]] #veins_centroid[ordered_veins[-4:]] #print(np.sum(veins_area <= 1000)) # I found that for my images, a threshold of 100000 (1e5) pixel^3 removed # the noise left by the segmentation method and kept only the largest veins. # This should be adjusted depending on the species/images/maginification. large_veins_ids = veins_label[veins_area > (100000 / 8)] largest_veins = np.in1d(unique_vein_volumes, large_veins_ids).reshape( raw_pred_stack.shape) del unique_vein_volumes # Get the values again vein_volume = np.sum(largest_veins) * (px_edge * (px_edge * 2)**2) del props_of_unique_veins gc.collect() #Check if it's correct #io.imsave(base_folder_name + sample_name + '/' + folder_name + 'test_veins.tif', # img_as_ubyte(largest_veins)) # io.imshow(largest_veins[100]) ################### ## BUNDLE SHEATHS ################### print('### BUNDLE SHEATHS ###') if bs_value > 0: # Get the bs volumes unique_bs_volumes = label(raw_pred_stack == bs_value, connectivity=1) props_of_unique_bs = regionprops(unique_bs_volumes) # io.imshow(unique_bs_volumes[100]) bs_area = np.zeros(len(props_of_unique_bs)) bs_label = np.zeros(len(props_of_unique_bs)) bs_centroid = np.zeros([len(props_of_unique_bs), 3]) for regions in np.arange(len(props_of_unique_bs)): bs_area[regions] = props_of_unique_bs[regions].area bs_label[regions] = props_of_unique_bs[regions].label bs_centroid[regions] = props_of_unique_bs[regions].centroid # Find the largest bs ordered_bs = np.argsort(bs_area) #bs_area[ordered_bs[-80:]] #bs_area[ordered_bs[:1000]] #bs_centroid[ordered_bs[-4:]] #print(np.sum(bs_area <= 1000)) # I found that for my images, a threshold of 100000 (1e5) pixel^3 removed # the noise left by the segmentation method and kept only the largest bs. # This should be adjusted depending on the species/images/maginification. large_bs_ids = bs_label[bs_area > 100000] largest_bs = np.in1d(unique_bs_volumes, large_bs_ids).reshape( raw_pred_stack.shape) del unique_bs_volumes # Get the values again bs_volume = np.sum(largest_bs) * (px_edge * (px_edge * 2)**2) del props_of_unique_bs gc.collect() #Check if it's correct #io.imsave(base_folder_name + sample_name + '/' + folder_name + 'test_bs.tif', # img_as_ubyte(largest_bs)) # io.imshow(largest_bs[100]) else: print('bundle sheath not labelled -- skipped') ################### ## AIRSPACE ################### ######################################### ## CREATE THE FULLSIZE SEGMENTED STACK ## ######################################### # My segmenteation procedure used a reduced size stack, since my original # images are too big to be handled. I do want to use my original images for # their quality and details, so I use the binary image and add on top of it # the background, epidermis, and veins that have been segmented. That way, I # keep the detail I want at the airspace-cell interface, while still having a # good background, epidermis, and vein segmentation to remove the tissues that # are not need for some traits. if (reuse_raw_binary == 'True'): ############################## ## LOADING THE BINARY STACK ## ## IN ORIGINAL SIZE ## ############################## print('') print('### LOADING ORIGINAL SIZED BINARY STACK ###') binary_stack = img_as_bool( io.imread(filepath + binary_filename)) if len(binary_stack.shape) == 4: binary_stack = binary_stack[:, :, :, 0] # Trim at the edges -- The ML does a bad job there if trim_slices == 0: if trim_column_L == 0: if trim_column_R == 0: binary_stack = binary_stack else: if trim_column_L == 0: if trim_column_R == 0: binary_stack = binary_stack[ trim_slices:-trim_slices, :, :] else: binary_stack = binary_stack[ trim_slices:-trim_slices, :, (trim_column_L * 2):(-trim_column_R * 2)] # Check and trim the binary stack if necessary # This is to match the dimensions between all images # Basically, it trims odds numbered dimension so to be able to divide/multiply them by 2. binary_stack = Trim_Individual_Stack(binary_stack, raw_pred_stack) else: print( '### USING PREDICTIONS INSTEAD OF ORIGINAL BINARY STACK ###' ) if (to_resize == 'False'): if (reuse_raw_binary == 'True'): new_shape = binary_stack.shape new_shape = raw_pred_stack.shape else: new_shape = tuple([ raw_pred_stack.shape[0], raw_pred_stack.shape[1] * int(to_resize), raw_pred_stack.shape[2] * int(to_resize) ]) # This cell creates an empty array filled with the backgroud color (177), then # adds all of the leaf to it. Looping over each slice (this is more memory # efficient than working on the whole stack), it takes the ML segmented image, # resize the slice, and adds it to the empty array. bg_value_new = 177 vein_value_new = 147 ias_value_new = 255 bs_value_new = 102 mesophyll_value_new = 0 print('### CREATING THE POST-PROCESSED SEGMENTED STACK ###') # Assign an array filled with the background value 177. large_segmented_stack = np.full(shape=new_shape, fill_value=bg_value_new, dtype='uint8') for idx in np.arange(large_segmented_stack.shape[0]): # Creates a boolean 2D array of the veins (from the largest veins id'ed earlier) temp_veins = img_as_bool( transform.resize(largest_veins[idx], [new_shape[1], new_shape[2]], anti_aliasing=False, order=0)) # Creates a boolean 2D array of the bundle sheath (from the largest veins id'ed earlier) if bs_value > 0: temp_bs = img_as_bool( transform.resize(largest_bs[idx], [new_shape[1], new_shape[2]], anti_aliasing=False, order=0)) # Creates a 2D array with the epidermis being assinged values 30 or 60 temp_epid = transform.resize(unique_epidermis_volumes[idx], [new_shape[1], new_shape[2]], anti_aliasing=False, preserve_range=True, order=0) * 30 # Creates a 2D mask of only the leaf to remove the backgroud from the # original sized binary image. leaf_mask = img_as_bool( transform.resize(raw_pred_stack[idx] != bg_value, [new_shape[1], new_shape[2]], anti_aliasing=False, order=0)) # binary_stack is a boolean, so you need to multiply it. if (reuse_raw_binary == 'True'): large_segmented_stack[idx][leaf_mask] = binary_stack[idx][ leaf_mask] * ias_value_new else: temp_cells = img_as_bool( transform.resize( raw_pred_stack[idx] == mesophyll_value, [new_shape[1], new_shape[2]], anti_aliasing=False, order=0)) temp_air = img_as_bool( transform.resize(raw_pred_stack[idx] == ias_value, [new_shape[1], new_shape[2]], anti_aliasing=False, order=0)) large_segmented_stack[idx][leaf_mask] = temp_cells[ leaf_mask] * mesophyll_value_new large_segmented_stack[idx][ leaf_mask] = temp_air[leaf_mask] * ias_value_new large_segmented_stack[idx][temp_veins] = vein_value_new if bs_value > 0: large_segmented_stack[idx][temp_bs] = bs_value_new large_segmented_stack[idx][temp_epid != 0] = temp_epid[ temp_epid != 0] # io.imshow(large_segmented_stack[100]) print("") print('### Validate the values in the stack ###') print((np.unique(large_segmented_stack[100]))) # Special tiff saving option for ImageJ compatibility when files larger than # 2 Gb. It's like it doesn't recognize something if you don't turn this option # on for large files and then ImageJ or FIJI fail to load the large stack # (happens on my linux machine installed with openSUSE Tumbleweed). if large_segmented_stack.nbytes >= 2e9: imgj_bool = True else: imgj_bool = False # Save the image print("") print('### Saving post-processed segmented stack ###') io.imsave(base_folder_name + sample_name + '/' + sample_name + 'SEGMENTED.tif', large_segmented_stack, imagej=imgj_bool) ################################################ ## COMPUTE TRAITS ON THE ORIGINAL SIZED STACK ## ################################################ print('') print('### COMPUTE TRAITS ###') print('') # Redefine the values for the different tissues as used in the segmented image. # The epidermis will be defined later. bg_value = 177 spongy_value = 0 palisade_value = 0 if spongy_value == palisade_value: mesophyll_value = spongy_value else: mesophyll_value = [spongy_value, palisade_value] ias_value = 255 vein_value = 147 bs_value = 102 # Find the values of each epidermis: assumes adaxial epidermis is at the top of the image # Find the values of each epidermis: assumes adaxial epidermis is at the top of the image epid_vals = [30, 60] epid_bool = [ i in epid_vals for i in large_segmented_stack[200, :, 200] ] epid_indx = [i for i, x in enumerate(epid_bool) if x] adaxial_epidermis_value = large_segmented_stack[200, epid_indx[0], 200] # adaxial_epidermis_value = large_segmented_stack[100, :, 100][( # large_segmented_stack[100, :, 100] != bg_value).argmax()] if adaxial_epidermis_value == 30: abaxial_epidermis_value = 60 else: if adaxial_epidermis_value == 60: abaxial_epidermis_value = 30 #Measure the different volumes # Somehow those two lines can give negative values in some cases # I expect it's because of bad labelling of the background. # leaf_volume = np.sum(large_segmented_stack != bg_value) * vx_volume # mesophyll_volume = np.sum((large_segmented_stack != bg_value) & (large_segmented_stack # != adaxial_epidermis_value) & (large_segmented_stack != abaxial_epidermis_value)) * vx_volume cell_volume = np.sum( large_segmented_stack == mesophyll_value) * vx_volume air_volume = np.sum(large_segmented_stack == ias_value) * vx_volume epidermis_abaxial_volume = np.sum( large_segmented_stack == abaxial_epidermis_value) * vx_volume epidermis_adaxial_volume = np.sum( large_segmented_stack == adaxial_epidermis_value) * vx_volume vein_volume = np.sum(large_segmented_stack == vein_value) * vx_volume bundle_sheath_volume = np.sum( large_segmented_stack == bs_value) * vx_volume mesophyll_volume = cell_volume + air_volume leaf_volume = mesophyll_volume + epidermis_abaxial_volume + epidermis_adaxial_volume + vein_volume + bundle_sheath_volume #Measure the thickness of the leaf, the epidermis, and the mesophyll leaf_thickness = np.sum(np.array(large_segmented_stack != bg_value, dtype='bool'), axis=1) * px_edge mesophyll_thickness = np.sum( (large_segmented_stack != bg_value) & (large_segmented_stack != adaxial_epidermis_value) & (large_segmented_stack != abaxial_epidermis_value), axis=1) * px_edge epidermis_abaxial_thickness = np.sum( large_segmented_stack == abaxial_epidermis_value, axis=1) * px_edge epidermis_adaxial_thickness = np.sum( large_segmented_stack == adaxial_epidermis_value, axis=1) * px_edge print('Leaf thickness') print((np.median(leaf_thickness), leaf_thickness.mean(), leaf_thickness.std())) print('Mesophyll thickness') print((np.median(mesophyll_thickness), mesophyll_thickness.mean(), mesophyll_thickness.std())) print('Epidermis thickness (Adaxial)') print((np.median(epidermis_adaxial_thickness), epidermis_adaxial_thickness.mean(), epidermis_adaxial_thickness.std())) print('Epidermis thickness (Abaxial)') print((np.median(epidermis_abaxial_thickness), epidermis_abaxial_thickness.mean(), epidermis_abaxial_thickness.std())) # Leaf area # I was lazy here as I assume the leaf is parallel to the border of the image. leaf_area = large_segmented_stack.shape[ 0] * large_segmented_stack.shape[2] * (px_edge**2) #Caluculate Surface Area (adapted from Matt Jenkins' code) # This can take quite a lot of RAM!!! # This gives very similar results to BoneJ. BoneJ uses the Lorensen algorithm, # which is available as use_classic=True in te marching_cubes_lewiner() function. # However, the help page for this function warns that the Lorensen algorithm # might result in topologically incorrect results, and as such the Lewiner # algorithm is better (and faster too). So it is probably a better approach. # , spacing=(px_edge,px_edge,px_edge)) print("") print('### Compute surface area of IAS ###') print('### This may take a while and freeze your computer ###') ias_vert_faces = marching_cubes_lewiner( large_segmented_stack == ias_value, 0, allow_degenerate=False, step_size=2) ias_SA = mesh_surface_area(ias_vert_faces[0], ias_vert_faces[1]) true_ias_SA = ias_SA * (px_edge**2) print(('IAS surface area: ' + str(true_ias_SA) + ' µm**2')) print(('or ' + str(float(true_ias_SA / 1000000)) + ' mm**2')) # end Matt's code adaptation try: bs_volume except NameError: bs_volume = 0 print(('Sm: ' + str(true_ias_SA / leaf_area))) print(('SAmes/Vmes: ' + str(true_ias_SA / mesophyll_volume))) # NOTE ON SA CODE ABOVE # The same procedure as for epidermises and veins could be done, i.e. using the # label() function to identify all of the un-connected airspace volumes and # compute the surface area on each of them. That way we can get the surface # area of the largest airspace and the connectivity term presented in Earles # et al. (2018) (Beyond porosity - Bromeliad paper). # This is done in the code below. ## Label all of the ias regions #unique_ias_volumes = label(large_segmented_stack == ias_value, connectivity=1) #props_of_unique_ias = regionprops(unique_ias_volumes) # ## Find the size and properties of the epidermis regions #ias_area = np.zeros(len(props_of_unique_ias)) #ias_label = np.zeros(len(props_of_unique_ias)) #ias_centroid = np.zeros([len(props_of_unique_ias),3]) #ias_SA = np.zeros([len(props_of_unique_ias),3]) #for regions in np.arange(len(props_of_unique_ias)): # ias_area[regions] = props_of_unique_ias[regions].area # ias_label[regions] = props_of_unique_ias[regions].label # ias_centroid[regions] = props_of_unique_ias[regions].centroid # ## Find the two largest ias #ordered_ias = np.argsort(ias_area) #print('Check for the largest values and adjust use the necessary value to compute the largest connected IAS.') #print(ias_area[ordered_ias[-4:]]) #print(ias_label[ordered_ias[-4:]]) # #largests_ias = ias_label[ordered_ias[-4:]] #ias_SA = np.zeros(len(largests_ias)) #for i in np.arange(len(ias_SA)): # ias_vert_faces = marching_cubes_lewiner(unique_ias_volumes == largests_ias[i], 0, spacing=(px_edge,px_edge,px_edge)) # ias_SA[i] = mesh_surface_area(ias_vert_faces[0],ias_vert_faces[1]) # print(ias_SA[i]) # ## Adjust depending on if you have IAS that are large but not connected such as ## when bundle sheath extensions are present. #largest_connected_ias_SA = sum(ias_SA[-1:]) #largest_connected_ias_volume = sum(ias_area[ordered_ias[-1:]]) * vx_volume # Write the data into a data frame data_out = { 'LeafArea': leaf_area, 'LeafThickness': leaf_thickness.mean(), 'LeafThickness_SD': leaf_thickness.std(), 'MesophyllThickness': mesophyll_thickness.mean(), 'MesophyllThickness_SD': mesophyll_thickness.std(), 'ADEpidermisThickness': epidermis_adaxial_thickness.mean(), 'ADEpidermisThickness_SD': epidermis_adaxial_thickness.std(), 'ABEpidermisThickness': epidermis_abaxial_thickness.mean(), 'ABEpidermisThickness_SD': epidermis_abaxial_thickness.std(), 'LeafVolume': leaf_volume, 'MesophyllVolume': mesophyll_volume, 'ADEpidermisVolume': epidermis_adaxial_volume, 'ABEpidermisVolume': epidermis_abaxial_volume, 'VeinVolume': vein_volume, 'BSVolume': bs_volume, 'VeinBSVolume': vein_volume + bs_volume, 'CellVolume': cell_volume, 'IASVolume': air_volume, 'IASSurfaceArea': true_ias_SA, 'PixelSize': px_edge, '_SLICEStrimmed': trim_slices, '_X_trimmed_left': trim_column_L * 2, '_X_trimmed_right': trim_column_L * 2 } # 'IASLargestConnectedVolume':largest_connected_ias_volume, # 'IASLargestConnectedSA':largest_connected_ias_SA results_out = DataFrame(data_out, index={sample_name}) # Save the data to a CSV print('### Saving results to text file ###') results_out.to_csv(base_folder_name + sample_name + '/' + sample_name + 'RESULTS.txt', sep='\t', encoding='utf-8') print('Data saved') for key, value in data_out.items(): print(str(key) + ' : ' + str(round(value, 3))) print('') print('Done with ' + sample_name) print('') # j = j + 1
def march(volume, _): verts, faces, normals, values = marching_cubes_lewiner(volume, 1) faces = correct_mesh_orientation(volume, verts, faces) return verts, None, faces
def reconstruction(net, cuda, calib_tensor, resolution, b_min, b_max, thresh=0.5, use_octree=False, num_samples=10000, transform=None): ''' Reconstruct meshes from sdf predicted by the network. :param net: a BasePixImpNet object. call image filter beforehead. :param cuda: cuda device :param calib_tensor: calibration tensor :param resolution: resolution of the grid cell :param b_min: bounding box corner [x_min, y_min, z_min] :param b_max: bounding box corner [x_max, y_max, z_max] :param use_octree: whether to use octree acceleration :param num_samples: how many points to query each gpu iteration :return: marching cubes results. ''' # First we create a grid by resolution # and transforming matrix for grid coordinates to real world xyz coords, mat = create_grid(resolution, resolution, resolution) #b_min, b_max, transform=transform) calib = calib_tensor[0].cpu().numpy() calib_inv = inv(calib) coords = coords.reshape(3, -1).T coords = np.matmul( np.concatenate([coords, np.ones((coords.shape[0], 1))], 1), calib_inv.T)[:, :3] coords = coords.T.reshape(3, resolution, resolution, resolution) # Then we define the lambda function for cell evaluation def eval_func(points): points = np.expand_dims(points, axis=0) points = np.repeat(points, 1, axis=0) samples = torch.from_numpy(points).to(device=cuda).float() net.query(samples, calib_tensor) pred = net.get_preds()[0][0] return pred.detach().cpu().numpy() # Then we evaluate the grid if use_octree: sdf = eval_grid_octree(coords, eval_func, num_samples=num_samples) else: sdf = eval_grid(coords, eval_func, num_samples=num_samples) # Finally we do marching cubes try: verts, faces, normals, values = measure.marching_cubes_lewiner( sdf, thresh) # transform verts into world coordinate system trans_mat = np.matmul(calib_inv, mat) verts = np.matmul(trans_mat[:3, :3], verts.T) + trans_mat[:3, 3:4] verts = verts.T # in case mesh has flip transformation if np.linalg.det(trans_mat[:3, :3]) < 0.0: faces = faces[:, ::-1] return verts, faces, normals, values except: print('error cannot marching cubes') return -1
def _get_isosurface(self, interp_factor=1): """ Parameters ---------- interp_factor : TYPE, optional DESCRIPTION. The default is 1. Returns ------- TYPE DESCRIPTION. verts TYPE DESCRIPTION. faces TYPE DESCRIPTION. normals TYPE DESCRIPTION. vlues """ # Amount of kpoints needed to add on to fully sample 1st BZ padding_x = self.padding[0] padding_y = self.padding[1] padding_z = self.padding[2] eigen_matrix = np.pad(self.V_matrix, ((padding_x, padding_x), (padding_y, padding_y), (padding_z, padding_z)), "wrap") bnd = self.surface_boundaries if interp_factor != 1: # Fourier interpolate the mapped function E(x,y,z) eigen_matrix = fft_interpolate(eigen_matrix, interp_factor) # after the FFT we loose the center of the BZ, using numpy roll we # bring back the center of the BZ # eigen_matrix = np.roll(eigen_matrix,4 , # axis=[0, 1, 2]) try: verts, faces, normals, values = measure.marching_cubes_lewiner( eigen_matrix, self.isovalue) except BaseException: print("No isosurface for this band") return None, None, None, None # recenter for ix in range(3): if self.file == "bxsf" or self.file == "qe" or self.file == 'lobster': # verts[:, ix] -= verts[:, ix].min() # verts[:, ix] -= (verts[:, ix].max() - # verts[:, ix].min()) / 2 verts[:, ix] *= self.dxyz[ix] / interp_factor # if bnd is not None and interp_factor != 1: # print((verts[:, ix].min() - bnd[ix][0])) # verts[:, ix] -= (verts[:, ix].min() - bnd[ix][0]) verts[:, ix] -= 1 * self.supercell[ix] # if bnd is not None and interp_factor != 1: # print((verts[:, ix].min() - bnd[ix][0])) # verts[:, ix] -= (verts[:, ix].min() - bnd[ix][0]) else: verts[:, ix] -= verts[:, ix].min() verts[:, ix] -= (verts[:, ix].max() - verts[:, ix].min()) / 2 verts[:, ix] *= self.dxyz[ix] / interp_factor # verts[:, ix] -= 2*self.supercell[ix] #verts[:, ix] -= self.supercell[ix] if bnd is not None and interp_factor != 1: verts[:, ix] -= (verts[:, ix].min() - bnd[ix][0]) #+self.origin[ix] # verts[:, ix] *= self.dxyz[ix] / interp_factor # print(self.dxyz) # if self.file == "bxsf": # verts[:, ix] -= 0.5 # if bnd is not None and interp_factor != 1: # print((verts[:, ix].min() - bnd[ix][0])) # verts[:, ix] -= (verts[:, ix].min() - bnd[ix][0]) # if self.file == "bxsf": # verts[:, ix] -= 0.50 # x_shift = verts[:,0].min() - bnd[0] # y_shift = verts[:,1].min() - bnd[1] # z_shift = verts[:,2].min() - bnd[2] # transfare from fraction to cartesian # verts = np.dot(verts, self.reciprocal_) # new_faces = np.zeros(shape=(len(faces), 4)) # new_faces[:, 0] = 3 # new_faces[:, 1:] = faces # faces = new_faces return verts, faces, normals, values
from sklearn.metrics import average_precision_score as ap_score from torch.utils.data import DataLoader from torchvision import datasets, models, transforms from tqdm import tqdm from skimage import measure from dataset import PeopleDataset from Model import Version1, Version2 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") batch_size = 4 train_data = PeopleDataset(flag='', data_range=(2000, 2500)) net = Version1().to(device) net.load_state_dict(torch.load('models/model_experiment1.pth')) imgs = [] imgs.append(train_data[7][1]) imgs.append(train_data[165][1]) imgs.append(train_data[165 + 125][1]) imgs.append(train_data[165 + 125 * 2][1]) from mayavi import mlab for img in imgs: # voxels=net(img.unsqueeze(0).cuda())[3][0].cpu().detach().numpy() voxels = img.detach().cpu().numpy() verts, faces, normals, values = measure.marching_cubes_lewiner( voxels, level=0.1, step_size=1, spacing=(16., 16., 16.)) # doctest: +SKIP mlab.triangular_mesh([vert[0] for vert in verts], [vert[1] for vert in verts], [vert[2] for vert in verts], faces) # doctest: +SKIP mlab.show()
def plot(grid, **kwargs): """ """ u, ucft, ax, plot_error, title, celldata = None, None, None, False, "", False if 'u' in kwargs: u = kwargs.pop('u') if 'ufct' in kwargs: ufct = kwargs.pop('ufct') if 'plot_error' in kwargs: plot_error = kwargs.pop('plot_error') if 'title' in kwargs: title = kwargs.pop('title') if 'ax' in kwargs: ax = kwargs['ax'] if 'celldata' in kwargs: celldata = kwargs.pop('celldata') d = grid.dim if ax == None: if d == 3: ax = plt.gca(projection='3d') else: ax = plt.gca() if celldata: x = grid.coordCenters() else: x = grid.coord() ax.set_title(title) u = u.reshape(x[0].shape) if x[0].shape != u.shape: raise ValueError( f"Problem in plot x[0].shape = {x[0].shape} != {u.shape} = u.shape (celldata={celldata})" ) if d == 1: if u is None: ax.plot(x[0], np.full_like(x[0], 1), 'rx-') else: ax.plot(x[0], u, '-xb') if plot_error: if ufct is None: raise ValueError("Problem: need exact solution") plt.plot(x.ravel(), ufct(x), '-r') elif d == 2: if u is None: ax.plot(x[0], x[1], marker='x', color='r', linestyle='none') else: ax.plot(x[0], x[1], marker='x', color='r', linestyle='none') cnt = ax.contour(*x, u) ax.clabel(cnt, cnt.levels, inline=True, fmt='%.1f', fontsize=10) elif d == 3: if u is None: ax.scatter(x[0], x[1], x[2], color='r') else: from skimage.measure import marching_cubes_lewiner verts, faces, _, _ = marching_cubes_lewiner(u, np.mean(u), spacing=(0.1, 0.1, 0.1)) ax.plot_trisurf(verts[:, 0], verts[:, 1], faces, verts[:, 2], cmap='Spectral', lw=1) # else: # import pyvista as pv # grid = pv.StructuredGrid(x[0], x[1], x[2]) # grid["U"] = u # contours = grid.contour([np.mean(u)]) # pv.set_plot_theme('document') # p = pv.Plotter() # p.add_mesh(contours, scalars=contours.points[:, 2], show_scalar_bar=False) # p.show() else: raise ValueError(f"Not written: plot in d={d}") if not 'ax' in kwargs: plt.show()
number_cells = 10 #100 length_cell = np.array([(max_grid[0] - min_grid[0]) / number_cells, (max_grid[1] - min_grid[1]) / number_cells, (max_grid[2] - min_grid[2]) / number_cells]) # Create a volume grid to compute the scalar field for surface reconstruction volume = np.zeros((number_cells + 1, number_cells + 1, number_cells + 1), dtype=np.float32) # Compute the scalar field in the grid compute_hoppe(points, normals, volume, number_cells, min_grid, length_cell) #compute_eimls(points,volume,number_cells,min_grid,length_cell) # Compute the mesh from the scalar field based on marching cubes algorithm verts, faces, normals, values = measure.marching_cubes_lewiner( volume, level=0.0, spacing=(length_cell[0], length_cell[1], length_cell[2])) # Plot the mesh using matplotlib 3D fig = plt.figure(figsize=(10, 10)) ax = fig.add_subplot(111, projection='3d') mesh = Poly3DCollection(verts[faces]) mesh.set_edgecolor('k') ax.add_collection3d(mesh) ax.set_xlim(0, number_cells * length_cell[0]) ax.set_ylim(0, number_cells * length_cell[1]) ax.set_zlim(0, number_cells * length_cell[2]) plt.axis('off') plt.show()
if not reco_screw: pr = -(p[0, :, mid, :] - pref[:, mid, :]) / scale else: pr = -pscrew[:, mid, :] / scale # reconstruct x = op.domain.element(np.zeros(op.domain.shape)) reconstruct_medianfilter(op, pr, x, niter=8, bounds=[0, 1]) y = x.data # recon = op.adjoint # y = recon(pr).data if reco_3d: try: verts, faces, _, _ = measure.marching_cubes_lewiner( y) # spacing=(0.1, 0.1, 0.1)) fig = plt.figure(2398472309476) ax = fig.add_subplot(111, projection='3d') ax.plot_trisurf(verts[:, 0], verts[:, 1], faces, verts[:, 2], cmap='Spectral', lw=1) ax.invert_zaxis() plt.pause(.1) except: print("No surfacelevel plot was produced.") pass _, (ax1, ax2, ax3) = plt.subplots(1, 3, num='892348923')
for indexX in range(dim_x): for indexY in range(dim_y): for indexZ in range(dim_z): if (str(data[(indexX + (500 * (indexY + (500 * (indexZ)))))]) != outlier): Data[indexX][indexY][indexZ] = data[(indexX + (500 * (indexY + (500 * (indexZ)))))] else: Data[indexX][indexY][indexZ] = 0.0 x, y, z = np.mgrid[0:499:500, 0:499:500, 0:99:100] vol = Data verts, faces, p, q = measure.marching_cubes_lewiner(vol, -1000, spacing=(1, 1, 1)) fig = plt.figure() ax = fig.gca(projection='3d') surf = ax.plot_trisurf(verts[:, 0], verts[:, 1], faces, verts[:, 2], linewidth=0.2, cmap="viridis", lw=1) fig.colorbar(surf) plt.show()
''' for level in range(1, 10): for j in range(0, rows - 2): for k in range(0, cols - 2): pixel_value = show[j][k] if pixel_value == 0: pass elif pixel_value == 127: voxel[j + 1][k + 1][:4] = pixel_value else: voxel[j + 1][k + 1][:10] = pixel_value print("voxel created!!") verts, faces, normals, values = measure.marching_cubes_lewiner( volume=voxel, level=120, spacing=(1., 1., 1.), gradient_direction='descent') mymesh = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype)) for i, f in enumerate(faces): for j in range(3): mymesh.vectors[i][j] = verts[f[j], :] print("mesh created!!") mymesh.save('test.stl') print("mesh saved")
def plot_isosurface(data, level=None, color=default_color, wireframe=True, surface=True, controls=True): """Plot a surface at constant value (like a 2d contour) :param float level: value where the surface should lie :param color: color of the surface, although it can be an array, the length is difficult to predict beforehand, if per vertex color are needed, it is better to set them on the returned mesh afterwards. :param bool wireframe: draw lines between the vertices :param bool surface: draw faces/triangles between the vertices :param bool controls: add controls to change the isosurface :return: :any:`Mesh` """ from skimage import measure if level is None: level = np.median(data) if hasattr(measure, 'marching_cubes_lewiner'): values = measure.marching_cubes_lewiner(data, level) else: values = measure.marching_cubes(data, level) verts, triangles = values[: 2] # version 0.13 returns 4 values, normals, values # in the future we may want to support normals and the values (with colormap) # and require skimage >= 0.13 x, y, z = verts.T mesh = plot_trisurf(x, y, z, triangles=triangles, color=color) if controls: vmin, vmax = np.percentile(data, 1), np.percentile(data, 99) step = (vmax - vmin) / 250 level_slider = ipywidgets.FloatSlider(value=level, min=vmin, max=vmax, step=step, icon='eye') recompute_button = ipywidgets.Button(description='update') controls = ipywidgets.HBox(children=[level_slider, recompute_button]) current.container.children += (controls, ) def recompute(*_ignore): level = level_slider.value recompute_button.description = "updating..." if hasattr(measure, 'marching_cubes_lewiner'): values = measure.marching_cubes_lewiner(data, level) else: values = measure.marching_cubes(data, level) verts, triangles = values[: 2] # version 0.13 returns 4 values, normals, values # in the future we may want to support normals and the values (with colormap) # and require skimage >= 0.13 x, y, z = verts.T with mesh.hold_sync(): mesh.x = x mesh.y = y mesh.z = z mesh.triangles = triangles.astype(dtype=np.uint32) recompute_button.description = "update" recompute_button.on_click(recompute) return mesh
def from_binary_vol(cls, downsampled_volume_zyx, fullres_box_zyx=None, method='skimage', step_size=1): """ Alternate constructor. Run marching cubes on the given volume and return a Mesh object. Args: downsampled_volume_zyx: A binary volume, possibly at a downsampled resolution. fullres_box_zyx: The bounding-box inhabited by the given volume, in FULL-res coordinates. method: Which library to use for marching_cubes. For now the only choice is 'skimage'. """ assert downsampled_volume_zyx.ndim == 3 if fullres_box_zyx is None: fullres_box_zyx = np.array([(0,0,0), downsampled_volume_zyx.shape]) else: fullres_box_zyx = np.asarray(fullres_box_zyx) # Infer the resolution of the downsampled volume resolution = (fullres_box_zyx[1] - fullres_box_zyx[0]) // downsampled_volume_zyx.shape try: if method == 'skimage': padding = np.array([0,0,0]) # Tiny volumes trigger a corner case in skimage, so we pad them with zeros. # This results in faces on all sides of the volume, # but it's not clear what else to do. if (np.array(downsampled_volume_zyx.shape) <= 2).any(): padding = np.array([2,2,2], dtype=int) - downsampled_volume_zyx.shape padding = np.maximum([0,0,0], padding) downsampled_volume_zyx = np.pad( downsampled_volume_zyx, tuple(zip(padding, padding)), 'constant' ) vertices_zyx, faces, normals_zyx, _values = marching_cubes_lewiner(downsampled_volume_zyx, 0.5, step_size=step_size) # Skimage assumes that the coordinate origin is CENTERED inside pixel (0,0,0), # whereas we assume that the origin is the UPPER-LEFT corner of pixel (0,0,0). # Therefore, shift the results by a half-pixel. vertices_zyx += 0.5 if padding.any(): vertices_zyx -= padding else: msg = f"Uknown method: {method}" logger.error(msg) raise RuntimeError(msg) except ValueError: if downsampled_volume_zyx.all(): # Completely full boxes are not meshable -- they would be # open on all sides, leaving no vertices or faces. # Just return an empty mesh. empty_vertices = np.zeros( (0, 3), dtype=np.float32 ) empty_faces = np.zeros( (0, 3), dtype=np.uint32 ) return Mesh(empty_vertices, empty_faces, box=fullres_box_zyx) else: logger.error("Error during mesh generation") raise # Upscale and translate the mesh into place vertices_zyx[:] *= resolution vertices_zyx[:] += fullres_box_zyx[0] return Mesh(vertices_zyx, faces, normals_zyx, fullres_box_zyx)
(8*x)**2 + (8*y-2)**2 + (8*z)**2 + 16 - 1.85*1.85 ) * ( (8*x)**2 + (8*y-2)**2 + (8*z)**2 + 16 - 1.85*1.85 ) - 64 * ( (8*x)**2 + (8*y-2)**2 ) ) * ( ( (8*x)**2 + ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2 + 16 - 1.85*1.85 ) * ( (8*x)**2 + ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2 + 16 - 1.85*1.85 ) - 64 * ( ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2 ) ) + 1025 # Uncommenting the line below will yield different results for classic MC #vol = -vol elif SELECT == 4: vol = ellipsoid(4, 3, 2, levelset=True) isovalue = 0 # Get surface meshes t0 = time.time() vertices1, faces1, _ = marching_cubes_lewiner(vol, isovalue, gradient_direction=gradient_dir, use_classic=False) print('finding surface lewiner took %1.0f ms' % (1000*(time.time()-t0)) ) t0 = time.time() vertices2, faces2, _ = marching_cubes_classic(vol, isovalue, gradient_direction=gradient_dir) print('finding surface classic took %1.0f ms' % (1000*(time.time()-t0)) ) # Show vv.figure(1); vv.clf() a1 = vv.subplot(121); m1 = vv.mesh(np.fliplr(vertices1), faces1) a2 = vv.subplot(122); m2 = vv.mesh(np.fliplr(vertices2), faces2) a1.camera = a2.camera # visvis uses right-hand rule, gradient_direction param uses left-hand rule m1.cullFaces = m2.cullFaces = 'front' # None, front or back
def get_surface_high_res_mesh(sdf, resolution=100, box_side_length=2.0, largest_component=True): # get low res mesh to sample point cloud grid = get_grid_uniform(64, box_side_length=box_side_length) z = [] points = grid['grid_points'] for i, pnts in enumerate(torch.split(points, 100000, dim=0)): z.append(sdf(pnts).detach().cpu().numpy()) z = np.concatenate(z, axis=0) z = z.astype(np.float32) if np.sign(z.min() * z.max()) >= 0: return trimesh.Trimesh([]) verts, faces, normals, values = measure.marching_cubes_lewiner( volume=z.reshape(grid['xyz'][1].shape[0], grid['xyz'][0].shape[0], grid['xyz'][2].shape[0]).transpose([1, 0, 2]), level=0, spacing=(grid['xyz'][0][2] - grid['xyz'][0][1], grid['xyz'][0][2] - grid['xyz'][0][1], grid['xyz'][0][2] - grid['xyz'][0][1])) verts = verts + \ np.array([grid['xyz'][0][0], grid['xyz'][1][0], grid['xyz'][2][0]]) mesh_low_res = trimesh.Trimesh(verts, faces, normals) components = mesh_low_res.split(only_watertight=False) areas = np.array([c.area for c in components], dtype=np.float) mesh_low_res = components[areas.argmax()] recon_pc = trimesh.sample.sample_surface(mesh_low_res, 10000)[0] recon_pc = torch.from_numpy(recon_pc).float().cuda() # Center and align the recon pc s_mean = recon_pc.mean(dim=0) s_cov = recon_pc - s_mean s_cov = torch.mm(s_cov.transpose(0, 1), s_cov) vecs = torch.eig(s_cov, True)[1].transpose(0, 1) if torch.det(vecs) < 0: vecs = torch.mm( torch.tensor([[1, 0, 0], [0, 0, 1], [0, 1, 0]]).cuda().float(), vecs) helper = torch.bmm( vecs.unsqueeze(0).repeat(recon_pc.shape[0], 1, 1), (recon_pc - s_mean).unsqueeze(-1)).squeeze() grid_aligned = get_grid(helper.cpu(), resolution) grid_points = grid_aligned['grid_points'] g = [] for i, pnts in enumerate(torch.split(grid_points, 100000, dim=0)): g.append((torch.bmm( vecs.unsqueeze(0).repeat(pnts.shape[0], 1, 1).transpose(1, 2), pnts.unsqueeze(-1)).squeeze() + s_mean).cpu().detach()) grid_points = torch.cat(g, dim=0) # MC to new grid points = grid_points z = [] for i, pnts in enumerate(torch.split(points, 50000, dim=0)): z.append(sdf(pnts.cuda()).detach().cpu().numpy()) z = np.concatenate(z, axis=0) meshexport = None if (not (np.min(z) > 0 or np.max(z) < 0)): z = z.astype(np.float32) verts, faces, normals, values = measure.marching_cubes_lewiner( volume=z.reshape(grid_aligned['xyz'][1].shape[0], grid_aligned['xyz'][0].shape[0], grid_aligned['xyz'][2].shape[0]).transpose( [1, 0, 2]), level=0, spacing=(grid_aligned['xyz'][0][2] - grid_aligned['xyz'][0][1], grid_aligned['xyz'][0][2] - grid_aligned['xyz'][0][1], grid_aligned['xyz'][0][2] - grid_aligned['xyz'][0][1])) verts = torch.from_numpy(verts).cuda().float() verts = torch.bmm( vecs.unsqueeze(0).repeat(verts.shape[0], 1, 1).transpose(1, 2), verts.unsqueeze(-1)).squeeze() verts = (verts + grid_points[0].cuda()).cpu().numpy() meshexport = trimesh.Trimesh(verts, faces, normals) if largest_component: components = meshexport.split(only_watertight=False) areas = np.array([c.area for c in components], dtype=np.float) meshexport = components[areas.argmax()] return meshexport
def plot_isosurface( filename=None, vlsvobj=None, filedir=None, step=None, outputdir=None, nooverwrite=None, # surf_var=None, surf_op=None, surf_level=None, color_var=None, color_op=None, surf_step=1, # title=None, cbtitle=None, draw=None, usesci=None, # boxm=[], boxre=[], colormap=None, run=None, wmark=None, nocb=None, unit=None, thick=1.0, scale=1.0, vmin=None, vmax=None, lin=None, symlog=None, ): ''' Plots a coloured plot with axes and a colour bar. :kword filename: path to .vlsv file to use for input. Assumes a bulk file. :kword vlsvobj: Optionally provide a python vlsvfile object instead :kword filedir: Optionally provide directory where files are located and use step for bulk file name :kword step: output step index, used for constructing output (and possibly input) filename :kword outputdir: path to directory where output files are created (default: $HOME/Plots/) If directory does not exist, it will be created. If the string does not end in a forward slash, the final parti will be used as a perfix for the files. :kword nooverwrite: Set to only perform actions if the target output file does not yet exist :kword surf_var: Variable to read for defining surface :kword surf_op: Operator to use for variable to read surface :kword surf_level: Level at which to define surface :kword surf_step: Vertex stepping for surface generation: larger value returns coarser surface :kword color_var: Variable to read for coloring surface :kword color_op: Operator to use for variable to color surface ('x', 'y', 'z'; if None and color_var is a vector, the magnitude is taken) :kword boxm: zoom box extents [x0,x1,y0,y1] in metres (default and truncate to: whole simulation box) :kword boxre: zoom box extents [x0,x1,y0,y1] in Earth radii (default and truncate to: whole simulation box) :kword colormap: colour scale for plot, use e.g. hot_desaturated, jet, viridis, plasma, inferno, magma, parula, nipy_spectral, RdBu, bwr :kword run: run identifier, used for constructing output filename :kword title: string to use as plot title instead of time :kword cbtitle: string to use as colorbar title instead of map name :kword unit: Plot axes using 10^{unit} m (default: Earth radius R_E) :kwird usesci: Use scientific notation for colorbar ticks? (default: 1) :kword vmin,vmax: min and max values for colour scale and colour bar. If no values are given, min and max values for whole plot (non-zero rho regions only) are used. :kword lin: Flag for using linear colour scaling instead of log :kword symlog: Use logarithmic scaling, but linear when abs(value) is below the value given to symlog. Allows symmetric quasi-logarithmic plots of e.g. transverse field components. A given of 0 translates to a threshold of max(abs(vmin),abs(vmax)) * 1.e-2. :kword wmark: If set to non-zero, will plot a Vlasiator watermark in the top left corner. :kword draw: Set to nonzero in order to draw image on-screen instead of saving to file (requires x-windowing) :kword scale: Scale text size (default=1.0) :kword thick: line and axis thickness, default=1.0 :kword nocb: Set to suppress drawing of colourbar :returns: Outputs an image to a file or to the screen. .. code-block:: python # Example usage: ''' # Verify the location of this watermark image watermarkimage = os.path.join(os.path.dirname(__file__), 'logo_color.png') # watermarkimage=os.path.expandvars('$HOME/appl_taito/analysator/pyPlot/logo_color.png') outputprefix = '' if outputdir == None: outputdir = os.path.expandvars('$HOME/Plots/') outputprefixind = outputdir.rfind('/') if outputprefixind >= 0: outputprefix = outputdir[outputprefixind + 1:] outputdir = outputdir[:outputprefixind + 1] if not os.path.exists(outputdir): os.makedirs(outputdir) # Input file or object if filename != None: f = pt.vlsvfile.VlsvReader(filename) elif vlsvobj != None: f = vlsvobj elif ((filedir != None) and (step != None)): filename = filedir + 'bulk.' + str(step).rjust(7, '0') + '.vlsv' f = pt.vlsvfile.VlsvReader(filename) else: print( "Error, needs a .vlsv file name, python object, or directory and step" ) return # Scientific notation for colorbar ticks? if usesci == None: usesci = 1 if colormap == None: # Default values colormap = "hot_desaturated" if color_op != None: colormap = "bwr" cmapuse = matplotlib.cm.get_cmap(name=colormap) fontsize = 8 * scale # Most text fontsize2 = 10 * scale # Time title fontsize3 = 5 * scale # Colour bar ticks # Plot title with time timeval = None timeval = f.read_parameter("time") if timeval == None: timeval = f.read_parameter("t") if timeval == None: print "Unknown time format encountered" # Plot title with time if title == None: if timeval == None: print "Unknown time format encountered" plot_title = '' else: #plot_title = "t="+str(np.int(timeval))+' s' plot_title = "t=" + '{:4.2f}'.format(timeval) + ' s' else: plot_title = title # step, used for file name if step != None: stepstr = '_' + str(step).rjust(7, '0') else: stepstr = '' # If run name isn't given, just put "plot" in the output file name if run == None: run = 'plot' # Verify validity of operator surf_opstr = '' color_opstr = '' if color_op != None: if color_op != 'x' and color_op != 'y' and color_op != 'z': print("Unknown operator " + color_op + ", defaulting to None/magnitude for a vector.") color_op = None else: # For components, always use linear scale, unless symlog is set color_opstr = '_' + color_op if symlog == None: lin = 1 # Verify validity of operator if surf_op != None: if surf_op != 'x' and surf_op != 'y' and surf_op != 'z': print("Unknown operator " + surf_op) surf_op = None else: surf_opstr = '_' + surf_op # Output file name surf_varstr = surf_var.replace("/", "_") if color_var != None: color_varstr = color_var.replace("/", "_") else: color_varstr = "solid" savefigname = outputdir + outputprefix + run + "_isosurface_" + surf_varstr + surf_opstr + "-" + color_varstr + color_opstr + stepstr + ".png" # Check if target file already exists and overwriting is disabled if (nooverwrite != None and os.path.exists(savefigname)): # Also check that file is not empty if os.stat(savefigname).st_size > 0: return else: print("Found existing file " + savefigname + " of size zero. Re-rendering.") Re = 6.371e+6 # Earth radius in m #read in mesh size and cells in ordinary space [xsize, ysize, zsize] = f.get_spatial_mesh_size() [xmin, ymin, zmin, xmax, ymax, zmax] = f.get_spatial_mesh_extent() cellsize = (xmax - xmin) / xsize cellids = f.read_variable("CellID") # xsize = f.read_parameter("xcells_ini") # ysize = f.read_parameter("ycells_ini") # zsize = f.read_parameter("zcells_ini") # xmin = f.read_parameter("xmin") # xmax = f.read_parameter("xmax") # ymin = f.read_parameter("ymin") # ymax = f.read_parameter("ymax") # zmin = f.read_parameter("zmin") # zmax = f.read_parameter("zmax") if (xsize == 1) or (ysize == 1) or (zsize == 1): print("Error: isosurface plotting requires 3D spatial domain!") return simext = [xmin, xmax, ymin, ymax, zmin, zmax] sizes = [xsize, ysize, zsize] # Select window to draw if len(boxm) == 6: boxcoords = boxm elif len(boxre) == 6: boxcoords = [i * Re for i in boxre] else: boxcoords = simext # If box extents were provided manually, truncate to simulation extents boxcoords[0] = max(boxcoords[0], simext[0]) boxcoords[1] = min(boxcoords[1], simext[1]) boxcoords[2] = max(boxcoords[2], simext[2]) boxcoords[3] = min(boxcoords[3], simext[3]) boxcoords[4] = max(boxcoords[4], simext[4]) boxcoords[5] = min(boxcoords[5], simext[5]) # Axes and units (default R_E) if unit != None: # Use m or km or other if unit == 0: unitstr = r'm' if unit == 3: unitstr = r'km' else: unitstr = r'$10^{' + str(int(unit)) + '}$ m' unit = np.power(10, int(unit)) else: unitstr = r'$\mathrm{R}_{\mathrm{E}}$' unit = Re # Scale data extent and plot box simext_org = simext simext = [i / unit for i in simext] boxcoords = [i / unit for i in boxcoords] if color_op == None and color_var != None: color_op = 'pass' cb_title = color_var color_data = f.read_variable(color_var, cellids=[1, 2]) # If value was vector value, take magnitude if np.size(color_data) != 2: cb_title = r"$|" + color_var + "|$" elif color_op != None and color_var != None: cb_title = r" $" + color_var + "_" + color_op + "$" else: # color_var==None cb_title = "" nocb = 1 if f.check_variable(surf_var) != True: print("Error, surface variable " + surf_var + " not found!") return if surf_op == None: surf_data = f.read_variable(surf_var) # If value was vector value, take magnitude if np.ndim(surf_data) != 1: surf_data = np.linalg.norm(np.asarray(surf_data), axis=-1) else: surf_data = f.read_variable(surf_var, operator=surf_op) if np.ndim(surf_data) != 1: print("Error reading surface variable " + surf_var + "! Exiting.") return -1 # Reshape data to ordered 3D arrays for plotting #color_data = color_data[cellids.argsort()].reshape([sizes[2],sizes[1],sizes[0]]) surf_data = surf_data[cellids.argsort()].reshape( [sizes[2], sizes[1], sizes[0]]) # The data we have now is Z,Y,X # Rotate data so it's X,Y,Z surf_data = np.swapaxes(surf_data, 0, 2) if surf_level == None: surf_level = 0.5 * (np.amin(surf_data) + np.amax(surf_data)) print("Minimum found surface value " + str(np.amin(surf_data)) + " surface level " + str(surf_level) + " max " + str(np.amax(surf_data))) # Select ploitting back-end based on on-screen plotting or direct to file without requiring x-windowing if draw != None: plt.switch_backend('TkAgg') else: plt.switch_backend('Agg') # skimage.measure.marching_cubes_lewiner(volume, level=None, spacing=(1.0, 1.0, 1.0), # gradient_direction='descent', step_size=1, # allow_degenerate=True, use_classic=False) # #Generate mask for only visible section of data # # Apparently the marching cubes method does not ignore masked values, so this doesn't directly help. # [Xmesh,Ymesh, Zmesh] = scipy.meshgrid(np.linspace(simext[0],simext[1],num=sizes[0]),np.linspace(simext[2],simext[3],num=sizes[1]),np.linspace(simext[4],simext[5],num=sizes[2])) # print("simext",simext) # print("sizes",sizes) # print("boxcoords", boxcoords) # maskgrid = np.ma.masked_where(Xmesh<(boxcoords[0]), Xmesh) # maskgrid = np.ma.masked_where(Xmesh>(boxcoords[1]), maskgrid) # maskgrid = np.ma.masked_where(Ymesh<(boxcoords[2]), maskgrid) # maskgrid = np.ma.masked_where(Ymesh>(boxcoords[3]), maskgrid) # maskgrid = np.ma.masked_where(Zmesh<(boxcoords[4]), maskgrid) # maskgrid = np.ma.masked_where(Zmesh>(boxcoords[5]), maskgrid) # surf_data = np.ma.asarray(surf_data) # surf_data.mask = maskgrid.mask # print(surf_data.count()) surf_spacing = ((xmax - xmin) / (xsize * unit), (ymax - ymin) / (ysize * unit), (zmax - zmin) / (zsize * unit)) verts, faces, normals, arrays = measure.marching_cubes_lewiner( surf_data, level=surf_level, spacing=surf_spacing, gradient_direction='descent', step_size=surf_step, allow_degenerate=True, use_classic=False) # offset with respect to simulation domain corner #print("simext",simext) verts[:, 0] = verts[:, 0] + simext[0] verts[:, 1] = verts[:, 1] + simext[2] verts[:, 2] = verts[:, 2] + simext[4] # # Crop surface to box area # verts = [] # faces = [] # for i in np.arange(len(verts_nocrop[:,0])): # if ((verts_nocrop[i,0] > boxcoords[0]) and (verts_nocrop[i,0] < boxcoords[1]) and # (verts_nocrop[i,1] > boxcoords[2]) and (verts_nocrop[i,1] < boxcoords[3]) and # (verts_nocrop[i,2] > boxcoords[4]) and (verts_nocrop[i,2] < boxcoords[5])): # verts.append(verts_nocrop[i,:]) # faces.append(faces_nocrop[i,:]) # This is now incorrect # else: # dropped = dropped+1 # verts = np.asarray(verts) # faces = np.asarray(faces) # Next find color variable values at vertices if color_var != None: nverts = len(verts[:, 0]) print("Extracting color values for " + str(nverts) + " vertices and " + str(len(faces[:, 0])) + " faces.") all_coords = np.empty((nverts, 3)) for i in np.arange(nverts): # # due to mesh generation, some coordinates may be outside simulation domain # WARNING this means it might be doing wrong things in the periodic dimension of 2.9D runs. coords = verts[i, :] * unit coords[0] = max(coords[0], simext_org[0] + 0.1 * cellsize) coords[0] = min(coords[0], simext_org[1] - cellsize) coords[1] = max(coords[1], simext_org[2] + 0.1 * cellsize) coords[1] = min(coords[1], simext_org[3] - cellsize) coords[2] = max(coords[2], simext_org[4] + 0.1 * cellsize) coords[2] = min(coords[2], simext_org[5] - cellsize) all_coords[i] = coords # Use interpolated values, WARNING periodic y (2.9 polar) hard-coded here /!\ color_data = f.read_interpolated_variable( color_var, all_coords, operator=color_op, periodic=["False", "True", "False"]) # Make sure color data is 1-dimensional (e.g. magnitude of E instead of 3 components) if np.ndim(color_data) != 1: color_data = np.linalg.norm(color_data, axis=-1) if color_var == None: # dummy norm print("No surface color given, using dummy setup") norm = BoundaryNorm([0, 1], ncolors=cmapuse.N, clip=True) vminuse = 0 vmaxuse = 1 else: # If automatic range finding is required, find min and max of array # Performs range-finding on a masked array to work even if array contains invalid values color_data = np.ma.masked_invalid(color_data) if vmin != None: vminuse = vmin else: vminuse = np.ma.amin(color_data) if vmax != None: vmaxuse = vmax else: vmaxuse = np.ma.amax(color_data) # If vminuse and vmaxuse are extracted from data, different signs, and close to each other, adjust to be symmetric # e.g. to plot transverse field components if vmin == None and vmax == None: if (vminuse * vmaxuse < 0) and ( abs(abs(vminuse) - abs(vmaxuse)) / abs(vminuse) < 0.4 ) and (abs(abs(vminuse) - abs(vmaxuse)) / abs(vmaxuse) < 0.4): absval = max(abs(vminuse), abs(vmaxuse)) if vminuse < 0: vminuse = -absval vmaxuse = absval else: vminuse = absval vmaxuse = -absval # Check that lower bound is valid for logarithmic plots if (vminuse <= 0) and (lin == None) and (symlog == None): # Drop negative and zero values vminuse = np.ma.amin(np.ma.masked_less_equal(color_data, 0)) # If symlog scaling is set: if symlog != None: if symlog > 0: linthresh = symlog else: linthresh = max(abs(vminuse), abs(vmaxuse)) * 1.e-2 # Lin or log colour scaling, defaults to log if lin == None: # Special SymLogNorm case if symlog != None: #norm = SymLogNorm(linthresh=linthresh, linscale = 0.3, vmin=vminuse, vmax=vmaxuse, ncolors=cmapuse.N, clip=True) norm = SymLogNorm(linthresh=linthresh, linscale=0.3, vmin=vminuse, vmax=vmaxuse, clip=True) maxlog = int(np.ceil(np.log10(vmaxuse))) minlog = int(np.ceil(np.log10(-vminuse))) logthresh = int(np.floor(np.log10(linthresh))) logstep = 1 ticks = ( [-(10**x) for x in range(logthresh, minlog + 1, logstep)][::-1] + [0.0] + [(10**x) for x in range(logthresh, maxlog + 1, logstep)]) else: norm = LogNorm(vmin=vminuse, vmax=vmaxuse) ticks = LogLocator(base=10, subs=range(10)) # where to show labels else: # Linear levels = MaxNLocator(nbins=255).tick_values(vminuse, vmaxuse) norm = BoundaryNorm(levels, ncolors=cmapuse.N, clip=True) ticks = np.linspace(vminuse, vmaxuse, num=7) print("Selected color range: " + str(vminuse) + " to " + str(vmaxuse)) # Create 300 dpi image of suitable size fig = plt.figure(figsize=[4.0, 4.0], dpi=300) #ax1 = fig.gca(projection='3d') ax1 = fig.add_subplot(111, projection='3d') # Generate virtual bounding box to get equal aspect maxrange = np.array([ boxcoords[1] - boxcoords[0], boxcoords[3] - boxcoords[2], boxcoords[5] - boxcoords[4] ]).max() / 2.0 midvals = np.array([ boxcoords[1] + boxcoords[0], boxcoords[3] + boxcoords[2], boxcoords[5] + boxcoords[4] ]) / 2.0 # Three options: # 2.9D ecliptic, 2.9D polar, or 3D if ysize < 0.2 * xsize: # 2.9D polar, perform rotation generatedsurface = ax1.plot_trisurf(verts[:, 2], verts[:, 0], verts[:, 1], triangles=faces, cmap=cmapuse, norm=norm, vmin=vminuse, vmax=vmaxuse, lw=0, shade=False, edgecolors=None, antialiased=False) ax1.set_xlabel("z [" + unitstr + "]", fontsize=fontsize3) ax1.set_ylabel("x [" + unitstr + "]", fontsize=fontsize3) ax1.set_zlabel("y [" + unitstr + "]", fontsize=fontsize3) # Set camera angle ax1.view_init(elev=30., azim=90) # Set virtual bounding box ax1.set_xlim([midvals[2] - maxrange, midvals[2] + maxrange]) ax1.set_ylim([midvals[0] - maxrange, midvals[0] + maxrange]) ax1.set_zlim([midvals[1] - maxrange, midvals[1] + maxrange]) ax1.tick_params(labelsize=fontsize3) #,width=1.5,length=3) else: # 3D or 2.9D ecliptic, leave as is generatedsurface = ax1.plot_trisurf(verts[:, 0], verts[:, 1], verts[:, 2], triangles=faces, cmap=cmapuse, norm=norm, vmin=vminuse, vmax=vmaxuse, lw=0.2, shade=False, edgecolors=None) ax1.set_xlabel("x [" + unitstr + "]", fontsize=fontsize3) ax1.set_ylabel("y [" + unitstr + "]", fontsize=fontsize3) ax1.set_zlabel("z [" + unitstr + "]", fontsize=fontsize3) # Set camera angle ax1.view_init(elev=30., azim=0) # Set virtual bounding box ax1.set_xlim([midvals[0] - maxrange, midvals[0] + maxrange]) ax1.set_ylim([midvals[1] - maxrange, midvals[1] + maxrange]) ax1.set_zlim([midvals[2] - maxrange, midvals[2] + maxrange]) ax1.tick_params(labelsize=fontsize3) #,width=1.5,length=3) # Setting per-triangle colours for plot_trisurf needs to be done # as a separate set_array call. if color_var != None: # Find face-averaged colors # (simply setting the array to color_data failed for some reason) colors = np.mean(color_data[faces], axis=1) generatedsurface.set_array(colors) # Title and plot limits if len(plot_title) != 0: ax1.set_title(plot_title, fontsize=fontsize2, fontweight='bold') # ax1.set_aspect('equal') #<<<--- this does not work for 3D plots! # for axis in ['top','bottom','left','right']: # ax1.spines[axis].set_linewidth(thick) # ax1.xaxis.set_tick_params(width=thick,length=3) # ax1.yaxis.set_tick_params(width=thick,length=3) # #ax1.xaxis.set_tick_params(which='minor',width=3,length=5) # #ax1.yaxis.set_tick_params(which='minor',width=3,length=5) if cbtitle == None: cb_title_use = cb_title else: cb_title_use = cbtitle if nocb == None: # First draw colorbar if usesci == 0: cb = fig.colorbar(generatedsurface, ticks=ticks, drawedges=False, fraction=0.023, pad=0.02) else: cb = fig.colorbar(generatedsurface, ticks=ticks, format=mtick.FuncFormatter(fmt), drawedges=False, fraction=0.046, pad=0.04) if len(cb_title_use) != 0: cb.ax.set_title(cb_title_use, fontsize=fontsize2, fontweight='bold') cb.ax.tick_params(labelsize=fontsize3) #,width=1.5,length=3) cb.outline.set_linewidth(thick) # if too many subticks: if lin == None and usesci != 0 and symlog == None: # Note: if usesci==0, only tick labels at powers of 10 are shown anyway. # For non-square pictures, adjust tick count nlabels = len(cb.ax.yaxis.get_ticklabels()) / ratio valids = ['1', '2', '3', '4', '5', '6', '7', '8', '9'] if nlabels > 10: valids = ['1', '2', '3', '4', '5', '6', '8'] if nlabels > 19: valids = ['1', '2', '5'] if nlabels > 28: valids = ['1'] # for label in cb.ax.yaxis.get_ticklabels()[::labelincrement]: for label in cb.ax.yaxis.get_ticklabels(): # labels will be in format $x.0\times10^{y}$ if not label.get_text()[1] in valids: label.set_visible(False) # Add Vlasiator watermark if wmark != None: wm = plt.imread(get_sample_data(watermarkimage)) newax = fig.add_axes([0.01, 0.90, 0.3, 0.08], anchor='NW', zorder=-1) newax.imshow(wm) newax.axis('off') plt.tight_layout() savefig_pad = 0.1 # The default is 0.1 bbox_inches = None # Save output or draw on-screen if draw == None: # Note: generated title can cause strange PNG header problems # in rare cases. This problem is under investigation, but is related to the exact generated # title string. This try-catch attempts to simplify the time string until output succedes. try: plt.savefig(savefigname, dpi=300, bbox_inches=bbox_inches, pad_inches=savefig_pad) savechange = 0 except: savechange = 1 plot_title = "t=" + '{:4.1f}'.format(timeval) + ' s ' ax1.set_title(plot_title, fontsize=fontsize2, fontweight='bold') try: plt.savefig(savefigname, dpi=300, bbox_inches=bbox_inches, pad_inches=savefig_pad) except: plot_title = "t=" + str(np.int(timeval)) + ' s ' ax1.set_title(plot_title, fontsize=fontsize2, fontweight='bold') try: plt.savefig(savefigname, dpi=300, bbox_inches=bbox_inches, pad_inches=savefig_pad) except: plot_title = "" ax1.set_title(plot_title, fontsize=fontsize2, fontweight='bold') try: plt.savefig(savefigname, dpi=300, bbox_inches=bbox_inches, pad_inches=savefig_pad) except: print "Error:", sys.exc_info() print( "Error with attempting to save figure, sometimes due to matplotlib LaTeX integration." ) print( "Usually removing the title should work, but this time even that failed." ) savechange = -1 if savechange > 0: print("Due to rendering error, replaced image title with " + plot_title) if savechange >= 0: print(savefigname + "\n") else: plt.draw() plt.show()
def regionprops_3D(im): r""" Calculates various metrics for each labeled region in a 3D image. The ``regionsprops`` method in **skimage** is very thorough for 2D images, but is a bit limited when it comes to 3D images, so this function aims to fill this gap. Parameters ---------- im : array_like An imaging containing at least one labeled region. If a boolean image is received than the ``True`` voxels are treated as a single region labeled ``1``. Regions labeled 0 are ignored in all cases. Returns ------- An augmented version of the list returned by skimage's ``regionprops``. Information, such as ``volume``, can be found for region A using the following syntax: ``result[A-1].volume``. Notes ----- This function may seem slow compared to the skimage version, but that is because they defer calculation of certain properties until they are accessed while this one evalulates everything (inlcuding the deferred properties from skimage's ``regionprops``) Regions can be identified using a watershed algorithm, which can be a bit tricky to obtain desired results. *PoreSpy* includes the SNOW algorithm, which may be helpful. """ print('_' * 60) print('Calculating regionprops') results = regionprops(im, coordinates='xy') for i in tqdm(range(len(results))): mask = results[i].image mask_padded = sp.pad(mask, pad_width=1, mode='constant') temp = spim.distance_transform_edt(mask_padded) dt = extract_subsection(temp, shape=mask.shape) # --------------------------------------------------------------------- # Slice indices results[i].slice = results[i]._slice # --------------------------------------------------------------------- # Volume of regions in voxels results[i].volume = results[i].area # --------------------------------------------------------------------- # Volume of bounding box, in voxels results[i].bbox_volume = sp.prod(mask.shape) # --------------------------------------------------------------------- # Create an image of the border results[i].border = dt == 1 # --------------------------------------------------------------------- # Create an image of the maximal inscribed sphere r = dt.max() inv_dt = spim.distance_transform_edt(dt < r) results[i].inscribed_sphere = inv_dt < r # --------------------------------------------------------------------- # Find surface area using marching cubes and analyze the mesh tmp = sp.pad(sp.atleast_3d(mask), pad_width=1, mode='constant') tmp = spim.convolve(tmp, weights=ball(1)) / 5 verts, faces, norms, vals = marching_cubes_lewiner(volume=tmp, level=0) results[i].surface_mesh_vertices = verts results[i].surface_mesh_simplices = faces area = mesh_surface_area(verts, faces) results[i].surface_area = area # --------------------------------------------------------------------- # Find sphericity vol = results[i].volume r = (3 / 4 / sp.pi * vol)**(1 / 3) a_equiv = 4 * sp.pi * (r)**2 a_region = results[i].surface_area results[i].sphericity = a_equiv / a_region # --------------------------------------------------------------------- # Find skeleton of region results[i].skeleton = skeletonize_3d(mask) # --------------------------------------------------------------------- # Volume of convex image, equal to area in 2D, so just translating results[i].convex_volume = results[i].convex_area # --------------------------------------------------------------------- # Convert region grid to a graph am = grid_to_graph(*mask.shape, mask=mask) results[i].graph = am return results
def plot_cond(time, ds, outDir=''): # plt.close() # Use marching cubes to obtain the surface mesh of these ellipsoids # arr=np.transpose((ds['QCLOUD'].isel(Time=time)+ds['QICE'].isel(Time=time)).values) arr = np.transpose((ds['qcond'].isel(Time=time)).values) verts, faces, normals, values = measure.marching_cubes_lewiner(arr, 2e-5) # verts2, faces2, normals2, values2 = measure.marching_cubes_lewiner(arr, 5e-5) # Display resulting triangular mesh using Matplotlib. This can also be done # with mayavi (see skimage.measure.marching_cubes_lewiner docstring). fig = plt.figure(figsize=(9, 6)) ax = fig.add_subplot(111, projection='3d') # Fancy indexing: `verts[faces]` to generate a collection of triangles # to make it look less triangle like, reduce alpha mesh = Poly3DCollection(verts[faces], alpha=0.05) mesh.set_edgecolor('w') mesh.set_facecolor(mpl.cm.Greys(250)) ax.add_collection3d(mesh) # mesh2 = Poly3DCollection(verts2[faces2],alpha=1) # mesh2.set_edgecolor('w') # mesh2.set_facecolor((0,0,1)) # ax.add_collection3d(mesh2) ax.set_xlabel("x (km)") ax.set_ylabel("y (km)") ax.set_zlabel("z (km)") ax.set_xticks([0, 96]) ax.set_xticklabels([192, 0]) ax.set_yticks([0, 96]) ax.set_yticklabels([0, 192]) ax.set_zticks([0, 18, 35]) ax.set_zticklabels([0, 4, 13]) #hts at ztick levels ax.set_xlim(0, 95) # a = 6 (times two for 2nd ellipsoid) ax.set_ylim(0, 95) # b = 10 ax.set_zlim(0, 35) # c = 16 ax.grid(False) ax.w_xaxis.set_pane_color((1.0, 1.0, 1.0, 1.0)) ax.w_yaxis.set_pane_color((1.0, 1.0, 1.0, 1.0)) # ax.w_zaxis.set_pane_color((1.0, 0.0, 1.0, 1.0)) # ax.set_axis_off() ax.view_init(elev=0., azim=-35) ax.view_init(elev=5., azim=-35) ax.w_xaxis.line.set_lw(0.) ax.w_yaxis.line.set_lw(0.) ax.w_zaxis.line.set_lw(0.) #turning off tick marks only ax.xaxis._axinfo['tick']['inward_factor'] = 0 ax.xaxis._axinfo['tick']['outward_factor'] = 0. ax.yaxis._axinfo['tick']['inward_factor'] = 0 ax.yaxis._axinfo['tick']['outward_factor'] = 0. ax.zaxis._axinfo['tick']['inward_factor'] = 0 ax.zaxis._axinfo['tick']['outward_factor'] = 0. ax.text2D(0.53, 0.85, 't=' + str(time + 1), transform=ax.transAxes, size=12) # plt.title('t='+str(time+1)) plt.tight_layout() # plt.show() fname = outDir + 'img' + str(time).zfill(4) + '.png' plt.savefig(fname) plt.close("all") im = Image.open(fname) croppedIm = im.crop((90, 60, 885, 550)) #left top right bottom croppedIm.save(fname) return
def regionprops_3D(im): r""" Calculates various metrics for each labeled region in a 3D image. The ``regionsprops`` method in **skimage** is very thorough for 2D images, but is a bit limited when it comes to 3D images, so this function aims to fill this gap. Parameters ---------- im : array_like An imaging containing at least one labeled region. If a boolean image is received than the ``True`` voxels are treated as a single region labeled ``1``. Regions labeled 0 are ignored in all cases. Returns ------- props : list An augmented version of the list returned by skimage's ``regionprops``. Information, such as ``volume``, can be found for region A using the following syntax: ``result[A-1].volume``. The returned list contains all the metrics normally returned by **skimage.measure.regionprops** plus the following: 'slice': Slice indices into the image that can be used to extract the region 'volume': Volume of the region in number of voxels. 'bbox_volume': Volume of the bounding box that contains the region. 'border': The edges of the region, found as the locations where the distance transform is 1. 'inscribed_sphere': An image containing the largest sphere can can fit entirely inside the region. 'surface_mesh_vertices': Obtained by applying the marching cubes algorithm on the region, AFTER first blurring the voxel image. This allows marching cubes more freedom to fit the surface contours. See also ``surface_mesh_simplices`` 'surface_mesh_simplices': This accompanies ``surface_mesh_vertices`` and together they can be used to define the region as a mesh. 'surface_area': Calculated using the mesh obtained as described above, using the ``porespy.metrics.mesh_surface_area`` method. 'sphericity': Defined as the ratio of the area of a sphere with the same volume as the region to the actual surface area of the region. 'skeleton': The medial axis of the region obtained using the ``skeletonize_3D`` method from **skimage**. 'convex_volume': Same as convex_area, but translated to a more meaningful name. See Also -------- snow_partitioning Notes ----- This function may seem slow compared to the skimage version, but that is because they defer calculation of certain properties until they are accessed, while this one evalulates everything (inlcuding the deferred properties from skimage's ``regionprops``) Regions can be identified using a watershed algorithm, which can be a bit tricky to obtain desired results. *PoreSpy* includes the SNOW algorithm, which may be helpful. """ print('_' * 60) print('Calculating regionprops') results = regionprops(im, coordinates='xy') for i in tqdm(range(len(results))): mask = results[i].image mask_padded = sp.pad(mask, pad_width=1, mode='constant') temp = spim.distance_transform_edt(mask_padded) dt = extract_subsection(temp, shape=mask.shape) # --------------------------------------------------------------------- # Slice indices results[i].slice = results[i]._slice # --------------------------------------------------------------------- # Volume of regions in voxels results[i].volume = results[i].area # --------------------------------------------------------------------- # Volume of bounding box, in voxels results[i].bbox_volume = sp.prod(mask.shape) # --------------------------------------------------------------------- # Create an image of the border results[i].border = dt == 1 # --------------------------------------------------------------------- # Create an image of the maximal inscribed sphere r = dt.max() inv_dt = spim.distance_transform_edt(dt < r) results[i].inscribed_sphere = inv_dt < r # --------------------------------------------------------------------- # Find surface area using marching cubes and analyze the mesh tmp = sp.pad(sp.atleast_3d(mask), pad_width=1, mode='constant') tmp = spim.convolve(tmp, weights=ball(1)) / 5 verts, faces, norms, vals = marching_cubes_lewiner(volume=tmp, level=0) results[i].surface_mesh_vertices = verts results[i].surface_mesh_simplices = faces area = mesh_surface_area(verts, faces) results[i].surface_area = area # --------------------------------------------------------------------- # Find sphericity vol = results[i].volume r = (3 / 4 / sp.pi * vol)**(1 / 3) a_equiv = 4 * sp.pi * (r)**2 a_region = results[i].surface_area results[i].sphericity = a_equiv / a_region # --------------------------------------------------------------------- # Find skeleton of region results[i].skeleton = skeletonize_3d(mask) # --------------------------------------------------------------------- # Volume of convex image, equal to area in 2D, so just translating results[i].convex_volume = results[i].convex_area return results
def main(): # <========= PARSE ARGUMENTS import argparse parser = argparse.ArgumentParser( description='Fit SMPL body to voxels predictions.') parser.add_argument('--testno', type=int, default=83, help='ID of the tested image (0-based).') parser.add_argument('--thr', type=float, default=0.5, help='Value to threshold the voxels.') parser.add_argument('--setname', default='lsp', help='Test set name. Options: lsp | bmvc') args = parser.parse_args() testno = args.testno thr = args.thr setname = args.setname print('Options:') print('\tinput testno: %d' % testno) print('\tinput thr: %.1f' % thr) print('\tsetname: %s' % setname) # =========> # <========= LOAD SMPL MODEL m = load_model( join(SMPL_PATH, 'models/basicModel_neutral_lbs_10_207_0_v1.0.0.pkl')) # Init upright t-pose initial_model = m.copy() initial_model.pose[0:3] = np.array((np.pi, 0, 0)) # =========> # <========= DEFINITIONS voxelSize = 128.0 datapath = 'sample_data/up/' obj_path = '%s/obj%0.1f/' % (datapath, thr) fig_path = '%s/fig%0.1f/' % (datapath, thr) mat_path = '%s/mat%0.1f/' % (datapath, thr) mkdir_safe(obj_path) mkdir_safe(fig_path) mkdir_safe(mat_path) with open('testnames/namesUP' + setname + '.txt', 'r') as f: names = f.readlines() # =========> # <========= LOAD files fileinfo = join(datapath, os.path.basename(names[testno][0:-12] + '_info.mat')) filergb = join(datapath, os.path.basename(names[testno][0:-12] + '_image.png')) rgb = misc.imread(filergb) fileoutput = join(datapath, 'outputs_%d.mat' % (testno + 1)) filesegm = join(datapath, 'segm_%03d.mat' % (testno + 1)) info = sio.loadmat(fileinfo) dict_segm = sio.loadmat(filesegm) dict_pred = sio.loadmat(fileoutput) # =========> # Set ground truth pose and shape m.pose[:] = info['pose'] m.betas[:] = info['shape'] # <========= NETWORK output voxels_gt = dict_pred['gt'] # gt voxels in 128-128-128 voxels_pred = dict_pred['pred'] # gt voxels in 128-128-128 voxels_gt = np.transpose(voxels_gt, (2, 1, 0)) # zyx -> xyz voxels_pred = np.transpose(voxels_pred, (2, 1, 0)) voxels_gt = voxels_gt[:, :, ::-1] voxels_pred = voxels_pred[:, :, ::-1] # =========> # <========= BINVOX params from ground truth model m binvox_trans = np.min(m.r, axis=0) binvox_dim = np.max(m.r, axis=0) - binvox_trans binvox_scale = max(binvox_dim) # =========> # <========= SCALING params from segmentation segm = dict_segm['segm'].transpose() # Bbox in the segm image segmix = np.array(np.transpose(segm.nonzero())) segMaxX, segMaxY = np.max(segmix, axis=0) segMinX, segMinY = np.min(segmix, axis=0) # Scale is the longest segmentation bbox dimension segmScale = max((segMaxX - segMinX + 1) / voxelSize, (segMaxY - segMinY + 1) / voxelSize) voxels_gt_tight_shape = np.round(voxelSize * binvox_dim / binvox_scale) segmScale = np.round( segmScale * voxels_gt_tight_shape) / voxels_gt_tight_shape # =========> # <========= PADDING params from padded ground truth voxels voxels_gt_points = np.array(np.transpose((voxels_gt > 0.5).nonzero())) padding = np.min(voxels_gt_points, axis=0).astype( int) # min x,y,z coordinates that have a filled voxel # ==========> # <========= RUN MARCHING CUBES ON THE PREDICTED VOXELS marching_vertices, triangles, normals, values = measure.marching_cubes_lewiner( voxels_pred, thr) # =========> # <========= Transform vertices to be at SMPL coordinates, remove padding and scale marching_vertices = ((marching_vertices - padding) / segmScale) / voxelSize * binvox_scale + binvox_trans # =========> filejoints3D = join(datapath, 'joints3D_%d.mat' % (testno + 1)) dict_joints3D = sio.loadmat(filejoints3D) joints3D = dict_joints3D['pred'] jix = np.array((5, 4, 3, 2, 1, 0, 6, 7, 8, 9, 15, 14, 13, 12, 11, 10)) joints3D = joints3D[jix] if joints3D.shape[0] == 16 and all(v == 0 for v in joints3D[6, :]): joints3D = joints3D + m.J_transformed[0, :].r # <========= FIT SMPL BODY MODEL print('\n\n=> (1) Find theta that fits joints3D with beta=0\n\n') smpl_utils.optimize_on_joints3D(model=initial_model, joints3D=joints3D, viz=False) print('\n\n=> (2) Find trans that fits voxels and joints3D (optional)\n\n') trans_model = initial_model.copy() smpl_utils.iterative_optimize_on_vertices( model=trans_model, vertices=marching_vertices, joints3D=trans_model.J_transformed.r, vertices_prob=values, opt_cross=False, opt_trans=True, itr=15, viz=False) print('\n\n=> (3) Find theta and beta that fit voxels and joints3D\n\n') final_model = trans_model.copy() smpl_utils.iterative_optimize_on_vertices( model=final_model, vertices=marching_vertices, joints3D=final_model.J_transformed.r, vertices_prob=values, opt_cross=True, itr=3, viz=False) # =========> # <========= COMPUTE SURFACE ERROR surface_err = np.sqrt( (np.power(final_model.r - m.r, 2).sum(axis=1))).mean() * 1000 print('Surface error: %.2f' % surface_err) # =========> # <========= VISUALIZE plt.figure(figsize=(35, 10)) plt.subplot(1, 4, 1) plt.imshow(np.fliplr(rgb)) plt.title('RGB input') plt.subplot(1, 4, 2) smpl_utils.renderBody(m) plt.title('Ground truth') plt.subplot(1, 4, 3) smpl_utils.renderBody(initial_model) plt.title('Initial fit') plt.subplot(1, 4, 4) smpl_utils.renderBody(final_model) plt.title('Final fit') plt.savefig('%s/image_%03d.jpg' % (fig_path, (testno + 1))) # =========> # <========= WRITE MESH OBJ FILES smpl_utils.save_smpl_obj(join(obj_path, '%s_gt.obj' % (testno + 1)), m) smpl_utils.save_smpl_obj(join(obj_path, '%s_initial.obj' % (testno + 1)), initial_model) smpl_utils.save_smpl_obj(join(obj_path, '%s_final.obj' % (testno + 1)), final_model) smpl_utils.save_smpl_obj(join(obj_path, '%s_mcubes.obj' % (testno + 1)), meshobj(marching_vertices, triangles)) # =========> # <========= WRITE SMPL PARAMS TO MAT FILES dict_params = {} dict_params['gt'] = {} dict_params['gt']['pose'] = m.pose.r dict_params['gt']['betas'] = m.betas.r dict_params['gt']['vertices'] = m.r dict_params['gt']['joints3D'] = m.J_transformed.r dict_params['initial'] = {} dict_params['initial']['pose'] = initial_model.pose.r dict_params['initial']['betas'] = initial_model.betas.r dict_params['initial']['vertices'] = initial_model.r dict_params['initial']['joints3D'] = initial_model.J_transformed.r dict_params['final'] = {} dict_params['final']['pose'] = final_model.pose.r dict_params['final']['betas'] = final_model.betas.r dict_params['final']['vertices'] = final_model.r dict_params['final']['joints3D'] = final_model.J_transformed.r dict_params['mcubes'] = {} dict_params['mcubes']['vertices'] = marching_vertices dict_params['mcubes']['triangles'] = triangles dict_params['mcubes']['values'] = values sio.savemat(join(mat_path, 'smpl_params_%s.mat' % (testno + 1)), dict_params)
def fermi3D(procar, outcar, bands, scale=1, mode='plain', st=False, **kwargs): """ This function plots 3d fermi surface list of acceptable kwargs : plotting_package nprocess face_colors arrow_colors arrow_spin atom orbital spin """ # Initilizing the arguments : print('This is a revised version by Shutong.') if 'plotting_package' in kwargs: plotting_package = kwargs['plotting_package'] else: plotting_package = 'mayavi' if 'nprocess' in kwargs: nprocess = kwargs['nprocess'] else: nprocess = 2 if 'face_colors' in kwargs: face_colors = kwargs['face_colors'] else: face_colors = None if 'cmap' in kwargs: cmap = kwargs['cmap'] else: cmap = 'jet' if 'atoms' in kwargs: atoms = kwargs['atoms'] else: atoms = [-1] # project all atoms if 'orbitals' in kwargs: orbitals = kwargs['orbitals'] else: orbitals = [-1] if 'spin' in kwargs: spin = kwargs['spin'] else: spin = [0] if "mask_points" in kwargs: mask_points = kwargs['mask_points'] else: mask_points = 1 if "energy" in kwargs: energy = kwargs['energy'] else: energy = 0 if "transparent" in kwargs: transparent = kwargs['transparent'] else: transparent = False if "arrow_projection" in kwargs: arrow_projection = kwargs['arrow_projection'] else: arrow_projection = 2 if plotting_package == 'mayavi': try: from mayavi import mlab from tvtk.api import tvtk except: print( "You have selected mayavi as plottin package. please install mayavi or choose a different package" ) return elif plotting_package == 'plotly': try: import plotly.plotly as py import plotly.figure_factory as ff import plotly.graph_objs as go cmap = mpl.cm.get_cmap(cmap) figs = [] except: print( "You have selected plotly as plottin package. please install plotly or choose a different package" ) return elif plotting_package == 'matplotlib': try: import matplotlib.pylab as plt from mpl_toolkits.mplot3d.art3d import Poly3DCollection except: print( "You have selected matplotlib as plotting package. please install matplotlib or choose a different package" ) return elif plotting_package == 'ipyvolume': try: import ipyvolume.pylab as ipv except: print( "You have selected ipyvolume as plotting package. please install ipyvolume or choose a different package" ) return permissive = False #get fermi from outcar outcarparser = UtilsProcar() vasp = outputs.BSVasprun('vasprun.xml') #e_fermi = outcarparser.FermiOutcar(outcar) e_fermi = vasp.efermi print('Fermi=', e_fermi) e_fermi += energy #get reciprocal lattice from outcar #recLat = outcarparser.RecLatOutcar(outcar) #get reciprocal lattice from vasprun.xml recLat = vasp.lattice_rec.matrix #parsing the Procar file procarFile = ProcarParser() procarFile.readFile(procar, permissive) cell = mg.io.vasp.inputs.Poscar.from_file("POSCAR", False, False) #poly = get_wigner_seitz(recLat) poly = cell.structure.lattice.get_brillouin_zone() br_points = [] for iface in poly: for ipoint in iface: br_points.append(ipoint) br_points = np.unique(br_points, axis=0) print('Number of bands: %d' % procarFile.bandsCount) print('Number of koints %d' % procarFile.kpointsCount) print('Number of ions: %d' % procarFile.ionsCount) print('Number of orbitals: %d' % procarFile.orbitalCount) print('Number of spins: %d' % procarFile.ispin) #selecting the data data = ProcarSelect(procarFile, deepCopy=True) if bands == -1: bands = range(data.bands.shape[1]) kvector = data.kpoints kmax = np.max(kvector) kmin = np.min(kvector) if abs(kmax) != abs(kmin): print("The mesh provided is gamma center, symmetrizing data") print("For a better fermi surface, use a non-gamma centered k-mesh") #data = symmetrize(data) kvector = data.kpoints kvector_red = data.kpoints.copy() kvector_cart = np.dot(kvector_red, recLat) # This section finds points that are outside of the 1st BZ and and creates those points in the 1st BZ kvector_cart, kvector_red, has_points_out = bring_pnts_to_BZ( recLat, kvector_cart, kvector_red, br_points) # has_points_out = False # getting the mesh grid in each dirrection kx_red = np.unique(kvector_red[:, 0]) ky_red = np.unique(kvector_red[:, 1]) kz_red = np.unique(kvector_red[:, 2]) # getting the lengths between kpoints in each direction klength_x = np.abs(kx_red[-1] - kx_red[-2]) klength_y = np.abs(ky_red[-1] - ky_red[-2]) klength_z = np.abs(kz_red[-1] - kz_red[-2]) klengths = [klength_x, klength_y, klength_z] # getting number of kpoints in each direction with the addition of kpoints needed to sample the 1st BZ fully (in reduced) nkx_red = kx_red.shape[0] nky_red = ky_red.shape[0] nkz_red = kz_red.shape[0] # getting numner of kpoints in each direction provided by vasp nkx_orig = np.unique(kvector[:, 0]).shape[0] nky_orig = np.unique(kvector[:, 1]).shape[0] nkz_orig = np.unique(kvector[:, 2]).shape[0] # Amount of kpoints needed to add on to fully sample 1st BZ padding_x = (nkx_red - nkx_orig) // 2 padding_y = (nky_red - nky_orig) // 2 padding_z = (nkz_red - nkz_orig) // 2 if mode == 'parametric': data.selectIspin(spin) data.selectAtoms(atoms, fortran=False) data.selectOrbital(orbitals) elif mode == 'external': if "color_file" in kwargs: rf = open(kwargs['color_file']) lines = rf.readlines() counter = 0 color_kvector = [] color_eigen = [] for iline in lines: if counter < 2: if 'band' in iline: counter += 1 continue temp = [float(x) for x in iline.split()] color_kvector.append([temp[0], temp[1], temp[2]]) counter = -1 for iline in lines: if 'band' in iline: counter += 1 iband = int(iline.split()[-1]) color_eigen.append([]) continue color_eigen[counter].append(float(iline.split()[-1])) rf.close() color_kvector = np.array(color_kvector) color_kvector_red = color_kvector.copy() color_kvector_cart = np.dot(color_kvector, recLat) if has_points_out: color_kvector_cart, color_kvector_red, temp = bring_pnts_to_BZ( recLat, color_kvector_cart, color_kvector_red, br_points) else: print( "mode selected was external, but no color_file name was provided" ) return if st: dataX = ProcarSelect(procarFile, deepCopy=True) dataY = ProcarSelect(procarFile, deepCopy=True) dataZ = ProcarSelect(procarFile, deepCopy=True) dataX.kpoints = data.kpoints dataY.kpoints = data.kpoints dataZ.kpoints = data.kpoints dataX.spd = data.spd dataY.spd = data.spd dataZ.spd = data.spd dataX.bands = data.bands dataY.bands = data.bands dataZ.bands = data.bands dataX.selectIspin([1]) dataY.selectIspin([2]) dataZ.selectIspin([3]) dataX.selectAtoms(atoms, fortran=False) dataY.selectAtoms(atoms, fortran=False) dataZ.selectAtoms(atoms, fortran=False) dataX.selectOrbital(orbitals) dataY.selectOrbital(orbitals) dataZ.selectOrbital(orbitals) ic = 0 for iband in bands: print("Plotting band %d" % iband) eigen = data.bands[:, iband] # mapping the eigen values on the mesh grid to a matrix mapped_func, kpoint_matrix = mapping_func(kvector, eigen) # adding the points from the 2nd BZ to 1st BZ to fully sample the BZ. Check np.pad("wrap") for more information mapped_func = np.pad(mapped_func[:-1, :-1, :-1], ((padding_x, padding_x), (padding_y, padding_y), (padding_z, padding_z)), 'wrap') # Fourier interpolate the mapped function E(x,y,z) surf_equation = fft_interpolate(mapped_func, scale) # after the FFT we loose the center of the BZ, using numpy roll we bring back the center of the BZ surf_equation = np.roll(surf_equation, (scale) // 2, axis=[0, 1, 2]) try: # creating the isosurface if possible verts, faces, normals, values = measure.marching_cubes_lewiner( surf_equation, e_fermi) except: print("No isosurface for this band") continue # the vertices provided are scaled and shifted to start from zero # we center them to zero, and rescale them to fit the real BZ by multiplying by the klength in each direction for ix in range(3): verts[:, ix] -= verts[:, ix].min() verts[:, ix] -= (verts[:, ix].max() - verts[:, ix].min()) / 2 verts[:, ix] *= klengths[ix] / scale #dx=[padding_x+nkx_orig//2, padding_y+nky_orig//2,padding_z+nkz_orig//2] #verts[:] -=dx #for ix in range(3): # verts[:,ix] *= klengths[ix] # the vertices need to be transformed to reciprocal spcae from recuded space, to find the points that are # in 2nd BZ, to be removed verts = np.dot(verts, recLat) # identifying the points in 2nd BZ and removing them if has_points_out: args = [] for ivert in range(len(verts)): args.append([br_points, verts[ivert]]) p = Pool(nprocess) results = np.array(p.map(is_outside, args)) p.close() out_verts = np.arange(0, len(results))[results] new_faces = [] # outs_bool_mat = np.zeros(shape=faces.shape,dtype=np.bool) for iface in faces: remove = False for ivert in iface: if ivert in out_verts: remove = True continue if not remove: new_faces.append(iface) faces = np.array(new_faces) print("done removing") # At this point we have the plain Fermi surface, we can color the surface depending on the projection # We create the center of faces by averaging coordinates of corners if mode == 'parametric': character = data.spd[:, iband] centers = np.zeros(shape=(len(faces), 3)) for iface in range(len(faces)): centers[iface, 0:3] = np.average(verts[faces[iface]], axis=0) colors = interpolate.griddata(kvector_cart, character, centers, method="nearest") elif mode == 'external': character = np.array(color_eigen[ic]) ic += 1 centers = np.zeros(shape=(len(faces), 3)) for iface in range(len(faces)): centers[iface, 0:3] = np.average(verts[faces[iface]], axis=0) colors = interpolate.griddata(color_kvector_cart, character, centers, method="nearest") if st: projection_x = dataX.spd[:, iband] projection_y = dataY.spd[:, iband] projection_z = dataZ.spd[:, iband] verts_spin, faces_spin, normals, values = measure.marching_cubes_lewiner( mapped_func, e_fermi) for ix in range(3): verts_spin[:, ix] -= verts_spin[:, ix].min() verts_spin[:, ix] -= (verts_spin[:, ix].max() - verts_spin[:, ix].min()) / 2 verts_spin[:, ix] *= klengths[ix] verts_spin = np.dot(verts_spin, recLat) if has_points_out: args = [] for ivert in range(len(verts_spin)): args.append([br_points, verts_spin[ivert]]) p = Pool(nprocess) results = np.array(p.map(is_outside, args)) p.close() out_verts = np.arange(0, len(results))[results] new_faces = [] for iface in faces_spin: remove = False for ivert in iface: if ivert in out_verts: remove = True continue if not remove: new_faces.append(iface) faces_spin = np.array(new_faces) centers = np.zeros(shape=(len(faces_spin), 3)) for iface in range(len(faces_spin)): centers[iface, 0:3] = np.average(verts_spin[faces_spin[iface]], axis=0) colors1 = interpolate.griddata(kvector_cart, projection_x, centers, method="linear") colors2 = interpolate.griddata(kvector_cart, projection_y, centers, method="linear") colors3 = interpolate.griddata(kvector_cart, projection_z, centers, method="linear") spin_arrows = np.vstack((colors1, colors2, colors3)).T if plotting_package == 'mayavi': polydata = tvtk.PolyData(points=verts, polys=faces) if face_colors != None: mlab.pipeline.surface(polydata, representation='surface', color=face_colors[ic], opacity=1, name='band-' + str(iband)) ic += 1 else: if mode == 'plain': if not (transparent): s = mlab.pipeline.surface(polydata, representation='surface', color=(0, 0.5, 1), opacity=1, name='band-' + str(iband)) elif mode == 'parametric' or mode == 'external': polydata.cell_data.scalars = colors polydata.cell_data.scalars.name = 'celldata' mlab.pipeline.surface(polydata, vmin=0, vmax=colors.max(), colormap=cmap) cb = mlab.colorbar(orientation='vertical') if st: x, y, z = list(zip(*centers)) u, v, w = list(zip(*spin_arrows)) pnts = mlab.quiver3d(x, y, z, u, v, w, line_width=5, mode='arrow', resolution=25, reset_zoom=False, name='spin-' + str(iband), mask_points=mask_points, scalars=spin_arrows[:, arrow_projection], vmin=-1, vmax=1, colormap=cmap) pnts.glyph.color_mode = 'color_by_scalar' pnts.glyph.glyph_source.glyph_source.shaft_radius = 0.05 pnts.glyph.glyph_source.glyph_source.tip_radius = 0.1 elif plotting_package == 'plotly': if mode == 'plain': if not (transparent): x, y, z = zip(*verts) fig = ff.create_trisurf(x=x, y=y, z=z, plot_edges=False, simplices=faces, title="band-%d" % ic) figs.append(fig['data'][0]) elif mode == 'parametric' or mode == 'external': face_colors = cmap(colors) colormap = [ 'rgb(%i,%i,%i)' % (x[0], x[1], x[2]) for x in (face_colors * 255).round() ] x, y, z = zip(*verts) fig = ff.create_trisurf(x=x, y=y, z=z, plot_edges=False, colormap=colormap, simplices=faces, show_colorbar=True, title="band-%d" % ic) figs.append(fig['data'][0]) elif plotting_package == 'matplotlib': if mode == 'plain': x, y, z = zip(*verts) ax.plot_trisurf(x, y, faces, z, linewidth=0.2, antialiased=True) elif mode == 'parametric' or mode == 'external': print( 'coloring the faces is not implemented in matplot lib, please use another plotting package.we recomend mayavi.' ) elif plotting_package == 'ipyvolume': if mode == 'plain': ipv.figure() ipv.plot_trisurf(verts[:, 0], verts[:, 1], verts[:, 2], triangles=faces) elif mode == 'paramteric' or mode == 'external': face_colors = cmap(colors) colormap = [ 'rgb(%i,%i,%i)' % (x[0], x[1], x[2]) for x in (face_colors * 255).round() ] ipv.figure() ipv.plot_trisurf(verts[:, 0], verts[:, 1], verts[:, 2], triangles=faces, color=cmap) cell = mg.io.vasp.inputs.Poscar.from_file("POSCAR", False, False) #poly = get_wigner_seitz(recLat) poly = cell.structure.lattice.get_brillouin_zone() # plot brilliouin zone if plotting_package == 'mayavi': brillouin_point = [] brillouin_faces = [] point_count = 0 for iface in poly: single_face = [] for ipoint in iface: single_face.append(point_count) brillouin_point.append(list(ipoint)) point_count += 1 brillouin_faces.append(single_face) polydata_br = tvtk.PolyData(points=brillouin_point, polys=brillouin_faces) mlab.pipeline.surface(polydata_br, representation='wireframe', color=(0, 0, 0), line_width=4, name="BRZ") elif plotting_package == 'plotly': for iface in poly: iface = np.pad(iface, ((0, 1), (0, 0)), 'wrap') x, y, z = iface[:, 0], iface[:, 1], iface[:, 2] plane = go.Scatter3d(x=x, y=y, z=z, mode='lines', line=dict(color='black', width=4)) figs.append(plane) elif plotting_package == 'matplotlib': fig = plt.figure() ax = fig.add_subplot(111, projection='3d') brillouin_zone = Poly3DCollection(poly, facecolors=["None"] * len(poly), alpha=1, linewidth=4) brillouin_zone.set_edgecolor("k") ax.add_collection3d(brillouin_zone, zs=0, zdir='z') if plotting_package == 'mayavi': mlab.colorbar(orientation='vertical') #,label_fmt='%.1f') mlab.show() elif plotting_package == 'plotly': layout = go.Layout(showlegend=False) fig = go.Figure(data=figs, layout=layout) py.iplot(fig) elif plotting_package == 'matplotlib': plt.show() elif plotting_package == 'ipyvolume': ipv.show() return
def display_valence(self, valeur=20000, multiple=6): ''' La fonction display_valence permet d'afficher les orbitales de Valence de l'Atome. Paramètres : - valeur : valeur de la Fonction d'Onde selon laquelle sera tracée les isosurfaces ( valeur par défaut de 20000 ) - multiple : demi épaisseur de la largeur en rayons de Bohr ( 1 a0 = 0.529*10**(-10) m ) du repère cartésien cubiques dans lequel sera calculer la Focntion d'Onde ( valeur par défaut de 6 ) ''' n_rows = len( self.valence ) # On choisit autant de lignes qu'il y a d'orbitales à afficher n_columns = 0 for i in range( n_rows ): # Determination du nombre de colonnes ( en mesurant le maximum de nombres de sous orbitales parmis les orbitales de Valence ) oa = self.valence[i] print(oa) m_liste = oa.magnetic_number if n_columns < len(m_liste): n_columns = len(m_liste) types = [] for i in range( n_rows ): # Création de la matrice de configuration des subplots line = [] for j in range(n_columns): line.append( {'is_3d': True} ) # On doit definir tout les subplots dans un tableau avec la mention {'is_3d': True} types.append( line ) # si jamais le graphique sera un plot 3D ( c'est notre cas ) names = [] for i in range( n_rows ): # Création de la liste de configuration des noms des subplots ( afin de pouvoir afficher le nom des orbitales affichées ) row = [] oa = self.valence[i] m_liste = oa.magnetic_number for j in range(n_columns): if j < len(m_liste): row.append( orbitale_name(oa.prim_num, oa.sec_num, m_liste[j]) ) # On rajoute dans une liste 'row' les noms des sous orbitales une par une, else: # choisi par la fonction orbitale_name par leur n, l et m row.append( '' ) # Si il n'y a pas d'orbitale affichée sur ce subplot, on décide de ne pas afficher de nom, mais il faut tout de même remplir la liste, d'où le '' names = names + row # on ajoute les listes ' row' une par une dans une liste générale de noms d'orbitales qui servira peut après names = tuple( names) # On transforme la liste des noms d'orbitales en tuple print(names) fig = tools.make_subplots( rows=n_rows, cols=n_columns, specs=types ) # Création des subplots avec la configuration créée plus haut annotations = tools.make_subplots( rows=n_rows, cols=n_columns, subplot_titles=names)['layout'][ 'annotations'] # Création des noms des subplots fig['layout'].update( annotations=annotations ) # On met à jour les noms des subplots avec la configuration 'annotations' for i in range( n_rows): # On parcourt les lignes du tableau de subplots oa = self.valence[i] Zeff = self.Slater_Zeff[i] borne = multiple * a0( ) # Creation d'une variable 'borne' qui nous servira à donner les limites du mgrid que l'on créer à la ligne suivante X, Y, Z = np.mgrid[ -borne:borne:100j, -borne:borne:100j, -borne:borne: 100j] # Création d'un mgrid pour X,Y,Z ( un tableau de coordonnées de 220 points dans chaque direction m_liste = oa.magnetic_number #Extraction de la liste des m de l'orbitale étudiée for j in range( len(m_liste) ): # On parcourt les colonnes du tableau pour la ligne n°i Fonction = Fonction_Onde( X, Y, Z, oa.prim_num, oa.sec_num, m_liste[j], Zeff ) # Calcule de la Focntion d'Onde dans l'espace ( le mgrid ) verts, faces = measure.marching_cubes_lewiner( abs(Fonction), valeur, spacing=(X[1, 0, 0] - X[0, 0, 0], Y[0, 1, 0] - Y[0, 0, 0], Z[0, 0, 1] - Z[0, 0, 0]))[:2] #Création des faces de # l'isosurface ainsi que des lignes entre ces faces, (l'isosurface est defini pour la valeur absolue de la fonction d'onde lorsque celle ci est égale à la variable # donnée ' valeur ' verts = verts - borne # on recentre les surfaces sur l'Origine du repère cartésien de départ ( mgrid ) data = plotly_triangular_mesh_oa(verts, faces, n=oa.prim_num, l=oa.sec_num, index=m_liste[j], Zeff=Zeff, intensities=Fonction_Onde, colorscale=color(), showscale=False) # On invoque alors la fonction 'plotly_triangular_mesh' qui nous crée un dictionnaire contenant toutes les informations sur la figure fig.add_traces( data, rows=[i + 1] * len(data), cols=[j + 1] * len(data) ) # On place la figure 'data' à sa place dans le tableau de subplots for i in range( len(fig['data']) ): # Modification de l'opacité de toutes les surfaces ( 0: transparent , 1: opaque ) fig['data'][i].update(opacity=0.7) plot(fig) # On affiche les subplots
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d.art3d import Poly3DCollection from skimage import measure from skimage.draw import ellipsoid # Generate a level set about zero of two identical ellipsoids in 3D ellip_base = ellipsoid(6, 10, 16, levelset=True) ellip_double = np.concatenate((ellip_base[:-1, ...], ellip_base[2:, ...]), axis=0) # Use marching cubes to obtain the surface mesh of these ellipsoids verts, faces, normals, values = measure.marching_cubes_lewiner(ellip_double, 0) # Display resulting triangular mesh using Matplotlib. This can also be done # with mayavi (see skimage.measure.marching_cubes_lewiner docstring). fig = plt.figure(figsize=(10, 10)) ax = fig.add_subplot(111, projection='3d') # Fancy indexing: `verts[faces]` to generate a collection of triangles mesh = Poly3DCollection(verts[faces]) mesh.set_edgecolor('k') ax.add_collection3d(mesh) ax.set_xlabel("x-axis: a = 6 per ellipsoid") ax.set_ylabel("y-axis: b = 10") ax.set_zlabel("z-axis: c = 16")