Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nucleus Detection gives error #844

Closed
ankana999 opened this issue Aug 15, 2024 · 1 comment
Closed

Nucleus Detection gives error #844

ankana999 opened this issue Aug 15, 2024 · 1 comment

Comments

@ankana999
Copy link

  • TIA Toolbox version:
  • Python version:
  • Operating System:
    tiatoolbox 1.5.1 (Python 3.10.12) on Linux-5.15.0-118-generic-x86_64-with-glibc2.35.

I am trying to use the pretrained nucleus detection model with conic dataset and here is my code.
It's essentially the same code from the website almost. I am running it in my machine and it's giving me error.

I have ndpi images for colon biopsy slides. I want to predict different kind of cells using this code but I am struggling to figure out where I am going wrong.


import logging
import warnings

if logging.getLogger().hasHandlers():
    logging.getLogger().handlers.clear()

import cv2
import joblib
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

from tiatoolbox import logger
from tiatoolbox.models.engine.nucleus_instance_segmentor import NucleusInstanceSegmentor
from tiatoolbox.utils.misc import download_data, imread


# We need this function to visualize the nuclear predictions
from tiatoolbox.utils.visualization import (
    overlay_prediction_contours,
)
from tiatoolbox.wsicore.wsireader import WSIReader

warnings.filterwarnings("ignore")
mpl.rcParams["figure.dpi"] = 300  # for high resolution figure in notebook
mpl.rcParams["figure.facecolor"] = "white"  # To make sure text is visible in dark mode
plt.rcParams.update({"font.size": 5})

from tiatoolbox.models import IOPatchPredictorConfig
ioconfig = IOPatchPredictorConfig(
    margin= 128,
    tile_shape= [256,256],
    patch_input_shape=(31, 31),
    stride_shape=(8, 8),
    input_resolutions=[{"resolution": 0.25, "units": "mpp"}]
)

# Instantiate the nucleus instance segmentor
inst_segmentor = NucleusInstanceSegmentor(
    pretrained_model="mapde-conic",
    num_loader_workers=0,
    num_postproc_workers=0,
    batch_size=4,
    auto_generate_mask=False,
    verbose=True,
)

import os
from CoNICmain.misc.utils import rmdir,recur_find_ext
OUT_DIR = '/mnt/nas/Ankana/AI_pred_Ankana/CoNIC_annotated_images/segmented_output/sample_wsi_results/'
wsi_file_name='/mnt/nas/Ankana/AI_pred_Ankana/CoNIC_annotated_images/raw_data/SUSI-493-V0-HE-01-01 - 2024-07-29 17.19.56.ndpi'
rmdir(OUT_DIR)
print(wsi_file_name)
print(os.path.exists(OUT_DIR))

# WSI prediction
# if ON_GPU=False, this part will take more than a couple of hours to process.
wsi_output = inst_segmentor.predict(
    [wsi_file_name],
    ioconfig=ioconfig,
    masks=None,
    save_dir=OUT_DIR,
    mode="wsi",
    on_gpu=True,
    crash_on_exception=True,
)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Cell In[18], line 3
      1 # WSI prediction
      2 # if ON_GPU=False, this part will take more than a couple of hours to process.
----> 3 wsi_output = inst_segmentor.predict(
      4     [wsi_file_name],
      5     ioconfig=ioconfig,
      6     masks=None,
      7     save_dir=OUT_DIR,
      8     mode="wsi",
      9     on_gpu=ON_GPU,
     10     crash_on_exception=True,
     11 )

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/engine/semantic_segmentor.py:1399, in SemanticSegmentor.predict(self, imgs, masks, mode, ioconfig, patch_input_shape, patch_output_shape, stride_shape, resolution, units, save_dir, on_gpu, crash_on_exception)
   1396 # ? what will happen if this crash midway?
   1397 # => may not be able to retrieve the result dict
   1398 for wsi_idx, img_path in enumerate(imgs):
-> 1399     self._predict_wsi_handle_exception(
   1400         imgs=imgs,
   1401         wsi_idx=wsi_idx,
   1402         img_path=img_path,
   1403         mode=mode,
   1404         ioconfig=ioconfig,
   1405         save_dir=save_dir,
   1406         crash_on_exception=crash_on_exception,
   1407     )
   1409 # clean up the cache directories
   1410 try:

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/engine/semantic_segmentor.py:1249, in SemanticSegmentor._predict_wsi_handle_exception(self, imgs, wsi_idx, img_path, mode, ioconfig, save_dir, crash_on_exception)
   1247 wsi_save_path = save_dir.joinpath(f"{wsi_idx}")
   1248 if crash_on_exception:
-> 1249     raise err  # noqa: TRY201
   1250 logging.exception("Crashed on %s", wsi_save_path)

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/engine/semantic_segmentor.py:1225, in SemanticSegmentor._predict_wsi_handle_exception(self, imgs, wsi_idx, img_path, mode, ioconfig, save_dir, crash_on_exception)
   1223 try:
   1224     wsi_save_path = save_dir / f"{wsi_idx}"
-> 1225     self._predict_one_wsi(wsi_idx, ioconfig, str(wsi_save_path), mode)
   1227     # Do not use dict with file name as key, because it can be
   1228     # overwritten. It may be user intention to provide files with a
   1229     # same name multiple times (maybe they have different root path)
   1230     self._outputs.append([str(img_path), str(wsi_save_path)])

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/engine/nucleus_instance_segmentor.py:743, in NucleusInstanceSegmentor._predict_one_wsi(self, wsi_idx, ioconfig, save_path, mode)
    740     tile_patch_outputs = patch_outputs[sel_indices]
    741     self._to_shared_space(wsi_idx, tile_patch_inputs, tile_patch_outputs)
--> 743     tile_infer_output = self._infer_once()
    745     self._process_tile_predictions(
    746         ioconfig,
    747         tile_bounds,
   (...)
    750         tile_infer_output,
    751     )
    753 self._merge_post_process_results()

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/engine/nucleus_instance_segmentor.py:645, in NucleusInstanceSegmentor._infer_once(self)
    638 batch_size = sample_infos.shape[0]
    639 # ! depending on the protocol of the output within infer_batch
    640 # ! this may change, how to enforce/document/expose this in a
    641 # ! sensible way?
    642 
    643 # assume to return a list of L output,
    644 # each of shape N x etc. (N=batch size)
--> 645 sample_outputs = self.model.infer_batch(
    646     self._model,
    647     sample_datas,
    648     on_gpu=self._on_gpu,
    649 )
    650 # repackage so that it's a N list, each contains
    651 # L x etc. output
    652 sample_outputs = [np.split(v, batch_size, axis=0) for v in sample_outputs]

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/architecture/mapde.py:291, in MapDe.infer_batch(model, batch_data, on_gpu)
    288 model.eval()  # infer mode
    290 with torch.inference_mode():
--> 291     pred = model(patch_imgs_gpu)
    293 pred = pred.permute(0, 2, 3, 1).contiguous()
    294 pred = pred.cpu().numpy()

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/torch/nn/modules/module.py:1518, in Module._wrapped_call_impl(self, *args, **kwargs)
   1516     return self._compiled_call_impl(*args, **kwargs)  # type: ignore[misc]
   1517 else:
-> 1518     return self._call_impl(*args, **kwargs)

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/torch/nn/modules/module.py:1527, in Module._call_impl(self, *args, **kwargs)
   1522 # If we don't have any hooks, we want to skip the rest of the logic in
   1523 # this function, and just call forward.
   1524 if not (self._backward_hooks or self._backward_pre_hooks or self._forward_hooks or self._forward_pre_hooks
   1525         or _global_backward_pre_hooks or _global_backward_hooks
   1526         or _global_forward_hooks or _global_forward_pre_hooks):
-> 1527     return forward_call(*args, **kwargs)
   1529 try:
   1530     result = None

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/torch/nn/parallel/data_parallel.py:183, in DataParallel.forward(self, *inputs, **kwargs)
    180     module_kwargs = ({},)
    182 if len(self.device_ids) == 1:
--> 183     return self.module(*inputs[0], **module_kwargs[0])
    184 replicas = self.replicate(self.module, self.device_ids[:len(inputs)])
    185 outputs = self.parallel_apply(replicas, inputs, module_kwargs)

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/torch/nn/modules/module.py:1518, in Module._wrapped_call_impl(self, *args, **kwargs)
   1516     return self._compiled_call_impl(*args, **kwargs)  # type: ignore[misc]
   1517 else:
-> 1518     return self._call_impl(*args, **kwargs)

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/torch/nn/modules/module.py:1527, in Module._call_impl(self, *args, **kwargs)
   1522 # If we don't have any hooks, we want to skip the rest of the logic in
   1523 # this function, and just call forward.
   1524 if not (self._backward_hooks or self._backward_pre_hooks or self._forward_hooks or self._forward_pre_hooks
   1525         or _global_backward_pre_hooks or _global_backward_hooks
   1526         or _global_forward_hooks or _global_forward_pre_hooks):
-> 1527     return forward_call(*args, **kwargs)
   1529 try:
   1530     result = None

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/architecture/mapde.py:228, in MapDe.forward(self, input_tensor)
    213 def forward(self: MapDe, input_tensor: torch.Tensor) -> torch.Tensor:
    214     """Logic for using layers defined in init.
    215 
    216     This method defines how layers are used in forward operation.
   (...)
    226 
    227     """
--> 228     logits, _, _, _ = super().forward(input_tensor)
    229     out = F.conv2d(logits, self.dist_filter, padding="same")
    230     return F.relu(out)

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/architecture/micronet.py:533, in MicroNet.forward(self, input_tensor)
    515 def forward(  # skipcq: PYL-W0221
    516     self: MicroNet,
    517     input_tensor: torch.Tensor,
    518 ) -> list[torch.Tensor, torch.Tensor, torch.Tensor]:
    519     """Logic for using layers defined in init.
    520 
    521     This method defines how layers are used in forward operation.
   (...)
    531 
    532     """
--> 533     b1 = group1_forward_branch(
    534         self.layer["b1"],
    535         input_tensor,
    536         functional.interpolate(input_tensor, size=(128, 128), mode="bicubic"),
    537     )
    538     b2 = group1_forward_branch(
    539         self.layer["b2"],
    540         b1,
    541         functional.interpolate(input_tensor, size=(64, 64), mode="bicubic"),
    542     )
    543     b3 = group1_forward_branch(
    544         self.layer["b3"],
    545         b2,
    546         functional.interpolate(input_tensor, size=(32, 32), mode="bicubic"),
    547     )

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/architecture/micronet.py:49, in group1_forward_branch(layer, in_tensor, resized_feat)
     47 b = layer["conv3"](resized_feat)
     48 b = layer["conv4"](b)
---> 49 return torch.cat(tensors=(a, b), dim=1)

RuntimeError: Sizes of tensors must match except in dimension 1. Expected size 13 but got size 124 for tensor number 1 in the list.

Is the code correct for what I am trying to do?

Similarly When I did

from tiatoolbox.models import IOPatchPredictorConfig
ioconfig = IOPatchPredictorConfig(
    margin= 128,
    tile_shape= [252,252],
    patch_input_shape=(252, 252),
    stride_shape=(150, 150),
    input_resolutions=[{"resolution": 0.25, "units": "mpp"}]
)

# Instantiate the nucleus instance segmentor
inst_segmentor = NucleusInstanceSegmentor(
    pretrained_model="mapde-conic",
    num_loader_workers=0,
    num_postproc_workers=0,
    batch_size=8,
    auto_generate_mask=False,
    verbose=True,
)

I am getting another error

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[28], line 3
      1 # WSI prediction
      2 # if ON_GPU=False, this part will take more than a couple of hours to process.
----> 3 wsi_output = inst_segmentor.predict(
      4     [wsi_file_name],
      5     ioconfig=ioconfig,
      6     masks=None,
      7     save_dir=OUT_DIR,
      8     mode="wsi",
      9     on_gpu=ON_GPU,
     10     crash_on_exception=True,
     11 )

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/engine/semantic_segmentor.py:1399, in SemanticSegmentor.predict(self, imgs, masks, mode, ioconfig, patch_input_shape, patch_output_shape, stride_shape, resolution, units, save_dir, on_gpu, crash_on_exception)
   1396 # ? what will happen if this crash midway?
   1397 # => may not be able to retrieve the result dict
   1398 for wsi_idx, img_path in enumerate(imgs):
-> 1399     self._predict_wsi_handle_exception(
   1400         imgs=imgs,
   1401         wsi_idx=wsi_idx,
   1402         img_path=img_path,
   1403         mode=mode,
   1404         ioconfig=ioconfig,
   1405         save_dir=save_dir,
   1406         crash_on_exception=crash_on_exception,
   1407     )
   1409 # clean up the cache directories
   1410 try:

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/engine/semantic_segmentor.py:1249, in SemanticSegmentor._predict_wsi_handle_exception(self, imgs, wsi_idx, img_path, mode, ioconfig, save_dir, crash_on_exception)
   1247 wsi_save_path = save_dir.joinpath(f"{wsi_idx}")
   1248 if crash_on_exception:
-> 1249     raise err  # noqa: TRY201
   1250 logging.exception("Crashed on %s", wsi_save_path)

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/engine/semantic_segmentor.py:1225, in SemanticSegmentor._predict_wsi_handle_exception(self, imgs, wsi_idx, img_path, mode, ioconfig, save_dir, crash_on_exception)
   1223 try:
   1224     wsi_save_path = save_dir / f"{wsi_idx}"
-> 1225     self._predict_one_wsi(wsi_idx, ioconfig, str(wsi_save_path), mode)
   1227     # Do not use dict with file name as key, because it can be
   1228     # overwritten. It may be user intention to provide files with a
   1229     # same name multiple times (maybe they have different root path)
   1230     self._outputs.append([str(img_path), str(wsi_save_path)])

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/engine/nucleus_instance_segmentor.py:745, in NucleusInstanceSegmentor._predict_one_wsi(self, wsi_idx, ioconfig, save_path, mode)
    741         self._to_shared_space(wsi_idx, tile_patch_inputs, tile_patch_outputs)
    743         tile_infer_output = self._infer_once()
--> 745         self._process_tile_predictions(
    746             ioconfig,
    747             tile_bounds,
    748             tile_flag,
    749             set_idx,
    750             tile_infer_output,
    751         )
    753     self._merge_post_process_results()
    754 joblib.dump(self._wsi_inst_info, f"{save_path}.dat")

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/engine/nucleus_instance_segmentor.py:780, in NucleusInstanceSegmentor._process_tile_predictions(self, ioconfig, tile_bounds, tile_flag, tile_mode, tile_output)
    778     future = self._postproc_workers.submit(_process_tile_predictions, *args)
    779 else:
--> 780     future = _process_tile_predictions(*args)
    781 self._futures.append(future)

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/engine/nucleus_instance_segmentor.py:288, in _process_tile_predictions(ioconfig, tile_bounds, tile_flag, tile_mode, tile_output, ref_inst_dict, postproc, merge_predictions)
    282     head_raw = merge_predictions(
    283         head_tile_shape[::-1],
    284         head_predictions,
    285         head_locations,
    286     )
    287     head_raws.append(head_raw)
--> 288 _, inst_dict = postproc(head_raws)
    290 new_inst_dict, remove_insts_in_orig = _process_instance_predictions(
    291     inst_dict,
    292     ioconfig,
   (...)
    297     ref_inst_dict,
    298 )
    300 return new_inst_dict, remove_insts_in_orig

File /mnt/nas/Ankana/my_jupyter_env/lib/python3.10/site-packages/tiatoolbox/models/architecture/mapde.py:249, in MapDe.postproc(self, prediction_map)
    233 def postproc(self: MapDe, prediction_map: np.ndarray) -> np.ndarray:
    234     """Post-processing script for MicroNet.
    235 
    236     Performs peak detection and extracts coordinates in x, y format.
   (...)
    246 
    247     """
    248     coordinates = peak_local_max(
--> 249         np.squeeze(prediction_map[0], axis=2),
    250         min_distance=self.min_distance,
    251         threshold_abs=self.threshold_abs,
    252         exclude_border=False,
    253     )
    254     return np.fliplr(coordinates)

IndexError: list index out of range
@shaneahmed
Copy link
Member

The NucleusInstanceSegmentor is not designed for nucleus detection. We started working on the detection engine in #538 but this has been postponed as we are redesigning the engines from scratch in #578 .
For now you can use the code from #538 to work out the solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants