diff --git a/.gitignore b/.gitignore index 08aaa8ee46cef6362c9395208a3ab9ba1f2b3f48..a49dc7eddd6144f2d4a44bbd6c0aa6323186679d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ .idea/* # build and conda-build artifacts -build/* -conda-bld/* +*build/ +*conda-bld/ dist/* -*.egg-info/* \ No newline at end of file +*.egg-info \ No newline at end of file diff --git a/README.md b/README.md index 86b9852fabe6af13b049125948a373c499a82c60..ee124fd541e5e6c00e82cea924754a35af33006a 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,15 @@ and exposes an extensible API to facilitate low-latency analysis. 1. Install Miniforge for environment management:<br>https://github.com/conda-forge/miniforge/releases 2. Under the Start menu, open `Miniforge3 > Miniforge Prompt` -## Option 2: install SVLT from source: +## Install SVLT from source: 1. Install Git:<br>https://git-scm.com/download/win -2. In the new terminal, clone the model_server repository:<br>`cd %userprofile%`<br>`git clone https://git.embl.de/grp-almf/svlt.git` -3. Create the target environment: `mamba env create --file requirements.yml --name svlt_env` -4. Activate the target environment: `mamba activate svlt_env` -5. Add the project source as a Python package: `pip install --no-deps -e .` +2. In the new terminal, clone the model_server repository:<br>`cd %userprofile%`<br>`git clone git@git.embl.de:grp-almf/svlt.git` +3. Requirements depend on which SVLT packages to include. To create the target environment for svlt-pheno:<br> +`mamba env create --file svlt-pheno/requirements.yml --name svlt-pheno-env` +4. Activate the target environment: `mamba activate svlt-pheno-env` +5. Add the same packages from source in editable mode: `pip install --no-deps -e ./svlt-core ./svlt-pheno` ## To start the server: -1. From the Miniforge prompt, run `mamba activate svlt_env` +1. From the Miniforge prompt, run `mamba activate svlt-pheno-env` 2. Then run `python -m scripts.run_server --port 6221` 3. A browser window should appear, with basic status information. diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml deleted file mode 100644 index 7a71c05a02dabd7701bd4480e5ace864e87dc657..0000000000000000000000000000000000000000 --- a/conda-recipe/meta.yaml +++ /dev/null @@ -1,49 +0,0 @@ -{% set name = "model_server" %} -{% set version = "2024.7.26" %} -{% set pyproject = load_file_data('../pyproject.toml', from_recipe_dir=True) %} -{% set pp = pyproject.get('project') %} - -debug: - {{ pyproject|pprint }} - -package: - name: {{ pp.get('name') }} - version: {{ pp.get('version') }} - -source: - - path: .. - - path: ../tests - folder: tests - -build: - noarch: python - script: {{ PYTHON }} -m pip install -vv --no-deps . - number: 0 - -requirements: - host: - - python >=3.9 - - setuptools >=61.0 - - pip - run: - - python >=3.9 - {% for dep in pp.get('dependencies') %} - - {{ dep.lower() }} - {% endfor %} - - pip - - -test: - imports: - - model_server - commands: - - python -m unittest discover - requires: - - pip - source_files: - - tests - -about: - summary: Service for analyzing microscope images - license: 'MIT' - license_file: ../LICENSE \ No newline at end of file diff --git a/conda-recipe/publish.py b/conda-recipe/publish.py deleted file mode 100644 index 071d9a5f52e6950848653e4d78e010eea65993b4..0000000000000000000000000000000000000000 --- a/conda-recipe/publish.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -Automate registration of conda build artifacts to EMBL GitLab; -assumes API access token is recorded in ~/.pypirc shell configuration file -""" -from configparser import ConfigParser -import json -from pathlib import Path -import requests - -id = '5668' -proj = 'model_server' -root = Path('../conda-bld/') - -# get authentication info from local config file -cfg = ConfigParser() -cfg.read(Path.home() / '.pypirc') -user = cfg['gitlab-model-server']['username'] -pwd = cfg['gitlab-model-server']['password'] - -with open(root / 'channeldata.json', 'r') as fh: - chdata = json.load(fh) - -# upload to GitLab API -res = {} -for sd in ['noarch', 'win-64']: - with open(root / sd / 'repodata.json', 'r') as fh: - dd = json.load(fh) - pkgname = f'conda_{sd}' - - if len(dd['packages']) == 0: - continue - - # put each .tar.bz2 - for fn in dd['packages'].keys(): - ver = dd['packages'][fn]['version'] - stem = fn.split('.tar.bz2')[0] - res[(sd, fn)] = requests.put( - f'https://git.embl.de/api/v4/projects/{id}/packages/generic/{pkgname}/{ver}/{fn}?status=default', - files={'file': open(root / sd / fn, 'rb')}, - headers={'PRIVATE-TOKEN': pwd}, - ) - - # put requirements.yml - fn = 'requirements.yml' - res[(sd, fn)] = requests.put( - f'https://git.embl.de/api/v4/projects/{id}/packages/generic/{pkgname}/{ver}/{fn}?status=default', - files={'file': open(root.parent / fn, 'r')}, - headers={'PRIVATE-TOKEN': pwd, 'Content-Type': 'text/html'}, - ) -print('Finished') -print(res) \ No newline at end of file diff --git a/scripts/run_server.py b/scripts/run_server.py deleted file mode 100644 index 3710c86eaec383a4bd8816a4a8f81992db01914f..0000000000000000000000000000000000000000 --- a/scripts/run_server.py +++ /dev/null @@ -1,49 +0,0 @@ -import argparse - -from model_server.conf.defaults import root, server_conf -from model_server.conf.startup import main - -def parse_args(): - parser = argparse.ArgumentParser( - description='Start model server with optional arguments', - ) - parser.add_argument( - '--confpath', - default='model_server.conf.fastapi', - help='path to server startup configuration', - ) - parser.add_argument( - '--host', - default=server_conf['host'], - help='bind socket to this host' - ) - parser.add_argument( - '--port', - default=str(server_conf['port']), - help='bind socket to this port', - ) - parser.add_argument( - '--root', - default=root.__str__(), - help='root directory of session data' - ) - parser.add_argument( - '--debug', - action='store_true', - help='display extra information that is helpful for debugging' - ) - parser.add_argument( - '--reload', - action='store_true', - help='automatically restart server when changes are noticed, for development purposes' - ) - - return parser.parse_args() - - -if __name__ == '__main__': - args = parse_args() - print('CLI args:\n' + str(args)) - main(**args.__dict__) - print('Finished') - diff --git a/svlt-core/pyproject.toml b/svlt-core/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..d9a17fab6e62b1d623f76a15933ecb4b3293609c --- /dev/null +++ b/svlt-core/pyproject.toml @@ -0,0 +1,34 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + + +[project] +name = "svlt-core" +license = {file = "LICENSE"} +version = "2025.02.01" +authors = [ + { name="Christopher Rhodes", email="christopher.rhodes@embl.de" }, +] +description = "Service for analyzing microscope images" +readme = "README.md" +requires-python = ">=3.9" +dependencies = [ + "czifile", + "fastapi >=0.101", + "imagecodecs", + "matplotlib", + "numpy", + "pandas", + "pillow", + "pydantic ~=1.10.1", + "scikit-image >=0.21.0", + "scikit-learn >=1.5.0", + "tifffile", + "uvicorn >=0.23.0", + "zstd", +] + +[project.urls] +Homepage = "https://git.embl.de/rhodes/model_server" +Issues = "https://git.embl.de/rhodes/model_server/-/issues" \ No newline at end of file diff --git a/svlt-core/requirements.yml b/svlt-core/requirements.yml new file mode 100644 index 0000000000000000000000000000000000000000..6d77a684b2e22a2e6557cdf5942822221453002a --- /dev/null +++ b/svlt-core/requirements.yml @@ -0,0 +1,20 @@ +name: svlt-core +channels: + - conda-forge +dependencies: + - czifile + - fastapi>=0.101 + - imagecodecs + - matplotlib + - nd2 + - numpy + - pandas + - pillow + - pip + - pydantic=1.10.* + - python=3.9.* + - pytorch=1.* + - scikit-image>=0.21.0 + - tifffile + - uvicorn>=0.23.0 + - zstd diff --git a/svlt-core/scripts/run_server.py b/svlt-core/scripts/run_server.py new file mode 100644 index 0000000000000000000000000000000000000000..57e7785d0d67b81512a0dea0296376bd93197eca --- /dev/null +++ b/svlt-core/scripts/run_server.py @@ -0,0 +1,8 @@ +from svlt.conf.startup import main, parse_args + +if __name__ == '__main__': + args = parse_args() + print('CLI args:\n' + str(args)) + main(confpath='svlt.api', **args.__dict__) + print('Finished') + diff --git a/svlt/core/accessors.py b/svlt-core/src/svlt/accessors.py similarity index 99% rename from svlt/core/accessors.py rename to svlt-core/src/svlt/accessors.py index 2129820310ef7f19c02326ec45b593f00ff8a863..8f7f31152101422e87dfe4fd2b0f4494f049a329 100644 --- a/svlt/core/accessors.py +++ b/svlt-core/src/svlt/accessors.py @@ -19,7 +19,7 @@ class GenericImageDataAccessor(ABC): @abstractmethod def __init__(self): """ - Abstract base class that exposes an interfaces for image data, irrespective of whether it is instantiated + Abstract svlt-core class that exposes an interfaces for image data, irrespective of whether it is instantiated from file I/O or other means. Enforces X, Y, C, Z dimensions in that order. """ self.lazy = False diff --git a/svlt/core/annotators.py b/svlt-core/src/svlt/annotators.py similarity index 100% rename from svlt/core/annotators.py rename to svlt-core/src/svlt/annotators.py diff --git a/svlt/core/api.py b/svlt-core/src/svlt/api.py similarity index 90% rename from svlt/core/api.py rename to svlt-core/src/svlt/api.py index 9acbf5be5427a8555aee0a92bc792b50904f9d35..f257069f5ffb8ede27d8262a6d4ef98180f5a6b0 100644 --- a/svlt/core/api.py +++ b/svlt-core/src/svlt/api.py @@ -7,13 +7,12 @@ from pydantic import BaseModel, Field from .accessors import generate_file_accessor, generate_multiposition_file_accessors from .models import BinaryThresholdSegmentationModel -from ..rois.models import IntensityThresholdInstanceMaskSegmentationModel -from .pipelines.shared import PipelineRecord +from .pipelines.common import PipelineRecord from .session import session, AccessorIdError, InvalidPathError, WriteAccessorError app = FastAPI(debug=True) -from .pipelines.router import router +from svlt.pipelines.router import router app.include_router(router) @@ -72,7 +71,7 @@ def _change_path(key, path, touch=False) -> Union[str, None]: session.set_data_directory(key, path) if touch: uid = str(uuid.uuid4()) - with open(Path(path) / 'svlt.touch', 'w') as fh: + with open(Path(path) / '.touch', 'w') as fh: fh.write(uid) return uid except InvalidPathError as e: @@ -131,13 +130,6 @@ def load_binary_threshold_model(p: BinaryThresholdSegmentationParams, model_id=N return {'model_id': result} -@app.put('/models/classify/threshold/load') -def load_intensity_threshold_instance_segmentation_model(p: BinaryThresholdSegmentationParams, model_id=None) -> dict: - result = session.load_model(IntensityThresholdInstanceMaskSegmentationModel, key=model_id, params=p) - session.log_info(f'Loaded permissive instance segmentation model {result}') - return {'model_id': result} - - @app.get('/accessors') def list_accessors() -> Dict: return session.list_accessors() @@ -257,11 +249,4 @@ def get_output_accessor_id_for_task(task_id: str) -> str: @app.get('/tasks') def list_tasks() -> Dict[str, TaskInfo]: - return session.tasks.list_tasks() - - -@app.get('/phenobase/bounding_box') -def get_phenobase_bounding_boxes() -> list: - if session.phenobase is None: - return [] - return session.phenobase.list_bounding_boxes() \ No newline at end of file + return session.tasks.list_tasks() \ No newline at end of file diff --git a/svlt/__init__.py b/svlt-core/src/svlt/clients/__init__.py similarity index 100% rename from svlt/__init__.py rename to svlt-core/src/svlt/clients/__init__.py diff --git a/svlt/clients/batch_runner.py b/svlt-core/src/svlt/clients/batch_runner.py similarity index 99% rename from svlt/clients/batch_runner.py rename to svlt-core/src/svlt/clients/batch_runner.py index 439cbeb65f888ce7b1cce7c33e286f4d610f3bef..30d6358e5b69d1b4071c07209939a944b1051965 100644 --- a/svlt/clients/batch_runner.py +++ b/svlt-core/src/svlt/clients/batch_runner.py @@ -89,7 +89,7 @@ class FileBatchRunnerClient(HttpClient): catch=catch ) if verify: - pa_touch = local_path / 'svlt.touch' + pa_touch = local_path / 'svlt-pheno.touch' try: with open(pa_touch, 'r') as fh: cont = fh.read() diff --git a/svlt/clients/py3.py b/svlt-core/src/svlt/clients/py3.py similarity index 97% rename from svlt/clients/py3.py rename to svlt-core/src/svlt/clients/py3.py index f96823bdbd997e26c4b28fa54622984d8eb7d8f9..0c399861235e4c290cd09e2d3f76ca8cc56615c4 100644 --- a/svlt/clients/py3.py +++ b/svlt-core/src/svlt/clients/py3.py @@ -7,7 +7,7 @@ class HttpClient(object): Local client for batch-running and testing purposes """ - app_name = 'svlt.core.api:app' + app_name = 'svlt-pheno.src.api:app' def __init__(self, host='127.0.0.1', port=6221, raise_nonsuccess=False, **kwargs) -> None: self.uri = f'http://{host}:{port}/' diff --git a/svlt/clients/__init__.py b/svlt-core/src/svlt/conf/__init__.py similarity index 100% rename from svlt/clients/__init__.py rename to svlt-core/src/svlt/conf/__init__.py diff --git a/svlt/conf/defaults.py b/svlt-core/src/svlt/conf/defaults.py similarity index 76% rename from svlt/conf/defaults.py rename to svlt-core/src/svlt/conf/defaults.py index 35352e91dce877be1832877c76ea80b3bb59ac76..4ee5e99c3282ffef1cb5ba80d07ea8a91b7ae81e 100644 --- a/svlt/conf/defaults.py +++ b/svlt-core/src/svlt/conf/defaults.py @@ -1,13 +1,13 @@ from pathlib import Path -root = Path.home() / 'svlt' / 'sessions' +root = Path.home() / 'svlt-pheno' / 'sessions' subdirectories = { 'conf': 'conf', 'logs': 'logs', 'inbound_images': 'images/inbound', 'outbound_images': 'images/outbound', - 'rois': 'rois', + 'svlt-pheno': 'svlt-pheno', 'tables': 'tables', } server_conf = { diff --git a/svlt/conf/startup.py b/svlt-core/src/svlt/conf/startup.py similarity index 59% rename from svlt/conf/startup.py rename to svlt-core/src/svlt/conf/startup.py index b340363769b2ce49f0c994e3812c4031a1ec4fda..71c85ea03c5149ac1172db94578defa213eafd2f 100644 --- a/svlt/conf/startup.py +++ b/svlt-core/src/svlt/conf/startup.py @@ -1,3 +1,4 @@ +import argparse from multiprocessing import Process import os import requests @@ -6,8 +7,10 @@ from urllib3 import Retry import uvicorn import webbrowser +from svlt.conf.defaults import server_conf, root -def main(host, port, confpath, reload, debug, root) -> None: + +def main(confpath, host, port, reload, debug, root) -> None: if root: os.environ['SVLT_SESSION_ROOT'] = root @@ -56,3 +59,36 @@ def main(host, port, confpath, reload, debug, root) -> None: server_process.terminate() return session_path + + +def parse_args(): + parser = argparse.ArgumentParser( + description='Start model server with optional arguments', + ) + parser.add_argument( + '--host', + default=server_conf['host'], + help='bind socket to this host' + ) + parser.add_argument( + '--port', + default=str(server_conf['port']), + help='bind socket to this port', + ) + parser.add_argument( + '--root', + default=root.__str__(), + help='root directory of session data' + ) + parser.add_argument( + '--debug', + action='store_true', + help='display extra information that is helpful for debugging' + ) + parser.add_argument( + '--reload', + action='store_true', + help='automatically restart server when changes are noticed, for development purposes' + ) + + return parser.parse_args() diff --git a/svlt/conf/testing.py b/svlt-core/src/svlt/conf/testing.py similarity index 94% rename from svlt/conf/testing.py rename to svlt-core/src/svlt/conf/testing.py index 5dc2c72a5b4357a5449d26454b208aab09159205..da11763dcf2042970e0bf6b1e62af50cfd27d1dd 100644 --- a/svlt/conf/testing.py +++ b/svlt-core/src/svlt/conf/testing.py @@ -10,15 +10,13 @@ from fastapi import APIRouter import numpy as np from pydantic import BaseModel -from .fastapi import app -from ..core.accessors import GenericImageDataAccessor, InMemoryDataAccessor -from ..core.models import SemanticSegmentationModel, InstanceMaskSegmentationModel -from ..core.pipelines.shared import call_pipeline, PipelineParams, PipelineQueueRecord, PipelineTrace -from ..core.session import session +from svlt.ilastik.fastapi import app +from svlt.accessors import generate_file_accessor, GenericImageDataAccessor, InMemoryDataAccessor +from svlt.models import SemanticSegmentationModel, InstanceMaskSegmentationModel +from ..pipelines.common import call_pipeline, PipelineParams, PipelineQueueRecord, PipelineTrace +from svlt.session import session from ..clients.py3 import HttpClient -from ..core.accessors import generate_file_accessor - """ Configure additional endpoints for testing """ @@ -179,7 +177,7 @@ def setup_test_data(): data_paths = [ os.environ.get('UNITTEST_DATA_ROOT'), _winpath(os.environ.get('UNITTEST_DATA_ROOT')), - Path.home() / 'svlt' / 'testing', + Path.home() / 'svlt-pheno' / 'testing', os.getcwd(), ] root = None diff --git a/svlt/core/czi_util.py b/svlt-core/src/svlt/czi_util.py similarity index 100% rename from svlt/core/czi_util.py rename to svlt-core/src/svlt/czi_util.py diff --git a/svlt/core/models.py b/svlt-core/src/svlt/models.py similarity index 98% rename from svlt/core/models.py rename to svlt-core/src/svlt/models.py index f4064feb451804bee297839fa02c8213ee637243..657091791c101af34cf234d391bac15c5814a927 100644 --- a/svlt/core/models.py +++ b/svlt-core/src/svlt/models.py @@ -8,7 +8,7 @@ class Model(ABC): def __init__(self, autoload: bool = True, info: dict = None): """ - Abstract base class for an inference model that uses image data as an input. + Abstract svlt-core class for an inference model that uses image data as an input. :param autoload: automatically load model and dependencies into memory if True :param info: optional dictionary of JSON-serializable information to report to API """ diff --git a/svlt/conf/__init__.py b/svlt-core/src/svlt/pipelines/__init__.py similarity index 100% rename from svlt/conf/__init__.py rename to svlt-core/src/svlt/pipelines/__init__.py diff --git a/svlt/core/pipelines/shared.py b/svlt-core/src/svlt/pipelines/common.py similarity index 88% rename from svlt/core/pipelines/shared.py rename to svlt-core/src/svlt/pipelines/common.py index e9a00b562fd33dbb9ea4907244703fbcf443fa43..63ffb9255a87f9a2bb5accc4620a936f9c24a247 100644 --- a/svlt/core/pipelines/shared.py +++ b/svlt-core/src/svlt/pipelines/common.py @@ -7,7 +7,6 @@ from fastapi import HTTPException from pydantic import BaseModel, Field, root_validator from ..accessors import GenericImageDataAccessor, InMemoryDataAccessor -from svlt.rois.roiset import PatchParams, RoiSetExportParams from ..session import session, AccessorIdError @@ -258,34 +257,6 @@ class PipelineTrace(OrderedDict): return paths -class RoiSetPipelineParams(PipelineParams): - exports: RoiSetExportParams = RoiSetExportParams() - roiset_index: Dict[str, int] - patches: Dict[str, PatchParams] = {} - - -def call_roiset_pipeline(func, p: RoiSetPipelineParams) -> Union[PipelineRecord, PipelineQueueRecord]: - """ - Wraps pipelines.shared.call_pipeline for pipeline functions that return an RoiSet in addition to PipelineTrace. - Upon execution, add the returned RoiSet to session PhenoBase and otherwise delegate to call_pipeline - :param func: pipeline function with the signature: - func(accessors: Dict[str, GenericImageDataAccessor], models: Dict[str, Model], **k) -> (PipelineTrace, RoiSet) - :param p: pipeline parameters object, which must contain a unique index for the RoiSet - :return: - record objects describing either the results of pipeline execution or the queued task - """ - def outer(*args, **kwargs): - trace, rois = func(*args, **kwargs) - session.add_roiset( - roiset=rois, - index_dict=p.roiset_index, - export_params=p.exports, - ) - session.write_phenobase_table() - return trace - return call_pipeline(outer, p) - - class Error(Exception): pass diff --git a/svlt/core/pipelines/router.py b/svlt-core/src/svlt/pipelines/router.py similarity index 100% rename from svlt/core/pipelines/router.py rename to svlt-core/src/svlt/pipelines/router.py diff --git a/svlt/core/pipelines/segment.py b/svlt-core/src/svlt/pipelines/segment.py similarity index 96% rename from svlt/core/pipelines/segment.py rename to svlt-core/src/svlt/pipelines/segment.py index 8604ddd276f9e277c0a654b5bead491ca15eb801..d26f55e0242e9c4244afcd41fe081d9a60f55899 100644 --- a/svlt/core/pipelines/segment.py +++ b/svlt-core/src/svlt/pipelines/segment.py @@ -1,7 +1,7 @@ from fastapi import APIRouter from typing import Dict, Union -from .shared import call_pipeline, IncompatibleModelsError, PipelineTrace, PipelineParams, PipelineQueueRecord, PipelineRecord +from .common import call_pipeline, IncompatibleModelsError, PipelineTrace, PipelineParams, PipelineQueueRecord, PipelineRecord from ..accessors import GenericImageDataAccessor from ..models import Model, SemanticSegmentationModel from ..process import smooth diff --git a/svlt/core/pipelines/segment_zproj.py b/svlt-core/src/svlt/pipelines/segment_zproj.py similarity index 94% rename from svlt/core/pipelines/segment_zproj.py rename to svlt-core/src/svlt/pipelines/segment_zproj.py index 6067553aea2aa54ee7fbf6af558e7a7a1b5bcdef..67dc8403f9f8296a38a8dba74029fa780c9a3111 100644 --- a/svlt/core/pipelines/segment_zproj.py +++ b/svlt-core/src/svlt/pipelines/segment_zproj.py @@ -2,7 +2,7 @@ from fastapi import APIRouter from typing import Dict, Union from .segment import SegmentParams, SegmentRecord, segment_pipeline -from .shared import call_pipeline, PipelineQueueRecord, PipelineTrace +from .common import call_pipeline, PipelineQueueRecord, PipelineTrace from ..accessors import GenericImageDataAccessor from ..models import Model diff --git a/svlt/core/process.py b/svlt-core/src/svlt/process.py similarity index 100% rename from svlt/core/process.py rename to svlt-core/src/svlt/process.py diff --git a/svlt/core/session.py b/svlt-core/src/svlt/session.py similarity index 93% rename from svlt/core/session.py rename to svlt-core/src/svlt/session.py index 7c898bb3d25adb762f83f00f1b9da4e3fa87b6e1..221987d84f31795e0a32be22bedd2ef83c1fbbfb 100644 --- a/svlt/core/session.py +++ b/svlt-core/src/svlt/session.py @@ -2,7 +2,6 @@ from collections import OrderedDict import itertools import logging import os -from typing import Dict import psutil import uuid @@ -14,11 +13,9 @@ from typing import Union import pandas as pd -from ..conf import defaults +from .conf import defaults from .accessors import GenericImageDataAccessor from .models import Model -from svlt.rois.phenobase import PhenoBase, RoiSetIndex -from svlt.rois.roiset import RoiSetExportParams, RoiSet logger = logging.getLogger(__name__) @@ -474,38 +471,6 @@ class _Session(object): return mid return None - def add_roiset( - self, - roiset: RoiSet, - index_dict: RoiSetIndex, - export_params: RoiSetExportParams = None, - ): - """ - Add an RoiSet into PhenoBase; initialize PhenoBase if empty - :param roiset: RoiSet object to be added to PhenoBase - :param index_dict: dict that uniquely identifies the RoiSet; keys must match existing ones - :param export_params: (optional) parameters for exporting each RoiSet patch series - return: dict of added RoiSet in PhenoBase if successful - """ - if self.phenobase is None: - self.phenobase = PhenoBase.from_roiset( - root=self.paths['outbound_images'], - roiset=roiset, - index_dict=index_dict, - export_params=export_params, - ) - else: - self.phenobase.push( - roiset, - index_dict=index_dict, - export_params=export_params, - ) - return index_dict - - def write_phenobase_table(self): - if self.phenobase is not None: - return self.phenobase.write_df() - def restart(self, **kwargs): self.__init__() diff --git a/svlt/core/util.py b/svlt-core/src/svlt/util.py similarity index 96% rename from svlt/core/util.py rename to svlt-core/src/svlt/util.py index 173f2954d26505f6d7938102979053b4404f8e4a..bd88a1974e2bf132d7d49cb16b719a39c92bb811 100644 --- a/svlt/core/util.py +++ b/svlt-core/src/svlt/util.py @@ -1,15 +1,11 @@ -from collections import OrderedDict -from math import ceil from pathlib import Path import re from time import localtime, strftime -from typing import Dict, List -from time import perf_counter import pandas as pd -from svlt.core.accessors import GenericImageDataAccessor, InMemoryDataAccessor, write_accessor_data_to_file -from svlt.core.models import Model +from .accessors import InMemoryDataAccessor, write_accessor_data_to_file + def autonumber_new_directory(where: str, prefix: str) -> str: """ diff --git a/pyproject.toml b/svlt-ilastik/pyproject.toml similarity index 95% rename from pyproject.toml rename to svlt-ilastik/pyproject.toml index d2741541a66bcf061898d7163ef5f52cec311bf9..cb5750e093fa647dff5ea5b51690f164046c1333 100644 --- a/pyproject.toml +++ b/svlt-ilastik/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] -name = "model_server" +name = "svlt-ilastik" license = {file = "LICENSE"} version = "2025.02.01" authors = [ @@ -19,7 +19,6 @@ dependencies = [ "glasbey", "ilastik ==1.4.1b6", "imagecodecs", - "jupyterlab", "matplotlib", "numpy", "pandas", diff --git a/requirements.yml b/svlt-ilastik/requirements.yml similarity index 96% rename from requirements.yml rename to svlt-ilastik/requirements.yml index 6360af6fdc28ec878db6fa0f88028ab99b0b90a3..17b48c72bea7451e62e98aad382a3899c3cc2335 100644 --- a/requirements.yml +++ b/svlt-ilastik/requirements.yml @@ -8,7 +8,6 @@ dependencies: - fastapi>=0.101 - ilastik=1.4.1b6 - imagecodecs - - jupyterlab - matplotlib - nd2 - numpy diff --git a/svlt-ilastik/scripts/run_server.py b/svlt-ilastik/scripts/run_server.py new file mode 100644 index 0000000000000000000000000000000000000000..ae3a6967b7bb443ed231478e5a7d703d5af1f98d --- /dev/null +++ b/svlt-ilastik/scripts/run_server.py @@ -0,0 +1,8 @@ +from svlt.conf.startup import main, parse_args + +if __name__ == '__main__': + args = parse_args() + print('CLI args:\n' + str(args)) + main(confpath='svlt.ilastik.fastapi', **args.__dict__) + print('Finished') + diff --git a/svlt/core/__init__.py b/svlt-ilastik/src/svlt/ilastik/__init__.py similarity index 100% rename from svlt/core/__init__.py rename to svlt-ilastik/src/svlt/ilastik/__init__.py diff --git a/svlt/core/pipelines/__init__.py b/svlt-ilastik/src/svlt/ilastik/examples/__init__.py similarity index 100% rename from svlt/core/pipelines/__init__.py rename to svlt-ilastik/src/svlt/ilastik/examples/__init__.py diff --git a/svlt/ilastik/examples/ilastik3d.py b/svlt-ilastik/src/svlt/ilastik/examples/ilastik3d.py similarity index 100% rename from svlt/ilastik/examples/ilastik3d.py rename to svlt-ilastik/src/svlt/ilastik/examples/ilastik3d.py diff --git a/svlt-ilastik/src/svlt/ilastik/fastapi.py b/svlt-ilastik/src/svlt/ilastik/fastapi.py new file mode 100644 index 0000000000000000000000000000000000000000..f3f894413911c6db84a3f7cb9c4feaf9fb15fc7d --- /dev/null +++ b/svlt-ilastik/src/svlt/ilastik/fastapi.py @@ -0,0 +1,7 @@ +import importlib + +from svlt.api import app + +for ex in ['ilastik', 'rois']: + m = importlib.import_module(f'svlt.{ex}.router', package=__package__) + app.include_router(m.router) diff --git a/svlt/ilastik/models.py b/svlt-ilastik/src/svlt/ilastik/models.py similarity index 97% rename from svlt/ilastik/models.py rename to svlt-ilastik/src/svlt/ilastik/models.py index fdcc4faa4d9120a2e55f3e70c2cfd0e0fe057be9..54b959240292f0a7d66fb49947fd1a86ebf279f7 100644 --- a/svlt/ilastik/models.py +++ b/svlt-ilastik/src/svlt/ilastik/models.py @@ -7,9 +7,9 @@ import warnings import numpy as np import vigra -from svlt.core.accessors import PatchStack -from svlt.core.accessors import GenericImageDataAccessor, InMemoryDataAccessor -from svlt.core.models import Model, ImageToImageModel, InstanceMaskSegmentationModel, InvalidInputImageError, ParameterExpectedError, SemanticSegmentationModel +from svlt.accessors import PatchStack +from svlt.accessors import GenericImageDataAccessor, InMemoryDataAccessor +from svlt.models import Model, ImageToImageModel, InstanceMaskSegmentationModel, InvalidInputImageError, ParameterExpectedError, SemanticSegmentationModel class IlastikModel(Model): diff --git a/svlt/ilastik/__init__.py b/svlt-ilastik/src/svlt/ilastik/pipelines/__init__.py similarity index 100% rename from svlt/ilastik/__init__.py rename to svlt-ilastik/src/svlt/ilastik/pipelines/__init__.py diff --git a/svlt/ilastik/pipelines/px_then_ob.py b/svlt-ilastik/src/svlt/ilastik/pipelines/px_then_ob.py similarity index 92% rename from svlt/ilastik/pipelines/px_then_ob.py rename to svlt-ilastik/src/svlt/ilastik/pipelines/px_then_ob.py index 312178df8bf8fb2cb55e2b4ef3c724cddb7be46b..26e072c40c00360bedaebfd91b53fa7060025e47 100644 --- a/svlt/ilastik/pipelines/px_then_ob.py +++ b/svlt-ilastik/src/svlt/ilastik/pipelines/px_then_ob.py @@ -3,9 +3,9 @@ from typing import Dict from fastapi import APIRouter, HTTPException from pydantic import Field -from svlt.core.accessors import GenericImageDataAccessor -from svlt.core.models import Model -from svlt.core.pipelines.shared import call_pipeline, PipelineTrace, PipelineParams, PipelineRecord +from svlt.accessors import GenericImageDataAccessor +from svlt.models import Model +from svlt.pipelines.common import call_pipeline, PipelineTrace, PipelineParams, PipelineRecord from ..models import IlastikPixelClassifierModel, IlastikObjectClassifierFromPixelPredictionsModel diff --git a/svlt/ilastik/router.py b/svlt-ilastik/src/svlt/ilastik/router.py similarity index 98% rename from svlt/ilastik/router.py rename to svlt-ilastik/src/svlt/ilastik/router.py index a0180de79d633cc3e96316ade0faf17660c7f5fd..81b10757c22a9bd07cedc5965091e2b06bbc84ca 100644 --- a/svlt/ilastik/router.py +++ b/svlt-ilastik/src/svlt/ilastik/router.py @@ -3,7 +3,7 @@ from pathlib import Path from fastapi import APIRouter from pydantic import BaseModel, Field -from svlt.core.session import session +from svlt.session import session from svlt.ilastik import models as ilm diff --git a/svlt-pheno/pyproject.toml b/svlt-pheno/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..3ce5f70b6ccbf8235c94fd0c2c79f84dc34e66cd --- /dev/null +++ b/svlt-pheno/pyproject.toml @@ -0,0 +1,35 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + + +[project] +name = "svlt-pheno" +license = {file = "LICENSE"} +version = "2025.02.01" +authors = [ + { name="Christopher Rhodes", email="christopher.rhodes@embl.de" }, +] +description = "Service for analyzing microscope images" +readme = "README.md" +requires-python = ">=3.9" +dependencies = [ + "czifile", + "fastapi >=0.101", + "glasbey", + "imagecodecs", + "matplotlib", + "numpy", + "pandas", + "pillow", + "pydantic ~=1.10.1", + "scikit-image >=0.21.0", + "scikit-learn >=1.5.0", + "tifffile", + "uvicorn >=0.23.0", + "zstd", +] + +[project.urls] +Homepage = "https://git.embl.de/rhodes/model_server" +Issues = "https://git.embl.de/rhodes/model_server/-/issues" \ No newline at end of file diff --git a/svlt-pheno/requirements.yml b/svlt-pheno/requirements.yml new file mode 100644 index 0000000000000000000000000000000000000000..5100aeeed4cf7f7bc6d6e9c6e4e970d279827a97 --- /dev/null +++ b/svlt-pheno/requirements.yml @@ -0,0 +1,22 @@ +name: svlt-pheno +channels: + - conda-forge +dependencies: + - czifile + - fastapi>=0.101 + - imagecodecs + - matplotlib + - nd2 + - numpy + - pandas + - pillow + - pip + - pydantic=1.10.* + - python=3.9.* + - scikit-image>=0.21.0 + - scikit-learn>=1.5.0 + - tifffile + - uvicorn>=0.23.0 + - zstd + - pip: + - glasbey diff --git a/svlt-pheno/scripts/run_server.py b/svlt-pheno/scripts/run_server.py new file mode 100644 index 0000000000000000000000000000000000000000..e6d72bdfa640cea414df474e289a5b1c70369981 --- /dev/null +++ b/svlt-pheno/scripts/run_server.py @@ -0,0 +1,8 @@ +from svlt.conf.startup import main, parse_args + +if __name__ == '__main__': + args = parse_args() + print('CLI args:\n' + str(args)) + main(confpath='svlt.rois.fastapi', **args.__dict__) + print('Finished') + diff --git a/svlt/ilastik/examples/__init__.py b/svlt-pheno/src/svlt/rois/__init__.py similarity index 100% rename from svlt/ilastik/examples/__init__.py rename to svlt-pheno/src/svlt/rois/__init__.py diff --git a/svlt/ilastik/pipelines/__init__.py b/svlt-pheno/src/svlt/rois/clients/__init__.py similarity index 100% rename from svlt/ilastik/pipelines/__init__.py rename to svlt-pheno/src/svlt/rois/clients/__init__.py diff --git a/svlt/clients/phenobase.py b/svlt-pheno/src/svlt/rois/clients/phenobase.py similarity index 100% rename from svlt/clients/phenobase.py rename to svlt-pheno/src/svlt/rois/clients/phenobase.py diff --git a/svlt/rois/derived.py b/svlt-pheno/src/svlt/rois/derived.py similarity index 96% rename from svlt/rois/derived.py rename to svlt-pheno/src/svlt/rois/derived.py index dc24b7a967f191d1b5425d8d2489540c3ece0430..bf14df1de6ff93499e3c840ccc4a125cf3bbae2e 100644 --- a/svlt/rois/derived.py +++ b/svlt-pheno/src/svlt/rois/derived.py @@ -3,8 +3,8 @@ from pathlib import Path import numpy as np import pandas as pd -from svlt.core.models import InstanceMaskSegmentationModel -from svlt.core.process import mask_largest_object +from svlt.models import InstanceMaskSegmentationModel +from svlt.process import mask_largest_object from svlt.rois.roiset import RoiSetExportParams, RoiSet diff --git a/svlt/rois/df.py b/svlt-pheno/src/svlt/rois/df.py similarity index 100% rename from svlt/rois/df.py rename to svlt-pheno/src/svlt/rois/df.py diff --git a/svlt-pheno/src/svlt/rois/fastapi.py b/svlt-pheno/src/svlt/rois/fastapi.py new file mode 100644 index 0000000000000000000000000000000000000000..70851e15f4e415c0a7fdb8871faf486bba3ebc3f --- /dev/null +++ b/svlt-pheno/src/svlt/rois/fastapi.py @@ -0,0 +1,7 @@ +import importlib + +from svlt.api import app + +for ex in ['rois']: + m = importlib.import_module(f'svlt.{ex}.router', package=__package__) + app.include_router(m.router) diff --git a/svlt/rois/features.py b/svlt-pheno/src/svlt/rois/features.py similarity index 100% rename from svlt/rois/features.py rename to svlt-pheno/src/svlt/rois/features.py diff --git a/svlt/rois/labels.py b/svlt-pheno/src/svlt/rois/labels.py similarity index 99% rename from svlt/rois/labels.py rename to svlt-pheno/src/svlt/rois/labels.py index c8837d610a7f766e717df414ecb9698cae0a48a9..30cd9821ba5633280143c1f6c65dfe8086029dd0 100644 --- a/svlt/rois/labels.py +++ b/svlt-pheno/src/svlt/rois/labels.py @@ -6,7 +6,7 @@ from scipy.stats import moment from skimage.filters import sobel from skimage.measure import label, shannon_entropy, regionprops_table -from svlt.core.accessors import GenericImageDataAccessor, InMemoryDataAccessor +from svlt.accessors import GenericImageDataAccessor, InMemoryDataAccessor from svlt.rois.df import filter_df, insert_level, is_df_3d, df_insert_slices diff --git a/svlt/rois/models.py b/svlt-pheno/src/svlt/rois/models.py similarity index 94% rename from svlt/rois/models.py rename to svlt-pheno/src/svlt/rois/models.py index 367a1dfd2c744fa69a994354cf86a409a0bd83e5..50ac29adbd8317eb1f20674ced865798f63a3ab5 100644 --- a/svlt/rois/models.py +++ b/svlt-pheno/src/svlt/rois/models.py @@ -2,8 +2,8 @@ import numpy as np import pandas as pd from skimage.measure import regionprops_table -from svlt.core.accessors import GenericImageDataAccessor, PatchStack, InMemoryDataAccessor -from svlt.core.models import InstanceMaskSegmentationModel +from svlt.accessors import GenericImageDataAccessor, PatchStack, InMemoryDataAccessor +from svlt.models import InstanceMaskSegmentationModel from svlt.rois.labels import get_label_ids diff --git a/svlt/rois/phenobase.py b/svlt-pheno/src/svlt/rois/phenobase.py similarity index 88% rename from svlt/rois/phenobase.py rename to svlt-pheno/src/svlt/rois/phenobase.py index 8d36e4b6381f3f8624145d8662059779b67a342d..9bd759ab216509ab7957bfadc9570f3c408ce033 100644 --- a/svlt/rois/phenobase.py +++ b/svlt-pheno/src/svlt/rois/phenobase.py @@ -5,8 +5,9 @@ from typing import Dict, Union import pandas as pd from sklearn.model_selection import train_test_split -from svlt.core.accessors import PatchStack, GenericImageFileAccessor -from svlt.rois.roiset import PatchParams, RoiSet, RoiSetExportParams +from svlt.accessors import PatchStack, GenericImageFileAccessor +from svlt.session import session +from svlt.rois.roiset import RoiSet, RoiSetExportParams class RoiSetIndex(dict): """ @@ -325,6 +326,60 @@ class PhenoBase(object): def list_bounding_boxes(self): return self.df.bounding_box.reset_index().to_dict(orient='records') +class _SessionPhenoBase(object): + def __init__(self): + """ + Singleton class for a phenobase attached to a server session that persists RoiSet data + """ + self._phenobase = None + + def add_roiset( + self, + roiset: RoiSet, + index_dict: RoiSetIndex, + export_params: RoiSetExportParams = None, + ): + """ + Add an RoiSet into PhenoBase; initialize PhenoBase if empty + :param roiset: RoiSet object to be added to PhenoBase + :param index_dict: dict that uniquely identifies the RoiSet; keys must match existing ones + :param export_params: (optional) parameters for exporting each RoiSet patch series + return: dict of added RoiSet in PhenoBase if successful + """ + if self._phenobase is None: + self._phenobase = PhenoBase.from_roiset( + root=session.paths['outbound_images'], + roiset=roiset, + index_dict=index_dict, + export_params=export_params, + ) + else: + self._phenobase.push( + roiset, + index_dict=index_dict, + export_params=export_params, + ) + return index_dict + + def write_phenobase_table(self): + if self._phenobase is not None: + return self._phenobase.write_df() + + def list_bounding_boxes(self): + if self._phenobase is not None: + return self._phenobase.list_bounding_boxes() + return [] + + @property + def count(self): + if self._phenobase is not None: + return self._phenobase.count + return 0 + + + +session_phenobase = _SessionPhenoBase() + class Error(Exception): pass diff --git a/svlt/rois/__init__.py b/svlt-pheno/src/svlt/rois/pipelines/__init__.py similarity index 100% rename from svlt/rois/__init__.py rename to svlt-pheno/src/svlt/rois/pipelines/__init__.py diff --git a/svlt/rois/pipelines/add_roiset.py b/svlt-pheno/src/svlt/rois/pipelines/add_roiset.py similarity index 86% rename from svlt/rois/pipelines/add_roiset.py rename to svlt-pheno/src/svlt/rois/pipelines/add_roiset.py index 35248d19077f742f8108c0c65f81e766a2334219..ae95cd4b81f7a8529701e00ba0989a7d8d0ca20e 100644 --- a/svlt/rois/pipelines/add_roiset.py +++ b/svlt-pheno/src/svlt/rois/pipelines/add_roiset.py @@ -3,9 +3,10 @@ from typing import Dict, Union from fastapi import APIRouter from pydantic import Field -from svlt.core.accessors import GenericImageDataAccessor -from svlt.core.models import Model -from svlt.core.pipelines.shared import call_roiset_pipeline, RoiSetPipelineParams, PipelineQueueRecord, PipelineRecord, PipelineTrace +from svlt.accessors import GenericImageDataAccessor +from svlt.models import Model +from svlt.pipelines.common import PipelineQueueRecord, PipelineRecord, PipelineTrace +from svlt.rois.pipelines.common import RoiSetPipelineParams, call_roiset_pipeline from svlt.rois.roiset import RoiSet, RoiSetMetaParams diff --git a/svlt-pheno/src/svlt/rois/pipelines/common.py b/svlt-pheno/src/svlt/rois/pipelines/common.py new file mode 100644 index 0000000000000000000000000000000000000000..1e2f783fa7d3f1b69da708d3bdde98e059be91a5 --- /dev/null +++ b/svlt-pheno/src/svlt/rois/pipelines/common.py @@ -0,0 +1,33 @@ +from typing import Dict, Union + +from ..phenobase import session_phenobase +from svlt.pipelines.common import PipelineParams, PipelineRecord, PipelineQueueRecord, call_pipeline +from svlt.rois.roiset import RoiSetExportParams, PatchParams + + +class RoiSetPipelineParams(PipelineParams): + exports: RoiSetExportParams = RoiSetExportParams() + roiset_index: Dict[str, int] + patches: Dict[str, PatchParams] = {} + + +def call_roiset_pipeline(func, p: RoiSetPipelineParams) -> Union[PipelineRecord, PipelineQueueRecord]: + """ + Wraps pipelines.shared.call_pipeline for pipeline functions that return an RoiSet in addition to PipelineTrace. + Upon execution, add the returned RoiSet to session PhenoBase and otherwise delegate to call_pipeline + :param func: pipeline function with the signature: + func(accessors: Dict[str, GenericImageDataAccessor], models: Dict[str, Model], **k) -> (PipelineTrace, RoiSet) + :param p: pipeline parameters object, which must contain a unique index for the RoiSet + :return: + record objects describing either the results of pipeline execution or the queued task + """ + def outer(*args, **kwargs): + trace, rois = func(*args, **kwargs) + session_phenobase.add_roiset( + roiset=rois, + index_dict=p.roiset_index, + export_params=p.exports, + ) + session_phenobase.write_phenobase_table() + return trace + return call_pipeline(outer, p) diff --git a/svlt/rois/pipelines/roiset_obmap.py b/svlt-pheno/src/svlt/rois/pipelines/roiset_obmap.py similarity index 90% rename from svlt/rois/pipelines/roiset_obmap.py rename to svlt-pheno/src/svlt/rois/pipelines/roiset_obmap.py index a16865b23502c93a9182b9bd9ee2309ea9e83f8d..69c65338589049a3eacabb8ea5ed9baf7b77e71c 100644 --- a/svlt/rois/pipelines/roiset_obmap.py +++ b/svlt-pheno/src/svlt/rois/pipelines/roiset_obmap.py @@ -3,15 +3,15 @@ from typing import Dict, Union from fastapi import APIRouter from pydantic import BaseModel, Field -from svlt.core.accessors import GenericImageDataAccessor -from svlt.core.pipelines.segment_zproj import segment_zproj_pipeline -from svlt.core.pipelines.shared import call_pipeline +from svlt.accessors import GenericImageDataAccessor +from svlt.pipelines.segment_zproj import segment_zproj_pipeline +from svlt.pipelines.common import call_pipeline from svlt.rois.roiset import RoiSet, RoiSetMetaParams, RoiSetExportParams from svlt.rois.labels import get_label_ids -from svlt.core.pipelines.shared import PipelineQueueRecord, PipelineTrace, PipelineParams, PipelineRecord +from svlt.pipelines.common import PipelineQueueRecord, PipelineTrace, PipelineParams, PipelineRecord -from svlt.core.models import Model, InstanceMaskSegmentationModel +from svlt.models import Model, InstanceMaskSegmentationModel class RoiSetObjectMapParams(PipelineParams): class _SegmentationParams(BaseModel): diff --git a/svlt/rois/router.py b/svlt-pheno/src/svlt/rois/pipelines/router.py similarity index 75% rename from svlt/rois/router.py rename to svlt-pheno/src/svlt/rois/pipelines/router.py index a28e2ffb11bd2724c2806d24f09eda358e7bf91c..96695cb003d00513d10687c1cea36719973af8a1 100644 --- a/svlt/rois/router.py +++ b/svlt-pheno/src/svlt/rois/pipelines/router.py @@ -3,13 +3,13 @@ import importlib from fastapi import APIRouter router = APIRouter( - prefix='/rois/pipelines', + prefix='/pipelines', tags=['pipelines'], ) for m in ['roiset_obmap', 'add_roiset']: router.include_router( importlib.import_module( - f'{__package__}.pipelines.{m}' + f'{__package__}.{m}' ).router ) \ No newline at end of file diff --git a/svlt/rois/roiset.py b/svlt-pheno/src/svlt/rois/roiset.py similarity index 98% rename from svlt/rois/roiset.py rename to svlt-pheno/src/svlt/rois/roiset.py index 4acf991f72c6b9b617017ee489df2512d1ded318..2ea0c8b5f6a7881dbb8551e84ba16aab8192c7a2 100644 --- a/svlt/rois/roiset.py +++ b/svlt-pheno/src/svlt/rois/roiset.py @@ -14,12 +14,12 @@ from skimage import draw from skimage.measure import approximate_polygon, find_contours, label, points_in_poly, regionprops from skimage.morphology import binary_dilation, disk -from svlt.core.accessors import GenericImageDataAccessor, InMemoryDataAccessor, write_accessor_data_to_file -from svlt.core.models import InstanceMaskSegmentationModel -from svlt.core.process import get_safe_contours, pad, rescale, make_rgb, safe_add -from svlt.core.annotators import draw_box_on_patch, draw_contours_on_patch, draw_boxes_on_3d_image -from svlt.core.accessors import generate_file_accessor, PatchStack -from svlt.core.process import mask_largest_object +from svlt.accessors import GenericImageDataAccessor, InMemoryDataAccessor, write_accessor_data_to_file +from svlt.models import InstanceMaskSegmentationModel +from svlt.process import get_safe_contours, pad, rescale, make_rgb, safe_add +from svlt.annotators import draw_box_on_patch, draw_contours_on_patch, draw_boxes_on_3d_image +from svlt.accessors import generate_file_accessor, PatchStack +from svlt.process import mask_largest_object from svlt.rois.df import filter_df_overlap_seg, is_df_3d, insert_level, read_roiset_df, df_insert_slices from svlt.rois.labels import get_label_ids, focus_metrics, make_df_from_object_ids, make_object_ids_from_df, \ NoDeprojectChannelSpecifiedError diff --git a/svlt-pheno/src/svlt/rois/router.py b/svlt-pheno/src/svlt/rois/router.py new file mode 100644 index 0000000000000000000000000000000000000000..d6bb489b48e1ed5235c9bcb9dd6b0682a4457d43 --- /dev/null +++ b/svlt-pheno/src/svlt/rois/router.py @@ -0,0 +1,35 @@ +import importlib +from typing import Union + +from fastapi import APIRouter +from pydantic import BaseModel, Field + +from svlt.session import session +from .phenobase import session_phenobase +from .models import IntensityThresholdInstanceMaskSegmentationModel + +router = APIRouter( + prefix='/pheno', + tags=['pheno'], +) + +router.include_router( + importlib.import_module( + f'{__package__}.pipelines.router' + ).router +) + +@router.get('/phenobase/bounding_box') +def get_phenobase_bounding_boxes() -> list: + if session_phenobase is None: + return [] + return session_phenobase.list_bounding_boxes() + +class BinaryThresholdSegmentationParams(BaseModel): + tr: Union[int, float] = Field(0.5, description='Threshold for binary segmentation') + +@router.put('/models/classify/threshold/load') +def load_intensity_threshold_instance_segmentation_model(p: BinaryThresholdSegmentationParams, model_id=None) -> dict: + result = session.load_model(IntensityThresholdInstanceMaskSegmentationModel, key=model_id, params=p) + session.log_info(f'Loaded permissive instance segmentation model {result}') + return {'model_id': result} \ No newline at end of file diff --git a/svlt/conf/fastapi.py b/svlt/conf/fastapi.py deleted file mode 100644 index f72963229f49f231f6ecc51283ac593590607133..0000000000000000000000000000000000000000 --- a/svlt/conf/fastapi.py +++ /dev/null @@ -1,7 +0,0 @@ -import importlib - -from ..core.api import app - -for ex in ['ilastik', 'rois']: - m = importlib.import_module(f'..{ex}.router', package=__package__) - app.include_router(m.router) diff --git a/svlt/core/share.py b/svlt/core/share.py deleted file mode 100644 index fad2b2f03d1a6c19909215a3f34c28867c37ac71..0000000000000000000000000000000000000000 --- a/svlt/core/share.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -Classes that manage data sharing between server and outside processes. Currently just a directory on a shared -filesystem; but may expand to include data transfer over API, shared memory objects, etc. -""" - -import os -import pathlib - -def validate_directory_exists(path): - return os.path.exists(path) - -class SharedImageDirectory(object): - - def __init__(self, path: pathlib.Path): - self.path = path - self.is_memory_mapped = False - - if not validate_directory_exists(path): - raise InvalidDirectoryError(f'Invalid directory:\n{path}') - -class InvalidDirectoryError(Exception): - pass - diff --git a/svlt/rois/pipelines/__init__.py b/tests/svlt-core/__init__.py similarity index 100% rename from svlt/rois/pipelines/__init__.py rename to tests/svlt-core/__init__.py diff --git a/tests/base/test_accessors.py b/tests/svlt-core/test_accessors.py similarity index 97% rename from tests/base/test_accessors.py rename to tests/svlt-core/test_accessors.py index 09f5a04f05459d1595d4a445b7f6187d3808db83..9aea9097f4fee586db44696b9443d6456c37b46f 100644 --- a/tests/base/test_accessors.py +++ b/tests/svlt-core/test_accessors.py @@ -2,9 +2,9 @@ import unittest import numpy as np -from svlt.core.accessors import PatchStack, make_patch_stack_from_file, FileNotFoundError, Nd2ImageFileAccessor +from svlt.accessors import PatchStack, make_patch_stack_from_file, FileNotFoundError, Nd2ImageFileAccessor -from svlt.core.accessors import CziImageFileAccessor, DataShapeError, generate_file_accessor, InMemoryDataAccessor, PngFileAccessor, write_accessor_data_to_file, TifSingleSeriesFileAccessor +from svlt.accessors import CziImageFileAccessor, DataShapeError, generate_file_accessor, InMemoryDataAccessor, PngFileAccessor, write_accessor_data_to_file, TifSingleSeriesFileAccessor import svlt.conf.testing as conf data = conf.meta['image_files'] @@ -179,7 +179,7 @@ class TestCziImageFileAccess(unittest.TestCase): self.assertEqual(cf.data.shape[3], mono.data.shape[2]) def test_write_two_channel_png(self): - from svlt.core.process import resample_to_8bit + from svlt.process import resample_to_8bit cf = CziImageFileAccessor(data['czifile']['path']) acc = cf.get_channels([0, 1]) opa = output_path / f'{cf.fpath.stem}_2ch.png' @@ -528,7 +528,7 @@ class TestNd2ImageFileAccess(unittest.TestCase): class TestMultipositionCziFileAccess(unittest.TestCase): def test_multiposition_czi(self): - from svlt.core.accessors import generate_multiposition_file_accessors + from svlt.accessors import generate_multiposition_file_accessors f = data['multipos_czi'] c = f['c'] nz = f['z'] diff --git a/tests/base/test_api.py b/tests/svlt-core/test_api.py similarity index 97% rename from tests/base/test_api.py rename to tests/svlt-core/test_api.py index 854b2690de8f94c991f2c84848140be625456245..cc834d232b70ec048f1753dce28a6e0b01e5a078 100644 --- a/tests/base/test_api.py +++ b/tests/svlt-core/test_api.py @@ -1,12 +1,11 @@ from pathlib import Path import svlt.conf.testing as conf -from svlt.conf.testing import TestServerBaseClass czifile = conf.meta['image_files']['czifile'] -class TestApiFromAutomatedClient(TestServerBaseClass): +class TestApiFromAutomatedClient(conf.TestServerBaseClass): input_data = czifile @@ -216,7 +215,7 @@ class TestApiFromAutomatedClient(TestServerBaseClass): self.assertEqual(self.assertGetSuccess(f'/tasks/get/{task_id}')['status'], 'READY') self.assertEqual( self.assertGetSuccess(f'/tasks/get/{task_id}')['target'], - 'svlt.core.pipelines.segment.segment_pipeline' + 'svlt.pipelines.segment.segment_pipeline' ) # run task @@ -226,12 +225,6 @@ class TestApiFromAutomatedClient(TestServerBaseClass): self.assertTrue(all(acc.unique()[0] == [0, 1])) self.assertTrue(all(acc.unique()[1] > 0)) - def test_permissive_instance_segmentation_model(self): - self.assertPutSuccess( - '/models/classify/threshold/load', - body={} - ) - def test_run_dummy_task(self): acc_id = self.assertPutSuccess('/testing/accessors/dummy_accessor/load') acc_in = self.get_accessor(acc_id) diff --git a/tests/base/test_model.py b/tests/svlt-core/test_models.py similarity index 93% rename from tests/base/test_model.py rename to tests/svlt-core/test_models.py index f8b137d986dd94dee5ef55bea977853a097764ba..eef0cbbc023cab059cdbaa76a24cefb4f915ff59 100644 --- a/tests/base/test_model.py +++ b/tests/svlt-core/test_models.py @@ -2,8 +2,8 @@ import unittest import svlt.conf.testing as conf from svlt.conf.testing import DummySemanticSegmentationModel, DummyInstanceMaskSegmentationModel -from svlt.core.accessors import CziImageFileAccessor -from svlt.core.models import CouldNotLoadModelError, BinaryThresholdSegmentationModel +from svlt.accessors import CziImageFileAccessor +from svlt.models import CouldNotLoadModelError, BinaryThresholdSegmentationModel czifile = conf.meta['image_files']['czifile'] diff --git a/tests/base/test_pipelines.py b/tests/svlt-core/test_pipelines.py similarity index 95% rename from tests/base/test_pipelines.py rename to tests/svlt-core/test_pipelines.py index d128d86d50d76d66f5f6ce9e84f5fade170c1467..e46cd70b8b76c76898dff491e10dfdbaa1c63db0 100644 --- a/tests/base/test_pipelines.py +++ b/tests/svlt-core/test_pipelines.py @@ -2,10 +2,10 @@ import unittest import numpy as np -from svlt.core.accessors import generate_file_accessor, InMemoryDataAccessor, write_accessor_data_to_file -from svlt.core.pipelines import segment, segment_zproj -from svlt.core.pipelines.shared import PipelineParams, PipelineRecord, PipelineTrace -from svlt.core.session import RunTaskError, session +from svlt.accessors import generate_file_accessor, InMemoryDataAccessor, write_accessor_data_to_file +from svlt.pipelines import segment, segment_zproj +from svlt.pipelines.common import PipelineParams, PipelineRecord, PipelineTrace +from svlt.session import RunTaskError, session import svlt.conf.testing as conf from svlt.conf.testing import DummySemanticSegmentationModel diff --git a/tests/base/test_process.py b/tests/svlt-core/test_process.py similarity index 95% rename from tests/base/test_process.py rename to tests/svlt-core/test_process.py index 515c0c7cf87211a47d8df530ad7a6afe3b24341c..53fb83624061e3f81498c9a0114bd208f26acc5f 100644 --- a/tests/base/test_process.py +++ b/tests/svlt-core/test_process.py @@ -2,8 +2,8 @@ import unittest import numpy as np -from svlt.core.annotators import draw_contours_on_patch -from svlt.core.process import get_safe_contours, mask_largest_object, pad, smooth +from svlt.annotators import draw_contours_on_patch +from svlt.process import get_safe_contours, mask_largest_object, pad, smooth class TestProcessingUtilityMethods(unittest.TestCase): def setUp(self) -> None: diff --git a/tests/base/test_session.py b/tests/svlt-core/test_session.py similarity index 97% rename from tests/base/test_session.py rename to tests/svlt-core/test_session.py index 89612d71cb919e49aa3261a62c80660ebb9f59da..62e13f82e7eedf8cab281f6d96b4e4cfdc1454a5 100644 --- a/tests/base/test_session.py +++ b/tests/svlt-core/test_session.py @@ -3,8 +3,8 @@ import pathlib import unittest import numpy as np -from svlt.core.accessors import generate_file_accessor, InMemoryDataAccessor -from svlt.core.session import session +from svlt.accessors import generate_file_accessor, InMemoryDataAccessor +from svlt.session import session import svlt.conf.testing as conf class TestGetSessionObject(unittest.TestCase): @@ -46,7 +46,7 @@ class TestGetSessionObject(unittest.TestCase): logfile1 = session1.logfile self.assertTrue(logfile1.exists()) - from svlt.core.session import session as session2 + from svlt.session import session as session2 self.assertEqual(session1, session2) logfile2 = session2.logfile self.assertTrue(logfile2.exists()) diff --git a/tests/base/__init__.py b/tests/svlt-ilastik/__init__.py similarity index 100% rename from tests/base/__init__.py rename to tests/svlt-ilastik/__init__.py diff --git a/tests/test_ilastik/test_ilastik.py b/tests/svlt-ilastik/test_ilastik.py similarity index 98% rename from tests/test_ilastik/test_ilastik.py rename to tests/svlt-ilastik/test_ilastik.py index cea849ef6906668ea80fa06b373f4b854fff8827..5e0cc8ae46946ef6ce1a3e21304929cc0f15985f 100644 --- a/tests/test_ilastik/test_ilastik.py +++ b/tests/svlt-ilastik/test_ilastik.py @@ -4,13 +4,13 @@ import unittest import numpy as np -from svlt.core.accessors import CziImageFileAccessor, generate_file_accessor, InMemoryDataAccessor, PatchStack, write_accessor_data_to_file -from svlt.core.api import app +from svlt.accessors import CziImageFileAccessor, generate_file_accessor, InMemoryDataAccessor, PatchStack, write_accessor_data_to_file +from svlt.conf.testing import app from svlt.ilastik import models as ilm from svlt.ilastik.pipelines import px_then_ob from svlt.ilastik.router import router from svlt.rois.roiset import RoiSet, RoiSetMetaParams -from svlt.core.pipelines import segment +from svlt.pipelines import segment import svlt.conf.testing as conf data = conf.meta['image_files'] @@ -206,7 +206,7 @@ class TestIlastikPixelClassification(unittest.TestCase): class TestServerTestCase(conf.TestServerBaseClass): - app_name = 'tests.test_ilastik.test_ilastik:app' + app_name = 'tests.svlt-ilastik.test_ilastik:app' input_data = czifile diff --git a/tests/rois/__init__.py b/tests/svlt-pheno/__init__.py similarity index 100% rename from tests/rois/__init__.py rename to tests/svlt-pheno/__init__.py diff --git a/tests/rois/test_features.py b/tests/svlt-pheno/test_features.py similarity index 96% rename from tests/rois/test_features.py rename to tests/svlt-pheno/test_features.py index f8ada24d78383fac6ba619da4cacff04a6ad59bc..0d4baeddcf062167a89882888f9d5233a7c81ffa 100644 --- a/tests/rois/test_features.py +++ b/tests/svlt-pheno/test_features.py @@ -1,6 +1,6 @@ import unittest -from svlt.core.accessors import generate_file_accessor +from svlt.accessors import generate_file_accessor import svlt.conf.testing as conf from svlt.rois.features import regionprops from svlt.rois.roiset import RoiSet, RoiSetMetaParams @@ -73,7 +73,7 @@ class TestRoiSetMonoProducts(unittest.TestCase): def test_empty_roiset_extract_features(self): import numpy as np - from svlt.core.accessors import InMemoryDataAccessor + from svlt.accessors import InMemoryDataAccessor arr_mask = InMemoryDataAccessor(np.zeros([*stack.hw, 1, stack.nz], dtype='uint8')) diff --git a/tests/svlt-pheno/test_models.py b/tests/svlt-pheno/test_models.py new file mode 100644 index 0000000000000000000000000000000000000000..9bf4124f926b3779986a955fa6e1536e683c6df9 --- /dev/null +++ b/tests/svlt-pheno/test_models.py @@ -0,0 +1,12 @@ +import svlt.conf.testing as conf + +czifile = conf.meta['image_files']['czifile'] + + +class TestApiFromAutomatedClient(conf.TestServerBaseClass): + + def test_permissive_instance_segmentation_model(self): + self.assertPutSuccess( + '/pheno/models/classify/threshold/load', + body={} + ) \ No newline at end of file diff --git a/tests/rois/test_phenobase.py b/tests/svlt-pheno/test_phenobase.py similarity index 95% rename from tests/rois/test_phenobase.py rename to tests/svlt-pheno/test_phenobase.py index bb86016096b6172c23af7e58b5408c969dc52b86..5b81537bd75d3a419d08a0b289d2dfc83def39d7 100644 --- a/tests/rois/test_phenobase.py +++ b/tests/svlt-pheno/test_phenobase.py @@ -4,12 +4,11 @@ import unittest import numpy as np -from svlt.core.accessors import generate_file_accessor, InMemoryDataAccessor -from svlt.core.session import session +from svlt.accessors import generate_file_accessor, InMemoryDataAccessor from svlt.conf import testing as conf from svlt.rois.models import IntensityThresholdInstanceMaskSegmentationModel -from svlt.rois.phenobase import MissingPatchSeriesError, PhenoBase, RoiSetIndexError +from svlt.rois.phenobase import MissingPatchSeriesError, PhenoBase, RoiSetIndexError, session_phenobase from svlt.rois.roiset import RoiSet, PatchParams, RoiSetExportParams, RoiSetMetaParams data = conf.meta['image_files'] @@ -234,7 +233,7 @@ class TestPhenoBase(unittest.TestCase): self.assertEqual(phenobase.count - count, 0) -class TestPhenoBaseApi(unittest.TestCase): +class TestSessionPhenoBase(unittest.TestCase): def test_initiate_phenobase_with_roiset(self): data = conf.meta['image_files'] @@ -254,13 +253,13 @@ class TestPhenoBaseApi(unittest.TestCase): self.assertGreater(roiset1.count, 2) # initiate phenobase in session scope - self.assertIsNone(session.phenobase) - session.add_roiset( + self.assertIsNone(session_phenobase._phenobase) + session_phenobase.add_roiset( roiset1, index_dict={'X': 0, 'T': 0}, export_params=RoiSetExportParams(patches={'ch0': PatchParams(white_channel=0)}), ) - self.assertEqual(session.phenobase.count, roiset1.count) + self.assertEqual(session_phenobase.count, roiset1.count) # add to phenobase in session scope roiset2 = RoiSet.from_binary_mask( @@ -274,9 +273,9 @@ class TestPhenoBaseApi(unittest.TestCase): ) self.assertGreater(roiset2.count, 2) - session.add_roiset( + session_phenobase.add_roiset( roiset2, index_dict={'X': 1, 'T': 1}, export_params=RoiSetExportParams(patches={'ch0': PatchParams(white_channel=0)}), ) - self.assertEqual(session.phenobase.count, roiset1.count + roiset2.count) \ No newline at end of file + self.assertEqual(session_phenobase.count, roiset1.count + roiset2.count) \ No newline at end of file diff --git a/tests/rois/test_roiset.py b/tests/svlt-pheno/test_roiset.py similarity index 98% rename from tests/rois/test_roiset.py rename to tests/svlt-pheno/test_roiset.py index 842273640ceb4477720c008b8da5112698450b4f..f0c688f0815c7d5252061224fb160444f9b737e2 100644 --- a/tests/rois/test_roiset.py +++ b/tests/svlt-pheno/test_roiset.py @@ -6,11 +6,10 @@ from pathlib import Path import pandas as pd -from svlt.core.accessors import generate_file_accessor, InMemoryDataAccessor, write_accessor_data_to_file -from svlt.core.process import smooth +from svlt.accessors import generate_file_accessor, InMemoryDataAccessor, write_accessor_data_to_file +from svlt.process import smooth import svlt.conf.testing as conf -from svlt.conf.testing import DummyInstanceMaskSegmentationModel from svlt.rois.labels import get_label_ids from svlt.rois.roiset import RoiSet, RoiSetExportParams, RoiSetMetaParams @@ -91,7 +90,7 @@ class TestRoiSetMonoProducts(BaseTestRoiSetMonoProducts, unittest.TestCase): roiset = RoiSet.from_object_ids(self.stack_ch_pa, zero_obmap) self.assertEqual(roiset.count, 0) cl = 'dummy_class' - roiset.classify_by(cl, [0], DummyInstanceMaskSegmentationModel()) + roiset.classify_by(cl, [0], conf.DummyInstanceMaskSegmentationModel()) self.assertTrue(cl in roiset.df().classifications.columns) def test_create_roiset_with_no_3d_objects(self): @@ -100,7 +99,7 @@ class TestRoiSetMonoProducts(BaseTestRoiSetMonoProducts, unittest.TestCase): roiset = RoiSet.from_object_ids(self.stack_ch_pa, zero_obmap) self.assertEqual(roiset.count, 0) cl = 'dummy_class' - roiset.classify_by(cl, [0], DummyInstanceMaskSegmentationModel()) + roiset.classify_by(cl, [0], conf.DummyInstanceMaskSegmentationModel()) self.assertTrue(cl in roiset.df().classifications.columns) def test_slices_are_valid(self): @@ -208,7 +207,7 @@ class TestRoiSetMonoProducts(BaseTestRoiSetMonoProducts, unittest.TestCase): def test_classify_by(self): roiset = self._make_roi_set() cl = 'dummy_class' - roiset.classify_by(cl, [0], DummyInstanceMaskSegmentationModel()) + roiset.classify_by(cl, [0], conf.DummyInstanceMaskSegmentationModel()) self.assertTrue(all(roiset.df()['classifications', cl].unique() == [1])) self.assertTrue(all(np.unique(roiset.get_object_class_map(cl).data) == [0, 1])) return roiset @@ -216,7 +215,7 @@ class TestRoiSetMonoProducts(BaseTestRoiSetMonoProducts, unittest.TestCase): def test_classify_by_multiple_channels(self): roiset = RoiSet.from_binary_mask(self.stack, self.seg_mask, params=RoiSetMetaParams(deproject_channel=0)) cl = 'dummy_class' - roiset.classify_by(cl, [0, 1], DummyInstanceMaskSegmentationModel()) + roiset.classify_by(cl, [0, 1], conf.DummyInstanceMaskSegmentationModel()) self.assertTrue(all(roiset.df().classifications[cl].unique() == [1])) self.assertTrue(all(np.unique(roiset.get_object_class_map('dummy_class').data) == [0, 1])) return roiset @@ -234,7 +233,7 @@ class TestRoiSetMonoProducts(BaseTestRoiSetMonoProducts, unittest.TestCase): # classify first RoiSet cl = 'dummy_class' - roiset1.classify_by(cl, [0, 1], DummyInstanceMaskSegmentationModel()) + roiset1.classify_by(cl, [0, 1], conf.DummyInstanceMaskSegmentationModel()) self.assertTrue(cl in roiset1.classification_columns) self.assertFalse(cl in roiset2.classification_columns) @@ -707,7 +706,7 @@ class TestRoiSetMultichannelProducts(BaseTestRoiSetMonoProducts, unittest.TestCa 'annotated_zstacks': {}, 'object_classes': True, }) - self.roiset.classify_by('dummy_class', [3], DummyInstanceMaskSegmentationModel()) + self.roiset.classify_by('dummy_class', [3], conf.DummyInstanceMaskSegmentationModel()) interm = self.roiset.get_export_product_accessors( params=p ) diff --git a/tests/rois/test_roiset_derived.py b/tests/svlt-pheno/test_roiset_derived.py similarity index 97% rename from tests/rois/test_roiset_derived.py rename to tests/svlt-pheno/test_roiset_derived.py index 395f19e88dc5d44d340e24c9494b619363bf5e16..deabcb2457a271494fe88b9063ae7bd8532fd437 100644 --- a/tests/rois/test_roiset_derived.py +++ b/tests/svlt-pheno/test_roiset_derived.py @@ -5,7 +5,7 @@ import numpy as np from svlt.rois.roiset import RoiSetMetaParams from svlt.rois.derived import RoiSetWithDerivedChannelsExportParams, RoiSetWithDerivedChannels -from svlt.core.accessors import generate_file_accessor, PatchStack +from svlt.accessors import generate_file_accessor, PatchStack import svlt.conf.testing as conf from svlt.conf.testing import DummyInstanceMaskSegmentationModel diff --git a/tests/rois/test_roiset_pipeline.py b/tests/svlt-pheno/test_roiset_pipeline.py similarity index 93% rename from tests/rois/test_roiset_pipeline.py rename to tests/svlt-pheno/test_roiset_pipeline.py index 0cab688745769963f90435d39112e0eec648534d..6a6b137035b22ac49ec4cf356b58d0597f5a3874 100644 --- a/tests/rois/test_roiset_pipeline.py +++ b/tests/svlt-pheno/test_roiset_pipeline.py @@ -4,7 +4,7 @@ import unittest import numpy as np -from svlt.core.accessors import generate_file_accessor +from svlt.accessors import generate_file_accessor import svlt.conf.testing as conf from svlt.rois.pipelines.roiset_obmap import RoiSetObjectMapParams, roiset_object_map_pipeline @@ -60,7 +60,7 @@ class BaseTestRoiSetMonoProducts(object): } def _get_models(self): - from svlt.core.models import BinaryThresholdSegmentationModel + from svlt.models import BinaryThresholdSegmentationModel from svlt.rois.models import IntensityThresholdInstanceMaskSegmentationModel return { 'pixel_classifier_segmentation': { @@ -83,17 +83,17 @@ class TestRoiSetWorkflow(BaseTestRoiSetMonoProducts, unittest.TestCase): 'pixel_classifier_segmentation_model_id': 'px_id', 'object_classifier_model_id': 'ob_id', 'segmentation': { - 'channel': test_params['segmentation_channel'], + 'channel': 0, 'smooth': 1.5, 'binary_closing': True, }, - 'patches_channel': test_params['patches_channel'], + 'patches_channel': 1, 'roi_params': self._get_roi_params(), 'export_params': self._get_export_params(), } def test_object_map_workflow(self): - n_rois = 18 + n_rois = 4 acc_in = generate_file_accessor(self.fpi) params = RoiSetObjectMapParams( **self._pipeline_params(), @@ -134,7 +134,7 @@ class TestRoiSetWorkflowOverApi(conf.TestServerBaseClass, BaseTestRoiSetMonoProd def test_load_object_classifier(self): mid = self.assertPutSuccess( - 'models/classify/threshold/load/', + 'pheno/models/classify/threshold/load/', body={'tr': 0} )['model_id'] self.assertTrue(mid.startswith('IntensityThresholdInstanceMaskSegmentation')) @@ -142,7 +142,7 @@ class TestRoiSetWorkflowOverApi(conf.TestServerBaseClass, BaseTestRoiSetMonoProd def _object_map_workflow(self, ob_classifer_id): return self.assertPutSuccess( - 'rois/pipelines/roiset_to_obmap', + 'pheno/pipelines/roiset_to_obmap', body={ 'accessor_id': self.test_load_input_accessor(), 'pixel_classifier_segmentation_model_id': self.test_load_pixel_classifier(), @@ -171,7 +171,7 @@ class TestTaskQueuedRoiSetWorkflowOverApi(TestRoiSetWorkflowOverApi): def _object_map_workflow(self, ob_classifer_id): res_queue = self.assertPutSuccess( - 'rois/pipelines/roiset_to_obmap', + 'pheno/pipelines/roiset_to_obmap', body={ 'schedule': True, 'accessor_id': self.test_load_input_accessor(), @@ -210,7 +210,7 @@ class TestAddRoiSetOverApi(conf.TestServerBaseClass): acc_id_mask = self.assertPutSuccess(f'accessors/read_from_file/{pa_mask.name}') res = self.assertPutSuccess( - 'rois/pipelines/add_roiset', + 'pheno/pipelines/add_roiset', body={ 'accessor_id': acc_id_stack, 'labels_accessor_id': acc_id_mask, # binary mask, not labels, so there's only on ROI @@ -226,5 +226,5 @@ class TestAddRoiSetOverApi(conf.TestServerBaseClass): self.assertEqual(sd['P'], 1) # RoiSet has one entry - phenobase = self.assertGetSuccess('phenobase/bounding_box') + phenobase = self.assertGetSuccess('pheno/phenobase/bounding_box') self.assertEqual(len(phenobase), 1) \ No newline at end of file diff --git a/tests/test_ilastik/__init__.py b/tests/test_ilastik/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000