lapstack

Installing and running

lapstack is a single native binary. Compared with the browser app it has no stack-size ceiling, streams frames from disk so memory stays flat however long the stack, takes an optional CUDA build, and scripts.

Getting it

Each release on GitHub carries an archive per platform — Linux x86-64, Windows x86-64, macOS on Apple silicon and on Intel — with the lapstack binary, the raw decoder beside it, the license, the third-party notices and the decoder's source. Unpack it anywhere and put the directory on your PATH, or run the binary from there. The Linux and Windows builds have the CUDA path; every build has the wgpu one. See the download page.

Or build from source, from a checkout of the repository:

shell
cargo build --release
# the binary lands at target/release/lapstack, and next to it
# liblapstack_raw.so (.dylib on macOS, lapstack_raw.dll on Windows)

The second file is the camera raw decoder, a component of its own that lapstack loads when it meets a raw file: keep the two together, or point LAPSTACK_RAW_LIB at the library. Without it every other input still works and a raw is refused with a message saying what is missing. The decoder is free software under the LGPL (it is built on rawler); its source ships with lapstack, and a build of your own dropped in its place is what lapstack will use.

With the GPU paths — CUDA, and the browser app's own kernels on any other GPU through Vulkan, Metal or Direct3D 12:

shell
cargo build --release --features gpu,wgpu -p lapstack-cli

Both are loaded at run time, so no CUDA toolkit is needed at build time and a binary built with the features still runs on a machine without a GPU — --gpu says what it looked for and the CPU does the work. See GPU acceleration.

The shape of a command

shell
lapstack [options] frame1 frame2 ...

A directory among the inputs stands for the image files in it, in natural order (f2 before f10). So both of these work:

shell
lapstack -o stacked.tif frames/*.tif
lapstack -o stacked.tif frames/

The command you will actually type

shell
lapstack --align-coarsen 2 --save-depth -o stacked.tif frames/*.tif

That aligns the frames, fuses them, runs the depth pass, writes stacked.tif cropped to the area every frame covers, and writes the depth map beside it.

With CUDA:

shell
lapstack --gpu --gpu-align --align-coarsen 2 --save-depth -o stacked.tif frames/*.tif

On frames that are already registered — a microscope stack, or frames you aligned elsewhere — --no-align streams them straight from disk:

shell
lapstack --no-align -o stacked.tif aligned/*.png

Raws work directly, and a .dng output writes a linear DNG:

shell
lapstack --align-coarsen 2 -o stacked.dng shoot/*.NEF

Input and output

Input is PNG, JPEG or TIFF (eight- or sixteen-bit), or a camera raw — NEF, CR2, CR3, ARW, DNG, RAF, ORF, RW2, PEF, IIQ, 3FR and the rest. Raws are developed as shot, with no exposure adjustment.

Output keeps the input's bit depth; sixteen bits need PNG or TIFF, and a raw counts as sixteen-bit. The output carries the first frame's metadata — EXIF, ICC profile and XMP — unless --no-metadata says otherwise. See formats and metadata.

TIFF output is written by lapstack's own uncompressed writer, which is why it can put EXIF and GPS sub-IFDs where they belong.

The options worth knowing first

OptionWhy
--draft NRun everything at 1/2^N the size. The fastest way to check settings before committing
--align-coarsen 2The CLI defaults to 0 (full resolution). Passing 2 is a large speed-up at sub-pixel accuracy, and is what the app does
--no-alignFrames already registered — skips most of the work
--save-depthWrite the depth map beside the output
--halo-control PTurn on when you can see a rim beside a bright object
--wavAlso write the weighted average
--split RULE + --dry-runBatch, and check the batch before running it
--gpu --gpu-alignThe GPU paths — CUDA, or any GPU through wgpu — when the build has them

lapstack --help lists every option; the option reference groups them.

Drafts

shell
lapstack --draft 2 --save-depth -o check.tif frames/*.tif

Every frame is block-averaged by 2^N as it is decoded, so the whole run — alignment, fusion, depth pass, every output — happens at 1/2^N the size. Five 45-megapixel raws at --draft 2 align, fuse and run the depth pass in about four seconds, raw development included.

--crop and the scale bar's calibration follow the frame's own pixels, so a draft's output is exactly the full run's output, shrunk.

What the log tells you

The run reports, as it goes:

  • Each frame decoded, its registration (shift, scale, rotation) and its brightness gain
  • Frames resampled because they were not frame 0's size
  • The crop window the automatic crop settled on
  • The dust spots found, if a dust map was given
  • What the scale bar came to, if one was asked for
  • Which cue decided the near end, for the stereo and 3D outputs
  • The frames' scale trend over the stack, as a hint

Exit status

0 on success. 1 if a run failed, or if any stack of a --split batch failed — the batch continues past a failure and summarizes at the end.

Source: sites/lapstack/content/docs/cli/index.md