Exemple #1
0
def compute_and_visualize_tracts(trekker, position, affine, affine_vtk, n_tracts_max):
    """ Compute tractograms using the Trekker library.

    :param trekker: Trekker library instance
    :type trekker: Trekker.T
    :param position: 3 double coordinates (x, y, z) in list or array
    :type position: list
    :param affine: 4 x 4 numpy double array
    :type affine: numpy.ndarray
    :param affine_vtk: vtkMatrix4x4 isntance with affine transformation matrix
    :type affine_vtk: vtkMatrix4x4
    :param n_tracts_max: maximum number of tracts to compute
    :type n_tracts_max: int
    """

    # root = vtk.vtkMultiBlockDataSet()
    # Juuso's
    # seed = np.array([[-8.49, -8.39, 2.5]])
    # Baran M1
    # seed = np.array([[27.53, -77.37, 46.42]])
    seed_trk = img_utils.convert_world_to_voxel(position, affine)
    bundle = vtkMultiBlockDataSet()
    n_branches, n_tracts, count_loop = 0, 0, 0
    n_threads = 2 * const.N_CPU - 1

    while n_tracts < n_tracts_max:
        n_param = 1 + (count_loop % 10)
        # rescale the alpha value that defines the opacity of the branch
        # the n interval is [1, 10] and the new interval is [51, 255]
        # the new interval is defined to have no 0 opacity (minimum is 51, i.e., 20%)
        alpha = (n_param - 1) * (255 - 51) / (10 - 1) + 51
        trekker.minFODamp(n_param * 0.01)

        # print("seed example: {}".format(seed_trk))
        trekker.seed_coordinates(np.repeat(seed_trk, n_threads, axis=0))
        # print("trk list len: ", len(trekker.run()))
        trk_list = trekker.run()
        n_tracts += len(trk_list)
        if len(trk_list):
            branch = compute_tracts(trk_list, n_tract=0, alpha=alpha)
            bundle.SetBlock(n_branches, branch)
            n_branches += 1

        count_loop += 1

        if (count_loop == 20) and (n_tracts == 0):
            break

    Publisher.sendMessage('Remove tracts')
    if n_tracts:
        Publisher.sendMessage('Update tracts', root=bundle, affine_vtk=affine_vtk,
                              coord_offset=position, coord_offset_w=seed_trk[0].tolist())
Exemple #2
0
    def run(self):

        trekker, affine, offset, n_tracts_total, seed_radius, n_threads = self.inp
        # as a single block, computes all the maximum number of tracts at once, not optimal for navigation
        n_threads = n_tracts_total
        p_old = np.array([[0., 0., 0.]])
        root = vtk.vtkMultiBlockDataSet()

        # Compute the tracts
        # print('ComputeTractsThread: event {}'.format(self.event.is_set()))
        while not self.event.is_set():
            try:
                coord, m_img, m_img_flip = self.coord_queue.get_nowait()

                # translate the coordinate along the normal vector of the object/coil
                coord_offset = m_img_flip[:3, -1] - offset * m_img_flip[:3, 2]
                # coord_offset = np.array([[27.53, -77.37, 46.42]])
                dist = abs(np.linalg.norm(p_old - np.asarray(coord_offset)))
                p_old = coord_offset.copy()
                seed_trk = img_utils.convert_world_to_voxel(
                    coord_offset, affine)
                # Juuso's
                # seed_trk = np.array([[-8.49, -8.39, 2.5]])
                # Baran M1
                # seed_trk = np.array([[27.53, -77.37, 46.42]])
                # print("Seed: {}".format(seed))

                # set the seeds for trekker, one seed is repeated n_threads times
                # trekker has internal multiprocessing approach done in C. Here the number of available threads is give,
                # but in case a large number of tracts is requested, it will compute all in parallel automatically
                # for a more fluent navigation, better to compute the maximum number the computer handles
                trekker.seed_coordinates(np.repeat(seed_trk, n_threads,
                                                   axis=0))
                # run the trekker, this is the slowest line of code, be careful to just use once!
                trk_list = trekker.run()

                if trk_list:
                    # if the seed is outside the defined radius, restart the bundle computation
                    if dist >= seed_radius:
                        root = tracts_computation(trk_list, root, 0)
                    self.visualization_queue.put_nowait((coord, m_img, root))

                self.coord_queue.task_done()
                time.sleep(self.sle)
            except queue.Empty:
                pass
            except queue.Full:
                self.coord_queue.task_done()
Exemple #3
0
def compute_tracts(trekker, position, affine, affine_vtk, n_tracts):
    """ Compute tractograms using the Trekker library.

    :param trekker: Trekker library instance
    :type trekker: Trekker.T
    :param position: 3 double coordinates (x, y, z) in list or array
    :type position: list
    :param affine: 4 x 4 numpy double array
    :type affine: numpy.ndarray
    :param affine_vtk: vtkMatrix4x4 isntance with affine transformation matrix
    :type affine_vtk: vtkMatrix4x4
    :param n_tracts: number of tracts to compute
    :type n_tracts: int
    """

    # during neuronavigation, root needs to be initialized outside the while loop so the new tracts
    # can be appended to the root block set
    root = vtk.vtkMultiBlockDataSet()
    # Juuso's
    # seed = np.array([[-8.49, -8.39, 2.5]])
    # Baran M1
    # seed = np.array([[27.53, -77.37, 46.42]])
    seed_trk = img_utils.convert_world_to_voxel(position, affine)
    # print("seed example: {}".format(seed_trk))
    trekker.seed_coordinates(np.repeat(seed_trk, n_tracts, axis=0))
    # print("trk list len: ", len(trekker.run()))
    trk_list = trekker.run()
    if trk_list:
        root = tracts_computation(trk_list, root, 0)
        Publisher.sendMessage('Remove tracts')
        Publisher.sendMessage('Update tracts',
                              flag=True,
                              root=root,
                              affine_vtk=affine_vtk)
    else:
        Publisher.sendMessage('Remove tracts')
Exemple #4
0
    def run(self):

        trekker, affine, offset, n_tracts_total, seed_radius, n_threads = self.inp
        # n_threads = n_tracts_total
        p_old = np.array([[0., 0., 0.]])
        n_tracts = 0

        # Compute the tracts
        # print('ComputeTractsThread: event {}'.format(self.event.is_set()))
        while not self.event.is_set():
            try:
                # print("Computing tracts")
                # get from the queue the coordinates, coregistration transformation matrix, and flipped matrix
                m_img_flip = self.coord_tracts_queue.get_nowait()
                # coord, m_img, m_img_flip = self.coord_queue.get_nowait()
                # print('ComputeTractsThread: get {}'.format(count))

                # 20200402: in this new refactored version the m_img comes different than the position
                # the new version m_img is already flixped in y, which means that Y is negative
                # if only the Y is negative maybe no need for the flip_x funtcion at all in the navigation
                # but check all coord_queue before why now the m_img comes different than position
                # 20200403: indeed flip_x is just a -1 multiplication to the Y coordinate, remove function flip_x
                # m_img_flip = m_img.copy()
                # m_img_flip[1, -1] = -m_img_flip[1, -1]

                # translate the coordinate along the normal vector of the object/coil
                coord_offset = m_img_flip[:3, -1] - offset * m_img_flip[:3, 2]
                # coord_offset = np.array([[27.53, -77.37, 46.42]])
                dist = abs(np.linalg.norm(p_old - np.asarray(coord_offset)))
                p_old = coord_offset.copy()

                # print("p_new_shape", coord_offset.shape)
                # print("m_img_flip_shape", m_img_flip.shape)
                seed_trk = img_utils.convert_world_to_voxel(
                    coord_offset, affine)
                # Juuso's
                # seed_trk = np.array([[-8.49, -8.39, 2.5]])
                # Baran M1
                # seed_trk = np.array([[27.53, -77.37, 46.42]])
                # print("Seed: {}".format(seed))

                # set the seeds for trekker, one seed is repeated n_threads times
                # trekker has internal multiprocessing approach done in C. Here the number of available threads is give,
                # but in case a large number of tracts is requested, it will compute all in parallel automatically
                # for a more fluent navigation, better to compute the maximum number the computer handles
                trekker.seed_coordinates(np.repeat(seed_trk, n_threads,
                                                   axis=0))

                # run the trekker, this is the slowest line of code, be careful to just use once!
                trk_list = trekker.run()

                if trk_list:
                    # print("dist: {}".format(dist))
                    if dist >= seed_radius:
                        # when moving the coil further than the seed_radius restart the bundle computation
                        bundle = vtk.vtkMultiBlockDataSet()
                        n_branches = 0
                        branch = tracts_computation_branch(trk_list)
                        bundle.SetBlock(n_branches, branch)
                        n_branches += 1
                        n_tracts = branch.GetNumberOfBlocks()

                    # TODO: maybe keep computing even if reaches the maximum
                    elif dist < seed_radius and n_tracts < n_tracts_total:
                        # compute tracts blocks and add to bungle until reaches the maximum number of tracts
                        branch = tracts_computation_branch(trk_list)
                        if bundle:
                            bundle.SetBlock(n_branches, branch)
                            n_tracts += branch.GetNumberOfBlocks()
                            n_branches += 1

                else:
                    bundle = None

                # rethink if this should be inside the if condition, it may lock the thread if no tracts are found
                # use no wait to ensure maximum speed and avoid visualizing old tracts in the queue, this might
                # be more evident in slow computer or for heavier tract computations, it is better slow update
                # than visualizing old data
                # self.visualization_queue.put_nowait([coord, m_img, bundle])
                self.tracts_queue.put_nowait(
                    (bundle, self.affine_vtk, coord_offset))
                # print('ComputeTractsThread: put {}'.format(count))

                self.coord_tracts_queue.task_done()
                # self.coord_queue.task_done()
                # print('ComputeTractsThread: done {}'.format(count))

                # sleep required to prevent user interface from being unresponsive
                time.sleep(self.sle)
            # if no coordinates pass
            except queue.Empty:
                # print("Empty queue in tractography")
                pass
            # if queue is full mark as done (may not be needed in this new "nowait" method)
            except queue.Full:
                # self.coord_queue.task_done()
                self.coord_tracts_queue.task_done()
Exemple #5
0
    def run(self):

        # trekker, affine, offset, n_tracts_total, seed_radius, n_threads = self.inp
        trekker, affine, offset, n_tracts_total, seed_radius, n_threads, act_data, affine_vtk, img_shift = self.inp

        # n_threads = n_tracts_total
        p_old = np.array([[0., 0., 0.]])
        p_old_pre = np.array([[0., 0., 0.]])
        coord_offset = None
        n_tracts = 0
        n_branches = 0
        bundle = None
        sph_sampling = True
        dist_radius = 1.5

        xyz = img_utils.random_sample_sphere(radius=seed_radius, size=100)
        coord_list_sph = np.hstack([xyz, np.ones([xyz.shape[0], 1])]).T
        m_seed = np.identity(4)
        # Compute the tracts
        # print('ComputeTractsThread: event {}'.format(self.event.is_set()))
        while not self.event.is_set():
            try:
                # print("Computing tracts")
                # get from the queue the coordinates, coregistration transformation matrix, and flipped matrix
                m_img_flip = self.coord_tracts_queue.get_nowait()
                # coord, m_img, m_img_flip = self.coord_queue.get_nowait()
                # print('ComputeTractsThread: get {}'.format(count))

                # TODO: Remove this is not needed
                # 20200402: in this new refactored version the m_img comes different than the position
                # the new version m_img is already flixped in y, which means that Y is negative
                # if only the Y is negative maybe no need for the flip_x funtcion at all in the navigation
                # but check all coord_queue before why now the m_img comes different than position
                # 20200403: indeed flip_x is just a -1 multiplication to the Y coordinate, remove function flip_x
                # m_img_flip = m_img.copy()
                # m_img_flip[1, -1] = -m_img_flip[1, -1]

                # DEBUG: Uncomment the m_img_flip below so that distance is fixed and tracts keep computing
                # m_img_flip[:3, -1] = (5., 10., 12.)
                dist = abs(
                    np.linalg.norm(p_old_pre - np.asarray(m_img_flip[:3, -1])))
                p_old_pre = m_img_flip[:3, -1].copy()

                # Uncertanity visualization  --
                # each tract branch is computed with one set of parameters ajusted from 1 to 10
                n_param = 1 + (n_branches % 10)
                # rescale the alpha value that defines the opacity of the branch
                # the n interval is [1, 10] and the new interval is [51, 255]
                # the new interval is defined to have no 0 opacity (minimum is 51, i.e., 20%)
                alpha = (n_param - 1) * (255 - 51) / (10 - 1) + 51
                trekker.minFODamp(n_param * 0.01)
                trekker.dataSupportExponent(n_param * 0.1)
                # ---

                # When moving the coil further than the seed_radius restart the bundle computation
                # Currently, it stops to compute tracts when the maximum number of tracts is reached maybe keep
                # computing even if reaches the maximum
                if dist >= dist_radius:
                    # Anatomic constrained seed computation ---
                    # The original seed location is replaced by the gray-white  matter interface that is closest to
                    # the coil center
                    try:
                        #TODO: Create a dialog error to say when the ACT data is not loaded and prevent
                        # the interface from freezing. Give the user a chance to load it (maybe in task_navigator)
                        coord_list_w_tr = m_img_flip @ self.coord_list_w
                        coord_offset = grid_offset(act_data, coord_list_w_tr,
                                                   img_shift)
                    except IndexError:
                        # This error might be caused by the coordinate exceeding the image array dimensions.
                        # Needs further verification.
                        coord_offset = None
                    # ---

                    # Translate the coordinate along the normal vector of the object/coil ---
                    if coord_offset is None:
                        # apply the coil transformation matrix
                        coord_offset = m_img_flip[:3,
                                                  -1] - offset * m_img_flip[:3,
                                                                            2]
                    # ---

                    # convert the world coordinates to the voxel space for using as a seed in Trekker
                    # seed_trk.shape == [1, 3]
                    seed_trk = img_utils.convert_world_to_voxel(
                        coord_offset, affine)
                    # print("Desired: {}".format(seed_trk.shape))

                    # DEBUG: uncomment the seed_trk below
                    # Juuso's
                    # seed_trk = np.array([[-8.49, -8.39, 2.5]])
                    # Baran M1
                    # seed_trk = np.array([[27.53, -77.37, 46.42]])
                    # print("Given: {}".format(seed_trk.shape))
                    # print("Seed: {}".format(seed))

                    # Spherical sampling of seed coordinates ---
                    if sph_sampling:
                        # CHECK: We use ACT only for the origin seed, but not for all the other coordinates.
                        # Check how this can be solved. Applying ACT to all coordinates is maybe too much.
                        # Maybe it doesn't matter because the ACT is just to help finding the closest location to
                        # the TMS coil center. Also, note that the spherical sampling is applied only when the coil
                        # location changes, all further iterations used the fixed n_threads samples to compute the
                        # remaining tracts.

                        # samples = np.random.choice(self.sph_idx, size=n_threads, p=self.pdf)
                        # m_seed[:-1, -1] = seed_trk
                        # sph_seed = m_seed @ self.coord_list_sph
                        # seed_trk_r = sph_seed[samples, :]
                        samples = np.random.choice(coord_list_sph.shape[1],
                                                   size=n_threads)
                        m_seed[:-1, -1] = seed_trk
                        seed_trk_r = m_seed @ coord_list_sph[:, samples]
                        seed_trk_r = seed_trk_r[:-1, :].T
                    else:
                        # set the seeds for trekker, one seed is repeated n_threads times
                        # shape (24, 3)
                        seed_trk_r = np.repeat(seed_trk, n_threads, axis=0)

                    # ---

                    # Trekker has internal multiprocessing approach done in C. Here the number of available threads is
                    # given, but in case a large number of tracts is requested, it will compute all in parallel
                    # automatically for a more fluent navigation, better to compute the maximum number the computer
                    # handles
                    trekker.seed_coordinates(seed_trk_r)
                    # trekker.seed_coordinates(np.repeat(seed_trk, n_threads, axis=0))

                    # run the trekker, this is the slowest line of code, be careful to just use once!
                    trk_list = trekker.run()

                    # check if any tract was found, otherwise doesn't count
                    if trk_list:
                        bundle = vtk.vtkMultiBlockDataSet()
                        branch = tracts_computation_branch(trk_list, alpha)
                        bundle.SetBlock(n_branches, branch)
                        n_branches = 1
                        n_tracts = branch.GetNumberOfBlocks()
                    else:
                        bundle = None
                        n_branches = 0
                        n_tracts = 0

                elif dist < dist_radius and n_tracts < n_tracts_total:
                    trk_list = trekker.run()
                    if trk_list:
                        # compute tract blocks and add to bundle until reaches the maximum number of tracts
                        # the alpha changes depending on the parameter set
                        branch = tracts_computation_branch(trk_list, alpha)
                        if bundle:
                            bundle.SetBlock(n_branches, branch)
                            n_tracts += branch.GetNumberOfBlocks()
                            n_branches += 1
                        else:
                            bundle = vtk.vtkMultiBlockDataSet()
                            bundle.SetBlock(n_branches, branch)
                            n_branches = 1
                            n_tracts = branch.GetNumberOfBlocks()
                    # else:
                    #     bundle = None

                # else:
                #     bundle = None

                # rethink if this should be inside the if condition, it may lock the thread if no tracts are found
                # use no wait to ensure maximum speed and avoid visualizing old tracts in the queue, this might
                # be more evident in slow computer or for heavier tract computations, it is better slow update
                # than visualizing old data
                # self.visualization_queue.put_nowait([coord, m_img, bundle])
                self.tracts_queue.put_nowait(
                    (bundle, affine_vtk, coord_offset))
                # print('ComputeTractsThread: put {}'.format(count))

                self.coord_tracts_queue.task_done()
                # self.coord_queue.task_done()
                # print('ComputeTractsThread: done {}'.format(count))

                # sleep required to prevent user interface from being unresponsive
                time.sleep(self.sle)
            # if no coordinates pass
            except queue.Empty:
                # print("Empty queue in tractography")
                pass
            # if queue is full mark as done (may not be needed in this new "nowait" method)
            except queue.Full:
                # self.coord_queue.task_done()
                self.coord_tracts_queue.task_done()