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 segment_images(inpDir, outDir, config_data): """ Workflow for data with similar morphology as connexin-43 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_slice_by_slice( struct_img, sigma=gaussian_smoothing_sigma) s3_param = config_data['s3_param'] bw = dot_3d_wrapper(structure_img_smooth, s3_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 nuc_seg(image, scale, sigma, f2, hol_min, hol_max, minA): img = intensity_normalization(image, scale) img_s = image_smoothing_gaussian_3d(img, sigma) bw = filament_2d_wrapper(img_s, f2) bw_2 = hole_filling(bw, hol_min, hol_max) seg = remove_small_objects(bw_2 > 0, min_size=minA, connectivity=1, in_place=False) seg2, n = label(seg, return_num=True) return (img_s, bw_2, seg2, n)
def pex_seg(image, img_seg): slices = [] Vol = [] pex_seg_img = [] pex_seg_n = [] slices = ndi.find_objects(img_seg) img = intensity_normalization(image, scale) img = image_smoothing_gaussian_3d(img, sigma) for i, c in enumerate(slices): cell_V = img_seg[c] vt, f, n, val = marching_cubes_lewiner(cell_V) Tvol = 1 / 6 * det(vt[f]) vol = abs(sum(Tvol)) Vol.append(vol) cell = image[c] cell_s = img[c] bw = dot_3d_wrapper(cell_s, s3) mask = remove_small_objects(bw > 0, min_size=2, connectivity=1, in_place=False) seed = dilation(peak_local_max(cell, labels=label(mask), min_distance=2, indices=False), selem=ball(1)) ws_map = -1 * ndi.distance_transform_edt(bw) seg = watershed(ws_map, label(seed), mask=mask, watershed_line=True) regions = regionprops(seg) n = len(regions) n = max(0, n) pex_seg_img.append(seg) pex_seg_n.append(n) df = pd.DataFrame({ "Slices": slices, "Cell Vol": Vol, "Pex count": pex_seg_n }) return df, pex_seg_img
def Workflow_cardio_npm1_100x(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 = [0.5, 2.5] gaussian_smoothing_sigma = 1 gaussian_smoothing_truncate_range = 3.0 dot_2d_sigma = 2 dot_2d_sigma_extra = 1 dot_2d_cutoff = 0.025 minArea = 1 low_level_min_size = 1000 ########################################################################## 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 = 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 ################### # step 1: low level thresholding #global_otsu = threshold_otsu(structure_img_smooth) global_tri = threshold_triangle(structure_img_smooth) global_median = np.percentile(structure_img_smooth, 50) th_low_level = (global_tri + global_median) / 2 #print(global_median) #print(global_tri) #print(th_low_level) #imsave('img_smooth.tiff', structure_img_smooth) bw_low_level = structure_img_smooth > th_low_level bw_low_level = remove_small_objects(bw_low_level, min_size=low_level_min_size, connectivity=1, in_place=True) bw_low_level = dilation(bw_low_level, selem=ball(2)) # step 2: high level thresholding local_cutoff = 0.333 * threshold_otsu(structure_img_smooth) 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]) if local_otsu > local_cutoff: bw_high_level[np.logical_and( structure_img_smooth > 1.2 * local_otsu, single_obj)] = 1 #imsave('seg_coarse.tiff', bw_high_level.astype(np.uint8)) out_img_list.append(bw_high_level.copy()) out_name_list.append('bw_coarse') response_bright = dot_slice_by_slice(structure_img_smooth, log_sigma=dot_2d_sigma) ''' response_dark = dot_slice_by_slice(1 - structure_img_smooth, log_sigma=dot_2d_sigma) response_dark_extra = dot_slice_by_slice(1 - structure_img_smooth, log_sigma=dot_2d_sigma_extra) imsave('res_dark_1.tiff', response_dark) imsave('res_dark_2.tiff', response_dark_extra) #inner_mask = bw_high_level.copy() #for zz in range(inner_mask.shape[0]): # inner_mask[zz,:,:] = binary_fill_holes(inner_mask[zz,:,:]) holes = np.logical_or(response_dark>dot_2d_cutoff , response_dark_extra>dot_2d_cutoff) #holes[~inner_mask] = 0 ''' bw_extra = response_bright > 0.03 # dot_2d_cutoff bw_extra[~bw_low_level] = 0 bw_final = np.logical_or(bw_extra, bw_high_level) #bw_final[holes]=0 ################### # POST-PROCESSING ################### seg = remove_small_objects(bw_final, min_size=minArea, connectivity=1, in_place=True) # 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) elif output_type == 'array': return seg elif output_type == 'array_with_contour': return (seg, generate_segmentation_contour(seg)) else: # the hook for pre-defined RnD output functions (AICS internal) img_list, name_list = NPM1_output(out_img_list, out_name_list, output_type, output_path, fn) if output_type == 'QCB': return img_list, name_list
def FABP3_Cardio_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 # ADD you parameters here intensity_norm_param = [1, 18] minArea = 0 gaussian_smoothing_sigma = 1 # lower_bound = 685 # upper_b = 18 # ADD-HERE ########################################################################## out_img_list = [] out_name_list = [] ################### # PRE_PROCESSING ################### # intenisty normalization (min/max) struct_img = intensity_normalization(struct_img, scaling_param=intensity_norm_param) # upper_bound = np.mean(struct_img) + 18 * np.std(struct_img) # struct_img[struct_img < lower_bound] = lower_bound # struct_img[struct_img > upper_bound] = upper_bound # struct_img = (struct_img - lower_bound).astype(float)/(upper_bound - lower_bound) out_img_list.append(struct_img.copy()) out_name_list.append("im_norm") # rescale if needed if rescale_ratio > 0: struct_img = 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) # smoothing with gaussian filter structure_img_smooth = image_smoothing_gaussian_slice_by_slice( struct_img, sigma=gaussian_smoothing_sigma) # ADD-HERE out_img_list.append(structure_img_smooth.copy()) out_name_list.append("im_smooth") # core algorithm # th_li = threshold_li(structure_img_smooth) # PARAMETERS for this step ## s2_param = [[1, 0.02]] bw = dot_2d_slice_by_slice_wrapper(structure_img_smooth, s2_param) # POST-PROCESSING seg = remove_small_objects(bw, 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 == "TEMPLATE": # the hook for pre-defined output functions template_output(out_img_list, out_name_list, output_type, output_path, fn)
def Workflow_cardio_myl7( 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 Cardio MYL7 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 = [8, 15.5] vesselness_sigma = [1] vesselness_cutoff = 0.01 minArea = 15 ########################################################################## 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) # smoothing with gaussian filter structure_img_smooth = edge_preserving_smoothing_3d(struct_img) out_img_list.append(structure_img_smooth.copy()) out_name_list.append("im_smooth") ################### # core algorithm ################### # vesselness 3d response = vesselness3D(structure_img_smooth, sigmas=vesselness_sigma, tau=1, whiteonblack=True) bw = response > vesselness_cutoff ################### # 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}")
def run(path, f3_param=[[1, 0.01]], minArea=20, saveNumber=0): """ use aicssegmentation to pre-process raw data and then make/save a 3D mask """ print('=== path:', path) # load x/y/z voxel size (assumes .tif was saved with Fiji xVoxel, yVoxel, zVoxel = readVoxelSize(path) print(' xVoxel:', xVoxel, 'yVoxel:', yVoxel, 'zVoxel:', zVoxel) # load the data reader = AICSImage(path) IMG = reader.data.astype(np.float32) print(' IMG.shape:', IMG.shape) structure_channel = 0 struct_img0 = IMG[0, structure_channel, :, :, :].copy() # give us a guess for our intensity_scaling_param parameters #from aicssegmentation.core.pre_processing_utils import suggest_normalization_param #suggest_normalization_param(struct_img0) low_ratio, high_ratio = my_suggest_normalization_param(struct_img0) #intensity_scaling_param = [0.0, 22.5] intensity_scaling_param = [low_ratio, high_ratio] print('*** intensity_normalization() intensity_scaling_param:', intensity_scaling_param) # intensity normalization print('=== calling intensity_normalization()') struct_img = intensity_normalization(struct_img0, scaling_param=intensity_scaling_param) # smoothing with edge preserving smoothing print('=== calling edge_preserving_smoothing_3d()') structure_img_smooth = edge_preserving_smoothing_3d(struct_img) # """ see: notebooks/playground_filament3d.ipynb scale_x is set based on the estimated thickness of your target filaments. For example, if visually the thickness of the filaments is usually 3~4 pixels, then you may want to set scale_x as 1 or something near 1 (like 1.25). Multiple scales can be used, if you have filaments of very different thickness. cutoff_x is a threshold applied on the actual filter reponse to get the binary result. Smaller cutoff_x may yielf more filaments, especially detecting more dim ones and thicker segmentation, while larger cutoff_x could be less permisive and yield less filaments and slimmer segmentation. """ #f3_param = [[1, 0.01]] # [scale_1, cutoff_1] print('=== calling filament_3d_wrapper() f3_param:', f3_param) bw = filament_3d_wrapper(structure_img_smooth, f3_param) # #minArea = 20 # from recipe print('=== calling remove_small_objects() minArea:', minArea) seg = remove_small_objects(bw > 0, min_size=minArea, connectivity=1, in_place=False) # # save original file again (with saveNumber saveNumberStr = '' if saveNumber > 1: saveNumberStr = '_' + str(saveNumber) # # save mask seg = seg > 0 out = seg.astype(np.uint8) out[out > 0] = 255 # save _dvMask maskPath = os.path.splitext(path)[0] + '_dvMask' + saveNumberStr + '.tif' print('=== saving 3D mask [WILL FAIL IF FILE EXISTS] as maskPath:', maskPath) try: writer = omeTifWriter.OmeTifWriter(maskPath) writer.save(out) except (OSError) as e: print(' error: file already exists, di dnot resave, maskPath:', maskPath) # # analyze skeleton, take a 3d mask and analyze as a 1-pixel skeleton retDict0, mySkeleton = myAnalyzeSkeleton(out=out, imagePath=path) retDict = OrderedDict() retDict['tifPath'] = path retDict['maskPath'] = maskPath retDict['tifFile'] = os.path.basename(path) retDict['xVoxel'] = xVoxel retDict['yVoxel'] = yVoxel retDict['zVoxel'] = zVoxel # retDict['params'] = OrderedDict() retDict['params']['saveNumber'] = saveNumber retDict['params'][ 'intensity_scaling_param'] = intensity_scaling_param # calculated in my_suggest_normalization_param retDict['params']['f3_param'] = f3_param[ 0] # cludge, not sure where to put this. f3_param is a list of list but screws up my .csv output !!! retDict['params']['minArea'] = minArea retDict.update(retDict0) # save 1-pixel skeleton: mySkeleton # save _dvSkel skelPath = os.path.splitext(path)[0] + '_dvSkel' + saveNumberStr + '.tif' print('=== saving 3D skel [WILL FAIL IF FILE EXISTS] as maskPath:', skelPath) try: writer = omeTifWriter.OmeTifWriter(skelPath) writer.save(mySkeleton) except (OSError) as e: print(' error: file already exists, di dnot resave, skelPath:', skelPath) return retDict
def segment_images(inpDir, outDir, config_data): """ Workflow for data with shell like shapes such as lamin B1 (interphase-specific) 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) middle_frame_method = config_data['middle_frame_method'] mid_z = get_middle_frame(structure_img_smooth, method=middle_frame_method) f2_param = config_data['f2_param'] bw_mid_z = filament_2d_wrapper(structure_img_smooth[mid_z, :, :], f2_param) hole_max = config_data['hole_max'] hole_min = config_data['hole_min'] bw_fill_mid_z = hole_filling(bw_mid_z, hole_min, hole_max) seed = get_3dseed_from_mid_frame( np.logical_xor(bw_fill_mid_z, bw_mid_z), struct_img.shape, mid_z, hole_min) bw_filled = watershed( struct_img, seed.astype(int), watershed_line=True) > 0 seg = np.logical_xor(bw_filled, dilation(bw_filled, selem=ball(1))) 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()
for file in files: if file.endswith('.tif'): image_list.append(file) #creates an empty list for the object counts object_counts = [] #loops through every image in the image_list for x in image_list: im_path = os.path.join(DATAPATH, x) os.path.isfile(im_path) img = imread(im_path) #this could definitely be a more complicated normalization function #suggest_normalization_param(img) img_norm = intensity_normalization(img, [5.0, 15.0]) img_smooth = edge_preserving_smoothing_3d(img_norm) bc_img = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, cmask(8)) bc_img = sobel(bc_img) #thresholds the image based on yen's method block_size = 35 thresh = threshold_yen(bc_img) binary = bc_img > thresh #helps to subtract the background n = 12 l = 256 np.random.seed(1) im = np.zeros((l, l)) points = l * np.random.random((2, n**2))
def Workflow_lmnb1_interphase(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 = [4000] gaussian_smoothing_sigma = 1 gaussian_smoothing_truncate_range = 3.0 f2_param = [[1, 0.01], [2, 0.01], [3, 0.01]] hole_max = 40000 hole_min = 400 ########################################################################## 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 = 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) # smoothing with boundary preserving smoothing 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 ################### # get the mid frame mid_z = get_middle_frame(structure_img_smooth, method='intensity') # 2d filament filter on the middle frame bw_mid_z = filament_2d_wrapper(structure_img_smooth[mid_z, :, :], f2_param) # hole filling bw_fill_mid_z = hole_filling(bw_mid_z, hole_min, hole_max) seed = get_3dseed_from_mid_frame(np.logical_xor(bw_fill_mid_z, bw_mid_z), struct_img.shape, mid_z, hole_min, bg_seed=True) seg_filled = watershed( struct_img, seed.astype(int), watershed_line=True) > 1 # in watershed result, 1 is for background # get the actual shell seg = find_boundaries(seg_filled, connectivity=1, mode='thick') # output 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 == 'array': return seg elif output_type == 'array_with_contour': return (seg, generate_segmentation_contour(seg)) else: print('your can implement your output hook here, but not yet') quit()
def cell_mask(image, scale, sigma): img1 = intensity_normalization(image, scale) img_s = image_smoothing_gaussian_3d(img1, sigma) thr = threshold_otsu(img_s) bw = img_s > thr * 0.8 return (img_s, bw)
def Workflow_lmnb1_interphase( 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 LMNB1 interphase 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 = [4000] gaussian_smoothing_sigma = 1 gaussian_smoothing_truncate_range = 3.0 f2_param = [[1, 0.01], [2, 0.01], [3, 0.01]] hole_max = 40000 hole_min = 400 ########################################################################## 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) # smoothing with boundary preserving smoothing 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 ################### # get the mid frame mid_z = get_middle_frame(structure_img_smooth, method="intensity") # 2d filament filter on the middle frame bw_mid_z = filament_2d_wrapper(structure_img_smooth[mid_z, :, :], f2_param) # hole filling bw_fill_mid_z = hole_filling(bw_mid_z, hole_min, hole_max) seed = get_3dseed_from_mid_frame( np.logical_xor(bw_fill_mid_z, bw_mid_z), struct_img.shape, mid_z, hole_min, bg_seed=True, ) seg_filled = ( watershed(struct_img, seed.astype(int), watershed_line=True) > 1 ) # in watershed result, 1 is for background # get the actual shell seg = find_boundaries(seg_filled, connectivity=1, mode="thick") # output 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}")
def Workflow_fbl_labelfree_4dn( 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 FBL Labelfree 4dn 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: minArea = 5 low_level_min_size = 7000 s2_param = [[0.5, 0.1]] intensity_scaling_param = [0.5, 19.5] gaussian_smoothing_sigma = 1 ########################################################################## out_img_list = [] out_name_list = [] ################### # PRE_PROCESSING ################### # intenisty normalization struct_norm = intensity_normalization( struct_img, scaling_param=intensity_scaling_param) out_img_list.append(struct_img.copy()) out_name_list.append("im_norm") # smoothing struct_smooth = image_smoothing_gaussian_3d(struct_norm, sigma=gaussian_smoothing_sigma) out_img_list.append(struct_smooth.copy()) out_name_list.append("im_smooth") ################### # core algorithm ################### # step 1: low level thresholding # global_otsu = threshold_otsu(structure_img_smooth) global_tri = threshold_triangle(struct_smooth) global_median = np.percentile(struct_smooth, 50) th_low_level = (global_tri + global_median) / 2 bw_low_level = struct_smooth > th_low_level bw_low_level = remove_small_objects(bw_low_level, min_size=low_level_min_size, connectivity=1, in_place=True) # step 2: high level thresholding 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(struct_smooth[single_obj]) bw_high_level[np.logical_and(struct_smooth > local_otsu * 1.2, single_obj)] = 1 # step 3: finer segmentation response2d = dot_2d_slice_by_slice_wrapper(struct_smooth, s2_param) bw_finer = remove_small_objects(response2d, min_size=minArea, connectivity=1, in_place=True) # merge finer level detection into high level coarse segmentation # to include outside dim parts bw_high_level[bw_finer > 0] = 1 ################### # POST-PROCESSING # make sure the variable name of final segmentation is 'seg' ################### seg = remove_small_objects(bw_high_level, min_size=minArea, connectivity=1, in_place=True) # 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 # TODO CREATE OUT_IMG_LIST/OUT_NAMELIST? 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}")
def Workflow_son( 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 SON 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 = [2, 30] vesselness_sigma = [1.2] vesselness_cutoff = 0.15 minArea = 15 # dot_2d_sigma = 1 dot_3d_sigma = 1.15 ########################################################################## 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") # smoothing with boundary preserving smoothing structure_img_smooth = edge_preserving_smoothing_3d(struct_img) out_img_list.append(structure_img_smooth.copy()) out_name_list.append("im_smooth") ################### # core algorithm ################### response_f3 = vesselness3D(structure_img_smooth, sigmas=vesselness_sigma, tau=1, whiteonblack=True) response_f3 = response_f3 > vesselness_cutoff response_s3_1 = dot_3d(structure_img_smooth, log_sigma=dot_3d_sigma) response_s3_3 = dot_3d(structure_img_smooth, log_sigma=3) bw_small_inverse = remove_small_objects(response_s3_1 > 0.03, min_size=150) bw_small = np.logical_xor(bw_small_inverse, response_s3_1 > 0.02) bw_medium = np.logical_or(bw_small, response_s3_1 > 0.07) bw_large = np.logical_or(response_s3_3 > 0.2, response_f3 > 0.25) bw = np.logical_or(np.logical_or(bw_small, bw_medium), bw_large) ################### # POST-PROCESSING ################### bw = remove_small_objects(bw > 0, min_size=minArea, connectivity=1, in_place=False) for zz in range(bw.shape[0]): bw[zz, :, :] = remove_small_objects(bw[zz, :, :], min_size=3, connectivity=1, in_place=False) seg = remove_small_objects(bw > 0, min_size=minArea, connectivity=1, in_place=False) 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}")
def Workflow_son(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 = [2, 30] vesselness_sigma = [1.2] vesselness_cutoff = 0.15 minArea = 15 dot_2d_sigma = 1 dot_3d_sigma = 1.15 ########################################################################## ################### # PRE_PROCESSING ################### # intenisty normalization (min/max) struct_img = intensity_normalization(struct_img, scaling_param=intensity_norm_param) # smoothing with boundary preserving smoothing structure_img_smooth = edge_preserving_smoothing_3d(struct_img) ################### # core algorithm ################### response_f3 = vesselness3D(structure_img_smooth, sigmas=vesselness_sigma, tau=1, whiteonblack=True) response_f3 = response_f3 > vesselness_cutoff response_s3_1 = dot_3d(structure_img_smooth, log_sigma=dot_3d_sigma) response_s3_3 = dot_3d(structure_img_smooth, log_sigma=3) bw_small_inverse = remove_small_objects(response_s3_1>0.03, min_size=150) bw_small = np.logical_xor(bw_small_inverse, response_s3_1>0.02) bw_medium = np.logical_or(bw_small, response_s3_1>0.07) bw_large = np.logical_or(response_s3_3>0.2, response_f3>0.25) bw = np.logical_or( np.logical_or(bw_small, bw_medium), bw_large) ################### # POST-PROCESSING ################### bw = remove_small_objects(bw>0, min_size=minArea, connectivity=1, in_place=False) for zz in range(bw.shape[0]): bw[zz,: , :] = remove_small_objects(bw[zz,:,:], min_size=3, connectivity=1, in_place=False) seg = remove_small_objects(bw>0, min_size=minArea, connectivity=1, in_place=False) seg = seg>0 seg = seg.astype(np.uint8) seg[seg>0]=255 if output_type == 'default': # the default final output save_segmentation(seg, False, output_path, fn) elif output_type == 'array': return seg elif output_type == 'array_with_contour': return (seg, generate_segmentation_contour(seg)) else: print('your can implement your output hook here, but not yet') quit()
def segment_images(inpDir, outDir, config_data): """ Workflow for dot like shapes such as Centrin-2, Desmoplakin, PMP34. 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 an 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'] if config_data["gaussian_smoothing"] == "gaussian_slice_by_slice": structure_img_smooth = image_smoothing_gaussian_slice_by_slice( struct_img, sigma=gaussian_smoothing_sigma) else: structure_img_smooth = image_smoothing_gaussian_3d( struct_img, sigma=gaussian_smoothing_sigma) s3_param = config_data['s3_param'] bw = dot_3d_wrapper(structure_img_smooth, s3_param) minArea = config_data['minArea'] Mask = remove_small_objects(bw > 0, min_size=minArea, connectivity=1, in_place=False) Seed = dilation(peak_local_max(struct_img, labels=label(Mask), min_distance=2, indices=False), selem=ball(1)) Watershed_Map = -1 * distance_transform_edt(bw) seg = watershed(Watershed_Map, label(Seed), mask=Mask, watershed_line=True) seg = remove_small_objects(seg > 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 Workflow_lmnb1_mitotic(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 = [4000] gaussian_smoothing_sigma = 1 gaussian_smoothing_truncate_range = 3.0 f2_param = [[0.5, 0.01]] minArea = 5 ########################################################################## 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 = 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) # smoothing with boundary preserving smoothing 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 ################### # 2d filament filter on the middle frame bw = filament_2d_wrapper(structure_img_smooth, f2_param) ################### # POST-PROCESSING ################### bw = remove_small_objects(bw > 0, min_size=minArea, connectivity=1, in_place=False) # output seg = bw > 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 == 'array': return seg elif output_type == 'array_with_contour': return (seg, generate_segmentation_contour(seg)) else: print('your can implement your output hook here, but not yet') quit()
from aicssegmentation.core.visual import blob2dExplorer_single, fila2dExplorer_single, random_colormap from aicssegmentation.core.seg_dot import dot_2d, logSlice from aicssegmentation.core.vessel import filament_2d_wrapper from aicssegmentation.core.utils import hole_filling from aicssegmentation.core.pre_processing_utils import intensity_normalization, suggest_normalization_param, edge_preserving_smoothing_3d fn = '/Users/claytonwandishin/UNC_PCNA/161124 u2os #5 ncs 6NCS_0_1c1.tif' os.path.isfile(fn) tif = TiffFile(fn) num_img = len(tif.pages) print(num_img) img_seq = [] for ii in range(num_img): img_seq.append(imread(fn, key=ii)) img = img_seq[135] suggest_normalization_param(img) img_norm = intensity_normalization(img, [0.5, 8.0]) img_smooth = edge_preserving_smoothing_3d(img_norm) interact(blob2dExplorer_single, im=fixed(img_smooth), \ sigma=widgets.FloatRangeSlider(value=(1,5), min=1, max=15,step=1,continuous_update=False), \ th=widgets.FloatSlider(value=0.02,min=0.01, max=0.1, step=0.01,continuous_update=False)) ksize = (65, 65) blur_smooth = cv2.blur(np.float32(img_smooth), ksize) nuc_detection = dot_2d(blur_smooth, 11) nuc_mask = nuc_detection > 0.0015 final_seg = hole_filling(remove_small_objects(nuc_mask), .5, 2500) plt.imshow(final_seg)
def Workflow_cardio_npm1_100x( 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 Cardio NPM1 100x 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 = [0.5, 2.5] gaussian_smoothing_sigma = 1 gaussian_smoothing_truncate_range = 3.0 dot_2d_sigma = 2 # dot_2d_sigma_extra = 1 # dot_2d_cutoff = 0.025 minArea = 1 low_level_min_size = 1000 ########################################################################## 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 ################### # step 1: low level thresholding # global_otsu = threshold_otsu(structure_img_smooth) global_tri = threshold_triangle(structure_img_smooth) global_median = np.percentile(structure_img_smooth, 50) th_low_level = (global_tri + global_median) / 2 # print(global_median) # print(global_tri) # print(th_low_level) # imsave('img_smooth.tiff', structure_img_smooth) bw_low_level = structure_img_smooth > th_low_level bw_low_level = remove_small_objects(bw_low_level, min_size=low_level_min_size, connectivity=1, in_place=True) bw_low_level = dilation(bw_low_level, selem=ball(2)) # step 2: high level thresholding local_cutoff = 0.333 * threshold_otsu(structure_img_smooth) 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]) if local_otsu > local_cutoff: bw_high_level[np.logical_and( structure_img_smooth > 1.2 * local_otsu, single_obj)] = 1 # imsave('seg_coarse.tiff', bw_high_level.astype(np.uint8)) out_img_list.append(bw_high_level.copy()) out_name_list.append("bw_coarse") response_bright = dot_slice_by_slice(structure_img_smooth, log_sigma=dot_2d_sigma) bw_extra = response_bright > 0.03 # dot_2d_cutoff bw_extra[~bw_low_level] = 0 bw_final = np.logical_or(bw_extra, bw_high_level) # bw_final[holes]=0 ################### # POST-PROCESSING ################### seg = remove_small_objects(bw_final, min_size=minArea, connectivity=1, in_place=True) # 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}")
def Workflow_lamp1( 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 LAMP1 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_scaling_param = [3, 19] gaussian_smoothing_sigma = 1 gaussian_smoothing_truncate_range = 3.0 minArea = 15 # ves_th_2d = 0.1 vesselness_sigma = [1] vesselness_cutoff = 0.15 # hole_min = 60 hole_max = 1600 log_sigma_1 = 5 log_cutoff_1 = 0.09 log_sigma_2 = 2.5 log_cutoff_2 = 0.07 log_sigma_3 = 1 log_cutoff_3 = 0.01 ########################################################################## out_img_list = [] out_name_list = [] # intenisty normalization struct_img = intensity_normalization( struct_img, scaling_param=intensity_scaling_param ) out_img_list.append(struct_img.copy()) out_name_list.append("im_norm") 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 ) structure_img_smooth = image_smoothing_gaussian_slice_by_slice( 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") # spot detection response1 = dot_slice_by_slice(structure_img_smooth, log_sigma=log_sigma_1) bw1 = response1 > log_cutoff_1 response2 = dot_slice_by_slice(structure_img_smooth, log_sigma=log_sigma_2) bw2 = response2 > log_cutoff_2 bw_spot = np.logical_or(bw1, bw2) response3 = dot_slice_by_slice(structure_img_smooth, log_sigma=log_sigma_3) bw3 = response3 > log_cutoff_3 bw_spot = np.logical_or(bw_spot, bw3) # ring/filament detection ves = vesselnessSliceBySlice( structure_img_smooth, sigmas=vesselness_sigma, tau=1, whiteonblack=True ) bw_ves = ves > vesselness_cutoff # fill holes partial_fill = np.logical_or(bw_spot, bw_ves) out_img_list.append(partial_fill.copy()) out_name_list.append("interm_before_hole") holes = np.zeros_like(partial_fill) for zz in range(partial_fill.shape[0]): background_lab = label(~partial_fill[zz, :, :], connectivity=1) out = np.copy(background_lab) component_sizes = np.bincount(background_lab.ravel()) too_big = component_sizes > hole_max too_big_mask = too_big[background_lab] out[too_big_mask] = 0 # too_small = component_sizes <hole_min # too_small_mask = too_small[background_lab] # out[too_small_mask] = 0 holes[zz, :, :] = out full_fill = np.logical_or(partial_fill, holes) seg = remove_small_objects( full_fill, 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}")
def myRun(path): """ scale_x is set based on the estimated thickness of your target filaments. For example, if visually the thickness of the filaments is usually 3~4 pixels, then you may want to set scale_x as 1 or something near 1 (like 1.25). Multiple scales can be used, if you have filaments of very different thickness. cutoff_x is a threshold applied on the actual filter reponse to get the binary result. Smaller cutoff_x may yielf more filaments, especially detecting more dim ones and thicker segmentation, while larger cutoff_x could be less permisive and yield less filaments and slimmer segmentation. """ f3_param=[[1, 0.01]] f3_param=[[5, 0.001], [3, 0.001]] stackData = tifffile.imread(path) numSlices = stackData.shape[0] _printStackParams('stackData', stackData) stackData = slidingZ(stackData, upDownSlices=1) stackData = medianFilter(stackData) # give us a guess for our intensity_scaling_param parameters low_ratio, high_ratio = my_suggest_normalization_param(stackData) # try per slice normData = stackData.astype(np.float64) normData[:] = 0 for i in range(numSlices): oneSlice = stackData[i,:,:] low_ratio, high_ratio = my_suggest_normalization_param(oneSlice) print(i, low_ratio, high_ratio) #low_ratio = 0.2 low_ratio -= 0.2 high_ratio -= 1 intensity_scaling_param = [low_ratio, high_ratio] sliceNormData = intensity_normalization(oneSlice, scaling_param=intensity_scaling_param) normData[i,:,:] = sliceNormData #sys.exit() ''' #intensity_scaling_param = [0.0, 22.5] intensity_scaling_param = [low_ratio, high_ratio] print(' === intensity_normalization() intensity_scaling_param:', intensity_scaling_param) # intensity normalization print(' === calling intensity_normalization()') normData = intensity_normalization(stackData, scaling_param=intensity_scaling_param) _printStackParams('normData', normData) ''' # smoothing with edge preserving smoothing print(' === calling edge_preserving_smoothing_3d()') smoothData = edge_preserving_smoothing_3d(normData) _printStackParams('smoothData', smoothData) print(' === calling filament_3d_wrapper() f3_param:', f3_param) filamentData = filament_3d_wrapper(smoothData, f3_param) #filamentData = filamentData > 0 _printStackParams('filamentData', filamentData) #filamentData2 = slidingZ(filamentData, upDownSlices=1) # # napari print('opening in napari') scale = (1, 0.6, 0.6) with napari.gui_qt(): viewer = napari.Viewer(title='xxx') minContrast = 0 maxContrast = 255 myImageLayer = viewer.add_image(stackData, scale=scale, contrast_limits=(minContrast, maxContrast), colormap='green', visible=True, name='stackData') minContrast = 0 maxContrast = 1 myImageLayer = viewer.add_image(normData, scale=scale, contrast_limits=(minContrast, maxContrast), opacity=0.6, colormap='gray', visible=True, name='normData') minContrast = 0 maxContrast = 1 myImageLayer = viewer.add_image(filamentData, scale=scale, contrast_limits=(minContrast, maxContrast), opacity=0.6, colormap='blue', visible=True, name='filamentData') '''
def Workflow_slc25a17( 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 SLC25A17 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 = [2, 36] gaussian_smoothing_sigma = 1 gaussian_smoothing_truncate_range = 3.0 dot_3d_sigma = 1 dot_3d_cutoff = 0.045 # 0.03 #0.04 minArea = 3 # 5 ########################################################################## 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_slice_by_slice( 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 ################### # step 1: LOG 3d response = dot_3d(structure_img_smooth, log_sigma=dot_3d_sigma) bw = response > dot_3d_cutoff bw = remove_small_objects(bw > 0, min_size=minArea, connectivity=1, in_place=False) out_img_list.append(bw.copy()) out_name_list.append("interm_mask") # step 2: 'local_maxi + watershed' for cell cutting local_maxi = peak_local_max(struct_img, labels=label(bw), min_distance=2, indices=False) out_img_list.append(local_maxi.copy()) out_name_list.append("interm_local_max") distance = distance_transform_edt(bw) im_watershed = watershed( -1 * distance, label(dilation(local_maxi, selem=ball(1))), mask=bw, watershed_line=True, ) ################### # POST-PROCESSING ################### seg = remove_small_objects(im_watershed, min_size=minArea, connectivity=1, in_place=False) # HACK: Only for 2019 April Release ##### if np.count_nonzero(seg > 0) < 50000: print("FLAG: please check the meta data of the original CZI for QC") # 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}")
filepaths_src = [] dirNames = [] for dirName, subdirList, fileList in os.walk(args.dir_src): dirNames.append(dirName) for fname in fileList: filepaths_src.append(os.path.join(dirName, fname)) for dirName in dirNames: # print(dirName.replace(args.dir_src, args.dir_dst)) os.makedirs(dirName.replace(args.dir_src, args.dir_dst), exist_ok=True) # # path = r'E:\hackathon_data\Segmentation\SegmentationMiniData\Harvard_Lung\LUNG-1-LN\LUNG-1-LN_40X_8.tif' for path_src in filepaths_src: print(path_src) path_dst = path_src.replace(args.dir_src, args.dir_dst) path_dst = path_dst.replace('.tif', '.jpg') img = tifffile.imread(path_src) lower, upper = suggest_normalization_param(img) img_norm = intensity_normalization(img, [lower, upper]) img = (img_norm * 255).astype(np.uint8) print(img.shape, img.dtype) if len(img.shape) > 2: for i in range(img.shape[0]): img_slice = Image.fromarray(img[i, :, :]) img_slice.thumbnail(args.max_size) # inplace operation path_dst_slice = path_dst.replace('.', '_{}.'.format(i)) img_slice.save(path_dst_slice) else: img = Image.fromarray(img) img.thumbnail(args.max_size) # inplace operation img.save(path_dst) # break
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}")