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 ST6GAL1_HiPSC_Pipeline(struct_img, rescale_ratio, output_type, output_path, fn, output_func=None): ########################################################################## # PARAMETERS: # note that these parameters are supposed to be fixed for the structure # and work well accross different datasets intensity_norm_param = [9, 19] gaussian_smoothing_sigma = 1 gaussian_smoothing_truncate_range = 3.0 cell_wise_min_area = 1200 dot_3d_sigma = 1.6 dot_3d_cutoff = 0.02 minArea = 10 thin_dist = 1 thin_dist_preserve = 1.6 ########################################################################## out_img_list = [] out_name_list = [] ################### # PRE_PROCESSING ################### # intenisty normalization (min/max) struct_img = intensity_normalization(struct_img, scaling_param=intensity_norm_param) out_img_list.append(struct_img.copy()) out_name_list.append('im_norm') # rescale if needed if rescale_ratio > 0: struct_img = processing.resize(struct_img, [1, rescale_ratio, rescale_ratio], method="cubic") struct_img = (struct_img - struct_img.min() + 1e-8) / (struct_img.max() - struct_img.min() + 1e-8) gaussian_smoothing_truncate_range = gaussian_smoothing_truncate_range * rescale_ratio # smoothing with gaussian filter structure_img_smooth = image_smoothing_gaussian_3d( struct_img, sigma=gaussian_smoothing_sigma, truncate_range=gaussian_smoothing_truncate_range) out_img_list.append(structure_img_smooth.copy()) out_name_list.append('im_smooth') ################### # core algorithm ################### # cell-wise local adaptive thresholding th_low_level = threshold_triangle(structure_img_smooth) bw_low_level = structure_img_smooth > th_low_level bw_low_level = remove_small_objects(bw_low_level, min_size=cell_wise_min_area, connectivity=1, in_place=True) bw_low_level = dilation(bw_low_level, selem=ball(2)) bw_high_level = np.zeros_like(bw_low_level) lab_low, num_obj = label(bw_low_level, return_num=True, connectivity=1) for idx in range(num_obj): single_obj = lab_low == (idx + 1) local_otsu = threshold_otsu(structure_img_smooth[single_obj > 0]) bw_high_level[np.logical_and(structure_img_smooth > local_otsu * 0.98, single_obj)] = 1 # LOG 3d to capture spots response = dot_3d(structure_img_smooth, log_sigma=dot_3d_sigma) bw_extra = response > dot_3d_cutoff # thinning bw_high_level = topology_preserving_thinning(bw_high_level, thin_dist_preserve, thin_dist) # combine the two parts bw = np.logical_or(bw_high_level, bw_extra) ################### # POST-PROCESSING ################### seg = remove_small_objects(bw > 0, min_size=minArea, connectivity=1, in_place=False) # output seg = seg > 0 seg = seg.astype(np.uint8) seg[seg > 0] = 255 out_img_list.append(seg.copy()) out_name_list.append('bw_final') if output_type == 'default': # the default final output save_segmentation(seg, False, output_path, fn) elif output_type == 'AICS_pipeline': # pre-defined output function for pipeline data save_segmentation(seg, True, output_path, fn) elif output_type == 'customize': # the hook for passing in a customized output function output_fun(out_img_list, out_name_list, output_path, fn) else: # the hook for other pre-defined RnD output functions (AICS internal) ST6GAL1_output(out_img_list, out_name_list, output_type, output_path, fn)
def Workflow_st6gal1( struct_img: np.ndarray, rescale_ratio: float = -1, output_type: str = "default", output_path: Union[str, Path] = None, fn: Union[str, Path] = None, output_func=None, ): """ classic segmentation workflow wrapper for structure ST6GAL1 Parameter: ----------- struct_img: np.ndarray the 3D image to be segmented rescale_ratio: float an optional parameter to allow rescale the image before running the segmentation functions, default is no rescaling output_type: str select how to handle output. Currently, four types are supported: 1. default: the result will be saved at output_path whose filename is original name without extention + "_struct_segmentaiton.tiff" 2. array: the segmentation result will be simply returned as a numpy array 3. array_with_contour: segmentation result will be returned together with the contour of the segmentation 4. customize: pass in an extra output_func to do a special save. All the intermediate results, names of these results, the output_path, and the original filename (without extension) will be passed in to output_func. """ ########################################################################## # PARAMETERS: # note that these parameters are supposed to be fixed for the structure # and work well accross different datasets intensity_norm_param = [9, 19] gaussian_smoothing_sigma = 1 gaussian_smoothing_truncate_range = 3.0 cell_wise_min_area = 1200 dot_3d_sigma = 1.6 dot_3d_cutoff = 0.02 minArea = 10 thin_dist = 1 thin_dist_preserve = 1.6 ########################################################################## out_img_list = [] out_name_list = [] ################### # PRE_PROCESSING ################### # intenisty normalization (min/max) struct_img = intensity_normalization(struct_img, scaling_param=intensity_norm_param) out_img_list.append(struct_img.copy()) out_name_list.append("im_norm") # rescale if needed if rescale_ratio > 0: struct_img = zoom(struct_img, (1, rescale_ratio, rescale_ratio), order=2) struct_img = (struct_img - struct_img.min() + 1e-8) / ( struct_img.max() - struct_img.min() + 1e-8 ) gaussian_smoothing_truncate_range = ( gaussian_smoothing_truncate_range * rescale_ratio ) # smoothing with gaussian filter structure_img_smooth = image_smoothing_gaussian_3d( struct_img, sigma=gaussian_smoothing_sigma, truncate_range=gaussian_smoothing_truncate_range, ) out_img_list.append(structure_img_smooth.copy()) out_name_list.append("im_smooth") ################### # core algorithm ################### # cell-wise local adaptive thresholding th_low_level = threshold_triangle(structure_img_smooth) bw_low_level = structure_img_smooth > th_low_level bw_low_level = remove_small_objects( bw_low_level, min_size=cell_wise_min_area, connectivity=1, in_place=True ) bw_low_level = dilation(bw_low_level, selem=ball(2)) bw_high_level = np.zeros_like(bw_low_level) lab_low, num_obj = label(bw_low_level, return_num=True, connectivity=1) for idx in range(num_obj): single_obj = lab_low == (idx + 1) local_otsu = threshold_otsu(structure_img_smooth[single_obj > 0]) bw_high_level[ np.logical_and(structure_img_smooth > local_otsu * 0.98, single_obj) ] = 1 # LOG 3d to capture spots response = dot_3d(structure_img_smooth, log_sigma=dot_3d_sigma) bw_extra = response > dot_3d_cutoff # thinning bw_high_level = topology_preserving_thinning( bw_high_level, thin_dist_preserve, thin_dist ) # combine the two parts bw = np.logical_or(bw_high_level, bw_extra) ################### # POST-PROCESSING ################### seg = remove_small_objects(bw > 0, min_size=minArea, connectivity=1, in_place=False) # output seg = seg > 0 seg = seg.astype(np.uint8) seg[seg > 0] = 255 out_img_list.append(seg.copy()) out_name_list.append("bw_final") if output_type == "default": # the default final output, simply save it to the output path save_segmentation(seg, False, Path(output_path), fn) elif output_type == "customize": # the hook for passing in a customized output function # use "out_img_list" and "out_name_list" in your hook to # customize your output functions output_func(out_img_list, out_name_list, Path(output_path), fn) elif output_type == "array": return seg elif output_type == "array_with_contour": return (seg, generate_segmentation_contour(seg)) else: raise NotImplementedError("invalid output type: {output_type}")