Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
767441d
copy code from old PR
Jiaqi-Lv Nov 6, 2025
d2a9702
preliminiary testing
Jiaqi-Lv Nov 7, 2025
44c4994
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 7, 2025
0f8d4fe
initial prototype
Jiaqi-Lv Nov 10, 2025
d42b78a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 10, 2025
b7f829c
clean up
Jiaqi-Lv Nov 10, 2025
cba5fd5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 10, 2025
f2cdcc4
update
Jiaqi-Lv Nov 11, 2025
dd99d97
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 11, 2025
c468a8b
update pipeline
Jiaqi-Lv Nov 12, 2025
14f870a
update pipeline
Jiaqi-Lv Nov 12, 2025
6e65fba
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 12, 2025
7eb916e
refactor code
Jiaqi-Lv Nov 12, 2025
8442ac2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 12, 2025
de83074
clean up
Jiaqi-Lv Nov 12, 2025
17e5422
Merge branch 'dev-define-engines-abc' into dev-define-nucleus-detecti…
shaneahmed Nov 17, 2025
f5b1885
update patch mode processing
Jiaqi-Lv Nov 22, 2025
551e43c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 22, 2025
12f985a
tidy up code
Jiaqi-Lv Nov 22, 2025
05b2c7d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 22, 2025
2afbf8c
fix precommit
Jiaqi-Lv Nov 23, 2025
367295d
update test
Jiaqi-Lv Nov 23, 2025
7912abe
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 23, 2025
0a72e8b
improve tests
Jiaqi-Lv Nov 24, 2025
f8b4189
improve tests
Jiaqi-Lv Nov 24, 2025
228731c
precommit
Jiaqi-Lv Nov 24, 2025
6c26a0f
fix deepsource
Jiaqi-Lv Nov 25, 2025
7ffea5b
fix deepsource
Jiaqi-Lv Nov 27, 2025
79fc088
Merge branch 'dev-define-engines-abc' into dev-define-nucleus-detecti…
shaneahmed Dec 1, 2025
198982f
Merge branch 'dev-define-engines-abc' into dev-define-nucleus-detecti…
shaneahmed Dec 5, 2025
884bdf0
Merge branch 'dev-define-engines-abc' into dev-define-nucleus-detecti…
shaneahmed Dec 5, 2025
d63a7cc
refactor code and improve typing
Jiaqi-Lv Dec 10, 2025
a90f748
add test for map_overlap postprocessing
Jiaqi-Lv Dec 10, 2025
2e004f1
refactor postprocessing and saving
Jiaqi-Lv Dec 11, 2025
3638985
update save as zarr
Jiaqi-Lv Dec 11, 2025
af6d2c1
improve test coverage
Jiaqi-Lv Dec 11, 2025
1974b2f
improve tests and address comments
Jiaqi-Lv Dec 12, 2025
775e6a1
fix tests
Jiaqi-Lv Dec 12, 2025
b5f9c7d
:wrench: Add `run` function
shaneahmed Dec 12, 2025
86e809d
use smaller wsi for testing
Jiaqi-Lv Dec 12, 2025
abf02f6
reduce code complexity
Jiaqi-Lv Dec 12, 2025
5428618
add run params
Jiaqi-Lv Dec 12, 2025
dd74781
rename function
Jiaqi-Lv Dec 15, 2025
89505ae
:construction: Review and minor changes.
shaneahmed Dec 15, 2025
f14e4cb
fix tests
Jiaqi-Lv Dec 15, 2025
54a6447
fix tests
Jiaqi-Lv Dec 15, 2025
656dbb9
fix tests
Jiaqi-Lv Dec 15, 2025
10d09bd
update post processing and saving
Jiaqi-Lv Dec 15, 2025
b58dd88
fix tests
Jiaqi-Lv Dec 15, 2025
d2eae54
update model postproc function
Jiaqi-Lv Dec 15, 2025
d44d943
fix deepsource
Jiaqi-Lv Dec 15, 2025
b875e97
update RunParams
Jiaqi-Lv Dec 15, 2025
8e5ccfc
remove redundant code
Jiaqi-Lv Dec 16, 2025
4f99f74
:construction: Minor changes to patch output
shaneahmed Dec 16, 2025
36cd0e9
:construction: Rearrange order of tests
shaneahmed Dec 16, 2025
5ac5fd0
:construction: Add __init__
shaneahmed Dec 16, 2025
780740e
:memo: Update docstrings for module and functions
shaneahmed Dec 16, 2025
66a5638
update docstring
Jiaqi-Lv Dec 16, 2025
3c02c00
add cli
Jiaqi-Lv Dec 16, 2025
bca25eb
:pencil2: Fix typos and minor issues
shaneahmed Dec 17, 2025
af46446
Merge branch 'dev-define-engines-abc' into dev-define-nucleus-detecti…
shaneahmed Dec 18, 2025
050e6a8
initial update
Jiaqi-Lv Dec 18, 2025
bffaabd
update WSI postprocessing
Jiaqi-Lv Dec 18, 2025
6acf6f3
remove unused code
Jiaqi-Lv Dec 18, 2025
57191b1
remove unnecessary logging
Jiaqi-Lv Dec 18, 2025
7db6457
:white_check_mark: Address review comments
shaneahmed Dec 19, 2025
0be5bfc
Merge branch 'dev-define-engines-abc' into dev-define-nucleus-detecti…
shaneahmed Dec 19, 2025
b3b5642
replace cache_dir with temp dir
Jiaqi-Lv Dec 19, 2025
87a8265
merge
Jiaqi-Lv Dec 19, 2025
1bbc7e6
merge nucleus_detector
Jiaqi-Lv Dec 19, 2025
6da7b55
Merge remote-tracking branch 'origin/dev-define-engines-abc' into dev…
Jiaqi-Lv Jan 9, 2026
602deca
add midog config
Jiaqi-Lv Jan 9, 2026
4c9573d
Merge branch 'dev-define-engines-abc' into dev-define-KongNet-model
Jiaqi-Lv Jan 22, 2026
1824ab0
Update KongNet config and tests
Jiaqi-Lv Jan 22, 2026
579d1ef
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 22, 2026
5149e30
fix precommit and update docs
Jiaqi-Lv Jan 26, 2026
35e9ad5
fix typing
Jiaqi-Lv Jan 26, 2026
47fba14
fix deepsource
Jiaqi-Lv Jan 26, 2026
b6d119e
update notebook
Jiaqi-Lv Jan 27, 2026
fe7f9bd
Merge branch 'dev-define-engines-abc' into dev-define-KongNet-model
Jiaqi-Lv Jan 29, 2026
fc60b80
Merge branch 'dev-define-KongNet-model' of https://github.com/TissueI…
Jiaqi-Lv Jan 29, 2026
e521ca8
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 29, 2026
bcc6c7d
add to notebook
Jan 29, 2026
e929853
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 29, 2026
91e480c
remove test.py
Jiaqi-Lv Jan 29, 2026
3fc0439
update notebook
Jiaqi-Lv Jan 29, 2026
6e3858c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 29, 2026
032a4f1
Merge branch 'dev-define-engines-abc' into dev-define-KongNet-model
Jiaqi-Lv Feb 2, 2026
0095898
update notebook
Jiaqi-Lv Feb 2, 2026
7b29006
address comments
Jiaqi-Lv Feb 2, 2026
09f0a6a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 2, 2026
4b9eb2f
update notebook
Jiaqi-Lv Feb 2, 2026
5fec7f4
address comments
Jiaqi-Lv Feb 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/images/nucleus_detector.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,384 changes: 1,384 additions & 0 deletions examples/nucleus-detection.ipynb

Large diffs are not rendered by default.

436 changes: 436 additions & 0 deletions tests/models/test_arch_kongnet.py

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions tests/models/test_arch_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import torch

from tiatoolbox.models.architecture.utils import (
AttentionModule,
UpSample2x,
centre_crop,
centre_crop_to_shape,
nms_on_detection_maps,
peak_detection_map_overlap,
)

Expand Down Expand Up @@ -160,3 +162,38 @@ def test_peak_detection_map_overlap() -> None:
assert peak_map[0, 0, 0] == 1.0
assert peak_map[3, 3, 0] == 1.0
assert np.sum(peak_map) == 2.0


def test_nms_on_detection_maps() -> None:
"""Test for NMS on detection maps."""
heatmap = np.zeros((7, 7, 3), dtype=np.float32)
nms_map = nms_on_detection_maps(heatmap, min_distance=3)
assert np.sum(nms_map) == 0.0 # No peaks

heatmap[0, 0, 0] = 0.9 # Peak in channel 0 (valid)
heatmap[0, 1, 0] = 0.6 # Peak in channel 0 (suppressed)
heatmap[0, 0, 1] = 0.8 # Peak in channel 1 (suppressed)

heatmap[5, 5, 2] = 0.9 # Peak in channel 2 (valid)
heatmap[4, 4, 1] = 0.7 # Peak in channel 1 (suppressed)

nms_map = nms_on_detection_maps(heatmap, min_distance=3)
assert nms_map[0, 0, 0] == 0.9
assert nms_map[5, 5, 2] == 0.9


def test_attention_module() -> None:
"""Test for Attention module."""
test_input = torch.zeros((1, 16, 32, 32), dtype=torch.float32)

# Default to identity
attention = AttentionModule(name=None, in_channels=16)
output = attention(test_input)
assert torch.sum(output - test_input) == 0

attention = AttentionModule(name="scse", in_channels=16, reduction=4)
output = attention(test_input)
assert output.shape == test_input.shape

with pytest.raises(ValueError, match=r"Attention random_name is not implemented"):
_ = AttentionModule(name="random_name", in_channels=16)
184 changes: 184 additions & 0 deletions tiatoolbox/data/pretrained_model.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -964,3 +964,187 @@ grandqc_tissue_detection:
patch_output_shape: [512, 512]
stride_shape: [256, 256]
save_resolution: {'units': 'mpp', 'resolution': 10.0}

KongNet_CoNIC_1:
hf_repo_id: TIACentre/KongNet_pretrained_weights
architecture:
class: kongnet.KongNet
kwargs:
num_heads: 6
num_channels_per_head: [3, 3, 3, 3, 3, 3]
target_channels: [2, 5, 8, 11, 14, 17]
wide_decoder: False
min_distance: 5
threshold_abs: 0.5
class_dict: {
0: "Neutrophil",
1: "Epithelial",
2: "Lymphocyte",
3: "Plasma",
4: "Eosinophil",
5: "Connective",
}
postproc_tile_shape: [2048, 2048]
ioconfig:
class: io_config.IOSegmentorConfig
kwargs:
input_resolutions:
- {'units': 'mpp', 'resolution': 0.5}
output_resolutions:
- {'units': 'mpp', 'resolution': 0.5}
patch_input_shape: [256, 256]
patch_output_shape: [256, 256]
stride_shape: [248, 248]
save_resolution: {'units': 'baseline', 'resolution': 1.0}

KongNet_PanNuke_1:
hf_repo_id: TIACentre/KongNet_pretrained_weights
architecture:
class: kongnet.KongNet
kwargs:
num_heads: 6
num_channels_per_head: [3, 3, 3, 3, 3, 3]
target_channels: [5, 8, 11, 14, 17]
wide_decoder: False
min_distance: 11
threshold_abs: 0.5
class_dict: {
0: "Neoplastic",
1: "Inflammatory",
2: "Connective",
3: "Dead",
4: "Epithelial",
}
postproc_tile_shape: [2048, 2048]
ioconfig:
class: io_config.IOSegmentorConfig
kwargs:
input_resolutions:
- {'units': 'mpp', 'resolution': 0.25}
output_resolutions:
- {'units': 'mpp', 'resolution': 0.25}
patch_input_shape: [256, 256]
patch_output_shape: [256, 256]
stride_shape: [240, 240]
save_resolution: {'units': 'baseline', 'resolution': 1.0}

KongNet_MONKEY_1:
hf_repo_id: TIACentre/KongNet_pretrained_weights
architecture:
class: kongnet.KongNet
kwargs:
num_heads: 3
num_channels_per_head: [3, 3, 3]
target_channels: [2, 5, 8]
wide_decoder: True
min_distance: 11
threshold_abs: 0.5
class_dict: {
0: "Overall_Inflammatory",
1: "Lymphocyte",
2: "Monocyte",
}
postproc_tile_shape: [2048, 2048]
ioconfig:
class: io_config.IOSegmentorConfig
kwargs:
input_resolutions:
- {'units': 'mpp', 'resolution': 0.25}
output_resolutions:
- {'units': 'mpp', 'resolution': 0.25}
patch_input_shape: [256, 256]
patch_output_shape: [256, 256]
stride_shape: [224, 224]
save_resolution: {'units': 'baseline', 'resolution': 1.0}

KongNet_PUMA_T1_3:
hf_repo_id: TIACentre/KongNet_pretrained_weights
architecture:
class: kongnet.KongNet
kwargs:
num_heads: 3
num_channels_per_head: [3, 3, 3]
target_channels: [2, 5, 8]
wide_decoder: False
min_distance: 13
threshold_abs: 0.5
class_dict: {
0: "Tumour_Cell",
1: "Lymphocyte",
2: "Other_Cell",
}
postproc_tile_shape: [2048, 2048]
ioconfig:
class: io_config.IOSegmentorConfig
kwargs:
input_resolutions:
- {'units': 'mpp', 'resolution': 0.25}
output_resolutions:
- {'units': 'mpp', 'resolution': 0.25}
patch_input_shape: [256, 256]
patch_output_shape: [256, 256]
stride_shape: [224, 224]
save_resolution: {'units': 'baseline', 'resolution': 1.0}

KongNet_PUMA_T2_3:
hf_repo_id: TIACentre/KongNet_pretrained_weights
architecture:
class: kongnet.KongNet
kwargs:
num_heads: 10
num_channels_per_head: [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
target_channels: [2, 5, 8, 11, 14, 17, 20, 23, 26, 29]
wide_decoder: False
min_distance: 13
threshold_abs: 0.5
class_dict: {
0: "Tumour_Cell",
1: "Lymphocyte",
2: "Plasma_Cell",
3: "Histiocyte",
4: "Melanophage",
5: "Neutrophil",
6: "Stroma_Cell",
7: "Epithelial_Cell",
8: "Endothelial_Cell",
9: "Apoptotic_Cell",
}
postproc_tile_shape: [2048, 2048]
ioconfig:
class: io_config.IOSegmentorConfig
kwargs:
input_resolutions:
- {'units': 'mpp', 'resolution': 0.25}
output_resolutions:
- {'units': 'mpp', 'resolution': 0.25}
patch_input_shape: [256, 256]
patch_output_shape: [256, 256]
stride_shape: [224, 224]
save_resolution: {'units': 'baseline', 'resolution': 1.0}

KongNet_Det_MIDOG_1:
hf_repo_id: TIACentre/KongNet_pretrained_weights
architecture:
class: kongnet.KongNet
kwargs:
num_heads: 1
num_channels_per_head: [1]
target_channels: [0]
wide_decoder: False
min_distance: 21
threshold_abs: 0.99
class_dict: {
0: "Mitotic_Figure"
}
postproc_tile_shape: [2048, 2048]
ioconfig:
class: io_config.IOSegmentorConfig
kwargs:
input_resolutions:
- {'units': 'mpp', 'resolution': 0.5}
output_resolutions:
- {'units': 'mpp', 'resolution': 0.5}
patch_input_shape: [512, 512]
patch_output_shape: [512, 512]
stride_shape: [492, 492]
save_resolution: {'units': 'baseline', 'resolution': 1.0}
71 changes: 1 addition & 70 deletions tiatoolbox/models/architecture/grandqc.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

Key Components:
---------------
- SegmentationHead:
Final layer for segmentation output.
- Conv2dReLU:
Convolutional block with BatchNorm and ReLU activation.
- DecoderBlock:
Expand Down Expand Up @@ -60,77 +58,10 @@
from torch import nn

from tiatoolbox.models.architecture.timm_efficientnet import EfficientNetEncoder
from tiatoolbox.models.architecture.utils import SegmentationHead
from tiatoolbox.models.models_abc import ModelABC


class SegmentationHead(nn.Sequential):
"""Segmentation head for UNet++ architecture.

This class defines the final segmentation layer for the UNet++ model.
It applies a convolution followed by optional upsampling and activation
to produce the segmentation output.

Attributes:
conv2d (nn.Conv2d):
Convolutional layer for feature transformation.
upsampling_layer (nn.Module):
Upsampling layer (bilinear interpolation or identity).
activation (nn.Module):
Activation function applied after upsampling.

Example:
>>> head = SegmentationHead(in_channels=64, out_channels=2)
>>> x = torch.randn(1, 64, 128, 128)
>>> output = head(x)
>>> output.shape
... torch.Size([1, 2, 128, 128])

"""

def __init__(
self: SegmentationHead,
in_channels: int,
out_channels: int,
kernel_size: int = 3,
activation: nn.Module | None = None,
upsampling: int = 1,
) -> None:
"""Initialize the SegmentationHead module.

This method sets up the segmentation head by creating a convolutional layer,
an optional upsampling layer, and an activation function. It is typically
used as the final stage in UNet++ architectures for semantic segmentation.

Args:
in_channels (int):
Number of input channels to the segmentation head.
out_channels (int):
Number of output channels (usually equal to the number of classes).
kernel_size (int):
Size of the convolution kernel. Defaults to 3.
activation (nn.Module | None):
Activation function applied after convolution. Defaults to None.
upsampling (int):
Upsampling factor applied to the output. Defaults to 1.

Raises:
ValueError:
If `kernel_size` or `upsampling` is not a positive integer.

"""
conv2d = nn.Conv2d(
in_channels, out_channels, kernel_size=kernel_size, padding=kernel_size // 2
)
upsampling_layer = (
nn.UpsamplingBilinear2d(scale_factor=upsampling)
if upsampling > 1
else nn.Identity()
)
if activation is None:
activation = nn.Identity()
super().__init__(conv2d, upsampling_layer, activation)


class Conv2dReLU(nn.Sequential):
"""Conv2d + BatchNorm + ReLU block.

Expand Down
Loading