From 4728053309f9ae40c199f694b7eb6795f8d87531 Mon Sep 17 00:00:00 2001 From: Diane Adjavon <diane.adjavon@ed.ac.uk> Date: Thu, 12 Jan 2023 11:51:14 +0000 Subject: [PATCH] refactor: Remove unused code and obsolete tests Relates to #52 --- examples/extraction/pos_example.py | 1 - src/agora/io/bridge.py | 6 - src/agora/io/signal.py | 18 -- src/agora/io/utils.py | 23 -- src/agora/io/writer.py | 229 ------------------ src/agora/utils/example.py | 53 ---- src/agora/utils/lineage.py | 57 ----- src/aliby/baby_client.py | 69 ------ src/aliby/haystack.py | 31 --- src/aliby/io/omero.py | 5 +- src/aliby/io/utils.py | 50 ---- src/aliby/pipeline.py | 32 --- src/aliby/tile/tiler.py | 2 - src/aliby/tile/traps.py | 100 +------- src/aliby/utils/argo.py | 55 +---- src/aliby/utils/cache.py | 141 ----------- src/extraction/core/extractor.py | 1 + src/extraction/core/functions/io.py | 12 - src/extraction/core/functions/utils.py | 19 -- src/extraction/core/functions/versioning.py | 1 + src/extraction/core/omero.py | 35 --- src/logfile_parser/swainlab_parser.py | 17 +- .../benchmarks/post_processing.py | 1 + src/postprocessor/core/abc.py | 1 + src/postprocessor/core/group.py | 137 ----------- src/postprocessor/core/lineageprocess.py | 9 +- src/postprocessor/core/processor.py | 41 +--- .../core/reshapers/bud_metric.py | 1 - src/postprocessor/core/reshapers/buddings.py | 3 +- src/postprocessor/core/reshapers/merger.py | 7 +- src/postprocessor/core/reshapers/picker.py | 26 +- src/postprocessor/grouper.py | 5 - tests/agora/example_test.py | 25 -- tests/aliby/network/test_baby_client.py | 91 ------- tests/aliby/network/test_post_processing.py | 8 - 35 files changed, 23 insertions(+), 1289 deletions(-) delete mode 100644 src/agora/utils/example.py delete mode 100644 src/aliby/io/utils.py delete mode 100644 src/aliby/utils/cache.py delete mode 100644 src/extraction/core/functions/io.py delete mode 100644 src/extraction/core/functions/utils.py delete mode 100644 src/extraction/core/omero.py delete mode 100644 src/postprocessor/core/group.py delete mode 100644 tests/agora/example_test.py delete mode 100644 tests/aliby/network/test_baby_client.py diff --git a/examples/extraction/pos_example.py b/examples/extraction/pos_example.py index 9372fa1d..da44f464 100644 --- a/examples/extraction/pos_example.py +++ b/examples/extraction/pos_example.py @@ -14,5 +14,4 @@ params = Parameters( ext = Extractor(params, omero_id=19310) -# ext.extract_exp(tile_size=117) d = ext.extract_tp(tp=1, tile_size=117) diff --git a/src/agora/io/bridge.py b/src/agora/io/bridge.py index 478408f3..9eb0c640 100644 --- a/src/agora/io/bridge.py +++ b/src/agora/io/bridge.py @@ -151,12 +151,6 @@ def attrs_from_h5(fpath: str): return dict(f.attrs) -def parameters_from_h5(fpath: str): - """Return parameters from an h5 file.""" - attrs = attrs_from_h5(fpath) - return yaml.safe_load(attrs["parameters"]) - - def image_creds_from_h5(fpath: str): """Return image id and server credentials from an h5.""" attrs = attrs_from_h5(fpath) diff --git a/src/agora/io/signal.py b/src/agora/io/signal.py index 4c6bf0f7..61568824 100644 --- a/src/agora/io/signal.py +++ b/src/agora/io/signal.py @@ -351,24 +351,6 @@ class Signal(BridgeH5): def stem(self): return self.filename.stem - # def dataset_to_df(self, f: h5py.File, path: str): - - # all_indices = self.index_names - - # valid_indices = { - # k: f[path][k][()] for k in all_indices if k in f[path].keys() - # } - - # new_index = pd.MultiIndex.from_arrays( - # list(valid_indices.values()), names=valid_indices.keys() - # ) - - # return pd.DataFrame( - # f[path + "/values"][()], - # index=new_index, - # columns=f[path + "/timepoint"][()], - # ) - def store_signal_url( self, fullname: str, node: t.Union[h5py.Dataset, h5py.Group] ): diff --git a/src/agora/io/utils.py b/src/agora/io/utils.py index 0acca82c..b32b9314 100644 --- a/src/agora/io/utils.py +++ b/src/agora/io/utils.py @@ -35,29 +35,6 @@ def imread(path): return cv2.imread(str(path), -1) -class ImageCache: - """HDF5-based image cache for faster loading of the images once they've - been read. - """ - - def __init__(self, file, name, shape, remote_fn): - self.store = h5py.File(file, "a") - # Create a dataset - self.dataset = self.store.create_dataset( - name, shape, dtype=np.float, fill_value=np.nan - ) - self.remote_fn = remote_fn - - def __getitem__(self, item): - cached = self.dataset[item] - if np.any(np.isnan(cached)): - full = self.remote_fn(item) - self.dataset[item] = full - return full - else: - return cached - - class Cache: """ Fixed-length mapping to use as a cache. diff --git a/src/agora/io/writer.py b/src/agora/io/writer.py index f11827fa..bc8769a2 100644 --- a/src/agora/io/writer.py +++ b/src/agora/io/writer.py @@ -135,10 +135,6 @@ class DynamicWriter: # write all data, signified by the empty tuple hgroup[key][()] = data - # def _check_key(self, key): - # if key not in self.datatypes: - # raise KeyError(f"No defined data type for key {key}") - def write(self, data: dict, overwrite: list, meta: dict = {}): """ Write data and metadata to h5 file. @@ -222,220 +218,6 @@ class TilerWriter(DynamicWriter): super().write(data=data, overwrite=overwrite, meta=meta) -# Alan: we use complex numbers because... -@timed() -def save_complex(array, dataset): - # append array, an 1D array of complex numbers, onto dataset, a 2D array of real numbers - n = len(array) - if n > 0: - dataset.resize(dataset.shape[0] + n, axis=0) - dataset[-n:, 0] = array.real - dataset[-n:, 1] = array.imag - - -@timed() -def load_complex(dataset): - # convert 2D dataset into a 1D array of complex numbers - array = dataset[:, 0] + 1j * dataset[:, 1] - return array - - -class BabyWriter(DynamicWriter): - """ - Write data stored in a Baby instance to h5 files. - - Assumes the edgemasks are of form ((max_ncells, max_tps, tile_size, tile_size), bool). - """ - - compression = "gzip" - max_ncells = 2e5 # Alan: Could just make this None - max_tps = 1e3 # Could just make this None - # the number of cells in a chunk for edge masks - chunk_cells = 25 - default_tile_size = 117 - datatypes = { - "centres": ((None, 2), np.uint16), - "position": ((None,), np.uint16), - "angles": ((None,), h5py.vlen_dtype(np.float32)), - "radii": ((None,), h5py.vlen_dtype(np.float32)), - "edgemasks": ( - (max_ncells, max_tps, default_tile_size, default_tile_size), - bool, - ), - "ellipse_dims": ((None, 2), np.float32), - "cell_label": ((None,), np.uint16), - "trap": ((None,), np.uint16), - "timepoint": ((None,), np.uint16), - # "mother_assign": ((None,), h5py.vlen_dtype(np.uint16)), - "mother_assign_dynamic": ((None,), np.uint16), - "volumes": ((None,), np.float32), - } - group = "cell_info" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._traps_initialised = False - - def __init_trap_info(self): - # requires traps to have been initialised - trap_metadata = load_attributes(self.file, "trap_info") - tile_size = trap_metadata.get("tile_size", self.default_tile_size) - max_tps = self.metadata["time_settings/ntimepoints"][0] - self.datatypes["edgemasks"] = ( - (self.max_ncells, max_tps, tile_size, tile_size), - bool, - ) - self._traps_initialised = True - - def __init_edgemasks(self, hgroup, edgemasks, current_indices, n_cells): - # create the values dataset in the h5 file - # holds the edge masks and has shape (n_tps, n_cells, tile_size, tile_size) - key = "edgemasks" - max_shape, dtype = self.datatypes[key] - shape = (n_cells, 1) + max_shape[2:] - chunks = (self.chunk_cells, 1) + max_shape[2:] - val_dset = hgroup.create_dataset( - "values", - shape=shape, - maxshape=max_shape, - dtype=dtype, - chunks=chunks, - compression=self.compression, - ) - val_dset[:, 0] = edgemasks - # create index dataset in the h5 file: - # the (trap, cell_id) description used to index into the values and has shape (n_cells, 2) - ix_max_shape = (max_shape[0], 2) - ix_shape = (0, 2) - ix_dtype = np.uint16 - ix_dset = hgroup.create_dataset( - "indices", - shape=ix_shape, - maxshape=ix_max_shape, - dtype=ix_dtype, - compression=self.compression, - ) - save_complex(current_indices, ix_dset) - - def __append_edgemasks(self, hgroup, edgemasks, current_indices): - val_dset = hgroup["values"] - ix_dset = hgroup["indices"] - existing_indices = load_complex(ix_dset) - # check if there are any new labels - available = np.in1d(current_indices, existing_indices) - missing = current_indices[~available] - all_indices = np.concatenate([existing_indices, missing]) - # resizing - debug_t = perf_counter() # for timing code for debugging - n_tps = val_dset.shape[1] + 1 - n_add_cells = len(missing) - # resize dataset for Time and Cells - new_shape = (val_dset.shape[0] + n_add_cells, n_tps) + val_dset.shape[ - 2: - ] - val_dset.resize(new_shape) - logging.debug(f"Timing:resizing:{perf_counter() - debug_t}") - # write data - cell_indices = np.where(np.in1d(all_indices, current_indices))[0] - for ix, mask in zip(cell_indices, edgemasks): - try: - val_dset[ix, n_tps - 1] = mask - except Exception as e: - logging.debug( - "Exception: {}:{}, {}, {}".format( - e, ix, n_tps, val_dset.shape - ) - ) - # save the index values - save_complex(missing, ix_dset) - - def write_edgemasks(self, data, keys, hgroup): - """ - Write edgemasks to h5 file. - - Parameters - ---------- - data: list of arrays - Data to be written, in the form (trap_ids, cell_labels, edgemasks) - keys: list of str - Names corresponding to the elements of data. - For example: ["trap", "cell_label", "edgemasks"] - hgroup: group object - Group to write to in h5 file. - """ - if not self._traps_initialised: - self.__init_trap_info() - key = "edgemasks" - val_key = "values" - traps, cell_labels, edgemasks = data - n_cells = len(cell_labels) - hgroup = hgroup.require_group(key) - # create complex indices with traps as real part and cell_labels as imaginary part - current_indices = np.array(traps) + 1j * np.array(cell_labels) - if val_key not in hgroup: - self.__init_edgemasks(hgroup, edgemasks, current_indices, n_cells) - else: - self.__append_edgemasks(hgroup, edgemasks, current_indices) - - def write( - self, data: dict, overwrite: list, tp: int = None, meta: dict = {} - ): - """ - Write data from a Baby instance, including edgemasks. - - Parameters - ---------- - data: dict - A dict of datasets and data - overwrite: list of str - A list of datasets to overwrite - tp: int - The time point of interest - meta: dict, optional - Metadata to be written as attributes of the h5 file - """ - with h5py.File(self.file, "a") as store: - hgroup = store.require_group(self.group) - # write data - for key, value in data.items(): - if key not in self.datatypes: - raise KeyError( - f"BabyWriter: No defined data type for key {key}" - ) - else: - try: - if key.startswith("attrs/"): - # metadata - key = key.split("/")[1] - hgroup.attrs[key] = value - elif key in overwrite: - # delete and replace existing dataset - self._overwrite(value, key, hgroup) - elif key == "edgemasks": - keys = ["trap", "cell_label", "edgemasks"] - value = [data[x] for x in keys] - edgemask_dset = hgroup.get(key + "/values", None) - if ( - edgemask_dset - and tp < edgemask_dset[()].shape[1] - ): - # data already exists - print( - f"BabyWriter: Skipping edgemasks in tp {tp}" - ) - else: - self.write_edgemasks(value, keys, hgroup) - else: - # append or create new dataset - self._append(value, key, hgroup) - except Exception as e: - print(key, value) - raise (e) - # write metadata - for key, value in meta.items(): - hgroup.attrs[key] = value - - class LinearBabyWriter(DynamicWriter): """ Write data stored in a Baby instance to h5 files. @@ -456,7 +238,6 @@ class LinearBabyWriter(DynamicWriter): "cell_label": ((None,), np.uint16), "trap": ((None,), np.uint16), "timepoint": ((None,), np.uint16), - # "mother_assign": ((None,), h5py.vlen_dtype(np.uint16)), "mother_assign_dynamic": ((None,), np.uint16), "volumes": ((None,), np.float32), } @@ -667,14 +448,6 @@ class Writer(BridgeH5): if overwrite == "overwrite": # TODO refactor overwriting if path in f: del f[path] - # elif overwrite == "accumulate": # Add a number if needed - # if path in f: - # parent, name = path.rsplit("/", maxsplit=1) - # n = sum([x.startswith(name) for x in f[path]]) - # path = path + str(n).zfill(3) - # elif overwrite == "skip": - # if path in f: - # logging.debug("Skipping dataset {}".format(path)) logging.debug( "{} {} to {} and {} metadata fields".format( overwrite, type(data), path, len(meta) @@ -931,8 +704,6 @@ class Writer(BridgeH5): return existing_cells, new_cells - - def locate_indices(existing, new): if new.any(): if new.shape[1] > 1: diff --git a/src/agora/utils/example.py b/src/agora/utils/example.py deleted file mode 100644 index e3ff571a..00000000 --- a/src/agora/utils/example.py +++ /dev/null @@ -1,53 +0,0 @@ -"""This is an example module to show the structure.""" -from typing import Union - - -class ExampleClass: - """This is an example class to show the structure.""" - - def __init__(self, parameter: int): - """This class takes one parameter and is used to add one to that - parameter. - - :param parameter: The parameter for this class - """ - self.parameter = parameter - - def add_one(self): - """Takes the parameter and adds one. - - >>> x = ExampleClass(1) - >>> x.add_one() - 2 - - :return: the parameter + 1 - """ - return self.parameter + 1 - - def add_n(self, n: int): - """Adds n to the class instance's parameter. - - For instance - >>> x = ExampleClass(1) - >>> x.add_n(10) - 11 - - :param n: The number to add - :return: the parameter + n - """ - return self.parameter + n - - -def example_function(parameter: Union[int, str]): - """This is a factory function for an ExampleClass. - - :param parameter: the parameter to give to the example class - :return: An example class - """ - try: - return ExampleClass(int(parameter)) - except ValueError as e: - raise ValueError( - f"The parameter {parameter} could not be turned " - f"into an integer." - ) from e diff --git a/src/agora/utils/lineage.py b/src/agora/utils/lineage.py index 5b668686..52fb552b 100644 --- a/src/agora/utils/lineage.py +++ b/src/agora/utils/lineage.py @@ -20,60 +20,3 @@ def mb_array_to_dict(mb_array: np.ndarray): for mo, daughters in groupsort(mo_da).items() } - -def mb_array_to_indices(mb_array: np.ndarray): - """ - Convert a lineage ndarray (trap, mother_id, daughter_id) - into a dictionary of lists ( mother_id ->[daughters_ids] ) - """ - return pd.MultiIndex.from_arrays(mb_array[:, :2].T).union( - pd.MultiIndex.from_arrays(mb_array[:, [0, 2]].T) - ) - - -def group_matrix( - matrix: np.ndarray, - n_keys: int = 2, -) -> t.Dict[t.Tuple[int], t.List[int]]: - """Group a matrix of integers by grouping the first two columns - and setting the third one in a list. - - - Parameters - ---------- - matrix : np.ndarray - id_matrix, generally its columns are three integers indicating trap, - mother and daughter. - n_keys : int - number of keys to use to determine groups. - - Returns - ------- - t.Dict[t.Tuple[int], t.Collection[int, ...]] - The column(s) not used for generaeting keys are grouped as values. - - Examples - -------- - FIXME: Add docs. - - """ - lineage_dict = {} - if len(matrix): - - daughter = matrix[:, n_keys] - mother_global_id = matrix[:, :n_keys] - - iterator = groupby( - zip(mother_global_id, daughter), lambda x: str(x[0]) - ) - lineage_dict = {key: [x[1] for x in group] for key, group in iterator} - - def str_to_tuple(k: str) -> t.Tuple[int, ...]: - return tuple([int(x) for x in re.findall("[0-9]+", k)]) - - # Convert keys from str to tuple - lineage_dict = { - str_to_tuple(k): sorted(v) for k, v in lineage_dict.items() - } - - return lineage_dict diff --git a/src/aliby/baby_client.py b/src/aliby/baby_client.py index f13c5b02..6004af88 100644 --- a/src/aliby/baby_client.py +++ b/src/aliby/baby_client.py @@ -174,75 +174,6 @@ class BabyRunner: return format_segmentation(segmentation, tp) -class BabyClient: - """A dummy BabyClient object for Dask Demo. - - - Does segmentation one time point at a time. - Should work better with the parallelisation. - """ - - bf_channel = 0 - model_name = "prime95b_brightfield_60x_5z" - url = "http://localhost:5101" - max_tries = 50 - sleep_time = 0.1 - - def __init__(self, tiler): - self.tiler = tiler - self._session = None - - @property - def session(self): - if self._session is None: - r_session = requests.get(self.url + f"/session/{self.model_name}") - r_session.raise_for_status() - self._session = r_session.json()["sessionid"] - return self._session - - def get_data(self, tp): - return self.tiler.get_tp_data(tp, self.bf_channel).swapaxes(1, 3) - - # def queue_image(self, img, **kwargs): - # bit_depth = img.dtype.itemsize * 8 # bit depth = byte_size * 8 - # data = create_request(img.shape, bit_depth, img, **kwargs) - # status = requests.post( - # self.url + f"/segment?sessionid={self.session}", - # data=data, - # headers={"Content-Type": data.content_type}, - # ) - # status.raise_for_status() - # return status - - def get_segmentation(self): - try: - seg_response = requests.get( - self.url + f"/segment?sessionid={self.session}", timeout=120 - ) - seg_response.raise_for_status() - result = seg_response.json() - except Timeout as e: - raise e - except HTTPError as e: - raise e - return result - - def run_tp(self, tp, **kwargs): - # Get data - img = self.get_data(tp) - # Queue image - _ = self.queue_image(img, **kwargs) - # Get segmentation - for _ in range(self.max_tries): - try: - seg = self.get_segmentation() - break - except (Timeout, HTTPError): - time.sleep(self.sleep_time) - continue - return format_segmentation(seg, tp) - - def choose_model_from_params( modelset_filter=None, camera="prime95b", diff --git a/src/aliby/haystack.py b/src/aliby/haystack.py index 340584c5..d1368ffd 100644 --- a/src/aliby/haystack.py +++ b/src/aliby/haystack.py @@ -37,8 +37,6 @@ def timer(func, *args, **kwargs): ################## CUSTOM OBJECTS ################################## - - class ModelPredictor: """Generic object that takes a NN and returns the prediction. @@ -77,32 +75,3 @@ class ModelPredictorWriter(DynamicWriter): "timepoint": ((None,), np.uint16), } self.group = f"{self.name}_info" - - -class Saver: - channel_names = {0: "BrightField", 1: "GFP"} - - def __init__(self, tiler, save_directory, pos_name): - """This class straight up saves the trap data for use with neural networks in the future.""" - self.tiler = tiler - self.name = pos_name - self.save_dir = Path(save_directory) - - def channel_dir(self, index): - ch_dir = self.save_dir / self.channel_names[index] - if not ch_dir.exists(): - ch_dir.mkdir() - return ch_dir - - def get_data(self, tp, ch): - return self.tiler.get_tp_data(tp, ch).swapaxes(1, 3).swapaxes(1, 2) - - def cache(self, tp): - # Get a given time point - # split into channels - for ch in self.channel_names: - ch_dir = self.channel_dir(ch) - data = self.get_data(tp, ch) - for tid, trap in enumerate(data): - np.save(ch_dir / f"{self.name}_{tid}_{tp}.npy", trap) - return diff --git a/src/aliby/io/omero.py b/src/aliby/io/omero.py index 97b4f33d..5377c48a 100644 --- a/src/aliby/io/omero.py +++ b/src/aliby/io/omero.py @@ -127,7 +127,7 @@ class BridgeOmero: @abstractmethod def init_interface(self): - ... + pass @property def file_annotations(self): @@ -302,9 +302,6 @@ class Image(BridgeOmero): # metadata = load_attributes(filepath) bridge = BridgeH5(filepath) image_id = bridge.meta_h5["image_id"] - # server_info = safe_load(bridge.meta_h5["parameters"])["general"][ - # "server_info" - # ] return cls(image_id, **cls.server_info_from_h5(filepath)) @property diff --git a/src/aliby/io/utils.py b/src/aliby/io/utils.py deleted file mode 100644 index d59a2f65..00000000 --- a/src/aliby/io/utils.py +++ /dev/null @@ -1,50 +0,0 @@ -import re -import struct - - -def clean_ascii(text): - return re.sub(r"[^\x20-\x7F]", ".", text) - - -def xxd(x, start=0, stop=None): - if stop is None: - stop = len(x) - for i in range(start, stop, 8): - # Row number - print("%04d" % i, end=" ") - # Hexadecimal bytes - for r in range(i, i + 8): - print("%02x" % x[r], end="") - if (r + 1) % 4 == 0: - print(" ", end="") - # ASCII - print( - " ", - clean_ascii(x[i : i + 8].decode("utf-8", errors="ignore")), - " ", - end="", - ) - # Int32 - print( - "{:>10} {:>10}".format(*struct.unpack("II", x[i : i + 8])), - end=" ", - ) - print("") # Newline - return - - -# Buffer reading functions -def read_int(buffer, n=1): - res = struct.unpack("I" * n, buffer.read(4 * n)) - if n == 1: - res = res[0] - return res - - -def read_string(buffer): - return "".join([x.decode() for x in iter(lambda: buffer.read(1), b"\x00")]) - - -def read_delim(buffer, n): - delim = read_int(buffer, n) - assert all([x == 0 for x in delim]), "Unknown nonzero value in delimiter" diff --git a/src/aliby/pipeline.py b/src/aliby/pipeline.py index ea566746..04484584 100644 --- a/src/aliby/pipeline.py +++ b/src/aliby/pipeline.py @@ -522,15 +522,6 @@ class Pipeline(ProcessABC): finally: _close_session(session) - # try: - # compiler = ExperimentCompiler(None, filepath) - # tmp = compiler.run() - # po = PageOrganiser(tmp, grid_spec=(3, 2)) - # po.plot() - # po.save(fullpath / f"{directory}report.pdf") - # except Exception as e: - # print("Report failed: {}".format(e)) - @staticmethod def check_earlystop(filename: str, es_parameters: dict, tile_size: int): s = Signal(filename) @@ -685,29 +676,6 @@ class Pipeline(ProcessABC): except Exception: pass - # Delete datasets to overwrite and update pipeline data - # Use existing parameters - # with h5py.File(filename, "a") as f: - # pparams = PipelineParameters.from_yaml( - # f.attrs["parameters"] - # ).to_dict() - - # for k, v in ow.items(): - # if v: - # for gname in self.writer_groups[k]: - # if gname in f: - # del f[gname] - - # pparams[k] = config[k] - # meta.add_fields( - # { - # "parameters": PipelineParameters.from_dict( - # pparams - # ).to_yaml() - # }, - # overwrite=True, - # ) - meta.run() meta.add_fields( # Add non-logfile metadata { diff --git a/src/aliby/tile/tiler.py b/src/aliby/tile/tiler.py index 16f9b2f8..e0f4ff7d 100644 --- a/src/aliby/tile/tiler.py +++ b/src/aliby/tile/tiler.py @@ -281,8 +281,6 @@ class Tiler(ProcessABC): trap_locs = TrapLocations.read_hdf5(filepath) metadata = BridgeH5(filepath).meta_h5 metadata["channels"] = image.metadata["channels"] - # metadata["zsectioning/nsections"] = image.metadata["zsectioning/nsections"] - # metadata["channels/zsect"] = image.metadata["channels/zsect"] if parameters is None: parameters = TilerParameters.default() tiler = cls( diff --git a/src/aliby/tile/traps.py b/src/aliby/tile/traps.py index 8d1d776c..4eddeb7e 100644 --- a/src/aliby/tile/traps.py +++ b/src/aliby/tile/traps.py @@ -140,9 +140,6 @@ def segment_traps( return traps_retry -### - - def identify_trap_locations( image, trap_template, optimize_scale=True, downscale=0.35, trap_size=None ): @@ -240,103 +237,10 @@ def identify_trap_locations( def stretch_image(image): + # FIXME Used in aliby.utils.imageViewer image = ((image - image.min()) / (image.max() - image.min())) * 255 minval = np.percentile(image, 2) maxval = np.percentile(image, 98) image = np.clip(image, minval, maxval) image = (image - minval) / (maxval - minval) - return image - - -def get_tile_shapes(x, tile_size): - half_size = tile_size // 2 - xmin = int(x[0] - half_size) - ymin = max(0, int(x[1] - half_size)) - - return xmin, xmin + tile_size, ymin, ymin + tile_size - - -def in_image(img, xmin, xmax, ymin, ymax, xidx=2, yidx=3): - if xmin >= 0 and ymin >= 0: - if xmax < img.shape[xidx] and ymax < img.shape[yidx]: - return True - else: - return False - - -def get_xy_tile(img, xmin, xmax, ymin, ymax, xidx=2, yidx=3, pad_val=None): - if pad_val is None: - pad_val = np.median(img) - # Get the tile from the image - idx = [slice(None)] * len(img.shape) - idx[xidx] = slice(max(0, xmin), min(xmax, img.shape[xidx])) - idx[yidx] = slice(max(0, ymin), min(ymax, img.shape[yidx])) - tile = img[tuple(idx)] - # Check if the tile is in the image - if in_image(img, xmin, xmax, ymin, ymax, xidx, yidx): - return tile - else: - # Add padding - pad_shape = [(0, 0)] * len(img.shape) - pad_shape[xidx] = (max(-xmin, 0), max(xmax - img.shape[xidx], 0)) - pad_shape[yidx] = (max(-ymin, 0), max(ymax - img.shape[yidx], 0)) - tile = np.pad(tile, pad_shape, constant_values=pad_val) - return tile - - -def tile_where(centre, x, y, MAX_X, MAX_Y): - # Find the position of the tile - xmin = int(centre[1] - x // 2) - ymin = int(centre[0] - y // 2) - xmax = xmin + x - ymax = ymin + y - # What do we actually have available? - r_xmin = max(0, xmin) - r_xmax = min(MAX_X, xmax) - r_ymin = max(0, ymin) - r_ymax = min(MAX_Y, ymax) - return xmin, ymin, xmax, ymax, r_xmin, r_ymin, r_xmax, r_ymax - - -def get_tile(shape, center, raw_expt, ch, t, z): - """Returns a tile from the raw experiment with a given shape. - - :param shape: The shape of the tile in (C, T, Z, Y, X) order. - :param center: The x,y position of the centre of the tile - :param - """ - _, _, x, y, _ = shape - _, _, MAX_X, MAX_Y, _ = raw_expt.shape - tile = np.full(shape, np.nan) - - # Find the position of the tile - xmin = int(center[1] - x // 2) - ymin = int(center[0] - y // 2) - xmax = xmin + x - ymax = ymin + y - # What do we actually have available? - r_xmin = max(0, xmin) - r_xmax = min(MAX_X, xmax) - r_ymin = max(0, ymin) - r_ymax = min(MAX_Y, ymax) - - # Fill values - tile[ - :, - :, - (r_xmin - xmin) : (r_xmax - xmin), - (r_ymin - ymin) : (r_ymax - ymin), - :, - ] = raw_expt[ch, t, r_xmin:r_xmax, r_ymin:r_ymax, z] - # fill_val = np.nanmedian(tile) - # np.nan_to_num(tile, nan=fill_val, copy=False) - return tile - - -def centre(img, percentage=0.3): - y, x = img.shape - cropx = int(np.ceil(x * percentage)) - cropy = int(np.ceil(y * percentage)) - startx = int(x // 2 - (cropx // 2)) - starty = int(y // 2 - (cropy // 2)) - return img[starty : starty + cropy, startx : startx + cropx] + return image \ No newline at end of file diff --git a/src/aliby/utils/argo.py b/src/aliby/utils/argo.py index 24255065..b4d80c36 100644 --- a/src/aliby/utils/argo.py +++ b/src/aliby/utils/argo.py @@ -282,10 +282,6 @@ class OmeroExplorer: for k, v in self.cache.items() } - # @staticfunction - # def number_of_X(logfile: str): - # return re.findall("X", logfile) - def dset_count( self, dset: t.Union[int, _DatasetWrapper], @@ -351,17 +347,6 @@ class Argo(OmeroExplorer): super().__init__(*args, **kwargs) -def get_creds(): - return ( - "upload", - "***REMOVED***", # OMERO Password - ) - - -def list_files(dset): - return {x for x in dset.listAnnotations() if hasattr(x, "getFileName")} - - def annot_from_dset(dset, kind): v = [x for x in dset.listAnnotations() if hasattr(x, "getFileName")] infname = kind if kind == "log" else kind.title() @@ -374,7 +359,6 @@ def annot_from_dset(dset, kind): except Exception as e: print(f"Conversion from acquisition file failed: {e}") return {} - return acq @@ -393,6 +377,7 @@ def check_channels(acq, channels, _all=True): def get_chs(exptype): + # TODO Documentation exptypes = { "dual_ph": ("GFP", "pHluorin405", "mCherry"), "ph": ("GFP", "pHluorin405"), @@ -403,6 +388,7 @@ def get_chs(exptype): def load_annot_from_cache(exp_id, cache_dir="cache/"): + # TODO Documentation if type(cache_dir) is not PosixPath: cache_dir = Path(cache_dir) @@ -428,16 +414,6 @@ def parse_annot(str_io, fmt): return parser.parse(io.StringIO(str_io)) -def get_log_date(annot_sets): - log = get_annot(annot_sets, "log") - return log.get("date", None) - - -def get_log_microscope(annot_sets): - log = get_annot(annot_sets, "log") - return log.get("microscope", None) - - def get_annotsets(dset): annot_files = [ annot.getFile() @@ -457,12 +433,8 @@ def get_annotsets(dset): return annot_sets -# def has_tags(d, tags): -# if set(tags).intersection(annot_from_dset(d, "log").get("omero_tags", [])): -# return True - - def load_acq(dset): + # TODO Documentation try: acq = annot_from_dset(dset, kind="acq") return acq @@ -472,6 +444,7 @@ def load_acq(dset): def has_channels(dset, exptype): + # TODO Documentation acq = load_acq(dset) if acq: return check_channels(acq, get_chs(exptype)) @@ -479,26 +452,8 @@ def has_channels(dset, exptype): return -# Custom functions -def compare_dsets_voltages_exp(dsets): - a = {} - for d in dsets: - try: - acq = annot_from_dset(d, kind="acq")["channels"] - a[d.getId()] = { - k: (v, e) - for k, v, e in zip( - acq["channel"], acq["voltage"], acq["exposure"] - ) - } - - except Exception as e: - print(d, f"Data set voltage comparison did not work:{e}") - - return a - - def get_logfile(dset): + # TODO Documentation annot_file = [ annot.getFile() for annot in dset.listAnnotations() diff --git a/src/aliby/utils/cache.py b/src/aliby/utils/cache.py deleted file mode 100644 index a256c635..00000000 --- a/src/aliby/utils/cache.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -Utility functions and classes -""" -import itertools -import logging -import operator -from functools import partial, wraps -from pathlib import Path -from time import perf_counter -from typing import Callable - -import cv2 -import h5py -import imageio -import numpy as np - - -def repr_obj(obj, indent=0): - """ - Helper function to display info about OMERO objects. - Not all objects will have a "name" or owner field. - """ - string = """%s%s:%s Name:"%s" (owner=%s)""" % ( - " " * indent, - obj.OMERO_CLASS, - obj.getId(), - obj.getName(), - obj.getAnnotation(), - ) - - return string - - -def imread(path): - return cv2.imread(str(path), -1) - - -class ImageCache: - """HDF5-based image cache for faster loading of the images once they've - been read. - """ - - def __init__(self, file, name, shape, remote_fn): - self.store = h5py.File(file, "a") - # Create a dataset - self.dataset = self.store.create_dataset( - name, shape, dtype=np.float, fill_value=np.nan - ) - self.remote_fn = remote_fn - - def __getitem__(self, item): - cached = self.dataset[item] - if np.any(np.isnan(cached)): - full = self.remote_fn(item) - self.dataset[item] = full - return full - else: - return cached - - -class Cache: - """ - Fixed-length mapping to use as a cache. - Deletes items in FIFO manner when maximum allowed length is reached. - """ - - def __init__(self, max_len=5000, load_fn: Callable = imread): - """ - :param max_len: Maximum number of items in the cache. - :param load_fn: The function used to load new items if they are not - available in the Cache - """ - self._dict = dict() - self._queue = [] - self.load_fn = load_fn - self.max_len = max_len - - def __getitem__(self, item): - if item not in self._dict: - self.load_item(item) - return self._dict[item] - - def load_item(self, item): - self._dict[item] = self.load_fn(item) - # Clean up the queue - self._queue.append(item) - if len(self._queue) > self.max_len: - del self._dict[self._queue.pop(0)] - - def clear(self): - self._dict.clear() - self._queue.clear() - - -def accumulate(lst: list): - lst = sorted(lst) - it = itertools.groupby(lst, operator.itemgetter(0)) - for key, sub_iter in it: - yield key, [x[1] for x in sub_iter] - - -def get_store_path(save_dir, store, name): - """Create a path to a position-specific store. - - This combines the name and the store's base name into a file path within save_dir. - For example. - >>> get_store_path('data', 'baby_seg.h5', 'pos001') - Path(data/pos001baby_seg.h5') - - :param save_dir: The root directory in which to save the file, absolute - path. - :param store: The base name of the store - :param name: The name of the position - :return: Path(save_dir) / name+store - """ - store = Path(save_dir) / store - store = store.with_name(name + store.name) - return store - - -def parametrized(dec): - def layer(*args, **kwargs): - def repl(f): - return dec(f, *args, **kwargs) - - return repl - - return layer - - -@parametrized -def timed(f, name=None): - @wraps(f) - def decorated(*args, **kwargs): - t = perf_counter() - res = f(*args, **kwargs) - to_print = name or f.__name__ - logging.debug(f"Timing:{to_print}:{perf_counter() - t}s") - return res - - return decorated diff --git a/src/extraction/core/extractor.py b/src/extraction/core/extractor.py index 6a5af6bc..a44d6ea6 100644 --- a/src/extraction/core/extractor.py +++ b/src/extraction/core/extractor.py @@ -451,6 +451,7 @@ class Extractor(ProcessABC): An example is d["GFP"]["np_max"]["mean"][0], which gives a tuple of the calculated mean GFP fluorescence for all cells. """ + # TODO Can we split the different extraction types into sub-methods to make this easier to read? if tree is None: # use default tree: extraction_tree = self.params.tree diff --git a/src/extraction/core/functions/io.py b/src/extraction/core/functions/io.py deleted file mode 100644 index 3b377bdf..00000000 --- a/src/extraction/core/functions/io.py +++ /dev/null @@ -1,12 +0,0 @@ -from yaml import dump, load - - -def dict_to_yaml(d, f): - with open(f, "w") as f: - dump(d, f) - - -def add_attrs(hdfile, path, files): - group = hdfile.create_group(path) - for k, v in files: - group.attrs[k] = v diff --git a/src/extraction/core/functions/utils.py b/src/extraction/core/functions/utils.py deleted file mode 100644 index dcae68c9..00000000 --- a/src/extraction/core/functions/utils.py +++ /dev/null @@ -1,19 +0,0 @@ -from collections import deque - - -def depth(d): - """ - Copied from https://stackoverflow.com/a/23499088 - - Used to determine the depth of our config trees and fill them - """ - queue = deque([(id(d), d, 1)]) - memo = set() - while queue: - id_, o, level = queue.popleft() - if id_ in memo: - continue - memo.add(id_) - if isinstance(o, dict): - queue += ((id(v), v, level + 1) for v in o.values()) - return level diff --git a/src/extraction/core/functions/versioning.py b/src/extraction/core/functions/versioning.py index 40c1e8f9..4c77a9f8 100644 --- a/src/extraction/core/functions/versioning.py +++ b/src/extraction/core/functions/versioning.py @@ -2,6 +2,7 @@ import git def get_sha(): + # FIXME Unused, but *should* be used... repo = git.Repo(search_parent_directories=True) sha = repo.head.object.hexsha return sha diff --git a/src/extraction/core/omero.py b/src/extraction/core/omero.py deleted file mode 100644 index e69aa747..00000000 --- a/src/extraction/core/omero.py +++ /dev/null @@ -1,35 +0,0 @@ -from omero.gateway import BlitzGateway -from tqdm import tqdm - - -# Helper funs -def connect_omero(): - conn = BlitzGateway(*get_creds(), host="islay.bio.ed.ac.uk", port=4064) - conn.connect() - return conn - - -def get_creds(): - return ( - "upload", - "***REMOVED***", # OMERO Password - ) - - -def download_file(f): - """ - Download file in chunks using FileWrapper object - """ - desc = ( - "Downloading " - + f.getFileName() - + " (" - + str(round(f.getFileSize() / 1000**2, 2)) - + "Mb)" - ) - - down_file = bytearray() - for c in tqdm(f.getFileInChunks(), desc=desc): - down_file += c - - return down_file diff --git a/src/logfile_parser/swainlab_parser.py b/src/logfile_parser/swainlab_parser.py index a83cb87c..c5f200da 100644 --- a/src/logfile_parser/swainlab_parser.py +++ b/src/logfile_parser/swainlab_parser.py @@ -1,5 +1,5 @@ #!/usr/bin/env jupyter - +# TODO should this be merged to the regular logfile_parser structure? """ Description of new logfile: @@ -298,18 +298,3 @@ def parse_x(string: str, type: str, **kwargs): def parse_from_swainlab_grammar(filepath: t.Union[str, PosixPath]): return parse_from_grammar(filepath, grammar) - - -# test_file = "/home/alan/Documents/dev/skeletons/scripts/dev/C1_60x.log" -# test_file = "/home/alan/Documents/dev/skeletons/scripts/dev/bak" -# test_file = "/home/alan/Documents/dev/skeletons/scripts/dev/two_tables.log" -# test_file = "/home/alan/Downloads/pH_med_to_low 1.log" -# test_file = "/home/alan/Documents/dev/skeletons/scripts/data/577_2022_12_20_pHCalibrate6_7_00/pHCalibrate6_7.log" - - -# d = parse_from_grammar(test_file, grammar) -# print(d) - -# from logfile_parser.legacy import get_legacy_log_example_interface - -# original = get_legacy_log_example_interface() diff --git a/src/postprocessor/benchmarks/post_processing.py b/src/postprocessor/benchmarks/post_processing.py index d17651a8..d36e2afb 100644 --- a/src/postprocessor/benchmarks/post_processing.py +++ b/src/postprocessor/benchmarks/post_processing.py @@ -1,3 +1,4 @@ +# TODO remove/to snippets? """ Post-processing utilities diff --git a/src/postprocessor/core/abc.py b/src/postprocessor/core/abc.py index a299e19f..ffb677b4 100644 --- a/src/postprocessor/core/abc.py +++ b/src/postprocessor/core/abc.py @@ -16,6 +16,7 @@ class PostProcessABC(ProcessABC): @classmethod def as_function(cls, data, *extra_data, **kwargs): + # FIXME can this be a __call__ method instead? # Find the parameter's default parameters = cls.default_parameters(**kwargs) return cls(parameters=parameters).run(data, *extra_data) diff --git a/src/postprocessor/core/group.py b/src/postprocessor/core/group.py deleted file mode 100644 index 5613e5de..00000000 --- a/src/postprocessor/core/group.py +++ /dev/null @@ -1,137 +0,0 @@ -""" -Class to group multiple positions into one using one different available criteria. -""" - -import re -from pathlib import Path - -import h5py -import pandas as pd -from agora.io.bridge import groupsort -from agora.io.signal import Signal - -from postprocessor.core.abc import ParametersABC, ProcessABC - - -class GroupParameters(ParametersABC): - def __init__(self, by="name", processes=[], signals=[]): - self.by = by - self.signals = signals - self.processes = processes - - @classmethod - def default(cls): - return cls.from_dict({"by": "name", "signals": [], "processes": []}) - - -class Group(ProcessABC): - def __init__(self, parameters): - super().__init__(parameters) - - def get_position_filenames(self, exp_root, poses): - """ - Get filenames as a dictionary where the key is the position and value the filename. - """ - central_store = Path(exp_root) / "store.h5" - if central_store.exists(): - hdf = h5py.File(central_store, "r") - self.filenames = [ - pos.attrs["filename"] for pos in hdf["/positions/"] - ] - hdf.close() - else: # If no central store just list position files in expt root folder - fullfiles = [x for x in Path(exp_root).glob("*store.h5")] - files = [x.name for x in Path(exp_root).glob("*store.h5")] - filenames = [False for _ in poses] - for i, pos in enumerate(poses): - matches = [ - True if re.match(pos + ".*.h5", fname) else False - for fname in files - ] - if any(matches): - assert sum(matches) == 1, "More than one match" - filenames[i] = (pos, fullfiles[matches.index(True)]) - - self.filenames = { - fname[0]: fname[1] for fname in filenames if fname - } - - self.positions = list(self.filenames.keys()) - return self.filenames - - def get_signals(self): - # hdf = h5py.File(central_store, "r") - # keys_d = groupsort(keys) - self.signals = {pos: {} for pos in self.filenames.keys()} - for pos, fname in self.filenames.items(): - for signal in self.parameters.signals: - self.signals[pos][signal] = pd.read_hdf(fname, signal) - - return self.signals - - def gen_groups(self): - if self.by == "group": # Use group names in metadata - pass - elif self.by == "name": # Infer groups from signal concatenation - # Remove last four characters and find commonalities larger than 4 - # characters between posnames and group them that way. - groupnames = list(set([x[:-3] for x in self.positions])) - self.group_signal_tree = {group: [] for group in groupnames} - self.poses_grouped = {group: [] for group in groupnames} - for pos in self.positions: - group = groupnames[groupnames.index(pos[:-3])] - self.group_signal_tree[group].append(self.signals[pos]) - self.poses_grouped[group].append(pos) - - elif ( - type(self.by) == tuple - ): # Manually give groups as tuple or list of positions - pass - - def concat_signals(self): - self.concated_signals = {group: {} for group in self.group_signal_tree} - for k, group in self.group_signal_tree.items(): - for signal in self.parameters.signals: - self.concated_signals[k][signal] = pd.concat( - [g[signal] for g in group], keys=self.poses_grouped[k] - ) - - return self.concated_signals - - def process_signals(self, grouped_signals): - pass - - def run(self, central_store, poses): - - self.get_position_filenames(central_store, poses) - self.get_signals() - self.gen_groups() - self.concat_signals() - # processed_signals = self.process_signals(grouped_signals) - - return self.concated_signals - # return processed_signals - - -poses = [ - x.name.split("store")[0] - for x in Path( - "/shared_libs/pipeline-core/scripts/data/ph_calibration_dual_phl_ura8_5_04_5_83_7_69_7_13_6_59__01" - ).rglob("*") - if x.name != "images.h5" -] -gr = Group( - GroupParameters( - signals=[ - "/extraction/general/None/area", - "/extraction/mCherry/np_max/median", - ] - ) -) -gr.run( - central_store="/shared_libs/pipeline-core/scripts/data/ph_calibration_dual_phl_ura8_5_04_5_83_7_69_7_13_6_59__01", - poses=poses, -) -signal = Signal( - "/shared_libs/pipeline-core/scripts/data/ph_calibration_dual_phl_ura8_5_04_5_83_7_69_7_13_6_59__01/ph_5_04_001store.h5" -) diff --git a/src/postprocessor/core/lineageprocess.py b/src/postprocessor/core/lineageprocess.py index 34ccf234..f10d5b3e 100644 --- a/src/postprocessor/core/lineageprocess.py +++ b/src/postprocessor/core/lineageprocess.py @@ -1,3 +1,4 @@ +# TODO Module docstring import typing as t from abc import abstractmethod @@ -7,8 +8,6 @@ import pandas as pd from agora.abc import ParametersABC from postprocessor.core.abc import PostProcessABC -# from agora.utils.lineage import group_matrix - class LineageProcessParameters(ParametersABC): """ @@ -47,18 +46,14 @@ class LineageProcess(PostProcessABC): Overrides PostProcess.as_function classmethod. Lineage functions require lineage information to be passed if run as function. """ - # if isinstance(lineage, np.ndarray): - # lineage = group_matrix(lineage, n_keys=2) - parameters = cls.default_parameters(**kwargs) return cls(parameters=parameters).run( data, lineage=lineage, *extra_data ) - # super().as_function(data, *extra_data, lineage=lineage, **kwargs) def load_lineage(self, lineage): """ Reshape the lineage information if needed """ - + # TODO does this need to be a function? self.lineage = lineage diff --git a/src/postprocessor/core/processor.py b/src/postprocessor/core/processor.py index 37efd0d4..4b9161c0 100644 --- a/src/postprocessor/core/processor.py +++ b/src/postprocessor/core/processor.py @@ -56,26 +56,16 @@ class PostProcessorParameters(ParametersABC): "/extraction/general/None/volume", ], ], - # [ - # "savgol", - # [ - # "/extraction/general/None/volume", - # ], - # ], [ "dsignal", [ "/extraction/general/None/volume", - # "/postprocessing/savgol/extraction_general_None_volume", ], ], [ "bud_metric", [ "/extraction/general/None/volume", - # "/postprocessing/dsignal/postprocessing_savgol_extraction_general_None_volume", - # "/postprocessing/savgol/extraction_general_None_volume", - # "/postprocessing/dsignal/extraction_general_None_volume", ], ], [ @@ -84,15 +74,6 @@ class PostProcessorParameters(ParametersABC): "/postprocessing/bud_metric/extraction_general_None_volume", ], ], - # [ - # "aggregate", - # [ - # [ - # "/extraction/general/None/volume", - # "postprocessing/dsignal/extraction_general_None_volume", - # ], - # ], - # ], ], } param_sets = { @@ -105,22 +86,12 @@ class PostProcessorParameters(ParametersABC): outpaths["aggregate"] = "/postprocessing/experiment_wide/aggregated/" if "ph_batman" in kind: - # targets["processes"]["bud_metric"].append( - # [ - # [ - # "/extraction/em_ratio/np_max/mean", - # "/extraction/em_ratio/np_max/median", - # ], - # ] - # ) targets["processes"]["dsignal"].append( [ "/extraction/em_ratio/np_max/mean", "/extraction/em_ratio/np_max/median", "/extraction/em_ratio_bgsub/np_max/mean", "/extraction/em_ratio_bgsub/np_max/median", - # "/postprocessing/bud_metric/extraction_em_ratio_np_max_mean", - # "/postprocessing/bud_metric/extraction_em_ratio_np_max_median", ] ) targets["processes"]["aggregate"].append( @@ -132,10 +103,6 @@ class PostProcessorParameters(ParametersABC): "/extraction/em_ratio_bgsub/np_max/median", "/extraction/gsum/np_max/median", "/extraction/gsum/np_max/mean", - # "postprocessing/bud_metric/extraction_em_ratio_np_max_mean", - # "postprocessing/bud_metric/extraction_em_ratio_np_max_median", - # "postprocessing/dsignal/postprocessing_bud_metric_extraction_em_ratio_np_max_median", - # "postprocessing/dsignal/postprocessing_bud_metric_extraction_em_ratio_np_max_mean", ] ], ) @@ -178,6 +145,7 @@ class PostProcessor(ProcessABC): self.targets = parameters["targets"] def run_prepost(self): + # TODO Split function """Important processes run before normal post-processing ones""" merge_events = self.merger.run( @@ -301,12 +269,7 @@ class PostProcessor(ProcessABC): return x def run(self): - # import cProfile - # import pstats - - # profile = cProfile.Profile() - # profile.enable() - + # TODO Documentation :) + Split self.run_prepost() for process, datasets in tqdm(self.targets["processes"]): diff --git a/src/postprocessor/core/reshapers/bud_metric.py b/src/postprocessor/core/reshapers/bud_metric.py index 0893db66..c527134d 100644 --- a/src/postprocessor/core/reshapers/bud_metric.py +++ b/src/postprocessor/core/reshapers/bud_metric.py @@ -33,7 +33,6 @@ class bud_metric(LineageProcess): mother_bud_ids: Dict[pd.Index, Tuple[pd.Index]] = None, ): if mother_bud_ids is None: - # filtered_lineage = self.filter_signal_cells(signal) mother_bud_ids = mb_array_to_dict(self.lineage) return self.get_bud_metric(signal, mother_bud_ids) diff --git a/src/postprocessor/core/reshapers/buddings.py b/src/postprocessor/core/reshapers/buddings.py index 0b01dad7..4b3fbba9 100644 --- a/src/postprocessor/core/reshapers/buddings.py +++ b/src/postprocessor/core/reshapers/buddings.py @@ -25,16 +25,17 @@ class buddingsParameters(LineageProcessParameters): FIXME: Add docs. """ - _defaults = {"lineage_location": "postprocessing/lineage_merged"} +# TODO Why not capitalized? class buddings(LineageProcess): """ Calculate buddings in a trap assuming one mother per trap returns a pandas series with the buddings """ + # TODO might want to define "buddings" more scientifically def __init__(self, parameters: buddingsParameters): super().__init__(parameters) diff --git a/src/postprocessor/core/reshapers/merger.py b/src/postprocessor/core/reshapers/merger.py index 16d2b598..1fbf4155 100644 --- a/src/postprocessor/core/reshapers/merger.py +++ b/src/postprocessor/core/reshapers/merger.py @@ -3,7 +3,7 @@ from agora.abc import ParametersABC from postprocessor.core.abc import PostProcessABC from postprocessor.core.functions.tracks import get_joinable - +# TODO Why not capitalized? class mergerParameters(ParametersABC): """ :param tol: float or int threshold of average (prediction error/std) necessary @@ -22,6 +22,7 @@ class mergerParameters(ParametersABC): } +# TODO Why not capitalized? class merger(PostProcessABC): """ Combines rows of tracklet that are likely to be the same. @@ -34,8 +35,4 @@ class merger(PostProcessABC): joinable = [] if signal.shape[1] > 4: joinable = get_joinable(signal, tol=self.parameters.tolerance) - # merged, _ = merge_tracks(signal) # , min_len=self.window + 1) - # indices = (*zip(*merged.index.tolist()),) - # names = merged.index.names - # return {name: ids for name, ids in zip(names, indices)} return joinable diff --git a/src/postprocessor/core/reshapers/picker.py b/src/postprocessor/core/reshapers/picker.py index bfabc76a..b198b7fd 100644 --- a/src/postprocessor/core/reshapers/picker.py +++ b/src/postprocessor/core/reshapers/picker.py @@ -1,34 +1,20 @@ -# from abc import ABC, abstractmethod - -# from copy import copy -# from itertools import groupby -# from typing import List, Tuple, Union import typing as t -from typing import Union -# import igraph as ig import numpy as np import pandas as pd from agora.abc import ParametersABC from agora.io.cells import Cells -# from postprocessor.core.functions.tracks import max_nonstop_ntps, max_ntps from agora.utils.association import validate_association from postprocessor.core.lineageprocess import LineageProcess -# from utils_find_1st import cmp_equal, find_1st - class pickerParameters(ParametersABC): _defaults = { "sequence": [ ["lineage", "intersection", "families"], - # ["condition", "intersection", "any_present", 0.7], - # ["condition", "intersection", "growing", 80], ["condition", "intersection", "present", 7], - # ["condition", "intersection", "mb_guess", 3, 0.7], - # ("lineage", "intersection", "full_families"), ], } @@ -80,16 +66,8 @@ class picker(LineageProcess): idx = idx[valid_indices] mothers_daughters = mothers_daughters[valid_lineage] - - # return mothers_daughters, idx return idx - def loc_lineage(self, kymo: pd.DataFrame, how: str, lineage=None): - _, valid_indices = self.pick_by_lineage( - kymo, how, mothers_daughters=lineage - ) - return kymo.loc[[tuple(x) for x in valid_indices]] - def pick_by_condition(self, signals, condition, thresh): idx = self.switch_case(signals, condition, thresh) return idx @@ -131,7 +109,7 @@ class picker(LineageProcess): self, signals: pd.DataFrame, condition: str, - threshold: Union[float, int, list], + threshold: t.Union[float, int, list], ): if len(threshold) == 1: threshold = [_as_int(*threshold, signals.shape[1])] @@ -145,7 +123,7 @@ class picker(LineageProcess): return set(signals.index[case_mgr[condition](signals, *threshold)]) -def _as_int(threshold: Union[float, int], ntps: int): +def _as_int(threshold: t.Union[float, int], ntps: int): if type(threshold) is float: threshold = ntps * threshold return threshold diff --git a/src/postprocessor/grouper.py b/src/postprocessor/grouper.py index 167c0b5e..ec04a6f5 100644 --- a/src/postprocessor/grouper.py +++ b/src/postprocessor/grouper.py @@ -15,11 +15,6 @@ import pandas as pd import seaborn as sns from pathos.multiprocessing import Pool -from agora.utils.kymograph import ( - drop_level, - get_mother_ilocs_from_daughters, - intersection_matrix, -) from postprocessor.chainer import Chainer diff --git a/tests/agora/example_test.py b/tests/agora/example_test.py deleted file mode 100644 index 539b410a..00000000 --- a/tests/agora/example_test.py +++ /dev/null @@ -1,25 +0,0 @@ -"""This is an example test file to show the structure.""" -import pytest - -from agora.utils.example import ExampleClass, example_function - - -class TestExampleClass: - x = ExampleClass(1) - - def test_add_one(self): - assert self.x.add_one() == 2 - - def test_add_n(self): - assert self.x.add_n(10) == 11 - - -def test_example_function(): - x = example_function(1) - assert isinstance(x, ExampleClass) - assert x.parameter == 1 - - -def test_example_function_fail(): - with pytest.raises(ValueError): - example_function("hello") diff --git a/tests/aliby/network/test_baby_client.py b/tests/aliby/network/test_baby_client.py deleted file mode 100644 index a8d131a0..00000000 --- a/tests/aliby/network/test_baby_client.py +++ /dev/null @@ -1,91 +0,0 @@ -import pytest - -pytest.mark.skip - -import json -import time - -import numpy as np - -# from aliby.experiment import ExperimentLocal -from aliby.baby_client import BabyClient -from aliby.tile.tiler import Tiler - - -@pytest.mark.skip( - reason="No longer usable, requires local files. Kept until replaced." -) -def test_client(): - root_dir = ( - "/Users/s1893247/PhD/pipeline-core/data/glclvl_0" - ".1_mig1_msn2_maf1_sfp1_dot6_03" - ) - - expt = ExperimentLocal(root_dir, finished=True) - seg_expt = Tiler(expt, finished=True) - - print(seg_expt.positions) - seg_expt.current_position = "pos007" - - config = { - "camera": "evolve", - "channel": "brightfield", - "zoom": "60x", - "n_stacks": "5z", - } - - baby_client = BabyClient(expt, **config) - - print("The session is {}".format(baby_client.sessions["default"])) - - # Channel 0, 0, X,Y,Z all - num_timepoints = 5 - - traps_tps = [ - seg_expt.get_tiles_timepoint( - tp, tile_size=81, channels=[0], z=[0, 1, 2, 3, 4] - ).squeeze() - for tp in range(num_timepoints) - ] - - segmentations = [] - try: - for i, timpoint in enumerate(traps_tps): - print("Sending timepoint {};".format(i)) - status = baby_client.queue_image( - timpoint, - baby_client.sessions["default"], - assign_mothers=True, - return_baprobs=True, - with_edgemasks=True, - ) - while True: - try: - print("Loading.", end="") - result = baby_client.get_segmentation( - baby_client.sessions["default"] - ) - except: - print(".", end="") - time.sleep(1) - continue - break - print("Received timepoint {}".format(i)) - segmentations.append(result) - except Exception as e: - print(segmentations) - raise e - - with open("segmentations.json", "w") as fd: - json.dump(segmentations, fd) - - print("Done.") - # print(len(segmentations[0])) - # for i in range(5): - # print("trap {}".format(i)) - # for k, v in segmentations[0][i].items(): - # print(k, v) - # - # import matplotlib.pyplot as plt - # plt.imshow(np.squeeze(batches[0][0, ..., 0])) - # plt.savefig('test_baby.pdf') diff --git a/tests/aliby/network/test_post_processing.py b/tests/aliby/network/test_post_processing.py index 3ac8ba40..dbf9b8a9 100644 --- a/tests/aliby/network/test_post_processing.py +++ b/tests/aliby/network/test_post_processing.py @@ -7,14 +7,6 @@ import skimage.morphology as morph from scipy import ndimage from skimage import draw -# from aliby.post_processing import ( -# circle_outline, -# conical, -# ellipse_perimeter, -# union_of_spheres, -# volume_of_sphere, -# ) - @pytest.mark.skip( reason="No longer usable, post_processing unused inside aliby. Kept temporarily" -- GitLab