def segment_images(inpDir, outDir, config_data): 
    """ Workflow for data with similar morphology
    as sialyltransferase 1.

    Args:
        inpDir : path to the input directory
        outDir : path to the output directory
        config_data : path to the configuration file
    """

    logging.basicConfig(format='%(asctime)s - %(name)-8s - %(levelname)-8s - %(message)s',
                        datefmt='%d-%b-%y %H:%M:%S')
    logger = logging.getLogger("main")
    logger.setLevel(logging.INFO)

    inpDir_files = os.listdir(inpDir)
    for i,f in enumerate(inpDir_files):
        logger.info('Segmenting image : {}'.format(f))
        
        # Load image
        br = BioReader(os.path.join(inpDir,f))
        image = br.read_image()
        structure_channel = 0 
        struct_img0 = image[:,:,:,structure_channel,0]
        struct_img0 = struct_img0.transpose(2,0,1).astype(np.float32)

        # main algorithm
        intensity_scaling_param = config_data['intensity_scaling_param']
        struct_img = intensity_normalization(struct_img0, scaling_param=intensity_scaling_param) 
        gaussian_smoothing_sigma = config_data['gaussian_smoothing_sigma'] 
        structure_img_smooth = image_smoothing_gaussian_3d(struct_img, sigma=gaussian_smoothing_sigma)
        global_thresh_method = config_data['global_thresh_method'] 
        object_minArea = config_data['object_minArea'] 
        bw, object_for_debug = MO(structure_img_smooth, global_thresh_method=global_thresh_method, object_minArea=object_minArea, return_object=True)
        thin_dist_preserve = config_data['thin_dist_preserve']
        thin_dist = config_data['thin_dist']
        bw_thin = topology_preserving_thinning(bw>0, thin_dist_preserve, thin_dist)
        s3_param = config_data['s3_param']
        bw_extra = dot_3d_wrapper(structure_img_smooth, s3_param)
        bw_combine = np.logical_or(bw_extra>0, bw_thin)
        minArea = config_data['minArea']
        seg = remove_small_objects(bw_combine>0, min_size=minArea, connectivity=1, in_place=False)
        seg = seg > 0
        out_img=seg.astype(np.uint8)
        out_img[out_img>0]=255
    
        # create output image
        out_img = out_img.transpose(1,2,0)
        out_img = out_img.reshape((out_img.shape[0], out_img.shape[1], out_img.shape[2], 1, 1))

        # write image using BFIO
        bw = BioWriter(os.path.join(outDir,f), metadata=br.read_metadata())
        bw.num_x(out_img.shape[1])
        bw.num_y(out_img.shape[0])
        bw.num_z(out_img.shape[2])
        bw.num_c(out_img.shape[3])
        bw.num_t(out_img.shape[4])
        bw.pixel_type(dtype='uint8')
        bw.write_image(out_img)
        bw.close_image()
    def test_correctness(self):
        # calculate the result with the plugin code
        with BioReader(self.infile.name) as reader:
            with BioWriter(self.outfile.name,
                           metadata=reader.metadata) as writer:
                rolling_ball(
                    reader=reader,
                    writer=writer,
                    ball_radius=self.ball_radius,
                    light_background=False,
                )

        # read the image we just wrote into a numpy array
        with BioReader(self.outfile.name) as reader:
            plugin_result = reader[:]

        # calculate the true result
        background = restoration.rolling_ball(self.random_image,
                                              radius=self.ball_radius)
        true_result = self.random_image - background

        # assert correctness
        self.assertTrue(numpy.all(numpy.equal(true_result, plugin_result)),
                        f'The plugin resulted in a different image')
        return
def segment_images(inpDir, outDir, config_data):
    """ Workflow for data with a spotty appearance
    in each 2d frame such as fibrillarin and beta catenin.

    Args:
        inpDir : path to the input directory
        outDir : path to the output directory
        config_data : path to the configuration file
    """

    logging.basicConfig(
        format='%(asctime)s - %(name)-8s - %(levelname)-8s - %(message)s',
        datefmt='%d-%b-%y %H:%M:%S')
    logger = logging.getLogger("main")
    logger.setLevel(logging.INFO)

    inpDir_files = os.listdir(inpDir)
    for i, f in enumerate(inpDir_files):
        logger.info('Segmenting image : {}'.format(f))

        # Load image
        br = BioReader(os.path.join(inpDir, f))
        image = br.read_image()
        structure_channel = 0
        struct_img0 = image[:, :, :, structure_channel, 0]
        struct_img0 = struct_img0.transpose(2, 0, 1).astype(np.float32)

        # main algorithm
        intensity_scaling_param = config_data['intensity_scaling_param']
        struct_img = intensity_normalization(
            struct_img0, scaling_param=intensity_scaling_param)
        gaussian_smoothing_sigma = config_data['gaussian_smoothing_sigma']
        structure_img_smooth = image_smoothing_gaussian_3d(
            struct_img, sigma=gaussian_smoothing_sigma)
        s2_param = config_data['s2_param']
        bw = dot_2d_slice_by_slice_wrapper(structure_img_smooth, s2_param)
        minArea = config_data['minArea']
        seg = remove_small_objects(bw > 0,
                                   min_size=minArea,
                                   connectivity=1,
                                   in_place=False)
        seg = seg > 0
        out_img = seg.astype(np.uint8)
        out_img[out_img > 0] = 255

        # create output image
        out_img = out_img.transpose(1, 2, 0)
        out_img = out_img.reshape(
            (out_img.shape[0], out_img.shape[1], out_img.shape[2], 1, 1))

        # write image using BFIO
        bw = BioWriter(os.path.join(outDir, f))
        bw.num_x(out_img.shape[1])
        bw.num_y(out_img.shape[0])
        bw.num_z(out_img.shape[2])
        bw.num_c(out_img.shape[3])
        bw.num_t(out_img.shape[4])
        bw.pixel_type(dtype='uint8')
        bw.write_image(out_img)
        bw.close_image()
Beispiel #4
0
def estimate_slice_entropies_thread(
        file_path: Path,
        smoothing: bool,
        z_index: int,
) -> distogram.Distogram:
    tile_indices = list(helpers.iter_tiles_2d(file_path))
    if len(tile_indices) > 25:
        tile_indices = list(random.sample(tile_indices, 25))

    tile_histograms = list()

    with BioReader(file_path) as reader:
        for x_min, x_max, y_min, y_max in tile_indices:
            tile = numpy.asarray(
                reader[y_min:y_max, x_min:x_max, z_index, 0, 0],
                dtype=numpy.float32,
            )
            if smoothing:
                tile = scipy.ndimage.gaussian_filter(tile, sigma=1, mode='constant', cval=numpy.mean(tile))

            tile_histograms.append(helpers.distogram_from_batch(
                tile.flat,
                constants.MAX_BINS * 2,
                constants.WEIGHTED_BINS,
            ))

    return reduce(
        lambda residual, value: distogram.merge(residual, value),
        tile_histograms,
        distogram.Distogram(bin_count=constants.MAX_BINS, weighted_diff=constants.WEIGHTED_BINS),
    )
Beispiel #5
0
def create_and_write_output(predictions_path, output_path, inpDir):
    """
    This script uses the bfio utility to write the output.
    Inputs:
        predictions_path: The directory in which the neural networks writes its 3 channel output
        output_path: The directory in which the user wants the final binary output
        inpDir: The input directory consisting of the input collection
    """

    filenames = sorted(os.listdir(predictions_path))
    for filename in filenames:

        # read the 3 channel output image from the neural network
        image = cv2.imread(os.path.join(predictions_path, filename))

        # create binary image output using the create_binary function
        out_image = create_binary(image)

        # read and store the metadata from the input image
        with BioReader(os.path.join(inpDir, filename)) as br:
            metadata = br.metadata

        # Write the binary output consisting of the metadata using bfio.
        output_image_5channel = np.zeros(
            (out_image.shape[0], out_image.shape[1], 1, 1, 1), dtype=np.uint8)
        output_image_5channel[:, :, 0, 0, 0] = out_image

        with BioWriter(os.path.join(output_path, filename),
                       metadata=metadata) as bw:
            bw.dtype = output_image_5channel.dtype
            bw.write(output_image_5channel)
Beispiel #6
0
def read_file(input_directory, pixelsize, output_directory):

    img_pixelsize_x = pixelsize
    img_pixelsize_y = pixelsize
    modelfile_path = "2d_cell_net_v0-cytoplasm.modeldef.h5"
    weightfile_path = "snapshot_cytoplasm_iter_1000.caffemodel.h5"
    iofile_path = "output.h5"
    out_path = Path(output_directory)
    rootdir1 = Path(input_directory)
    """ Convert the tif to tiled tiff """
    javabridge.start_vm(args=["-Dlog4j.configuration=file:{}".format(LOG4J)],
                        class_path=JARS,
                        run_headless=True)
    i = 0
    try:
        for PATH in rootdir1.glob('**/*'):
            tile_grid_size = 1
            tile_size = tile_grid_size * 1024

            # Set up the BioReader
            with BioReader(PATH, backend='java',
                           max_workers=cpu_count()) as br:

                # Loop through timepoints
                for t in range(br.T):

                    # Loop through channels
                    for c in range(br.C):

                        with BioWriter(out_path.joinpath(f"final{i}.ome.tif"),
                                       metadata=br.metadata,
                                       backend='java') as bw:

                            # Loop through z-slices
                            for z in range(br.Z):

                                # Loop across the length of the image
                                for y in range(0, br.Y, tile_size):
                                    y_max = min([br.Y, y + tile_size])

                                    # Loop across the depth of the image
                                    for x in range(0, br.X, tile_size):
                                        x_max = min([br.X, x + tile_size])

                                        input_img = np.squeeze(br[y:y_max,
                                                                  x:x_max,
                                                                  z:z + 1, c,
                                                                  t])
                                        img = unet_segmentation(
                                            input_img, img_pixelsize_x,
                                            img_pixelsize_y, modelfile_path,
                                            weightfile_path, iofile_path)
                                        bw[y:y_max, x:x_max, ...] = img
                                        os.remove("output.h5")

            i += 1

    finally:
        # Close the javabridge. Since this is in the finally block, it is always run
        javabridge.kill_vm()
Beispiel #7
0
def unshade_image(img,
                  out_dir,
                  brightfield,
                  darkfield,
                  photobleach=None,
                  offset=None):

    with ProcessManager.thread() as active_threads:

        with BioReader(img, max_workers=active_threads.count) as br:

            with BioWriter(out_dir.joinpath(img.name),
                           metadata=br.metadata,
                           max_workers=active_threads.count) as bw:

                new_img = br[:, :, :1, 0, 0].squeeze().astype(np.float32)

                new_img = new_img - darkfield
                new_img = np.divide(new_img, brightfield)

                if photobleach != None:
                    new_img = new_img - np.float32(photobleach)
                if offset != None:
                    new_img = new_img + np.float32(offset)

                new_img[new_img < 0] = 0

                new_img = new_img.astype(br.dtype)

                bw[:] = new_img
Beispiel #8
0
def filter_by_size(file_paths: List[Path],
                   size_threshold: int) -> Tuple[List[Path], List[Path]]:
    """ Partitions the input files by the memory-footprint for the images.

    Args:
        file_paths: The list of files to partition.
        size_threshold: The memory-size (in MB) to use as a threshold.

    Returns:
        A 2-tuple of lists of paths.
         The first list contains small images and the second list contains large images.
    """
    small_files, large_files = list(), list()
    threshold: int = size_threshold * 1024 * 1024

    for file_path in file_paths:
        with BioReader(file_path) as reader:
            num_pixels = numpy.prod(reader.shape)
            dtype = reader.dtype

        if dtype in (numpy.uint8, bool):
            pixel_bytes = 8
        elif dtype == numpy.uint16:
            pixel_bytes = 16
        elif dtype == numpy.uint32:
            pixel_bytes = 32
        else:
            pixel_bytes = 64

        image_size = num_pixels * (pixel_bytes / 8)  # Convert bits to bytes
        (small_files
         if image_size <= threshold else large_files).append(file_path)

    return small_files, large_files
Beispiel #9
0
def iter_strip(file_path: Path, index: int, axis: int) -> TileIndices:
    """ A Generator of tile_indices in the indexed strip along the given axis.

    Args:
        file_path: Path to the image.
        index: index of the current strip.
        axis: 0 for a horizontal strip, 1 for a vertical strip

    Yields:
        A 4-tuple representing the coordinates of each tile.
    """
    with BioReader(file_path) as reader:
        if axis == 0:
            x_end, y_end = reader.X, reader.Y
        else:
            x_end, y_end = reader.Y, reader.X

    num_strips = y_end // constants.TILE_STRIDE
    if y_end % constants.TILE_STRIDE != 0:
        num_strips += 1
    num_tiles_in_strip = x_end // constants.TILE_STRIDE
    if x_end % constants.TILE_STRIDE != 0:
        num_tiles_in_strip += 1

    y_min = index * constants.TILE_STRIDE
    y_max = min(y_end, y_min + constants.TILE_STRIDE)

    for i in range(num_tiles_in_strip):
        x_min = i * constants.TILE_STRIDE
        x_max = min(x_end, x_min + constants.TILE_STRIDE)

        if axis == 0:
            yield x_min, x_max, y_min, y_max
        else:
            yield y_min, y_max, x_min, x_max
Beispiel #10
0
def write_corrected_images(
    *,
    group: list[utils.FPFileDict],
    channel_ordering: list[int],
    components_dir: Path,
    output_dir: Path,
):
    logger.info(f'writing corrected images...')

    files = [file['file'] for file in group]
    if len(channel_ordering) == 0:
        channel_ordering = list(range(len(files)))
    files = [files[c] for c in channel_ordering]

    for image_path in files:

        component_path = components_dir.joinpath(image_path.name)
        assert component_path.exists()

        output_path = output_dir.joinpath(image_path.name)
        if output_path.exists():
            continue
        logger.info(f'writing image {image_path.name}...')

        with BioReader(image_path) as image_reader, BioReader(
                component_path) as component_reader:
            with BioWriter(output_path,
                           metadata=image_reader.metadata) as writer:

                for y_min in range(0, writer.Y, utils.TILE_SIZE_2D):
                    y_max = min(writer.Y, y_min + utils.TILE_SIZE_2D)

                    for x_min in range(0, writer.X, utils.TILE_SIZE_2D):
                        x_max = min(writer.X, x_min + utils.TILE_SIZE_2D)

                        image_tile = numpy.squeeze(image_reader[y_min:y_max,
                                                                x_min:x_max, 0,
                                                                0, 0])
                        component_tile = numpy.squeeze(
                            component_reader[y_min:y_max, x_min:x_max, 0, 0,
                                             0])

                        writer[y_min:y_max, x_min:x_max, 0, 0,
                               0] = image_tile - component_tile

    return
Beispiel #11
0
 def load_and_store(fname, ind):
     with ProcessManager.thread() as active_threads:
         with BioReader(fname['file'],
                        max_workers=active_threads.count) as br:
             I = np.squeeze(br[:, :, :1, 0, 0])
         img_stack[:, :, ind] = cv2.resize(
             I, (OPTIONS['size'], OPTIONS['size']),
             interpolation=cv2.INTER_LINEAR).astype(np.float64)
Beispiel #12
0
def make_tile(x_min, x_max, y_min, y_max, stitchPath):
    """make_tile Create a supertile

    This method identifies images that have stitching vector positions
    within the bounds of the supertile defined by the x and y input
    arguments. It then spawns threads to load images and store in the
    supertile buffer. Finally it returns the assembled supertile to
    allow the main thread to generate the write thread.

    Args:
        x_min ([int]): Minimum x bound of the tile
        x_max ([int]): Maximum x bound of the tile
        y_min ([int]): Minimum y bound of the tile
        y_max ([int]): Maximum y bound of the tile
        stitchPath ([str]): Path to the stitching vector

    Returns:
        [type]: [description]
    """
    # Parse the stitching vector
    outvals = _parse_stitch(stitchPath, imgPath, True)

    # Get the data type
    br = BioReader(str(Path(imgPath).joinpath(outvals['filePos'][0]['file'])))
    dtype = br._pix['type']

    # initialize the supertile
    template = np.zeros((y_max - y_min, x_max - x_min, 1, 1, 1), dtype=dtype)

    # get images in bounds of current super tile
    with ThreadPoolExecutor(max([multiprocessing.cpu_count(), 2])) as executor:
        for f in outvals['filePos']:
            if (f['posX'] >= x_min and f['posX'] <= x_max) or (
                    f['posX'] + f['width'] >= x_min
                    and f['posX'] + f['width'] <= x_max):
                if (f['posY'] >= y_min and f['posY'] <= y_max) or (
                        f['posY'] + f['height'] >= y_min
                        and f['posY'] + f['height'] <= y_max):

                    # get bounds of image within the tile
                    Xt = [max(0, f['posX'] - x_min)]
                    Xt.append(
                        min(x_max - x_min, f['posX'] + f['width'] - x_min))
                    Yt = [max(0, f['posY'] - y_min)]
                    Yt.append(
                        min(y_max - y_min, f['posY'] + f['height'] - y_min))

                    # get bounds of image within the image
                    Xi = [max(0, x_min - f['posX'])]
                    Xi.append(min(f['width'], x_max - f['posX']))
                    Yi = [max(0, y_min - f['posY'])]
                    Yi.append(min(f['height'], y_max - f['posY']))

                    executor.submit(buffer_image,
                                    str(Path(imgPath).joinpath(f['file'])),
                                    template, Xi, Yi, Xt, Yt)

    return template
Beispiel #13
0
    def test_scalable_features(self):
        # First, load entire image and calculate features. 
        bunny_file = os.path.join(dir_path, 'test_data/bunny.ome.tif')
        with BioReader(bunny_file) as br:
            ref_img = np.squeeze(br[:])
        
        verts, faces, _, _ = measure.marching_cubes((ref_img == 255).astype(np.uint8), 0, allow_degenerate=False)
        ref_features = mesh.mesh_spectral_features(verts, faces, k=50, scale_invariant=False)

        self.assertEquals(len(ref_features), 50)

        # Now calculate features using chunking. 
        with BioReader(bunny_file) as br:
            labels, features = mesh.mesh_and_featurize_image(br, chunk_size=(20, 20, 20), num_features=50, scale_invariant=False)
        
        self.assertTrue(
            np.allclose(ref_features, features.flatten(), atol=1.e-3)
        )
Beispiel #14
0
    def tester(t, ij):
        try:
            print("Testing {} data type...".format(t))
            shape = (2048, 2048)
            print("Creating Array...")
            array = np.random.randint(0, 255, size=shape, dtype=np.uint16)
            print("Converting Array...")
            array = NUMPY_TYPES[t][0](array)
            dtype0 = ij.py.dtype(array)
            print("The initial data type is {}".format(dtype0))
            temp_path = Path(__file__).with_name("data-convert-temp")
            print("Writing image array to file...")
            with BioWriter(temp_path) as writer:
                writer.X = shape[0]
                writer.Y = shape[1]
                writer.dtype = array.dtype
                writer[:] = array[:]
            print("Reading image from file...")
            arr = BioReader(temp_path)
            print("Getting data type after reading image...")
            dtype1 = ij.py.dtype(arr[:, :, 0:1, 0, 0])
            print("Data type after reading image is {}".format(dtype1))
            # print('Trying to convert to PlanarImg')
            # planarimg = ij.planar(arr)
            if dtype0 != dtype1:
                print("Manully forcing data type back to {}".format(dtype0))
                arr = NUMPY_TYPES[t][0](arr[:, :, 0:1, 0, 0])
                print("Converting to Java object...")
                arr = ij_converter.to_java(ij, np.squeeze(arr), "ArrayImg")
                print("Getting data type after manually forcing...")
                dtype2 = ij.py.dtype(arr)
                print("Data type after manual forcing is {}".format(dtype2))
                val_dtype = dtype2
            else:
                arr = ij_converter.to_java(ij, np.squeeze(arr[:, :, 0:1, 0,
                                                              0]), "ArrayImg")
                val_dtype = dtype1

            value = 5
            print(
                "Converting input (value) to Java primitive type {}...".format(
                    val_dtype))
            val = ij_converter.to_java(ij, value, t, val_dtype)
            print("Calling ImageJ op...")
            out = ij.op().math().add(arr, val)
            print("The op was SUCCESSFUL with data type {}".format(t))

        except:
            print("Testing data type {} was NOT SUCCESSFUL".format(t))
            print(traceback.format_exc())

        finally:
            print("Shutting down JVM...\n\n")
            del ij
            jpype.shutdownJVM()
Beispiel #15
0
def write_cropped_images(
        file_paths: list[Path],
        output_dir: Path,
        bounding_box: helpers.BoundingBox,
):
    """ Crops and writes the given group of images using the given bounding box.

    Args:
        file_paths: A list of Paths for the input images.
        output_dir: A Path to the output directory.
        bounding_box: The bounding-box to use for cropping the images

    """
    z1, z2, y1, y2, x1, x2 = bounding_box
    out_depth, out_width, out_height = z2 - z1, y2 - y1, x2 - x1
    logger.info(f'Superset bounding {bounding_box = }...')
    logger.info(f'Cropping to shape (z, y, x) = {out_depth, out_width, out_height}...')

    for file_path in file_paths:
        out_path = output_dir.joinpath(helpers.replace_extension(file_path.name))
        logger.info(f'Writing {out_path.name}...')

        with BioReader(file_path) as reader:
            with BioWriter(out_path, metadata=reader.metadata, max_workers=constants.NUM_THREADS) as writer:
                writer.Z = out_depth
                writer.Y = out_width
                writer.X = out_height

                for z_out in range(writer.Z):
                    z_in = z_out + z1

                    for out_y in range(0, writer.Y, constants.TILE_STRIDE):
                        out_y_max = min(writer.Y, out_y + constants.TILE_STRIDE)
                        in_y = out_y + y1
                        in_y_max = min(y2, in_y + constants.TILE_STRIDE)

                        for out_x in range(0, writer.X, constants.TILE_STRIDE):
                            out_x_max = min(writer.X, out_x + constants.TILE_STRIDE)
                            in_x = out_x + x1
                            in_x_max = min(x2, in_x + constants.TILE_STRIDE)

                            try:
                                tile = reader[in_y:in_y_max, in_x:in_x_max, z_in:z_in + 1, 0, 0]
                                writer[out_y:out_y_max, out_x:out_x_max, z_out:z_out + 1, 0, 0] = tile[:]
                            except AssertionError as e:
                                logger.error(
                                    f'failed to read tile {(in_y, in_y_max, in_x, in_x_max, z_in, z_in + 1) = }\n'
                                    f'and write to {(out_y, out_y_max, out_x, out_x_max, z_out, z_out + 1) = }\n'
                                    f'because {e}'
                                )
                                raise e
    return
Beispiel #16
0
def unshade_batch(files: typing.List[Path],
                  out_dir: Path,
                  brightfield: Path,
                  darkfield: Path,
                  photobleach: typing.Optional[Path] = None):

    if photobleach != None:
        with open(photobleach, 'r') as f:
            reader = csv.reader(f)
            photo_offset = {
                line[0]: float(line[1])
                for line in reader if line[0] != 'file'
            }
        offset = np.mean([o for o in photo_offset.values()])
    else:
        offset = None

    with ProcessManager.process():

        with BioReader(brightfield, max_workers=2) as bf:
            brightfield_image = bf[:, :, :, 0, 0].squeeze()

        with BioReader(darkfield, max_workers=2) as df:
            darkfield_image = df[:, :, :, 0, 0].squeeze()

        threads = []

        for file in files:

            if photobleach != None:
                pb = photo_offset[file['file']]
            else:
                pb = None

            ProcessManager.submit_thread(unshade_image, file['file'], out_dir,
                                         brightfield_image, darkfield_image,
                                         pb, offset)

        ProcessManager.join_threads()
Beispiel #17
0
def _estimate_pearson_coefficient(
    path_1: Path,
    path_2: Path,
    selected_tiles: utils.TileIndices,
) -> float:
    total_r = 0

    with BioReader(path_1) as reader_1, BioReader(path_2) as reader_2:
        for _, _, y_min, y_max, x_min, x_max in selected_tiles:
            tile_1 = numpy.squeeze(reader_1[y_min:y_max, x_min:x_max, 0, 0,
                                            0]).flatten()
            tile_2 = numpy.squeeze(reader_2[y_min:y_max, x_min:x_max, 0, 0,
                                            0]).flatten()

            random_indices = numpy.random.permutation(
                tile_1.shape[0])[:100_000]
            tile_1 = tile_1[random_indices]
            tile_2 = tile_2[random_indices]

            total_r += scipy.stats.pearsonr(tile_1, tile_2)[0]

    return total_r / len(selected_tiles)
Beispiel #18
0
def assemble_image(vector_path: pathlib.Path, out_path: pathlib.Path,
                   depth: int) -> None:
    """Assemble a 2d or 3d image

    This method assembles one image from one stitching vector. It can 
    assemble both 2d and z-stacked 3d images It is intended to run as 
    a process to parallelize stitching of multiple images.

    The basic approach to stitching is:
    1. Parse the stitching vector and abstract the image dimensions
    2. Generate a thread for each subsection (supertile) of an image.

    Args:
        vector_path: Path to the stitching vector
        out_path: Path to the output directory
        depth: depth of the input images
    """

    # Grab a free process
    with ProcessManager.process():

        # Parse the stitching vector
        parsed_vector = _parse_stitch(vector_path, timesliceNaming)

        # Initialize the output image
        with BioReader(parsed_vector['filePos'][0]['file']) as br:
            bw = BioWriter(out_path.joinpath(parsed_vector['name']),
                           metadata=br.metadata,
                           max_workers=ProcessManager._active_threads)
            bw.x = parsed_vector['width']
            bw.y = parsed_vector['height']
            bw.z = depth

        # Assemble the images
        ProcessManager.log(f'Begin assembly')

        for z in range(depth):
            ProcessManager.log(f'Assembling Z position : {z}')
            for x in range(0, parsed_vector['width'], chunk_size):
                X_range = min(x + chunk_size, parsed_vector['width']
                              )  # max x-pixel index in the assembled image
                for y in range(0, parsed_vector['height'], chunk_size):
                    Y_range = min(y + chunk_size, parsed_vector['height']
                                  )  # max y-pixel index in the assembled image

                    ProcessManager.submit_thread(make_tile, x, X_range, y,
                                                 Y_range, z, parsed_vector, bw)

            ProcessManager.join_threads()

        bw.close()
Beispiel #19
0
def buffer_image(image_path, supertile_buffer, Xi, Yi, Xt, Yt):
    """buffer_image Load and image and store in buffer

    This method loads an image and stores it in the appropriate
    position based on the stitching vector coordinates within
    a large tile of the output image. It is intended to be
    used as a thread to increase the reading component to
    assembling the image.
    
    Args:
        image_path ([str]): Path to image to load
        supertile_buffer ([np.ndarray]): A supertile storing multiple images
        Xi ([list]): Xmin and Xmax of pixels to load from the image
        Yi ([list]): Ymin and Ymax of pixels to load from the image
        Xt ([list]): X position within the buffer to store the image
        Yt ([list]): Y position within the buffer to store the image
    """

    # Load the image
    br = BioReader(image_path, max_workers=2)
    image = br.read_image(X=Xi, Y=Yi)  # only get the first z,c,t layer

    # Put the image in the buffer
    supertile_buffer[Yt[0]:Yt[1], Xt[0]:Xt[1], ...] = image
Beispiel #20
0
def _get_resized_image_stack(flist):
    """ Load all images in a list and resize to OPTIONS['size']

    When files are parsed, the variables are used in an index to provide a
    method to reference a specific file name by its dimensions. This function
    returns the variable index based on the input filename pattern.

    Inputs:ed th
        flist - Paths of list of images to load and resize
    Outputs:
        img_stack - A 3D stack of 2D images
        X - width of image
        Y - height of image
    """

    #Initialize the output
    with BioReader(flist[0]['file']) as br:
        X = br.x
        Y = br.y

    if len(flist) > OPTIONS['n_sample']:
        N = OPTIONS['n_sample']
        samples = np.random.randint(len(flist), size=(N, ),
                                    replace=False).tolist()
        flist = [flist[s] for s in samples]
    else:
        N = len(flist)

    img_stack = np.zeros((OPTIONS['size'], OPTIONS['size'], N),
                         dtype=np.float64)

    def load_and_store(fname, ind):
        with ProcessManager.thread() as active_threads:
            with BioReader(fname['file'],
                           max_workers=active_threads.count) as br:
                I = np.squeeze(br[:, :, :1, 0, 0])
            img_stack[:, :, ind] = cv2.resize(
                I, (OPTIONS['size'], OPTIONS['size']),
                interpolation=cv2.INTER_LINEAR).astype(np.float64)

    # Load every image as a z-slice
    with ThreadPoolExecutor() as executor:
        for ind, fname in enumerate(flist):
            executor.submit(load_and_store, fname, ind)

    return img_stack, X, Y
Beispiel #21
0
def iter_tiles_2d(file_path: Path) -> TileIndices:
    """ A Generator of tile_indices in a 3d image.

    Args:
        file_path: Path to the image.

    Yields:
        A 4-tuple representing the coordinates of each tile.
    """
    with BioReader(file_path) as reader:
        x_end, y_end = reader.X, reader.Y

    for y_min in range(0, y_end, constants.TILE_STRIDE):
        y_max = min(y_end, y_min + constants.TILE_STRIDE)

        for x_min in range(0, x_end, constants.TILE_STRIDE):
            x_max = min(x_end, x_min + constants.TILE_STRIDE)

            yield x_min, x_max, y_min, y_max
Beispiel #22
0
def verify_group_shape(file_paths: list[Path]):
    """ Verifies that all given images have the same x, y, and z dimensions.

    Args:
        file_paths: A list of file-paths that belong to the same group.
    """
    # Verify that all images in the group have the same dimensions.
    depths, heights, widths = set(), set(), set()
    for file_path in file_paths:
        with BioReader(file_path) as reader:
            depths.add(reader.Z), heights.add(reader.X), widths.add(reader.Y)

    if len(depths) > 1 or len(heights) > 1 or len(widths) > 1:
        message = 'Group contains images which have different dimensions.'
        logger.error(message)
        raise ValueError(message)

    logger.info(f'Starting from shape {(depths.pop(), heights.pop(), widths.pop())}...')
    return
Beispiel #23
0
def label_cython(input_path: Path, output_path: Path, connectivity: int):
    """ Label the input image and writes labels back out.

    Args:
        input_path: Path to input image.
        output_path: Path for output image.
        connectivity: Connectivity kind.
    """
    with ProcessManager.thread() as active_threads:
        with BioReader(
                input_path,
                max_workers=active_threads.count,
        ) as reader:

            with BioWriter(
                    output_path,
                    max_workers=active_threads.count,
                    metadata=reader.metadata,
            ) as writer:
                # Load an image and convert to binary
                image = numpy.squeeze(reader[..., 0, 0])

                if not numpy.any(image):
                    writer.dtype = numpy.uint8
                    writer[:] = numpy.zeros_like(image, dtype=numpy.uint8)
                    return

                image = (image > 0)
                if connectivity > image.ndim:
                    ProcessManager.log(
                        f'{input_path.name}: Connectivity is not less than or equal to the number of image dimensions, '
                        f'skipping this image. connectivity={connectivity}, ndim={image.ndim}'
                    )
                    return

                # Run the labeling algorithm
                labels = ftl.label_nd(image, connectivity)

                # Save the image
                writer.dtype = labels.dtype
                writer[:] = labels
    return True
def zarr_to_tif(zarr_path: Path, out_path: Path):
    with BioReader(zarr_path, max_workers=utils.NUM_THREADS) as reader:
        with BioWriter(out_path,
                       metadata=reader.metadata,
                       max_workers=utils.NUM_THREADS) as writer:
            writer.dtype = numpy.uint32

            for z in range(reader.Z):

                for y in range(0, reader.Y, utils.TILE_SIZE):
                    y_max = min(reader.Y, y + utils.TILE_SIZE)

                    for x in range(0, reader.X, utils.TILE_SIZE):
                        x_max = min(reader.X, x + utils.TILE_SIZE)

                        tile = reader[y:y_max, x:x_max, z:z + 1, 0, 0]
                        writer[y:y_max, x:x_max, z:z + 1, 0, 0] = tile

    shutil.rmtree(zarr_path)
    return
Beispiel #25
0
    def test_tile_generator(self):
        with BioReader(self.infile.name) as reader:
            for index in range(self.num_strips):
                for axis in (0, 1):
                    tiles = list(
                        helpers.iter_strip(Path(self.infile.name), index,
                                           axis))
                    self.assertEqual(len(tiles), self.num_strips)

                    for i, (x, x_max, y, y_max) in enumerate(tiles):
                        tile = reader[y:y_max, x:x_max, 0:1, 0, 0]
                        tile = tile if axis == 0 else numpy.transpose(tile)
                        true_rows = self.hanging if index == (
                            self.num_strips - 1) else constants.TILE_STRIDE
                        true_cols = self.hanging if i == (
                            self.num_strips - 1) else constants.TILE_STRIDE
                        self.assertEqual(
                            tile.shape, (true_rows, true_cols),
                            f'index {index}, axis {axis}, tile {i}, shape {tile.shape}'
                        )
        return
    def _score_tiles_thread(
            self, file_path: Path) -> tuple[utils.ScoresDict, int, int]:
        """ This method runs in a single thread and scores all tiles for a
            single file.

        Args:
            file_path: Path to image for which the tiles need to be scored.

        Returns:
            A Dictionary of tile-scores. See `utils/types.py`
        """
        with BioReader(file_path, max_workers=utils.NUM_THREADS) as reader:

            scores_dict: utils.ScoresDict = dict()
            logger.info(f'Ranking tiles in {file_path.name}...')
            num_tiles = utils.count_tiles(reader)
            image_min = numpy.iinfo(reader.dtype).max
            image_max = -numpy.iinfo(reader.dtype).min

            for i, (_, _, y_min, y_max, x_min,
                    x_max) in enumerate(utils.tile_indices(reader)):
                if i % 10 == 0:
                    logger.info(
                        f'Ranking tiles in {file_path.name}. Progress {100 * i / num_tiles:6.2f} %'
                    )

                tile = numpy.squeeze(reader[y_min:y_max, x_min:x_max, 0, 0, 0])

                # TODO: Actually handle 3d images properly with 3d tile-chunks.
                key = (0, 1, y_min, y_max, x_min, x_max)
                if key in scores_dict:
                    scores_dict[key] = (max if self.__is_high_better else min)(
                        scores_dict[key], self._score_tile(tile))
                else:
                    scores_dict[key] = self._score_tile(tile)

                image_min = numpy.min(tile[tile > 0], initial=image_min)
                image_max = numpy.max(tile, initial=image_max)

        return scores_dict, image_min, image_max
Beispiel #27
0
def load_img(image_path, target_size=None):
    img = BioReader(image_path, backend='python').read().squeeze()
    dim = len(img.shape)

    # Only grayscale or RGB
    if dim not in [2, 3]:
        return None

    # Only three channels.
    if (dim == 3) and (img.shape[-1] != 3):
        return None

    # If the image is grayscale, expand to three dims.
    if dim == 2:
        img = np.stack((img, ) * 3, axis=-1)

    if target_size is not None:
        img = transform.resize(img,
                               output_shape=target_size,
                               anti_aliasing=True)

    return img
Beispiel #28
0
def main(imgPath: pathlib.Path,
         stitchPath: pathlib.Path,
         outDir: pathlib.Path,
         timesliceNaming: typing.Optional[bool]
         ) -> None:

    '''Setup stitching variables/objects'''
    # Get a list of stitching vectors
    vectors = list(stitchPath.iterdir())
    vectors.sort()

    # Try to infer a filepattern from the files on disk for faster matching later
    global fp # make the filepattern global to share between processes
    try:
        pattern = filepattern.infer_pattern([f.name for f in imgPath.iterdir()])
        logger.info(f'Inferred file pattern: {pattern}')
        fp = filepattern.FilePattern(imgPath,pattern)

    # Pattern inference didn't work, so just get a list of files
    except:
        logger.info(f'Unable to infer pattern, defaulting to: .*')
        fp = filepattern.FilePattern(imgPath,'.*')

    # get z depth
    with BioReader(next(imgPath.iterdir())) as br:
        depth = br.z

    '''Run stitching jobs in separate processes'''
    ProcessManager.init_processes('main','asmbl')

    for v in vectors:
        # Check to see if the file is a valid stitching vector
        if 'img-global-positions' not in v.name:
            continue

        ProcessManager.submit_process(assemble_image,v,outDir, depth)

    ProcessManager.join_processes()
Beispiel #29
0
def determine_bounding_box_thread(
        file_path: Path,
        smoothing: bool,
        crop_y: bool,
        crop_x: bool,
        z_index: int,
) -> tuple[int, int, int, int]:
    with BioReader(file_path) as reader:
        x_end, y_end, z_end = reader.X, reader.Y, reader.Z

    if crop_y:
        y1 = find_gradient_spike_xy(file_path, z_index, True, True, smoothing)
        y2 = find_gradient_spike_xy(file_path, z_index, True, False, smoothing)
    else:
        y1, y2 = 0, y_end

    if crop_x:
        x1 = find_gradient_spike_xy(file_path, z_index, False, True, smoothing)
        x2 = find_gradient_spike_xy(file_path, z_index, False, False, smoothing)
    else:
        x1, x2 = 0, x_end

    return y1, y2, x1, x2
Beispiel #30
0
def estimate_slice_entropies(file_path: Path, smoothing: bool) -> list[float]:
    with BioReader(file_path) as reader:
        z_end = reader.Z

    # Find a bounding box for each image in the group.
    slice_histograms = list()
    with ProcessPoolExecutor(max_workers=constants.NUM_THREADS) as executor:
        processes = [
            executor.submit(
                estimate_slice_entropies_thread,
                file_path,
                smoothing,
                z,
            )
            for z in range(z_end)
        ]
        for process in processes:
            slice_histograms.append(process.result())

    return [
        scipy.stats.entropy([c for _, c in histogram.bins])
        for histogram in slice_histograms
    ]