def save(self, tile_indices, output_dir, tile): # Overwrite the original preprocessed tile with corrected version path = cytokit_io.get_processor_img_path(tile_indices.region_index, tile_indices.tile_x, tile_indices.tile_y) cytokit_io.save_tile(osp.join(output_dir, path), tile, config=self.config) return path
def postprocess_tile(tile, tile_indices, ops, log_fn, task_config): output_dir = task_config.output_dir # Illumination Correction if ops.illumination_op: # Prepare and save illumination images, if not already done ops.illumination_op.prepare_region_data(output_dir) path = ops.illumination_op.save_region_data(output_dir) if path is not None: log_fn('Illumination data saved to "{}"'.format(path)) # Run correction for tile tile = ops.illumination_op.run(tile, tile_indices) log_fn('Illumination correction complete', tile) else: log_fn('Skipping illumination correction', debug=True) # Spectral Unmixing if ops.unmixing_op: # Prepare unmixing models for each region ops.unmixing_op.prepare_region_data(output_dir) # Run correction for tile tile = ops.unmixing_op.run(tile, tile_indices) log_fn('Spectral unmixing complete', tile) else: log_fn('Skipping spectral unmixing', debug=True) # Get best focus data # TODO Prevent needing to re-read the processor data file each time best_focus_data = function_data.get_best_focus_coord_map(output_dir) best_focus_z_plane = best_focus_data[(tile_indices.region_index, tile_indices.tile_x, tile_indices.tile_y)] # Rerun cytometry based on corrected tile tile, cyto_data = ops.cytometry_op.run( tile, best_focus_z_plane=best_focus_z_plane) paths = ops.cytometry_op.save(tile_indices, output_dir, cyto_data) log_fn( 'Postprocessing cytometry complete; Statistics saved to "{}"'.format( paths[-1]), cyto_data[0]) # Save resulting tile path = cytokit_io.get_processor_img_path(tile_indices.region_index, tile_indices.tile_x, tile_indices.tile_y) cytokit_io.save_tile(osp.join(output_dir, path), tile, config=task_config.exp_config) log_fn('Saved postprocessed tile to "{}"'.format(path), tile)
def preprocess_tile(tile, tile_indices, ops, log_fn, task_config): output_dir = task_config.output_dir # Drift Compensation if ops.align_op: tile = ops.align_op.run(tile) log_fn('Drift compensation complete', tile) else: log_fn('Skipping drift compensation', debug=True) # Crop off overlap in imaging process if ops.crop_op: tile = ops.crop_op.run(tile) log_fn('Tile overlap crop complete', tile) else: log_fn('Skipping tile crop', debug=True) # Resample images for improved downstream speed if ops.resize_op: tile = ops.resize_op.run(tile) log_fn('Tile resize complete', tile) else: log_fn('Skipping tile resize', debug=True) # Best Focal Plane Selection best_focus_data = None if ops.focus_op: # Used the cropped, but un-deconvolved tile for focal plane selection best_focus_data = ops.focus_op.run(tile) ops.focus_op.save(tile_indices, output_dir, best_focus_data) log_fn('Focal plane selection complete', best_focus_data[0]) else: log_fn('Skipping focal plane selection', debug=True) # Deconvolution if ops.decon_op: tile = ops.decon_op.run(tile) log_fn('Deconvolution complete', tile) else: log_fn('Skipping deconvolution', debug=True) # Cytometry (segmentation + quantification) if ops.cytometry_op: best_focus_z_plane = best_focus_data[1] if best_focus_data else None tile, cyto_data = ops.cytometry_op.run( tile, best_focus_z_plane=best_focus_z_plane, tile_indices=tile_indices) paths = ops.cytometry_op.save(tile_indices, output_dir, cyto_data) log_fn( 'Tile cytometry complete; Statistics saved to "{}"'.format( paths[-1]), cyto_data[0]) else: log_fn('Skipping tile cytometry', debug=True) # Tile summary statistic operations if ops.summary_op: ops.summary_op.run(tile) log_fn('Tile statistic summary complete') else: log_fn('Skipping tile statistic summary', debug=True) # Save the output tile if tile generation/assembly was enabled if task_config.op_flags.run_tile_generator: path = cytokit_io.get_processor_img_path(tile_indices.region_index, tile_indices.tile_x, tile_indices.tile_y) cytokit_io.save_tile(osp.join(output_dir, path), tile, config=task_config.exp_config) log_fn('Saved preprocessed tile to path "{}"'.format(path), tile)
def get_tile_montage(config, image_dir, hyperstack, icyc=0, iz=0, ich=0, ireg=0, bw=0, bv_fn=None, allow_missing=False, imread_fn=None): """Generate a montage image for a specific cycle, z-plane, channel, and region This function supports both raw, flattened 2D images as well as consolidated, 5D hyperstacks (as determined by `hyperstack` argument) Args: config: Experiment configuration image_dir: Location of tiled images; These should include all z-planes, cycles, and channels in individual tif files (e.g. the output of the pre-processing or segmentation pipelines) hyperstack: Flag indicating whether or not images are 5D hyperstacks or flattened 2D images: - Hyperstacks are typically results from any sort of processing or segmentation step - Flattened 2D images are typically raw files generated directly from a microscope icyc: 0-based cycle index iz: 0-based z-plane index ich: 0-based channel index ireg: 0-based region index bw: Border width (in pixels) to add to each tile in the montage image, which useful for determining tile location within the montage; If <= 0, this parameter will do nothing bv_fn: Border value function with signature `fn(tile_x, tile_y) --> float`; if not given all border values are assigned a value of 0 allow_missing: Flag indicating whether or not to allow missing tiles into the montage; defaults to false and is generally only useful when debugging missing data imread_fn: When not using 5D hyperstacks (i.e. reading raw image files) this can be useful for cases when, for example, raw, single-channel files are actually 3 channel files with the first two channels blank (this happens w/ Keyence somehow). This function will take an image path and must return a single 2D image with shape (rows, cols) Returns: A (usually very large) 2D array containing all tiles stitched together """ tile_indexes = list(range(config.n_tiles_per_region)) tw, th = config.tile_width, config.tile_height tiles = [] for itile in tile_indexes: tx, ty = config.get_tile_coordinates(itile) # If operating on a hyperstack, extract the appropriate slice to add to the montage if hyperstack: path = cytokit_io.get_processor_img_path(ireg, tx, ty) path = osp.join(image_dir, path) if not osp.exists(path) and allow_missing: tile = np.zeros((th, tw)) else: tile = cytokit_io.read_tile(path) tile = tile[icyc, iz, ich, :, :] # Otherwise, assume raw acquisition files are to be loaded and then cropped before being added else: path = cytokit_io.get_raw_img_path(ireg, itile, icyc, ich, iz) path = osp.join(image_dir, path) if not osp.exists(path) and allow_missing: tile = np.zeros((th, tw)) else: tile = cytokit_io.read_image(path) if imread_fn is None else imread_fn(path) if tile.ndim != 2: raise ValueError( 'Expecting 2D image at path "{}" but shape found is {}. Consider using the ' '`imread_fn` argument to specify a custom function to open files or if already using it, ' 'make sure that results are 2D' .format(path, tile.shape) ) tile = tile_crop.apply_slice(tile, tile_crop.get_slice(config)) # Highlight borders, if configured to do so if bw > 0: bv = 0 if bv_fn is None else bv_fn(tx, ty) tile[0:bw, :] = bv tile[-bw:, :] = bv tile[:, 0:bw] = bv tile[:, -bw:] = bv # Add to montage tiles.append(tile) return core.montage(tiles, config)