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)
Exemple #7
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)


Exemple #11
0
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}
Exemple #12
0
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
Exemple #13
0
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))
Exemple #15
0
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)
Exemple #16
0
                                       ((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)
Exemple #17
0
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)
Exemple #18
0
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)))))
Exemple #20
0
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
Exemple #21
0
 def march(volume, _):
     verts, faces, normals, values = marching_cubes_lewiner(volume, 1)
     faces = correct_mesh_orientation(volume, verts, faces)
     return verts, None, faces
Exemple #22
0
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
Exemple #23
0
    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()
Exemple #26
0
    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()
Exemple #27
0
    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')
Exemple #28
0
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()
Exemple #29
0
'''
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")
Exemple #30
0
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
Exemple #31
0
    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)
Exemple #32
0
                    (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
Exemple #33
0
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
Exemple #36
0
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
Exemple #37
0
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
Exemple #38
0
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)
Exemple #39
0
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
Exemple #40
0
    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")