def _threshold_block(block_id, blocking, ds_in, ds_out, threshold,
                     threshold_mode, channel, sigma):
    fu.log("start processing block %i" % block_id)
    block = blocking.getBlock(block_id)
    bb = vu.block_to_bb(block)

    bb = vu.block_to_bb(block)
    if channel is None:
        input_ = ds_in[bb]
    else:
        channel_ = [channel] if isinstance(channel, int) else channel
        in_shape = (len(channel_), ) + tuple(b.stop - b.start for b in bb)
        input_ = np.zeros(in_shape, dtype=ds_in.dtype)
        for chan_id, chan in enumerate(channel_):
            bb_inp = (slice(chan, chan + 1), ) + bb
            input_[chan_id] = ds_in[bb_inp].squeeze()
        input_ = np.mean(input_, axis=0)

    input_ = vu.normalize(input_)
    if sigma > 0:
        input_ = vu.apply_filter(input_, 'gaussianSmoothing', sigma)
        input_ = vu.normalize(input_)

    if threshold_mode == 'greater':
        input_ = input_ > threshold
    elif threshold_mode == 'less':
        input_ = input_ < threshold
    elif threshold_mode == 'equal':
        input_ = input_ == threshold
    else:
        raise RuntimeError("Thresholding Mode %s not supported" %
                           threshold_mode)

    ds_out[bb] = input_.astype('uint8')
    fu.log_block_success(block_id)
def _read_data(ds_in, input_bb, config):
    # read the input data
    if ds_in.ndim == 4:
        channel_begin = config.get('channel_begin', 0)
        channel_end = config.get('channel_end', None)
        input_bb = (slice(channel_begin, channel_end), ) + input_bb
        input_ = vu.normalize(ds_in[input_bb])
        agglomerate = config.get('agglomerate_channels', 'mean')
        assert agglomerate in ('mean', 'max', 'min')
        input_ = getattr(np, agglomerate)(input_, axis=0)
    else:
        input_ = vu.normalize(ds_in[input_bb])
    return input_
def _cc_block_with_mask(block_id, blocking,
                        ds_in, ds_out, threshold,
                        threshold_mode, mask,
                        channel, sigma):
    fu.log("start processing block %i" % block_id)
    block = blocking.getBlock(block_id)
    bb = vu.block_to_bb(block)

    # get the mask and check if we have any pixels
    in_mask = mask[bb].astype('bool')
    if np.sum(in_mask) == 0:
        fu.log_block_success(block_id)
        return 0

    bb = vu.block_to_bb(block)
    if channel is None:
        input_ = ds_in[bb]
    else:
        channel_ = [channel] if isinstance(channel, int) else channel
        in_shape = (len(channel_),) + tuple(b.stop - b.start for b in bb)
        input_ = np.zeros(in_shape, dtype=ds_in.dtype)
        for chan_id, chan in enumerate(channel_):
            bb_inp = (slice(chan, chan + 1),) + bb
            input_[chan_id] = ds_in[bb_inp].squeeze()
        input_ = np.mean(input_, axis=0)

    input_ = vu.normalize(input_)
    if sigma > 0:
        input_ = vu.apply_filter(input_, 'gaussianSmoothing', sigma)
        input_ = vu.normalize(input_)

    if threshold_mode == 'greater':
        input_ = input_ > threshold
    elif threshold_mode == 'less':
        input_ = input_ < threshold
    elif threshold_mode == 'equal':
        input_ = input_ == threshold
    else:
        raise RuntimeError("Thresholding Mode %s not supported" % threshold_mode)

    input_[np.logical_not(in_mask)] = 0
    if np.sum(input_) == 0:
        fu.log_block_success(block_id)
        return 0

    components = label(input_)
    ds_out[bb] = components
    fu.log_block_success(block_id)
    return int(components.max()) + 1
Exemple #4
0
def _make_hmap(input_, distances, alpha, sigma_weights):
    distances = 1. - vu.normalize(distances)
    hmap = alpha * input_ + (1. - alpha) * distances
    # smooth input if sigma is given
    if sigma_weights != 0:
        hmap = vu.apply_filter(hmap, 'gaussianSmoothing', sigma_weights)
    return hmap
def _mws_block_pass1(block_id, blocking, ds_in, ds_out, mask, offsets, strides,
                     randomize_strides, halo, noise_level, max_block_id,
                     tmp_folder):
    fu.log("(Pass1) start processing block %i" % block_id)

    block = blocking.getBlockWithHalo(block_id, halo)
    in_bb = vu.block_to_bb(block.outerBlock)

    if mask is None:
        bb_mask = None
    else:
        bb_mask = mask[in_bb].astype('bool')
        if np.sum(bb_mask) == 0:
            fu.log_block_success(block_id)
            return

    aff_bb = (slice(None), ) + in_bb
    affs = vu.normalize(ds_in[aff_bb])

    seg = mutex_watershed(affs,
                          offsets,
                          strides=strides,
                          mask=bb_mask,
                          randomize_strides=randomize_strides,
                          noise_level=noise_level)

    out_bb = vu.block_to_bb(block.innerBlock)
    local_bb = vu.block_to_bb(block.innerBlockLocal)
    seg = seg[local_bb]

    # FIXME once vigra supports uint64 or we implement our own ...
    # seg = vigra.analysis.labelVolumeWithBackground(seg)

    # offset with lowest block coordinate
    offset_id = block_id * np.prod(blocking.blockShape)
    vigra.analysis.relabelConsecutive(seg,
                                      start_label=offset_id,
                                      keep_zeros=True,
                                      out=seg)
    ds_out[out_bb] = seg

    # get the state of the segmentation of this block
    grid_graph = compute_grid_graph(seg.shape, mask=bb_mask)
    affs = affs[(slice(None), ) + local_bb]
    # FIXME this function yields incorrect uv-ids !
    state_uvs, state_weights, state_attractive = grid_graph.compute_state_for_segmentation(
        affs, seg, offsets, n_attractive_channels=3, ignore_label=True)
    # serialize the states
    save_path = os.path.join(tmp_folder, 'seg_state_block%i.h5' % block_id)
    with vu.file_reader(save_path) as f:
        f.create_dataset('edges', data=state_uvs)
        f.create_dataset('weights', data=state_weights)
        f.create_dataset('attractive_edge_mask', data=state_attractive)

    # write max-id for the last block
    if block_id == max_block_id:
        _write_nlabels(ds_out, seg)
    # log block success
    fu.log_block_success(block_id)
    def check_results(self, in_key, feat_func):
        f = z5py.File(self.input_path)
        ds_inp = f[in_key]
        ds_inp.n_threads = 8
        ds_ws = f[self.ws_key]
        ds_ws.n_threads = 8

        # load features
        features = z5py.File(self.output_path)[self.output_key][:]

        # load seg and input, make rag
        seg = ds_ws[:]
        inp = normalize(ds_inp[:]) if ds_inp.ndim == 3 else normalize(ds_inp[:3])
        rag = nrag.gridRag(seg, numberOfLabels=int(seg.max()) + 1)

        # compute nifty lens
        if inp.ndim == 3:
            len_nifty = nrag.accumulateEdgeMeanAndLength(rag, inp)[:, 1]
        else:
            len_nifty = nrag.accumulateEdgeMeanAndLength(rag, inp[0])[:, 1]
        self.assertTrue(np.allclose(len_nifty, features[:, -1]))

        # compute nifty features
        features_nifty = feat_func(rag, inp)

        # check feature shape
        self.assertEqual(len(features_nifty), len(features))
        self.assertEqual(features_nifty.shape[1], features.shape[1] - 1)

        # debugging: check closeness of the min values
        # close = np.isclose(features_nifty[:, 2], features[:, 2])
        # print(close.sum(), '/', len(close))
        # not_close = ~close
        # print(np.where(not_close)[:10])
        # print(features[:, 2][not_close][:10])
        # print(features_nifty[:, 2][not_close][:10])

        # we can only assert equality for mean, std, min, max and len
        # -> mean
        self.assertTrue(np.allclose(features_nifty[:, 0], features[:, 0]))
        # -> std
        self.assertTrue(np.allclose(features_nifty[:, 1], features[:, 1]))
        # -> min
        self.assertTrue(np.allclose(features_nifty[:, 2], features[:, 2]))
        # -> max
        self.assertTrue(np.allclose(features_nifty[:, 8], features[:, 8]))
Exemple #7
0
def cast_type(data, dtype):
    if np.dtype(data.dtype) == np.dtype(dtype):
        return data
    # special casting for uint8
    elif np.dtype(dtype) == 'uint8':
        data = vu.normalize(data)
        data *= 255
        return data.astype('uint8')
    else:
        return data.astype(dtype)
def _apply_filter(blocking, block_id, ds_in, ds_out,
                  halo, filter_name, sigma, apply_in_2d):
    fu.log("start processing block %i" % block_id)
    block = blocking.getBlockWithHalo(block_id, halo)
    bb_in = vu.block_to_bb(block.outerBlock)
    input_ = vu.normalize(ds_in[bb_in])
    response = vu.apply_filter(input_, filter_name, sigma, apply_in_2d)
    bb_out = vu.block_to_bb(block.innerBlock)
    inner_bb = vu.block_to_bb(block.innerBlockLocal)
    ds_out[bb_out] = response[inner_bb]
    fu.log_block_success(block_id)
def _block_features(block_id, blocking, ds_in, ds_labels, ds_out, ignore_label,
                    channel, feature_names):
    fu.log("start processing block %i" % block_id)
    block = blocking.getBlock(block_id)
    bb = vu.block_to_bb(block)

    labels = ds_labels[bb]

    # check if we have an ignore label and return
    # if this block is purely ignore label
    if ignore_label is not None:
        if np.sum(labels != ignore_label) == 0:
            fu.log_block_success(block_id)
            return

    # get global normalization values
    min_val = 0
    max_val = 255. if ds_in.dtype == np.dtype('uint8') else 1.

    bb_in = bb if channel is None else (channel, ) + bb
    input_ = ds_in[bb_in]
    input_ = vu.normalize(input_, min_val, max_val)

    ids = np.unique(labels)
    if ids[0] == 0:
        feat_slice = np.s_[:]
        exp_len = len(ids)
    else:
        feat_slice = np.s_[1:]
        exp_len = len(ids) + 1

    # relabel consecutive in order to save memory
    labels = relabel_sequential(labels, ids)

    feats = vigra.analysis.extractRegionFeatures(input_,
                                                 labels.astype('uint32'),
                                                 features=feature_names,
                                                 ignoreLabel=ignore_label)
    assert len(
        feats['count']) == exp_len, "%i, %i" % (len(feats['count']), exp_len)

    # make serialization
    n_cols = len(feature_names) + 1
    data = np.zeros(n_cols * len(ids), dtype='float32')
    # write the ids
    data[::n_cols] = ids.astype('float32')
    # write all the features
    for feat_id, feat_name in enumerate(feature_names, 1):
        data[feat_id::n_cols] = feats[feat_name][feat_slice]

    chunks = blocking.blockShape
    chunk_id = tuple(b.start // ch for b, ch in zip(bb, chunks))
    ds_out.write_chunk(chunk_id, data, True)
    fu.log_block_success(block_id)
            def filter_block(block_id):
                block = blocking.getBlock(block_id)
                bb = tuple(slice(beg, end) for beg, end in zip(block.begin, block.end))
                seg = ds_seg[bb].astype('uint32')
                if seg.sum() == 0:
                    return

                inp = normalize(ds_fg[bb])
                mean_fg = vigra.analysis.extractRegionFeatures(inp, seg, features=['mean'])['mean']
                fg_ids = np.where(mean_fg > self.threshold)[0]
                filtered = np.isin(seg, fg_ids)
                ds_seg[bb] = filtered.astype(ds_seg.dtype)
Exemple #11
0
    def check_subresults(self):
        f_feat = z5py.File(
            os.path.join(self.tmp_folder, 'region_features_tmp.n5'))
        ds_feat = f_feat['block_feats']
        feature_names = ds_feat.attrs['feature_names']
        n_cols = len(feature_names) + 1

        with z5py.File(self.input_path) as f:
            data = f[self.input_key]
            data.n_threads = self.max_jobs
            data = normalize(data[:], 0, 255)

            segmentation = f[self.seg_key]
            segmentation.max_jobs = self.max_jobs
            segmentation = segmentation[:].astype('uint32')
        blocking = nt.blocking([0, 0, 0], data.shape, self.block_shape)

        n_blocks = blocking.numberOfBlocks
        for block_id in range(n_blocks):
            block = blocking.getBlock(block_id)
            bb = tuple(
                slice(beg, end) for beg, end in zip(block.begin, block.end))
            inp = data[bb]
            seg = segmentation[bb].astype('uint32')

            # load the sub-result
            chunk_id = tuple(beg // bs
                             for beg, bs in zip(block.begin, self.block_shape))
            res = ds_feat.read_chunk(chunk_id)
            if res is None:
                self.assertEqual(seg.sum(), 0)
                continue

            # check that ids are correct
            ids = res[::n_cols].astype('uint32')
            expected_ids = np.unique(seg)
            self.assertEqual(ids.shape, expected_ids.shape)
            self.assertTrue(np.array_equal(ids, expected_ids))

            # check that the features are correct
            expected = vigra.analysis.extractRegionFeatures(
                inp, seg, features=feature_names, ignoreLabel=0)
            for feat_id, feat_name in enumerate(feature_names, 1):
                feat_res = res[feat_id::n_cols]
                self.check_features(feat_res, expected, feat_name, ids)

        return feature_names
Exemple #12
0
def _block_features(block_id, blocking, ds_in, ds_labels, ds_out,
                    ignore_label):
    fu.log("start processing block %i" % block_id)
    block = blocking.getBlock(block_id)
    bb = vu.block_to_bb(block)

    labels = ds_labels[bb]

    # check if we have an ignore label and return
    # if this block is purely ignore label
    if ignore_label is not None:
        if np.sum(labels != ignore_label) == 0:
            fu.log_block_success(block_id)
            return

    # TODO support multichannel
    # get global normalization values
    min_val = 0
    max_val = 255. if ds_in.dtype == np.dtype('uint8') else 1.

    input_ = vu.normalize(ds_in[bb], min_val, max_val)
    # TODO support more features
    # TODO we might want to check for overflows and in general allow vigra to
    # work with uint64s ...
    labels = labels.astype('uint32')
    feats = vigra.analysis.extractRegionFeatures(input_,
                                                 labels,
                                                 features=['mean', 'count'],
                                                 ignoreLabel=ignore_label)

    counts = feats['count']
    feats = feats['mean']

    # make serialization
    ids = np.unique(labels)
    data = np.zeros(3 * len(ids), dtype='float32')
    # write the ids
    data[::3] = ids.astype('float32')
    # write the counts
    data[1::3] = counts[ids]
    # write the features
    data[2::3] = feats[ids]

    chunks = blocking.blockShape
    chunk_id = tuple(b.start // ch for b, ch in zip(bb, chunks))
    ds_out.write_chunk(chunk_id, data, True)
    fu.log_block_success(block_id)
Exemple #13
0
def _insert_affinities(affs, objs, offsets, dilate_by):
    dtype = affs.dtype
    # compute affinities to objs and bring them to our aff convention
    affs_insert, mask = compute_affinities(objs, offsets)
    mask = mask == 0
    affs_insert = 1. - affs_insert
    affs_insert[mask] = 0

    # dilate affinity channels
    for c in range(affs_insert.shape[0]):
        affs_insert[c] = dilate(affs_insert[c], iterations=dilate_by, dilate_2d=True)
    # dirty hack: z affinities look pretty weird, so we add the averaged xy affinities
    affs_insert[0] += np.mean(affs_insert[1:3], axis=0)

    # insert affinities
    affs = vu.normalize(affs)
    affs += affs_insert
    affs = np.clip(affs, 0., 1.)
    affs = cast(affs, dtype)
    return affs
Exemple #14
0
    def check_result(self, feature_names):
        # load the result
        with z5py.File(self.output_path) as f:
            res = f[self.output_key][:]
        # compute the vigra result
        with z5py.File(self.input_path) as f:
            inp = f[self.input_key]
            inp.n_threads = self.max_jobs
            inp = normalize(inp[:], 0, 255)

            seg = f[self.seg_key]
            seg.max_jobs = self.max_jobs
            seg = seg[:].astype('uint32')

        expected = vigra.analysis.extractRegionFeatures(inp,
                                                        seg,
                                                        features=feature_names,
                                                        ignoreLabel=0)
        for feat_id, feat_name in enumerate(feature_names):
            self.check_features(res[:, feat_id], expected, feat_name)
Exemple #15
0
def _mws_block(block_id, blocking, ds_in, ds_out, mask, offsets, strides,
               randomize_strides, halo, noise_level):
    fu.log("start processing block %i" % block_id)

    in_bb, out_bb, local_bb = _get_bbs(blocking, block_id, halo)
    if mask is None:
        bb_mask = None
    else:
        bb_mask = mask[in_bb].astype('bool')
        if np.sum(bb_mask) == 0:
            fu.log_block_success(block_id)
            return

    aff_bb = (slice(None), ) + in_bb
    affs = ds_in[aff_bb]
    if affs.sum() == 0:
        fu.log_block_success(block_id)
        return
    affs = vu.normalize(affs)

    seg = su.mutex_watershed(affs,
                             offsets,
                             strides=strides,
                             mask=bb_mask,
                             randomize_strides=randomize_strides,
                             noise_level=noise_level)
    seg = seg[local_bb]

    # FIXME once vigra supports uint64 or we implement our own ...
    # seg = vigra.analysis.labelVolumeWithBackground(seg)

    # offset with lowest block coordinate
    offset_id = block_id * np.prod(blocking.blockShape)
    vigra.analysis.relabelConsecutive(seg,
                                      start_label=offset_id,
                                      keep_zeros=True,
                                      out=seg)
    ds_out[out_bb] = seg

    # log block success
    fu.log_block_success(block_id)
def _mws_block(block_id, blocking, ds_in, ds_out, mask, offsets, strides,
               randomize_strides, halo, noise_level):
    fu.log("start processing block %i" % block_id)

    in_bb, out_bb, local_bb = _get_bbs(blocking, block_id, halo)
    if mask is None:
        bb_mask = None
    else:
        bb_mask = mask[in_bb].astype('bool')
        if np.sum(bb_mask) == 0:
            fu.log_block_success(block_id)
            return

    aff_bb = (slice(None), ) + in_bb
    affs = ds_in[aff_bb]
    if affs.sum() == 0:
        fu.log_block_success(block_id)
        return

    affs = vu.normalize(affs)
    seg = mutex_watershed(affs,
                          offsets,
                          strides=strides,
                          mask=bb_mask,
                          randomize_strides=randomize_strides,
                          noise_level=noise_level)
    seg = seg[local_bb]

    # offset with lowest block coordinate
    offset_id = max(block_id * int(np.prod(blocking.blockShape)), 1)
    assert offset_id < np.iinfo('uint64').max, "Id overflow"
    vigra.analysis.relabelConsecutive(seg,
                                      start_label=offset_id,
                                      keep_zeros=True,
                                      out=seg)
    ds_out[out_bb] = seg

    # log block success
    fu.log_block_success(block_id)
def dt_watershed(input_, threshold=.25, sigma=2., alpha=.9, min_seg_size=100, suppression=True):
    # make distance transform
    threshd = (input_ > threshold).astype('uint32')
    dt = vigra.filters.distanceTransform(threshd)
    dt = vigra.filters.gaussianSmoothing(dt, sigma)

    # make seeds
    seeds = vigra.analysis.localMaxima3D(dt, allowPlateaus=True, allowAtBorder=True,
                                         marker=np.nan)
    seeds = np.isnan(seeds).astype('uint32')
    seeds = vigra.analysis.labelVolumeWithBackground(seeds)
    # non-max suppression
    if suppression:
        seeds = np.array(np.where(seeds)).transpose()
        seeds = nonMaximumDistanceSuppression(dt, seeds)
        seeds = _points_to_vol(seeds, dt.shape)

    # make hmap
    hmap = alpha * input_ + (1. - alpha) * (1. - normalize(dt))

    ws, max_id = watershed(hmap, seeds, min_seg_size)
    return ws, max_id
Exemple #18
0
def _mws_block_pass2(block_id, blocking,
                     ds_in, ds_out,
                     mask, offsets,
                     strides, randomize_strides,
                     halo, noise_level, max_block_id,
                     tmp_folder):
    fu.log("(Pass2) start processing block %i" % block_id)

    block = blocking.getBlockWithHalo(block_id, halo)
    in_bb = vu.block_to_bb(block.outerBlock)

    if mask is None:
        # if we don't have a mask, initialize with fully 'in-mask' volume
        # bb_mask = np.ones(tuple(b.stop - b.begin for b in in_bb),
        #                   dtype='bool')
        bb_mask = None
    else:
        bb_mask = mask[in_bb].astype('bool')
        if np.sum(bb_mask) == 0:
            fu.log_block_success(block_id)
            return

    # TODO does this make sense ?
    # set the mask for parts of indirect neighbor blocks
    # (which are also part of pass 2) to 0
    # bb_mask = mask_corners(bb_mask, halo)

    aff_bb = (slice(None),) + in_bb
    affs = vu.normalize(ds_in[aff_bb])

    # load seeds
    seeds = ds_out[in_bb]
    seed_ids = np.unique(seeds)
    if seed_ids[0] == 0:
        seed_ids = seed_ids[1:]

    # load the serialized state for the neighboring (pass1) blocks
    # and find relevant edges between seed ids

    seed_edges = []
    seed_edge_weights = []
    attractive_mask = []

    for axis in range(3):
        for to_lower in (False, True):
            ngb_id = blocking.getNeighborId(block_id, axis, to_lower)

            # get path to state serialization and check if it exists
            save_path = os.path.join(tmp_folder, 'seg_state_block%i.h5' % ngb_id)
            if not os.path.exists(save_path):
                continue

            with vu.file_reader(save_path) as f:
                # first, load the edges and see if they have overlap with our seed ids
                ngb_edges = f['edges'][:]
                ngb_edge_mask = np.in1d(ngb_edges, seed_ids).reshape(ngb_edges.shape)
                ngb_edge_mask = ngb_edge_mask.all(axis=1)

                # if we have edges, load the corresponding weights
                # and attractive / repulsive state
                if ngb_edge_mask.sum() > 0:
                    ngb_edges = ngb_edges[ngb_edge_mask]
                    ngb_weights = f['weights'][:][ngb_edge_mask]
                    ngb_attractive_edges = f['attractive_edge_mask'][:][ngb_edge_mask]

                    seed_edges.append(ngb_edges)
                    seed_edge_weights.append(ngb_weights)
                    attractive_mask.append(ngb_attractive_edges)

    seed_edges = np.concatenate(seed_edges, axis=0)
    seed_edge_weights = np.concatenate(seed_edge_weights)
    attractive_mask = np.concatenate(attractive_mask)
    assert len(seed_edges) == len(seed_edge_weights) == len(attractive_mask)

    repulsive_mask = np.logical_not(attractive_mask)
    attractive_edges, repulsive_edges = seed_edges[attractive_mask], seed_edges[repulsive_mask]
    attractive_weights, repulsive_weights = seed_edge_weights[attractive_mask], seed_edge_weights[repulsive_mask]

    # run mws segmentation with seeds
    seg, grid_graph = su.mutex_watershed_with_seeds(affs, offsets, seeds, strides=strides,
                                                    mask=bb_mask, randomize_strides=randomize_strides,
                                                    noise_level=noise_level, return_graph=True,
                                                    seed_state={'attractive': (attractive_edges, attractive_weights),
                                                                'repulsive': (repulsive_edges, repulsive_weights)})
    # offset with lowest block coordinate
    offset_id = block_id * np.prod(blocking.blockShape)
    vigra.analysis.relabelConsecutive(seg, start_label=offset_id, keep_zeros=True, out=seg)

    # find assignment of seed ids to segmentation ids
    assignments = grid_graph.get_seed_assignments_from_node_labels(seg.flatten())

    # get the cropped segmentation
    local_bb = vu.block_to_bb(block.innerBlockLocal)
    seg_crop = seg[local_bb]

    # filter the assignments from ids that are not in the crop
    crop_ids = np.unique(seg_crop)
    filter_mask = np.in1d(assignments[:, 1], crop_ids)
    assignments = assignments[filter_mask]

    # store assignments to tmp folder
    save_path = os.path.join(tmp_folder, 'mws_two_pass_assignments_block_%i.npy' % block_id)
    np.save(save_path, assignments)

    out_bb = vu.block_to_bb(block.innerBlock)
    ds_out[out_bb] = seg_crop

    # write max-id for the last block
    if block_id == max_block_id:
        _write_nlabels(ds_out, seg)
    # log block success
    fu.log_block_success(block_id)
Exemple #19
0
def _accumulate_block(block_id, blocking, ds_in, ds_labels, ds_edges, ds_out,
                      filters, sigmas, halo, ignore_label, apply_in_2d,
                      channel_agglomeration):

    fu.log("start processing block %i" % block_id)
    chunk_pos = blocking.blockGridPosition(block_id)

    # load edges and construct the graph if this block has edges
    edges = ds_edges.read_chunk(chunk_pos)
    if edges is None:
        fu.log("block %i has no edges" % block_id)
        fu.log_block_success(block_id)
        return
    edges = edges.reshape((edges.size, 2))
    graph = ndist.Graph(edges)

    shape = ds_labels.shape
    # get the bounding
    if sum(halo) > 0:
        block = blocking.getBlockWithHalo(block_id, halo)
        block_shape = block.outerBlock.shape
        bb_in = vu.block_to_bb(block.outerBlock)
        bb = vu.block_to_bb(block.innerBlock)
        bb_local = vu.block_to_bb(block.innerBlockLocal)
        # increase inner bounding box by 1 in posirive direction
        # in accordance with the graph extraction
        bb = tuple(
            slice(b.start, min(b.stop + 1, sh)) for b, sh in zip(bb, shape))
        bb_local = tuple(
            slice(b.start, min(b.stop + 1, bsh))
            for b, bsh in zip(bb_local, block_shape))
    else:
        block = blocking.getBlock(block_id)
        bb = vu.block_to_bb(block)
        bb = tuple(
            slice(b.start, min(b.stop + 1, sh)) for b, sh in zip(bb, shape))
        bb_in = bb
        bb_local = slice(None)

    input_dim = ds_in.ndim
    # TODO make choice of channels optional
    if input_dim == 4:
        bb_in = (slice(0, 3), ) + bb_in

    input_ = vu.normalize(ds_in[bb_in])
    if input_dim == 4:
        assert channel_agglomeration is not None
        input_ = getattr(np, channel_agglomeration)(input_, axis=0)

    # load labels
    labels = ds_labels[bb]

    # TODO pre-smoothing ?!
    # accumulate the edge features
    edge_features = [
        _accumulate_filter(input_, graph, labels, bb_local, filter_name, sigma,
                           ignore_label, filter_name == filters[-1]
                           and sigma == sigmas[-1], apply_in_2d)
        for filter_name in filters for sigma in sigmas
    ]
    edge_features = np.concatenate(edge_features, axis=1)

    # save the features
    fu.log("saving feature result of shape %s" % str(edge_features.shape))
    ds_out.write_chunk(chunk_pos, edge_features.flatten(), True)
    fu.log_block_success(block_id)
    return edge_features.shape[1]
Exemple #20
0
def _block_features(block_id, blocking, ds_in, ds_labels, ds_out, ignore_label,
                    feature_list):
    fu.log("start processing block %i" % block_id)
    block = blocking.getBlock(block_id)
    bb = vu.block_to_bb(block)

    labels = ds_labels[bb]

    # check if we have an ignore label and return
    # if this block is purely ignore label
    if ignore_label is not None:
        if np.sum(labels != ignore_label) == 0:
            fu.log_block_success(block_id)
            return

    # TODO support multichannel
    # get global normalization values
    min_val = 0
    max_val = 255. if ds_in.dtype == np.dtype('uint8') else 1.

    input_ = vu.normalize(ds_in[bb], min_val, max_val)
    # TODO support more features
    # TODO we might want to check for overflows and in general allow vigra to
    # work with uint64s ...

    labels = labels.astype('uint32')
    feats = vigra.analysis.extractRegionFeatures(input_,
                                                 labels,
                                                 features=feature_list,
                                                 ignoreLabel=ignore_label)
    # make serialization
    num_features = len(feature_list)
    ids = np.unique(labels)

    # Number of actual feature values, since some features (like Histogram) compute multiple values
    num_feature_vals = 1  # start with 1 for ids
    for i in range(0, num_features):
        feature_vals = feats[feature_list[i]]
        if (len(feature_vals.shape) == 2):
            num_feature_vals += feature_vals.shape[1]
        else:
            num_feature_vals += 1

    data = np.zeros((len(ids), num_feature_vals), dtype='float32')
    # write the ids
    data[:, 0] = ids.astype('float32')

    feature_indices = [''] * num_feature_vals

    # write features
    feature_i = 1
    for i in range(num_features):
        feature_vals = feats[feature_list[i]][ids]
        if len(feature_vals.shape) == 2:
            for j in range(feature_vals.shape[1]):
                data[:, feature_i] = feature_vals[:, j]
                feature_indices[feature_i] = feature_list[i]
                feature_i += 1
        else:
            data[:, feature_i] = feature_vals
            feature_indices[feature_i] = feature_list[i]
            feature_i += 1

    data = data.reshape((-1))
    # z5py cant handle nan, so we need to replace it
    data[np.isnan(data)] = 0
    chunks = blocking.blockShape
    chunk_id = tuple(b.start // ch for b, ch in zip(bb, chunks))
    ds_out.write_chunk(chunk_id, data, True)
    ds_out.attrs['feature_indices'] = feature_indices
    fu.log_block_success(block_id)
Exemple #21
0
def _agglomerate_block(blocking, block_id, ds_in, ds_out, config):
    fu.log("start processing block %i" % block_id)
    have_ignore_label = config['have_ignore_label']
    use_mala_agglomeration = config.get('use_mala_agglomeration', True)
    threshold = config.get('threshold', 0.9)
    size_regularizer = config.get('size_regularizer', .5)
    invert_inputs = config.get('invert_inputs', False)
    offsets = config.get('offsets', None)

    bb = vu.block_to_bb(blocking.getBlock(block_id))
    # load the segmentation / output
    seg = ds_out[bb]

    # check if this block is empty
    if np.sum(seg) == 0:
        fu.log_block_success(block_id)
        return

    # load the input data
    ndim_in = ds_in.ndim
    if ndim_in == 4:
        assert offsets is not None
        assert len(offsets) <= ds_in.shape[0]
        bb_in = (slice(0, len(offsets)),) + bb
        input_ = vu.normalize(ds_in[bb_in])
    else:
        assert offsets is None
        input_ = vu.normalize(ds_in[bb])

    if invert_inputs:
        input_ = 1. - input_

    id_offset = int(seg[seg != 0].min())

    # relabel the segmentation
    _, max_id, _ = relabelConsecutive(seg, out=seg, keep_zeros=True, start_label=1)
    seg = seg.astype('uint32')

    # construct rag
    rag = nrag.gridRag(seg, numberOfLabels=max_id + 1,
                       numberOfThreads=1)

    # extract edge features
    if offsets is None:
        edge_features = nrag.accumulateEdgeMeanAndLength(rag, input_, numberOfThreads=1)
    else:
        edge_features = nrag.accumulateAffinityStandartFeatures(rag, input_, offsets,
                                                                numberOfThreads=1)
    edge_features, edge_sizes = edge_features[:, 0], edge_features[:, -1]
    uv_ids = rag.uvIds()
    # set edges to ignore label to be maximally repulsive
    if have_ignore_label:
        ignore_mask = (uv_ids == 0).any(axis=1)
        edge_features[ignore_mask] = 1

    # build undirected graph
    n_nodes = rag.numberOfNodes
    graph = nifty.graph.undirectedGraph(n_nodes)
    graph.insertEdges(uv_ids)

    if use_mala_agglomeration:
        node_labels = mala_clustering(graph, edge_features,
                                      edge_sizes, threshold)
    else:
        node_ids, node_sizes = np.unique(seg, return_counts=True)
        if node_ids[0] != 0:
            node_sizes = np.concatenate([np.array([0]), node_sizes])
        n_stop = int(threshold * n_nodes)
        node_labels = agglomerative_clustering(graph, edge_features,
                                               node_sizes, edge_sizes,
                                               n_stop, size_regularizer)

    # run clusteting
    node_labels, max_id, _ = relabelConsecutive(node_labels, start_label=1, keep_zeros=True)

    fu.log("reduced number of labels from %i to %i" % (n_nodes, max_id + 1))

    # project node labels back to segmentation
    seg = nrag.projectScalarNodeDataToPixels(rag, node_labels, numberOfThreads=1)
    seg = seg.astype('uint64')
    # add offset back to segmentation
    seg[seg != 0] += id_offset

    ds_out[bb] = seg
    # log block success
    fu.log_block_success(block_id)
Exemple #22
0
def _accumulate_block(block_id, blocking, ds_in, ds_labels, out_prefix,
                      graph_block_prefix, filters, sigmas, halo, ignore_label,
                      apply_in_2d, channel_agglomeration):

    fu.log("start processing block %i" % block_id)
    # load graph and check if this block has edges
    graph = ndist.Graph(graph_block_prefix + str(block_id))
    if graph.numberOfEdges == 0:
        fu.log("block %i has no edges" % block_id)
        fu.log_block_success(block_id)
        return

    shape = ds_labels.shape
    # get the bounding
    if sum(halo) > 0:
        block = blocking.getBlockWithHalo(block_id, halo)
        block_shape = block.outerBlock.shape
        bb_in = vu.block_to_bb(block.outerBlock)
        bb = vu.block_to_bb(block.innerBlock)
        bb_local = vu.block_to_bb(block.innerBlockLocal)
        # increase inner bounding box by 1 in posirive direction
        # in accordance with the graph extraction
        bb = tuple(
            slice(b.start, min(b.stop + 1, sh)) for b, sh in zip(bb, shape))
        bb_local = tuple(
            slice(b.start, min(b.stop + 1, bsh))
            for b, bsh in zip(bb_local, block_shape))
    else:
        block = blocking.getBlock(block_id)
        bb = vu.block_to_bb(block)
        bb = tuple(
            slice(b.start, min(b.stop + 1, sh)) for b, sh in zip(bb, shape))
        bb_in = bb
        bb_local = slice(None)

    input_dim = ds_in.ndim
    # TODO make choice of channels optional
    if input_dim == 4:
        bb_in = (slice(0, 3), ) + bb_in

    input_ = vu.normalize(ds_in[bb_in])
    if input_dim == 4:
        assert channel_agglomeration is not None
        input_ = getattr(np, channel_agglomeration)(input_, axis=0)

    # load labels
    labels = ds_labels[bb]

    # TODO pre-smoothing ?!
    # accumulate the edge features
    edge_features = [
        _accumulate_filter(input_, graph, labels, bb_local, filter_name, sigma,
                           ignore_label, filter_name == filters[-1]
                           and sigma == sigmas[-1], apply_in_2d)
        for filter_name in filters for sigma in sigmas
    ]
    edge_features = np.concatenate(edge_features, axis=1)

    # save the features
    save_path = out_prefix + str(block_id)
    fu.log("saving feature result of shape %s to %s" %
           (str(edge_features.shape), save_path))
    save_root, save_key = os.path.split(save_path)
    with z5py.N5File(save_root) as f:
        f.create_dataset(save_key,
                         data=edge_features,
                         chunks=edge_features.shape)

    fu.log_block_success(block_id)