The SFLZ file format ==================== ``.sflz`` (Surfalize LZ) is surfalize's native file format. It is designed to store one height (topography) channel together with an arbitrary number of additional image layers and free-form metadata, while keeping the file size as small as possible through optional compression and optional integer quantization. Design goals ------------ * **Small files.** Layer data may be compressed with zlib or LZMA, and the height channel may be quantized to a compact integer type. * **Lossless when needed.** Storing the height channel in a floating point type reproduces the data bit-for-bit, including non-measured points (``NaN``). * **Arbitrary channels.** Besides the height channel, any number of named image layers (grayscale or multi-channel, e.g. RGB) can be stored, each with its own dimensions and data type. * **Free-form metadata.** Metadata is stored as a JSON document, so strings, numbers, booleans, nested structures and timestamps all round-trip. * **Unit aware.** The lateral spacing and height values carry an explicit length unit (micrometers by default), converted to and from surfalize's internal micrometers on write and read. * **Versioned.** The file carries a version string so the layout can evolve in the future without breaking existing readers. The current version is 1.0. All multi-byte numbers are little-endian. Overall structure ----------------- .. code-block:: text +-------------------------------+ | magic "SFLZ" (4) | | version string (10, ASCII) | +-------------------------------+ | file header (33 bytes) | +-------------------------------+ | metadata blob (JSON, UTF-8) | +-------------------------------+ | layer 0 header + data | <- height channel ("topography") | layer 1 header + data | <- optional image layer | ... | +-------------------------------+ Header ------ The 4-byte magic ``SFLZ`` is followed by a 10-byte, space-padded ASCII version string (``"1.0"``). The fixed-size file header follows: .. list-table:: :header-rows: 1 :widths: 25 15 60 * - Field - Type - Description * - ``step_x`` - float64 - Pixel spacing in x, expressed in ``unit``. * - ``step_y`` - float64 - Pixel spacing in y, expressed in ``unit``. * - ``unit`` - 8 bytes - Length unit of ``step_x``, ``step_y`` and the height channel (e.g. ``um``, ``nm``, ``mm``, ``m``). Defaults to ``um``. * - ``num_layers`` - uint16 - Total number of layers, including the height channel. * - ``num_metadata`` - uint16 - Number of top-level metadata entries (informational). * - ``compression_algorithm`` - uint8 - ``0`` = none, ``1`` = zlib, ``2`` = LZMA. Applies to every layer. * - ``metadata_size`` - uint32 - Length in bytes of the metadata blob that follows the header. Metadata blob ------------- Immediately after the header come ``metadata_size`` bytes of UTF-8 encoded JSON, representing the ``Surface.metadata`` dictionary. JSON-native types (str, int, float, bool, list, dict, ``null``) are preserved. ``datetime`` objects are encoded as ``{"__datetime__": ""}`` and restored on read. Any other type is stored as its string representation. If there is no metadata, ``metadata_size`` is ``0`` and the blob is empty. Layers ------ Each layer consists of a variable-length header followed by its compressed data block. The header is: .. list-table:: :header-rows: 1 :widths: 22 15 63 * - Field - Type - Description * - ``name_length`` - uint16 - Length of the layer name in bytes. * - ``name`` - bytes - Layer name (UTF-8). The height channel is named ``topography``. * - ``is_height`` - bool (uint8) - ``True`` for the height channel, ``False`` for image layers. * - ``width`` - uint32 - Number of columns. * - ``height`` - uint32 - Number of rows. * - ``channels`` - uint8 - Number of channels per pixel (``1`` for grayscale, ``3`` for RGB, ...). * - ``datatype`` - 4 bytes - NumPy dtype string of the stored samples, e.g. ``