Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
ljcornel committed May 21, 2024
2 parents 4ad61bc + a860f96 commit d9505d6
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 14 deletions.
1 change: 1 addition & 0 deletions geti_sdk/data_models/enums/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Domain(Enum):
ANOMALY_SEGMENTATION = "ANOMALY_SEGMENTATION"
INSTANCE_SEGMENTATION = "INSTANCE_SEGMENTATION"
ROTATED_DETECTION = "ROTATED_DETECTION"
ANOMALY = "ANOMALY"

def __str__(self) -> str:
"""
Expand Down
65 changes: 51 additions & 14 deletions geti_sdk/deployment/deployed_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import numpy as np
from model_api.adapters import OpenvinoAdapter, OVMSAdapter
from model_api.models import Model as model_api_Model
from model_api.tilers import DetectionTiler, InstanceSegmentationTiler, Tiler
from openvino.runtime import Core
from packaging.version import Version

Expand Down Expand Up @@ -63,6 +64,11 @@
SEGMENTATION_SALIENCY_KEY = "soft_prediction"
FEATURE_VECTOR_KEY = "feature_vector"

TILER_MAPPING = {
Domain.DETECTION: DetectionTiler,
Domain.INSTANCE_SEGMENTATION: InstanceSegmentationTiler,
}

OVMS_TIMEOUT = 10 # Max time to wait for OVMS models to become available


Expand Down Expand Up @@ -95,6 +101,8 @@ def __attrs_post_init__(self):
self._feature_vector_location: Optional[str] = None

self._converter: Optional[InferenceResultsToPredictionConverter] = None
self._tiling_enabled: bool = False
self._tiler: Optional[Tiler] = None

@property
def model_data_path(self) -> str:
Expand Down Expand Up @@ -288,6 +296,11 @@ def load_inference_model(
)
self._parse_label_schema_from_dict(label_dictionary)

# Load a Results-to-Prediction converter
self._converter = ConverterFactory.create_converter(
self.label_schema, configuration
)

model = model_api_Model.create_model(
model=model_adapter,
model_type=model_type,
Expand All @@ -297,10 +310,24 @@ def load_inference_model(
)
self._inference_model = model

# Load a Results-to-Prediction converter
self._converter = ConverterFactory.create_converter(
self.label_schema, configuration
)
# Extract tiling parameters, if applicable
tiling_parameters = configuration_json.get("tiling_parameters", None)

enable_tiling = False
if tiling_parameters is not None:
enable_tiling = tiling_parameters.get("enable_tiling", False)
if isinstance(enable_tiling, dict):
enable_tiling = enable_tiling.get("value", False)

if enable_tiling:
logging.info("Tiling is enabled for this model, initializing Tiler")
tiler_type = TILER_MAPPING.get(self._converter.domain, None)
if tiler_type is None:
raise ValueError(
f"Tiling is not supported for domain {self._converter.domain}"
)
self._tiler = tiler_type(model=model, execution_mode="sync")
self._tiling_enabled = True

# TODO: This is a workaround to fix the issue that causes the output blob name
# to be unset. Remove this once it has been fixed on ModelAPI side
Expand Down Expand Up @@ -563,22 +590,32 @@ def infer(self, image: np.ndarray, explain: bool = False) -> Prediction:
image
:return: Dictionary containing the model outputs
"""
preprocessed_image, metadata = self._preprocess(image)
# metadata is a dict with keys 'original_shape' and 'resized_shape'
inference_results: Dict[str, np.ndarray] = self._inference_model.infer_sync(
preprocessed_image
)
postprocessing_results = self._postprocess(inference_results, metadata=metadata)
if not self._tiling_enabled:
preprocessed_image, metadata = self._preprocess(image)
# metadata is a dict with keys 'original_shape' and 'resized_shape'
inference_results: Dict[str, np.ndarray] = self._inference_model.infer_sync(
preprocessed_image
)
postprocessing_results = self._postprocess(
inference_results, metadata=metadata
)
else:
postprocessing_results = self._tiler(image)

prediction = self._converter.convert_to_prediction(
postprocessing_results, image_shape=metadata["original_shape"]
postprocessing_results, image_shape=image.shape
)

# Add optional explainability outputs
if explain:
saliency_map, repr_vector = self._postprocess_explain_outputs(
inference_results=inference_results, metadata=metadata
)
if not self._tiling_enabled:
saliency_map, repr_vector = self._postprocess_explain_outputs(
inference_results=inference_results, metadata=metadata
)
else:
repr_vector = postprocessing_results.feature_vector
saliency_map = postprocessing_results.saliency_map

prediction.feature_vector = repr_vector
result_medium = ResultMedium(name="saliency map", type="saliency map")
result_medium.data = saliency_map
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@
class InferenceResultsToPredictionConverter(metaclass=abc.ABCMeta):
"""Interface for the converter"""

@property
@abc.abstractmethod
def domain(self) -> Domain:
"""
Return the domain for which the converter applies
:return: The task domain for which the label converter applies
"""
raise NotImplementedError

@abc.abstractmethod
def convert_to_prediction(self, predictions: NamedTuple, **kwargs) -> Prediction:
"""
Expand All @@ -68,6 +78,8 @@ class ClassificationToPredictionConverter(InferenceResultsToPredictionConverter)
:param label_schema: LabelSchema containing the label info of the task
"""

domain = Domain.CLASSIFICATION

def __init__(self, label_schema: LabelSchema):
all_labels = label_schema.get_labels(include_empty=True)
# add empty labels if only one non-empty label exits
Expand Down Expand Up @@ -119,6 +131,8 @@ class DetectionToPredictionConverter(InferenceResultsToPredictionConverter):
:param configuration: optional model configuration setting
"""

domain = Domain.DETECTION

def __init__(
self, label_schema: LabelSchema, configuration: Optional[Dict[str, Any]] = None
):
Expand Down Expand Up @@ -191,6 +205,8 @@ class RotatedRectToPredictionConverter(DetectionToPredictionConverter):
:param label_schema: LabelSchema containing the label info of the task
"""

domain = Domain.ROTATED_DETECTION

def convert_to_prediction(
self, predictions: InstanceSegmentationResult, **kwargs
) -> Prediction:
Expand Down Expand Up @@ -256,6 +272,8 @@ def convert_to_prediction(
class MaskToAnnotationConverter(InferenceResultsToPredictionConverter):
"""Converts DetectionBox Predictions ModelAPI to Prediction object."""

domain = Domain.INSTANCE_SEGMENTATION

def __init__(
self, label_schema: LabelSchema, configuration: Optional[Dict[str, Any]] = None
):
Expand Down Expand Up @@ -337,6 +355,8 @@ class SegmentationToPredictionConverter(InferenceResultsToPredictionConverter):
:param label_schema: LabelSchema containing the label info of the task
"""

domain = Domain.SEGMENTATION

def __init__(self, label_schema: LabelSchema):
self.labels = label_schema.get_labels(include_empty=False)
# NB: index=0 is reserved for the background label
Expand Down Expand Up @@ -366,6 +386,8 @@ class AnomalyToPredictionConverter(InferenceResultsToPredictionConverter):
:param label_schema: LabelSchema containing the label info of the task
"""

domain = Domain.ANOMALY

def __init__(self, label_schema: LabelSchema):
self.labels = label_schema.get_labels(include_empty=False)
self.normal_label = next(
Expand Down

0 comments on commit d9505d6

Please sign in to comment.