Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "python3 -c \"import sys,json; d=json.load(sys.stdin); print(d['tool_input'].get('file_path',''))\" | { read -r f; [[ \"$f\" == *.jl ]] && runic -i \"$f\"; } 2>/dev/null || true",
"statusMessage": "Formatting Julia with Runic..."
}
]
}
]
}
}
2 changes: 2 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# runic formatting
a885a8b
19 changes: 18 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,24 @@ jobs:
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v4
- uses: codecov/codecov-action@v5
with:
file: lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
docs:
name: Documentation
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
with:
version: '1'
- uses: julia-actions/cache@v2
- name: registry_add
run: julia -e 'using Pkg; pkg"registry add General https://github.com/HolyLab/HolyLabRegistry.git"'
- uses: julia-actions/julia-docdeploy@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
deps/deps.jl
Manifest.toml
Manifest-v*.toml
docs/build/
14 changes: 12 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "RegisterDriver"
uuid = "935ac36e-2656-11e9-1e3b-cbaa636797af"
version = "1.0.0"
authors = ["Tim Holy <tim.holy@gmail.com>"]
version = "0.2.4"

[deps]
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
Expand All @@ -16,19 +16,29 @@ SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[compat]
Aqua = "0.8"
AxisArrays = "0.4"
Distributed = "1"
Documenter = "1"
ExplicitImports = "1"
Formatting = "0.4"
HDF5 = "0.12, 0.13, 0.14, 0.15, 0.16, 0.17"
ImageCore = "0.8.1, 0.9, 0.10"
ImageMetadata = "0.9"
JLD = "0.9, 0.10, 0.11, 0.12, 0.13"
RegisterCore = "0.2, 1"
RegisterWorkerShell = "0.2, 1"
SharedArrays = "1"
StaticArrays = "0.11, 0.12, 1"
Test = "1"
julia = "1.10"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
AxisArrays = "39de3d68-74b9-583c-8d2d-e117c070f3a9"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["AxisArrays", "Test"]
test = ["Aqua", "AxisArrays", "Documenter", "ExplicitImports", "Test"]
95 changes: 92 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,95 @@
# RegisterDriver.jl

This package supports distributed computing to accelerate image registration.
It wraps the [BlockRegistration](https://github.com/HolyLab/BlockRegistration.jl) framework.
[![CI](https://github.com/HolyLab/RegisterDriver.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/HolyLab/RegisterDriver.jl/actions/workflows/CI.yml)
[![codecov](https://codecov.io/gh/HolyLab/RegisterDriver.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/HolyLab/RegisterDriver.jl)
[![docs-stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://holylab.github.io/RegisterDriver.jl/stable)
[![docs-dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://holylab.github.io/RegisterDriver.jl/dev)
[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)

For an introduction, see the documentation for [RegisterWorkerApertures](https://github.com/HolyLab/RegisterWorkerApertures.jl).
RegisterDriver.jl drives image registration workflows in the
[BlockRegistration](https://github.com/HolyLab/BlockRegistration.jl) ecosystem.
It runs `AbstractWorker` algorithms frame-by-frame across an image stack,
optionally in parallel across multiple threads, and saves results to disk in JLD
format.

## Installation

RegisterDriver.jl is distributed through the
[HolyLab registry](https://github.com/HolyLab/HolyLabRegistry).
Add that registry before installing:

```julia
using Pkg
pkg"registry add https://github.com/HolyLab/HolyLabRegistry.git"
Pkg.add("RegisterDriver")
```

## Concepts

### Workers and monitors

A *worker* is an `AbstractWorker` instance (from a `RegisterWorker*` package
such as
[RegisterWorkerApertures](https://github.com/HolyLab/RegisterWorkerApertures.jl))
that encapsulates a registration algorithm for a particular compute device.
Before running, create a *monitor* dict that names which computed quantities to
collect from each frame:

```julia
algorithm = MyWorker(fixed, params...) # construct an AbstractWorker
mon = monitor(algorithm, (:tform, :mismatch)) # fields to record
```

### The driver

`driver` iterates the worker over every frame of an image stack. It handles
initialisation, per-frame execution, and teardown, then either saves the
collected values to a JLD file or returns them in-memory for single-image use.

## Usage

### Single image (in-memory)

```julia
result = driver(algorithm, img, mon)
tform = result[:tform]
```

### Image stack saved to a file

```julia
driver("results.jld", algorithm, img, mon)
```

Results for each frame are stored inside the JLD file: scalars as plain
vectors, bit-type arrays as higher-dimensional HDF5 datasets, and other values
inside per-frame `"stack<n>"` groups.

### Parallel multi-threaded registration

Start Julia with multiple threads (e.g. `julia --threads=4`), then assign one
worker per worker thread:

```julia
tids = threadids()
algorithms = [MyWorker(fixed, params...; workertid=t) for t in tids]
monitors = [monitor(algorithms[1], (:tform, :mismatch)) for _ in tids]
driver("results.jld", algorithms, monitors)
```

`threadids()` returns the sorted list of thread IDs that Julia actually
schedules `@threads` tasks on (typically excluding thread 1, which drives the
writer).

### Loading a device-specific backend

Some workers require a device-specific mismatch package (e.g. a CUDA backend)
to be loaded on the driver process before registration starts:

```julia
mm_package_loader(algorithm)
driver("results.jld", algorithm, img, mon)
```

For a full introduction see the
[RegisterWorkerApertures documentation](https://github.com/HolyLab/RegisterWorkerApertures.jl).
7 changes: 7 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
RegisterDriver = "935ac36e-2656-11e9-1e3b-cbaa636797af"

[compat]
Documenter = "1"
julia = "1.10"
22 changes: 22 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Documenter
using RegisterDriver

DocMeta.setdocmeta!(RegisterDriver, :DocTestSetup, :(using RegisterDriver); recursive=true)

makedocs(;
modules=[RegisterDriver],
sitename="RegisterDriver.jl",
checkdocs=:exports,
format=Documenter.HTML(;
canonical="https://holylab.github.io/RegisterDriver.jl",
),
pages=[
"Home" => "index.md",
"API Reference" => "api.md",
],
)

deploydocs(;
repo="github.com/HolyLab/RegisterDriver.jl",
devbranch="master",
)
14 changes: 14 additions & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# API Reference

## Running registration

```@docs
driver
```

## Utilities

```@docs
mm_package_loader
threadids
```
96 changes: 96 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# RegisterDriver.jl

```@docs
RegisterDriver
```

RegisterDriver.jl drives image registration workflows in the
[BlockRegistration](https://github.com/HolyLab/BlockRegistration.jl) ecosystem.
It runs `AbstractWorker` algorithms frame-by-frame across an image stack,
optionally in parallel across multiple threads, and saves results to disk in JLD
format.

## Installation

RegisterDriver.jl is distributed through the
[HolyLab registry](https://github.com/HolyLab/HolyLabRegistry).
Add that registry before installing:

```julia
using Pkg
pkg"registry add https://github.com/HolyLab/HolyLabRegistry.git"
Pkg.add("RegisterDriver")
```

## Concepts

### Workers and monitors

A *worker* is an `AbstractWorker` instance (from a `RegisterWorker*` package
such as
[RegisterWorkerApertures](https://github.com/HolyLab/RegisterWorkerApertures.jl))
that encapsulates a registration algorithm for a particular compute device.
Before running, create a *monitor* dict that names which computed quantities to
collect from each frame:

```julia
algorithm = MyWorker(fixed, params...) # construct an AbstractWorker
mon = monitor(algorithm, (:tform, :mismatch)) # fields to record
```

The `monitor` function is provided by
[RegisterWorkerShell](https://github.com/HolyLab/RegisterWorkerShell.jl).

### The driver

[`driver`](@ref) iterates the worker over every frame of an image stack. It
handles initialisation, per-frame execution, and teardown, then either saves the
collected values to a JLD file or returns them in-memory for single-image use.

## Usage

### Single image (in-memory)

```julia
result = driver(algorithm, img, mon)
tform = result[:tform]
```

### Image stack saved to a file

```julia
driver("results.jld", algorithm, img, mon)
```

Results for each frame are stored inside the JLD file: scalars as plain
vectors, bit-type arrays as higher-dimensional HDF5 datasets, and other values
inside per-frame `"stack<n>"` groups.

### Parallel multi-threaded registration

Start Julia with multiple threads (e.g. `julia --threads=4`), then assign one
worker per worker thread:

```julia
tids = threadids()
algorithms = [MyWorker(fixed, params...; workertid=t) for t in tids]
monitors = [monitor(algorithms[1], (:tform, :mismatch)) for _ in tids]
driver("results.jld", algorithms, monitors)
```

[`threadids()`](@ref) returns the sorted list of thread IDs that Julia actually
schedules `@threads` tasks on (typically excluding thread 1, which drives the
writer task).

### Loading a device-specific backend

Some workers require a device-specific mismatch package (e.g. a CUDA backend)
to be loaded on the driver process before registration starts:

```julia
mm_package_loader(algorithm)
driver("results.jld", algorithm, img, mon)
```

For a full introduction see the
[RegisterWorkerApertures documentation](https://github.com/HolyLab/RegisterWorkerApertures.jl).
Loading
Loading