Skip to content

Repository files navigation

FocusWeave

Focus stacking via Laplacian pyramid fusion. Takes a set of images captured at different focus distances and combines them into a single image where the entire subject is sharp.

Download

Pre-built archives for Windows, macOS and Linux are on the releases tab, alongside Python wheels. FocusWeave can also be installed via

pip install FocusWeave

Basic usage

Point focusweave at a folder of images and it will produce stacked.jpg inside that folder:

focusweave path/to/images/

To output to a specific file, format it as so.

focusweave path/to/images/ --output result.tiff

Command-line options

Output options

--output PATH           Output file path, supports JPG, PNG, TIFF, and WebP images.
--quality N             JPEG output quality 1–95 (default: 95).

Alignment options
--no-align              Skip alignment entirely. Use when images are already registered.
--reference N           Index of the image to align all others to (default: middle image).
--global-align          Align every image directly to the reference instead of
                        chaining through neighbours. More robust when images are not
                        ordered by similarity.
--full-res              Run the fine alignment pass at full resolution instead of the
                        default 1024px cap. More accurate but significantly slower.
--min-shift PIXELS      Minimum shift in pixels before alignment is applied (default: 5.0).
--no-rotation           Suppress rotation correction during alignment.
--no-scale              Suppress scale correction during alignment.
--no-shear              Suppress shear correction during alignment.
--no-translation        Suppress translation correction during alignment.

Canvas options
--keep-size             Keep the output the same size as the inputs; warps are applied
                        in-place rather than expanding the canvas.
--crop                  Crop the output to the intersection of all image extents —
                        removes all border regions but produces a smaller result.
--no-fill               Fill border regions with black instead of reflecting edge pixels.
                        Pairs naturally with --crop to trim the borders away.

Stacking options
--levels N              Laplacian pyramid levels (default: auto from image size).
--sharpness EXPONENT    Weight sharpness exponent (default: 4.0). Higher values favour
                        the sharpest image more aggressively at each pixel, approaching
                        a hard winner-take-all selection. Useful range is roughly
                        1.0 (soft blend) to 8.0 (near-hard selection).
--workers N             Number of frames fused concurrently. The default is automatic:
                        one per core, capped so the workers' buffers fit in free
                        memory. Each worker costs roughly 110 MB per megapixel of
                        output (160 MB for 16-bit sources). Pass a number to
                        override it.

Culling options
--cull [THRESHOLD]      Remove wholly out-of-focus images before stacking. Each frame
                        is scored by the high- to low-frequency energy ratio of its
                        Tenengrad response; frames scoring below THRESHOLD are dropped.
                        The threshold is absolute, not relative to the sharpest frame.
                        THRESHOLD defaults to 0.6 when --cull is given without a value.
                        At least the two sharpest frames are always retained.

Slabbing options

Slabbing splits a large image set into overlapping sub-stacks, stacks each one independently, then fuses the results. This can improve quality by reducing the number of images competing in each fusion pass.

--slab SIZE OVERLAP     Enable slabbing. SIZE is images per sub-stack; OVERLAP is how
                        many images adjacent slabs share. Example: --slab 20 5
--output-steps          Save each intermediate slab result to a focusweave_slabs/
                        folder inside the output directory. Requires --slab.
--only-slab             Stop after producing slabs; skip the final fusion. Implies
                        --output-steps. Requires --slab.
--recursive-slab        If the layer-1 slab results still outnumber SIZE, apply
                        slabbing again as layer 2, and so on, until the count fits in
                        a single stack pass.
--slab-format EXT       File format for slab output images (e.g. tiff, png, jpg).
                        Defaults to tiff. Requires --output-steps or --only-slab.

Batch options

Batch mode stacks a folder of folders, treating each subfolder as its own set:

focusweave --batch path/to/shoot/
focusweave --batch path/to/shoot/ --output path/to/results/

--batch FOLDER          Stack each subfolder of FOLDER separately. Results are
                        named after their subfolder and written into FOLDER,
                        or into --output, which is then a folder rather than a
                        file. Every other option applies to each set. A set that
                        fails is reported and the rest carry on.
--batch-format EXT      Format for batch results: inherit (the default), or an
                        extension such as tiff, png or jpg. inherit uses the
                        most common extension among each set's images, so a set
                        of 16-bit TIFFs comes out as a 16-bit TIFF.

Other

--timings               Print how long each stage took.
--version, -V           Show the version number and exit.
--opencv-version        Show the version of the OpenCV library in use and exit.
--formats               List the supported image extensions and exit.
--help                  Displays the list of commands

Memory usage

Peak memory is roughly 110 MB per megapixel of output per worker, or 160 MB for 16-bit sources. The worker count defaults to one per core, capped so those buffers fit in free memory. To cut memory to the minimum at the cost of processing time, set workers to 1:

focusweave path/to/images/ --workers 1

Python API

focusweave can be installed into your environment directly via pip:

pip install focusweave

Installing from git builds from source, so it needs the prerequisites listed under Building from source.

All public symbols are importable from the top-level focusweave package. Only numpy is required at runtime. The main entry point is FocusStackConfig and run:

from pathlib import Path
from focusweave import FocusStackConfig, run

cfg = FocusStackConfig(images=Path("path/to/images/"))
result = run(cfg)

# result.image is a uint8 (or uint16) RGB numpy array

Images can be supplied as a folder path, a list of Path objects, or a list of pre-loaded numpy arrays:

import numpy as np
from focusweave import FocusStackConfig, run

images: list[np.ndarray] = [...]  # pre-loaded uint8 RGB arrays
cfg = FocusStackConfig(images=images, workers=4)
result = run(cfg)

A progress callback can be passed to run to receive stage-by-stage updates:

from pathlib import Path
from focusweave import FocusStackConfig, run

def on_progress(fraction: float, stage: str, message: str) -> None:
    print(f"[{stage}] {fraction * 100:.1f}%  {message}")

cfg = FocusStackConfig(images=Path("path/to/images/"))
result = run(cfg, progress=on_progress)

Long-running stacks can be cancelled by supplying an interrupt callback in the config. If it returns True at any checkpoint, Interrupted is raised:

from pathlib import Path
from focusweave import FocusStackConfig, Interrupted, run

cancelled = False

cfg = FocusStackConfig(
    images=Path("path/to/images/"),
    interrupt=lambda: cancelled,
)

try:
    result = run(cfg)
except Interrupted:
    print("Stack cancelled.")

Images can be read and written without pulling in another imaging library:

from pathlib import Path
from focusweave import load_image, save_image

frame = load_image(Path("frame_00.tiff"))   # uint8 or uint16 RGB
save_image(result.image, Path("stacked.tiff"))

See python/focusweave/api_example.py for a more complete example, or run it:

python -m focusweave.api_example path/to/images/ --streaming

Building from source

A Rust toolchain (1.82 or newer) is required, plus OpenCV 4 development files and libclang, which the opencv crate compiles and links against. Python 3.10 or newer if you want the bindings.

sudo apt-get install libopencv-dev libclang-dev   # Debian, Ubuntu
brew install opencv llvm                          # macOS
choco install llvm opencv                         # Windows, plus the
                                                  # environment variables in
                                                  # RUNNING.md

Command line binary:

cargo build --release -p focusweave-cli
./target/release/focusweave --help

Python package, via maturin:

pip install maturin
maturin develop --release

Once installed, the focusweave command is available on your PATH and is the same CLI as the native binary.

RUNNING.md walks through building, running and troubleshooting in more detail. docs/PORTING-NOTES.md records how the Rust implementation relates to the original Python one, including where the two deliberately differ.

Testing

cargo test --workspace

covers the parts that stand alone from image data. The port was validated against the original Python implementation until the two were meant to diverge; the last commit with that comparison is 638c5f0.

Algorithms

The focus stacking algorithm is based on Laplacian pyramid fusion as described in:

Wang, W., & Chang, F. (2011). A Multi-focus Image Fusion Method Based on Laplacian Pyramid. Journal of Computers.

Image alignment uses a custom coarse-to-fine pipeline built on an enhanced correlation coefficient solver (Evangelidis & Psarakis, 2008), implemented here. It seeds ECC with a phase-correlation translation estimate, applies CLAHE normalisation and a focus-aware pixel mask to concentrate the optimisation on sharp, informative regions, then validates the result against the seed to reject false minima. Warps are composed mathematically through a neighbour chain so interpolation error does not accumulate across the stack.

About

A focus stacking utility designed to be used from the commandline or programmatically

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Contributors

Languages