Exemple #1
0
    def gt_to_vtk(din):
        """
        From the graph-tool property value type creates an equivalent
        vtkDataArray object.

        Args:
            din (str): graph-tool property value type

        Returns:
            vtkDataArray object
        """

        # Check that a string object is passed
        if not isinstance(din, str):
            error_msg = 'str object required as input.'
            raise pexceptions.PySegInputError(expr='gt_to_vtk (TypesConverter)',
                                              msg=error_msg)

        if (din == 'bool') or (din == 'vector<bool>'):
            return vtk.vtkIntArray()  # was vtk.vtkBitArray()
        elif (din == 'int16_t') or (din == 'vector<int16_t>'):
            return vtk.vtkTypeInt16Array()
        elif (din == 'int32_t') or (din == 'vector<int32_t>'):
            return vtk.vtkIntArray()
        elif (din == 'int64_t') or (din == 'vector<int64_t>'):
            return vtk.vtkTypeInt64Array()
        elif (din == 'double') or (din == 'vector<double>'):
            return vtk.vtkFloatArray()
        else:
            error_msg = 'Graph-tool alias not identified.'
            raise pexceptions.PySegInputError(expr='gt_to_vtk (TypesConverter)',
                                              msg=error_msg)
Exemple #2
0
    def gt_to_numpy(din):
        """
        From the graph-tool property value type return an equivalent numpy data
        type.

        Args:
            din (str): graph-tool property value type

        Returns:
            numpy type
        """

        # Check that a string object is passed
        if not isinstance(din, str):
            error_msg = 'str object required as input.'
            raise pexceptions.PySegInputError(
                expr='gt_to_numpy (TypesConverter)', msg=error_msg
            )

        if (din == 'bool') or (din == 'vector<bool>'):
            return np.bool
        elif (din == 'int16_t') or (din == 'vector<int16_t>'):
            return np.int16
        elif (din == 'int32_t') or (din == 'vector<int32_t>'):
            return np.int32
        elif (din == 'int64_t') or (din == 'vector<int64_t>'):
            return np.int64
        elif (din == 'double') or (din == 'vector<double>'):
            return np.float
        else:
            error_msg = 'Graph-tool alias not identified.'
            raise pexceptions.PySegInputError(
                expr='gt_to_numpy (TypesConverter)', msg=error_msg
            )
Exemple #3
0
def get_target_voxels_in_membrane_mask(ribo_mask, mem_mask, verbose=False):
    """
    Gets target voxels from a ribosome mask and pre-filters them to those that
    are inside a membrane mask (value 1).

    Prints out the target voxel numbers before and after filtering and warns of
    the voxels that are not inside the membrane mask.

    Args:
        ribo_mask (numpy.ndarray): a ribosome mask
        mem_mask (numpy.ndarray): a membrane mask
        verbose (boolean, optional): it True (default False), additionally
            prints out the target voxels before and after filtering

    Returns:
         a list of the target voxels that are inside the membrane mask as tuples
         in form (x, y, z)
    """
    if (isinstance(ribo_mask, np.ndarray) and (len(ribo_mask.shape) == 3)
            and isinstance(mem_mask, np.ndarray)
            and (len(mem_mask.shape) == 3)):
        if ribo_mask.shape != mem_mask.shape:
            error_msg = (
                'Both input 3D numpy ndarray objects have to have the '
                'same scales.')
            raise pexceptions.PySegInputError(
                expr='get_target_voxels_in_membrane_mask', msg=error_msg)
        # Find the set of voxels of ribosome centers mapped on the membrane,
        # called 'target voxels' from now on:
        target_voxels = get_foreground_voxels_from_mask(ribo_mask)
        print '%s target voxels' % len(target_voxels)
        if verbose:
            print target_voxels

        target_voxels_in_membrane_mask = []
        for target_voxel in target_voxels:
            if mem_mask[target_voxel[0], target_voxel[1],
                        target_voxel[2]] == 1:
                target_voxels_in_membrane_mask.append(target_voxel)
            else:
                error_msg = (
                    'Target voxel (%s, %s, %s) not inside the '
                    'membrane!' %
                    (target_voxel[0], target_voxel[1], target_voxel[2]))
                raise pexceptions.PySegInputWarning(
                    expr='get_target_voxels_in_membrane_mask', msg=error_msg)
        print('%s target voxels in membrane' %
              len(target_voxels_in_membrane_mask))
        if verbose:
            print target_voxels_in_membrane_mask
        return target_voxels_in_membrane_mask
    else:
        error_msg = ('3D numpy ndarray objects required as first and second '
                     'input')
        raise pexceptions.PySegInputError(
            expr='get_target_voxels_in_membrane_mask', msg=error_msg)
Exemple #4
0
def load_tomo(fname, mmap=False):
    """
    Loads a tomogram in MRC, EM or VTI format and converts it into a numpy
    format.

    Args:
        fname (str): full path to the tomogram, has to end with '.mrc', '.em' or
            '.vti'
        mmap (boolean, optional): if True (default False) a numpy.memmap object
            is loaded instead of numpy.ndarray, which means that data are not
            loaded completely to memory, this is useful only for very large
            tomograms. Only valid with formats MRC and EM. VERY IMPORTANT: This
            subclass of ndarray has some unpleasant interaction with some
            operations, because it does not quite fit properly as a subclass of
            numpy.ndarray

    Returns:
        numpy.ndarray or numpy.memmap object
    """
    # Input parsing
    stem, ext = os.path.splitext(fname)
    if mmap and (not (ext == '.mrc' or (ext == '.em'))):
        error_msg = ('mmap option is only valid for MRC or EM formats, current '
                     + ext)
        raise pexceptions.PySegInputError(expr='load_tomo', msg=error_msg)

    # if ext == '.fits':
    #     im_data = pyfits.getdata(fname).transpose()
    elif ext == '.mrc':
        image = ImageIO()
        if mmap:
            image.readMRC(fname, memmap=mmap)
        else:
            image.readMRC(fname)
        im_data = image.data
    elif ext == '.em':
        image = ImageIO()
        if mmap:
            image.readEM(fname, memmap=mmap)
        else:
            image.readEM(fname)
        im_data = image.data
    elif ext == '.vti':
        reader = vtk.vtkXMLImageDataReader()
        reader.SetFileName(fname)
        reader.Update()
        im_data = vti_to_numpy(reader.GetOutput())
    else:
        error_msg = '%s is non valid format.' % ext
        raise pexceptions.PySegInputError(expr='load_tomo', msg=error_msg)

    # For avoiding 2D arrays
    if len(im_data.shape) == 2:
        im_data = np.reshape(im_data, (im_data.shape[0], im_data.shape[1], 1))

    return im_data
Exemple #5
0
    def foreground_neighbors_of_voxel(mask, voxel):
        """
        Returns neighbor voxels with value 1 (foreground) of a given voxel
        inside a binary mask of a membrane segmentation.

        Args:
            mask (numpy.ndarray): a binary 3D mask
            voxel (tuple): voxel coordinates in the mask as a tuple of integers
                of length 3: (x, y, z)

        Returns:
            a list of tuples with neighbor voxels coordinates in format
            [(x1, y1, z1), (x2, y2, z2), ...]
        """
        neighbor_voxels = []
        if isinstance(mask, np.ndarray) and (len(mask.shape) == 3):
            if isinstance(voxel, tuple) and (len(voxel) == 3):
                x = voxel[0]
                y = voxel[1]
                z = voxel[2]
                x_size = mask.shape[0]
                y_size = mask.shape[1]
                z_size = mask.shape[2]
                # iterate over all possible (max 26) neighbor voxels
                # combinations
                for i in (x - 1, x, x + 1):
                    for j in (y - 1, y, y + 1):
                        for k in (z - 1, z, z + 1):
                            # do not add the voxel itself and voxels outside the
                            # border
                            if ((i, j, k) != (x, y, z)
                                    and i in xrange(0, x_size)
                                    and j in xrange(0, y_size)
                                    and k in xrange(0, z_size)):
                                # add only foreground voxels to the neighbors
                                # list
                                if mask[i, j, k] == 1:
                                    neighbor_voxels.append((i, j, k))
            else:
                error_msg = ('A tuple of integers of length 3 required as the'
                             'second input.')
                raise pexceptions.PySegInputError(
                    expr='foreground_neighbors_of_voxel (VoxelGraph)',
                    msg=error_msg)
        else:
            error_msg = 'A 3D numpy ndarray required as the first input.'
            raise pexceptions.PySegInputError(
                expr='foreground_neighbors_of_voxel (VoxelGraph)',
                msg=error_msg)
        return neighbor_voxels
Exemple #6
0
    def vtk_to_numpy(din):
        """
        From a vtkDataArray object returns an equivalent numpy data type.

        Args:
            din (vtk.vtkDataArray): input vtkDataArray object

        Returns:
            numpy type
        """

        # Check that a type object is passed
        if not isinstance(din, vtk.vtkDataArray):
            error_msg = 'vtkDataArray object required as input.'
            raise pexceptions.PySegInputError(
                expr='vtk_to_numpy (TypesConverter)', msg=error_msg
            )

        if isinstance(din, vtk.vtkBitArray):
            return np.bool
        elif (isinstance(din, vtk.vtkIntArray) or
                isinstance(din, vtk.vtkTypeInt32Array)):
            return np.int
        elif isinstance(din, vtk.vtkTypeInt8Array):
            return np.int8
        elif isinstance(din, vtk.vtkTypeInt16Array):
            return np.int16
        elif isinstance(din, vtk.vtkTypeInt64Array):
            return np.int64
        elif isinstance(din, vtk.vtkTypeUInt8Array):
            return np.uint8
        elif isinstance(din, vtk.vtkTypeUInt16Array):
            return np.uint16
        elif isinstance(din, vtk.vtkTypeUInt32Array):
            return np.uint32
        elif isinstance(din, vtk.vtkTypeUInt64Array):
            return np.uint64
        elif (isinstance(din, vtk.vtkFloatArray) or
                isinstance(din, vtk.vtkTypeFloat32Array)):
            return np.float32
        elif (isinstance(din, vtk.vtkDoubleArray) or
                isinstance(din, vtk.vtkTypeFloat64Array)):
            return np.float64
        else:
            error_msg = 'VTK type not identified.'
            raise pexceptions.PySegInputError(
                expr='numpy_to_vtk_array (TypesConverter)', msg=error_msg
            )
Exemple #7
0
def get_foreground_voxels_from_mask(mask):
    """
    Gets foreground (non-zero) voxel coordinates from a mask (binary tomographic
    data).

    Args:
        mask (numpy ndarray): a 3D array holding the mask, which should be
            binary

    Returns:
        a list of foreground (nonzero) voxel coordinates as tuples in form
        (x, y, z)
    """
    voxels = []
    # check that the mask is a 3D numpy array:
    if isinstance(mask, np.ndarray) and (len(mask.shape) == 3):
        indices = mask.nonzero()
        voxels_num = indices[0].size
        for i in xrange(voxels_num):
            voxel_i = (indices[0][i], indices[1][i], indices[2][i])
            voxels.append(voxel_i)
    else:
        error_msg = 'A 3D numpy ndarray object required as input.'
        raise pexceptions.PySegInputError(
            expr='get_foreground_voxels_from_mask', msg=error_msg)
    return voxels
Exemple #8
0
def ndarray_voxels_to_tupel_list(voxels_ndarray):
    """
    Turns voxel coordinates from a 2D numpy ndarray in format
    [[x1, y1, z1], [x2, y2, z2], ...] into a list of tuples in format
    [(x1, y1, z1), (x2, y2, z2), ...].

    Args:
        voxels_ndarray (numpy.ndarray): a 2D array containing voxel coordinates
            in format [[x1, y1, z1], [x2, y2, z2], ...]

    Returns:
        a list of tuples containing the voxel coordinates in format
        [(x1, y1, z1), (x2, y2, z2), ...]
    """
    tupel_list = []
    # check that voxels_ndarray is a 2D numpy array with 3 columns:
    if (isinstance(voxels_ndarray, np.ndarray)
            and (len(voxels_ndarray.shape) == 2)
            and (voxels_ndarray.shape[1] == 3)):
        voxels_list = voxels_ndarray.tolist()
        for voxel in voxels_list:
            x = voxel[0]
            y = voxel[1]
            z = voxel[2]
            tupel_list.append((x, y, z))
    else:
        error_msg = 'A 2D numpy ndarray with 3 columns required as input.'
        raise pexceptions.PySegInputError(expr='ndarray_voxels_to_tupel_list',
                                          msg=error_msg)
    return tupel_list
Exemple #9
0
    def distance_between_voxels(voxel1, voxel2):
        """
        Calculates and returns the Euclidean distance between two voxels.

        Args:
            voxel1 (tuple): first voxel coordinates in form of a tuple of
                integers of length 3 (x1, y1, z1)
            voxel2 (tuple): second voxel coordinates in form of a tuple of
                integers of length 3 (x2, y2, z2)

        Returns:
            the Euclidean distance between two voxels (float)
        """
        if (isinstance(voxel1, tuple) and (len(voxel1) == 3) and
                isinstance(voxel2, tuple) and (len(voxel2) == 3)):
            sum_of_squared_differences = 0
            for i in range(3):  # for each dimension
                sum_of_squared_differences += (voxel1[i] - voxel2[i]) ** 2
            return math.sqrt(sum_of_squared_differences)
        else:
            error_msg = ('Tuples of integers of length 3 required as first and '
                         'second input.')
            raise pexceptions.PySegInputError(
                expr='distance_between_voxels (SegmentationGraph)',
                msg=error_msg
            )
Exemple #10
0
def __filter_null_triangles(surface, verbose=False):
    """
    For a given VTK PolyData surface, filters out triangles with zero area, if
    any are present.

    Is used by the function run_gen_surface.

    Args:
        surface (vtk.PolyData): surface of triangles
        verbose (boolean, optional): if True (default False), some extra
        information will be printed out

    Returns:
        the filtered triangle surface (vtk.PolyData)
    """
    if isinstance(surface, vtk.vtkPolyData):

        # Check numbers of cells (polygons or triangles) (and all points).
        print 'The surface has %s cells' % surface.GetNumberOfCells()
        if verbose:
            print '%s points' % surface.GetNumberOfPoints()

        null_area_triangles = 0
        for i in range(surface.GetNumberOfCells()):
            # Get the cell i and check if it's a triangle:
            cell = surface.GetCell(i)
            if isinstance(cell, vtk.vtkTriangle):
                # Get the 3 points which made up the triangular cell i:
                points_cell = cell.GetPoints()

                # Calculate the area of the triangle i;
                area = cell.TriangleArea(points_cell.GetPoint(0),
                                         points_cell.GetPoint(1),
                                         points_cell.GetPoint(2))
                if area <= 0:
                    if verbose:
                        print(
                            'Triangle %s is marked for deletion, because its'
                            'area is not > 0' % i)
                    surface.DeleteCell(i)
                    null_area_triangles += 1

            else:
                print 'Oops, the cell number %s is not a triangle!' % i

        if null_area_triangles:
            surface.RemoveDeletedCells()
            print('%s triangles with area = 0 were removed, resulting in:' %
                  null_area_triangles)
            # Recheck numbers of cells (polygons or triangles):
            print '%s cells' % surface.GetNumberOfCells()

    else:
        error_msg = 'The first input must be a vtkPolyData object.'
        raise pexceptions.PySegInputError(expr='__filter_null_triangles',
                                          msg=error_msg)

    return surface
Exemple #11
0
    def build_graph_from_np_ndarray(self, mask, verbose=False):
        """
        Builds a graph from a binary mask of a membrane segmentation, including
        only voxels with value 1 (foreground voxels).

        Each foreground voxel, its foreground neighbor voxels and edges with
        euclidean distances between the voxel and its neighbor voxels (all
        scaled in nm) are added to the graph.

        Args:
            mask (numpy.ndarray): a binary 3D mask
            verbose (boolean, optional): if True (default False), some extra
                information will be printed out

        Returns:
            None
        """
        if isinstance(mask, np.ndarray) and (len(mask.shape) == 3):
            if mask.shape != (self.scale_x, self.scale_y, self.scale_z):
                error_msg = (
                    'Scales of the input mask have to be equal to '
                    'those set during the generation of the VoxelGraph'
                    'object.')
                raise pexceptions.PySegInputError(
                    expr='build_graph_from_np_ndarray (VoxelGraph)',
                    msg=error_msg)
            # Find the set of the membrane voxels, which become the vertices of
            # the graph:
            membrane_voxels = get_foreground_voxels_from_mask(mask)
            print '%s membrane voxels' % len(membrane_voxels)
            if verbose:
                print membrane_voxels
            self.__expand_voxels(mask, membrane_voxels, verbose)
        else:
            error_msg = 'A 3D numpy ndarray object required as first input.'
            raise pexceptions.PySegInputError(
                expr='build_graph_from_np_ndarray (VoxelGraph)', msg=error_msg)
Exemple #12
0
def save_vtp(poly, fname):
    """
    Saves a VTK PolyData object into a VTP file.

    Args:
        poly (vtk.vtkPolyData): input VTK PolyData object
        fname (str): output file name

    Returns:
        None
    """
    writer = vtk.vtkXMLPolyDataWriter()
    writer.SetFileName(fname)
    writer.SetInputData(poly)
    if writer.Write() != 1:
        error_msg = 'Error writing the file %s.' % fname
        raise pexceptions.PySegInputError(expr='save_vtp', msg=error_msg)
Exemple #13
0
def save_numpy(array, fname):
    """
    Saves a numpy array to a file in MRC, EM or VTI format.

    Args:
        array (numpy.ndarray): input array
        fname (str): full path to the tomogram, has to end with '.mrc', '.em' or
            '.vti'

    Returns:
        None
    """
    _, ext = os.path.splitext(fname)

    # Parse input array for fulfilling format requirements
    if (ext == '.mrc') or (ext == '.em'):
        if ((array.dtype != 'ubyte') and (array.dtype != 'int16') and
                (array.dtype != 'float32')):
            array = array.astype('float32')
        # if (len(array.shape) == 3) and (array.shape[2] == 1):
        #   array = np.reshape(array, (array.shape[0], array.shape[1]))

    if ext == '.vti':
        pname, fnameh = os.path.split(fname)
        save_vti(numpy_to_vti(array), fnameh, pname)
    # elif ext == '.fits':
    #     warnings.resetwarnings()
    #     warnings.filterwarnings('ignore', category=UserWarning, append=True)
    #     pyfits.writeto(fname, array, clobber=True, output_verify='silentfix')
    #     warnings.resetwarnings()
    #     warnings.filterwarnings('always', category=UserWarning, append=True)
    elif ext == '.mrc':
        img = ImageIO()
        # img.setData(np.transpose(array, (1, 0, 2)))
        img.setData(array)
        img.writeMRC(fname)
    elif ext == '.em':
        img = ImageIO()
        # img.setData(np.transpose(array, (1, 0, 2)))
        img.setData(array)
        img.writeEM(fname)
    else:
        error_msg = 'Format not valid %s.' % ext
        raise pexceptions.PySegInputError(expr='save_numpy', msg=error_msg)
Exemple #14
0
def save_vti(image, fname, outputdir):
    """
    Saves a VTK image data object into a VTI file.

    Args:
        image (vtk.vtkImageData):
        fname (str): output file name, should end with '.vti'
        outputdir (str): output directory

    Returns:
        None
    """
    writer = vtk.vtkXMLImageDataWriter()
    outputfile = outputdir + '/' + fname
    writer.SetFileName(outputfile)
    writer.SetInputData(image)
    if writer.Write() != 1:
        error_msg = 'Error writing the %s file on %s.' % (fname, outputdir)
        raise pexceptions.PySegInputError(expr='save_vti', msg=error_msg)
Exemple #15
0
    def get_vertex_property_array(self, property_name):
        """
        Gets a numpy array with all values of a vertex property of the graph,
        printing out the number of values, the minimal and the maximal value.

        Args:
            property_name (str): vertex property name

        Returns:
            an array (numpy.ndarray) with all values of the vertex property
        """
        if (isinstance(property_name, str) and
                property_name in self.graph.vertex_properties):
            values = self.graph.vertex_properties[property_name].get_array()
            print '%s "%s" values' % (len(values), property_name)
            print 'min = %s, max = %s' % (min(values), max(values))
            return values
        else:
            error_msg = ('The input "%s" is not a str object or is not found '
                         'in vertex properties of the graph.' % property_name)
            raise pexceptions.PySegInputError(
                expr='get_vertex_property_array (SegmentationGraph)',
                msg=error_msg)
Exemple #16
0
    def calculate_density(self, mask=None, target_coordinates=None,
                          verbose=False):
        """
        Calculates ribosome density for each membrane graph vertex.

        Calculates shortest geodesic distances (d) for each vertex in the graph
        to each reachable ribosome center mapped on the membrane given by a
        binary mask with coordinates in pixels or an array of coordinates in nm.
        Then, calculates a density measure of ribosomes at each vertex or
        membrane voxel: D = sum {over all reachable ribosomes} (1 / (d + 1)).
        Adds the density as vertex PropertyMap to the graph. Returns an array
        with the same shape as the underlying segmentation with the densities
        plus 1, in order to distinguish membrane voxels with 0 density from the
        background.

        Args:
            mask (numpy.ndarray, optional): a binary mask of the ribosome
                centers as 3D array where indices are coordinates in pixels
                (default None)
            target_coordinates (numpy.ndarray, optional): the ribosome centers
                coordinates in nm as 2D array in format
                [[x1, y1, z1], [x2, y2, z2], ...] (default None)
            verbose (boolean, optional): if True (default False), some extra
                information will be printed out

        Returns:
            a 3D numpy ndarray with the densities + 1

        Note:
            One of the first two parameters, mask or target_coordinates, has to
            be given.
        """
        import ribosome_density as rd
        # If a mask is given, find the set of voxels of ribosome centers mapped
        # on the membrane, 'target_voxels', and rescale them to nm,
        # 'target_coordinates':
        if mask is not None:
            if mask.shape != (self.scale_x, self.scale_y, self.scale_z):
                error_msg = ("Scales of the input 'mask' have to be equal to "
                             "those set during the generation of the graph.")
                raise pexceptions.PySegInputError(
                    expr='calculate_density (SegmentationGraph)', msg=error_msg
                )
            # output as a list of tuples [(x1,y1,z1), (x2,y2,z2), ...] in pixels
            target_voxels = rd.get_foreground_voxels_from_mask(mask)
            # for rescaling have to convert to an ndarray
            target_ndarray_voxels = rd.tupel_list_to_ndarray_voxels(
                target_voxels
            )
            # rescale to nm, output an ndarray [[x1,y1,z1], [x2,y2,z2], ...]
            target_ndarray_coordinates = (target_ndarray_voxels
                                          * self.scale_factor_to_nm)
            # convert to a list of tuples, which are in nm now
            target_coordinates = rd.ndarray_voxels_to_tupel_list(
                target_ndarray_coordinates
            )
        # If target_coordinates are given (in nm), convert them from a numpy
        # ndarray to a list of tuples:
        elif target_coordinates is not None:
            target_coordinates = rd.ndarray_voxels_to_tupel_list(
                target_coordinates
            )
        # Exit if the target_voxels list is empty:
        if len(target_coordinates) == 0:
            error_msg = ("No target voxels were found! Check your input "
                         "('mask' or 'target_coordinates').")
            raise pexceptions.PySegInputError(
                expr='calculate_density (SegmentationGraph)', msg=error_msg
            )
        print '%s target voxels' % len(target_coordinates)
        if verbose:
            print target_coordinates

        # Pre-filter the target coordinates to those existing in the graph
        # (should already all be in the graph, but just in case):
        target_coordinates_in_graph = []
        for target_xyz in target_coordinates:
            if target_xyz in self.coordinates_to_vertex_index:
                target_coordinates_in_graph.append(target_xyz)
            else:
                error_msg = ('Target (%s, %s, %s) not inside the membrane!'
                             % (target_xyz[0], target_xyz[1], target_xyz[2]))
                raise pexceptions.PySegInputWarning(
                    expr='calculate_density (SegmentationGraph)', msg=error_msg
                )

        print '%s target coordinates in graph' % len(
            target_coordinates_in_graph)
        if verbose:
            print target_coordinates_in_graph

        # Get all indices of the target coordinates:
        target_vertices_indices = []
        for target_xyz in target_coordinates_in_graph:
            v_target_index = self.coordinates_to_vertex_index[target_xyz]
            target_vertices_indices.append(v_target_index)

        # Density calculation
        # Add a new vertex property to the graph, density:
        self.graph.vp.density = self.graph.new_vertex_property("float")
        # Dictionary mapping voxel coordinates (for the volume returned later)
        # to a list of density values falling within that voxel:
        voxel_to_densities = {}

        # For each vertex in the graph:
        for v_membrane in self.graph.vertices():
            # Get its coordinates:
            membrane_xyz = self.graph.vp.xyz[v_membrane]
            if verbose:
                print ('Membrane vertex (%s, %s, %s)'
                       % (membrane_xyz[0], membrane_xyz[1], membrane_xyz[2]))
            # Get a distance map with all pairs of distances between current
            # graph vertex (membrane_xyz) and target vertices (ribosome
            # coordinates):
            dist_map = shortest_distance(self.graph, source=v_membrane,
                                         target=target_vertices_indices,
                                         weights=self.graph.ep.distance)

            # Iterate over all shortest distances from the membrane vertex to
            # the target vertices, while calculating the density:
            # Initializing: membrane coordinates with no reachable ribosomes
            # will have a value of 0, those with reachable ribosomes > 0.
            density = 0
            # If there is only one target voxel, dist_map is a single value -
            # wrap it into a list.
            if len(target_coordinates_in_graph) == 1:
                dist_map = [dist_map]
            for d in dist_map:
                if verbose:
                    print '\tTarget vertex ...'
                # if unreachable, the maximum float64 is stored
                if d == np.finfo(np.float64).max:
                    if verbose:
                        print '\t\tunreachable'
                else:
                    if verbose:
                        print '\t\td = %s' % d
                    density += 1 / (d + 1)

            # Add the density of the membrane vertex as a property of the
            # current vertex in the graph:
            self.graph.vp.density[v_membrane] = density

            # Calculate the corresponding voxel of the vertex and add the
            # density to the list keyed by the voxel in the dictionary:
            # Scaling the coordinates back from nm to voxels. (Without round
            # float coordinates are truncated to the next lowest integer.)
            voxel_x = int(round(membrane_xyz[0] / self.scale_factor_to_nm))
            voxel_y = int(round(membrane_xyz[1] / self.scale_factor_to_nm))
            voxel_z = int(round(membrane_xyz[2] / self.scale_factor_to_nm))
            voxel = (voxel_x, voxel_y, voxel_z)
            if voxel in voxel_to_densities:
                voxel_to_densities[voxel].append(density)
            else:
                voxel_to_densities[voxel] = [density]

            if verbose:
                print '\tdensity = %s' % density
            if (self.graph.vertex_index[v_membrane] + 1) % 1000 == 0:
                now = datetime.now()
                print ('%s membrane vertices processed on: %s-%s-%s %s:%s:%s'
                       % (self.graph.vertex_index[v_membrane] + 1, now.year,
                          now.month, now.day, now.hour, now.minute, now.second))

        # Initialize an array scaled like the original segmentation, which will
        # hold in each membrane voxel the maximal density among the
        # corresponding vertex coordinates in the graph plus 1 and 0 in each
        # background (non-membrane) voxel:
        densities = np.zeros((self.scale_x, self.scale_y, self.scale_z),
                             dtype=np.float16)
        # The densities array membrane voxels are initialized with 1 in order to
        # distinguish membrane voxels from the background.
        for voxel in voxel_to_densities:
            densities[voxel[0], voxel[1], voxel[2]] = 1 + max(
                voxel_to_densities[voxel])
        if verbose:
            print 'densities:\n%s' % densities
        return densities
Exemple #17
0
def run_gen_surface(tomo,
                    outfile_base,
                    lbl=1,
                    mask=True,
                    save_input_as_vti=False,
                    verbose=False):
    """
    Runs pysurf_io.gen_surface function, which generates a VTK PolyData triangle
    surface for objects in a segmented volume with a given label.

    Removes triangles with zero area, if any are present, from the resulting
    surface.

    Args:
        tomo (str or numpy.ndarray): segmentation input file in one of the
            formats: '.mrc', '.em' or '.vti', or 3D array containing the
            segmentation
        outfile_base (str): the path and filename without the ending for saving
            the surface (ending '.surface.vtp' will be added automatically)
        lbl (int, optional): the label to be considered, 0 will be ignored,
            default 1
        mask (boolean, optional): if True (default), a mask of the binary
            objects is applied on the resulting surface to reduce artifacts
        save_input_as_vti (boolean, optional): if True (default False), the
            input is saved as a '.vti' file ('<outfile_base>.vti')
        verbose (boolean, optional): if True (default False), some extra
            information will be printed out

    Returns:
        the triangle surface (vtk.PolyData)
    """
    t_begin = time.time()

    # Generating the surface (vtkPolyData object)
    surface = io.gen_surface(tomo, lbl=lbl, mask=mask, verbose=verbose)

    # Filter out triangles with area=0, if any are present
    surface = __filter_null_triangles(surface, verbose=verbose)

    t_end = time.time()
    duration = t_end - t_begin
    print 'Surface generation took: %s min %s s' % divmod(duration, 60)

    # Writing the vtkPolyData surface into a VTP file
    io.save_vtp(surface, outfile_base + '.surface.vtp')
    print 'Surface was written to the file %s.vtp' % outfile_base

    if save_input_as_vti is True:
        # If input is a file name, read in the segmentation array from the file:
        if isinstance(tomo, str):
            tomo = io.load_tomo(tomo)
        elif not isinstance(tomo, np.ndarray):
            error_msg = 'Input must be either a file name or a ndarray.'
            raise pexceptions.PySegInputError(expr='run_gen_surface',
                                              msg=error_msg)

        # Save the segmentation as VTI for opening it in ParaView:
        io.save_numpy(tomo, outfile_base + '.vti')
        print 'Input was saved as the file %s.vti' % outfile_base

    return surface
Exemple #18
0
def gen_surface(tomo, lbl=1, mask=True, purge_ratio=1, field=False,
                mode_2d=False, verbose=False):
    """
    Generates a VTK PolyData surface from a segmented tomogram.

    Args:
        tomo (numpy.ndarray or str): the input segmentation as numpy ndarray or
            the file name in MRC, EM or VTI format
        lbl (int, optional): label for the foreground, default 1
        mask (boolean, optional): if True (default), the input segmentation is
            used as mask for the surface
        purge_ratio (int, optional): if greater than 1 (default), then 1 every
            purge_ratio points of the segmentation are randomly deleted
        field (boolean, optional): if True (default False), additionally returns
            the polarity distance scalar field
        mode_2d (boolean, optional): needed for polarity distance calculation
            (if field is True), if True (default False), ...
        verbose (boolean, optional): if True (default False), prints out
            messages for checking the progress

    Returns:
        - output surface (vtk.vtkPolyData)
        - polarity distance scalar field (np.ndarray), if field is True
    """
    # Check input format
    if isinstance(tomo, str):
        fname, fext = os.path.splitext(tomo)
        # if fext == '.fits':
        #     tomo = pyfits.getdata(tomo)
        if fext == '.mrc':
            hold = ImageIO()
            hold.readMRC(file=tomo)
            tomo = hold.data
        elif fext == '.em':
            hold = ImageIO()
            hold.readEM(file=tomo)
            tomo = hold.data
        elif fext == '.vti':
            reader = vtk.vtkXMLImageDataReader()
            reader.SetFileName(tomo)
            reader.Update()
            tomo = vti_to_numpy(reader.GetOutput())
        else:
            error_msg = 'Format %s not readable.' % fext
            raise pexceptions.PySegInputError(expr='gen_surface', msg=error_msg)
    elif not isinstance(tomo, np.ndarray):
        error_msg = 'Input must be either a file name or a ndarray.'
        raise pexceptions.PySegInputError(expr='gen_surface', msg=error_msg)

    # Load file with the cloud of points
    nx, ny, nz = tomo.shape
    cloud = vtk.vtkPolyData()
    points = vtk.vtkPoints()
    cloud.SetPoints(points)

    if purge_ratio <= 1:
        for x in range(nx):
            for y in range(ny):
                for z in range(nz):
                    if tomo[x, y, z] == lbl:
                        points.InsertNextPoint(x, y, z)
    else:
        count = 0
        mx_value = purge_ratio - 1
        purge = np.random.randint(0, purge_ratio+1, nx*ny*nz)
        for x in range(nx):
            for y in range(ny):
                for z in range(nz):
                    if purge[count] == mx_value:
                        if tomo[x, y, z] == lbl:
                            points.InsertNextPoint(x, y, z)
                    count += 1

    if verbose:
        print 'Cloud of points loaded...'

    # Creating the isosurface
    surf = vtk.vtkSurfaceReconstructionFilter()
    # surf.SetSampleSpacing(2)
    surf.SetSampleSpacing(purge_ratio)
    # surf.SetNeighborhoodSize(10)
    surf.SetInputData(cloud)
    contf = vtk.vtkContourFilter()
    contf.SetInputConnection(surf.GetOutputPort())
    # if thick is None:
    contf.SetValue(0, 0)
    # else:
        # contf.SetValue(0, thick)

    # Sometimes the contouring algorithm can create a volume whose gradient
    # vector and ordering of polygon (using the right hand rule) are
    # inconsistent. vtkReverseSense cures    this problem.
    reverse = vtk.vtkReverseSense()
    reverse.SetInputConnection(contf.GetOutputPort())
    reverse.ReverseCellsOn()
    reverse.ReverseNormalsOn()
    reverse.Update()
    rsurf = reverse.GetOutput()

    if verbose:
        print 'Isosurfaces generated...'

    # Translate and scale to the proper positions
    cloud.ComputeBounds()
    rsurf.ComputeBounds()
    xmin, xmax, ymin, ymax, zmin, zmax = cloud.GetBounds()
    rxmin, rxmax, rymin, rymax, rzmin, rzmax = rsurf.GetBounds()
    scale_x = (xmax-xmin) / (rxmax-rxmin)
    scale_y = (ymax-ymin) / (rymax-rymin)
    denom = rzmax - rzmin
    num = zmax - xmin
    if (denom == 0) or (num == 0):
        scale_z = 1
    else:
        scale_z = (zmax-zmin) / (rzmax-rzmin)
    transp = vtk.vtkTransform()
    transp.Translate(xmin, ymin, zmin)
    transp.Scale(scale_x, scale_y, scale_z)
    transp.Translate(-rxmin, -rymin, -rzmin)
    tpd = vtk.vtkTransformPolyDataFilter()
    tpd.SetInputData(rsurf)
    tpd.SetTransform(transp)
    tpd.Update()
    tsurf = tpd.GetOutput()

    if verbose:
        print 'Rescaled and translated...'

    # Masking according to distance to the original segmentation
    if mask:
        tomod = scipy.ndimage.morphology.distance_transform_edt(
            np.invert(tomo == lbl))
        for i in range(tsurf.GetNumberOfCells()):

            # Check if all points which made up the polygon are in the mask
            points_cell = tsurf.GetCell(i).GetPoints()
            count = 0
            for j in range(0, points_cell.GetNumberOfPoints()):
                x, y, z = points_cell.GetPoint(j)
                if (tomod[int(round(x)), int(round(y)), int(round(z))]
                        > MAX_DIST_SURF):
                    count += 1

            if count > 0:
                tsurf.DeleteCell(i)

        # Release free memory
        tsurf.RemoveDeletedCells()

        if verbose:
            print 'Mask applied...'

    # Field distance
    if field:

        # Get normal attributes
        norm_flt = vtk.vtkPolyDataNormals()
        norm_flt.SetInputData(tsurf)
        norm_flt.ComputeCellNormalsOn()
        norm_flt.AutoOrientNormalsOn()
        norm_flt.ConsistencyOn()
        norm_flt.Update()
        tsurf = norm_flt.GetOutput()
        # for i in range(tsurf.GetPointData().GetNumberOfArrays()):
        #    array = tsurf.GetPointData().GetArray(i)
        #    if array.GetNumberOfComponents() == 3:
        #        break
        array = tsurf.GetCellData().GetNormals()

        # Build membrane mask
        tomoh = np.ones(shape=tomo.shape, dtype=np.bool)
        tomon = np.ones(shape=(tomo.shape[0], tomo.shape[1], tomo.shape[2], 3),
                        dtype=TypesConverter().vtk_to_numpy(array))
        # for i in range(tsurf.GetNumberOfCells()):
        #     points_cell = tsurf.GetCell(i).GetPoints()
        #     for j in range(0, points_cell.GetNumberOfPoints()):
        #         x, y, z = points_cell.GetPoint(j)
        #         # print x, y, z, array.GetTuple(j)
        #         x, y, z = int(round(x)), int(round(y)), int(round(z))
        #         tomo[x, y, z] = False
        #         tomon[x, y, z, :] = array.GetTuple(j)
        for i in range(tsurf.GetNumberOfCells()):
            points_cell = tsurf.GetCell(i).GetPoints()
            for j in range(0, points_cell.GetNumberOfPoints()):
                x, y, z = points_cell.GetPoint(j)
                # print x, y, z, array.GetTuple(j)
                x, y, z = int(round(x)), int(round(y)), int(round(z))
                if tomo[x, y, z] == lbl:
                    tomoh[x, y, z] = False
                    tomon[x, y, z, :] = array.GetTuple(i)

        # Distance transform
        tomod, ids = scipy.ndimage.morphology.distance_transform_edt(
            tomoh, return_indices=True)

        # Compute polarity
        if mode_2d:
            for x in range(nx):
                for y in range(ny):
                    for z in range(nz):
                        i_x, i_y, i_z = (ids[0, x, y, z], ids[1, x, y, z],
                                         ids[2, x, y, z])
                        norm = tomon[i_x, i_y, i_z]
                        norm[2] = 0
                        pnorm = (i_x, i_y, 0)
                        p = (x, y, 0)
                        dprod = dot_norm(np.asarray(p, dtype=np.float),
                                         np.asarray(pnorm, dtype=np.float),
                                         np.asarray(norm, dtype=np.float))
                        tomod[x, y, z] = tomod[x, y, z] * np.sign(dprod)
        else:
            for x in range(nx):
                for y in range(ny):
                    for z in range(nz):
                        i_x, i_y, i_z = (ids[0, x, y, z], ids[1, x, y, z],
                                         ids[2, x, y, z])
                        hold_norm = tomon[i_x, i_y, i_z]
                        norm = hold_norm
                        # norm[0] = (-1) * hold_norm[1]
                        # norm[1] = hold_norm[0]
                        # norm[2] = hold_norm[2]
                        pnorm = (i_x, i_y, i_z)
                        p = (x, y, z)
                        dprod = dot_norm(np.asarray(pnorm, dtype=np.float),
                                         np.asarray(p, dtype=np.float),
                                         np.asarray(norm, dtype=np.float))
                        tomod[x, y, z] = tomod[x, y, z] * np.sign(dprod)

        if verbose:
            print 'Distance field generated...'

        return tsurf, tomod

    if verbose:
        print 'Finished!'

    return tsurf