コード例 #1
0
ファイル: mapping.py プロジェクト: balbasty/nitorch
    def _init(self, **kwargs):
        for key, val in kwargs:
            setattr(self, key, val)

        if self.permutation is None:
            self.permutation = tuple(range(self._dim))

        if self.slicer is None:
            # same layout as on-disk
            self.spatial = self._spatial
            self.affine = self._affine
            self.shape = self._shape
            self.slicer = expand_index([Ellipsis], self._shape)

        return self
コード例 #2
0
ファイル: mapping.py プロジェクト: balbasty/nitorch
    def slice(self, index, new_shape=None, _pre_expanded=False):
        """Extract a sub-part of the array.

        Indices can only be slices, ellipses, integers or None.

        Parameters
        ----------
        index : tuple[slice or ellipsis or int or None]

        Other Parameters
        ----------------
        new_shape : sequence[int], optional
            Output shape of the sliced object
        _pre_expanded : bool, default=False
            Set to True of `expand_index` has already been called on `index`

        Returns
        -------
        subarray : type(self)
            MappedArray object, with the indexing operations and affine
            matrix relating to the new sub-array.

        """
        index = expand_index(index, self.shape)
        new_shape = guess_shape(index, self.shape)
        if any(isinstance(idx, list) for idx in index) > 1:
            raise ValueError('List indices not currently supported '
                             '(otherwise we enter advanced indexing '
                             'territory and it becomes too complicated).')
        new = copy(self)
        new.shape = new_shape

        # compute new affine
        if self.affine is not None:
            spatial_shape = [
                sz for sz, msk in zip(self.shape, self.spatial) if msk
            ]
            spatial_index = [idx for idx in index if not is_newaxis(idx)]
            spatial_index = [
                idx for idx, msk in zip(spatial_index, self.spatial) if msk
            ]
            affine, _ = affine_sub(self.affine, spatial_shape,
                                   tuple(spatial_index))
        else:
            affine = None
        new.affine = affine

        # compute new slicer
        perm_shape = [self._shape[d] for d in self.permutation]
        new.slicer = compose_index(self.slicer, index, perm_shape)

        # compute new spatial mask
        spatial = []
        i = 0
        for idx in new.slicer:
            if is_newaxis(idx):
                spatial.append(False)
            else:
                # original axis
                if not is_droppedaxis(idx):
                    spatial.append(self._spatial[self.permutation[i]])
                i += 1
        new.spatial = tuple(spatial)

        return new
コード例 #3
0
ファイル: mapping.py プロジェクト: balbasty/nitorch
    def slice(self, index, new_shape=None):
        # overload slicer -> slice individual arrays
        index = expand_index(index, self.shape)
        new_shape = guess_shape(index, self.shape)
        assert len(index) > 0, "index should never be empty here"
        if any(isinstance(idx, list) for idx in index) > 1:
            raise ValueError('List indices not currently supported '
                             '(otherwise we enter advanced indexing '
                             'territory and it becomes too complicated).')
        index = list(index)
        shape_cat = self.shape[self._dim_cat]

        # find out which index corresponds to the concatenated dimension
        # + compute the concatenated dimension in the output array
        new_dim_cat = self._dim_cat
        nb_old_dim = -1
        for map_dim_cat, idx in enumerate(index):
            if is_newaxis(idx):
                # an axis was added: dim_cat moves to the right
                new_dim_cat = new_dim_cat + 1
            elif is_droppedaxis(idx):
                # an axis was dropped: dim_cat moves to the left
                new_dim_cat = new_dim_cat - 1
                nb_old_dim += 1
            else:
                nb_old_dim += 1
            if nb_old_dim >= self._dim_cat:
                # found the concatenated dimension
                break
        index_cat = index[map_dim_cat]
        index_cat = neg2pos(index_cat, shape_cat)  # /!\ do not call it again

        if is_droppedaxis(index_cat):
            # if the concatenated dimension is dropped, return the
            # corresponding array (sliced)
            if index_cat < 0 or index_cat >= shape_cat:
                raise IndexError('Index {} out of bounds [0, {}]'.format(
                    index_cat, shape_cat))
            nb_pre = 0
            for i in range(len(self._arrays)):
                if nb_pre < index_cat:
                    # we haven't found the volume yet
                    nb_pre += self._arrays[i].shape[self._dim_cat]
                    continue
                break
            if nb_pre > index_cat:
                i = i - 1
                nb_pre -= self._arrays[i].shape[self._dim_cat]
            index_cat = index_cat - nb_pre
            index[map_dim_cat] = index_cat
            return self._arrays[i].slice(tuple(index), new_shape)

        # else, we may have to drop some volumes and slice the others
        assert is_sliceaxis(index_cat), "This should not happen"
        arrays = self._arrays

        step = index_cat.step or 1
        if step < 0:
            # if negative step:
            # 1) invert everything
            invert_index = [slice(None)] * self.dim
            invert_index[self._dim_cat] = slice(None, None, -1)
            arrays = [array[tuple(invert_index)] for array in arrays]
            # 2) update index_cat
            index_cat = invert_slice(index_cat, shape_cat, neg2pos=False)

        # compute navigator
        # (step is positive)
        start, step, nb_elem_total = slice_navigator(index_cat,
                                                     shape_cat,
                                                     do_neg2pos=False)

        nb_pre = 0  # nb of slices on the left of the cursor
        kept_arrays = []  # arrays at least partly in bounds
        starts = []  # start in each kept array
        stops = []  # stop in each kept array
        size_since_start = 0  # nb of in-bounds slices left of the cursor
        while len(arrays) > 0:
            # pop array
            array, *arrays = arrays
            size_cat = array.shape[self._dim_cat]
            if nb_pre + size_cat < start:
                # discarded volumes at the beginning
                nb_pre += size_cat
                continue
            if nb_pre < start:
                # first volume
                kept_arrays.append(array)
                starts.append(start - nb_pre)
            elif index_cat.stop is None or nb_pre < index_cat.stop:
                # other kept volume
                kept_arrays.append(array)
                skip = size_since_start - (size_since_start // step) * step
                starts.append(skip)
            # compute stopping point
            nb_elem_prev = size_since_start // step
            nb_elem_remaining = nb_elem_total - nb_elem_prev
            nb_elem_this_volume = (size_cat - starts[-1]) // step
            if nb_elem_remaining <= nb_elem_this_volume:
                # last volume
                stops.append(nb_elem_remaining)
                break
            # read as much as possible
            size_since_start += size_cat
            nb_pre += size_cat
            stops.append(None)
            continue

        # slice kept arrays
        arrays = []
        for array, start, stop in zip(kept_arrays, starts, stops):
            index[map_dim_cat] = slice(start, stop, step)
            arrays.append(array[tuple(index)])

        # create new CatArray
        new = copy(self)
        new._arrays = arrays
        new._dim_cat = new_dim_cat
        new.shape = new_shape
        return new