diff --git a/model_server/clients/ilastik_map_objects.py b/model_server/clients/ilastik_map_objects.py index d04a1fbc269d5624ba93e019ec042273b697f878..ee16cbad2e2467a59c70e920e51408cab944819b 100644 --- a/model_server/clients/ilastik_map_objects.py +++ b/model_server/clients/ilastik_map_objects.py @@ -44,7 +44,7 @@ def main(request_func, in_abspath, params): resp = request_func( 'PUT', '/ilastik/seg/load/', - { + body={ 'project_file': px_ilp, 'duplicate': False, }, @@ -55,7 +55,7 @@ def main(request_func, in_abspath, params): # load object classifier resp = request_func( 'PUT', '/ilastik/pxmap_to_obj/load/', - { + body={ 'project_file': ob_ilp, 'duplicate': False, }, diff --git a/model_server/extensions/ilastik/models.py b/model_server/extensions/ilastik/models.py index 4a1c4c0ab785e7b2eac3b481447390d2ad28749d..e8afb2381fe0f950113492721a3d7a8c899f2e2e 100644 --- a/model_server/extensions/ilastik/models.py +++ b/model_server/extensions/ilastik/models.py @@ -15,6 +15,7 @@ from model_server.base.models import Model, ImageToImageModel, InstanceSegmentat class IlastikParams(BaseModel): project_file: str + duplicate: bool = True class IlastikModel(Model): diff --git a/model_server/extensions/ilastik/router.py b/model_server/extensions/ilastik/router.py index 9ad2733e8cbeba14674a57e25df5d2b8cd61874d..491e7e40ab678c34426ef0ae8a846c2fe065ea2f 100644 --- a/model_server/extensions/ilastik/router.py +++ b/model_server/extensions/ilastik/router.py @@ -15,7 +15,7 @@ router = APIRouter( session = Session() -def load_ilastik_model(model_class: ilm.IlastikModel, params: dict, duplicate=True) -> dict: +def load_ilastik_model(model_class: ilm.IlastikModel, params: ilm.IlastikParams) -> dict: """ Load an ilastik model of a given class and project filename. :param model_class: @@ -24,7 +24,7 @@ def load_ilastik_model(model_class: ilm.IlastikModel, params: dict, duplicate=Tr :return: dict containing model's ID """ project_file = params.project_file - if not duplicate: + if not params.duplicate: existing_model_id = session.find_param_in_loaded_models('project_file', project_file, is_path=True) if existing_model_id is not None: session.log_info(f'An ilastik model from {project_file} already existing exists; did not load a duplicate') @@ -34,27 +34,24 @@ def load_ilastik_model(model_class: ilm.IlastikModel, params: dict, duplicate=Tr return {'model_id': result} @router.put('/seg/load/') -def load_px_model(params: ilm.IlastikPixelClassifierParams, duplicate: bool = True) -> dict: +def load_px_model(params: ilm.IlastikPixelClassifierParams) -> dict: return load_ilastik_model( ilm.IlastikPixelClassifierModel, params, - duplicate=duplicate ) @router.put('/pxmap_to_obj/load/') -def load_pxmap_to_obj_model(params: ilm.IlastikParams, duplicate: bool = True) -> dict: +def load_pxmap_to_obj_model(params: ilm.IlastikParams) -> dict: return load_ilastik_model( ilm.IlastikObjectClassifierFromPixelPredictionsModel, params, - duplicate=duplicate ) @router.put('/seg_to_obj/load/') -def load_seg_to_obj_model(params: ilm.IlastikParams, duplicate: bool = True) -> dict: +def load_seg_to_obj_model(params: ilm.IlastikParams) -> dict: return load_ilastik_model( ilm.IlastikObjectClassifierFromSegmentationModel, params, - duplicate=duplicate ) @router.put('/pixel_then_object_classification/infer') diff --git a/model_server/extensions/ilastik/tests/test_ilastik.py b/model_server/extensions/ilastik/tests/test_ilastik.py index a48867c0e689aa4eb26589aa4a2d2e040ae7f81a..1907459d2d5d3a87be5da49e291fe15e4e1b7ca0 100644 --- a/model_server/extensions/ilastik/tests/test_ilastik.py +++ b/model_server/extensions/ilastik/tests/test_ilastik.py @@ -226,15 +226,13 @@ class TestIlastikOverApi(TestServerBaseClass): self.assertEqual(len(resp_list_1st), 1, resp_list_1st) resp_load_2nd = self._put( 'ilastik/seg/load/', - body={'project_file': str(ilastik_classifiers['px'])}, - query={'duplicate': True}, + body={'project_file': str(ilastik_classifiers['px']), 'duplicate': True}, ) resp_list_2nd = self._get('models').json() self.assertEqual(len(resp_list_2nd), 2, resp_list_2nd) resp_load_3rd = self._put( 'ilastik/seg/load/', - body={'project_file': str(ilastik_classifiers['px'])}, - query={'duplicate': False}, + body={'project_file': str(ilastik_classifiers['px']), 'duplicate': False}, ) resp_list_3rd = self._get('models').json() self.assertEqual(len(resp_list_3rd), 2, resp_list_3rd) @@ -273,13 +271,11 @@ class TestIlastikOverApi(TestServerBaseClass): # load models with these paths resp1 = self._put( 'ilastik/seg/load/', - body={'project_file': ilp_win}, - query={'duplicate': False}, + body={'project_file': ilp_win, 'duplicate': False}, ) resp2 = self._put( 'ilastik/seg/load/', - body={'project_file': ilp_posx}, - query={'duplicate': False}, + body={'project_file': ilp_posx, 'duplicate': False}, ) self.assertEqual(resp1.json(), resp2.json()) diff --git a/model_server/scripts/correct_ilastik_abspath.py b/model_server/scripts/correct_ilastik_abspath.py new file mode 100644 index 0000000000000000000000000000000000000000..f915bf3f54bcbe01cb097ee7ea9e7b2915eb18c5 --- /dev/null +++ b/model_server/scripts/correct_ilastik_abspath.py @@ -0,0 +1,48 @@ +from os.path import relpath +from pathlib import Path +import shutil + +import h5py + +def make_abspath_to_relpath(ilp_filename: str, my_root: Path, their_root: Path): + pa_ilp_old = my_root / ilp_filename + assert pa_ilp_old.exists() + pa_ilp_new = pa_ilp_old.parent / f'relpath_{pa_ilp_old.name}' + with h5py.File(shutil.copy(pa_ilp_old, pa_ilp_new), 'r+') as h5: + infos = h5['Input Data/infos'] + for lane in infos.keys(): + for role in infos[lane].keys(): + if len(infos[lane][role]) == 0: + continue + pa_img_abs = Path(infos[lane][role]['filePath'][()].decode()) + my_ilp_dir = (my_root / ilp_filename).parent + their_ilp_dir = (their_root / ilp_filename).parent + pa_img_rel = Path(relpath(pa_img_abs, their_ilp_dir)) + if pa_img_rel.parts[-2].upper().endswith('.H5'): + assert (my_ilp_dir / Path(*pa_img_rel.parts[0:-1])).exists() + else: + assert (my_ilp_dir / pa_img_rel).exists() + del infos[lane][role]['filePath'] + infos[lane][role]['filePath'] = str(pa_img_rel) + return pa_ilp_new + + + + +if __name__ == '__main__': + files = [ + '01_ilastik_files/240301_LSM900_DNA_PC.ilp', + '01_ilastik_files/240320_LSM900_DNA_OC_new.ilp', + '01_ilastik_files/240301_LSM900_TM_PC.ilp', + '01_ilastik_files/240320_LSM900_TM_OC_new.ilp' + ] + for f in files: + new_ilp = make_abspath_to_relpath( + f, + Path('w:/03_analysis/Trial3_LSM900'), + Path('/g/cuylen/01_Share/Filemaker/01_Experiments/Experiments_1100/1156/03_analysis/Trial3_LSM900') + ) + print(f'Finished converting {new_ilp}') + + + diff --git a/model_server/scripts/verify_multichannel_ilastik_inputs.py b/model_server/scripts/verify_multichannel_ilastik_inputs.py new file mode 100644 index 0000000000000000000000000000000000000000..1159b3f1ae42d9902474a854c7f85f1134748fe5 --- /dev/null +++ b/model_server/scripts/verify_multichannel_ilastik_inputs.py @@ -0,0 +1,111 @@ +from pathlib import Path + +import h5py +import numpy as np +import pandas as pd + +from model_server.base.accessors import generate_file_accessor, write_accessor_data_to_file, InMemoryDataAccessor +from model_server.extensions.ilastik.models import IlastikPixelClassifierModel, IlastikObjectClassifierFromPixelPredictionsModel + +def get_input_files(where_ilp: Path) -> list: + files = [] + with h5py.File(where_ilp, 'r') as h5: + infos = h5['Input Data/infos'] + for lane in infos.keys(): + lane_dict = {} + for role in infos[lane].keys(): + if len(infos[lane][role]) == 0: + continue + rel_path = Path(infos[lane][role]['filePath'][()].decode()) + lane_dict[role] = where_ilp.parent / rel_path + files.append(lane_dict) + return files + +if __name__ == '__main__': + where_out = Path('c:/Users/rhodes/projects/proj0015-model-server/issues/0032_multiple_input_channels/output') + root = Path('w:/03_analysis/Trial3_LSM900') + max_files = 1 + ilps = [ + '01_ilastik_files/relpath_240301_LSM900_DNA_PC.ilp', + '01_ilastik_files/relpath_240320_LSM900_DNA_OC_new.ilp', + '01_ilastik_files/relpath_240301_LSM900_TM_PC.ilp', + '01_ilastik_files/relpath_240320_LSM900_TM_OC_new.ilp' + ] + records = [] + for f in ilps: + ilp = root / f + assert ilp.exists() + outdir = where_out / ilp.stem + outdir.mkdir(parents=True, exist_ok=True) + + if ilp.stem.upper().endswith('_PC'): + mod = IlastikPixelClassifierModel( + params={'project_file': str(ilp)}, + enforce_embedded=False + ) + infiles = get_input_files(ilp) + for ln in infiles[0:max_files]: + acc_raw = generate_file_accessor(root / ln['Raw Data']) + pxmap = mod.infer(acc_raw)[0] + pxmap_fn = 'pxmap_' + ln['Raw Data'].stem + '.tif' + write_accessor_data_to_file(outdir / pxmap_fn, pxmap) + record = { + 'classifier': str(ilp.relative_to(root)), + 'input_raw_data': str(ln['Raw Data'].relative_to(root)), + 'input_raw_data_chroma': acc_raw.chroma, + 'input_raw_data_dtype': acc_raw.dtype, + 'input_raw_data_shape_dict': acc_raw.shape_dict, + 'output_file': pxmap_fn, + 'output_dtype': pxmap.dtype, + 'output_chroma': pxmap.chroma, + 'output_shape_dict': pxmap.shape_dict, + } + records.append(record) + + elif ilp.stem.upper().endswith('_OC_NEW'): + mod = IlastikObjectClassifierFromPixelPredictionsModel( + params={'project_file': str(ilp)}, + enforce_embedded=False + ) + infiles = get_input_files(ilp) + for ln in infiles[0:max_files]: + acc_raw = generate_file_accessor(root / ln['Raw Data']) + pa_pxmap = root / ln['Prediction Maps'] + + if pa_pxmap.parts[-2].upper().endswith('.H5'): + pa_h5f = root / Path(*pa_pxmap.parts[0:-1]) + h5_key = pa_pxmap.parts[-1] + pxmap_data = h5py.File(pa_h5f)[h5_key][()] # C x Y x X ? + pxmap_yxc = np.moveaxis( + pxmap_data, + [1, 2, 0], + [0, 1, 2] + ) + acc_pxmap = InMemoryDataAccessor(np.expand_dims(pxmap_yxc, -1)) + else: + acc_pxmap = generate_file_accessor(pa_pxmap) + obmap = mod.infer(acc_raw, acc_pxmap)[0] + obmap_fn = 'obmap_' + ln['Raw Data'].stem + '.tif' + write_accessor_data_to_file(outdir / obmap_fn, obmap) + record = { + 'classifier': str(ilp.relative_to(root)), + 'input_raw_data': str(ln['Raw Data'].relative_to(root)), + 'input_raw_data_chroma': acc_raw.chroma, + 'input_raw_data_dtype': acc_raw.dtype, + 'input_raw_data_shape_dict': acc_raw.shape_dict, + 'input_pxmap': str(ln['Prediction Maps'].relative_to(root)), + 'input_pxmap_chroma': acc_pxmap.chroma, + 'input_pxmap_dtype': acc_pxmap.dtype, + 'input_pxmap_shape_dict': acc_pxmap.shape_dict, + 'output_file': obmap_fn, + 'output_dtype': obmap.dtype, + 'output_chroma': obmap.chroma, + 'output_shape_dict': obmap.shape_dict, + } + records.append(record) + + else: + raise Exception(f'unidentified project file {ilp}') + + pd.DataFrame(records).to_csv(where_out / 'record.csv', index=False) + print('Finished')