Пример #1
0
def cut_slit(data,slit_number):
	slit_number -= 1

	data[scipy.isnan(data)] = 0.

	slice = data.sum(axis=1)

	# Find bottom of good data
	bottom = 0
	while slice[bottom]==0:
		bottom += 1

	slice = scipy.trim_zeros(slice)

	zeros = scipy.where(slice==0)
	zeros = zeros[0]+bottom

	# Special case for images with only one slit
	if zeros.size==0:
		# Deal with upper zeros
		slice = data.sum(axis=1)
		top = bottom
		while top<slice.size and slice[top]!=0:
			top += 1

		slice = data.sum(axis=0)
		indx = scipy.where(slice!=0)
		start = indx[0][0]
		end = indx[0][-1]+1
		return data[bottom:top,start:end],start,bottom

	borders = scipy.array_split(zeros,zeros.size/5)
	if slit_number>len(borders):
		return scipy.asarray(0),0,0

	if slit_number==0:
		start = 0
		end = borders[0][0]
	elif slit_number==len(borders):
		start = borders[slit_number-1][4]+1
		end = slice.size
	else:
		start = borders[slit_number-1][4]+1
		end = borders[slit_number][0]
	data = data[start:end]
	bottom = start
	slice = data.sum(axis=0)
	indx = scipy.where(slice!=0)
	start = indx[0][0]
	end = indx[0][-1]+1
	return data[:,start:end],start,bottom
Пример #2
0
def _create_alias_map(im, alias=None):
    r"""
    Creates an alias mapping between phases in original image and identifyable
    names. This mapping is used during network extraction to label
    interconnection between and properties of each phase.

    Parameters
    ----------
    im : ND-array
        Image of porous material where each phase is represented by unique
        integer. Phase integer should start from 1. Boolean image will extract
        only one network labeled with True's only.

    alias : dict (Optional)
        A dictionary that assigns unique image label to specific phase.
        For example {1: 'Solid'} will show all structural properties associated
        with label 1 as Solid phase properties.
        If ``None`` then default labelling will be used i.e {1: 'Phase1',..}.

    Returns
    -------
    A dictionary with numerical phase labels as key, and readable phase names
    as valuies. If no alias is provided then default labelling is used
    i.e {1: 'Phase1',..}
    """
    # -------------------------------------------------------------------------
    # Get alias if provided by user
    phases_num = sp.unique(im * 1)
    phases_num = sp.trim_zeros(phases_num)
    al = {}
    for values in phases_num:
        al[values] = 'phase{}'.format(values)
    if alias is not None:
        alias_sort = dict(sorted(alias.items()))
        phase_labels = sp.array([*alias_sort])
        al = alias
        if set(phase_labels) != set(phases_num):
            raise Exception('Alias labels does not match with image labels '
                            'please provide correct image labels')
    return al
Пример #3
0
def solve(d,orders):
    path = os.path.split(__file__)[0]

    lines = {}
    lines['cuar'] = numpy.loadtxt(path+"/data/cuar.lines")
    lines['hgne'] = numpy.loadtxt(path+"/data/hgne.lines")
    lines['xe'] = numpy.loadtxt(path+"/data/xe.lines")

    #startsoln = numpy.load(path+"/data/test_wavesol.dat",
    #                       allow_pickle=True)
    startsoln = numpy.load(path+"/data/esi_wavesolution.dat",
                           allow_pickle=True)

    #arclist = d.keys() # Under python 3 this does not produce a list
    #                        and so arclist[0] below fails
    arclist = list(d)
    arclist.sort()
    soln = []
    if d[arclist[0]].shape[1]>3000:
        xvals = numpy.arange(4096.)
        cuslice = slice(3860,3940)
        fw1 = 75.
        fw2 = 9
    else:
        xvals = numpy.arange(1.,4096.,2.)
        cuslice = slice(1930,1970)
        fw1 = 37.
        fw2 = 5

    """
    Do a temporary kludge.  In some cases, the finding of the orders
    fails, and only 9 orders are found.  In this case, we need to skip
    the first of the orders
    """
    if len(orders) == 9:
        ordstart = 1
        dord = 1
    else:
        ordstart = 0
        dord = 0
    for i in range(ordstart, 10):
      solution = startsoln[i]
      start,end = orders[(i-dord)]

      peaks = {}
      trace = {}
      fitD = {}
      WIDTH = 4
      import pylab
      for arc in arclist:
          data = numpy.nanmedian(d[arc][start:end],axis=0)
          data[scipy.isnan(data)] = 0.
          if i==0 and arc=='cuar':
              data[cuslice] = numpy.median(data)
          trace[arc] = data.copy()
          bak = ndimage.percentile_filter(data,50.,int(fw1))
          bak = getContinuum(bak,40.)
          data -= bak
          fitD[arc] = data/d[arc][start:end].std(0)
          p = ndimage.maximum_filter(data,fw2)
          std = clip(scipy.trim_zeros(data),3.)[1]
          nsig = ndimage.uniform_filter((data>7.*std)*1.,3)
          peak = scipy.where((nsig==1)&(p>10.*std)&(p==data))[0]
          peaks[arc] = []
          for p in peak:
              if p-WIDTH<0 or p+WIDTH+1>xvals.size:
                  continue
              x = xvals[p-WIDTH:p+WIDTH+1].copy()#-xvals[p]
              f = data[p-WIDTH:p+WIDTH+1].copy()
              fitdata = scipy.array([x,f]).T
              fit = scipy.array([0.,f.max(),xvals[p],1.])
              fit,chi = sf.ngaussfit(fitdata,fit,weight=1)
              peaks[arc].append(fit[2])#+xvals[p])
      for converge in range(15):
        wave = 10**sf.genfunc(xvals,0.,solution)

        refit = []
        corrA = {}
        err = wave[int(wave.size/2)]-wave[int(wave.size/2-1)]
        for arc in arclist:
            corr = []
            p = 10.**sf.genfunc(peaks[arc],0.,solution)
            for k in range(p.size):
                cent = p[k]
                diff = cent-lines[arc]
                corr.append(diff[abs(diff).argmin()])
            corr = numpy.array(corr)
            if corr.size<4:
                continue
            m,s = clip(corr)
            corr = numpy.median(corr[abs(corr-m)<5.*s])
            print(corr)
            corrA[arc] = corr
#        corr = m

        #for arc in arclist:
            p = 10.**sf.genfunc(peaks[arc],0.,solution)
            for k in range(p.size):
                pos = peaks[arc][k]
                cent = p[k]
                diff = abs(cent-lines[arc]-corr)
                if diff.min()<2.*err:
                    refit.append([pos,lines[arc][diff.argmin()]])
        refit = scipy.asarray(refit)
        solution = sf.lsqfit(refit,'polynomial',3)
        refit = []
        err = solution['coeff'][1]
        for arc in arclist:
            data = trace[arc]
            for pos in peaks[arc]:
                cent = sf.genfunc(pos,0.,solution)
                delta = 1e9
                match = None
                for j in lines[arc]:
                    diff = abs(cent-j)
                    if diff<delta and diff<1.*err:
                        delta = diff
                        match = j
                if match is not None:
                    refit.append([pos,match])
        refit = scipy.asarray(refit)
        refit[:,1] = numpy.log10(refit[:,1])
        solution = sf.lsqfit(refit,'chebyshev',3)

        #refit[:,0],refit[:,1] = refit[:,1].copy(),refit[:,0].copy()
        #refit = numpy.array([refit[:,1],refit[:,0]]).T
        #refit = refit[:,::-1]
        solution2 = sf.lsqfit(refit[:,::-1],'chebyshev',3)
        #soln.append([solution,solution2])

        w = 10**sf.genfunc(xvals,0.,solution)
        if (w==wave).all() or converge>8:
            print("Order %d converged in %d iterations"%(i,converge))
            soln.append([solution,solution2])
            break
            for arc in arclist:
                pylab.plot(w,trace[arc])
                pylab.plot(w,fitD[arc])
                pp = 10**sf.genfunc(peaks[arc],0.,solution)
                for p in pp:
                    pylab.axvline(p,c='k')
            for j in 10**refit[:,1]:
                if j>w[0] and j<w[-1]:
                    pylab.axvline(j)
            pylab.show()
            break
    return soln
Пример #4
0
def snow_dual(im, voxel_size=1,
              boundary_faces=['top', 'bottom', 'left', 'right', 'front', 'back'],
              marching_cubes_area=False):

    r"""
    Extracts a dual pore and solid network from a binary image using a modified
    version of the SNOW algorithm

    Parameters
    ----------
    im : ND-array
        Binary image in the Boolean form with True’s as void phase and False’s
        as solid phase. It can process the inverted configuration of the
        boolean image as well, but output labelling of phases will be inverted
        and solid phase properties will be assigned to void phase properties
        labels which will cause confusion while performing the simulation.
    voxel_size : scalar
        The resolution of the image, expressed as the length of one side of a
        voxel, so the volume of a voxel would be **voxel_size**-cubed.  The
        default is 1, which is useful when overlaying the PNM on the original
        image since the scale of the image is alway 1 unit lenth per voxel.
    boundary_faces : list of strings
        Boundary faces labels are provided to assign hypothetical boundary
        nodes having zero resistance to transport process. For cubical
        geometry, the user can choose ‘left’, ‘right’, ‘top’, ‘bottom’,
        ‘front’ and ‘back’ face labels to assign boundary nodes. If no label is
        assigned then all six faces will be selected as boundary nodes
        automatically which can be trimmed later on based on user requirements.
    marching_cubes_area : bool
        If ``True`` then the surface area and interfacial area between regions
        will be using the marching cube algorithm. This is a more accurate
        representation of area in extracted network, but is quite slow, so
        it is ``False`` by default.  The default method simply counts voxels
        so does not correctly account for the voxelated nature of the images.

    Returns
    -------
    A dictionary containing all the void and solid phase size data, as well as
    the network topological information.  The dictionary names use the OpenPNM
    convention (i.e. 'pore.coords', 'throat.conns') so it may be converted
    directly to an OpenPNM network object using the ``update`` command.

    References
    ----------
    [1] Gostick, J. "A versatile and efficient network extraction algorithm
    using marker-based watershed segmenation".  Phys. Rev. E 96, 023307 (2017)

    [2] Khan, ZA et al.  "Dual network extraction algorithm to investigate
    multiple transport processes in porous materials: Image-based modeling
    of pore and grain-scale processes. Computers and Chemical Engineering.
    123(6), 64-77 (2019)

    """
    # -------------------------------------------------------------------------
    # SNOW void phase
    pore_regions = snow_partitioning(im, return_all=True)
    # SNOW solid phase
    solid_regions = snow_partitioning(~im, return_all=True)
    # -------------------------------------------------------------------------
    # Combined Distance transform of two phases.
    pore_dt = pore_regions.dt
    solid_dt = solid_regions.dt
    dt = pore_dt + solid_dt
    pore_peaks = pore_regions.peaks
    solid_peaks = solid_regions.peaks
    peaks = pore_peaks + solid_peaks
    # Calculates combined void and solid regions for dual network extraction
    pore_regions = pore_regions.regions
    solid_regions = solid_regions.regions
    pore_region = pore_regions*im
    solid_region = solid_regions*~im
    solid_num = sp.amax(pore_regions)
    solid_region = solid_region + solid_num
    solid_region = solid_region * ~im
    regions = pore_region + solid_region
    b_num = sp.amax(regions)
    # -------------------------------------------------------------------------
    # Boundary Conditions
    regions = add_boundary_regions(regions=regions, faces=boundary_faces)
    # -------------------------------------------------------------------------
    # Padding distance transform to extract geometrical properties
    f = boundary_faces
    if f is not None:
        if im.ndim == 2:
            faces = [(int('left' in f)*3, int('right' in f)*3),
                     (int(('front') in f)*3 or int(('bottom') in f)*3,
                      int(('back') in f)*3 or int(('top') in f)*3)]
        if im.ndim == 3:
            faces = [(int('left' in f)*3, int('right' in f)*3),
                     (int('front' in f)*3, int('back' in f)*3),
                     (int('top' in f)*3, int('bottom' in f)*3)]
        dt = sp.pad(dt, pad_width=faces, mode='edge')
    else:
        dt = dt
    # -------------------------------------------------------------------------
    # Extract void,solid and throat information from image
    net = regions_to_network(im=regions, dt=dt, voxel_size=voxel_size)
    # -------------------------------------------------------------------------
    # -------------------------------------------------------------------------
    # Extract marching cube surface area and interfacial area of regions
    if marching_cubes_area:
        areas = region_surface_areas(regions=regions)
        interface_area = region_interface_areas(regions=regions, areas=areas,
                                                voxel_size=voxel_size)
        net['pore.surface_area'] = areas * voxel_size**2
        net['throat.area'] = interface_area.area
    # -------------------------------------------------------------------------
    # Find void to void, void to solid and solid to solid throat conns
    loc1 = net['throat.conns'][:, 0] < solid_num
    loc2 = net['throat.conns'][:, 1] >= solid_num
    loc3 = net['throat.conns'][:, 1] < b_num
    pore_solid_labels = loc1 * loc2 * loc3

    loc4 = net['throat.conns'][:, 0] >= solid_num
    loc5 = net['throat.conns'][:, 0] < b_num
    solid_solid_labels = loc4 * loc2 * loc5 * loc3

    loc6 = net['throat.conns'][:, 1] < solid_num
    pore_pore_labels = loc1 * loc6

    loc7 = net['throat.conns'][:, 1] >= b_num
    boundary_throat_labels = loc5 * loc7

    solid_labels = ((net['pore.label'] > solid_num) * ~
                    (net['pore.label'] > b_num))
    boundary_labels = net['pore.label'] > b_num
    b_sa = sp.zeros(len(boundary_labels[boundary_labels == 1.0]))
    # -------------------------------------------------------------------------
    # Calculates void interfacial area that connects with solid and vice versa
    p_conns = net['throat.conns'][:, 0][pore_solid_labels]
    ps = net['throat.area'][pore_solid_labels]
    p_sa = sp.bincount(p_conns, ps)
    s_conns = net['throat.conns'][:, 1][pore_solid_labels]
    s_pa = sp.bincount(s_conns, ps)
    s_pa = sp.trim_zeros(s_pa)  # remove pore surface area labels
    p_solid_surf = sp.concatenate((p_sa, s_pa, b_sa))
    # -------------------------------------------------------------------------
    # Calculates interfacial area using marching cube method
    if marching_cubes_area:
        ps_c = net['throat.area'][pore_solid_labels]
        p_sa_c = sp.bincount(p_conns, ps_c)
        s_pa_c = sp.bincount(s_conns, ps_c)
        s_pa_c = sp.trim_zeros(s_pa_c)  # remove pore surface area labels
        p_solid_surf = sp.concatenate((p_sa_c, s_pa_c, b_sa))
    # -------------------------------------------------------------------------
    # Adding additional information of dual network
    net['pore.solid_void_area'] = (p_solid_surf * voxel_size**2)
    net['throat.void'] = pore_pore_labels
    net['throat.interconnect'] = pore_solid_labels
    net['throat.solid'] = solid_solid_labels
    net['throat.boundary'] = boundary_throat_labels
    net['pore.void'] = net['pore.label'] <= solid_num
    net['pore.solid'] = solid_labels
    net['pore.boundary'] = boundary_labels

    class network_dict(dict):
        pass
    net = network_dict(net)
    net.im = im
    net.dt = dt
    net.regions = regions
    net.peaks = peaks
    net.pore_dt = pore_dt
    net.pore_regions = pore_region
    net.pore_peaks = pore_peaks
    net.solid_dt = solid_dt
    net.solid_regions = solid_region
    net.solid_peaks = solid_peaks

    return net
Пример #5
0
def cut_slit(data, slit_number):
    """
    cut_slit(data,slit_number)

    Find slit in 2d mask image.

    Inputs:
      data        - 2d array of mask image
          slit_number - the number of the slit to cut out (with 1 as the bottom)

    Outputs:
      2d array of slit data, left index of slit, bottom index of slit
    """
    data = data.copy()
    slit_number -= 1

    data[scipy.isnan(data)] = 0.

    slice = data.sum(axis=1)

    # Find bottom of good data
    bottom = 0
    while slice[bottom] == 0:
        bottom += 1

    slice = scipy.trim_zeros(slice)

    zeros = scipy.where(slice == 0)
    zeros = zeros[0] + bottom

    # Special case for images with only one slit
    if zeros.size == 0:
        # Deal with upper zeros
        slice = data.sum(axis=1)
        top = bottom
        while top < slice.size and slice[top] != 0:
            top += 1

        slice = data.sum(axis=0)
        indx = scipy.where(slice != 0)
        start = indx[0][0]
        end = indx[0][-1] + 1
        return data[bottom:top, start:end], start, bottom

        borders = []
        start = 0
        for i in range(zeros.size - 1):
            if zeros[i] + 1 != zeros[i + 1]:
                borders.append(zeros[start:i + 1].copy())
                start = i + 1
        borders.append(zeros[start:].copy())
    if slit_number > len(borders):
        return scipy.asarray(0), 0, 0

    if slit_number == 0:
        start = 0
        end = borders[0][0]
    elif slit_number == len(borders):
        start = borders[slit_number - 1][4] + 1
        end = slice.size
    else:
        start = borders[slit_number - 1][4] + 1
        end = borders[slit_number][0]

    data = data[start:end]
    bottom = start
    slice = data.sum(axis=0)
    indx = scipy.where(slice != 0)
    start = indx[0][0]
    end = indx[0][-1] + 1
    return data[:, start:end], start, bottom
Пример #6
0
def add_phase_interconnections(net, snow_partitioning_n, voxel_size=1,
                               marching_cubes_area=False,
                               alias=None):
    r"""
    This function connects networks of two or more phases together by
    interconnecting neighbouring nodes inside different phases.

    The resulting network can be used for the study of transport and kinetics
    at interphase of two phases.

    Parameters
    ----------
    network : 2D or 3D network
        A dictoionary containing structural information of two or more
        phases networks. The dictonary format must be same as porespy
        region_to_network function.

    snow_partitioning_n : tuple
        The output generated by snow_partitioning_n function. The tuple should
        have phases_max_labels and original image of material.

    voxel_size : scalar
        The resolution of the image, expressed as the length of one side of a
        voxel, so the volume of a voxel would be **voxel_size**-cubed.  The
        default is 1, which is useful when overlaying the PNM on the original
        image since the scale of the image is alway 1 unit lenth per voxel.

    marching_cubes_area : bool
        If ``True`` then the surface area and interfacial area between regions
        will be causing the marching cube algorithm. This is a more accurate
        representation of area in extracted network, but is quite slow, so
        it is ``False`` by default.  The default method simply counts voxels
        so does not correctly account for the voxelated nature of the images.

    alias : dict (Optional)
        A dictionary that assigns unique image label to specific phase.
        For example {1: 'Solid'} will show all structural properties associated
        with label 1 as Solid phase properties.
        If ``None`` then default labelling will be used i.e {1: 'Phase1',..}.

    Returns
    -------
    A dictionary containing network information of individual and connected
    networks. The dictionary names use the OpenPNM convention so it may be
    converted directly to an OpenPNM network object using the ``update``
    command.

    """
    # -------------------------------------------------------------------------
    # Get alias if provided by user
    im = snow_partitioning_n.im
    al = _create_alias_map(im, alias=alias)
    # -------------------------------------------------------------------------
    # Find interconnection and interfacial area between ith and jth phases
    conns1 = net['throat.conns'][:, 0]
    conns2 = net['throat.conns'][:, 1]
    label = net['pore.label'] - 1

    num = snow_partitioning_n.phase_max_label
    num = [0, *num]
    phases_num = sp.unique(im * 1)
    phases_num = sp.trim_zeros(phases_num)
    for i in phases_num:
        loc1 = sp.logical_and(conns1 >= num[i - 1], conns1 < num[i])
        loc2 = sp.logical_and(conns2 >= num[i - 1], conns2 < num[i])
        loc3 = sp.logical_and(label >= num[i - 1], label < num[i])
        net['throat.{}'.format(al[i])] = loc1 * loc2
        net['pore.{}'.format(al[i])] = loc3
        if i == phases_num[-1]:
            loc4 = sp.logical_and(conns1 < num[-1], conns2 >= num[-1])
            loc5 = label >= num[-1]
            net['throat.boundary'] = loc4
            net['pore.boundary'] = loc5
        for j in phases_num:
            if j > i:
                pi_pj_sa = sp.zeros_like(label)
                loc6 = sp.logical_and(conns2 >= num[j - 1], conns2 < num[j])
                pi_pj_conns = loc1 * loc6
                net['throat.{}_{}'.format(al[i], al[j])] = pi_pj_conns
                if any(pi_pj_conns):
                    # ---------------------------------------------------------
                    # Calculates phase[i] interfacial area that connects with
                    # phase[j] and vice versa
                    p_conns = net['throat.conns'][:, 0][pi_pj_conns]
                    s_conns = net['throat.conns'][:, 1][pi_pj_conns]
                    ps = net['throat.area'][pi_pj_conns]
                    p_sa = sp.bincount(p_conns, ps)
                    # trim zeros at head/tail position to avoid extra bins
                    p_sa = sp.trim_zeros(p_sa)
                    i_index = sp.arange(min(p_conns), max(p_conns) + 1)
                    j_index = sp.arange(min(s_conns), max(s_conns) + 1)
                    s_pa = sp.bincount(s_conns, ps)
                    s_pa = sp.trim_zeros(s_pa)
                    pi_pj_sa[i_index] = p_sa
                    pi_pj_sa[j_index] = s_pa
                    # ---------------------------------------------------------
                    # Calculates interfacial area using marching cube method
                    if marching_cubes_area:
                        ps_c = net['throat.area'][pi_pj_conns]
                        p_sa_c = sp.bincount(p_conns, ps_c)
                        p_sa_c = sp.trim_zeros(p_sa_c)
                        s_pa_c = sp.bincount(s_conns, ps_c)
                        s_pa_c = sp.trim_zeros(s_pa_c)
                        pi_pj_sa[i_index] = p_sa_c
                        pi_pj_sa[j_index] = s_pa_c
                    net['pore.{}_{}_area'.format(al[i],
                                                 al[j])] = (pi_pj_sa *
                                                            voxel_size ** 2)
    return net
Пример #7
0
def snow_n(im,
           voxel_size=1,
           boundary_faces=['top', 'bottom', 'left', 'right', 'front', 'back'],
           marching_cubes_area=False,
           alias=None):
    r"""
    Analyzes an image that has been segemented into N phases and extracts all
    a network for each of the N phases, including geometerical information as
    well as network connectivity between each phase.

    Parameters
    ----------
    im : ND-array
        Image of porous material where each phase is represented by unique
        integer. Phase integer should start from 1 (0 is ignored)

    voxel_size : scalar
        The resolution of the image, expressed as the length of one side of a
        voxel, so the volume of a voxel would be **voxel_size**-cubed.  The
        default is 1, which is useful when overlaying the PNM on the original
        image since the scale of the image is always 1 unit lenth per voxel.

    boundary_faces : list of strings
        Boundary faces labels are provided to assign hypothetical boundary
        nodes having zero resistance to transport process. For cubical
        geometry, the user can choose ‘left’, ‘right’, ‘top’, ‘bottom’,
        ‘front’ and ‘back’ face labels to assign boundary nodes. If no label is
        assigned then all six faces will be selected as boundary nodes
        automatically which can be trimmed later on based on user requirements.

    marching_cubes_area : bool
        If ``True`` then the surface area and interfacial area between regions
        will be calculated using the marching cube algorithm. This is a more
        accurate representation of area in extracted network, but is quite
        slow, so it is ``False`` by default.  The default method simply counts
        voxels so does not correctly account for the voxelated nature of the
        images.

    alias : dict (Optional)
        A dictionary that assigns unique image label to specific phases. For
        example {1: 'Solid'} will show all structural properties associated
        with label 1 as Solid phase properties. If ``None`` then default
        labelling will be used i.e {1: 'Phase1',..}.

    Returns
    -------
    A dictionary containing all N phases size data, as well as the
    network topological information.  The dictionary names use the OpenPNM
    convention (i.e. 'pore.coords', 'throat.conns') so it may be converted
    directly to an OpenPNM network object using the ``update`` command.
    """
    # -------------------------------------------------------------------------
    # Get alias if provided by user
    al = _create_alias_map(im, alias=alias)
    # -------------------------------------------------------------------------
    # Perform snow on each phase and merge all segmentation and dt together
    snow = snow_partitioning_n(im,
                               r_max=4,
                               sigma=0.4,
                               return_all=True,
                               mask=True,
                               randomize=False,
                               alias=al)
    # -------------------------------------------------------------------------
    # Add boundary regions
    f = boundary_faces
    regions = add_boundary_regions(regions=snow.regions, faces=f)
    # -------------------------------------------------------------------------
    # Padding distance transform to extract geometrical properties
    dt = pad_faces(im=snow.dt, faces=f)
    # -------------------------------------------------------------------------
    # For only one phase extraction with boundary regions
    phases_num = sp.unique(im).astype(int)
    phases_num = sp.trim_zeros(phases_num)
    if len(phases_num) == 1:
        if f is not None:
            snow.im = pad_faces(im=snow.im, faces=f)
        regions = regions * (snow.im.astype(bool))
        regions = make_contiguous(regions)
    # -------------------------------------------------------------------------
    # Extract N phases sites and bond information from image
    net = regions_to_network(im=regions, dt=dt, voxel_size=voxel_size)
    # -------------------------------------------------------------------------
    # Extract marching cube surface area and interfacial area of regions
    if marching_cubes_area:
        areas = region_surface_areas(regions=regions)
        interface_area = region_interface_areas(regions=regions,
                                                areas=areas,
                                                voxel_size=voxel_size)
        net['pore.surface_area'] = areas * voxel_size**2
        net['throat.area'] = interface_area.area
    # -------------------------------------------------------------------------
    # Find interconnection and interfacial area between ith and jth phases
    net = add_phase_interconnections(net=net,
                                     snow_partitioning_n=snow,
                                     marching_cubes_area=marching_cubes_area,
                                     alias=al)
    # -------------------------------------------------------------------------
    # label boundary cells
    net = label_boundary_cells(network=net, boundary_faces=f)
    # -------------------------------------------------------------------------

    temp = _net_dict(net)
    temp.im = im.copy()
    temp.dt = dt
    temp.regions = regions
    return temp
Пример #8
0
def snow_partitioning_n(im, r_max=4, sigma=0.4, return_all=True,
                        mask=True, randomize=False, alias=None):
    r"""
    This function partitions an imaging oontain an arbitrary number of phases
    into regions using a marker-based watershed segmentation. Its an extension
    of snow_partitioning function with all phases partitioned together.

    Parameters
    ----------
    im : ND-array
        Image of porous material where each phase is represented by unique
        integer starting from 1 (0's are ignored).
    r_max : scalar
        The radius of the spherical structuring element to use in the Maximum
        filter stage that is used to find peaks.  The default is 4.
    sigma : scalar
        The standard deviation of the Gaussian filter used.  The default is
        0.4. If 0 is given then the filter is not applied, which is useful if a
        distance transform is supplied as the ``im`` argument that has already
        been processed.
    return_all : boolean (default is False)
        If set to ``True`` a named tuple is returned containing the original
        image, the combined distance transform, list of each phase max label,
        and the final combined regions of all phases.
    mask : boolean (default is True)
        Apply a mask to the regions which are not under concern.
    randomize : boolean
        If ``True`` (default), then the region colors will be randomized before
        returning.  This is helpful for visualizing otherwise neighboring
        regions have similar coloring and are hard to distinguish.
    alias : dict (Optional)
        A dictionary that assigns unique image label to specific phases. For
        example {1: 'Solid'} will show all structural properties associated
        with label 1 as Solid phase properties. If ``None`` then default
        labelling will be used i.e {1: 'Phase1',..}.

    Returns
    -------
    An image the same shape as ``im`` with the all phases partitioned into
    regions using a marker based watershed with the peaks found by the
    SNOW algorithm [1].  If ``return_all`` is ``True`` then a **named tuple**
    is returned with the following attribute:

        * ``im`` : The actual image of the porous material
        * ``dt`` : The combined distance transform of the image
        * ``phase_max_label`` : The list of max label of each phase in order to
        distinguish between each other
        * ``regions`` : The partitioned regions of n phases using a marker
        based watershed with the peaks found by the SNOW algorithm

    References
    ----------
    [1] Gostick, J. "A versatile and efficient network extraction algorithm
    using marker-based watershed segmentation".  Physical Review E. (2017)

    [2] Khan, ZA et al. "Dual network extraction algorithm to investigate
    multiple transport processes in porous materials: Image-based modeling
    of pore and grain-scale processes".  Computers in Chemical Engineering.
    (2019)

    See Also
    ----------
    snow_partitioning

    Notes
    -----
    In principle it is possible to perform a distance transform on each
    phase separately, merge these into a single image, then apply the
    watershed only once. This, however, has been found to create edge artifacts
    between regions arising from the way watershed handles plateaus in the
    distance transform. To overcome this, this function applies the watershed
    to each of the distance transforms separately, then merges the segmented
    regions back into a single image.

    """
    # Get alias if provided by user
    al = _create_alias_map(im=im, alias=alias)
    # Perform snow on each phase and merge all segmentation and dt together
    phases_num = sp.unique(im * 1)
    phases_num = sp.trim_zeros(phases_num)
    combined_dt = 0
    combined_region = 0
    num = [0]
    for i in phases_num:
        print('_' * 60)
        if alias is None:
            print('Processing Phase {}'.format(i))
        else:
            print('Processing Phase {}'.format(al[i]))
        phase_snow = snow_partitioning(im == i,
                                       dt=None, r_max=r_max, sigma=sigma,
                                       return_all=return_all, mask=mask,
                                       randomize=randomize)
        if len(phases_num) == 1 and phases_num == 1:
            combined_dt = phase_snow.dt
            combined_region = phase_snow.regions
        else:
            combined_dt += phase_snow.dt
            phase_snow.regions *= phase_snow.im
            phase_snow.regions += num[i - 1]
            phase_ws = phase_snow.regions * phase_snow.im
            phase_ws[phase_ws == num[i - 1]] = 0
            combined_region += phase_ws
        num.append(sp.amax(combined_region))
    if return_all:
        tup = namedtuple('results', field_names=['im', 'dt', 'phase_max_label',
                                                 'regions'])
        tup.im = im
        tup.dt = combined_dt
        tup.phase_max_label = num[1:]
        tup.regions = combined_region
        return tup
    else:
        return combined_region
Пример #9
0
def snow_dual(
        im,
        voxel_size=1,
        boundary_faces=['top', 'bottom', 'left', 'right', 'front', 'back']):
    r"""
    Analyzes an image that has been partitioned into void and solid regions
    and extracts the void and solid phase geometry as well as network
    connectivity.

    Parameters
    ----------
    im : ND-array
        Binary image in the Boolean form with True’s as void phase and False’s
        as solid phase. It can process the inverted configuration of the
        boolean image as well, but output labelling of phases will be inverted
        and solid phase properties will be assigned to void phase properties
        labels which will cause confusion while performing the simulation.

    voxel_size : scalar
        The resolution of the image, expressed as the length of one side of a
        voxel, so the volume of a voxel would be **voxel_size**-cubed.  The
        default is 1, which is useful when overlaying the PNM on the original
        image since the scale of the image is alway 1 unit lenth per voxel.

    boundary_faces : list of strings
        Boundary faces labels are provided to assign hypothetical boundary
        nodes having zero resistance to transport process. For cubical
        geometry, the user can choose ‘left’, ‘right’, ‘top’, ‘bottom’,
        ‘front’ and ‘back’ face labels to assign boundary nodes. If no label is
        assigned then all six faces will be selected as boundary nodes
        automatically which can be trimmed later on based on user requirements.

    Returns
    -------
    A dictionary containing all the void and solid phase size data, as well as
    the network topological information.  The dictionary names use the OpenPNM
    convention (i.e. 'pore.coords', 'throat.conns') so it may be converted
    directly to an OpenPNM network object using the ``update`` command.
    """
    # -------------------------------------------------------------------------
    # SNOW void phase
    pore_regions = snow_partitioning(im, return_all=True)
    # SNOW solid phase
    solid_regions = snow_partitioning(~im, return_all=True)
    # -------------------------------------------------------------------------
    # Combined Distance transform of two phases.
    pore_dt = pore_regions.dt
    solid_dt = solid_regions.dt
    dt = pore_dt + solid_dt
    # Calculates combined void and solid regions for dual network extraction
    pore_regions = pore_regions.regions
    solid_regions = solid_regions.regions
    pore_region = pore_regions * im
    solid_region = solid_regions * ~im
    solid_num = sp.amax(pore_regions)
    solid_region = solid_region + solid_num
    solid_region = solid_region * ~im
    regions = pore_region + solid_region
    b_num = sp.amax(regions)
    # -------------------------------------------------------------------------
    # Boundary Conditions
    regions = add_boundary_regions(regions=regions, faces=boundary_faces)
    # -------------------------------------------------------------------------
    # Padding distance transform to extract geometrical properties
    f = boundary_faces
    if f is not None:
        faces = [(int('top' in f) * 3, int('bottom' in f) * 3),
                 (int('left' in f) * 3, int('right' in f) * 3)]
        if im.ndim == 3:
            faces = [(int('front' in f) * 3, int('back' in f) * 3)] + faces
        dt = sp.pad(dt, pad_width=faces, mode='edge')
    else:
        dt = dt
    # -------------------------------------------------------------------------
    # Extract void,solid and throat information from image
    net = regions_to_network(im=regions, dt=dt, voxel_size=voxel_size)
    # -------------------------------------------------------------------------
    # Find void to void, void to solid and solid to solid throat conns
    loc1 = net['throat.conns'][:, 0] < solid_num
    loc2 = net['throat.conns'][:, 1] >= solid_num
    loc3 = net['throat.conns'][:, 1] < b_num
    pore_solid_labels = loc1 * loc2 * loc3

    loc4 = net['throat.conns'][:, 0] >= solid_num
    loc5 = net['throat.conns'][:, 0] < b_num
    solid_solid_labels = loc4 * loc2 * loc5 * loc3

    loc6 = net['throat.conns'][:, 1] < solid_num
    pore_pore_labels = loc1 * loc6

    loc7 = net['throat.conns'][:, 1] >= b_num
    boundary_throat_labels = loc5 * loc7

    solid_labels = ((net['pore.label'] > solid_num) *
                    ~(net['pore.label'] > b_num))
    boundary_labels = net['pore.label'] > b_num
    b_sa = sp.zeros(len(boundary_labels[boundary_labels == 1.0]))
    # -------------------------------------------------------------------------
    # Calculates void interfacial area that connects with solid and vice versa
    p_conns = net['throat.conns'][:, 0][pore_solid_labels]
    ps = net['throat.area'][pore_solid_labels]
    p_sa = sp.bincount(p_conns, ps)
    s_conns = net['throat.conns'][:, 1][pore_solid_labels]
    s_pa = sp.bincount(s_conns, ps)
    s_pa = sp.trim_zeros(s_pa)  # remove pore surface area labels
    p_solid_surf = sp.concatenate((p_sa, s_pa, b_sa))
    # -------------------------------------------------------------------------
    # Calculates interfacial area using marching cube method
    ps_c = net['throat.area_mc'][pore_solid_labels]
    p_sa_c = sp.bincount(p_conns, ps_c)
    s_pa_c = sp.bincount(s_conns, ps_c)
    s_pa_c = sp.trim_zeros(s_pa_c)  # remove pore surface area labels
    p_solid_surf_c = sp.concatenate((p_sa_c, s_pa_c, b_sa))
    # -------------------------------------------------------------------------
    # Adding additional information of dual network
    net['pore.solid_IFA'] = p_solid_surf * voxel_size**2
    net['pore.solid_IFA_mc'] = (p_solid_surf_c * voxel_size**2)
    net['throat.void'] = pore_pore_labels
    net['throat.interconnect'] = pore_solid_labels
    net['throat.solid'] = solid_solid_labels
    net['throat.boundary'] = boundary_throat_labels
    net['pore.void'] = net['pore.label'] <= solid_num
    net['pore.solid'] = solid_labels
    net['pore.boundary'] = boundary_labels
    return net