From 731f3e52a9aa1ea44cdb04647599b29190160f7e Mon Sep 17 00:00:00 2001 From: Francesco Padovani Date: Thu, 21 Nov 2024 17:55:31 +0100 Subject: [PATCH] fix: uses custom FloatLineEdit for metadata dialogue --- cellacdc/_debug.py | 26 ++++++ cellacdc/apps.py | 31 +++---- cellacdc/core.py | 91 +++++++++++++++++- cellacdc/load.py | 29 ++++++ cellacdc/scripts/split_segm_mask_yeast.py | 107 ++++++++++++++++++++++ 5 files changed, 266 insertions(+), 18 deletions(-) create mode 100644 cellacdc/scripts/split_segm_mask_yeast.py diff --git a/cellacdc/_debug.py b/cellacdc/_debug.py index c0c4bd2c..75fbf920 100644 --- a/cellacdc/_debug.py +++ b/cellacdc/_debug.py @@ -1,9 +1,35 @@ import inspect +import numpy as np import pandas as pd from . import printl +def split_segm_masks_mother_bud_line(lab, obj, obj_bud, interc_perp, slope_perp): + import matplotlib.pyplot as plt + + lab = np.zeros_like(lab) + lab[obj.slice][obj.image] = obj.label + lab[obj_bud.slice][obj_bud.image] = obj_bud.label + + y0 = 0 + x0 = (y0 - interc_perp)/slope_perp + + x1 = lab.shape[1] + y1 = slope_perp*x1 + interc_perp + + x2 = 0 + y2 = interc_perp + + y3 = lab.shape[0] + x3 = (y3 - interc_perp)/slope_perp + + plt.imshow(lab) + plt.plot([x0, x1, x2, x3], [y0, y1, y2, y3], 'r') + plt.show() + + import pdb; pdb.set_trace() + def print_all_callers(): currentframe = inspect.currentframe() outerframes = inspect.getouterframes(currentframe, 2) diff --git a/cellacdc/apps.py b/cellacdc/apps.py index f0853299..cfe94d3d 100755 --- a/cellacdc/apps.py +++ b/cellacdc/apps.py @@ -5025,11 +5025,8 @@ def __init__( gridLayout.addWidget( self.TimeIncrementLabel, row, 0, alignment=Qt.AlignRight ) - self.TimeIncrementSpinBox = QDoubleSpinBox() - self.TimeIncrementSpinBox.setDecimals(7) - self.TimeIncrementSpinBox.setMaximum(2147483647.0) + self.TimeIncrementSpinBox = widgets.FloatLineEdit() self.TimeIncrementSpinBox.setValue(TimeIncrement) - self.TimeIncrementSpinBox.setAlignment(Qt.AlignCenter) gridLayout.addWidget(self.TimeIncrementSpinBox, row, 1) if SizeT == 1 or not ask_TimeIncrement: @@ -5041,11 +5038,8 @@ def __init__( gridLayout.addWidget( self.PhysicalSizeZLabel, row, 0, alignment=Qt.AlignRight ) - self.PhysicalSizeZSpinBox = QDoubleSpinBox() - self.PhysicalSizeZSpinBox.setDecimals(7) - self.PhysicalSizeZSpinBox.setMaximum(2147483647.0) + self.PhysicalSizeZSpinBox = widgets.FloatLineEdit() self.PhysicalSizeZSpinBox.setValue(PhysicalSizeZ) - self.PhysicalSizeZSpinBox.setAlignment(Qt.AlignCenter) gridLayout.addWidget(self.PhysicalSizeZSpinBox, row, 1) if SizeZ==1 or not ask_PhysicalSizes: @@ -5057,11 +5051,8 @@ def __init__( gridLayout.addWidget( self.PhysicalSizeYLabel, row, 0, alignment=Qt.AlignRight ) - self.PhysicalSizeYSpinBox = QDoubleSpinBox() - self.PhysicalSizeYSpinBox.setDecimals(7) - self.PhysicalSizeYSpinBox.setMaximum(2147483647.0) + self.PhysicalSizeYSpinBox = widgets.FloatLineEdit() self.PhysicalSizeYSpinBox.setValue(PhysicalSizeY) - self.PhysicalSizeYSpinBox.setAlignment(Qt.AlignCenter) gridLayout.addWidget(self.PhysicalSizeYSpinBox, row, 1) if not ask_PhysicalSizes: @@ -5073,11 +5064,8 @@ def __init__( gridLayout.addWidget( self.PhysicalSizeXLabel, row, 0, alignment=Qt.AlignRight ) - self.PhysicalSizeXSpinBox = QDoubleSpinBox() - self.PhysicalSizeXSpinBox.setDecimals(7) - self.PhysicalSizeXSpinBox.setMaximum(2147483647.0) + self.PhysicalSizeXSpinBox = widgets.FloatLineEdit() self.PhysicalSizeXSpinBox.setValue(PhysicalSizeX) - self.PhysicalSizeXSpinBox.setAlignment(Qt.AlignCenter) gridLayout.addWidget(self.PhysicalSizeXSpinBox, row, 1) if not ask_PhysicalSizes: @@ -9937,7 +9925,7 @@ class SelectSegmFileDialog(QDialog): def __init__( self, images_ls, parent_path, parent=None, addNewFileButton=False, basename='', infoText=None, - fileType='segmentation' + fileType='segmentation', allowMultipleSelection=False ): self.cancel = True self.selectedItemText = '' @@ -9945,6 +9933,7 @@ def __init__( self.removeOthers = False self.okAllPos = False self.newSegmEndName = None + self.allowMultipleSelection = allowMultipleSelection self.basename = basename images_ls = sorted(images_ls, key=len) @@ -9994,6 +9983,10 @@ def __init__( listWidget.addItems(images_ls) listWidget.setCurrentRow(0) listWidget.itemDoubleClicked.connect(self.listDoubleClicked) + if allowMultipleSelection: + listWidget.setSelectionMode( + QAbstractItemView.SelectionMode.ExtendedSelection + ) self.items = list(images_ls) self.listWidget = listWidget @@ -10063,6 +10056,10 @@ def ok_cb(self, event=None): self.cancel = False self.selectedItemText = self.listWidget.selectedItems()[0].text() self.selectedItemIdx = self.items.index(self.selectedItemText) + self.selectedItemTexts = [ + selectedItem.text() + for selectedItem in self.listWidget.selectedItems() + ] self.close() def exec_(self): diff --git a/cellacdc/core.py b/cellacdc/core.py index 29b735b1..4c1b2c61 100755 --- a/cellacdc/core.py +++ b/cellacdc/core.py @@ -2599,4 +2599,93 @@ def fucci_pipeline_executor_map(input, **filter_kwargs): processed_img = preprocess.fucci_filter(sum_img, **filter_kwargs) return frame_i, processed_img - \ No newline at end of file + +def split_segm_masks_mother_bud_line( + cells_segm_data, segm_data_to_split, acdc_df, + debug=False + ): + acdc_df = acdc_df.set_index(['frame_i', 'Cell_ID']) + split_segm_away = np.zeros_like(segm_data_to_split) + split_segm_close = np.zeros_like(segm_data_to_split) + + pbar = tqdm(total=len(cells_segm_data), ncols=100, position=1, leave=False) + for frame_i, lab in enumerate(cells_segm_data): + rp = skimage.measure.regionprops(lab) + rp_mapper = {obj.label:obj for obj in rp} + for obj in rp: + try: + ccs = acdc_df.at[(frame_i, obj.label), 'cell_cycle_stage'] + except Exception as err: + pbar.update() + continue + + if ccs != 'S': + pbar.update() + continue + + try: + relationship = acdc_df.at[(frame_i, obj.label), 'relationship'] + except Exception as err: + pbar.update() + continue + + if relationship == 'bud': + pbar.update() + continue + + bud_ID = int(acdc_df.at[(frame_i, obj.label), 'relative_ID']) + obj_bud = rp_mapper[bud_ID] + + moth_ID = obj.label + yc_m, xc_m = obj.centroid + yc_b, xc_b = obj_bud.centroid + + slope_mb = (yc_b - yc_m)/(xc_b - yc_b) + if slope_mb != 0: + slope_perp = -1/slope_mb + interc_perp = yc_m - xc_m*slope_perp + + if debug: + from cellacdc import _debug + _debug.split_segm_masks_mother_bud_line( + lab, obj, obj_bud, interc_perp, slope_perp + ) + + + + + + pbar.update() + pbar.close() + +def get_split_line_ref_points_img(img, slope_perp, interc_perp): + if slope_perp == np.inf: + ... + else: + Y, X = lab.shape + y0 = 0 + x0 = y0 - interc_perp/slope_perp + + x1 = X + y1 = slope_perp*x1 + interc_perp + + x2 = 0 + y2 = interc_perp + + y3 = Y + x3 = y3 - interc_perp/slope_perp + + if x0 < X: + x_ref_0 = x0 + y_ref_0 = y0 + else: + x_ref_0 = x1 + y_ref_0 = y1 + + if x1 > 0: + x_ref1 = x1 + y_ref1 = y1 + else: + x_ref1 = x2 + y_ref1 = 0 + \ No newline at end of file diff --git a/cellacdc/load.py b/cellacdc/load.py index a85f5c57..b38a1359 100755 --- a/cellacdc/load.py +++ b/cellacdc/load.py @@ -695,6 +695,35 @@ def get_segm_files(images_path): ] return segm_files +def get_segm_endnames_from_exp_path(exp_path, pos_foldernames=None): + if pos_foldernames is None: + pos_foldernames = myutils.get_pos_foldernames(exp_path) + + existingEndNames = set() + for p, pos in enumerate(pos_foldernames): + pos_path = os.path.join(exp_path, pos) + images_path = os.path.join(pos_path, 'Images') + basename, chNames = myutils.getBasenameAndChNames(images_path) + # Use first found channel, it doesn't matter for metrics + for chName in chNames: + filePath = myutils.getChannelFilePath(images_path, chName) + if filePath: + break + else: + raise FileNotFoundError( + f'None of the channels "{chNames}" were found in the path ' + f'"{images_path}".' + ) + _posData = loadData(filePath, chName) + _posData.getBasenameAndChNames() + found_files = get_segm_files(_posData.images_path) + _existingEndnames = get_endnames( + _posData.basename, found_files + ) + existingEndNames.update(_existingEndnames) + + return existingEndNames + def get_files_with(images_path: os.PathLike, with_text: str, ext: str=None): ls = myutils.listdir(images_path) found_files = [] diff --git a/cellacdc/scripts/split_segm_mask_yeast.py b/cellacdc/scripts/split_segm_mask_yeast.py new file mode 100644 index 00000000..b774ff0e --- /dev/null +++ b/cellacdc/scripts/split_segm_mask_yeast.py @@ -0,0 +1,107 @@ +import os + +from tqdm import tqdm + +import qtpy.compat + +from cellacdc import printl, myutils, apps, load, core +from cellacdc._run import _setup_app +from cellacdc.utils.base import NewThreadMultipleExpBaseUtil + +DEBUG = True + +def ask_select_folder(): + selected_path = qtpy.compat.getexistingdirectory( + caption='Select experiment folder to analyse', + basedir=myutils.getMostRecentPath() + ) + return selected_path + +def get_exp_path_pos_foldernames(selected_path): + folder_type = myutils.determine_folder_type(selected_path) + is_pos_folder, is_images_folder, exp_path = folder_type + if is_pos_folder: + exp_path = os.path.dirname(selected_path) + pos_foldernames = myutils.get_pos_foldernames(exp_path) + elif is_images_folder: + pos_path = os.path.dirname(selected_path) + exp_path = os.path.dirname(pos_path) + pos_foldernames = [os.path.basename(pos_path)] + else: + exp_path = selected_path + pos_foldernames = myutils.get_pos_foldernames(exp_path) + + return exp_path, pos_foldernames + +def select_segm_masks(exp_path, pos_foldernames): + infoText = 'Select which segmentation file OF THE CELLS:' + existingEndNames = load.get_segm_endnames_from_exp_path( + exp_path, pos_foldernames=pos_foldernames + ) + win = apps.SelectSegmFileDialog( + existingEndNames, exp_path, + infoText=infoText, + fileType='segmentation' + ) + win.exec_() + if win.cancel: + return + + cells_segm_endname = win.selectedItemText + + infoText = 'Select segmentation files to SPLIT:' + existingEndNames.discard(cells_segm_endname) + win = apps.SelectSegmFileDialog( + existingEndNames, exp_path, + infoText=infoText, + fileType='segmentation', + allowMultipleSelection=True + ) + win.exec_() + if win.cancel: + return + + list_segm_endnames_to_split = win.selectedItemTexts + return cells_segm_endname, list_segm_endnames_to_split + +def run(): + app, splashScreen = _setup_app(splashscreen=True) + splashScreen.close() + + selected_path = ask_select_folder() + if not selected_path: + exit('Execution cancelled') + + myutils.addToRecentPaths(selected_path) + exp_path, pos_foldernames = get_exp_path_pos_foldernames(selected_path) + + selected_segm_endnames = select_segm_masks(exp_path, pos_foldernames) + if selected_segm_endnames is None: + exit('Execution cancelled') + + cells_segm_endname, list_segm_endnames_to_split = selected_segm_endnames + acdc_df_endname = cells_segm_endname.replace('segm', 'acdc_output') + pbar = tqdm(total=len(pos_foldernames), ncols=100) + for pos in pos_foldernames: + images_path = os.path.join(exp_path, pos, 'Images') + cells_segm_data = load.load_segm_file( + images_path, end_name_segm_file=cells_segm_endname + ) + acdc_df = load.load_acdc_df_file( + images_path, end_name_acdc_df_file=acdc_df_endname + ) + for segm_endname in list_segm_endnames_to_split: + segm_data_to_split = load.load_segm_file( + images_path, end_name_segm_file=segm_endname + ) + core.split_segm_masks_mother_bud_line( + cells_segm_data, segm_data_to_split, acdc_df, + debug=DEBUG + ) + pbar.update() + + pbar.close() + + +if __name__ == '__main__': + run() \ No newline at end of file