def mean_projection(br, bw, x_range, y_range, **kwargs): """ Calculate the mean intensity projection Args: br (BioReader object): input file object bw (BioWriter object): output file object x_range (tuple): x-range of the img to be processed y_range (tuple): y-range of the img to be processed Returns: image array : Mean IP of the input volume """ with ProcessManager.thread(): br.max_workers = ProcessManager._active_threads bw.max_workers = ProcessManager._active_threads # x,y range of the volume x, x_max = x_range y, y_max = y_range # iterate over depth out_image = np.zeros((y_max - y, x_max - x), dtype=np.float64) for ind, z in enumerate(range(0, br.Z, tile_size_z)): z_max = min([br.Z, z + tile_size_z]) out_image += np.sum(br[y:y_max, x:x_max, z:z_max, ...].astype(np.float64), axis=2).squeeze() # output image out_image /= br.Z bw[y:y_max, x:x_max, 0:1, 0, 0] = out_image.astype(br.dtype)
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 load_and_scale(*args,**kwargs): sub_image = _get_higher_res(**kwargs) with ProcessManager.thread(): image = args[0] x_ind = args[1] y_ind = args[2] image[y_ind[0]:y_ind[1],x_ind[0]:x_ind[1]] = kwargs['slide_writer'].scale(sub_image)
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 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 max_min_projection(br, bw, x_range, y_range, **kwargs): """ Calculate the max or min intensity projection of a section of the input image. Args: br (BioReader object): input file object bw (BioWriter object): output file object x_range (tuple): x-range of the img to be processed y_range (tuple): y-range of the img to be processed Returns: image array : Max IP of the input volume """ with ProcessManager.thread(): br.max_workers = ProcessManager._active_threads bw.max_workers = ProcessManager._active_threads # set projection method if not 'method' in kwargs: method = np.max else: method = kwargs['method'] # x,y range of the volume x, x_max = x_range y, y_max = y_range # iterate over depth for ind, z in enumerate(range(0, br.Z, tile_size_z)): z_max = min([br.Z, z + tile_size_z]) if ind == 0: out_image = method(br[y:y_max, x:x_max, z:z_max, 0, 0], axis=2) else: out_image = np.dstack((out_image, method(br[y:y_max, x:x_max, z:z_max, 0, 0], axis=2))) # output image bw[y:y_max, x:x_max, 0:1, 0, 0] = method(out_image, axis=2)
def label_thread(input_path, output_path, connectivity): with ProcessManager.thread() as active_threads: with bfio.BioReader(input_path, max_workers=active_threads.count) as br: with bfio.BioWriter(output_path, max_workers=active_threads.count, metadata=br.metadata) as bw: # Load an image and convert to binary image = (br[..., 0, 0] > 0).squeeze() if connectivity > image.ndim: ProcessManager.log( "{}: Connectivity is not less than or equal to the number of image dimensions, skipping this image. connectivity={}, ndim={}" .format(input_path.name, connectivity, image.ndim)) return # Run the labeling algorithm labels = ftl.label_nd(image.squeeze(), connectivity) # Save the image bw.dtype = labels.dtype bw[:] = labels
def make_tile(x_min: int, x_max: int, y_min: int, y_max: int, z: int, parsed_vector: dict, bw: BioWriter) -> None: """Create a supertile from images and save to file This method builds a supertile, which is a section of the image defined by the global variable ``chunk_size`` and is composed of multiple smaller tiles defined by the ``BioReader._TILE_SIZE``. Images are stored on disk as compressed chunks that are ``_TILE_SIZE`` length and width, and the upper left pixel of a tile is always a multiple of ``_TILE_SIZE``. To prevent excessive file loading and to ensure files are properly placed, supertiles are created from smaller images and saved all at once. Args: x_min: Minimum x bound of the tile x_max: Maximum x bound of the tile y_min: Minimum y bound of the tile y_max: Maximum y bound of the tile z: Current z position to assemble parsed_vector: The result of _parse_vector local_threads: Used to determine the number of concurrent threads to run bw: The output file object """ with ProcessManager.thread() as active_threads: # Get the data type with BioReader(parsed_vector['filePos'][0]['file']) as br: dtype = br.dtype # initialize the supertile template = numpy.zeros((y_max-y_min,x_max-x_min,1,1,1),dtype=dtype) # get images in bounds of current super tile for f in parsed_vector['filePos']: # check that image is within the x-tile bounds if (f['posX'] >= x_min and f['posX'] <= x_max) \ or (f['posX']+f['width'] >= x_min and f['posX']+f['width'] <= x_max) \ or (f['posX'] <= x_min and f['posX']+f['width'] >= x_max): # check that image is within the y-tile bounds if (f['posY'] >= y_min and f['posY'] <= y_max) \ or (f['posY']+f['height'] >= y_min and f['posY']+f['height'] <= y_max) \ or (f['posY'] <= 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'])) # Load the image with BioReader(f['file'],max_workers=active_threads.count) as br: image = br[Yi[0]:Yi[1],Xi[0]:Xi[1],z:z+1,0,0] # only get the first c,t layer # Put the image in the buffer template[Yt[0]:Yt[1],Xt[0]:Xt[1],...] = image # Save the image bw.max_workers = ProcessManager._active_threads bw[y_min:y_max,x_min:x_max,z:z+1,0,0] = template
def _get_higher_res(S: int, slide_writer: PyramidWriter, X: typing.Tuple[int,int] = None, Y: typing.Tuple[int,int] = None, Z: typing.Tuple[int,int] = (0,1)): """ Recursive function for pyramid building This is a recursive function that builds an image pyramid by indicating an original region of an image at a given scale. This function then builds a pyramid up from the highest resolution components of the pyramid (the original images) to the given position resolution. As an example, imagine the following possible pyramid: Scale S=0 1234 / \ Scale S=1 12 34 / \ / \ Scale S=2 1 2 3 4 At scale 2 (the highest resolution) there are 4 original images. At scale 1, images are averaged and concatenated into one image (i.e. image 12). Calling this function using S=0 will attempt to generate 1234 by calling this function again to get image 12, which will then call this function again to get image 1 and then image 2. Note that this function actually builds images in quadrants (top left and right, bottom left and right) rather than two sections as displayed above. Due to the nature of how this function works, it is possible to build a pyramid in parallel, since building the subpyramid under image 12 can be run independently of the building of subpyramid under 34. Args: S: Top level scale from which the pyramid will be built file_path: Path to image slide_writer: object used to encode and write pyramid tiles X: Range of X values [min,max] to get at the indicated scale Y: Range of Y values [min,max] to get at the indicated scale Returns: image: The image corresponding to the X,Y values at scale S """ # Get the scale info scale_info = slide_writer.scale_info(S) if X == None: X = [0,scale_info['size'][0]] if Y == None: Y = [0,scale_info['size'][1]] # Modify upper bound to stay within resolution dimensions if X[1] > scale_info['size'][0]: X[1] = scale_info['size'][0] if Y[1] > scale_info['size'][1]: Y[1] = scale_info['size'][1] if str(S)==slide_writer.scale_info(-1)['key']: with ProcessManager.thread(): with bfio.BioReader(slide_writer.image_path,max_workers=1) as br: image = br[Y[0]:Y[1],X[0]:X[1],Z[0]:Z[1],...].squeeze() # Write the chunk slide_writer.store_chunk(image,str(S),(X[0],X[1],Y[0],Y[1])) return image else: # Initialize the output image = np.zeros((Y[1]-Y[0],X[1]-X[0]),dtype=slide_writer.dtype) # Set the subgrid dimensions subgrid_dims = [[2*X[0],2*X[1]],[2*Y[0],2*Y[1]]] for dim in subgrid_dims: while dim[1]-dim[0] > CHUNK_SIZE: dim.insert(1,dim[0] + ((dim[1] - dim[0]-1)//CHUNK_SIZE) * CHUNK_SIZE) def load_and_scale(*args,**kwargs): sub_image = _get_higher_res(**kwargs) with ProcessManager.thread(): image = args[0] x_ind = args[1] y_ind = args[2] image[y_ind[0]:y_ind[1],x_ind[0]:x_ind[1]] = kwargs['slide_writer'].scale(sub_image) with ThreadPoolExecutor(1) as executor: for y in range(0,len(subgrid_dims[1])-1): y_ind = [subgrid_dims[1][y] - subgrid_dims[1][0],subgrid_dims[1][y+1] - subgrid_dims[1][0]] y_ind = [np.ceil(yi/2).astype('int') for yi in y_ind] for x in range(0,len(subgrid_dims[0])-1): x_ind = [subgrid_dims[0][x] - subgrid_dims[0][0],subgrid_dims[0][x+1] - subgrid_dims[0][0]] x_ind = [np.ceil(xi/2).astype('int') for xi in x_ind] executor.submit(load_and_scale, image,x_ind,y_ind, # args X=subgrid_dims[0][x:x+2], # kwargs Y=subgrid_dims[1][y:y+2], Z=Z, S=S+1, slide_writer=slide_writer) # Write the chunk slide_writer.store_chunk(image,str(S),(X[0],X[1],Y[0],Y[1])) return image