Ejemplo n.º 1
0
def _prepare_classif_for_VipHomotopic_Cortical(classif, filling_size):
    array_classif = np.asarray(classif)

    # Dilate the cortex by 1 voxel, otherwise the topologically spherical front
    # initialized by VipHomotopic (Cortical surface mode) is at the inside of
    # the border, which means that the final segmentation will be one voxel
    # off. The default -fclosing parameter prevents this from happening in
    # sulci by closing them, but it still happens at the top of gyri. Setting a
    # fake voxel size of 1 mm is a trick for expressing the dilation in terms
    # of voxels.
    #
    # The 1-voxel border is necessary for AimsMorpho{Dilation,Erosion}.
    aimsdata_classif = aims.Volume_S16(classif.getSize(), [1, 1, 1])
    aimsdata_classif[:] = classif[:]
    saved_voxel_size = classif.header()["voxel_size"][:3]
    aimsdata_classif.setVoxelSize(1, 1, 1)
    dilated = aimsalgo.AimsMorphoDilation(aimsdata_classif, 1)
    # Restore the voxel size in case the header is shared with the aims.Volume
    # that aimsdata_classif was created from (classif). BUG: restoring the
    # value like this is not thread-safe!
    aimsdata_classif.setVoxelSize(saved_voxel_size)
    del aimsdata_classif
    array_dilated = dilated.np

    tmp_classif = aims.Volume(classif)
    array_tmp_classif = np.asarray(tmp_classif)
    array_tmp_classif[np.logical_and(array_tmp_classif == CSF_LABEL,
                                     array_dilated != 0)] = CORTEX_LABEL
    del dilated, array_dilated

    # This almost-white region will serve as a first boundary in VipHomotopic,
    # before the final dilation towards the white matter. This helps restore
    # the continuity of the white matter so that the homotopic criterion
    # chooses the right connections (i.e. do not create spurious strands or
    # planes through the cortex).
    white = aims.Volume_S16(classif.gteSize(), [1, 1, 1])
    white[:] = classif[:]
    # Restore the header (in particular the voxel_size), which may not have
    # been copied in the constructor because a border is requested.
    white.header().update(classif.header())
    array_white = white.np
    array_white[array_white != WHITE_LABEL] = CSF_LABEL
    dilated_white = aimsalgo.AimsMorphoDilation(white, filling_size)
    del white, array_white
    array_dilated_white = dilated_white.np
    STEP1_FRONT_BARRIER = 199
    array_tmp_classif[array_dilated_white != 0] = STEP1_FRONT_BARRIER
    del dilated_white, array_dilated_white
    array_tmp_classif[array_classif == WHITE_LABEL] = WHITE_LABEL

    return tmp_classif
Ejemplo n.º 2
0
def execution(self, context):
    from soma import aims
    graph = aims.read(self.read.fullPath())
    aims.GraphManip.buckets2Volume(graph)
    if self.extract_volume:
        vol = graph[self.extract_volume]
    else:
        atts = [
            x for x in graph.keys()
            if isinstance(graph[x], aims.rc_ptr_Volume_S16)
        ]
        if len(atts) == 0:
            raise RuntimeError(_t_('the ROI graph contains no voxel data'))
        elif len(atts) > 1:
            raise RuntimeError(
                _t_('the ROI graph contains several volumes. '
                    'Select the extract_volume parameter as one in ') + '( ' +
                ', '.join(atts) + ' )')
        vol = graph[atts[0]]
    # handle bounding box which may have cropped the data
    bmin = graph['boundingbox_min']
    bmax = graph['boundingbox_max']
    context.write('vol:', vol)
    context.write('bmin:', bmin, ', bmax:', bmax, ', size:', vol.getSize())
    if bmin[:3] != [0, 0, 0] \
            or bmax[:3] != [x+1 for x in vol.getSize()[:3]]:
        context.write('enlarging')
        # needs expanding in a bigger volume
        vol2 = aims.Volume_S16(bmax[0] + 1, bmax[1] + 1, bmax[2] + 1)
        vol2.fill(0)
        ar = vol2.np
        ar[bmin[0]:bmax[0] + 1, bmin[1]:bmax[1] + 1, bmin[2]:bmax[2] + 1, :] \
            = vol.np

        if self.extract_contours == 'Yes':
            ar_copy = ar.copy()
            for label in [v['roi_label'] for v in graph.vertices()]:
                ind = list(zip(*np.where(ar_copy == label)))
                for i in ind:
                    erase = True
                    for neigh in neighbors(*i):
                        if ar_copy[neigh] != label:
                            erase = False
                    if erase:
                        ar[i] = 0

        vol2.copyHeaderFrom(vol.header())
        aims.write(vol2, self.write.fullPath())
    else:
        # bounding box OK
        aims.write(vol.get(), self.write.fullPath())
    registration.getTransformationManager().copyReferential(
        self.read, self.write)
    if self.removeSource:
        for f in self.read.fullPaths():
            shelltools.rm(f)
Ejemplo n.º 3
0
def subject_labeling(gfile, dict_bck, translation, mask, vol_size, n_opal,
                     distmap_list, bck_list, proba_list, label_list,
                     patch_sizes):
    '''
    Label a subject sulcal graph (.arg file) for a specific pattern search using the SNIPE method
    '''
    print('Labeling %s' % gfile)
    distmap_list = [aims.Volume(d) for d in distmap_list]
    # Extract bucket
    fm = aims.FastMarching()
    if gfile not in dict_bck:
        graph = aims.read(gfile)
        side = gfile[gfile.rfind('/') + 1:gfile.rfind('/') + 2]
        data = extract_data(graph, flip=True if side == 'R' else False)
        sbck = data['bck2']
    else:
        sbck = dict_bck[gfile]
    sbck = np.array(sbck)
    sbck += translation
    sbck = np.asarray(apply_imask(sbck, mask))

    # Extract distmap
    fm = aims.FastMarching()
    vol = aims.Volume_S16(vol_size[0], vol_size[1], vol_size[2])
    vol.fill(0)
    for p in sbck:
        vol[p[0], p[1], p[2]] = 1
    sdistmap = fm.doit(vol, [0], [1])
    adistmap = np.asarray(sdistmap)
    adistmap[adistmap > pow(10, 10)] = 0

    # Compute classification
    grading_list = []
    for ps in patch_sizes:
        print('** PATCH SIZE %i' % ps)
        opm = OptimizedPatchMatch(patch_size=[ps, ps, ps],
                                  segmentation=False,
                                  k=n_opal)
        list_dfann = opm.run(distmap=sdistmap,
                             distmap_list=distmap_list,
                             bck_list=bck_list,
                             proba_list=proba_list)
        grading_list.append(grading(list_dfann, label_list))

    grade = np.nanmean(np.mean(grading_list, axis=0))
    ypred = 1 if grade > 0 else 0
    return ypred, grade
Ejemplo n.º 4
0
    def subject_labeling(self, gfile):
        print('Labeling %s' % gfile)
        # Extract bucket
        fm = aims.FastMarching()
        if gfile not in self.dict_bck:
            graph = aims.read(gfile)
            side = gfile[gfile.rfind('/') + 1:gfile.rfind('/') + 2]
            data = extract_data(graph, flip=True if side == 'R' else False)
            sbck = data['bck2']
        else:
            sbck = self.dict_bck[gfile]
        sbck = np.array(sbck)
        sbck += self.translation
        sbck = np.asarray(apply_imask(sbck, self.amask))

        # Extract distmap
        fm = aims.FastMarching()
        vol = aims.Volume_S16(self.vol_size[0], self.vol_size[1],
                              self.vol_size[2])
        vol.fill(0)
        for p in sbck:
            vol[p[0], p[1], p[2]] = 1
        sdistmap = fm.doit(vol, [0], [1])
        adistmap = np.asarray(sdistmap)
        adistmap[adistmap > pow(10, 10)] = 0

        # Compute classification
        grading_list = []
        for ps in self.patch_sizes:
            print('** PATCH SIZE %i' % ps)
            opm = OptimizedPatchMatch(patch_size=[ps, ps, ps],
                                      segmentation=False,
                                      k=self.n_opal)
            list_dfann = opm.run(distmap=sdistmap,
                                 distmap_list=self.distmap_list,
                                 bck_list=self.bck_list,
                                 proba_list=self.proba_list)
            grading_list.append(grading(list_dfann, self.label_list))

        grade = np.nanmean(np.mean(grading_list, axis=0))
        ypred = 1 if grade > 0 else 0
        return ypred, grade
Ejemplo n.º 5
0
    def learning(self, gfile_list):
        self.bck_list, self.label_list, self.distmap_list = [], [], []
        # Extract buckets and labels from the graphs
        for gfile in gfile_list:
            if gfile not in self.dict_bck:
                graph = aims.read(gfile)
                side = gfile[gfile.rfind('/') + 1:gfile.rfind('/') + 2]
                data = extract_data(graph, flip=True if side == 'R' else False)
                label = 0
                fn = []
                for name in data['names']:
                    if name.startswith(self.pattern):
                        label = 1
                    fn.append(
                        sum([
                            1 for n in self.names_filter if name.startswith(n)
                        ]))
                bck_filtered = np.asarray(data['bck2'])[np.asarray(fn) == 1]

                # save data
                self.dict_bck[gfile] = data['bck2']
                self.dict_label[gfile] = label
                self.dict_bck_filtered[gfile] = bck_filtered
            self.bck_list.append(self.dict_bck[gfile])
            self.label_list.append(self.dict_label[gfile])

        # Compute volume size + mask
        bb = np.array([[100., -100.], [100., -100.], [100., -100.]])
        for gfile in gfile_list:
            abck = np.asarray(self.dict_bck[gfile])
            for i in range(3):
                bb[i, 0] = min(bb[i, 0], int(min(abck[:, i])) - 1)
                bb[i, 1] = max(bb[i, 1], int(max(abck[:, i])) + 1)
        self.translation = [-int(bb[i, 0]) + 11 for i in range(3)]
        self.vol_size = [int((bb[i, 1] - bb[i, 0])) + 22 for i in range(3)]

        # Compute distmap volumes + mask
        fm = aims.FastMarching()
        vol_filtered = aims.Volume_S16(self.vol_size[0], self.vol_size[1],
                                       self.vol_size[2])
        for gfile in gfile_list:
            bck = np.asarray(self.dict_bck[gfile]) + self.translation
            bck_filtered = np.asarray(self.dict_bck_filtered[gfile])
            if len(bck_filtered) != 0:
                bck_filtered += self.translation
            # compute distmap
            vol = aims.Volume_S16(self.vol_size[0], self.vol_size[1],
                                  self.vol_size[2])
            vol.fill(0)
            for p in bck:
                vol[p[0], p[1], p[2]] = 1
            distmap = fm.doit(vol, [0], [1])
            adistmap = np.asarray(distmap)
            adistmap[adistmap > pow(10, 10)] = 0
            self.distmap_list.append(distmap)
            # compute mask
            for p in bck_filtered:
                vol_filtered[p[0], p[1], p[2]] = 1
        dilation = aimsalgo.MorphoGreyLevel_S16()
        mask = dilation.doDilation(vol_filtered, 5)
        self.amask = np.asarray(mask)

        # Compute proba_list
        y_train = self.label_list
        class_sample_count = np.array(
            [len(np.where(y_train == t)[0]) for t in np.unique(y_train)])
        w = [
            max(1., class_sample_count[1] / float(class_sample_count[0])),
            max(1., class_sample_count[0] / float(class_sample_count[1]))
        ]
        self.proba_list = [w[yi] for yi in y_train]
Ejemplo n.º 6
0
def fix_cortex_topology(input_classif, filling_size=2., fclosing=10.):
    """Fix the topology of a cortical segmentation.

    The topology of a hollow sphere is imposed onto a voxelwise segmentation of
    the cortex, which consists of the following labels:

    Label 0 (`CSF_LABEL`)
        Outside of the cortex, corresponding to the cerebrospinal fluid,
        defined in 26-connectivity.

    Label 100 (`CORTEX_LABEL`)
        The cortex itself, defined using 6-connectivity.

    Label 200 (`WHITE_LABEL`)
        Inside of the cortex, corresponds to the white matter,
        defined in 26-connectivity.


    Parameters
    ----------

    classif: aims.Volume
        The input voxelwise classification.

    filling_size: float
        The size, in millimetres, of the largest holes in either cortical
        boundary that will be filled. This must be kept smaller than the
        smallest cortical thickness in the image (see `Method` below for a more
        precise description of this parameter). The default value is 2 mm,
        which is appropriate for a human brain.

    fclosing: float
        The radius, in millimetres, of the morphological closing which is used
        by VipHomotopic in Cortical surface mode to retrieve the brain's outer
        envelope. The default value, 10 mm, is appropriate for a human brain.

    Returns
    -------

    The topology-corrected voxelwise classification is returned in an
    `aims.Volume_S16`.

    Raises
    ------

    OSError
        This function throws ``OSError`` if ``VipHomotopic`` cannot be found
        or executed.

    soma.subprocess.CalledProcessError
        This exception can occur if ``VipHomotopic``, which is in charge of the
        homotopic morphological operations, terminates with an error.


    Environment
    -----------

    This function needs the ``VipHomotopic`` command from the Morphologist
    image segmentation pipeline to reside in the ``PATH``. Note that the
    original ``VipHomotopic`` has hard-coded limits on the number of iterations
    for morphological operations, which may be exceeded when working on
    high-resolution (sub-millimetre) images.

    Input/output
    ------------

    The command ``VipHomotopic``, which is used to perform the homotopic
    morphological operations, reports progress on stdout/stderr.

    Images are passed to ``VipHomotopic`` using files under a temporary
    directory allocated with `tempfile.mkdtemp`.

    Method
    ------

    The topology correction is done in two main steps:

    1. A topologically spherical bounding box of the brain is computed and
    dilated towards the inside until it reaches the white matter. This
    retrieves a topologically correct object which fits the grey--white
    boundary.

    2. The previous object is eroded from the inside in the region where it
    overlaps with the cortex. This retrieves a topologically correct pial
    boundary.

    Each of these main steps is performed in two sub-steps: first the homotopic
    morpholological operation is performed until a boundary which is dilated by
    `filling_size`, then to the original boundary. This guides the front
    propagation, in order to prevent the formation of spurious strands.

    This method will change voxels from the cortex class to either the white
    matter or the CSF class, as needed to ensure the topology.

    Note that the output is not a deterministic function of the input, because
    the homotopic operations use a pseudo-random order for the front
    propagation.
    """
    fclosing = float(fclosing)
    assert fclosing >= 0
    filling_size = float(filling_size)
    assert filling_size >= 0

    # VipHomotopic only works with 16-bit signed integer voxels.
    conv = aims.ShallowConverter(intype=input_classif, outtype="Volume_S16")
    classif = conv(input_classif)

    tmp_classif = _prepare_classif_for_VipHomotopic_Cortical(
        classif, filling_size)

    tmp_dir = None
    try:
        tmp_dir = tempfile.mkdtemp(prefix="highres-cortex.")
        aims.write(tmp_classif, os.path.join(tmp_dir, "tmp_classif.nii.gz"))
        del tmp_classif
        with open(os.path.join(tmp_dir, "fake.han"), "w") as f:
            f.write("sequence: unknown\n"
                    "gray: mean: 120 sigma: 10\n"
                    "white: mean: 433 sigma: 10\n")
        # VipHomotopic in Cortical surface mode retrieves a spherical
        # grey--white boundary by iteratively eroding the bounding box of the
        # cortex in a homotopic manner. It will proceed in two steps, first
        # stopping at STEP1_FRONT_BARRIER, and finally at WHITE_LABEL.
        subprocess.check_call([
            "VipHomotopic", "-mode", "C", "-input", "tmp_classif.nii.gz",
            "-classif", "tmp_classif.nii.gz", "-hana", "fake.han", "-fclosing",
            repr(fclosing), "-output", "cortex.nii.gz"
        ],
                              cwd=tmp_dir)

        aims.write(classif, os.path.join(tmp_dir, "classif.nii.gz"))

        # First boundary to guide VipHomotopic (prevent leaking through holes
        # in sulci).
        aimsdata_classif = aims.Volume_S16(classif.getSize, [1, 1, 1])
        aimsdata_classif[:] = classif[:]
        # Restore the header (in particular the voxel_size), which may not have
        # been copied in the constructor because a border is requested.
        aimsdata_classif.header().update(classif.header())
        eroded = aimsalgo.AimsMorphoErosion(aimsdata_classif, filling_size)
        del classif, aimsdata_classif
        aims.write(eroded, os.path.join(tmp_dir, "eroded.nii.gz"))
        del eroded

        # The spherical grey--white boundary is dilated in a homotopic manner
        # until the border of eroded_classif is reached.
        subprocess.check_call([
            "VipHomotopic", "-mode", "H", "-input", "eroded.nii.gz", "-cortex",
            "cortex.nii.gz", "-fclosing", "0", "-output", "bigsulci.nii.gz"
        ],
                              cwd=tmp_dir)
        subprocess.check_call([
            "VipHomotopic", "-mode", "H", "-input", "classif.nii.gz",
            "-cortex", "bigsulci.nii.gz", "-fclosing", "0", "-output",
            "pial_surface.nii.gz"
        ],
                              cwd=tmp_dir)

        cortex = aims.read(os.path.join(tmp_dir, "cortex.nii.gz"))
        pial_surface = aims.read(os.path.join(tmp_dir, "pial_surface.nii.gz"))
    finally:
        shutil.rmtree(tmp_dir, ignore_errors=True)
    array_cortex = np.asarray(cortex)
    array_pial_surface = np.asarray(pial_surface)

    array_cortex[array_cortex == 0] = 200
    array_cortex[array_cortex == 255] = 100
    array_cortex[array_pial_surface != 0] = 0
    return cortex
Ejemplo n.º 7
0
def execution(self, context):

    fic = self.file_of_point
    a = aims.Volume_S16(self.dimX, self.dimY, self.dimZ)
    a.setVoxelSize(self.sizeX, self.sizeY, self.sizeZ)
    # mettre les valeurs :

    fic = open(self.file_of_point.fullPath(), 'r')
    tmp = 'x'
    b = 1  # en cas d'erreurs
    while tmp != '':
        tmp = fic.readline()
        tab = tmp.split()

        if len(tab) == 4:

            v = tab[0]
            v = int(v)

            x = tab[1]
            x = int(x)

            y = tab[2]
            y = int(y)

            z = tab[3]
            z = int(z)

            if (x <= self.dimX) and (y <= self.dimY) and (z <= self.dimZ):
                a.setValue(v, x, y, z)
            elif (x > self.dimX):
                context.write(
                    'dimX is strong - too long - perhaps "file_of_point" contains error'
                )
                b = 0
                break
            elif (y > self.dimY):
                context.write(
                    'dimY is strong - too long - perhaps "file_of_point" contains error'
                )
                b = 0
                break
            elif (z > self.dimZ):
                context.write(
                    'dimZ is strong - too long - perhaps "file_of_point" contains error'
                )
                b = 0
                break

    fic.close()

    # write image
    if (b == 1):

        context.write('write image')
        aims.write(a, self.image_output.fullPath())

        if self.create_roigraph == 'yes':
            if self.graph_output is not None:
                command = [
                    'AimsGraphConvert', '-i', self.image_output, '-o',
                    self.graph_output, '--roi', '--bucket'
                ]
                context.system(*command)
            else:
                context.write(
                    'graph_output is mandatory because you are chose create_roigraph=yes !'
                )
    else:

        context.write('correct error and try again')
Ejemplo n.º 8
0
    def _run_process(self):
        # Compute voronoi
        mri = aims.read(self.t1mri)
        true_graph = aims.read(self.true_graph)
        vs = true_graph['voxel_size']
        vvol = vs[0] * vs[1] * vs[2]
        true_bck, true_names, _ = extract_data(true_graph)
        nlist = list(set(true_names))
        dnames = {k: v + 1 for k, v in zip(nlist, range(len(nlist)))}
        dnum = {v + 1: k for k, v in zip(nlist, range(len(nlist)))}
        fm = aims.FastMarching()
        vol = aims.Volume_S16(mri.getSizeX(), mri.getSizeY(), mri.getSizeZ())
        vol.fill(0)
        for p, n in zip(true_bck, true_names):
            vol[p[0], p[1], p[2]] = dnames[n]

        fm.doit(vol, [0], list(dnames.values()))
        vor = fm.voronoiVol()

        # Compute error rates
        re = pd.DataFrame(index=[str(g) for g in self.labeled_graphs])
        for gfile in self.labeled_graphs:
            graph = aims.read(gfile)
            bck, _, labels = extract_data(graph)
            y_pred = [
                vor[int(round(p[0])),
                    int(round(p[1])),
                    int(round(p[2]))][0] for p in bck
            ]
            names = np.asarray([dnum[n] for n in y_pred])

            for ss in self.sulci_side_list:
                names_ss = labels[names == ss]
                labels_ss = names[labels == ss]

                re.ix[gfile, 'TP_' +
                      str(ss)] = float(len(names_ss[names_ss == ss])) * vvol
                re.ix[gfile, 'FP_' +
                      str(ss)] = float(len(labels_ss[labels_ss != ss])) * vvol
                re.ix[gfile, 'FN_' +
                      str(ss)] = float(len(names_ss[names_ss != ss])) * vvol
                re.ix[gfile, 's_' + str(ss)] = float(len(names_ss)) * vvol

            sum_s = sum(
                [re.ix[gfile, 's_' + str(ss)] for ss in self.sulci_side_list])
            for ss in self.sulci_side_list:
                FP = re.ix[gfile, 'FP_' + str(ss)]
                FN = re.ix[gfile, 'FN_' + str(ss)]
                VP = re.ix[gfile, 'TP_' + str(ss)]
                s = re.ix[gfile, 's_' + str(ss)]
                if FP + FN + 2 * VP != 0:
                    re.ix[gfile, 'ESI_' +
                          str(ss)] = s / sum_s * (FP + FN) / (FP + FN + 2 * VP)
                    re.ix[gfile, 'Elocal_' +
                          str(ss)] = s / sum_s * (FP + FN) / (FP + FN + VP)
                else:
                    re.ix[gfile, 'ESI_' + str(ss)] = 0
                    re.ix[gfile, 'Elocal_' + str(ss)] = 0

            re.ix[gfile, 'ESI'] = sum([
                re.ix[gfile, 'ESI_' + str(ss)] for ss in self.sulci_side_list
            ])

        re.to_csv(self.error_file)

        print('Mean ESI: %.3f' % re['ESI'].mean())
        print('Max ESI: %.3f' % re['ESI'].max())
        print()
        for ss in self.sulci_side_list:
            print('%s Elocal mean: %.3f, max: %.3f' %
                  (ss, re['Elocal_' + str(ss)].mean(),
                   re['Elocal_' + str(ss)].max()))