Skip to content
Snippets Groups Projects
Commit 8bf70444 authored by Christopher Randolph Rhodes's avatar Christopher Randolph Rhodes
Browse files

Confirmed ilastik pipeline runs with new module format

parent a197934e
No related branches found
No related tags found
No related merge requests found
......@@ -316,7 +316,7 @@ def write_accessor_data_to_file(fpath: Path, acc: GenericImageDataAccessor, mkdi
raise FileWriteError(f'Unable to write data to file of extension {ext}')
return True
# TODO: deprecate and move into GenericImageFile
def generate_file_accessor(fpath):
"""
Given an image file path, return an image accessor, assuming the file is a supported format and represents
......
from fastapi import FastAPI, HTTPException
from model_server.base.session import session, InvalidPathError
from model_server.extensions.ilastik.pipelines.px_then_ob import infer_px_then_ob_model
app = FastAPI(debug=True)
......@@ -53,16 +52,6 @@ def restart_session(root: str = None) -> dict:
def list_session_log() -> list:
return session.get_log_data()
# TODO: endpoints to load image data via file
@app.get('/data/file/import')
def import_data_from_file() -> str:
pass
# TODO: endpoints to load image data via file
@app.get('/data/file/import')
def import_data_from_file():
pass
@app.get('/models')
def list_active_models():
return session.describe_loaded_models()
......@@ -49,7 +49,7 @@ def segment(p: SegmentParams) -> SegmentRecord:
steps = pipeline(
generate_file_accessor(inpath),
session.models[p.model_id]['object'],
**p,
**p.dict(),
)
outpath = session.paths['outbound_images'] / (p.model_id + '_' + inpath.stem + '.tif')
write_accessor_data_to_file(outpath, steps.last)
......@@ -66,10 +66,10 @@ def segment(p: SegmentParams) -> SegmentRecord:
def pipeline(acc_in: GenericImageDataAccessor, model: SemanticSegmentationModel, **k) -> PipelineTrace:
d = PipelineTrace()
d['input'] = acc_in
if ch := k.get('channel'):
if ch := k.get('channel') is not None:
d['mono'] = acc_in.get_mono(ch)
d['inference'] = model.label_pixel_class(d.last)
if sm := k.get('smooth'):
if sm := k.get('smooth') is not None:
d['smooth'] = d.last.apply(lambda x: smooth(x, sm))
d['output'] = d.last
return d
\ No newline at end of file
......@@ -131,8 +131,6 @@ class _Session(object):
def log_error(self, msg):
logger.error(msg)
# TODO: load, describe, unload data accessors
def load_model(self, ModelClass: Model, params: Union[BaseModel, None] = None) -> dict:
"""
Load an instance of a given model class and attach to this session's model registry
......
......@@ -5,7 +5,7 @@ from pathlib import Path
from typing import Dict
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, validator
from pydantic import BaseModel, Field
from model_server.base.session import session
from model_server.base.util import PipelineTrace
......@@ -17,7 +17,15 @@ router = APIRouter(
prefix='/pipelines',
)
class Record(BaseModel):
class PxThenObParams(BaseModel):
px_model_id: str = Field(description='ID of model for pixel classification')
ob_model_id: str = Field(description='ID of model for object classification')
input_filename: str
channel: int = Field(None, description='Image channel to pass to pixel classification, or all channels if empty.')
mip: bool = Field(False, description='Use maximum intensity projection of input image if True')
class PxThenObRecord(BaseModel):
pixel_model_id: str
object_model_id: str
input_filepath: str
......@@ -26,16 +34,12 @@ class Record(BaseModel):
success: bool
timer_results: Dict[str, float]
class FileCallParams(BaseModel):
px_model_id: str
ob_model_id: str
input_filename: str
channel: int = None
mip: bool = False
@router.put('/pixel_then_object_classification/infer')
def infer_px_then_ob_maps(p: FileCallParams) -> Record:
def pixel_then_object_classification(p: PxThenObParams) -> PxThenObRecord:
"""
Workflow that specifically runs an ilastik pixel classifier, then passes results to an object classifier,
saving intermediate images
"""
def _validate(model_ids, inpaths):
for mid in model_ids:
......@@ -54,7 +58,7 @@ def infer_px_then_ob_maps(p: FileCallParams) -> Record:
inpath = session.paths['inbound_images'] / p.input_filename
_validate([p.px_model_id, p.ob_model_id], [inpath])
try:
record = infer_px_then_ob_model(
record = pipeline(
inpath,
session.models[p.px_model_id]['object'],
session.models[p.ob_model_id]['object'],
......@@ -68,27 +72,18 @@ def infer_px_then_ob_maps(p: FileCallParams) -> Record:
return record
def infer_px_then_ob_model(
def pipeline(
fpi: Path,
px_model: IlastikPixelClassifierModel,
ob_model: IlastikObjectClassifierFromPixelPredictionsModel,
where_output: Path,
channel: int = None,
**kwargs
) -> Record:
"""
Workflow that specifically runs an ilastik pixel classifier, then passes results to an object classifier,
saving intermediate images
:param fpi: Path object that references input image file
:param px_model: model instance for pixel classification
:param ob_model: model instance for object classification
:param where_output: Path object that references output image directory
:param channel: input image channel to pass to pixel classification, or all channels if None
:param kwargs: variable-length keyword arguments
:return:
"""
assert isinstance(px_model, IlastikPixelClassifierModel)
assert isinstance(ob_model, IlastikObjectClassifierFromPixelPredictionsModel)
) -> PxThenObRecord:
assert isinstance(px_model, IlastikPixelClassifierModel), \
'Expecting an ilastik pixel classification model'
assert isinstance(ob_model, IlastikObjectClassifierFromPixelPredictionsModel), \
'Expecting an ilastik object classification from pixel predictions model'
d = PipelineTrace()
raw_acc = generate_file_accessor(fpi)
......@@ -108,7 +103,7 @@ def infer_px_then_ob_model(
ob_map_path = where_output / (ob_model.model_id + '_obmap_' + fpi.stem + '.tif')
write_accessor_data_to_file(ob_map_path, d['ob_map'])
return Record(
return PxThenObRecord(
pixel_model_id=px_model.model_id,
object_model_id=ob_model.model_id,
input_filepath=str(fpi),
......
......@@ -4,9 +4,9 @@ import numpy as np
from model_server.base.accessors import CziImageFileAccessor, generate_file_accessor, InMemoryDataAccessor, PatchStack, write_accessor_data_to_file
from model_server.extensions.ilastik import models as ilm
from model_server.extensions.ilastik.pipelines.px_then_ob import infer_px_then_ob_model
from model_server.extensions.ilastik.pipelines import px_then_ob
from model_server.base.roiset import get_label_ids, RoiSet, RoiSetMetaParams
from model_server.base.pipelines.segment import classify_pixels
from model_server.base.pipelines import segment
import model_server.conf.testing as conf
data = conf.meta['image_files']
......@@ -180,7 +180,7 @@ class TestIlastikPixelClassification(unittest.TestCase):
self.assertEqual(objmap.data.max(), 2)
def test_ilastik_pixel_classification_as_workflow(self):
res = classify_pixels(
res = segment.pipeline(
generate_file_accessor(czifile['path']),
ilm.IlastikPixelClassifierModel(
params=ilm.IlastikPixelClassifierParams(project_file=ilastik_classifiers['px']['path'].__str__()),
......@@ -342,7 +342,7 @@ class TestIlastikOnMultichannelInputs(conf.TestServerBaseClass):
self.assertEqual(obmap.nz, img.nz)
def _call_workflow(self, channel):
return infer_px_then_ob_model(
return px_then_ob.pipeline(
self.pa_input_image,
ilm.IlastikPixelClassifierModel(
ilm.IlastikParams(project_file=self.pa_px_classifier.__str__()),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment