l1tv 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- l1tv-0.1.0/.github/workflows/ci.yml +70 -0
- l1tv-0.1.0/.github/workflows/release.yml +111 -0
- l1tv-0.1.0/.gitignore +50 -0
- l1tv-0.1.0/Auxiliary/deltaSNR.m +14 -0
- l1tv-0.1.0/Auxiliary/distAngle.m +14 -0
- l1tv-0.1.0/Auxiliary/distTransformL1.m +15 -0
- l1tv-0.1.0/Auxiliary/randCP.m +13 -0
- l1tv-0.1.0/Auxiliary/randl.m +10 -0
- l1tv-0.1.0/Auxiliary/wrapAngle.m +7 -0
- l1tv-0.1.0/CHANGELOG.md +59 -0
- l1tv-0.1.0/CITATION.cff +41 -0
- l1tv-0.1.0/Cargo.lock +230 -0
- l1tv-0.1.0/Cargo.toml +41 -0
- l1tv-0.1.0/Demos/demo_L1TV_Circ.m +40 -0
- l1tv-0.1.0/Demos/demo_L1TV_Real.m +37 -0
- l1tv-0.1.0/Docs/L1TV_Circ.png +0 -0
- l1tv-0.1.0/Docs/L1TV_Real.png +0 -0
- l1tv-0.1.0/L1TV_Circ.m +80 -0
- l1tv-0.1.0/L1TV_Real.m +62 -0
- l1tv-0.1.0/LICENSE +21 -0
- l1tv-0.1.0/PKG-INFO +147 -0
- l1tv-0.1.0/PORTED_BY.md +58 -0
- l1tv-0.1.0/README.md +23 -0
- l1tv-0.1.0/README_PYTHON.md +113 -0
- l1tv-0.1.0/demos_python/demo_circ.py +99 -0
- l1tv-0.1.0/demos_python/demo_real.py +95 -0
- l1tv-0.1.0/l1tv/__init__.py +161 -0
- l1tv-0.1.0/pyproject.toml +62 -0
- l1tv-0.1.0/setPath.m +8 -0
- l1tv-0.1.0/src/circ.rs +359 -0
- l1tv-0.1.0/src/dist_transform.rs +201 -0
- l1tv-0.1.0/src/lib.rs +61 -0
- l1tv-0.1.0/src/real.rs +282 -0
- l1tv-0.1.0/tests/conftest.py +45 -0
- l1tv-0.1.0/tests/make_fixtures.m +89 -0
- l1tv-0.1.0/tests/matlab_fixtures/circ_alpha0.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/circ_branchcut.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/circ_corners.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/circ_huge.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/circ_n100.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/circ_n100_w.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/circ_n2000.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/circ_tiny.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/real_alpha0.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/real_huge.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/real_int.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/real_n100.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/real_n100_w.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/real_n2000.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/real_small.mat +0 -0
- l1tv-0.1.0/tests/matlab_fixtures/real_tiny.mat +0 -0
- l1tv-0.1.0/tests/test_circ.py +99 -0
- l1tv-0.1.0/tests/test_matlab_parity.py +105 -0
- l1tv-0.1.0/tests/test_real.py +138 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
rust:
|
|
10
|
+
name: Rust ${{ matrix.os }}
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
# Pin Python so pyo3-ffi's build script doesn't pick up the runner's
|
|
19
|
+
# default Python (now 3.14 on GHA's latest images), which is newer than
|
|
20
|
+
# pyo3 0.24.2's supported range.
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.12"
|
|
24
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
25
|
+
with:
|
|
26
|
+
components: clippy, rustfmt
|
|
27
|
+
- uses: Swatinem/rust-cache@v2
|
|
28
|
+
- name: cargo fmt
|
|
29
|
+
run: cargo fmt --all -- --check
|
|
30
|
+
# clippy is platform-agnostic in intent; running it on one platform is
|
|
31
|
+
# enough and avoids macOS/Windows-specific lint flakes.
|
|
32
|
+
- name: cargo clippy
|
|
33
|
+
if: matrix.os == 'ubuntu-latest'
|
|
34
|
+
run: cargo clippy --all-targets -- -D warnings
|
|
35
|
+
- name: cargo test
|
|
36
|
+
run: cargo test --release
|
|
37
|
+
|
|
38
|
+
python:
|
|
39
|
+
name: Python ${{ matrix.python-version }} ${{ matrix.os }}
|
|
40
|
+
runs-on: ${{ matrix.os }}
|
|
41
|
+
strategy:
|
|
42
|
+
fail-fast: false
|
|
43
|
+
matrix:
|
|
44
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
45
|
+
python-version: ["3.9", "3.12"]
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
- uses: actions/setup-python@v5
|
|
49
|
+
with:
|
|
50
|
+
python-version: ${{ matrix.python-version }}
|
|
51
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
52
|
+
- uses: Swatinem/rust-cache@v2
|
|
53
|
+
# PEP 517 install — pulls maturin from build-system.requires, builds the
|
|
54
|
+
# Rust extension via maturin, and installs the package + test extras
|
|
55
|
+
# (pytest, scipy, matplotlib). Avoids the venv-required `maturin develop`
|
|
56
|
+
# path that fails on stock GitHub-hosted runners.
|
|
57
|
+
- name: install l1tv (PEP 517)
|
|
58
|
+
run: pip install ".[test]"
|
|
59
|
+
# Run pytest and demos from a directory that does NOT contain `l1tv/`,
|
|
60
|
+
# otherwise Python's cwd-on-sys.path rule shadows the installed package
|
|
61
|
+
# with the source-tree copy (which lacks the compiled `_core` extension).
|
|
62
|
+
- name: pytest
|
|
63
|
+
working-directory: ${{ runner.temp }}
|
|
64
|
+
run: pytest -v ${{ github.workspace }}/tests
|
|
65
|
+
- name: smoke demos
|
|
66
|
+
working-directory: ${{ runner.temp }}
|
|
67
|
+
shell: bash
|
|
68
|
+
run: |
|
|
69
|
+
python "${GITHUB_WORKSPACE}/demos_python/demo_real.py"
|
|
70
|
+
python "${GITHUB_WORKSPACE}/demos_python/demo_circ.py"
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
id-token: write # for trusted publishing to PyPI
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
linux:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
target: [x86_64, aarch64]
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- uses: actions/setup-python@v5
|
|
23
|
+
with: { python-version: "3.12" }
|
|
24
|
+
- uses: PyO3/maturin-action@v1
|
|
25
|
+
with:
|
|
26
|
+
target: ${{ matrix.target }}
|
|
27
|
+
# Drop --zig and rely on `manylinux: auto`. With --zig, maturin
|
|
28
|
+
# cross-links on the host and needs an aarch64-targeted Python on
|
|
29
|
+
# PATH (which there isn't), causing the aarch64 build to fail with
|
|
30
|
+
# "Couldn't find any python interpreters". `manylinux: auto` runs
|
|
31
|
+
# the build inside the official quay.io/pypa/manylinux*_<arch>
|
|
32
|
+
# container, which ships /opt/python/cp3X-cp3X/bin/python for
|
|
33
|
+
# every supported version; --find-interpreter picks them up so we
|
|
34
|
+
# get a wheel per Python per arch.
|
|
35
|
+
args: --release --out dist --find-interpreter
|
|
36
|
+
manylinux: auto
|
|
37
|
+
- uses: actions/upload-artifact@v4
|
|
38
|
+
with:
|
|
39
|
+
name: wheels-linux-${{ matrix.target }}
|
|
40
|
+
path: dist
|
|
41
|
+
|
|
42
|
+
macos:
|
|
43
|
+
# macos-latest is Apple Silicon (arm64). Phase 4 cross-cutting decision:
|
|
44
|
+
# drop the macos-13 (x86) runner family-wide due to 24-hour queue hangs.
|
|
45
|
+
# Intel-mac users fall back to source builds.
|
|
46
|
+
runs-on: macos-latest
|
|
47
|
+
strategy:
|
|
48
|
+
fail-fast: false
|
|
49
|
+
matrix:
|
|
50
|
+
target: [x86_64, aarch64]
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v4
|
|
53
|
+
- uses: actions/setup-python@v5
|
|
54
|
+
with: { python-version: "3.12" }
|
|
55
|
+
- uses: PyO3/maturin-action@v1
|
|
56
|
+
with:
|
|
57
|
+
target: ${{ matrix.target }}
|
|
58
|
+
args: --release --out dist
|
|
59
|
+
- uses: actions/upload-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: wheels-macos-${{ matrix.target }}
|
|
62
|
+
path: dist
|
|
63
|
+
|
|
64
|
+
windows:
|
|
65
|
+
runs-on: windows-latest
|
|
66
|
+
strategy:
|
|
67
|
+
fail-fast: false
|
|
68
|
+
matrix:
|
|
69
|
+
target: [x64]
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v4
|
|
72
|
+
- uses: actions/setup-python@v5
|
|
73
|
+
with: { python-version: "3.12" }
|
|
74
|
+
- uses: PyO3/maturin-action@v1
|
|
75
|
+
with:
|
|
76
|
+
target: ${{ matrix.target }}
|
|
77
|
+
args: --release --out dist
|
|
78
|
+
- uses: actions/upload-artifact@v4
|
|
79
|
+
with:
|
|
80
|
+
name: wheels-windows-${{ matrix.target }}
|
|
81
|
+
path: dist
|
|
82
|
+
|
|
83
|
+
sdist:
|
|
84
|
+
runs-on: ubuntu-latest
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/checkout@v4
|
|
87
|
+
- uses: PyO3/maturin-action@v1
|
|
88
|
+
with:
|
|
89
|
+
command: sdist
|
|
90
|
+
args: --out dist
|
|
91
|
+
- uses: actions/upload-artifact@v4
|
|
92
|
+
with:
|
|
93
|
+
name: wheels-sdist
|
|
94
|
+
path: dist
|
|
95
|
+
|
|
96
|
+
publish:
|
|
97
|
+
name: Publish to PyPI
|
|
98
|
+
runs-on: ubuntu-latest
|
|
99
|
+
needs: [linux, macos, windows, sdist]
|
|
100
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
101
|
+
# Tied to the `pypi` GitHub Actions environment — must match the
|
|
102
|
+
# "Environment name" in PyPI's trusted-publisher config for `l1tv`.
|
|
103
|
+
# Required reviewers / branch rules configured at the env level act as
|
|
104
|
+
# the gate before this job mints an OIDC token and uploads.
|
|
105
|
+
environment: pypi
|
|
106
|
+
steps:
|
|
107
|
+
- uses: actions/download-artifact@v4
|
|
108
|
+
with:
|
|
109
|
+
path: dist
|
|
110
|
+
merge-multiple: true
|
|
111
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
l1tv-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
|
|
2
|
+
unitTests/swUnitTestDistTransformL1.m
|
|
3
|
+
|
|
4
|
+
Docs/create_images.m
|
|
5
|
+
|
|
6
|
+
unitTests/swDistTransformL1FH.m
|
|
7
|
+
|
|
8
|
+
unitTests/swUnitTestDistAngles.m
|
|
9
|
+
|
|
10
|
+
unitTests/swUnitTestDistTransformNE.m
|
|
11
|
+
|
|
12
|
+
unitTests/swUnitTestMinL1TVCirc.m
|
|
13
|
+
|
|
14
|
+
# Rust build artifacts
|
|
15
|
+
target/
|
|
16
|
+
Cargo.lock.bak
|
|
17
|
+
|
|
18
|
+
# Compiled Python extension (platform-specific; built locally or by CI)
|
|
19
|
+
l1tv/_core*.so
|
|
20
|
+
l1tv/_core*.pyd
|
|
21
|
+
|
|
22
|
+
# Python cache
|
|
23
|
+
__pycache__/
|
|
24
|
+
*.pyc
|
|
25
|
+
*.pyo
|
|
26
|
+
.pytest_cache/
|
|
27
|
+
|
|
28
|
+
# macOS
|
|
29
|
+
.DS_Store
|
|
30
|
+
|
|
31
|
+
# Distribution / packaging
|
|
32
|
+
dist/
|
|
33
|
+
build/
|
|
34
|
+
*.egg-info/
|
|
35
|
+
.eggs/
|
|
36
|
+
|
|
37
|
+
# Editors / IDEs
|
|
38
|
+
.vscode/
|
|
39
|
+
.idea/
|
|
40
|
+
*.swp
|
|
41
|
+
|
|
42
|
+
# Jupyter
|
|
43
|
+
.ipynb_checkpoints/
|
|
44
|
+
|
|
45
|
+
# Demo plot artifacts (regenerated on demand)
|
|
46
|
+
demos_python/*.png
|
|
47
|
+
|
|
48
|
+
# Virtualenvs
|
|
49
|
+
.venv/
|
|
50
|
+
venv/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function dsnr = deltaSNR( groundTruth, data, estimate, dataSpace)
|
|
2
|
+
%deltaSNR Computes the signal to noise ratio improvement of a restoration
|
|
3
|
+
|
|
4
|
+
switch dataSpace
|
|
5
|
+
case 'circ'
|
|
6
|
+
dist = @(x,y) distAngle(x,y);
|
|
7
|
+
case 'real'
|
|
8
|
+
dist = @(x,y) abs(x - y);
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
dsnr = 10 * log10(sum( dist(groundTruth, data).^2 ) / sum( dist(groundTruth, estimate).^2 ));
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function d = distAngle( phi, psi )
|
|
2
|
+
%distAngle Distance between two angles phi and psi
|
|
3
|
+
|
|
4
|
+
if (all(abs(phi) <= pi)) && (all(abs(psi) <= pi))
|
|
5
|
+
% cheap computation for angles in the interval [-pi, pi]
|
|
6
|
+
aux = phi - psi;
|
|
7
|
+
d = min(abs([aux(:) + 2 * pi, aux(:), aux(:) - 2 * pi]), [], 2);
|
|
8
|
+
d = reshape(d, size(aux));
|
|
9
|
+
else
|
|
10
|
+
% more expensive calculation for angles outside the interval [-pi, pi]
|
|
11
|
+
aux = abs(angle(exp(1i * (phi - psi))));
|
|
12
|
+
d = min(aux, 2*pi-aux);
|
|
13
|
+
end
|
|
14
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function B = distTransformL1( B, R, alpha )
|
|
2
|
+
%distTransformL1 Fast computation of L1 distance transform
|
|
3
|
+
|
|
4
|
+
K = numel(B);
|
|
5
|
+
% forward pass
|
|
6
|
+
for k=2:K
|
|
7
|
+
B(k) = min( B(k), B(k-1) + alpha * (R(k) - R(k-1)));
|
|
8
|
+
end
|
|
9
|
+
% backward pass
|
|
10
|
+
for k=K-1:-1:1
|
|
11
|
+
B(k) = min( B(k), B(k+1) + alpha * (R(k+1) - R(k)));
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function r = randCP( r, lambda)
|
|
2
|
+
%randCP Bernoulli-thinned innovations approximating a compound Poisson.
|
|
3
|
+
% Each entry of r is independently zeroed with probability exp(-lambda);
|
|
4
|
+
% otherwise it is left at the supplied value. This matches the marginal
|
|
5
|
+
% P(N = 0) of a Poisson(lambda) point process and is used in the demos as
|
|
6
|
+
% a cheap surrogate for compound-Poisson innovations (good for small
|
|
7
|
+
% lambda; not the actual compound Poisson distribution).
|
|
8
|
+
|
|
9
|
+
x = rand(size(r)); % uniform distr. vector
|
|
10
|
+
r(x(:) <= exp(-lambda)) = 0;
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
function y = randl( varargin )
|
|
2
|
+
%randl Random normal Laplacian noise, mean = 0, variance sigma^2 = 1
|
|
3
|
+
% pdf: $\frac{1}{\sqrt{2}} e^{- \sqrt{2} |x|}$
|
|
4
|
+
% Same input arguments as function rand
|
|
5
|
+
|
|
6
|
+
x = rand( varargin{:} ) - 0.5;
|
|
7
|
+
y = -sign(x) .* log(1 - 2 * abs(x)) / sqrt(2);
|
|
8
|
+
|
|
9
|
+
end
|
|
10
|
+
|
l1tv-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `l1tv` Python package are documented in this file.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
|
|
5
|
+
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
The MATLAB reference implementation is tracked separately in this same repo's
|
|
8
|
+
git history; algorithmic semantics are unchanged across the port.
|
|
9
|
+
|
|
10
|
+
## [0.1.0] — Unreleased
|
|
11
|
+
|
|
12
|
+
Initial Python/Rust port of the MATLAB L1TV library by Storath, Weinmann
|
|
13
|
+
(2016).
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Rust core (`_l1tv_core` cdylib, pyo3 0.24.1, numpy 0.24, ndarray 0.16):
|
|
18
|
+
- `solve_l1_tv_real(y, alpha, weights, use_dist_transform)` — exact
|
|
19
|
+
L1-TV solver for real-valued 1-D signals.
|
|
20
|
+
- `solve_l1_tv_circ(y, alpha, weights, use_dist_transform)` — exact
|
|
21
|
+
L1-TV solver for circle-valued 1-D signals with shortest-arc distance.
|
|
22
|
+
- `dist_transform.l1_dt_with_argmin` — forward + backward L1 distance
|
|
23
|
+
transform with argmin tracking (used by both solvers).
|
|
24
|
+
- Python facade (`l1tv` package):
|
|
25
|
+
- `min_l1_tv` and `min_l1_tv_circ` public functions.
|
|
26
|
+
- Boundary handling: any 1-D shape (list, tuple, row, column) is
|
|
27
|
+
accepted via `np.ascontiguousarray + .ravel()`. Validates finite y,
|
|
28
|
+
non-negative alpha, shape-matched non-negative weights.
|
|
29
|
+
- Tests:
|
|
30
|
+
- 25 Rust unit tests covering DT correctness, candidate-set theorem,
|
|
31
|
+
edge cases, and naive-vs-DT objective parity for both solvers.
|
|
32
|
+
- 28 Python tests covering the public API, input validation, and shape
|
|
33
|
+
handling.
|
|
34
|
+
- 16 MATLAB-vs-Rust objective parity tests (parametrised) using
|
|
35
|
+
pre-generated `.mat` fixtures; relerr < 1e-10 on every fixture.
|
|
36
|
+
- Demos: `demos_python/demo_real.py` and `demo_circ.py` mirror the
|
|
37
|
+
original MATLAB demos and run end-to-end with optional matplotlib
|
|
38
|
+
plotting.
|
|
39
|
+
|
|
40
|
+
### Memory layout
|
|
41
|
+
|
|
42
|
+
The Rust port uses a rolling pair of K-cost columns plus a `u32` argmin-
|
|
43
|
+
pointer table for O(N) backtracking, replacing MATLAB's full `(K, N)`
|
|
44
|
+
double-precision cost table. Net memory roughly halves at typical sizes
|
|
45
|
+
and can drop further if argmin pointers shrink to `u16` (when `K < 65536`).
|
|
46
|
+
|
|
47
|
+
### Differences from the MATLAB reference
|
|
48
|
+
|
|
49
|
+
- Input shape: silently produced wrong results for row-vector input in
|
|
50
|
+
`L1TV_Circ.m` (the antipodal vertical concat became a 2×K matrix). The
|
|
51
|
+
Python facade rules this out by construction.
|
|
52
|
+
- Numerical type: `f64` only. Cast at the boundary if you need `f32`.
|
|
53
|
+
- `setPath.m`-style invasive path setup is not relevant; just
|
|
54
|
+
`pip install l1tv`.
|
|
55
|
+
|
|
56
|
+
These differences do not affect algorithmic semantics for column-vector
|
|
57
|
+
MATLAB inputs, which is the only shape the MATLAB demos used.
|
|
58
|
+
|
|
59
|
+
[0.1.0]: https://github.com/mstorath/L1TV/releases/tag/v0.1.0
|
l1tv-0.1.0/CITATION.cff
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite both the software and the associated paper below."
|
|
3
|
+
title: "L1TV: Exact L1-TV regularization of real- or circle-valued signals"
|
|
4
|
+
abstract: "Reference MATLAB implementation of exact, non-iterative algorithms for L1-TV regularization of real-valued and circle-valued (phase / orientation) signals. Solves the discrete L1-TV minimisation problem to global optimum via dynamic programming with a distance-transform speedup."
|
|
5
|
+
type: software
|
|
6
|
+
url: "https://github.com/mstorath/L1TV"
|
|
7
|
+
repository-code: "https://github.com/mstorath/L1TV"
|
|
8
|
+
license: MIT
|
|
9
|
+
authors:
|
|
10
|
+
- family-names: Storath
|
|
11
|
+
given-names: Martin
|
|
12
|
+
email: martin.storath@thws.de
|
|
13
|
+
orcid: "https://orcid.org/0000-0003-1427-0776"
|
|
14
|
+
affiliation: "Lab for Mathematical Methods in Computer Vision and Machine Learning, Technische Hochschule Würzburg-Schweinfurt"
|
|
15
|
+
- family-names: Weinmann
|
|
16
|
+
given-names: Andreas
|
|
17
|
+
email: andreas.weinmann@thws.de
|
|
18
|
+
orcid: "https://orcid.org/0000-0002-4969-7609"
|
|
19
|
+
affiliation: "Department of Mathematics and Natural Sciences, Hochschule Darmstadt"
|
|
20
|
+
preferred-citation:
|
|
21
|
+
type: article
|
|
22
|
+
title: "Exact algorithms for L1-TV regularization of real-valued or circle-valued signals"
|
|
23
|
+
authors:
|
|
24
|
+
- family-names: Storath
|
|
25
|
+
given-names: Martin
|
|
26
|
+
orcid: "https://orcid.org/0000-0003-1427-0776"
|
|
27
|
+
- family-names: Weinmann
|
|
28
|
+
given-names: Andreas
|
|
29
|
+
orcid: "https://orcid.org/0000-0002-4969-7609"
|
|
30
|
+
- family-names: Unser
|
|
31
|
+
given-names: Michael
|
|
32
|
+
orcid: "https://orcid.org/0000-0003-1248-2513"
|
|
33
|
+
journal: "SIAM Journal on Scientific Computing"
|
|
34
|
+
volume: 38
|
|
35
|
+
issue: 1
|
|
36
|
+
start: "A614"
|
|
37
|
+
end: "A630"
|
|
38
|
+
year: 2016
|
|
39
|
+
publisher:
|
|
40
|
+
name: "Society for Industrial and Applied Mathematics"
|
|
41
|
+
doi: "10.1137/15M101796X"
|
l1tv-0.1.0/Cargo.lock
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "_l1tv_core"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"ndarray",
|
|
10
|
+
"numpy",
|
|
11
|
+
"pyo3",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "autocfg"
|
|
16
|
+
version = "1.5.0"
|
|
17
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
18
|
+
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
19
|
+
|
|
20
|
+
[[package]]
|
|
21
|
+
name = "heck"
|
|
22
|
+
version = "0.5.0"
|
|
23
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
24
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
25
|
+
|
|
26
|
+
[[package]]
|
|
27
|
+
name = "libc"
|
|
28
|
+
version = "0.2.186"
|
|
29
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
30
|
+
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
|
31
|
+
|
|
32
|
+
[[package]]
|
|
33
|
+
name = "matrixmultiply"
|
|
34
|
+
version = "0.3.10"
|
|
35
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
36
|
+
checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08"
|
|
37
|
+
dependencies = [
|
|
38
|
+
"autocfg",
|
|
39
|
+
"rawpointer",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[[package]]
|
|
43
|
+
name = "ndarray"
|
|
44
|
+
version = "0.16.1"
|
|
45
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
46
|
+
checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841"
|
|
47
|
+
dependencies = [
|
|
48
|
+
"matrixmultiply",
|
|
49
|
+
"num-complex",
|
|
50
|
+
"num-integer",
|
|
51
|
+
"num-traits",
|
|
52
|
+
"portable-atomic",
|
|
53
|
+
"portable-atomic-util",
|
|
54
|
+
"rawpointer",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[[package]]
|
|
58
|
+
name = "num-complex"
|
|
59
|
+
version = "0.4.6"
|
|
60
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
61
|
+
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
|
62
|
+
dependencies = [
|
|
63
|
+
"num-traits",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[[package]]
|
|
67
|
+
name = "num-integer"
|
|
68
|
+
version = "0.1.46"
|
|
69
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
70
|
+
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
|
71
|
+
dependencies = [
|
|
72
|
+
"num-traits",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[[package]]
|
|
76
|
+
name = "num-traits"
|
|
77
|
+
version = "0.2.19"
|
|
78
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
79
|
+
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
|
80
|
+
dependencies = [
|
|
81
|
+
"autocfg",
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
[[package]]
|
|
85
|
+
name = "numpy"
|
|
86
|
+
version = "0.28.0"
|
|
87
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
88
|
+
checksum = "778da78c64ddc928ebf5ad9df5edf0789410ff3bdbf3619aed51cd789a6af1e2"
|
|
89
|
+
dependencies = [
|
|
90
|
+
"libc",
|
|
91
|
+
"ndarray",
|
|
92
|
+
"num-complex",
|
|
93
|
+
"num-integer",
|
|
94
|
+
"num-traits",
|
|
95
|
+
"pyo3",
|
|
96
|
+
"pyo3-build-config",
|
|
97
|
+
"rustc-hash",
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
[[package]]
|
|
101
|
+
name = "once_cell"
|
|
102
|
+
version = "1.21.4"
|
|
103
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
104
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
105
|
+
|
|
106
|
+
[[package]]
|
|
107
|
+
name = "portable-atomic"
|
|
108
|
+
version = "1.13.1"
|
|
109
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
110
|
+
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
|
111
|
+
|
|
112
|
+
[[package]]
|
|
113
|
+
name = "portable-atomic-util"
|
|
114
|
+
version = "0.2.7"
|
|
115
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
116
|
+
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
|
|
117
|
+
dependencies = [
|
|
118
|
+
"portable-atomic",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
[[package]]
|
|
122
|
+
name = "proc-macro2"
|
|
123
|
+
version = "1.0.106"
|
|
124
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
125
|
+
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
|
126
|
+
dependencies = [
|
|
127
|
+
"unicode-ident",
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
[[package]]
|
|
131
|
+
name = "pyo3"
|
|
132
|
+
version = "0.28.3"
|
|
133
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
134
|
+
checksum = "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12"
|
|
135
|
+
dependencies = [
|
|
136
|
+
"libc",
|
|
137
|
+
"once_cell",
|
|
138
|
+
"portable-atomic",
|
|
139
|
+
"pyo3-build-config",
|
|
140
|
+
"pyo3-ffi",
|
|
141
|
+
"pyo3-macros",
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
[[package]]
|
|
145
|
+
name = "pyo3-build-config"
|
|
146
|
+
version = "0.28.3"
|
|
147
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
148
|
+
checksum = "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e"
|
|
149
|
+
dependencies = [
|
|
150
|
+
"target-lexicon",
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
[[package]]
|
|
154
|
+
name = "pyo3-ffi"
|
|
155
|
+
version = "0.28.3"
|
|
156
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
157
|
+
checksum = "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e"
|
|
158
|
+
dependencies = [
|
|
159
|
+
"libc",
|
|
160
|
+
"pyo3-build-config",
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
[[package]]
|
|
164
|
+
name = "pyo3-macros"
|
|
165
|
+
version = "0.28.3"
|
|
166
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
167
|
+
checksum = "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813"
|
|
168
|
+
dependencies = [
|
|
169
|
+
"proc-macro2",
|
|
170
|
+
"pyo3-macros-backend",
|
|
171
|
+
"quote",
|
|
172
|
+
"syn",
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
[[package]]
|
|
176
|
+
name = "pyo3-macros-backend"
|
|
177
|
+
version = "0.28.3"
|
|
178
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
179
|
+
checksum = "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb"
|
|
180
|
+
dependencies = [
|
|
181
|
+
"heck",
|
|
182
|
+
"proc-macro2",
|
|
183
|
+
"pyo3-build-config",
|
|
184
|
+
"quote",
|
|
185
|
+
"syn",
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
[[package]]
|
|
189
|
+
name = "quote"
|
|
190
|
+
version = "1.0.45"
|
|
191
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
192
|
+
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
|
193
|
+
dependencies = [
|
|
194
|
+
"proc-macro2",
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
[[package]]
|
|
198
|
+
name = "rawpointer"
|
|
199
|
+
version = "0.2.1"
|
|
200
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
201
|
+
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
|
|
202
|
+
|
|
203
|
+
[[package]]
|
|
204
|
+
name = "rustc-hash"
|
|
205
|
+
version = "2.1.2"
|
|
206
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
207
|
+
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
|
|
208
|
+
|
|
209
|
+
[[package]]
|
|
210
|
+
name = "syn"
|
|
211
|
+
version = "2.0.117"
|
|
212
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
213
|
+
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
|
214
|
+
dependencies = [
|
|
215
|
+
"proc-macro2",
|
|
216
|
+
"quote",
|
|
217
|
+
"unicode-ident",
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
[[package]]
|
|
221
|
+
name = "target-lexicon"
|
|
222
|
+
version = "0.13.5"
|
|
223
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
224
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
225
|
+
|
|
226
|
+
[[package]]
|
|
227
|
+
name = "unicode-ident"
|
|
228
|
+
version = "1.0.24"
|
|
229
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
230
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
l1tv-0.1.0/Cargo.toml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "_l1tv_core"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Rust core for the l1tv Python package — exact L1-TV regularisation of real- or circle-valued 1-D signals (Storath, Weinmann, Unser 2016). Automated port of the original MATLAB by Claude Opus coding agent (Anthropic, 2026)."
|
|
6
|
+
authors = [
|
|
7
|
+
"Martin Storath <storath@gmail.com> (original MATLAB)",
|
|
8
|
+
"Andreas Weinmann (original MATLAB)",
|
|
9
|
+
"Claude Opus coding agent, Anthropic (Rust port, 2026)"
|
|
10
|
+
]
|
|
11
|
+
license = "MIT"
|
|
12
|
+
repository = "https://github.com/mstorath/L1TV"
|
|
13
|
+
readme = "README_PYTHON.md"
|
|
14
|
+
|
|
15
|
+
[lib]
|
|
16
|
+
name = "_l1tv_core"
|
|
17
|
+
crate-type = ["cdylib"]
|
|
18
|
+
|
|
19
|
+
[dependencies]
|
|
20
|
+
# pyo3 0.28 (June 2025+) is the first family of versions that supports
|
|
21
|
+
# CPython 3.14. The previous family-wide pin at 0.24.1 (a security-fix pin
|
|
22
|
+
# for GHSA-pph8-gcv7-4qj5) does not support 3.14 and broke the linux release
|
|
23
|
+
# build with `--find-interpreter` after the manylinux containers added 3.14.
|
|
24
|
+
# numpy crate version tracks pyo3 minor; ndarray version tracks numpy's.
|
|
25
|
+
pyo3 = { version = "0.28" }
|
|
26
|
+
numpy = "0.28"
|
|
27
|
+
ndarray = "0.16"
|
|
28
|
+
|
|
29
|
+
# `extension-module` is gated behind a Cargo feature instead of being on
|
|
30
|
+
# unconditionally. Reason: with the feature ON, pyo3 instructs the linker to
|
|
31
|
+
# leave Python symbols unresolved (resolved at runtime by the Python
|
|
32
|
+
# interpreter that loads the .so). That works for the wheel target but breaks
|
|
33
|
+
# `cargo test`, whose test binary has no Python interpreter to satisfy the
|
|
34
|
+
# undefined symbols at runtime — macOS's strict linker rejects the binary.
|
|
35
|
+
# Maturin enables the feature via [tool.maturin].features in pyproject.toml.
|
|
36
|
+
[features]
|
|
37
|
+
extension-module = ["pyo3/extension-module"]
|
|
38
|
+
|
|
39
|
+
[profile.release]
|
|
40
|
+
opt-level = 3
|
|
41
|
+
lto = true
|