From 7d7f0552c287e469e04639d5a42e5d5380588128 Mon Sep 17 00:00:00 2001 From: Christopher Rhodes <christopher.rhodes@embl.de> Date: Fri, 1 Sep 2023 14:55:44 +0200 Subject: [PATCH] Pass notebook filename and not path to load an ilastik model; assumes project files are in conf.server.paths['ilastik'] --- conf/server.py | 4 ++-- conf/testing.py | 9 +++++-- model_server/ilastik.py | 12 +++++++--- tests/test_ilastik.py | 53 +++++++++++++++++++++++++---------------- 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/conf/server.py b/conf/server.py index a143374d..707b60ac 100644 --- a/conf/server.py +++ b/conf/server.py @@ -9,8 +9,8 @@ paths = { 'inbound': root / 'images' / 'inbound', 'outbound': root / 'images' / 'outbound', }, - 'ilastik' : { - 'projects' : root / 'ilastik' + 'ilastik': { + 'projects': root / 'ilastik' } } diff --git a/conf/testing.py b/conf/testing.py index 7e76e6b0..cd6088e5 100644 --- a/conf/testing.py +++ b/conf/testing.py @@ -11,9 +11,14 @@ czifile = { 'z': 1, } +# ilastik = { +# 'pixel_classifier': root / 'testdata' / 'ilastik' / 'demo_px.ilp', +# 'object_classifier': root / 'testdata' / 'ilastik' / 'demo_obj.ilp', +# } + ilastik = { - 'pixel_classifier': root / 'testdata' / 'ilastik' / 'demo_px.ilp', - 'object_classifier': root / 'testdata' / 'ilastik' / 'demo_obj.ilp', + 'pixel_classifier': 'demo_px.ilp', + 'object_classifier': 'demo_obj.ilp', } output_path = root / 'testing_output' diff --git a/model_server/ilastik.py b/model_server/ilastik.py index 606c93d4..54419f78 100644 --- a/model_server/ilastik.py +++ b/model_server/ilastik.py @@ -1,8 +1,10 @@ import os +import pathlib import numpy as np import vigra +import conf.server from model_server.image import GenericImageDataAccessor, InMemoryDataAccessor from model_server.model import ImageToImageModel, ParameterExpectedError @@ -10,9 +12,13 @@ from model_server.model import ImageToImageModel, ParameterExpectedError class IlastikImageToImageModel(ImageToImageModel): def __init__(self, params, autoload=True): - if 'project_file' not in params or not os.path.exists(params['project_file']): - raise ParameterExpectedError('Ilastik model expects a project (*.ilp) file') self.project_file = str(params['project_file']) + self.project_file_abspath = pathlib.Path( + conf.server.paths['ilastik']['projects'] / self.project_file, + ) + if 'project_file' not in params or not self.project_file_abspath.exists(): + raise ParameterExpectedError('Ilastik model expects a project (*.ilp) file') + self.shell = None super().__init__(autoload, params) @@ -28,7 +34,7 @@ class IlastikImageToImageModel(ImageToImageModel): args = app.parse_args([]) args.headless = True - args.project = self.project_file + args.project = self.project_file_abspath.__str__() shell = app.main(args) if not isinstance(shell.workflow, self.get_workflow()): diff --git a/tests/test_ilastik.py b/tests/test_ilastik.py index 01ac4306..204ad237 100644 --- a/tests/test_ilastik.py +++ b/tests/test_ilastik.py @@ -3,7 +3,7 @@ import unittest import numpy as np -from conf.testing import czifile, ilastik, output_path +import conf.testing from model_server.image import CziImageFileAccessor, InMemoryDataAccessor, write_accessor_data_to_file from model_server.ilastik import IlastikObjectClassifierModel, IlastikPixelClassifierModel from model_server.model import Model @@ -12,7 +12,7 @@ from tests.test_api import TestServerBaseClass class TestIlastikPixelClassification(unittest.TestCase): def setUp(self) -> None: - self.cf = CziImageFileAccessor(czifile['path']) + self.cf = CziImageFileAccessor(conf.testing.czifile['path']) def test_faulthandler(self): # recreate error that is messing up ilastik @@ -25,18 +25,23 @@ class TestIlastikPixelClassification(unittest.TestCase): def test_raise_error_if_autoload_disabled(self): - model = IlastikPixelClassifierModel({'project_file': ilastik['pixel_classifier']}, autoload=False) + model = IlastikPixelClassifierModel( + {'project_file': conf.testing.ilastik['pixel_classifier']}, + autoload=False + ) w = 512 h = 256 input_img = InMemoryDataAccessor(data=np.random.rand(w, h, 1, 1)) with self.assertRaises(AttributeError): - pxmap , _= model.infer(input_img) + pxmap, _ = model.infer(input_img) def test_run_pixel_classifier_on_random_data(self): - model = IlastikPixelClassifierModel({'project_file': ilastik['pixel_classifier']}) + model = IlastikPixelClassifierModel( + {'project_file': conf.testing.ilastik['pixel_classifier']}, + ) w = 512 h = 256 @@ -48,12 +53,16 @@ class TestIlastikPixelClassification(unittest.TestCase): def test_run_pixel_classifier(self): channel = 0 - model = IlastikPixelClassifierModel({'project_file': ilastik['pixel_classifier']}) - cf = CziImageFileAccessor(czifile['path']) + model = IlastikPixelClassifierModel( + {'project_file': conf.testing.ilastik['pixel_classifier']} + ) + cf = CziImageFileAccessor( + conf.testing.czifile['path'] + ) mono_image = cf.get_one_channel_data(channel) - self.assertEqual(mono_image.shape_dict['X'], czifile['w']) - self.assertEqual(mono_image.shape_dict['Y'], czifile['h']) + self.assertEqual(mono_image.shape_dict['X'], conf.testing.czifile['w']) + self.assertEqual(mono_image.shape_dict['Y'], conf.testing.czifile['h']) self.assertEqual(mono_image.shape_dict['C'], 1) self.assertEqual(mono_image.shape_dict['Z'], 1) @@ -64,7 +73,7 @@ class TestIlastikPixelClassification(unittest.TestCase): self.assertEqual(pxmap.shape_dict['Z'], 1) self.assertTrue( write_accessor_data_to_file( - output_path / f'pxmap_{cf.fpath.stem}_ch{channel}.tif', + conf.testing.output_path / f'pxmap_{cf.fpath.stem}_ch{channel}.tif', pxmap ) ) @@ -74,13 +83,15 @@ class TestIlastikPixelClassification(unittest.TestCase): def test_run_object_classifier(self): self.test_run_pixel_classifier() - fp = czifile['path'] - model = IlastikObjectClassifierModel({'project_file': ilastik['object_classifier']}) + fp = conf.testing.czifile['path'] + model = IlastikObjectClassifierModel( + {'project_file': conf.testing.ilastik['object_classifier']} + ) objmap, _ = model.infer(self.mono_image, self.pxmap) self.assertTrue( write_accessor_data_to_file( - output_path / f'obmap_{fp.stem}.tif', + conf.testing.output_path / f'obmap_{fp.stem}.tif', objmap, ) ) @@ -88,9 +99,11 @@ class TestIlastikPixelClassification(unittest.TestCase): def test_ilastik_pixel_classification_as_workflow(self): result = infer_image_to_image( - czifile['path'], - IlastikPixelClassifierModel({'project_file': ilastik['pixel_classifier']}), - output_path, + conf.testing.czifile['path'], + IlastikPixelClassifierModel( + {'project_file': conf.testing.ilastik['pixel_classifier']} + ), + conf.testing.output_path, channel=0, ) self.assertTrue(result.success) @@ -100,7 +113,7 @@ class TestIlastikOverApi(TestServerBaseClass): def test_load_ilastik_pixel_model(self): resp_load = requests.put( self.uri + 'models/ilastik/pixel_classification/load/', - params={'project_file': str(ilastik['pixel_classifier'])}, + params={'project_file': str(conf.testing.ilastik['pixel_classifier'])}, ) model_id = resp_load.json()['model_id'] @@ -116,7 +129,7 @@ class TestIlastikOverApi(TestServerBaseClass): def test_load_ilastik_object_model(self): resp_load = requests.put( self.uri + 'models/ilastik/object_classification/load/', - params={'project_file': str(ilastik['object_classifier'])}, + params={'project_file': str(conf.testing.ilastik['object_classifier'])}, ) model_id = resp_load.json()['model_id'] @@ -134,8 +147,8 @@ class TestIlastikOverApi(TestServerBaseClass): self.uri + f'infer/from_image_file', params={ 'model_id': model_id, - 'input_filename': czifile['filename'], - 'channel': 2, + 'input_filename': conf.testing.czifile['filename'], + 'channel': 0, }, ) self.assertEqual(resp_infer.status_code, 200, resp_infer.content.decode()) -- GitLab