Releases: WSILabs/wsitools
v0.24.2 — DICOM engine edge-frame padding + QA toolkit
Fixed
- Corrupt edge frames in
convert --to dicom --factor/downsample/crop
to DICOM (the retile-engine DICOM path). Partial right/bottom edge frames —
and whole levels smaller than one frame — were encoded at their truncated
content size, but DICOM TILED_FULL requires every frame to be exactly
Rows×Columns; OpenSlide's DICOM reader (and other strict consumers) rejected
them (Dimensional mismatch reading JPEG, expected 256x256, got …). This is the
same class as the v0.24.1 TIFF edge-tile fix, which only covered the TIFF/IFE
encoder;dicomFrameEncodernow edge-replicates partial frames up to the full
frame size as well. The verbatim DICOM-source frame-copy path was never
affected. Added a cross-tool manual QA harness underscripts/qa/(matrix
generator + OpenSlide/Bio-Formats auto-validators + viewer checklist) that
surfaced this.
Prebuilt binaries
Statically linked; every target includes all codecs (jpeg, jpeg2000, htj2k, jpegxl, avif, webp).
| Target | Asset |
|---|---|
| Linux x86-64 | wsitools-linux-amd64.tar.gz |
| Linux arm64 | wsitools-linux-arm64.tar.gz |
| macOS Apple Silicon | wsitools-darwin-arm64.tar.gz |
| macOS Intel | wsitools-darwin-amd64.tar.gz |
| Windows x86-64 | wsitools-windows-amd64.zip |
Verify with sha256sum -c SHA256SUMS. macOS binaries are signed + notarized; an
unsigned (dry-run) build instead needs xattr -d com.apple.quarantine wsitools.
v0.24.1 — SVS/TIFF edge-tile padding + Aperio thumbnail at IFD 1
Fixed
- Corrupt edge tiles in re-encoded SVS/TIFF/OME-TIFF/COG-WSI/IFE output (TIFF
conformance). The retile engine encoded partial right/bottom edge tiles — and
whole pyramid levels smaller than one tile — at their truncated content size
instead of the full declaredTileWidth×TileLength. TIFF requires uniform
full-size tiles (pixels beyondImageWidth/Lengthare padding, ignored by
readers); OpenSlide and ImageScope reject a sub-full-size JPEG tile as a
"dimensional mismatch", rendering black/garbled edges (opentile is lenient and
masked it). Edge tiles are now padded up to the full tile size (last row/column
edge-replicated to avoid JPEG bleed) before encoding, across every engine-backed
re-encode path (convert --to svs|tiff|ome-tiff|cog-wsi,--factor,
downsample,crop,--to ife). DZI/SZI legitimately use partial edge tiles
and are unaffected. Verified pixel-faithful against OpenSlide on a Ventana BIF
source. - A pyramid level dropped in ImageScope for
--to svsfrom a source without a
thumbnail (BIF, IFE/Iris, …). Genuine Aperio SVS always carries the thumbnail
as the second IFD; ImageScope classifies IFD 1 positionally as the thumbnail, so
when no thumbnail was emitted the first reduced pyramid level landed at IFD 1 and
ImageScope dropped it (e.g. a 1×/4×/16×/64× source showed only 1×/16×/64×).
convert --to svsnow synthesizes an Aperio thumbnail (longest edge 1024px,
baseline JPEG, rendered from L0) at IFD 1 when the source has none, matching the
genuine Aperio layout; sources that already carry a thumbnail are unchanged.
Prebuilt binaries
Statically linked; every target includes all codecs (jpeg, jpeg2000, htj2k, jpegxl, avif, webp).
| Target | Asset |
|---|---|
| Linux x86-64 | wsitools-linux-amd64.tar.gz |
| Linux arm64 | wsitools-linux-arm64.tar.gz |
| macOS Apple Silicon | wsitools-darwin-arm64.tar.gz |
| macOS Intel | wsitools-darwin-amd64.tar.gz |
| Windows x86-64 | wsitools-windows-amd64.zip |
Verify with sha256sum -c SHA256SUMS. macOS binaries are signed + notarized; an
unsigned (dry-run) build instead needs xattr -d com.apple.quarantine wsitools.
v0.24.0 — Aperio-viewer colour fix; convert --tile-size; drop --dzi-tile-size/--jobs
Added
convert --tile-size N— output tile-size control for the re-encode
targets (svs/tiff/ome-tiff/cog-wsi,dzi/szi,dicom). When unset
(0), the output tile size matches the source — fixing the previously
hardcoded 256 in--factoranddownsample; passNto override. A
--tile-sizeequal to the source tiling is a no-op (a lossless tile-copy stays
a copy); a--tile-sizethat differs from the source forces a re-encode,
whose codec defaults to the source's own codec (erroring if that codec has
no encoder — pass--codec). Per-target notes:--to dicom: re-tiles only when a size differing from the source is
requested (the DICOMRows/Columnsframe tags then follow); otherwise
the existing verbatim frame-copy is unchanged.--to bif(verbatim Ventana/Roche DP-200 layout) and--to ife(Iris IFE
mandates fixed 256×256 tiles in v1.0): a--tile-sizethey can't honor is
rejected with a clear error rather than silently ignored.--to ife
always emits 256px tiles.
Changed
- Removed
--dzi-tile-size— replaced by the unified--tile-size. DZI/SZI
now default to the source tile size when--tile-sizeis unset (was a fixed
256). Breaking CLI change (pre-1.0). - Removed the
--jobsalias of--workersacrossconvert/crop/
downsample/transcode;--workersis the single canonical worker flag
(downsample's primary flips to--workers, defaultNumCPU). Breaking CLI
change (pre-1.0).
Fixed
- Wrong colours in Aperio-ecosystem viewers (ImageScope, OpenSlide) for
re-encoded / tile-copied JPEG output. A TIFF wrapping YCbCr JPEG tiles was
taggedPhotometricInterpretation=RGB(2), so spec-following readers skipped the
YCbCr→RGB conversion and rendered all colours wrong (opentile is lenient and
decoded by the JPEG's own markers, which masked the bug). The photometric is now
derived from the actual tile colour model: re-encoded JPEG tiles (always YCbCr)
are taggedYCbCr(6), and verbatim tile-copies are tagged from the source JPEG's
framing — JFIF/standardY,Cb,Crcomponent IDs →YCbCr(6), Aperio bare-JPEG
framing →RGB(2). Spans every re-encode/copy path (convert --to svs|tiff|ome-tiff|cog-wsi,--factor,downsample,crop, associated-image
rebuild). YCbCrSubSampling now accompanies the YCbCr photometric on all
containers. Verified pixel-faithful against OpenSlide for SVS/IFE/BIF sources.
Prebuilt binaries
Statically linked; every target includes all codecs (jpeg, jpeg2000, htj2k, jpegxl, avif, webp).
| Target | Asset |
|---|---|
| Linux x86-64 | wsitools-linux-amd64.tar.gz |
| Linux arm64 | wsitools-linux-arm64.tar.gz |
| macOS Apple Silicon | wsitools-darwin-arm64.tar.gz |
| macOS Intel | wsitools-darwin-amd64.tar.gz |
| Windows x86-64 | wsitools-windows-amd64.zip |
Verify with sha256sum -c SHA256SUMS. macOS binaries are signed + notarized; an
unsigned (dry-run) build instead needs xattr -d com.apple.quarantine wsitools.
v0.23.0 — prebuilt cross-platform binaries, BIF/IFE writers, validate, JPEG 2000 encoder
Added
-
Prebuilt binaries — every
vX.Y.Zrelease now ships statically-linked,
download-and-runwsitoolsbinaries for 5 targets (Linux amd64/arm64,
macOS arm64/amd64, Windows amd64), each bundling all six codecs with no system
libraries to install. Built on native runners (cgo rules out cross-compiling
the host) with the codec C libraries sourced statically via vcpkg
(vcpkg.json+.github/vcpkg-triplets/), a sharedbuild-staticcomposite
action, and a 5-target matrix inrelease.yml(notes → build →SHA256SUMS);
arelease-canaryworkflow guards the static build on release-relevant PRs.
Linux is glibc mostly-static (runs on all mainstream distros); Windows is
fully static; Intel-Mac binaries are cross-compiled on Apple Silicon (the
Intel runner pool is being sunset). macOS binaries are sign+notarize-ready
but ship unsigned until Developer-ID signing secrets are provisioned — see
docs/RELEASING.md. As part of this,internal/codec/htj2know discovers
OpenJPH viapkg-configinstead of hardcoded Homebrew paths (also fixes
Intel-Mac and clean-environment source builds). -
convert --to bif(experimental) — write a Ventana/Roche DP 200-shaped
BIF from any source (internal/tiff/bifwriter+cmd/wsitools/convert_bif.go).
Full pyramid as row-majorlevel=NIFDs (verbatim JPEG tile-copy for JPEG
sources;--codec jpegdecodes+re-encodes non-JPEG sources to self-contained
JPEG tiles), a whole-slide overview (the source'soverview/macrocarried
through + oriented to portrait when present, else synthesized from the tissue
at the DP 200 canonical 1251×3685), and synthesized<iScan>/<EncodeInfo>
metadata (scanner model, MPP, magnification). Renders correctly in
bio-formats / QuPath. Key finding: real DP 200 stores tiles row-major
(per the file's own<Frame>nodes), not serpentine — the whitepaper's
"serpentine" is theTileJointInfostitch-graph numbering only; opentile-go's
reader had conflated the two (the BIF read bug we filed, fixed in
opentile-go v0.45.3 / #57 — it now reads our output pixel-identically).
Re-encode runs on a worker pool
(--workers). Limitations: single-AOI, no Z; no separate label/thumbnail or
probability map carried; no--factor/--target-mag. -
wsitools validate <file>— new read-side command that checks a slide's
structural conformance using opentile-go v0.45.1'sValidateAPI
(ValidateFile→Reportof findings with severities and check codes).
Human or--jsonoutput; three-way exit code (0valid /2invalid /1
operational error);--strictpromotes warnings to failures. Calls
ValidateFiledirectly (notinternal/source), so a malformed file is
reported as anunopenablefinding rather than erroring out. -
JPEG 2000 is now a
--codecre-encode target (survey B1) — new
internal/codec/jp2kOpenJPEG-backed encoder (raw J2K codestream). Use
convert --to {tiff,cog-wsi,...} --codec jpeg2000; lossless via the knob
--quality reversible=true(the--qualityflag now accepts comma-separated
k=vknobs, e.g.q=85,reversible=true, in addition to a bare integer). The
lossless path round-trips pixel-identical (verified end-to-end after the
opentile-go v0.45.1 JP2K-decoder fix below). wsitools now has encoders for
jpeg / jpeg2000 / htj2k / jpegxl / avif / webp. -
convert --to dicom --codec jpeg(survey A4b) — explicit opt-in to
re-encode a source whose tiles are not a DICOM transfer syntax (LZW /
uncompressed / Deflate / AVIF / WebP) to JPEG-baseline, mirroring the TIFF
family's--codec. Without--codec, such a source is still rejected (no
silent codec assumptions; the error now suggests--codec jpeg);--codec
values other thanjpegare rejected (no JPEG 2000 / HTJ2K encoder exists —
survey B1). Each tile is decoded via opentile-go's level-decode and re-encoded
on demand (derivedsource.TranscodeToJPEG);codecColorinspects the
re-encoded frame so the DICOM photometric matches. Verified: LZW + uncompressed
590_cropImageScope crops → JPEG DICOM withdciodvfy0 errors
(YBR_FULL_422). A lossy-re-encode warning is logged. -
convert --to dicomnow frame-copies HTJ2K sources (survey A4a / B4). A
DICOM source whose frames are High-Throughput JPEG 2000 is re-emitted
verbatim with the matching transfer syntax —…1.2.4.201(reversible/lossless)
or…1.2.4.203(lossy) — instead of being rejected. HTJ2K shares JPEG 2000's
SIZ/COD codestream markers (the extra CAP marker is length-skipped), so the
existingInspectJP2K+PhotometricJP2Kderive components / reversibility /
photometric unchanged. Verified: a full3DHISTECH-HTJ2Kpyramid converts to
5 HTJ2K instances that read back pixel-identical to the source, and
dciodvfyreports 0 errors on every instance (it validates the HTJ2K
transfer syntax…4.201). -
convert --to dicomJPEG XL source frame-copy (survey A4a) — a source
whose frames are JPEG XL is frame-copied with the general JPEG XL transfer
syntax…1.2.4.112. JPEG XL exposes no header-only reversibility flag, so it
is marked lossy conservatively. Untested — no JPEG XL source fixture exists
yet; ships behind the same inspector path as the other codecs.
Changed
-
DICOM writer codestream inspection now uses opentile-go's
decoder.CodestreamInspector(header-only, no full decode) instead of
wsitools' hand-rolledjpegmeta/jp2kmetamarker parsers, which are deleted.
The inspector parses JPEG SOF / J2K SIZ+COD / HTJ2K / JXL headers and exposes
ColorEncoding+ChromaSubsampling+Lossless; wsitools keeps only the
codec→DICOM mapping (codecinspect.go). Behavior is unchanged for JPEG
(incl. Aperio APP14 raw-RGB →RGB, and 4:2:2 →YBR_FULL_422vs 4:4:4 →
YBR_FULL), JPEG 2000, and HTJ2K — verified by pixel-identical round-trips and
dciodvfy0-errors across CMU (JPEG + native label + JPEG associated), JP2K,
and HTJ2K. -
DICOM writer now depends on the
WSILabs/dicomfork of
github.com/suyashkumar/dicom(v1.1.0-wsilabs.1, via a directrequire).
Upstream v1.1.0's transfer-syntax UID dictionary predates HTJ2K (Sup 232) and
JPEG XL (Sup 235) and exposes no registration API, sodicom.Writerefused to
emit those syntaxes (UID '…' not found in dictionary) even though the body
encoding is identical Explicit VR LE. The fork adds the HTJ2K (…4.201–.205)
and JPEG XL (…4.110/.111/.112) UIDs and rebrands the module path so it is
consumable directly; a weekly GitHub Action tracks upstream and flags when the
UIDs land upstream so the fork can be retired. Read-side DICOM (via opentile-go)
still uses upstreamsuyashkumar/dicomas an indirect dependency. -
opentile-go v0.41.1 → v0.45.3. Picks up:
decoder.CodestreamInspector
(Inspect(src) → CodestreamInfo{Components, BitDepth, Lossless, ColorEncoding, ChromaSubsampling, Boxed}, opentile-go #41, v0.43.0 — now consumed by the
DICOM writer); a structural WSIValidateAPI (opentile.ValidateFile/
Validate/(*Slide).Validate→ findings-Report, v0.45.0); the
JPEG 2000 decoder colorspace fix (opentile-go #53, v0.45.1) — the decoder
now decides RGB-vs-YCbCr from the codestream (MCT/colorspace) instead of
force-assuming YCbCr, so wsitools' new JP2K-encoder output round-trips correct
colors (Aperio 33003 decoding unchanged); and theValidateslide-level MPP
fix (opentile-go #55, v0.45.2) —checkLevelGeometrynow accepts the
slide-levelMetadata.MPPand rolls a genuinely-missing MPP into a single
whole-file finding, instead of false-positiving onemissing-metadatawarning
per level on ndpi/leica-scn/dicom/generic-tiff/ife/szi (which carry MPP at the
slide level, not perLevel). Found via awsitools validatecorpus sweep;
and the BIF row-major tile-ordering fix (opentile-go #57, v0.45.3) — the
reader no longer hardcodes a serpentineTILE_OFFSETSremap (real Roche DP 200
is row-major, per the<Frame>nodes), so opentile now reads genuine Roche
slides ANDconvert --to bifoutput correctly (verified pixel-identical
round-trip). Filed from the BIF writer work (also #58 docs, #59 test oracle). -
opentile-go v0.41.0 → v0.41.1 (no API change) — picks up two decode fixes
for Aperio ImageScope exports (which re-encode the pyramid + associated
images in non-Aperio codecs): tiled LZW / uncompressed / Deflate levels now
decode (opentile-go #28), and non-JPEG associated images (e.g. an
uncompressed thumbnail) now decode (#29). Verified through wsitoolsregion
(tiled-LZW L0) andextract(uncompressed thumbnail). CI fixtures bumped to
wsi-fixtures v7, adding the590_cropImageScope export crops (which guard
the above) and a CC0 OME-TIFF fixture (CMU-1-Small-Region.ome.tiff) that
gives the OME-TIFF transform paths CI coverage.
Removed
internal/codec/aperioapp14— the v0.17-era keep-around encoder that
reproduced Aperio's APP14 + raw-RGB JPEG framing. It was never registered as a
codec.Factory, never imported by anything but its own test, and had zero
callers; no planned feature needs Aperio-identical re-encode (lossless paths
copy source tile bodies verbatim, preserving whatever framing the source
used). Recoverable from git history if a byte-faithful-Aperio path is ever
wanted.
Fixed
convert(re-encode),downsample,crop, andhash --mode pixelcould
not decode LZW / uncompressed / Deflate source tiles (Survey F1). Those
paths picked a standalone codec by source compression, which covers only
JPEG / JPEG 2000 — so re-encoding, downsampling, cropping, or pixel-hashing an
Aperio ImageScope export (tiled LZW / uncompressed / Deflate) errored
no decoder for source compression lzw|none, ev...
v0.22.0 — associated-image editing across all formats + JP2K optional
Added
- Associated-image editing extended to COG-WSI —
label/macro/thumbnail/overview removeandreplace(all types) viacogwsiwriterre-finalize. Pyramid tile bytes are copied verbatim (no re-encode); all other associated images and MPP/magnification/ICC are preserved — only the target image changes. - Associated-image editing extended to OME-TIFF (remove + replace, all types) via
streamwriterrebuild — lossy: rebuilds the file and regenerates a minimal OME-XML (instrument/acquisition/channel/vendorOriginalMetadataand pyramid-resolution annotations not preserved; pyramid pixels, geometry/MPP/magnification, ICC, and the other associated images are). Always-on runtime warning on every OME-TIFF edit. Associated replacements are JPEG-only (opentile-go's OME-TIFF reader can only decode JPEG/uncompressed associated images; LZW/Deflate would be unreadable). wsitools' OME-TIFF support is rudimentary — use Bio-Formats for serious OME-TIFF work; see docs/ome-tiff-limitations.md. This completes associated-image editing across all four editable formats: SVS, generic-TIFF, COG-WSI, and OME-TIFF.
Changed
- opentile-go bumped to v0.37.0 — JPEG 2000 decode is now optional. Build
with-tags nojp2kto drop the OpenJPEG dependency; libjpeg-turbo is now the
only required codec library. See docs/INSTALL.md for a
JPEG-only minimal install. JPEG 2000 is a legacy Aperio codec, no longer
marketed as required. - CI: a
Releaseworkflow now auto-creates the GitHub Release onv*tag push,
with notes pulled from the matchingCHANGELOG.mdsection and the title from
the annotated tag's subject (notes-only; no binary artifacts).
v0.21.0 — associated-image editing + convert --factor + OME-TIFF conformance
Added
- Associated-image editing — four command groups, each with
removeand
replacesubcommands:label,macro,thumbnail,overview. Supported
on SVS and generic-TIFF.removestrips the target associated image entirely (label PHI removal);
works for every type on both formats.replaceswaps it with a new image file. Supported for all types on
generic-TIFF; on SVS, only label replace is supported today
(opentile-go reads Aperio thumbnail/macro/overview as abbreviated JPEG, so
re-encoding those is a Slice-2 item — SVS non-labelreplaceerrors
clearly). Replacements carry the reader's classification markers
(SVSNewSubfileType=9for macro/overview;WSIImageTypeprivate tag for
generic-TIFF) so a replaced image is read back as the intended type.- Pyramid tile bytes are copied verbatim (no decode, no re-encode); only
the tail IFD is rewritten via a prefix-copy + tail-re-emit splice. Output
contains no recoverable label PHI. - Output defaults to
<stem>_relabeled<ext>next to the input (auto-numbered
if the path exists);-o/--outputfor an explicit path;--in-placefor
atomic overwrite (temp + fsync + rename). label replacedefaults to LZW + Predictor 2 (lossless, barcode-safe);
macro/thumbnail/overview replacedefault to JPEG.
--compression {jpeg,lzw,deflate,none}overrides.--resize fit|stretch|none(defaultfit),--bg RRGGBBletterbox fill
(defaultF5F5E6),--forceto skip the aspect guard,
--label-dims WxHto override target dimensions.- OME-TIFF and COG-WSI: planned (Slice 2 — SubIFD-range-aware splice +
OME-XML sync). Other formats (DICOM, NDPI, Philips, BIF, IFE, Leica)
are rejected with a pointer toconvert.
- opentile-go bumped to v0.36.0 — adds
AssociatedIFDOffsetused by the
splice engine to locate and excise associated-image IFDs without walking the
full IFD chain.
Dependencies
-
New:
github.com/hhrutter/lzw— pure-Go LZW encoder used for
lossless label replacement (LZW + Predictor 2). -
convert --factor N/--target-mag M— downsample while converting, for
--to svs|tiff|ome-tiff|cog-wsi, with correctly-scaled MPP (×N) and
magnification (÷N).dzi/szinot yet supported. -
downsampleis now format-preserving and works on more sources: it
reduces SVS, OME-TIFF, generic-TIFF, and COG-WSI slides in place (same
container in/out, MPP/mag scaled), instead of SVS-only. Sources with no
matching writer error with a pointer toconvert --to … --factor. Shares the
reduction engine (internal/downscale) withconvert --factor. -
Default soft memory limit: wsitools now sets
GOMEMLIMITto 75% of
physical RAM at startup so memory-heavy conversions degrade under GC
pressure instead of OOM-ing the host. Override with the global
--max-memoryflag (e.g.8000,12GiB,off) or theGOMEMLIMIT
environment variable; precedence is--max-memory>GOMEMLIMIT>
default.wsitools doctornow reports physical RAM and the active soft
limit with its source. -
scripts/bench-dzi.shnow reports peak resident memory (via
/usr/bin/time -l) alongside wall-clock time for both wsitools and
vips, with a memory ratio column.
Changed
- BREAKING — associated-image terminology "kind" → "type" (aligns with
opentile-go'sAssociatedImage.Type()):extract --kindis renamed toextract --type(no alias — the old flag is
removed).info --jsonassociated-image fieldkind→type.dump-ifds --jsonIFD-classification fieldkind→image_type(named
image_typerather thantypeto avoid colliding with--raw's existing
per-tag TIFFtypefield).
- opentile-go upgraded v0.26.0 → v0.31.0. v0.27–v0.29 are internal NDPI
decode-perf work (pixel-frame cache, cross-format decoder-handle pool,
ReadRegion allocation elimination); v0.30 adds a per-Slide read-memory
budget (OPENTILE_READ_MEMORY_BUDGET, default 1 GiB) that byte-bounds
the strip/tile decode caches, lowering peak RSS on wide NDPI slides;
v0.31 exposes raw TIFF tags cross-format (Slide.LevelTIFFTags/
AssociatedTIFFTags/TIFFDirectoriesOf, typedTIFFTag,
pixel-pointer-filtered) — the foundation for upcoming metadata
carry-through. Clean drop-in; no wsitools API changes. Later upgraded
through v0.33.0 (chroma-subsampling JP2K decode fix; separable Lanczos;
codec-domain scaled decode for JPEG2000/HTJ2K;dicom.ListWSMSeries). downsampleprimary reduction is now codec-agnostic: it uses codec-domain
scaled decode (DecodeOptions.Scale) where the source codec supports it and
falls back to full-decode + box otherwise.- JP2K sources now decode via wavelet resolution-reduction (opentile-go
v0.33.0) instead of full-decode + box — faster and sharper, but output
pixels are no longer byte-identical to prior releases for JP2K sources. - Fixes
downsample --factor 16on JPEG sources (previously errored with
scale=16 (want 1,2,4,8)). - Adds
downsamplesupport for AVIF / WebP / HTJ2K sources (previously
unsupported compression).
- JP2K sources now decode via wavelet resolution-reduction (opentile-go
v0.15.0 — striped-format source support
Added
- NDPI, OME-OneFrame, and Leica SCN (single-image) slides are now
supported across all CLI subcommands (info, transcode, downsample,
convert, hash, extract, dump-ifds, region). opentile-go synthesizes
tile geometry from striped MCU streams (NDPI) and single-frame OME
(OME-OneFrame); wsitools' tile-pipeline now operates on
opentile-go-tiled output verbatim.
Changed
- Dropped the "v0.2 sanity gate" in
internal/source/opentile.go
that rejected NDPI / OME-OneFrame / Leica-SCN. Stale since
opentile-go v0.14+ began synthesizing tile geometry. ErrUnsupportedFormat's message updated to drop the "v0.2"
version marker; the sentinel remains for genuinely-unsupported
future formats.
Bit-exact tile-copy caveat (convert)
convert --to cog-wsi from natively-tiled sources (SVS, Philips,
OME-tiled, BIF, IFE, generic-TIFF, COG-WSI, SZI, single-image
Leica-SCN) continues to produce bit-exact tile-copy COG-WSI output
— the source's compressed tile bytes appear verbatim in the
destination.
From striped sources (NDPI, OME-OneFrame), the COG-WSI output
contains opentile-go's synthesized JPEG tile bytes. These bytes
decode to the same pixels as the source region and are
deterministic (same input → same output), but they are NOT the
source's on-disk bytes (NDPI / OneFrame source files don't carry
tile bytes — they carry strip bytes).
Out of scope (deferred)
- Multi-channel fluorescence Leica SCN
(Leica-Fluorescence-1.scn). transcode/downsample assume RGB
channels; multi-channel handling is a future release. - Multi-image OME-TIFF where multiple
<Image>series each carry
their own pyramid.infoshows image 0 only.
Unchanged
- All other CLI surfaces (region, transcode/downsample/convert
on natively-tiled sources, etc.). - Output bytes from natively-tiled-source operations — same bytes
as v0.14.
v0.14.0 — info quality estimate + Makefile nohtj2k drop
Added
wsitools infonow includes a per-level codec quality summary
alongside compression. JPEG levels show estimated Q value +
chroma subsampling (4:4:4 / 4:2:2 / 4:2:0). JPEG 2000 levels show
reversible/irreversible transform + layer count. WebP levels show
lossless flag + estimated Q. Lossless codecs (LZW/Deflate/None)
surface as "lossless". Other codecs (AVIF, JPEG XL, HTJ2K)
currently surface compression only; quality inspectors land in
future releases without info-command changes.- New
cmd/wsitools/quality/package with pluggable Inspector
interface and per-codec subpackages:quality/jpeg,
quality/jpeg2000,quality/webp. New codecs register via
quality.Registerin their init().
Changed
- Dropped
-tags nohtj2kfromMakefile's defaultbuild/
installtargets. Local builds now exercise the full htj2k cgo
path against openjph (brew install openjphon macOS). Opt-out
withgo build -tags nohtj2k ./cmd/wsitoolsif needed.
Unchanged
- All other CLI surfaces (transcode, downsample, convert, dump-ifds,
extract, hash, doctor, version, region). - Output bytes from
transcode/downsample/convert— same
bytes as v0.13.
v0.13.0 — region subcommand
Added
-
wsitools regionsubcommand — extract a rectangular pixel region
from a slide at a chosen pyramid level and write as PNG.Flags:
--level N(required) — pyramid level index.--rect X,Y,W,HOR--x X --y Y --w W --h H(mutually
exclusive; one form required).--image N(default 0) — for multi-image OME-TIFF.--format rgb|rgba(default rgb).-o, --output PATH(required) — PNG output path.-f, --force— overwrite existing output file.
Out-of-bounds regions are white-filled per opentile-go v0.25's
ReadRegion semantics.Examples:
wsitools region --level 0 --rect 1000,1000,512,512 -o patch.png slide.svs wsitools region --level 2 --x 0 --y 0 --w 512 --h 512 -o thumb.png slide.svs
Dependencies
- Bumped
github.com/wsilabs/opentile-goto v0.25.0 (adds the
ReadRegionfamily the new subcommand consumes).
Unchanged
- All other CLI surfaces (transcode, downsample, convert, info,
dump-ifds, extract, hash, doctor, version). - Output bytes from existing commands — pixel-identical to v0.12
(verified viamake goldens-byte-stable).
v0.12.0 — adopt opentile-go v0.24 Level value-type + DecodedTile
Adopts opentile-go v0.24.0 (Level value-type + DecodedTile). No
behavior change for end-users; this is an internal type migration.
Dependencies
- Bumped
github.com/wsilabs/opentile-goto v0.24.0 (BREAKING
upstream: Level/Image interfaces → value-type structs; tile reads
moved to *Slide methods).
Changed (internal)
- Every
slide.Levels()[i].Tile(...)migrated to
slide.RawTile(i, ...). Every Level field access migrated from
method call to struct field. CLI surface unchanged.
Unchanged
- All CLI surfaces (transcode, downsample, convert, info, dump-ifds,
extract, hash, doctor, version). - Output bytes — pixel-identical to v0.11 (verified via
make goldens-byte-stable).