Skip to content

Releases: WSILabs/wsitools

throwaway: dry-run the release binary matrix

26 Jun 19:57

Choose a tag to compare

Release v0.0.0-rc.test

v0.22.0 — associated-image editing across all formats + JP2K optional

07 Jun 23:01

Choose a tag to compare

Added

  • Associated-image editing extended to COG-WSIlabel/macro/thumbnail/overview remove and replace (all types) via cogwsiwriter re-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 streamwriter rebuild — lossy: rebuilds the file and regenerates a minimal OME-XML (instrument/acquisition/channel/vendor OriginalMetadata and 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 nojp2k to 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 Release workflow now auto-creates the GitHub Release on v* tag push,
    with notes pulled from the matching CHANGELOG.md section 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

06 Jun 23:30

Choose a tag to compare

Added

  • Associated-image editing — four command groups, each with remove and
    replace subcommands: label, macro, thumbnail, overview. Supported
    on SVS and generic-TIFF.
    • remove strips the target associated image entirely (label PHI removal);
      works for every type on both formats.
    • replace swaps 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-label replace errors
      clearly). Replacements carry the reader's classification markers
      (SVS NewSubfileType=9 for macro/overview; WSIImageType private 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/--output for an explicit path; --in-place for
      atomic overwrite (temp + fsync + rename).
    • label replace defaults to LZW + Predictor 2 (lossless, barcode-safe);
      macro/thumbnail/overview replace default to JPEG.
      --compression {jpeg,lzw,deflate,none} overrides.
    • --resize fit|stretch|none (default fit), --bg RRGGBB letterbox fill
      (default F5F5E6), --force to skip the aspect guard,
      --label-dims WxH to 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 to convert.
  • opentile-go bumped to v0.36.0 — adds AssociatedIFDOffset used 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/szi not yet supported.

  • downsample is 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 to convert --to … --factor. Shares the
    reduction engine (internal/downscale) with convert --factor.

  • Default soft memory limit: wsitools now sets GOMEMLIMIT to 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-memory flag (e.g. 8000, 12GiB, off) or the GOMEMLIMIT
    environment variable; precedence is --max-memory > GOMEMLIMIT >
    default. wsitools doctor now reports physical RAM and the active soft
    limit with its source.

  • scripts/bench-dzi.sh now 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's AssociatedImage.Type()):
    • extract --kind is renamed to extract --type (no alias — the old flag is
      removed).
    • info --json associated-image field kindtype.
    • dump-ifds --json IFD-classification field kindimage_type (named
      image_type rather than type to avoid colliding with --raw's existing
      per-tag TIFF type field).
  • 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, typed TIFFTag,
    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).
  • downsample primary 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 16 on JPEG sources (previously errored with
      scale=16 (want 1,2,4,8)).
    • Adds downsample support for AVIF / WebP / HTJ2K sources (previously
      unsupported compression).

v0.15.0 — striped-format source support

26 May 00:51

Choose a tag to compare

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. info shows 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

25 May 23:37

Choose a tag to compare

Added

  • wsitools info now 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.Register in their init().

Changed

  • Dropped -tags nohtj2k from Makefile's default build /
    install targets. Local builds now exercise the full htj2k cgo
    path against openjph (brew install openjph on macOS). Opt-out
    with go build -tags nohtj2k ./cmd/wsitools if 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

25 May 21:58

Choose a tag to compare

Added

  • wsitools region subcommand — 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,H OR --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-go to v0.25.0 (adds the
    ReadRegion family 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 via make goldens-byte-stable).

v0.12.0 — adopt opentile-go v0.24 Level value-type + DecodedTile

25 May 18:20

Choose a tag to compare

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-go to 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).

v0.11.0 — adopt opentile-go v0.23 *Slide API

25 May 03:48

Choose a tag to compare

Migrates to opentile-go v0.23.0's new *Slide API. No behavior change
for end-users; this is an internal type migration. CLI surface
unchanged.

Dependencies

  • Bumped github.com/wsilabs/opentile-go to v0.23.0 (BREAKING upstream:
    opentile.Tiler interface replaced by *opentile.Slide struct).

Changed (internal)

  • Every opentile.OpenTiler(...) call site migrated to
    opentile.OpenFile(...). Every opentile.Tiler typed variable
    migrated to *opentile.Slide. Method-call shape preserved exactly.

Unchanged

  • All CLI surfaces (transcode, downsample, convert, info, dump-ifds,
    extract, hash, doctor, version).
  • Output bytes — pixel-identical AND byte-identical to v0.10 (verified
    via make goldens-byte-stable).

v0.10.0 — deterministic tile order

24 May 22:35

Choose a tag to compare

Adds deterministic tile-write order. Output of transcode, downsample, and convert is now byte-identical across runs and CPU counts.

Added

  • --tile-order={row-major|hilbert|morton} CLI flag on transcode, downsample, convert. Format-validated: SVS accepts row-major only; COG-WSI, generic-TIFF, OME-TIFF accept all three.
  • internal/tiff/tileorder package with OrderStrategy interface, RowMajor, HilbertCurve, Morton implementations, and a ByName registry.
  • Reorder buffer in streamwriter Sink (bounded; back-pressures workers when full).
  • cogwsiwriter finalize walks the tile spool in the chosen strategy's emission order.
  • make goldens-byte-stable Makefile target asserting deterministic output across GOMAXPROCS.
  • docs/superpowers/golden-masters-v0.10.0.txt canonical byte-stable hashes per (format × codec × sample).

Changed

  • Output bytes are now stable run-to-run. Existing pre-v0.10 file SHAs in docs/superpowers/golden-masters-v0.6.0-transcode.txt are historical-only (annotated as such) — they were captured from a non-deterministic writer and are not reproducible.

Unchanged

  • All CLI surfaces and Go APIs (additive only).
  • Default behavior — unflagged invocations produce row-major output as before.
  • Pixel-equality with v0.9 — only on-disk byte order changes, never decoded pixel values.

Install

```sh
go install github.com/wsilabs/wsitools/cmd/wsitools@v0.10.0
```

v0.9.0 — adopt opentile-go v0.22 decoder + resample

24 May 14:26

Choose a tag to compare

Switches wsitools to consume opentile-go v0.22.0's new decoder + resample subpackages. Deletes wsitools' own internal/decoder + internal/resample. Output is pixel-identical to v0.8.1 (verified via wsitools hash --mode pixel — byte-level file SHAs vary run-to-run on both v0.8 and v0.9 due to the concurrent tile-writer, but decoded pixel content is preserved exactly).

Dependencies

  • Bumped github.com/wsilabs/opentile-go to v0.22.0 (adds public decoder/ + resample/ subpackages).

Internal

  • Deleted internal/decoder/ (JPEG + JPEG 2000 decoders now sourced from opentile-go/decoder/{jpeg,jpeg2000}).
  • Deleted internal/resample/ (area-averaging resampler now sourced from opentile-go/resample Box kernel).
  • transcode.go, downsample.go, hash.go, extract.go, and internal/codec/jpeg/jpeg_test.go all use the registry-based decoder lookup with the new image-aware Decode signature.
  • cmd/wsitools/main.go blank-imports opentile-go/decoder/all to register every codec at startup.
  • Makefile passes -tags nohtj2k by default (htj2k decoder requires openjph; opt back in by removing the tag once openjph is installed).

Unchanged

  • All CLI surfaces (transcode, downsample, convert, info, dump-ifds, extract, hash, doctor, version).
  • All output formats; decoded pixels byte-identical to v0.8.1.
  • internal/codec/ (encoders) unchanged.

Install

```sh
go install github.com/wsilabs/wsitools/cmd/wsitools@v0.9.0
```