Ejemplo n.º 1
0
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()
Ejemplo n.º 2
0
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()
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
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}")
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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))
Ejemplo n.º 11
0
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()
Ejemplo n.º 12
0
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}")
Ejemplo n.º 15
0
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}")
Ejemplo n.º 16
0
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()
Ejemplo n.º 17
0
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()
Ejemplo n.º 18
0
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}")
Ejemplo n.º 21
0
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}")
Ejemplo n.º 22
0
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')
		
		'''
Ejemplo n.º 23
0
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}")
Ejemplo n.º 24
0
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
Ejemplo n.º 25
0
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}")