Metadata-Version: 2.4
Name: focusweave
Version: 2.0.0
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: Implementation :: CPython
Requires-Dist: numpy>=1.24
License-File: LICENSE
Summary: Focus stacking via Laplacian pyramid fusion
Requires-Python: >=3.10
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://github.com/AnthonyvW/FocusWeave

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](https://github.com/AnthonyvW/FocusWeave/releases), 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](#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`:

```python
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:

```python
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:

```python
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:

```python
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:

```python
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](https://maturin.rs):

    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](RUNNING.md) walks through building, running and troubleshooting
in more detail. [docs/PORTING-NOTES.md](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.
