WSITools

A third-party WSI preprocessing toolkit for patch extraction, tissue detection, annotation parsing, cross-stain registration and patch reconstruction — dormant since December 2024, and superseded for almost everything it does.

Purpose

Not one of my repositories, and the recommendation is not to adopt it. It is recorded here because it was evaluated, because the evaluation surfaced two capability gaps worth acting on, and because it is worth reading as reference code. There is no local_path because it has not been cloned.

From Jun Jiang at Mayo Clinic. Unlike most abandoned research code, this one has real provenance — three published papers used it (Frontiers in Medicine 2022 on tumour stroma reaction in HGSOC; J Pathol Inform 2021 on serous borderline ovarian tumours; J Med Imaging 2020 on automatic ink removal). That is research-grade code that produced results, which is why it is worth reading rather than dismissing.

But it has been dormant since 2024-12-10, it is TensorFlow-era in a PyTorch world, and its own README marks two of its four advertised scenarios “TODO: Incomplete”. 58 stars, 17 forks.

Licence — read before using any of it. The LICENSE file is stock GPL-3.0, verified byte-identical to the canonical FSF text. But setup.py line 15 declares license='Creative Commons Attribution-Noncommercial-Share Alike license'. Git history shows the CC string predates the existence of any LICENSE file and was never updated across two subsequent licence changes (Apache-2.0 in 2019, then GPL-3.0 in 2021), so it reads as stale metadata rather than a live dual grant. That is an inference, not a ruling — GPL-3.0 is copyleft and CC BY-NC-SA is non-commercial, and they imply different constraints on anything the group publishes. This needs the repo owner or the institutional office, not me. [unverified] Until it is settled, the safe posture is to read the algorithms, not to link the package into published code.

Data used

None. Nothing here has been run on Memorial data.

Methods

Install is pip install . from a clone — the README’s python setup.py install no longer works, as setuptools made that command fatal after 2025-10-31. There is no pyproject.toml, no version pins, no CI, and a Python 2.7-era version guard.

Verified API of the main entry point, read from source because the README’s own example is wrong — it passes output_dir as the first argument and no such parameter exists:

ExtractorParameters(save_dir=None, log_dir="./", save_format=".tfrecord", sample_cnt=-1,
                    patch_filter_by_area=None, with_anno=True, threads=20, rescale_rate=128,
                    patch_size=128, stride=128, patch_rescale_to=None, extract_layer=0,
                    randomize_order=False)
PatchExtractor(detector=None, parameters=None, feature_map=None, annotations=None)

What the parameters actually mean:

  • extract_layer is an OpenSlide pyramid level index, not a magnification. The module never reads mpp or objective power from slide properties — there is no magnification-aware conversion anywhere. The author’s own comment documents the workaround: to get 20x from a 40x slide, read level 0 at patch_size=1024 and set patch_rescale_to=512.
  • patch_size and stride are in pixels at the extracted level; real stride control exists (the README never mentions stride), so overlap is configurable.
  • rescale_rate is the downsample divisor for the tissue-detection thumbnail — unrelated to patch_rescale_to despite the names.
  • patch_filter_by_area is a fraction in [0,1], with the brightness threshold hardcoded at 85 and not exposed. Passing 0 silently disables filtering rather than filtering nothing.
  • Output filenames are {fn_str}_{x}_{y}{ext} with level-0 coordinates, so patches can be mapped back to slide space. The .h5 path instead stores coordinates in a sidecar file and depends on row order matching.

Tissue detection offers a LAB threshold or a Gaussian Naive Bayes classifier; the GNB path needs the user to generate training pixels through a bundled mouse-sampling UI.

Current state / open questions

Only one of the four advertised scenarios is solidly usable. Plain single-slide patch extraction to .jpg/.png works. Everything involving labels or the batch entry points is broken, silently wrong, or a literal stub:

  • The default configuration does not run: save_format defaults to .tfrecord, which raises unless a feature_map is supplied.
  • with_anno is a dead field. PatchExtractor never reads it; annotation handling is switched purely on whether an annotations object was passed. The shipped parallel_patch_extractor.py demo sets with_anno=True with annotations=None, so labelling is silently off.
  • The patch-label lookup samples the wrong pixel. get_patch_label(..., Center=True) adds a full patch_size to both axes instead of half, so it reads the top-left corner of the diagonally adjacent patch, not the current patch’s centre. Labels are silently wrong near every annotation boundary. The same bug is duplicated in the pairwise extractor.
  • The .h5 writer’s docstring claims it saves labels; the body never touches them.
  • The threaded path (parallel_save_patches) references a module-global patch_cnt that is never assigned — it writes zero patches, swallows the per-future errors, then crashes.
  • The pairwise extractor calls np.int, removed in NumPy 1.24, and tf.python_io, removed in TF2 — so it cannot run today without a two-line patch, and its tfrecord path is broken regardless.
  • Scenario 4 (pairwise plus annotations) is literally print('TODO: extract patches with annotations') followed by a silent return.
  • TensorFlow is imported unconditionally at module top, with a deprecated GPU probe at import time, even for users who only want PNGs. The result of that probe is then immediately overwritten with False, making the entire cuCIM/CuPy branch dead code.

Annotation parsing cannot read our GeoJSON. region_annotation.py parses Aperio ImageScope XML via minidomRegion/Vertex/GeoShape/Text/Type attributes. There is no JSON support at all, so a {organ}__{class}__{reviewer}.geojson file from current QuPath raises an XML syntax error. It also hardcodes 0.25 µm/pixel for micron-to-pixel conversion rather than reading the slide’s own mpp. This path is blocked twice over — by format and by the README’s own “TODO: Incomplete”.

Registration is weaker than advertised. auto_wsi_matcher.py does BRISK keypoint matching plus estimateAffinePartial2D for a coarse thumbnail alignment, then refines with phaseCorrelate — which is translation-only — and reduces everything to a single x/y offset for the whole slide pair. It explicitly rejects any pair showing more than 1° of rotation rather than correcting for it, and the module’s own TODOlist.txt lists “take rotation into account?” and “develop a neural network based method” as unimplemented.

What to use instead. Every capability here is better served by something already in QuPath Tooling:

Maintenance evidence gathered at the same time, for the comparison below: tiatoolbox v2.1.3 released 2026-07-21, last pushed 2026-07-24, 542 stars, 17 open issues, ~87 commits in the preceding year. pathml v3.0.7 released 2026-07-09, last pushed 2026-07-17, 460 stars, ~67 commits in the preceding year. Both are PyTorch-native. WSITools: no tagged releases, last push 2024-12-10, effectively no commits in the preceding year.

Need Use instead
Tiling + tissue detection tiatoolbox or pathml — both released within the last month at time of review, both PyTorch-native
Cross-stain registration tiatoolbox DFBRegister — deep-feature based, affine plus optional non-rigid B-spline, published for exactly this problem
TMA de-arraying QuPath’s own TMADearrayer — WSITools does connected-component blob isolation, not de-arraying with core IDs
Prediction heatmaps on a slide tiatoolbox — writes pyramidal OME-TIFF heatmaps and QuPath-readable JSON

The one thing with no direct equivalent is patch_blending.py, which mosaics processed patches back into a downsampled thumbnail with seam blending — a narrow utility worth reading rather than depending on.

Open questions: whether the licence ambiguity is ever resolved upstream, and whether the serial-section registration idea is worth pursuing at all given the biological ceiling described in Cross-Stain Registration.

Related: Patch Extraction — the tiling decision this evaluation was really about, and where the tool comparison lives.

Derived from: repository source read 2026-07-26 — wsitools/{patch_extraction,tissue_detection,wsi_annotation,wsi_registration,patch_reconstruction}/*.py, setup.py, LICENSE and README.md.