Source code for monailabel.interfaces.utils.transform
# Copyright (c) MONAI Consortium
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import time
from monai.transforms import Compose
from monailabel.interfaces.exception import MONAILabelError, MONAILabelException
logger = logging.getLogger(__name__)
[docs]def dump_data(data, level=logging.DEBUG):
if data and logging.getLogger().level == level:
logger.log(level, "**************************** DATA ********************************************")
for k in data:
v = data[k]
logger.log(
level,
"Data key: {} = {}".format(
k,
(
v.shape
if hasattr(v, "shape")
else v if type(v) in (int, float, bool, str, dict, tuple, list) else type(v)
),
),
)
logger.log(level, "******************************************************************************")
[docs]def shape_info(
data,
keys=(
"image",
"label",
"logits",
"pred",
"model",
"points",
"horizontal_vertical",
"nucleus_prediction",
"type_prediction",
),
):
info = []
for key in keys:
val = data.get(key) if data and hasattr(data, "get") else None
if val is not None and hasattr(val, "shape"):
info.append(f"{key}: {val.shape}({val.dtype})")
return "; ".join(info)
[docs]def run_transforms(data, callables, inverse=False, log_prefix="POST", log_name="Transform", use_compose=False):
"""
Run Transforms
:param data: Input data dictionary
:param callables: List of transforms or callable objects
:param inverse: Run inverse instead of call/forward function
:param log_prefix: Logging prefix (POST or PRE)
:param log_name: Type of callables for logging
:param use_compose: Use Compose to run individual callables
:return: Processed data after running transforms
"""
logger.setLevel(data.get("logging", "INFO").upper())
logger.info(f"{log_prefix} - Run {log_name}(s)")
logger.info(f"{log_prefix} - Input Keys: {list(data.keys())}")
if not callables:
return data
compose = Compose()
if isinstance(callables, Compose):
callables = callables.transforms
elif callable(callables):
callables = [callables]
for t in callables:
name = t.__class__.__name__
start = time.time()
dump_data(data)
if inverse:
if hasattr(t, "inverse"):
data = t.inverse(data)
else:
raise MONAILabelException(
MONAILabelError.TRANSFORM_ERROR,
f"{log_name} '{t.__class__.__name__}' has no invert method",
)
elif callable(t):
if use_compose:
compose.transforms = [t]
data = compose(data)
else:
data = t(data)
else:
raise MONAILabelException(
MONAILabelError.TRANSFORM_ERROR,
f"{log_name} '{t.__class__.__name__}' is not callable",
)
latency = round(time.time() - start, 4)
if data:
stage = log_prefix.lower()
if data.get("latencies") is None:
data["latencies"] = {}
if data["latencies"].get(stage) is None:
data["latencies"][stage] = {}
data["latencies"][stage][name] = latency
logger.info(f"{log_prefix} - {log_name} ({name}): Time: {latency}; {shape_info(data)}")
logger.debug("-----------------------------------------------------------------------------")
dump_data(data)
return data