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_layeris an OpenSlide pyramid level index, not a magnification. The module never readsmppor 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 atpatch_size=1024and setpatch_rescale_to=512.patch_sizeandstrideare in pixels at the extracted level; real stride control exists (the README never mentionsstride), so overlap is configurable.rescale_rateis the downsample divisor for the tissue-detection thumbnail — unrelated topatch_rescale_todespite the names.patch_filter_by_areais a fraction in [0,1], with the brightness threshold hardcoded at 85 and not exposed. Passing0silently 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.h5path 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_formatdefaults to.tfrecord, which raises unless afeature_mapis supplied. with_annois a dead field.PatchExtractornever reads it; annotation handling is switched purely on whether an annotations object was passed. The shippedparallel_patch_extractor.pydemo setswith_anno=Truewithannotations=None, so labelling is silently off.- The patch-label lookup samples the wrong pixel.
get_patch_label(..., Center=True)adds a fullpatch_sizeto 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
.h5writer’s docstring claims it saves labels; the body never touches them. - The threaded path (
parallel_save_patches) references a module-globalpatch_cntthat 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, andtf.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 minidom — Region/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.