Skip to content

Releases: WSILabs/wsitools

v0.24.2 — DICOM engine edge-frame padding + QA toolkit

28 Jun 19:43

Choose a tag to compare

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; dicomFrameEncoder now 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 under scripts/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

28 Jun 17:45

Choose a tag to compare

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 declared TileWidth×TileLength. TIFF requires uniform
    full-size tiles (pixels beyond ImageWidth/Length are 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 svs from 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 svs now 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

28 Jun 01:52

Choose a tag to compare

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 --factor and downsample; pass N to override. A
    --tile-size equal to the source tiling is a no-op (a lossless tile-copy stays
    a copy); a --tile-size that 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 DICOM Rows/Columns frame 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-size they 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-size is unset (was a fixed
    256). Breaking CLI change (pre-1.0).
  • Removed the --jobs alias of --workers across convert/crop/
    downsample/transcode; --workers is the single canonical worker flag
    (downsample's primary flips to --workers, default NumCPU). 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
    tagged PhotometricInterpretation=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 tagged YCbCr(6), and verbatim tile-copies are tagged from the source JPEG's
    framing — JFIF/standard Y,Cb,Cr component 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

27 Jun 00:55

Choose a tag to compare

Added

  • Prebuilt binaries — every vX.Y.Z release now ships statically-linked,
    download-and-run wsitools binaries 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 shared build-static composite
    action, and a 5-target matrix in release.yml (notes → build → SHA256SUMS);
    a release-canary workflow 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/htj2k now discovers
    OpenJPH via pkg-config instead 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-major level=N IFDs (verbatim JPEG tile-copy for JPEG
    sources; --codec jpeg decodes+re-encodes non-JPEG sources to self-contained
    JPEG tiles), a whole-slide overview (the source's overview/macro carried
    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 the TileJointInfo stitch-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's Validate API
    (ValidateFileReport of findings with severities and check codes).
    Human or --json output; three-way exit code (0 valid / 2 invalid / 1
    operational error); --strict promotes warnings to failures. Calls
    ValidateFile directly (not internal/source), so a malformed file is
    reported as an unopenable finding rather than erroring out.

  • JPEG 2000 is now a --codec re-encode target (survey B1) — new
    internal/codec/jp2k OpenJPEG-backed encoder (raw J2K codestream). Use
    convert --to {tiff,cog-wsi,...} --codec jpeg2000; lossless via the knob
    --quality reversible=true (the --quality flag now accepts comma-separated
    k=v knobs, 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 than jpeg are 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); codecColor inspects the
    re-encoded frame so the DICOM photometric matches. Verified: LZW + uncompressed
    590_crop ImageScope crops → JPEG DICOM with dciodvfy 0 errors
    (YBR_FULL_422). A lossy-re-encode warning is logged.

  • convert --to dicom now 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
    existing InspectJP2K + PhotometricJP2K derive components / reversibility /
    photometric unchanged. Verified: a full 3DHISTECH-HTJ2K pyramid converts to
    5 HTJ2K instances that read back pixel-identical to the source, and
    dciodvfy reports 0 errors on every instance (it validates the HTJ2K
    transfer syntax …4.201).

  • convert --to dicom JPEG 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-rolled jpegmeta/jp2kmeta marker 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_422 vs 4:4:4 →
    YBR_FULL), JPEG 2000, and HTJ2K — verified by pixel-identical round-trips and
    dciodvfy 0-errors across CMU (JPEG + native label + JPEG associated), JP2K,
    and HTJ2K.

  • DICOM writer now depends on the WSILabs/dicom fork of
    github.com/suyashkumar/dicom (v1.1.0-wsilabs.1, via a direct require).
    Upstream v1.1.0's transfer-syntax UID dictionary predates HTJ2K (Sup 232) and
    JPEG XL (Sup 235) and exposes no registration API, so dicom.Write refused 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 upstream suyashkumar/dicom as 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 WSI Validate API (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 the Validate slide-level MPP
    fix
    (opentile-go #55, v0.45.2) — checkLevelGeometry now accepts the
    slide-level Metadata.MPP and rolls a genuinely-missing MPP into a single
    whole-file finding, instead of false-positiving one missing-metadata warning
    per level on ndpi/leica-scn/dicom/generic-tiff/ife/szi (which carry MPP at the
    slide level, not per Level). Found via a wsitools validate corpus sweep;
    and the BIF row-major tile-ordering fix (opentile-go #57, v0.45.3) — the
    reader no longer hardcodes a serpentine TILE_OFFSETS remap (real Roche DP 200
    is row-major, per the <Frame> nodes), so opentile now reads genuine Roche
    slides AND convert --to bif output 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 wsitools region
    (tiled-LZW L0) and extract (uncompressed thumbnail). CI fixtures bumped to
    wsi-fixtures v7, adding the 590_crop ImageScope 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, and hash --mode pixel could
    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...
Read more

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