@fideus-labs/fiff

中文文档
0.8.2发布于 2026/10/09MITTypeScriptES Module近 30 天下载 7225

Present TIFF files as a zarrita.js Zarr store following the NGFF OME-Zarr data model

原始使用文档
文档目录24 节
  1. ✨ Features
  2. Reading
  3. Writing
  4. 📦 Installation
  5. ⚡ Usage
  6. Reading: Open an OME-TIFF as a Zarr store
  7. Reading: Load a Multiscales from a TiffStore
  8. Writing: Convert ngff-zarr Multiscales to OME-TIFF
  9. Worker Pool: Offloading Compression to Web Workers
  10. Custom Plane Reader ( getPlane )
  11. 🏭 Read API
  12. Factory Methods
  13. Options
  14. Public Accessors
  15. 📝 Write API
  16. toOmeTiff(multiscales, options?)
  17. WriteOptions
  18. 🛠️ Development
  19. 📋 Prerequisites
  20. 🔧 Setup
  21. 🗂️ Project Structure
  22. 📝 Commands
  23. 🤝 Contributing
  24. 📄 License

fiff

CI npm version license

Read and write OME-TIFF files through a zarrita.js Zarr store following the OME-Zarr v0.5 data model.

✨ Features

Reading

  • 🚀 Lazy HTTP range requests -- Chunk data is fetched on demand via geotiff.js readRasters(), no full file download needed
  • 📐 OME-XML support -- Parses OME-XML metadata for dimensions, channels, physical units, and all 6 DimensionOrder permutations
  • 🔍 Pyramid detection -- Automatically discovers multi-resolution levels via SubIFDs (modern), flat IFDs (legacy), or COG overviews
  • 🧩 Edge-chunk zero-padding -- Boundary chunks are automatically padded to full tile size for correct Zarr consumption
  • 🔗 zarrita.js compatible -- Implements the AsyncReadable interface; use directly with zarr.open() from zarrita.js
  • 📋 OME-Zarr v0.5 output -- Generates Zarr v3 metadata with ome.multiscales and ome.omero attributes

Writing

  • 📝 OME-TIFF generation -- Convert ngff-zarr Multiscales objects to valid OME-TIFF files with embedded OME-XML metadata
  • 🔻 Full pyramid support -- Multi-resolution levels are written as SubIFDs, matching the modern OME-TIFF pyramid convention
  • 🗜️ Deflate compression -- Async zlib/deflate via native CompressionStream (non-blocking) with synchronous fflate fallback
  • 🧵 Worker pool support -- Optional @fideus-labs/worker-pool integration offloads compression and decompression to Web Workers, fully releasing the main thread
  • 🧱 Tiled output -- Large images are automatically written as 256x256 tiles (configurable), the OME-TIFF recommended format for efficient random access
  • ⚡ Parallel plane reading -- Planes are read with bounded concurrency (configurable) for faster writes from async data sources
  • 💾 BigTIFF support -- Automatic 64-bit offset format when files exceed 4 GB, with manual override via format option
  • 📐 5D support -- Handles all dimension orders (XYZCT, XYZTC, etc.) and arbitrary combinations of T, C, Z, Y, X axes
  • 🔌 Custom plane reader -- Pluggable getPlane callback replaces the internal zarr.get(), enabling worker-pool decompression or zero-copy reads from uncompressed zarr arrays

📦 Installation

npm install @fideus-labs/fiff

For write support, also install the optional peer dependency:

npm install @fideus-labs/ngff-zarr

For worker pool support (offloading compression/decompression to Web Workers):

npm install @fideus-labs/worker-pool

⚡ Usage

Reading: Open an OME-TIFF as a Zarr store

From a remote URL

import { TiffStore } from "@fideus-labs/fiff";
import * as zarr from "zarrita";

const store = await TiffStore.fromUrl("https://example.com/image.ome.tif");
const group = await zarr.open(store as unknown as zarr.Readable, { kind: "group" });

// Open the full-resolution array (level 0)
const arr = await zarr.open(group.resolve("0"), { kind: "array" });
const chunk = await zarr.get(arr);

console.log(chunk.shape); // e.g. [1, 3, 1, 512, 512]
console.log(chunk.data);  // Float32Array, Uint16Array, etc.

From an ArrayBuffer

const response = await fetch("https://example.com/image.tif");
const buffer = await response.arrayBuffer();
const store = await TiffStore.fromArrayBuffer(buffer);

From a File / Blob

const file = document.querySelector("input[type=file]").files[0];
const store = await TiffStore.fromBlob(file);

From an existing GeoTIFF instance

import { fromUrl } from "geotiff";

const tiff = await fromUrl("https://example.com/image.tif");
const store = await TiffStore.fromGeoTIFF(tiff);

Accessing store metadata

const store = await TiffStore.fromUrl("https://example.com/image.ome.tif");

store.levels;         // number of resolution levels
store.dataType;       // "uint16", "float32", etc.
store.dimensionNames; // ["t", "c", "z", "y", "x"]
store.getShape(0);    // full-res shape, e.g. [1, 3, 1, 2048, 2048]
store.getShape(1);    // level 1 shape, e.g. [1, 3, 1, 1024, 1024]
store.ome;            // parsed OME-XML image metadata (if present)
store.pyramidInfo;    // pyramid structure details

Reading: Load a Multiscales from a TiffStore

TiffStore implements the zarrita Readable interface, so it can be passed directly to ngff-zarr's fromNgffZarr to obtain a Multiscales object:

import { TiffStore } from "@fideus-labs/fiff";
import { fromNgffZarr } from "@fideus-labs/ngff-zarr";

const store = await TiffStore.fromUrl("https://example.com/image.ome.tif");
const multiscales = await fromNgffZarr(store, { version: "0.5" });

const image = multiscales.images[0];
console.log(image.dims);        // e.g. ["t", "c", "z", "y", "x"]
console.log(image.data.shape);  // e.g. [1, 3, 1, 512, 512]
console.log(image.data.dtype);  // e.g. "uint16"
console.log(image.scale);       // e.g. { t: 1, c: 1, z: 1, y: 0.5, x: 0.5 }

console.log(multiscales.metadata.axes);     // axis definitions
console.log(multiscales.metadata.datasets); // dataset paths and transforms

Writing: Convert ngff-zarr Multiscales to OME-TIFF

toOmeTiff() takes an ngff-zarr Multiscales object and returns a complete OME-TIFF file as an ArrayBuffer.

Basic write

import { toOmeTiff } from "@fideus-labs/fiff";
import {
  createNgffImage,
  createAxis,
  createDataset,
  createMetadata,
  createMultiscales,
} from "@fideus-labs/ngff-zarr";
import * as zarr from "zarrita";

// 1. Create an ngff-zarr image
const image = await createNgffImage(
  [],              // no parent images
  [512, 512],      // shape: [y, x]
  "uint16",        // data type
  ["y", "x"],      // dimension names
  { y: 0.5, x: 0.5 },    // pixel spacing (micrometers)
  { y: 0.0, x: 0.0 },    // origin offsets
  "my-image",
);

// 2. Populate pixel data
const data = new Uint16Array(512 * 512);
for (let i = 0; i < data.length; i++) data[i] = i % 65536;
await zarr.set(image.data, null, {
  data,
  shape: [512, 512],
  stride: [512, 1],
});

// 3. Build the Multiscales object
const axes = [
  createAxis("y", "space", "micrometer"),
  createAxis("x", "space", "micrometer"),
];
const datasets = [createDataset("0", [0.5, 0.5], [0.0, 0.0])];
const metadata = createMetadata(axes, datasets, "my-image");
const multiscales = createMultiscales([image], metadata);

// 4. Write to OME-TIFF
const buffer = await toOmeTiff(multiscales);

// Save to disk (Node.js / Bun)
await Bun.write("output.ome.tif", buffer);

Write options

const buffer = await toOmeTiff(multiscales, {
  compression: "deflate",  // "deflate" (default) or "none"
  compressionLevel: 6,     // 1-9, default 6 (only for deflate)
  dimensionOrder: "XYZCT", // IFD layout order, default "XYZCT"
  imageName: "my-image",   // name in OME-XML metadata
  creator: "my-app",       // creator string in OME-XML
  tileSize: 256,           // tile size in px (0 = strip-based), default 256
  concurrency: 4,          // parallel plane reads, default 4
  format: "auto",          // "auto" | "classic" | "bigtiff", default "auto"
  getPlane: zarrGet,       // custom plane reader (e.g. ngff-zarr's zarrGet)
});

Worker Pool: Offloading Compression to Web Workers

By passing a @fideus-labs/worker-pool instance, deflate compression (writes) and decompression (reads) run on Web Workers using the standard CompressionStream / DecompressionStream APIs — fully releasing the main thread.

Writing with a worker pool

import { toOmeTiff } from "@fideus-labs/fiff";
import WorkerPool from "@fideus-labs/worker-pool";

const pool = new WorkerPool(navigator.hardwareConcurrency ?? 4);

const buffer = await toOmeTiff(multiscales, {
  pool,              // tiles are compressed on workers
  compression: "deflate",
});

pool.terminateWorkers();

Reading with a worker pool

import { TiffStore } from "@fideus-labs/fiff";
import WorkerPool from "@fideus-labs/worker-pool";

const pool = new WorkerPool(4);

// Registers a worker-backed deflate decoder with geotiff.js.
// This is a global registration — it affects all geotiff instances.
const store = await TiffStore.fromUrl(
  "https://example.com/image.ome.tif",
  { pool },
);

// All subsequent chunk reads decompress on workers
const group = await zarr.open(store as unknown as zarr.Readable, { kind: "group" });
const arr = await zarr.open(group.resolve("0"), { kind: "array" });
const chunk = await zarr.get(arr);

pool.terminateWorkers();

Low-level: buildTiff with a pool

import { buildTiff, type WritableIfd } from "@fideus-labs/fiff";
import WorkerPool from "@fideus-labs/worker-pool";

const pool = new WorkerPool(4);
const ifds: WritableIfd[] = [/* ... */];

const buffer = await buildTiff(ifds, {
  compression: "deflate",
  pool,
});

pool.terminateWorkers();

Custom Plane Reader (getPlane)

By default toOmeTiff() reads each (C, Z, T) plane with the built-in zarr.get(). You can replace this with a custom callback via the getPlane option. This is useful for:

  • Offloading decompression -- ngff-zarr's zarrGet runs blosc decompression on a worker pool instead of the main thread.
  • Skipping decompression entirely -- When the in-memory zarr arrays were created with bytesOnlyCodecs() (no compression), reads are a trivial memcpy with zero overhead.
import { toOmeTiff } from "@fideus-labs/fiff"
import { bytesOnlyCodecs, toMultiscales, zarrGet } from "@fideus-labs/ngff-zarr"

// Create multiscales with no compression (OME-TIFF will re-compress with deflate)
const multiscales = await toMultiscales(ngffImage, {
  method: Methods.ITKWASM_GAUSSIAN,
  codecs: bytesOnlyCodecs(),
})

// Write OME-TIFF using zarrGet to read planes (worker-pool-aware)
const buffer = await toOmeTiff(multiscales, {
  compression: "deflate",
  getPlane: zarrGet,
})

The GetPlane callback type mirrors zarr.get():

type GetPlane = (
  data: zarr.Array<zarr.DataType, zarr.Readable>,
  selection: (number | null)[],
) => Promise<{ data: unknown }>

How it works

  • Workers use CompressionStream("deflate") / DecompressionStream("deflate") -- no fflate or other dependencies inside the worker
  • The worker script is inlined as a blob URL at runtime (no separate file to serve)
  • ArrayBuffers are transferred (zero-copy) between the main thread and workers
  • When the compression level is not the default (6), or no pool is provided, fiff falls back to the existing main-thread path (CompressionStream -> fflate)
  • The pool's bounded concurrency replaces unbounded Promise.all over tiles

Multi-resolution pyramids

When the Multiscales object contains multiple images (resolution levels), all sub-resolution levels are written as SubIFDs:

const fullRes = await createNgffImage([], [1024, 1024], "uint16", ["y", "x"], ...);
const halfRes = await createNgffImage([], [512, 512], "uint16", ["y", "x"], ...);
// ... populate both images with zarr.set() ...

const datasets = [
  createDataset("0", [0.5, 0.5], [0.0, 0.0]),
  createDataset("1", [1.0, 1.0], [0.0, 0.0]),
];
const metadata = createMetadata(axes, datasets, "pyramid");
const multiscales = createMultiscales([fullRes, halfRes], metadata);

const buffer = await toOmeTiff(multiscales);
// Result: OME-TIFF with full-res IFDs + half-res SubIFDs

Round-trip: write then read back

import { toOmeTiff, TiffStore } from "@fideus-labs/fiff";
import * as zarr from "zarrita";

const buffer = await toOmeTiff(multiscales);
const store = await TiffStore.fromArrayBuffer(buffer);
const group = await zarr.open(store as unknown as zarr.Readable, { kind: "group" });
const arr = await zarr.open(group.resolve("0"), { kind: "array" });
const result = await zarr.get(arr);
// result.data contains the original pixel values

🏭 Read API

Factory Methods

MethodDescription
TiffStore.fromUrl(url, opts?)Open from a remote URL (HTTP range requests)
TiffStore.fromArrayBuffer(buf)Open from an in-memory ArrayBuffer
TiffStore.fromBlob(blob)Open from a Blob or File
TiffStore.fromGeoTIFF(tiff)Open from an already-opened GeoTIFF instance

Options

All factory methods accept an optional TiffStoreOptions object:

OptionTypeDefaultDescription
offsetsnumber[]undefinedPre-computed IFD byte offsets for O(1) access
headersRecord<string, string>undefinedAdditional HTTP headers for remote TIFF requests
poolDeflatePoolundefinedWorker pool for offloading decompression (global geotiff decoder override)
workerUrlstringundefinedCustom worker script URL (only used when pool is provided)

Public Accessors

AccessorTypeDescription
store.levelsnumberNumber of resolution levels
store.dataTypeZarrDataTypeZarr data type string
store.omeOmeImage[]Parsed OME-XML images (if present)
store.pyramidInfoPyramidInfoPyramid structure details
store.dimensionNamesstring[]Dimension names (e.g. ["t", "c", "z", "y", "x"])
store.getShape(l)number[]Shape for resolution level l
store.getChunkShape(l)number[]Chunk shape for resolution level l

📝 Write API

toOmeTiff(multiscales, options?)

ParameterTypeDescription
multiscalesMultiscalesngff-zarr Multiscales object with populated pixel data
optionsWriteOptionsOptional writer configuration

Returns: Promise<ArrayBuffer> -- a complete OME-TIFF file.

WriteOptions

OptionTypeDefaultDescription
compression"none" | "deflate""deflate"Pixel data compression
compressionLevelnumber6Deflate level 1-9 (higher = smaller)
dimensionOrderstring"XYZCT"IFD plane layout order
imageNamestring"image"Image name in OME-XML
creatorstring"fiff"Creator string in OME-XML
tileSizenumber256Tile size (0 = strip-based, must be x16)
concurrencynumber4Max parallel plane reads
format"auto" | "classic" | "bigtiff""auto"TIFF format (auto-detects BigTIFF > 4 GB)
poolDeflatePoolundefinedWorker pool for offloading deflate compression to Web Workers
workerUrlstringundefinedCustom worker script URL (only used when pool is provided)
getPlaneGetPlaneundefinedCustom plane reader replacing the internal zarr.get() call (see below)

🛠️ Development

📋 Prerequisites

🔧 Setup

git clone https://github.com/fideus-labs/fiff.git
cd fiff
bun install

🗂️ Project Structure

src/
  index.ts           # Public API exports
  tiff-store.ts      # TiffStore class (AsyncReadable implementation)
  metadata.ts        # Zarr v3 / OME-Zarr 0.5 metadata synthesis
  ome-xml.ts         # OME-XML parser (dimensions, channels, DimensionOrder)
  ifd-indexer.ts     # IFD-to-pyramid-level mapping (SubIFD/legacy/COG)
  chunk-reader.ts    # Pixel data reading via geotiff.js readRasters
  dtypes.ts          # TIFF ↔ Zarr data_type mapping
  utils.ts           # Key parsing, pixel window computation, encoding
  write.ts           # High-level toOmeTiff() writer
  tiff-writer.ts     # Low-level TIFF binary builder (IFDs, SubIFDs, deflate)
  ome-xml-writer.ts  # OME-XML generation from Multiscales metadata
  deflate-worker.ts  # Inline Web Worker source for compress/decompress
  worker-utils.ts    # Worker pool task factories and blob URL helper
  worker-decoder.ts  # Worker-backed geotiff deflate decoder
test/
  fixtures.ts        # Test TIFF generation helpers
  *.test.ts          # 201 tests across 11 files

📝 Commands

CommandDescription
bun run buildBuild to dist/ (ESM + declarations)
bun testRun all tests
bun run typecheckType-check the full project

🤝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md for setup instructions, code style guidelines, and the pull request workflow.

📄 License

MIT -- Copyright (c) Fideus Labs LLC

版本与发布记录

@fideus-labs/fiff 共发布 16 个版本,首次发布于 2026/02/15;过去 12 个月发布了 16 个版本。

版本发布日期距今
0.8.2latest2026/10/091 个月前
0.8.12026/10/081 个月前
0.8.02026/09/301 个月前
0.7.02026/02/218 个月前
0.6.02026/02/208 个月前
0.5.22026/02/188 个月前

关于 @fideus-labs/fiff 的常见问题

@fideus-labs/fiff 是什么?

Present TIFF files as a zarrita.js Zarr store following the NGFF OME-Zarr data model

如何安装 @fideus-labs/fiff?

在项目目录运行 npm install @fideus-labs/fiff,或使用 pnpm add / yarn add / bun add。

@fideus-labs/fiff 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@fideus-labs/fiff 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@fideus-labs/fiff 可以商用吗?

@fideus-labs/fiff 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@fideus-labs/fiff 的下载量有多少?

根据 npm 官方下载统计,@fideus-labs/fiff 在 2026/10/15 – 2026/11/13 这 30 天内被下载 7,225 次,日均约 241 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@fideus-labs/fiff 还在维护吗?

最新版本 0.8.2 发布于 2026/10/09(1 个月前);至今共发布 16 个版本,其中过去 12 个月发布了 16 个;首次发布于 2026/02/15。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。