Esempio n. 1
0
def app_instance(app_dir=None, studies=None, conf=None):
    app_dir = app_dir if app_dir else settings.MONAI_LABEL_APP_DIR
    studies = studies if studies else settings.MONAI_LABEL_STUDIES
    cache_key = f"{app_dir}{studies}"

    global apps
    app = apps.get(cache_key)
    if app is not None:
        return app

    conf = conf if conf else settings.MONAI_LABEL_APP_CONF
    logger.info(
        f"Initializing App from: {app_dir}; studies: {studies}; conf: {conf}")

    main_py = os.path.join(app_dir, "main.py")
    if not os.path.exists(main_py):
        raise MONAILabelException(MONAILabelError.APP_INIT_ERROR,
                                  "App Does NOT have main.py")

    c = get_class_of_subclass_from_file("main", main_py, "MONAILabelApp")
    if c is None:
        raise MONAILabelException(
            MONAILabelError.APP_INIT_ERROR,
            "App Does NOT Implement MONAILabelApp in main.py",
        )

    app = c(app_dir=app_dir, studies=studies, conf=conf)
    apps[cache_key] = app
    return app
Esempio n. 2
0
def init_class(class_path, class_args):
    if "." not in class_path:
        raise MONAILabelException(
            MONAILabelError.CLASS_INIT_ERROR, "Class path need to be in the form [module/file].[class_name]."
        )
    module_name, class_name = class_path.rsplit(".", 1)

    m = importlib.import_module(module_name)
    importlib.reload(m)
    c = getattr(m, class_name)
    return c(**class_args) if class_args else c()
Esempio n. 3
0
    def scoring(self, request, datastore=None):
        """
        Run scoring task over labels.

        Args:
            request: JSON object which contains `model`, `params` and `device`
            datastore: Datastore object.  If None then use default app level datastore to fetch the images

                For example::

                    {
                        "device": "cuda"
                        "method": "dice",
                        "y": "final",
                        "y_pred": "original",
                    }

        Raises:
            MONAILabelException: When ``method`` is not found

        Returns:
            JSON containing result of scoring method
        """
        method = request.get("method")
        if not method:
            raise MONAILabelException(
                MONAILabelError.INVALID_INPUT,
                "Method is not provided for Scoring Task",
            )

        task = self._scoring_methods.get(method)
        if not task:
            raise MONAILabelException(
                MONAILabelError.INVALID_INPUT,
                f"Scoring Task is not Initialized. There is no such scoring method '{method}' available",
            )

        request = copy.deepcopy(request)
        return task(copy.deepcopy(request),
                    datastore if datastore else self.datastore())
Esempio n. 4
0
    def train(self, request):
        """
        Run Training.  User APP has to implement this method to run training

        Args:
            request: JSON object which contains train configs that are part APP info

                For example::

                    {
                        "model": "mytrain",
                        "device": "cuda"
                        "max_epochs": 1,
                    }

        Returns:
            JSON containing train stats
        """
        model = request.get("model")
        if not model:
            raise MONAILabelException(
                MONAILabelError.INVALID_INPUT,
                "Model is not provided for Training Task",
            )

        task = self._trainers.get(model)
        if not task:
            raise MONAILabelException(
                MONAILabelError.INVALID_INPUT,
                f"Train Task is not Initialized. There is no model '{model}' available",
            )

        request = copy.deepcopy(request)
        result = task(request, self.datastore())

        # Run all scoring methods
        if self._auto_update_scoring:
            self.async_scoring(None)
        return result
Esempio n. 5
0
    def _get_network(self, device):
        path = self.get_path()
        logger.info(f"Infer model path: {path}")
        if not path and not self.network:
            if self.type == InferType.SCRIBBLES:
                return None

            raise MONAILabelException(
                MONAILabelError.INFERENCE_ERROR,
                f"Model Path ({self.path}) does not exist/valid",
            )

        cached = self._networks.get(device)
        statbuf = os.stat(path) if path else None
        network = None
        if cached:
            if statbuf and statbuf.st_mtime == cached[1]:
                network = cached[0]
            elif statbuf:
                logger.warning(
                    f"Reload model from cache.  Prev ts: {cached[1]}; Current ts: {statbuf.st_mtime}"
                )

        if network is None:
            if self.network:
                network = copy.deepcopy(self.network)
                network.to(torch.device(device))

                if path:
                    checkpoint = torch.load(path,
                                            map_location=torch.device(device))
                    model_state_dict = checkpoint.get(self.model_state_dict,
                                                      checkpoint)
                    network.load_state_dict(model_state_dict,
                                            strict=self.load_strict)
            else:
                network = torch.jit.load(path,
                                         map_location=torch.device(device)).to(
                                             torch.device)

            network.eval()
            self._networks[device] = (network,
                                      statbuf.st_mtime if statbuf else 0)

        return network
Esempio n. 6
0
    def next_sample(self, request):
        """
        Run Active Learning selection.  User APP has to implement this method to provide next sample for labelling.

        Args:
            request: JSON object which contains active learning configs that are part APP info

                For example::

                    {
                        "strategy": "random"
                    }

        Returns:
            JSON containing next image info that is selected for labeling
        """
        strategy = request.get("strategy")
        strategy = strategy if strategy else "random"

        task = self._strategies.get(strategy)
        if task is None:
            raise MONAILabelException(
                MONAILabelError.APP_INIT_ERROR,
                f"ActiveLearning Task is not Initialized. There is no such strategy '{strategy}' available",
            )

        image_id = task(request, self.datastore())
        if not image_id:
            return {}

        image_path = self._datastore.get_image_uri(image_id)

        # Run all scoring methods
        if self._auto_update_scoring:
            self.async_scoring(None)

        return {
            "id": image_id,
            "path": image_path,
        }
Esempio n. 7
0
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",
            )

        logger.info("{} - {} ({}): Time: {:.4f}; {}".format(
            log_prefix,
            log_name,
            name,
            float(time.time() - start),
            shape_info(data),
        ))
        logger.debug(
            "-----------------------------------------------------------------------------"
        )

    dump_data(data)
    return data
Esempio n. 8
0
    def infer_wsi(self, request, datastore=None):
        model = request.get("model")
        if not model:
            raise MONAILabelException(
                MONAILabelError.INVALID_INPUT,
                "Model is not provided for WSI/Inference Task",
            )

        task = self._infers.get(model)
        if not task:
            raise MONAILabelException(
                MONAILabelError.INVALID_INPUT,
                f"WSI/Inference Task is not Initialized. There is no model '{model}' available",
            )

        img_id = request["image"]
        image = img_id
        request_c = copy.deepcopy(task.config())
        request_c.update(request)
        request = request_c

        # Possibly direct image (numpy)
        if not isinstance(image, str):
            res = self.infer(request, datastore)
            logger.info(f"Latencies: {res.get('params', {}).get('latencies')}")
            return res

        request = copy.deepcopy(request)
        if not os.path.exists(image):
            datastore = datastore if datastore else self.datastore()
            image = datastore.get_image_uri(request["image"])

        start = time.time()
        infer_tasks = create_infer_wsi_tasks(request, image)
        if len(infer_tasks) > 1:
            logger.info(f"WSI Infer Request (final): {request}")

        logger.debug(f"Total WSI Tasks: {len(infer_tasks)}")
        request["logging"] = request.get(
            "logging", "WARNING" if len(infer_tasks) > 1 else "INFO")

        multi_gpu = request.get("multi_gpu", True)
        multi_gpus = request.get("gpus", "all")
        gpus = (list(range(torch.cuda.device_count())) if not multi_gpus
                or multi_gpus == "all" else multi_gpus.split(","))
        device_ids = [f"cuda:{id}" for id in gpus
                      ] if multi_gpu else [request.get("device", "cuda")]

        res_json = {"annotations": [None] * len(infer_tasks)}
        for idx, t in enumerate(infer_tasks):
            t["logging"] = request["logging"]
            t["device"] = (device_ids[idx % len(device_ids)] if
                           len(infer_tasks) > 1 else device_ids[random.randint(
                               0,
                               len(device_ids) - 1)])

        total = len(infer_tasks)
        max_workers = request.get("max_workers", len(device_ids))

        if len(infer_tasks) > 1 and (max_workers == 0 or max_workers > 1):
            logger.info(
                f"MultiGpu: {multi_gpu}; Using Device(s): {device_ids}; Max Workers: {max_workers}"
            )
            futures = {}
            with ThreadPoolExecutor(max_workers if max_workers else None,
                                    "WSI Infer") as executor:
                for t in infer_tasks:
                    futures[t["id"]] = t, executor.submit(
                        self._run_infer_wsi_task, t)

                for tid, (t, future) in futures.items():
                    res = future.result()
                    res_json["annotations"][tid] = res
                    finished = len([a for a in res_json["annotations"] if a])
                    logger.info(
                        f"{img_id} => {tid} => {t['device']} => {finished} / {total}; Latencies: {res.get('latencies')}"
                    )
        else:
            for t in infer_tasks:
                tid = t["id"]
                res = self._run_infer_wsi_task(t)
                res_json["annotations"][tid] = res
                finished = len([a for a in res_json["annotations"] if a])
                logger.info(
                    f"{img_id} => {tid} => {t['device']} => {finished} / {total}; Latencies: {res.get('latencies')}"
                )

        latency_total = time.time() - start
        logger.debug(f"WSI Infer Time Taken: {latency_total:.4f}")

        res_json["name"] = f"MONAILabel Annotations - {model}"
        res_json["description"] = task.description
        res_json["model"] = request.get("model")
        res_json["location"] = request.get("location")
        res_json["size"] = request.get("size")
        res_json["latencies"] = {"total": round(latency_total, 2)}

        res_file = None
        output = request.get("output", "dsa")
        logger.debug(f"+++ WSI Inference Output Type: {output}")

        loglevel = request.get("logging", "INFO").upper()
        if output == "asap":
            logger.debug("+++ Generating ASAP XML Annotation")
            res_file = create_asap_annotations_xml(res_json, loglevel)
        elif output == "dsa":
            logger.debug("+++ Generating DSA JSON Annotation")
            res_file = create_dsa_annotations_json(res_json, loglevel)
        else:
            logger.debug("+++ Return Default JSON Annotation")

        if len(infer_tasks) > 1:
            logger.info(
                f"Total Time Taken: {time.time() - start:.4f}; Total Infer Time: {latency_total:.4f}"
            )
        return {"file": res_file, "params": res_json}
Esempio n. 9
0
    def infer(self, request, datastore=None):
        """
        Run Inference for an exiting pre-trained model.

        Args:
            request: JSON object which contains `model`, `image`, `params` and `device`
            datastore: Datastore object.  If None then use default app level datastore to save labels if applicable

                For example::

                    {
                        "device": "cuda"
                        "model": "segmentation_spleen",
                        "image": "file://xyz",
                        "save_label": "true/false",
                        "label_tag": "original"
                    }

        Raises:
            MONAILabelException: When ``model`` is not found

        Returns:
            JSON containing `label` and `params`
        """
        model = request.get("model")
        if not model:
            raise MONAILabelException(
                MONAILabelError.INVALID_INPUT,
                "Model is not provided for Inference Task",
            )

        task = self._infers.get(model)
        if not task:
            raise MONAILabelException(
                MONAILabelError.INVALID_INPUT,
                f"Inference Task is not Initialized. There is no model '{model}' available",
            )

        request = copy.deepcopy(request)
        request["description"] = task.description

        image_id = request["image"]
        datastore = datastore if datastore else self.datastore()
        if os.path.exists(image_id):
            request["save_label"] = False
        else:
            request["image"] = datastore.get_image_uri(request["image"])

        # TODO:: BUG In MONAI? Currently can not load DICOM through ITK Loader
        if os.path.isdir(request["image"]):
            logger.info("Input is a Directory; Consider it as DICOM")
            logger.info(os.listdir(request["image"]))
            request["image"] = [
                os.path.join(f, request["image"])
                for f in os.listdir(request["image"])
            ]

        logger.debug(f"Image => {request['image']}")
        if self._infers_threadpool:

            def run_infer_in_thread(t, r):
                return t(r)

            f = self._infers_threadpool.submit(run_infer_in_thread,
                                               t=task,
                                               r=request)
            result_file_name, result_json = f.result(
                request.get("timeout", settings.MONAI_LABEL_INFER_TIMEOUT))
        else:
            result_file_name, result_json = task(request)

        label_id = None
        if result_file_name and os.path.exists(result_file_name):
            tag = request.get("label_tag", DefaultLabelTag.ORIGINAL)
            save_label = request.get("save_label", False)
            if save_label:
                label_id = datastore.save_label(image_id, result_file_name,
                                                tag, dict())
            else:
                label_id = result_file_name

        return {
            "label": label_id,
            "tag": DefaultLabelTag.ORIGINAL,
            "file": result_file_name,
            "params": result_json
        }