예제 #1
0
    def init_brickwall(self, volume_service, subset_labels, roi):
        sbm = None

        if roi["name"]:
            base_service = volume_service.base_service

            if not roi["server"] or not roi["uuid"]:
                assert isinstance(base_service, DvidVolumeService), \
                    "Since you aren't using a DVID input source, you must specify the ROI server and uuid."

            roi["server"] = (roi["server"] or volume_service.server)
            roi["uuid"] = (roi["uuid"] or volume_service.uuid)

            if roi["scale"] is not None:
                scale = roi["scale"]
            elif isinstance(volume_service, ScaledVolumeService):
                scale = volume_service.scale_delta
                assert scale <= 5, \
                    "The 'roi' option doesn't support volumes downscaled beyond level 5"
            else:
                scale = 0

            brick_shape = volume_service.preferred_message_shape
            assert not (brick_shape % 2**(5-scale)).any(), \
                "If using an ROI, select a brick shape that is divisible by 32"

            seg_box = volume_service.bounding_box_zyx
            seg_box = round_box(seg_box, 2**(5-scale))
            seg_box_s0 = seg_box * 2**scale
            seg_box_s5 = seg_box // 2**(5-scale)

            with Timer(f"Fetching mask for ROI '{roi['name']}' ({seg_box_s0[:, ::-1].tolist()})", logger):
                roi_mask_s5, _ = fetch_roi(roi["server"], roi["uuid"], roi["name"], format='mask', mask_box=seg_box_s5)

            # SBM 'full-res' corresponds to the input service voxels, not necessarily scale-0.
            sbm = SparseBlockMask(roi_mask_s5, seg_box, 2**(5-scale))

        elif subset_labels:
            try:
                sbm = volume_service.sparse_block_mask_for_labels([*subset_labels])
                if ((sbm.box[1] - sbm.box[0]) == 0).any():
                    raise RuntimeError("Could not find sparse masks for any of the subset-labels")
            except NotImplementedError:
                sbm = None

        with Timer("Initializing BrickWall", logger):
            # Aim for 2 GB RDD partitions when loading segmentation
            GB = 2**30
            target_partition_size_voxels = 2 * GB // np.uint64().nbytes

            # Apply halo WHILE downloading the data.
            # TODO: Allow the user to configure whether or not the halo should
            #       be fetched from the outset, or added after the blocks are loaded.
            halo = self.config["connectedcomponents"]["halo"]
            brickwall = BrickWall.from_volume_service(volume_service, 0, None, self.client, target_partition_size_voxels, halo, sbm, compression='lz4_2x')

        return brickwall
예제 #2
0
    def init_boxes(self, volume_service, subset_labels, roi):
        sbm = None
        if roi:
            base_service = volume_service.base_service
            assert isinstance(base_service, DvidVolumeService), \
                "Can't specify an ROI unless you're using a dvid input"

            assert isinstance(volume_service, (ScaledVolumeService, DvidVolumeService)), \
                "The 'roi' option doesn't support adapters other than 'rescale-level'"
            scale = 0
            if isinstance(volume_service, ScaledVolumeService):
                scale = volume_service.scale_delta
                assert scale <= 5, \
                    "The 'roi' option doesn't support volumes downscaled beyond level 5"

            server, uuid, _seg_instance = base_service.instance_triple

            brick_shape = volume_service.preferred_message_shape
            assert not (brick_shape % 2**(5-scale)).any(), \
                "If using an ROI, select a brick shape that is divisible by 32"

            seg_box = volume_service.bounding_box_zyx
            seg_box = round_box(seg_box, brick_shape)
            seg_box_s0 = seg_box * 2**scale
            seg_box_s5 = seg_box // 2**(5 - scale)

            with Timer(
                    f"Fetching mask for ROI '{roi}' ({seg_box_s0[:, ::-1].tolist()})",
                    logger):
                roi_mask_s5, _ = fetch_roi(server,
                                           uuid,
                                           roi,
                                           format='mask',
                                           mask_box=seg_box_s5)

            # SBM 'full-res' corresponds to the input service voxels, not necessarily scale-0.
            sbm = SparseBlockMask.create_from_highres_mask(
                roi_mask_s5, 2**(5 - scale), seg_box, brick_shape)
        elif subset_labels:
            try:
                sbm = volume_service.sparse_block_mask_for_labels(
                    [*subset_labels])
                if ((sbm.box[1] - sbm.box[0]) == 0).any():
                    raise RuntimeError(
                        "Could not find sparse masks for any of the subset-labels"
                    )
            except NotImplementedError:
                sbm = None

        if sbm is None:
            boxes = boxes_from_grid(volume_service.bounding_box_zyx,
                                    volume_service.preferred_message_shape,
                                    clipped=True)
            return np.array([*boxes])
        else:
            return sbm.sparse_boxes(brick_shape)
예제 #3
0
    def init_boxes(self, volume_service, roi):
        if not roi["name"]:
            boxes = boxes_from_grid(volume_service.bounding_box_zyx,
                                    volume_service.preferred_message_shape,
                                    clipped=True)
            return np.array([*boxes])

        base_service = volume_service.base_service

        if not roi["server"] or not roi["uuid"]:
            assert isinstance(base_service, DvidVolumeService), \
                "Since you aren't using a DVID input source, you must specify the ROI server and uuid."

        roi["server"] = (roi["server"] or volume_service.server)
        roi["uuid"] = (roi["uuid"] or volume_service.uuid)

        if roi["scale"] is not None:
            scale = roi["scale"]
        elif isinstance(volume_service, ScaledVolumeService):
            scale = volume_service.scale_delta
            assert scale <= 5, \
                "The 'roi' option doesn't support volumes downscaled beyond level 5"
        else:
            scale = 0

        brick_shape = volume_service.preferred_message_shape
        assert not (brick_shape % 2**(5-scale)).any(), \
            "If using an ROI, select a brick shape that is divisible by 32"

        seg_box = volume_service.bounding_box_zyx
        seg_box = round_box(seg_box, 2**(5 - scale))
        seg_box_s0 = seg_box * 2**scale
        seg_box_s5 = seg_box // 2**(5 - scale)

        with Timer(
                f"Fetching mask for ROI '{roi['name']}' ({seg_box_s0[:, ::-1].tolist()})",
                logger):
            roi_mask_s5, _ = fetch_roi(roi["server"],
                                       roi["uuid"],
                                       roi["name"],
                                       format='mask',
                                       mask_box=seg_box_s5)

        # SBM 'full-res' corresponds to the input service voxels, not necessarily scale-0.
        sbm = SparseBlockMask(roi_mask_s5, seg_box, 2**(5 - scale))
        boxes = sbm.sparse_boxes(brick_shape)

        # Clip boxes to the true (not rounded) bounding box
        boxes[:, 0] = np.maximum(boxes[:, 0],
                                 volume_service.bounding_box_zyx[0])
        boxes[:, 1] = np.minimum(boxes[:, 1],
                                 volume_service.bounding_box_zyx[1])
        return boxes
예제 #4
0
    def init_boxes(self, volume_service, roi):
        if not roi:
            boxes = boxes_from_grid(volume_service.bounding_box_zyx,
                                    volume_service.preferred_message_shape,
                                    clipped=True)
            return np.array([*boxes])

        base_service = volume_service.base_service
        assert isinstance(base_service, DvidVolumeService), \
            "Can't specify an ROI unless you're using a dvid input"

        assert isinstance(volume_service, (ScaledVolumeService, DvidVolumeService)), \
            "The 'roi' option doesn't support adapters other than 'rescale-level'"
        scale = 0
        if isinstance(volume_service, ScaledVolumeService):
            scale = volume_service.scale_delta
            assert scale <= 5, \
                "The 'roi' option doesn't support volumes downscaled beyond level 5"

        server, uuid, _seg_instance = base_service.instance_triple

        brick_shape = volume_service.preferred_message_shape
        assert not (brick_shape % 2**(5-scale)).any(), \
            "If using an ROI, select a brick shape that is divisible by 32"

        seg_box = volume_service.bounding_box_zyx
        seg_box = round_box(seg_box, 2**(5 - scale))
        seg_box_s0 = seg_box * 2**scale
        seg_box_s5 = seg_box // 2**(5 - scale)

        with Timer(
                f"Fetching mask for ROI '{roi}' ({seg_box_s0[:, ::-1].tolist()})",
                logger):
            roi_mask_s5, _ = fetch_roi(server,
                                       uuid,
                                       roi,
                                       format='mask',
                                       mask_box=seg_box_s5)

        # SBM 'full-res' corresponds to the input service voxels, not necessarily scale-0.
        sbm = SparseBlockMask(roi_mask_s5, seg_box, 2**(5 - scale))
        boxes = sbm.sparse_boxes(brick_shape)

        # Clip boxes to the true (not rounded) bounding box
        boxes[:, 0] = np.maximum(boxes[:, 0],
                                 volume_service.bounding_box_zyx[0])
        boxes[:, 1] = np.minimum(boxes[:, 1],
                                 volume_service.bounding_box_zyx[1])
        return boxes
예제 #5
0
    def init_boxes(self, volume_service, roi, chunk_shape_s0):
        """
        Return a set of bounding boxes to tile the given ROI.
        Scale 0 of the volume service should correspond to full-res data,
        which is 32x higher-res than ROI resolution.
        """
        if not roi["name"]:
            boxes = boxes_from_grid(volume_service.bounding_box_zyx,
                                    chunk_shape_s0,
                                    clipped=True)
            return np.array([*boxes])

        base_service = volume_service.base_service

        if not roi["server"] or not roi["uuid"]:
            assert isinstance(base_service, DvidVolumeService), \
                "Since you aren't using a DVID input source, you must specify the ROI server and uuid."

        roi["server"] = (roi["server"] or volume_service.server)
        roi["uuid"] = (roi["uuid"] or volume_service.uuid)

        assert not (chunk_shape_s0 % 2**5).any(), \
            "If using an ROI, select a chunk shape that is divisible by 32"

        seg_box_s0 = volume_service.bounding_box_zyx
        seg_box_s0 = round_box(seg_box_s0, 2**5)
        seg_box_s5 = seg_box_s0 // 2**5

        with Timer(
                f"Fetching mask for ROI '{roi['name']}' ({seg_box_s0[:, ::-1].tolist()})",
                logger):
            roi_mask_s5, _ = fetch_roi(roi["server"],
                                       roi["uuid"],
                                       roi["name"],
                                       format='mask',
                                       mask_box=seg_box_s5)

        # SBM 'full-res' corresponds to the input service voxels, not necessarily scale-0.
        sbm = SparseBlockMask(roi_mask_s5, seg_box_s0, 2**5)
        boxes = sbm.sparse_boxes(chunk_shape_s0)

        # Clip boxes to the true (not rounded) bounding box
        boxes[:, 0] = np.maximum(boxes[:, 0],
                                 volume_service.bounding_box_zyx[0])
        boxes[:, 1] = np.minimum(boxes[:, 1],
                                 volume_service.bounding_box_zyx[1])
        return boxes
예제 #6
0
    def init_boxes(self, volume_service, roi):
        if not roi["name"]:
            boxes = boxes_from_grid(volume_service.bounding_box_zyx,
                                    volume_service.preferred_message_shape,
                                    clipped=True)
            return np.array([*boxes])

        server, uuid, roi_name = roi["server"], roi["uuid"], roi["name"]
        roi_scale = roi["relative-scale"]

        brick_shape = volume_service.preferred_message_shape
        assert not (brick_shape % 2**roi_scale).any(), \
            "If using an ROI, select a brick shape that is divisible by 32"

        seg_box = volume_service.bounding_box_zyx
        seg_box = round_box(seg_box, 2**roi_scale)
        seg_box_s5 = seg_box // 2**roi_scale

        with Timer(
                f"Fetching mask for ROI '{roi_name}' ({seg_box[:, ::-1].tolist()})",
                logger):
            roi_mask_s5, _ = fetch_roi(server,
                                       uuid,
                                       roi_name,
                                       format='mask',
                                       mask_box=seg_box_s5)

        # SBM 'full-res' corresponds to the input service voxels, not necessarily scale-0.
        sbm = SparseBlockMask(roi_mask_s5, seg_box, 2**roi_scale)
        boxes = sbm.sparse_boxes(brick_shape)

        # Clip boxes to the true (not rounded) bounding box
        boxes[:, 0] = np.maximum(boxes[:, 0],
                                 volume_service.bounding_box_zyx[0])
        boxes[:, 1] = np.minimum(boxes[:, 1],
                                 volume_service.bounding_box_zyx[1])
        return boxes
예제 #7
0
def main():
    configure_default_logging()

    parser = argparse.ArgumentParser(
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('--no-downres', action='store_true')
    parser.add_argument('--only-within-roi')
    parser.add_argument('--not-within-roi')
    parser.add_argument('dvid_server')
    parser.add_argument('uuid')
    parser.add_argument('labelmap_instance')
    parser.add_argument('sparsevol_files', nargs='+')
    args = parser.parse_args()

    instance_info = (args.dvid_server, args.uuid, args.labelmap_instance)

    assert not args.only_within_roi or not args.not_within_roi, \
        "Can't supply both --only-within-roi and --not-within-roi.  Pick one or the other (or neither)."

    roi = args.only_within_roi or args.not_within_roi
    invert_roi = (args.not_within_roi is not None)

    if roi:
        roi_mask, mask_box = fetch_roi(args.dvid_server,
                                       args.uuid,
                                       roi,
                                       format='mask')
        roi_sbm = SparseBlockMask(roi_mask, mask_box * (2**5),
                                  2**5)  # ROIs are provided at scale 5
    else:
        roi_sbm = None

    # Ideally, we would choose the max label for the node we're writing to,
    # but the /maxlabel endpoint doesn't work for all nodes
    # instead, we'll use the repo-wide maxlabel from the /info JSON.
    #maxlabel = fetch_maxlabel(args.dvid_server, args.uuid, args.labelmap_instance)
    maxlabel = fetch_instance_info(
        args.dvid_server, args.uuid,
        args.labelmap_instance)["Extended"]["MaxRepoLabel"]

    for i, path in enumerate(args.sparsevol_files):
        maxlabel += 1
        name = os.path.split(path)[1]
        prefix_logger = PrefixedLogger(logger, f"Vol #{i:02d} {name}: ")

        with Timer(f"Pasting {name} as {maxlabel}", logger):
            overwritten_labels = overwrite_sparsevol(*instance_info, maxlabel,
                                                     path, roi_sbm, invert_roi,
                                                     args.no_downres,
                                                     prefix_logger)

        results_path = os.path.splitext(path)[0] + '.json'
        with open(results_path, 'w') as f:
            results = {
                'new-label': maxlabel,
                'overwritten_labels': sorted(overwritten_labels)
            }
            json.dump(results, f, indent=2, cls=NumpyConvertingEncoder)

    logger.info(f"Done.")
예제 #8
0
    def _init_mask(self):
        """
        - read the mask ROI as a volume
        - dilate/erode it if necessary
        - invert it if necessary
        - save to .h5 (just for offline debug)
        - return the scale-5 mask and its scale-5 bounding-box
        """
        options = self.config["masksegmentation"]
        roi = options["mask-roi"]
        invert_mask = options["invert-mask"]
        max_scale = options["max-pyramid-scale"]
        roi_dilation = options["dilate-roi"]
        roi_erosion = options["erode-roi"]
        seg_dilation = options["dilate-segmentation"]

        block_width = self.output_service.block_width

        # Select a mask_box that's large enough to divide evenly into the
        # block width even when reduced to the highest scale we'll be processing.
        seg_box = round_box(self.input_service.bounding_box_zyx,
                            block_width * 2**max_scale)
        seg_box_s5 = round_box(seg_box, 2**5) // (2**5)

        with Timer(f"Loading ROI '{roi}'", logger):
            roi_mask, _ = fetch_roi(self.input_service.server,
                                    self.input_service.uuid,
                                    roi,
                                    format='mask',
                                    mask_box=seg_box_s5)

        with h5py.File('roi-mask.h5', 'w') as f:
            f.create_dataset('mask',
                             data=roi_mask.view(np.uint8),
                             chunks=(128, 128, 128))

        assert not (roi_dilation and roi_erosion)

        if roi_dilation > 0:
            with Timer(f"Dilating ROI by {roi_dilation}", logger):
                roi_mask = vigra.filters.multiBinaryDilation(
                    roi_mask, roi_dilation)
            with h5py.File('dilated-roi-mask.h5', 'w') as f:
                f.create_dataset('mask',
                                 data=roi_mask.view(np.uint8),
                                 chunks=(128, 128, 128))

        if roi_erosion > 0:
            with Timer(f"Eroding ROI by {roi_erosion}", logger):
                roi_mask = vigra.filters.multiBinaryErosion(
                    roi_mask, roi_erosion)
            with h5py.File('eroded-roi-mask.h5', 'w') as f:
                f.create_dataset('mask',
                                 data=roi_mask.view(np.uint8),
                                 chunks=(128, 128, 128))

        assert not seg_dilation or invert_mask, \
            "Can't use 'dilate-segmentation'. The segmentation isn't downloaded unless 'invert-mask' is used."

        if invert_mask:
            with Timer("Inverting mask", logger):
                # Initialize the mask with entire segmentation at scale 5,
                # then subtract the roi from it.
                boxes = [
                    *boxes_from_grid(seg_box_s5, (64, 64, 2048), clipped=True)
                ]

                input_service = self.input_service

                def fetch_seg_mask_s5(box_s5):
                    seg_s5 = input_service.get_subvolume(box_s5, scale=5)
                    return box_s5, (seg_s5 != 0)

                boxes_and_mask = dask.bag.from_sequence(
                    boxes, 1).map(fetch_seg_mask_s5).compute()

                seg_mask = np.zeros(box_shape(seg_box_s5), bool)
                for box_s5, box_mask in boxes_and_mask:
                    overwrite_subvol(seg_mask, box_s5, box_mask)

                if seg_dilation == 0:
                    with h5py.File('segmentation-mask.h5', 'w') as f:
                        f.create_dataset('mask',
                                         data=seg_mask.view(np.uint8),
                                         chunks=(128, 128, 128))
                else:
                    with Timer(f"Dilating segmentation by {seg_dilation}",
                               logger):
                        seg_mask = vigra.filters.multiBinaryDilation(
                            seg_mask, seg_dilation)

                    with h5py.File('dilated-segmentation-mask.h5', 'w') as f:
                        f.create_dataset('mask',
                                         data=seg_mask.view(np.uint8),
                                         chunks=(128, 128, 128))

                seg_mask[roi_mask] = False
                roi_mask = seg_mask

        with h5py.File('final-mask.h5', 'w') as f:
            f.create_dataset('mask',
                             data=roi_mask.view(np.uint8),
                             chunks=(128, 128, 128))

        # Downsample the roi_mask to dvid-block resolution, just to see how many blocks it touches.
        block_mask = view_as_blocks(roi_mask, (2, 2, 2)).any(axis=(3, 4, 5))
        blocks_touched = block_mask.sum()
        voxel_total = blocks_touched * (block_width**3)
        logger.info(
            f"Mask touches {blocks_touched} blocks ({voxel_total / 1e9:.1f} Gigavoxels)"
        )

        return roi_mask, seg_box_s5
예제 #9
0
def export_roi(server, uuid, roi_name, scale, scaled_shape_zyx, parent_output_dir):
    """
    Export the ROI to a PNG stack (as binary images) at the requested scale.
    
    Args:
        server, uuid, roi_name:
            ROI instance to read
        
        scale:
            What scale to export as, relative to the full-res grayscale.
            Must be no greater than 5.
            (ROIs are natively at scale=5, so using that scale will result in no upscaling.)
        
        scaled_shape_zyx:
            The max shape of the exported volume, in scaled coordinates.
            The PNG stack files always start at (0,0,0), and extend to this shape.
            Any ROI blocks below 0 or above this shape are silently ignored.
        
        parent_output_dir:
            Where to write the directory of PNG images.
            (A child directory will be created here and named after the ROI instance.)        
    """
    from neuclease.util import view_as_blocks

    assert not ((scaled_shape_zyx * 2**scale) % 64).any(), \
        "The code below assumes that the volume shape is block aligned"

    # Fetch the ROI-block coords (always scale 5)
    roi_coords = fetch_roi((server, uuid, roi_name), format='coords')
    if len(roi_coords) == 0:
        return

    output_dir = f'{parent_output_dir}/{roi_name}-mask-scale-{scale}'
    os.makedirs(output_dir, exist_ok=True)

    # Create a mask for the scale we're using (hopefully it fits in RAM...)
    scaled_mask = np.zeros(scaled_shape_zyx, np.uint8)

    # Create a view of the scaled mask that allows us to broadcast on a per-block basis,
    # indexed as follows: scaled_mask_view[Bz,By,Bx,f,f,f],
    # where f = scale_diff_factor = 32 / (2**SCALE)
    # (ROIs are returned at scale 5!!)
    scale_diff_factor = (2**5) // (2**scale)
    scaled_mask_view = view_as_blocks(scaled_mask, 3*(scale_diff_factor,))

    roi_box = np.array([roi_coords.min(axis=0), 1+roi_coords.max(axis=0)])
    if (roi_box[0] < 0).any() or (roi_box[1] > scaled_mask_view.shape[:3]).any():
        # Drop coordinates outside the volume.
        # (Some ROIs extend beyond our sample.)
        (Z, Y, X) = scaled_mask_view.shape[:3] #@UnusedVariable
        roi_coords_df = pd.DataFrame(roi_coords, columns=list('zyx'))
        roi_coords_df.query('x >= 0 and y >= 0 and z >= 0 and x < @X and y < @Y and z < @Z', inplace=True)

        roi_coords = roi_coords_df.values
        roi_box = np.array([roi_coords.min(axis=0), 1+roi_coords.max(axis=0)])

    # Apply to the mask
    scaled_mask_view[tuple(roi_coords.transpose())] = 1
    scaled_mask = vigra.taggedView(scaled_mask, 'zyx')
    for z, z_slice in enumerate(tqdm(scaled_mask, leave=False)):
        vigra.impex.writeImage(z_slice, f'{output_dir}/{z:05d}.png', 'UINT8')
예제 #10
0
    def init_boxes(self, volume_service, subset_labels, roi):
        sbm = None
        if roi:
            base_service = volume_service.base_service
            assert isinstance(base_service, DvidVolumeService), \
                "Can't specify an ROI unless you're using a dvid input"

            assert isinstance(volume_service, (ScaledVolumeService, DvidVolumeService)), \
                "The 'roi' option doesn't support adapters other than 'rescale-level'"
            scale = 0
            if isinstance(volume_service, ScaledVolumeService):
                scale = volume_service.scale_delta
                assert scale <= 5, \
                    "The 'roi' option doesn't support volumes downscaled beyond level 5"

            server, uuid, _seg_instance = base_service.instance_triple

            brick_shape = volume_service.preferred_message_shape
            assert not (brick_shape % 2**(5-scale)).any(), \
                "If using an ROI, select a brick shape that is divisible by 32"

            seg_box = volume_service.bounding_box_zyx
            seg_box = round_box(seg_box, brick_shape)
            seg_box_s5 = seg_box // 2**(5 - scale)

            with Timer(f"Fetching mask for ROI '{roi}'", logger):
                roi_mask_s5, roi_box_s5 = fetch_roi(server,
                                                    uuid,
                                                    roi,
                                                    format='mask')

            # Restrict to input bounding box
            clipped_roi_box_s5 = box_intersection(seg_box_s5, roi_box_s5)
            clipped_roi_mask_s5 = extract_subvol(
                roi_mask_s5, clipped_roi_box_s5 - roi_box_s5[0])

            # Align to brick grid
            aligned_roi_box_s5 = round_box(clipped_roi_box_s5,
                                           brick_shape // 2**5, 'out')
            padding = (aligned_roi_box_s5 - clipped_roi_box_s5)
            padding[0] *= -1
            aligned_roi_mask_s5 = np.pad(clipped_roi_mask_s5,
                                         padding.transpose())

            # At the service native scale
            aligned_roi_box = (2**(5 - scale) * aligned_roi_box_s5)
            logger.info(
                f"Brick-aligned ROI '{roi}' has bounding-box {aligned_roi_box[:, ::-1].tolist()}"
            )

            # SBM 'full-res' corresponds to the input service voxels, not necessarily scale-0.
            sbm = SparseBlockMask.create_from_highres_mask(
                aligned_roi_mask_s5, 2**(5 - scale), aligned_roi_box,
                brick_shape)
        elif subset_labels:
            try:
                sbm = volume_service.sparse_block_mask_for_labels(
                    [*subset_labels])
                if ((sbm.box[1] - sbm.box[0]) == 0).any():
                    raise RuntimeError(
                        "Could not find sparse masks for any of the subset-labels"
                    )
            except NotImplementedError:
                sbm = None

        if sbm is None:
            boxes = boxes_from_grid(volume_service.bounding_box_zyx,
                                    volume_service.preferred_message_shape,
                                    clipped=True)
            return np.array([*boxes])
        else:
            boxes = sbm.sparse_boxes(brick_shape)
            boxes = np.array(boxes)

            # Clip
            boxes[:, 0, :] = np.maximum(volume_service.bounding_box_zyx[0],
                                        boxes[:, 0, :])
            boxes[:, 1, :] = np.minimum(volume_service.bounding_box_zyx[1],
                                        boxes[:, 1, :])
            assert (boxes[:,0,:] < boxes[:,1,:]).all(), \
                "After cropping to input volume, some bricks disappeared."

            return boxes
예제 #11
0
def check_in_rois(server, uuid, synapse_df, rois):
    """
    Adds a column 'in_roi' to the given points dataframe
    indicating whether or not each point is covered by a ROI in the given list of ROIs.
    Adds the column (IN-PLACE).
    
    Args:
        server:
            dvid server
        uuid:
            Where to pull the rois from
        synapse_df:
            A DataFrame with at least columns ['x', 'y', 'z', 'body']
        rois:
            list of strings (roi instance names)

    Returns:
        None (Operates in-place)
    """
    num_bodies = len(pd.unique(synapse_df['body']))
    logging.info(
        f"Checking in {len(rois)} ROIs for {len(synapse_df)} synapses from {num_bodies} bodies"
    )

    masks_and_boxes = []
    for roi in rois:
        logger.info(f"Fetching ROI '{roi}'")
        mask, box = fetch_roi(server, uuid, roi, format='mask')
        masks_and_boxes.append((mask, box))

    _masks, boxes = zip(*masks_and_boxes)
    boxes = np.array(boxes)

    # box/shape is in scale-5 coordinates
    logger.info("Combining ROIs into a single mask")
    combined_box = (boxes[:, 0, :].min(axis=0), boxes[:, 1, :].max(axis=0))
    combined_shape = (combined_box[1] - combined_box[0])
    combined_mask = np.zeros(combined_shape, dtype=bool)

    for mask, box in masks_and_boxes:
        offset_box = box - combined_box[0]
        combined_mask[box_to_slicing(*offset_box)] |= mask

    # Rescale points to scale 5 (ROIs are given at scale 5)
    logger.info("Scaling points")
    downsampled_coords_zyx = synapse_df[['z', 'y', 'x']] // (2**5)

    # Drop everything outside the combined_box
    logger.info("Excluding OOB points")
    min_z, min_y, min_x = combined_box[0]  #@UnusedVariable
    max_z, max_y, max_x = combined_box[1]  #@UnusedVariable
    q = 'z >= @min_z and y >= @min_y and x >= @min_x and z < @max_z and y < @max_y and x < @max_x'
    downsampled_coords_zyx.query(q, inplace=True)

    logging.info("Extracting mask values")
    synapse_df['in_roi'] = False
    downsampled_coords_zyx -= combined_box[0]
    synapse_df.loc[downsampled_coords_zyx.index,
                   'in_roi'] = combined_mask[tuple(
                       downsampled_coords_zyx.values.transpose())]

    roi_synapses = synapse_df['in_roi'].sum()
    roi_bodies = len(pd.unique(synapse_df['body'][synapse_df['in_roi']]))

    logging.info(
        f"Found {roi_synapses} synapses in the ROI from {roi_bodies} bodies")