feat: initialize standalone image-filter
Rename the distribution to image-filter and imports to image_filter. Keep medical fixtures local and document their setup for fresh clones. Include composite pipelines, consistent examples, and class diagrams.
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
# Numerical semantics and scope
|
||||
|
||||
This package is a greenfield implementation. Its numerical contracts are the
|
||||
documented behavior of SciPy, scikit-image, PyWavelets, and NumPy—not any
|
||||
application-specific filter implementation.
|
||||
|
||||
## Shared contract
|
||||
|
||||
- Inputs are finite, real numeric NumPy arrays with two or three inferred
|
||||
non-singleton spatial axes.
|
||||
- Filters preserve the complete input shape, including singleton and channel
|
||||
axes.
|
||||
- Numeric output is explicitly `float32` or `float64` (default `float64`).
|
||||
Canny always returns a boolean mask.
|
||||
- SciPy-backed filters accept `reflect`, `constant`, `nearest`, `mirror`, and
|
||||
`wrap`. `cval` is used only by `constant` mode.
|
||||
- Each filter exposes a `Capabilities` object. Unsupported dimensions and
|
||||
recognized-but-unimplemented options use domain-specific exceptions.
|
||||
|
||||
## MeanFilter
|
||||
|
||||
`MeanFilter` calls `scipy.ndimage.uniform_filter` exactly, with a scalar kernel
|
||||
size applied only to spatial axes. Channel and retained singleton axes use size
|
||||
one.
|
||||
|
||||
SciPy's `origin=0` alignment is the default. Odd kernels are centered on a
|
||||
sample. Even kernels necessarily have one extra sample on one side of the
|
||||
origin; the exact placement is SciPy's documented convention. Set `origin`
|
||||
explicitly to shift the footprint. Valid origins are checked at construction.
|
||||
This policy makes even-kernel behavior deliberate and testable.
|
||||
|
||||
## LoGFilter
|
||||
|
||||
`LoGFilter` sums second-order `scipy.ndimage.gaussian_filter` derivatives over
|
||||
the inferred spatial axes. This is numerically equivalent to
|
||||
`scipy.ndimage.gaussian_laplace` when every array axis is spatial, while also
|
||||
preventing smoothing or differentiation across channels. Sigma is expressed in
|
||||
pixels and may be scalar or one value per effective spatial axis.
|
||||
|
||||
## LawsFilter
|
||||
|
||||
Laws responses use normalized standard L3/E3/S3 and L5/E5/S5/W5/R5 vectors
|
||||
with `scipy.ndimage.convolve1d`. A configuration contains one two-character
|
||||
kernel name per spatial axis, such as `L5E5` or `L5E5S5`. Optional texture
|
||||
energy is the local uniform mean of the absolute response with window width
|
||||
`2 * energy_radius + 1`.
|
||||
|
||||
Rotation-invariant pooling is intentionally unsupported. Construct explicit
|
||||
orientation filters and combine their outputs according to the domain's chosen
|
||||
statistic instead of relying on an undocumented convention.
|
||||
|
||||
## GaborFilter
|
||||
|
||||
Gabor responses call `skimage.filters.gabor` for each orientation. Frequency is
|
||||
in cycles per pixel, angles are radians, and `sigma_y = sigma / gamma`.
|
||||
Available complex response projections are magnitude, real, and imaginary;
|
||||
multiple orientations use max, min, mean, or sum pooling.
|
||||
|
||||
The filter supports 2D data, including 2D multichannel arrays with an explicit
|
||||
channel axis. A 3D volume requires `SliceWise`, whose axis is part of the public
|
||||
configuration and provenance.
|
||||
|
||||
## WaveletFilter
|
||||
|
||||
Wavelet coefficients use PyWavelets' shift-invariant `pywt.swtn`. Configuration
|
||||
letters map directly to PyWavelets keys: `L` to approximation (`a`) and `H` to
|
||||
detail (`d`) in the order of inferred spatial axes. The selected array is the
|
||||
requested coefficient at the final decomposition level.
|
||||
|
||||
SWT requires transformed lengths divisible by `2**level`. Each spatial axis is
|
||||
padded only at its high edge to the next valid length using `numpy.pad`, then
|
||||
the coefficient is cropped to the original shape. `pad_mode` is therefore a
|
||||
NumPy padding mode rather than a PyWavelets signal-extension mode. The optional
|
||||
`normalize=True` is passed directly to `pywt.swtn`.
|
||||
|
||||
Rotation-invariant coefficient pooling and Riesz steering are not implemented;
|
||||
requesting rotation invariance raises `UnsupportedOptionError`. Decimated DWT
|
||||
reconstruction is a distinct operation and is outside this first API.
|
||||
|
||||
|
||||
## GaussianFilter
|
||||
|
||||
2D/3D Gaussian smoothing calls [scipy.ndimage.gaussian_filter](https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html).
|
||||
`sigma` is a finite positive scalar or immutable nonempty tuple, one value per
|
||||
inferred spatial axis in array order. Tuple length is validated on application.
|
||||
`truncate` is finite and positive (default 4 standard deviations). Input and
|
||||
intermediate values use the selected floating output dtype; no intensity
|
||||
normalization occurs. Channels and singleton axes are indexed out before the
|
||||
backend call, preserving SciPy 1.10 compatibility.
|
||||
|
||||
## MedianFilter
|
||||
|
||||
2D/3D rank filtering calls [scipy.ndimage.median_filter](https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.median_filter.html).
|
||||
`size` is a positive integer applied along each spatial axis. `origin` must be
|
||||
an integer in `[-size//2, (size-1)//2]`, interpreting the lower bound as
|
||||
`-(size//2)`. Even footprints select sorted element `n//2`, rather than averaging
|
||||
the middle pair. Input is converted to the selected floating dtype before
|
||||
filtering, including fractional `cval` padding for integer images and support
|
||||
for float16 input. Large 3D windows can require substantial backend memory.
|
||||
|
||||
## SobelFilter
|
||||
|
||||
2D/3D gradients call [scipy.ndimage.sobel](https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.sobel.html)
|
||||
on each spatial block. The default `axis=None` returns Euclidean magnitude
|
||||
`sqrt(sum(sobel(image, axis=i)**2))` over spatial axes, accumulated with `hypot`
|
||||
to avoid intermediate squared overflow. There is no division by dimensionality
|
||||
or kernel gain. Optional integer `axis` selects a signed component, indexing the
|
||||
inferred spatial axes (negative indices supported). Channels are neither
|
||||
smoothed nor differentiated. Computation uses the selected floating dtype.
|
||||
|
||||
## LaplacianFilter
|
||||
|
||||
2D/3D [scipy.ndimage.laplace](https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.laplace.html)
|
||||
sums discrete second differences over spatial axes. Input is cast to the
|
||||
selected floating dtype, retaining signed responses. This operation adds no
|
||||
Gaussian smoothing; use LoGFilter when that is desired.
|
||||
|
||||
## CannyFilter
|
||||
|
||||
[skimage.feature.canny](https://scikit-image.org/docs/stable/api/skimage.feature.html#skimage.feature.canny)
|
||||
produces a boolean edge mask from each 2D plane/channel. Singleton axes are
|
||||
restored afterward. A 3D image is rejected; use `SliceWise(CannyFilter(), axis=0)`
|
||||
with an explicitly chosen array axis. There is no implicit projection or slicing.
|
||||
|
||||
Canny rejects int64/uint64 with `InvalidImageError`, matching the backend
|
||||
restriction declared in its capabilities. Callers may explicitly convert to float
|
||||
and choose thresholds in the resulting intensity units.
|
||||
|
||||
The original supported input dtype reaches Canny. Its image conversion scales unsigned
|
||||
integers by their dtype maximum and signed integers to the signed unit range;
|
||||
floating input keeps its values, with no min/max normalization or clipping.
|
||||
Absolute thresholds are expressed in original dtype intensity units; the
|
||||
backend divides integer thresholds by the dtype maximum. For floating input,
|
||||
thresholds are already in the unchanged intensity units. Missing low/high
|
||||
thresholds become 0.1/0.2 in the backend's normalized scale. `use_quantiles=True`
|
||||
instead interprets thresholds as gradient-magnitude quantiles in [0, 1], with
|
||||
defaults 0.1/0.2. Thresholds must be finite, nonnegative, and ordered; ordering
|
||||
against missing defaults for absolute thresholds is checked by the backend at
|
||||
application time and converted to `InvalidConfigurationError`.
|
||||
|
||||
`sigma` must be finite and positive. `cval` is in the converted image's units.
|
||||
`mode` defaults to `constant`; it governs Gaussian padding, and Canny excludes
|
||||
the outermost edge pixels during mask erosion. There is no public mask option.
|
||||
|
||||
## Boundary and precision details for the new filters
|
||||
|
||||
All five accept `reflect` (half-sample reflection), `mirror` (whole-sample
|
||||
reflection), `nearest` (edge replication), `wrap` (periodic), and `constant`
|
||||
(finite `cval`, default 0). SciPy aliases are intentionally excluded. Numeric
|
||||
filters default to `reflect`. Boolean configuration values are not numbers.
|
||||
Like their backends, floating filters can lose precision or overflow for extreme
|
||||
finite inputs; finite outputs are verified on normal image ranges, not promised
|
||||
for arbitrary floating-point magnitudes. Provenance records parameters, effective
|
||||
spatial axes, shapes and dtypes; SliceWise records the nested filter and axis.
|
||||
|
||||
### Composite contract
|
||||
|
||||
`ImageFilter` defines `apply` and shared `apply_batch` behavior. Numerical
|
||||
leaves reuse the internal `NumericFilter` implementation for validation and
|
||||
history. `Pipeline` is an `ImageFilter` containing a nonempty immutable tuple
|
||||
of components. Nested pipelines are supported at arbitrary depth and execute
|
||||
depth-first in child order. History contains each executed leaf once; composite
|
||||
boundaries do not add history entries. Errors propagate from the failing child.
|
||||
|
||||
Pipeline dimensions are the intersection of child dimensions. This declaration
|
||||
does not guarantee that intermediate ranges or dtypes satisfy later children;
|
||||
each child validates its actual input. The last child determines output dtype.
|
||||
Batch processing uses the same component API for both leaves and pipelines.
|
||||
|
||||
`SliceWise` accepts any component with 2D capability, including a pipeline,
|
||||
and calls its public API on each plane with remapped channel metadata. The
|
||||
actual first plane result determines output dtype (including boolean Canny
|
||||
results). Its outer history records one adapter entry containing the nested
|
||||
configuration, rather than repeating history for every slice.
|
||||
Reference in New Issue
Block a user