Exemple #1
0
def check_device(gpu_number=0, callback_log=None):
    """ Check on which device (GPU or CPU) CellPose will be running. 

    Parameters
    ----------
    gpu_number : int, optional
        Index of GPU to be tested, by default 0
    """
    use_gpu = utils.use_gpu(gpu_number=gpu_number)
    if use_gpu:
        log_message('CellPose will be USING GPU', callback_fun=callback_log)
        device = mx.gpu()
    else:
        log_message('CellPose will be USING CPU', callback_fun=callback_log)
        device = mx.cpu()

    return device
def create_img_closest_obj(path_scan,
                           str_label,
                           strs_save,
                           path_save=None,
                           search_recursive=False,
                           truncate_distance=None,
                           callback_log=None,
                           callback_status=None,
                           callback_progress=None):
    """   Function to process label images and facilitate assignment to closest segmented object.
    Will create two 2D images with the same size as the label image. Pixel values in either image
    encode 
    1. Distance to the closted object, e.g. nuclei.
    2. Index of clostest object.

    Parameters
    ----------
    path_scan : pathlib Path object
        Path to scan for label images.
    str_label : str
        Unique string contained in label iamge to be saved.
    strs_save : tuple of strings
        Strings defining how to save the result images, either string will replace str_save.
    path_save : pathlib Path object
        Path to save results,
        - If Pathlib object, then this absolute path is used.
        - If 'string' a replacement operation path containing the analyzed mask will be applied (see create_output_path).
    truncate_distance : int
        Distance above which distances will be truncated. Using a value of 255 has the advantage
        that the saved image is 8bit and thus small.
    callback_log : callback, optional
        Callback function to provide function log. If none, print will be used.
        For more details see segwrap.utils_general.log_message
    """

    # Check scan directory
    if not path_scan.is_dir():
        log_message(f'Path {path_scan} does not exist.',
                    callback_fun=callback_log)
        return

    # Path to save results
    if isinstance(path_save, pathlib.PurePath):
        path_save_results = path_save
        if not path_save_results.is_dir():
            path_save_results.mkdir(parents=True)
        path_save_settings = path_save_results

    else:
        path_save_str_replace = path_save

    # Search files: recursively or not
    files_proc = []
    if search_recursive:
        for path_mask in path_scan.rglob(f'*{str_label}*'):
            files_proc.append(path_mask)
    else:
        for path_mask in path_scan.glob(f'*{str_label}*'):
            files_proc.append(path_mask)

    # Process files
    n_files = len(files_proc)
    for idx, file_label in enumerate(files_proc):

        log_message(f'\n>>> Processing file:\n{file_label}',
                    callback_fun=callback_status)

        # >>> Create path to save data if necessary
        if not isinstance(path_save, pathlib.PurePath):
            path_save_results = create_output_path(file_label.parent,
                                                   path_save_str_replace,
                                                   subfolder=None,
                                                   create_path=True)
            log_message(f'Results will be save here : {path_save_results}',
                        callback_fun=callback_status)

        # >>>> Read label image
        img_labels = imread(file_label)
        props = regionprops(img_labels)
        labels = np.array([prop.label for prop in props])
        n_objs = len(labels)

        # Loop over all nuclei and create create distance map
        log_message(
            f' Creating distance maps for {n_objs} objects. This can take a while ...',
            callback_fun=callback_log)
        dist_mat = np.zeros((img_labels.shape[0], img_labels.shape[1], n_objs),
                            dtype=np.uint16)
        mask_fill_indiv = np.zeros(
            (img_labels.shape[0], img_labels.shape[1], n_objs),
            dtype=np.uint16)

        for indx, obj_int in enumerate(tqdm(np.nditer(labels), total=n_objs)):

            # Create binary mask for current object and find contour
            img_label_loop = np.zeros(
                (img_labels.shape[0], img_labels.shape[1]))
            img_label_loop[img_labels == obj_int] = 1
            mask_fill_indiv[:, :, indx] = img_label_loop

            dist_obj = ndimage.distance_transform_edt(
                np.logical_not(img_label_loop))
            if truncate_distance:
                dist_obj[dist_obj > truncate_distance] = truncate_distance
            dist_mat[:, :, indx] = dist_obj.astype(np.uint16)

        # >>> Condense distmap in two matrixes: index and distance to closest object
        dist_obj_ind_3D = np.argsort(dist_mat, axis=2)
        dist_obj_dist_3D = np.take_along_axis(dist_mat,
                                              dist_obj_ind_3D,
                                              axis=2)

        # For index: replace Python matrix index with actual index from label image
        ind_obj_closest = np.zeros((img_labels.shape[0], img_labels.shape[1]))
        dist_obj_ind_2D = np.copy(dist_obj_ind_3D[:, :, 0])

        for indx, obj_int in enumerate(np.nditer(labels)):
            ind_obj_closest[dist_obj_ind_2D == indx] = obj_int

        # Save index of closest object
        name_save_ind = path_save_results / f'{file_label.stem.replace(str_label, strs_save[0])}.png'
        if str(name_save_ind) != str(file_label):
            imsave(name_save_ind,
                   ind_obj_closest.astype('uint16'),
                   check_contrast=False)
        else:
            log_message(
                f'Name to save index matrix could not be established: {name_save_ind}',
                callback_fun=callback_log)

        # Save distances to closest object
        name_save_dist = path_save_results / f'{file_label.stem.replace(str_label, strs_save[1])}.png'
        if str(name_save_dist) != str(file_label):
            imsave(name_save_dist,
                   dist_obj_dist_3D[:, :, 0].astype('uint16'),
                   check_contrast=False)
        else:
            log_message(
                f'Name to save index matrix could not be established: {name_save_dist}',
                callback_fun=callback_log)
Exemple #3
0
def cellpose_predict(data, config, path_save, callback_log=None):
    """ Perform prediction with CellPose. 

    Parameters
    ----------
    data : dict
        Contains data on which prediction should be performed. 
    config : dict
        Configuration of CellPose prediction. 
    path_save : pathline Path object
        Path where results will be saved. 
    """

    # Get data
    imgs = data['imgs']
    file_names = data['file_names']
    sizes_orginal = data['sizes_orginal']
    channels = data['channels']
    new_size = data['new_size']
    obj_name = data['obj_name']

    # Get config
    model_type = config['model_type']
    obj_size = config['obj_size']
    device = config['device']

    log_message(f'\nPerforming segmentation of {obj_name}\n',
                callback_fun=callback_log)

    start_time = time.time()

    if not path_save.is_dir():
        path_save.mkdir()

    # Perform segmentation with CellPose
    model = models.Cellpose(
        device, model_type=model_type)  # model_type can be 'cyto' or 'nuclei'
    masks, flows, styles, diams = model.eval(imgs,
                                             diameter=obj_size,
                                             channels=channels)

    # Display and save results
    log_message(f'\n Creating outputs ...\n', callback_fun=callback_log)
    n_img = len(imgs)

    for idx in tqdm(range(n_img)):
        file_name = file_names[idx]
        maski = masks[idx]
        flowi = flows[idx][0]
        imgi = imgs[idx]

        # Rescale each channel separately
        imgi_rescale = imgi.copy()

        for idim in range(3):
            imgdum = imgi[:, :, idim]
            pa, pb = np.percentile(imgdum, (0.1, 99.9))
            imgi_rescale[:, :, idim] = rescale_intensity(
                imgdum, in_range=(pa, pb), out_range=np.uint8).astype('uint8')

        # Save overview image
        fig = plt.figure(figsize=(12, 3))
        plot.show_segmentation(fig,
                               imgi_rescale.astype('uint8'),
                               maski,
                               flowi,
                               channels=channels)
        plt.tight_layout()

        plt.savefig(path_save / f'{file_name.stem}__segment__{obj_name}.png',
                    dpi=600)
        plt.close()

        # Save mask and flow images
        imsave(path_save / f'{file_name.stem}__flow__{obj_name}.png',
               flowi,
               check_contrast=False)

        if new_size:
            mask_full = resize_mask(maski, sizes_orginal[idx])

            imsave(path_save / f'{file_name.stem}__mask__{obj_name}.png',
                   mask_full.astype('uint16'),
                   check_contrast=False)
            imsave(path_save /
                   f'{file_name.stem}__mask_resize__{obj_name}.png',
                   maski.astype('uint16'),
                   check_contrast=False)

        else:
            imsave(path_save / f'{file_name.stem}__mask__{obj_name}.png',
                   maski.astype('uint16'),
                   check_contrast=False)

    log_message(
        f"\nSegmentation of provided images finished ({(time.time() - start_time)}s)",
        callback_fun=callback_log)
Exemple #4
0
def segment_cells_nuclei_indiv(path_scan,
                               strings,
                               img_ext,
                               new_size,
                               sizes,
                               models,
                               path_save,
                               input_subfolder=None,
                               callback_log=None,
                               callback_status=None,
                               callback_progress=None):
    """[summary] segment cells and nuclei in bulk, e.g. first all images are loaded and then segmented. 
    TODO: specify parameters
    Parameters
    ----------
    path_scan : [type]
        [description]
    strings : [type]
        [description]
    img_ext : [type]
        [description]
    new_size : tuple
        Defines resizing of image. If two elements, new size of image. If one element, resizing factor. 
        If emtpy, no resizing.
    sizes : [type]
        [description]
    models : [type]
        [description]
    path_save : pathlin object or string
        Path to save results,
        - If Pathlib object, then this absolute path is used.
        - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path).
          And results will be stored in subfolder 'segmentation-input'
    callback_log : [type], optional
        [description], by default None
    callback_status : [type], optional
        [description], by default None
    callback_progress : [type], optional
        [description], by default None
    """

    par_dict = locals()
    par_dict = clean_par_dict(par_dict)
    log_message(f"Function (segment_obj_indiv) called with: {str(par_dict)} ",
                callback_fun=callback_log)

    # Get parameters
    (str_cyto, str_nuclei) = strings
    (size_cells, size_nuclei) = sizes
    (model_cells, model_nuclei) = models

    # Use provided absolute user-path to save images.
    if isinstance(path_save, pathlib.PurePath):
        path_save_results = path_save
        if not path_save_results.is_dir():
            path_save_results.mkdir(parents=True)

    else:
        path_save_str_replace = path_save

    # Configurations
    device = check_device(callback_log=callback_log)

    config_nuclei = {
        'model_type': model_nuclei,
        'obj_size': size_nuclei,
        'device': device
    }

    config_cyto = {
        'model_type': model_cells,
        'obj_size': size_cells,
        'device': device
    }

    channels_cyto = [1, 3]
    channels_nuclei = [0, 1]

    # Loop over data
    log_message(f'\nLoading image pairs and segment them on the fly',
                callback_fun=callback_log)

    if not path_scan.is_dir():
        log_message(f'Path {path_scan} does not exist.',
                    callback_fun=callback_log)
        return

    # Search for file to be analyzed
    log_message(f'\nLoading images and segment them on the fly',
                callback_fun=callback_log)
    files_proc = []
    for path_img in path_scan.rglob(f'*{str_cyto}*{img_ext}'):
        if input_subfolder:
            if path_img.parts[-2] == input_subfolder:
                files_proc.append(path_img)
        else:
            files_proc.append(path_img)
    n_imgs = len(files_proc)

    if n_imgs == 0:
        log_message(f'NO IMAGES FOUND. Check your settings.',
                    callback_fun=callback_log)
        return

    # Process files

    for idx, path_cyto in enumerate(files_proc):
        imgs_cyto = []
        imgs_nuclei = []
        files_cyto = []
        files_nuclei = []
        sizes_orginal = []

        log_message(f'Segmenting image : {path_cyto.name}',
                    callback_fun=callback_log)

        if callback_status:
            callback_status(f'Segmenting image : {path_cyto.name}')

        if callback_progress:
            progress = float((idx + 1) / n_imgs)
            callback_progress(progress)

        # DAPI image: existing?
        path_nuclei = Path(str(path_cyto).replace(str_cyto, str_nuclei))
        if not path_nuclei.is_file():
            log_message(f'DAPI image not found : {path_nuclei}',
                        callback_fun=callback_log)
            continue

        # Read images
        img_cyto = imread(str(path_cyto))
        if img_cyto.ndim != 2:
            log_message(
                f'\nERROR\n  Input image of cell has to be 2D. Current image is {img_cyto.ndim}D',
                callback_fun=callback_log)
            continue

        img_nuclei = imread(str(path_nuclei))
        if img_nuclei.ndim != 2:
            log_message(
                f'\nERROR\n  Input image of cell has to be 2D. Current image is {img_nuclei.ndim}D',
                callback_fun=callback_log)
            continue

        sizes_orginal.append(img_cyto.shape)

        # Resize
        if new_size:

            # New size can also be defined as a scalar factor
            if len(new_size) == 1:
                scale_factor = new_size[0]
                img_size = img_cyto.shape
                new_size = tuple(int(ti / scale_factor) for ti in img_size)

            img_cyto = resize(img_cyto, new_size)
            img_nuclei = resize(img_nuclei, new_size)

        img_zeros = np.zeros(img_cyto.shape)

        # For cell segmentation
        img_3d = np.dstack([img_cyto, img_zeros, img_nuclei])
        imgs_cyto.append(img_3d)
        files_cyto.append(path_cyto)

        # For nuclei segmentation
        img_3d_dpi = np.dstack([img_zeros, img_zeros, img_nuclei])
        imgs_nuclei.append(img_3d_dpi)
        files_nuclei.append(path_nuclei)

        # Create new output path if specified
        if not isinstance(path_save, pathlib.PurePath):
            path_save_results = create_output_path(path_cyto.parent,
                                                   path_save_str_replace,
                                                   subfolder='',
                                                   create_path=True)
            path_save_settings = path_save_results

        # >>> Call function for prediction of cell
        data_cyto = {
            'imgs': imgs_cyto,
            'file_names': files_cyto,
            'sizes_orginal': sizes_orginal,
            'channels': channels_cyto,
            'new_size': new_size,
            'obj_name': 'cells'
        }

        cellpose_predict(data_cyto,
                         config_cyto,
                         path_save=path_save_results,
                         callback_log=callback_log)

        # >>> Call function for prediction of nuclei
        data_nuclei = {
            'imgs': imgs_nuclei,
            'file_names': files_nuclei,
            'sizes_orginal': sizes_orginal,
            'channels': channels_nuclei,
            'new_size': new_size,
            'obj_name': 'nuclei'
        }

        cellpose_predict(data_nuclei,
                         config_nuclei,
                         path_save=path_save_results,
                         callback_log=callback_log)

    # Save settings
    if len(imgs_cyto) > 0:
        fp = open(str(path_save_results / 'segmentation_settings.json'), "w")
        json.dump(par_dict, fp, indent=4, sort_keys=True)
        fp.close()

    log_message(f'\n BATCH SEGMENTATION finished', callback_fun=callback_log)
Exemple #5
0
def segment_obj_indiv(path_scan,
                      obj_name,
                      str_channel,
                      img_ext,
                      new_size,
                      obj_size,
                      model_type,
                      path_save,
                      input_subfolder=None,
                      callback_log=None,
                      callback_status=None,
                      callback_progress=None):
    """ Will recursively search folder for images to be analyzed!

    Parameters
    ----------
    path_scan : [type]
        [description]
    str_nuclei : [type]
        [description]
    img_ext : [type]
        [description]
    new_size : tuple
        Defines resizing of image. If two elements, new size of image. If one element, resizing factor. 
        If emtpy, no resizing.
    size_nuclei : [type]
        [description]
    model_nuclei : [type]
        [description]
    path_save : pathlib object or string
        Path to save results,
        - If Pathlib object, then this absolute path is used.
        - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path).
    input_subfolder : str
        Name of subfolder that contains results. If specified ONLY files in this folder will be processed.
    callback_log : [type], optional
        [description], by default None
    callback_status : [type], optional
        [description], by default None
    callback_progress : [type], optional
        [description], by default None

    Returns
    -------
    [type]
        [description]
    """

    # Print all input parameters
    par_dict = locals()
    par_dict = clean_par_dict(par_dict)
    log_message(f"Function (segment_obj_indiv) called with: {str(par_dict)} ",
                callback_fun=callback_log)

    # Configurations
    device = check_device(callback_log=callback_log)

    config = {'model_type': model_type, 'obj_size': obj_size, 'device': device}

    channels = [0, 1]

    # Use provided absolute user-path to save images.
    if isinstance(path_save, pathlib.PurePath):
        path_save_results = path_save
        if not path_save_results.is_dir():
            path_save_results.mkdir(parents=True)

    else:
        path_save_str_replace = path_save

    if not path_scan.is_dir():
        log_message(f'Path {path_scan} does not exist.',
                    callback_fun=callback_log)
        return

    # Search for file to be analyzed
    log_message(f'\nLoading images and segment them on the fly',
                callback_fun=callback_log)
    files_proc = []
    for path_img in path_scan.rglob(f'*{str_channel}*{img_ext}'):
        if input_subfolder:
            if path_img.parts[-2] == input_subfolder:
                files_proc.append(path_img)
        else:
            files_proc.append(path_img)
    n_imgs = len(files_proc)

    if n_imgs == 0:
        log_message(f'NO IMAGES FOUND. Check your settings.',
                    callback_fun=callback_log)
        return

    # Process files
    for idx, path_img in enumerate(files_proc):
        imgs = []
        files = []
        sizes_orginal = []

        log_message(f'Segmenting image : {path_img.name}',
                    callback_fun=callback_log)

        if callback_status:
            callback_status(f'Segmenting image : {path_img.name}')

        if callback_progress:
            progress = float((idx + 1) / n_imgs)
            callback_progress(progress)

        # Read images
        img = imread(str(path_img))
        if img.ndim != 2:
            log_message(
                f'\nERROR\n  Input image has to be 2D. Current image is {img.ndim}D',
                callback_fun=callback_log)
            continue

        sizes_orginal.append(img.shape)

        # Resize
        if new_size:

            # New size can also be defined as a scalar factor
            if len(new_size) == 1:
                scale_factor = new_size[0]
                img_size = img.shape
                new_size = tuple(int(ti / scale_factor) for ti in img_size)

            img = resize(img, new_size)

        img_zeros = np.zeros(img.shape)

        # For object segmentation
        img_3d_dpi = np.dstack([img_zeros, img_zeros, img])
        imgs.append(img_3d_dpi)
        files.append(path_img)

        # >>> Call function for prediction
        data = {
            'imgs': imgs,
            'file_names': files,
            'sizes_orginal': sizes_orginal,
            'channels': channels,
            'new_size': new_size,
            'obj_name': obj_name
        }

        # Create new output path if specified
        if not isinstance(path_save, pathlib.PurePath):
            path_save_results = create_output_path(path_img.parent,
                                                   path_save_str_replace,
                                                   subfolder='',
                                                   create_path=True)
            path_save_settings = path_save_results

        cellpose_predict(data,
                         config,
                         path_save=path_save_results,
                         callback_log=callback_log)

    # Save settings
    if len(imgs) > 0:
        fp = open(str(path_save_results / 'segmentation_settings.json'), "w")
        json.dump(par_dict, fp, indent=4, sort_keys=True)
        fp.close()

    log_message(f'\n BATCH SEGMENTATION finished', callback_fun=callback_log)
Exemple #6
0
def folder_prepare_prediction(path_process,
                              channel_ident,
                              img_ext,
                              path_save,
                              projection_type,
                              subfolder=None,
                              search_recursive=False,
                              callback_log=None,
                              callback_status=None,
                              callback_progress=None):
    """[summary]

    Parameters
    ----------
    path_process : [type]
        [description]
    channel_ident : str
        [description]
    img_ext : str
        Image extension
    path_save : pathlin object or string
        Path to save results,
        - If Pathlib object, then this absolute path is used.
        - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path).
          And results will be stored in subfolder 'segmentation-input'
    projection_type : [type]
        [description]
    subfolder: str
        subfolder where data should be stored. Will only be used when string replacement for path is used. 
    search_recursive : bool
        Recursively search folder, default: false.
    callback_log : [type], optional
        [description], by default None
    callback_status : [type], optional
        [description], by default None
    callback_progress : [type], optional
        [description], by default None
    """

    # Print all input parameters
    log_message(f"Function (segment_obj_indiv) called with: {str(locals())} ",
                callback_fun=callback_log)

    # Use provided absolute user-path to save images.
    if isinstance(path_save, pathlib.PurePath):
        path_save_results = path_save
        if not path_save_results.is_dir():
            path_save_results.mkdir(parents=True)
        path_save_settings = path_save_results

    else:
        path_save_str_replace = path_save

    # How to look for files
    files_proc = []
    if search_recursive:
        for path_img in path_process.rglob(f'*{channel_ident}*{img_ext}'):
            files_proc.append(path_img)
    else:
        for path_img in path_process.glob(f'*{channel_ident}*{img_ext}'):
            files_proc.append(path_img)

    # Process files
    n_files = len(files_proc)
    for idx, file_proc in enumerate(files_proc):

        log_message(f'\n>>> Processing file: {file_proc}',
                    callback_fun=callback_status)

        if callback_progress:
            progress = float((idx + 1) / n_files)
            callback_progress(progress)

        name_base = file_proc.stem

        # Create new output path if specified
        if not isinstance(path_save, pathlib.PurePath):
            path_save_results = create_output_path(file_proc.parent,
                                                   path_save_str_replace,
                                                   subfolder=subfolder,
                                                   create_path=True)
            log_message(f'Results will be save here : {path_save_results}',
                        callback_fun=callback_status)
            path_save_settings = path_save_results

        # Create subfolder when processing individual images
        if projection_type == 'indiv':
            path_save_indiv = path_save_results / name_base

            if not path_save_indiv.is_dir():
                path_save_indiv.mkdir(parents=True)
                path_save_settings = path_save_indiv

        # Open image
        print(file_proc)
        img = imread(str(file_proc))

        img_properties = {
            "file_process": str(file_proc),
            "img_name": file_proc.name,
            "img_path": str(file_proc.parent),
            "channel_ident": channel_ident,
            "projection_type": projection_type
        }

        name_json = path_save_settings / f'img-prop__{name_base}.json'
        with open(name_json, 'w') as fp:
            json.dump(img_properties, fp, sort_keys=True, indent=4)

        # Process depending specified option
        if projection_type == 'indiv':

            for i in range(img.shape[0]):
                name_save = path_save_indiv / f'{name_base}_Z{str(i+1).zfill(3)}.png'

                if name_save.is_file():
                    log_message(
                        f'File already exists. Will be overwritten {name_save}',
                        callback_fun=callback_log)
                imsave(str(name_save), img[i, :, :])

        else:

            if projection_type == 'mean':
                img_proj = img.mean(axis=0)

            elif projection_type == 'max':
                img_proj = img.max(axis=0)

            name_save = path_save_results / f'{name_base}.png'

            if name_save.is_file():
                log_message(
                    f'File already exists. Will be overwritten {name_save}',
                    callback_fun=callback_log)
            imsave(str(name_save), img_proj.astype('uint16'))
def cellpose_predict(data, config, path_save, callback_log=None):
    """ Perform prediction with CellPose. 

    Parameters
    ----------
    data : dict
        Contains data on which prediction should be performed. 
    config : dict
        Configuration of CellPose prediction. 
    path_save : pathline Path object
        Path where results will be saved. 
    """

    # Get data
    imgs = data['imgs']
    file_names = data['file_names']
    channels = data['channels']
    obj_name = data['obj_name']
    sizes_orginal = data['sizes_orginal']
    new_size = data['new_size']

    # Get config
    model_type = config['model_type']
    diameter = config['diameter']
    net_avg = config['net_avg']
    resample = config['resample']

    log_message(f'\nPerforming segmentation of {obj_name}\n',
                callback_fun=callback_log)

    start_time = time.time()

    if not path_save.is_dir():
        path_save.mkdir()

    # Perform segmentation with CellPose
    model = models.Cellpose(
        gpu=False,
        model_type=model_type)  # model_type can be 'cyto' or 'nuclei'
    masks, flows, styles, diams = model.eval(imgs,
                                             diameter=diameter,
                                             channels=channels,
                                             net_avg=net_avg,
                                             resample=resample)

    # Display and save results
    log_message(f'\n Creating outputs ...\n', callback_fun=callback_log)
    n_img = len(imgs)

    for idx in tqdm(range(n_img)):

        # Get images and file-name
        file_name = file_names[idx]
        maski = masks[idx]
        flowi = flows[idx][0]
        imgi = imgs[idx]

        # Rescale each channel separately
        imgi_norm = imgi.copy()
        for idim in range(3):
            imgdum = imgi[:, :, idim]

            # Renormalize to 8bit between 1 and 99 percentile
            pa = np.percentile(imgdum, 0.5)
            pb = np.percentile(imgdum, 99.5)

            if pb > pa:
                imgi_norm[:, :, idim] = 255 * (imgdum - pa) / (pb - pa)

        # Save flow
        io.imsave(str(path_save / f'{file_name.stem}__flow__{obj_name}.png'),
                  flowi)

        # Resize masks if necessary
        if new_size:
            mask_full = resize_mask(maski, sizes_orginal[idx])

            io.imsave(
                str(path_save / f'{file_name.stem}__mask__{obj_name}.png'),
                mask_full)
            io.imsave(
                str(path_save /
                    f'{file_name.stem}__mask_resize__{obj_name}.png'), maski)

        else:
            io.imsave(
                str(path_save / f'{file_name.stem}__mask__{obj_name}.png'),
                maski)

        # Save mask and flow images
        #f_mask = str(path_save / f'{file_name.stem}__mask__{obj_name}.png')
        #log_message(f'\nMask saved to file: {f_mask}\n', callback_fun=callback_log)

        #io.imsave(str(path_save / f'{file_name.stem}__mask__{obj_name}.png'), maski)

        # Save overview image
        fig = plt.figure(figsize=(12, 3))
        plot.show_segmentation(fig, imgi_norm.astype('uint8'), maski, flowi)
        plt.tight_layout()
        fig.savefig(str(path_save / f'{file_name.stem}__seg__{obj_name}.png'),
                    dpi=300)
        plt.close(fig)

    log_message(
        f"\nSegmentation of provided images finished ({(time.time() - start_time)}s)",
        callback_fun=callback_log)