Skip to content

Commit e21537c

Browse files
First version of filters
1 parent 1d445c9 commit e21537c

13 files changed

Lines changed: 2404 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ nanobind_add_module(_core
1313
NB_STATIC
1414
src/bindings/blocking.cxx
1515
src/bindings/module.cxx
16+
src/bindings/filters.cxx
1617
src/bindings/graph.cxx
1718
src/bindings/ground_truth.cxx
1819
src/bindings/segmentation.cxx

MIGRATION_GUIDE.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,88 @@ Notes:
946946
- Every value in the input must be present in the mapping.
947947
- Non-contiguous inputs are copied before entering C++.
948948

949+
## Image Filters
950+
951+
`bioimage-cpp` ships a small Gaussian-derivative filter set under
952+
`bic.filters`. The scope is the "ilastik filter set" exposed by
953+
`fastfilters`, which is also the most-used subset of `vigra.filters`.
954+
955+
Vigra / fastfilters:
956+
957+
```python
958+
import vigra.filters as vf
959+
out = vf.gaussianSmoothing(img, sigma=1.5)
960+
ev = vf.hessianOfGaussianEigenvalues(img, scale=1.5)
961+
962+
import fastfilters as ff
963+
out = ff.gaussianSmoothing(img, sigma=1.5)
964+
ev = ff.hessianOfGaussianEigenvalues(img, scale=1.5)
965+
```
966+
967+
bioimage-cpp:
968+
969+
```python
970+
import bioimage_cpp as bic
971+
972+
out = bic.filters.gaussian_smoothing(img, sigma=1.5)
973+
ev = bic.filters.hessian_of_gaussian_eigenvalues(img, sigma=1.5)
974+
```
975+
976+
Name mapping:
977+
978+
| vigra / fastfilters name | bioimage-cpp name |
979+
| --- | --- |
980+
| `gaussianSmoothing` | `gaussian_smoothing` |
981+
| `gaussianDerivative` | `gaussian_derivative` |
982+
| `gaussianGradientMagnitude` | `gaussian_gradient_magnitude` |
983+
| `laplacianOfGaussian` | `laplacian_of_gaussian` |
984+
| `hessianOfGaussianEigenvalues` | `hessian_of_gaussian_eigenvalues` |
985+
| `structureTensorEigenvalues` | `structure_tensor_eigenvalues` |
986+
987+
Common parameters:
988+
989+
- `sigma` is a positive scalar or a per-axis sequence of length
990+
`image.ndim`. Anisotropic sigma is supported on every filter.
991+
- `gaussian_derivative` takes an `order` argument that is a scalar or a
992+
per-axis sequence of ints in `{0, 1, 2}`.
993+
- `structure_tensor_eigenvalues` takes positional `inner_sigma` and
994+
`outer_sigma` (vigra calls them `innerScale` / `outerScale`).
995+
- `window_size` controls the kernel radius:
996+
`radius = ceil(window_size * sigma)`. `0.0` (the default) selects the
997+
vigra-style default `3 + 0.5 * order`. Matches the same-named parameter
998+
in vigra/fastfilters.
999+
1000+
Important differences from vigra and fastfilters:
1001+
1002+
- Only 2D and 3D scalar (single-channel) inputs are supported in v1.
1003+
Channels and leading batch axes should be looped externally — matches
1004+
fastfilters' convention. Vigra's `taggedView`/`AxisInfo` machinery is
1005+
not reproduced.
1006+
- C++ kernels operate on `float32`. `float64` inputs are accepted and the
1007+
output is cast back to `float64`. `uint8` and `uint16` are accepted with
1008+
a `float32` output (the typical ML-feature use case).
1009+
- Boundary handling is `mirror` (matches scipy `mode="mirror"`
1010+
reflection without edge-pixel repeat). Other boundary modes are not
1011+
exposed yet; the C++ layer carries an enum for future tiled processing.
1012+
- Eigenvalue outputs have a trailing axis of size `image.ndim`, sorted
1013+
largest → smallest. This matches `fastfilters`. To get vigra's
1014+
ascending order, reverse with `result[..., ::-1]`.
1015+
- No IIR / recursive Gaussian, no `convolve` / `recursiveFilter2D`, no
1016+
morphology, no distance transforms, no nonlinear diffusion, and no
1017+
non-local means in v1. Use `scipy.ndimage`, `skimage`, or the original
1018+
vigra/fastfilters bindings if you need those.
1019+
1020+
Implementation notes:
1021+
1022+
- All six filters are written as portable C++20 scalar code that the
1023+
compiler auto-vectorizes. No SIMD intrinsics, no per-file ISA flags, no
1024+
runtime CPU dispatch, no vendored SIMD library. This keeps the build
1025+
light enough to ship as portable PyPI wheels across Linux/macOS/Windows
1026+
and x86_64/arm64.
1027+
- Single-threaded for now. Threading can be added later via
1028+
`detail/threading.hxx::parallel_for_chunks` without changing the
1029+
public API.
1030+
9491031
## I/O and Build Dependencies
9501032

9511033
`bioimage-cpp` intentionally does not replace nifty or affogato I/O helpers.

0 commit comments

Comments
 (0)