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()
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), )
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)
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()
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
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
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
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
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)
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
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) )
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()
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
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()
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)
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()
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
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
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
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
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
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
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
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()
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
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 ]