Skip to content

Releases

v0.9.0a5 — 2D plane mode and CUDA install extras

Choose a tag to compare

@alxndrkalinin alxndrkalinin released this 07 Oct 00:40
fabb8a3

Fifth pre-release of the 0.9 series. The Allen Cell Structure Segmenter workflows now also segment a single 2D plane, and GPU support installs through a pip extra. pip install cubic continues to resolve 0.8.0 until 0.9.0 ships.

Plane mode for workflow_sec61b and workflow_tomm20 (#59)

Both workflows now accept a 2D (Y, X) plane as well as a 3D (Z, Y, X) stack, and return a mask of the input's shape on the input's device. Any other dimensionality raises ValueError.

  • A plane is segmented as a one-plane volume (1, Y, X). The result is exactly what aicssegmentation returns for that one-plane input, with GPU equal to CPU.
  • This is not the same as slicing the 3D mask. In a volume, normalization uses volume statistics, smoothing is 3D, the vesselness response is combined with the z-MIP, and the size filter uses 3D connectivity. Segmenting a plane alone drops all of these couplings.
  • To keep some z-context, pass a few neighbouring planes as a small volume, for example workflow_sec61b(vol[z - 1 : z + 2])[1].
  • 3D input is unchanged: masks are bitwise identical to 0.9.0a4.

On one GPU, a 640x960 plane takes about 5 ms for SEC61B and 3 ms for TOMM20, against 0.13 s and 0.08 s for a 48x640x960 volume.

cuda12 and cuda13 extras for GPU support (#60)

GPU support no longer needs CuPy, cuCIM and a CUDA Toolkit installed by hand. On Linux, install one extra:

pip install --pre 'cubic[cuda13]'  # driver with CUDA 13.x and a Turing or newer GPU
pip install --pre 'cubic[cuda12]'  # driver with CUDA 12.x, or a Maxwell, Pascal or Volta GPU
  • Each extra installs CuPy 14+ and cuCIM, plus the CUDA runtime libraries from PyPI, so only the NVIDIA driver is needed. nvidia-smi shows the driver's CUDA version.
  • cuCIM publishes Linux wheels only.
  • Install one extra, into an environment with no other cupy-cuda* package. uv refuses to combine the two, but pip does not.
  • uv sync --all-extras now errors on this project. Name the extras you want instead.
  • When CuPy or cuCIM fails to import, the CPU-fallback warning now quotes the original error and names the extras. CuPy also raises an import error when it is installed but cannot load its CUDA libraries.

v0.9.0a4 — GPU segmenter workflows and metric kernels

Choose a tag to compare

@alxndrkalinin alxndrkalinin released this 02 Oct 21:12

Fourth pre-release of the 0.9 series: GPU ports of two Allen Cell Structure Segmenter workflows, and fused GPU kernels for MicroMS3IM, per-region GLCM texture and FRC/FSC bin sums. pip install cubic continues to resolve 0.8.0 until 0.9.0 ships.

Existing APIs keep their results: the new kernels are used only on GPU input and agree with the array path to floating-point reduction order (about 1e-15 relative).

Allen Cell Structure Segmenter workflows (#57)

New cubic.segmentation.workflow_sec61b and workflow_tomm20 are device-agnostic ports of the aicssegmentation ER and mitochondria classic workflows, including ITK's gradient anisotropic diffusion and the slice-wise 2D vesselness filter, rewritten as whole-volume array operations with CUDA kernels on GPU.

  • For float32 input, masks were bitwise equal to aicssegmentation on every volume tested: 250 light-sheet ER and mitochondria z-stacks of 48x640x960 voxels, acquired and predicted.
  • On an NVIDIA A40, one such volume takes 0.37 s for SEC61B (up to 3 s when the serial-K replay triggers) against 85-88 s for the CPU reference, and 0.2 s for TOMM20 against 28-29 s.
  • The reference itself is not bitwise across CPUs: on AVX-512 hosts NumPy's float32 np.power rounds differently and moves 1-3 voxels per volume. The CPU path follows the host; the GPU kernel reproduces glibc's powf and so matches the AVX2 result.
  • The match is measured, not guaranteed for every input; see _K_SERIAL_MARGIN for the one heuristic involved.

Metric kernels (#58)

  • MicroMS3IM. The RI-factor fit evaluates its objective in one fused GPU reduction and stops the bisection at its float fixed point (56 objective evaluations instead of 204, identical alpha). New MicroMS3IM.score_stack(gt, pred) scores every slice of an (N, H, W) stack in batched passes, each slice scoring what score gives it alone.
  • Per-region GLCM. New cubic.feature.glcm_features_by_label(image, labels, ...) computes Haralick features for every labeled region in one pass per direction, matching glcm_features per region. Regions without a voxel pair, or holding a non-finite voxel, return NaN rows. Histogram memory is bounded by processing labels in chunks.
  • FRC/FSC bin sums. Radial and sectioned bin sums use a shared-memory histogram kernel on GPUs of compute capability 6.0 and above, falling back to bincount otherwise.

Measured on an NVIDIA A40:

step before after
MicroMS3IM fit, 576 slices of 634x954 45.9 s 1.5 s
MicroMS3IM scoring, 480 slices of 640x960 4.92 s 0.72 s
GLCM, 48x640x960 volume with 13 labeled regions 0.60 s (per-region loop) 0.13 s

v0.9.0a3 — single-image FRC/FSC split validity and ordering

Choose a tag to compare

@alxndrkalinin alxndrkalinin released this 30 Sep 19:55

Third pre-release of the 0.9 series: single-image FRC/FSC now warns when a split is used outside its assumptions, judges the sampling limit per direction in physical units, and splits raw counts before isotropic resampling instead of after. pip install cubic continues to resolve 0.8.0 until 0.9.0 ships.

split_type="checkerboard" remains the default; existing checkerboard workflows are unchanged in behavior and cost.

Single-image splits warn outside their assumptions (#55)

Single-image FRC/FSC has assumptions the API cannot check for the caller, and previously returned a plausible-looking number when any was violated.

binomial_split now warns when counts_mode='poisson_thinning' is handed non-negative integers. Thinning draws two Poisson variates from a shared rate, so the halves keep a high-frequency correlation floor (~0.33 measured on simulated counts) that can stop the curve ever crossing 1/7 and return nan with no explanation. This is a value-domain test, so it has no false positives on valid input.

Two inferential checks — per-axis bandwidth and a variance-vs-mean slope — were tried and removed after both misfired on synthetic counts. The module constants record why, and why neither interpolated nor deconvolved input is detectable from the array alone. Value sampling strides every axis, so a stack is not judged from its first plane, and caps at 2M values to stay off the critical path.

Sampling floor is per direction, in length (#56)

_CHECKERBOARD_VALIDITY_RADIUS = 0.9 was replaced. A normalized radius cannot express a physical, per-direction criterion, and 0.9 was wrong in three ways that share that cause:

  • Too loose. Reported resolution is 2·d_px / (r·f(r)), so Rieger's d_min ≥ 4·d_px is already breached at r = 0.797 and Koho's 2√2·d_px at r = 0.883, while r = 0.9 still reports 2.56·d_px — inside both. Everything in [0.797, 0.9) broke the criterion the warning itself quoted.
  • Wrong axis. The frequency grid is normalized by one Nyquist for the whole volume, so on anisotropic data one radius means different physical resolutions along XY and Z.
  • Never deduplicated. The message interpolated the radius into its own text, so Python's once-per-location filter never merged repeats. grid_crop_resolution reaches it once per slice per crop; 200 varying calls now collapse to 1 warning (suite-wide count 194 → 19).

_warn_if_below_split_floor compares the measured resolution against 2√2 × the pixel size for the direction measured, judging XY and axial separately.

Binomial split runs before resampling (#56)

resample_isotropic previously ran before the binomial split, so both halves were drawn from an interpolated volume and shared source samples; 0.9.0a3's predecessor only warned about it. The split now runs first and the same interpolation is applied to each half — a linear operator applied separately to independent inputs leaves them independent.

MEASURED against two genuinely independent Poisson acquisitions of one object:

XY error Z error
resample → split (old) −47.4% −0.3%
split → resample (new) +8.5% +0.5%

The old path also fed 44% non-integer values into a counts-mode split, an interpolated volume no longer being counts. The split runs inside the repeat loop, so n_repeats still yields per-ring and resolution standard deviations.

This does not rescue every binomial case: on the Koho pollen stack the one-bit crossing is absent with or without resampling, which is a threshold problem rather than an ordering one.

Pollen reference corrected (#55)

The 0.59 / 3.91 µm pair the 3D example was validated against appears in neither the paper nor the reference implementation — 3.91 was the outermost radial tick of the Fig. 4b polar plot. miplib's own notebook prints FWHM (Z: 3.8875587137876315 µm, XY: 0.5986743538754852 µm), which Fig. 4b rounds to 0.6 × 3.9 µm. Matching the notebook's settings (resample_order=0, bin_delta=10, one-bit, spline), cubic gives Z 3.872 / XY 0.578 µm — within 0.4% and 3.5%. The gap previously blamed on the axial formula was bin width and curve-fit type.

Behavior changes

  • split_type="binomial" with resample_isotropic=True now splits before resampling rather than warning; results change on that path (see the table above).
  • New RuntimeWarning/UserWarning can fire on existing calls that were previously silent — notably a checkerboard crossing between 2√2·d_px and the old 0.9 radius, and poisson_thinning on integer data.
  • fsc_resolution, frc_resolution and their defaults are otherwise unchanged.

Deprecated mask backend brought in line

Two paths the reworked checks did not reach, found in review of #56:

  • backend="mask" with split_type="binomial" and resample_isotropic=True analysed the original anisotropic array while spacing had already been rewritten to isotropic values. Pre-splitting resamples only the metadata up front, deferring the voxels to the repeat loop, and the mask backend returns before that loop. MEASURED: a (12, 48, 48) volume at spacing=[0.3, 0.1, 0.1] reached the backend unsplit at its original shape with spacing reading [0.1, 0.1, 0.1] — the axial pitch understated three-fold. It now receives the pre-split, resampled pair.
  • calculate_sectioned_fsc (public) and fsc_resolution(backend="mask"), which returns through it, applied the cut-off correction without any validity check, silently accepting sampling-limited results that warned before the rework. MEASURED: a 64³ Poisson volume reports 2.466 px in the sectors at 90° and 270°, below the 2.828 px floor, with no warning. The floor is now checked where the correction is applied, using a new _sector_pixel_size helper — a sector's polar angle is measured from the Z axis, so its pitch is sqrt((d_z·cos θ)² + (d_xy·sin θ)²), reducing to the axis pitch at 0°, 90°, 180° and 270°.

Testing

819 passed, 2 skipped locally on an A40 (GPU paths exercised); CI green on Python 3.10/3.11/3.12. All seven example notebooks re-executed against this code and committed with outputs. The two validity tests previously poked a private correction helper with a hand-built dataset, which cannot see a normalization bug; they now drive frc_resolution end to end.

Full changes: #55, #56, 10a1cc8, b4fb140.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RMWszUe53TCdX1XJ8rfWBj

v0.9.0a2 — MicroSSIM RI-factor fit GPU memory fix

Choose a tag to compare

@alxndrkalinin alxndrkalinin released this 29 Sep 06:02

Second pre-release of the 0.9 series: a GPU-memory fix for the MicroSSIM range-invariant (RI) factor fit. pip install cubic continues to resolve 0.8.0 until 0.9.0 ships.

MicroSSIM RI-factor fit memory

MicroSSIM.fit / MicroMS3IM.fit ran out of memory on 40–48 GB GPUs for realistic calibration sets. Two causes in get_global_ri_factor / get_ri_factor:

  • Per-slice elements were pooled with lists + np.concatenate, holding both copies at once. They are now written into preallocated (N, h, w) buffers.
  • Each objective evaluation of the bisection built ~15 temporaries the size of the whole pooled stack. The S / dS means are now reduced in bounded chunks (_CHUNK_ELEMS), recursing into slices larger than a chunk, with float64 accumulation on the device.

Measured on an A40, 576 slices of 640×960 float32: peak pool usage 40.51 GB → 12.86 GB, fitted alpha identical to 10 decimals, fit time unchanged. A single 1024² slice goes from 15.0× to 0.94× its element size above the input.

Behavior changes

  • The objective's chunk sums accumulate in float64, so float32 inputs give slightly more accurate means than NumPy's float32 mean() (measured difference ~4e-8 on a (3, 64, 64) float32 stack).
  • get_global_ri_factor on a zero-slice stack and get_ri_factor on empty element arrays raise a clear ValueError (0.9.0a1 raised NumPy's concatenate error and a bracketing RuntimeError, respectively).

Full changes: f4ca22a, #54.

🤖 Generated with Claude Code

v0.9.0a1 — repository-wide bug sweep (77 bugs)

Choose a tag to compare

@alxndrkalinin alxndrkalinin released this 30 Jul 01:15

First pre-release of the 0.9 series. Repository-wide bug sweep: 77 bugs fixed across all seven modules, plus stale-code removal, simplification, and tooling changes. Test suite 401 → 791 passing.

Shipped as an alpha rather than 0.9.0 final: several fixes change previously-returned numbers, ~24 public symbols are gone, and two correctness questions in the single-image FRC/FSC split are still open (see Known issues). pip install cubic continues to resolve 0.8.0 until 0.9.0 ships.

Highlights

  • Silently-wrong results fixed. The worst class in the sweep: functions that returned plausible numbers instead of failing, so no test caught them. morphology_correlations read true-vs-true entries and never touched the prediction (identical inputs scored 0.921 instead of 1.0). Sectioned FSC reported 0.1225 µm at 0.13 µm sampling — below the physical floor. Every binomial FRC returned NaN. ms_ssim wrapped uint8 arithmetic mod 256. Empty GLCM directions were averaged in as zeros, flipping a texture correlation's sign.
  • Even-shaped PSFs in create_backprojector were wrong two independent ways: centered grids used (S-1)/2 where ifftshift puts S//2 at DC (non-Hermitian mask, np.real discarding a large imaginary part), and f[::-1] maps j → -j-1 on even axes so the flipped PSF was not the conjugate OTF. Imaginary leakage 5.32e-1 → 1.46e-17. Odd shapes stay bit-identical to the reference.
  • Code that had never worked: five_crop_resolution/grid_crop_resolution raised IndexError unconditionally; Image.to_cpu() and cross-device to_same_device rejected CuPy input; remove_touching_objects failed on every GPU and every 2-D array; the skimage proxy had no host fallback.
  • Input mutation. richardson_lucy_xp did image *= mask, and img_as_float returns the same object for float input, so it destroyed the caller's array. Same class of bug in five other functions.
  • Performance, all byte-identical: remove_touching_objects 426× (5.0 s → 0.012 s), find_objects 165×, mesh extraction 11×, GLCM peak GPU pool 7.5× smaller (3.95 GB → 0.53 GB at 256³).

Resolution metrics

  • Sectioned FSC now projects a sector's shell radius onto Z rather than reporting it as an axial period, and uses the Koho et al. (2019) eq. (5) correction on isotropically resampled input. Reproduces the paper's Fig. 4b pollen stack at XY 0.586 µm / Z 4.38 µm against a published 0.59 / 3.91.
  • New axial_floor_factor (default 2.0) reports z as nan when the result falls below the pre-resampling axial Nyquist limit.
  • The one-image checkerboard calibration factor is clamped at 1.0. The Koho fit is an exponential centred on 0.98 that passes 1.0 at a crossing of ~0.925 and reaches 1.82 at the band edge, so a near-Nyquist crossing was reported up to 45% below the sampling limit — in both directions, and xy has no floor guard of its own.
  • spacing=None now means cycles-per-pixel consistently between radial_edges and radial_bin_id; previously it clipped every non-DC voxel into the last bin.
  • ssim gains normalize, which it had silently ignored while psnr and nrmse honoured it.

Breaking changes

This is not a patch release. ~24 removed public symbols, 13 signatures with a parameter removed, mask now keyword-only on pcc/nrmse/psnr/ssim, extract_features returning three values, clahe(kernel_size→n_tiles), and ~14 inputs that were previously accepted and now raise. The cubic.metrics.frc shims are gone.

Fixes that change previously-returned numbers — re-running an analysis will not reproduce prior values:

  • remove_thin_objects changed size_z <= min_z to < min_z, matching its docstring. min_z is defaulted, so every existing caller changes: at min_z=2, objects spanning 1/2/3/4 Z planes went from keeping {3,4} to keeping {2,3,4}.
  • band_limited_pcc, band_limited_ssim and spectral_pcc return nan for degenerate input, matching pcc.
  • clear_xy_borders no longer relabels in 3-D, so cellpose_segment ids can be gapped where they used to be contiguous.
  • get_true_features no longer derives feature ranges from the ground truth when feature_ranges is None — the old path normalized the true side but not the prediction.

Tooling

ruff selected only D and I, so pyflakes never ran and unused imports had accumulated; now ["D", "F", "I"]. rtree added to the mesh extra — extract_mesh_features raised ModuleNotFoundError for anyone following the documented install.

Known issues

  • Single-image checkerboard split is optimistic. Against a synthetic known band limit, two-image FRC is accurate to 1–2% but the checkerboard split reads ~20% too fine, and it collapses once the true limit approaches the split halves' Nyquist — which is half the image's, since the halves are 2× coarser per axis. On interpolated input it is far worse: 0.68× after a subpixel shift, 0.35× after a Gaussian smooth, and no threshold crossing at all after 2× upsampling. The binomial split (split_type="binomial") tracks the two-image reference to ~3% in all of those cases and is the better choice wherever photon counts are available; it is not reliable on deskewed/upsampled data either (0.40×).
  • Near-axis sectioned FSC does not decorrelate. With the default disable_3d_sum=False, checkerboard_split sums consecutive Z pairs before splitting in Y/X, so both halves share their Z content exactly and FSC on the pure k_z line is identically 1.0. axial_floor_factor catches the downstream symptom; the split geometry is the cause.

Numbers above were measured against scikit-image 0.26.0, while CI resolves 0.25.2. The library handles both, but DCR curve shapes differ enough between them that one test asserts a version-independent invariant instead of a fixture-specific value.

Install

pip install cubic==0.9.0a1

Full changelog: v0.8.0...v0.9.0a1

v0.8.0

Choose a tag to compare

@alxndrkalinin alxndrkalinin released this 24 Jul 01:07

First stable release of the 0.8 series, consolidating the two v0.8.0 alpha pre-releases (v0.8.0a1–a2). 5 commits since v0.7.0.

Highlights

  • Cellpose v4 DINO support — the GPU-resident segmentation path now covers all four v4 models: the SAM backbones (cpsam, cpsam_v2) and the DINOv3 backbones (cpdino, cpdino-vitb).
  • Wiener-Butterworth back projector for Richardson-Lucy deconvolution — converges in ~1–2 iterations instead of 10–50 (Guo et al. 2020), with the matched path byte-for-byte unchanged by default.

New features

Segmentation

  • segment_cellpose is now the canonical GPU-resident entry point, supporting all four Cellpose v4 backbones (SAM + DINO). segment_cpsam is kept as a deprecated alias (emits DeprecationWarning, slated for removal after 0.8). (#51)
  • Tunable tile size via a new bsize parameter (defaults 256 for SAM, 384 for DINO), centralized in _resolve_bsize with cellpose's SAM→256 guard and non-positive-bsize validation. (#51)

Preprocessing — deconvolution

  • create_backprojector (cubic.preprocessing) — device-agnostic NumPy/CuPy port of BackProjector.m, supporting traditional, gaussian, butterworth, wiener, and wiener-butterworth types. (#52)
  • backprojector toggle on richardson_lucy_xp, threaded through decon_xpy, richardson_lucy_iter, and deconv_iter_num_finder. Faithful unmatched RL update (no H^T1, no correction<0 clip, estimate floored at 1e-3, both projectors sum-normalized); circulant only, and the default (None) leaves the matched path unchanged. (#52)

Packaging

  • Require cellpose>=4.2.0 (the release that first ships cpsam_v2/cpdino).
  • New cellpose-dino extra for the dinov3 dependency (pip install cubic[cellpose-dino]).

Docs & examples

  • New notebook deconvolution_wb_backprojector_3d.ipynb — matched-vs-WB convergence by PSNR/SSIM and absolute FSC resolution (+ paired script + README row).
  • 3D feature-extraction notebook gains a CPU-vs-GPU comparison.

Install

pip install cubic==0.8.0

Full changelog: v0.7.0...v0.8.0

v0.8.0a2 — Wiener-Butterworth back projector for fast RL deconvolution

Choose a tag to compare

@alxndrkalinin alxndrkalinin released this 19 Jun 05:37

Adds an unmatched Wiener-Butterworth (WB) back projector to Richardson-Lucy deconvolution (Guo et al. 2020, Nat. Biotechnol. 38:1337), converging in ~1–2 iterations instead of 10–50. The matched path is byte-for-byte unchanged by default.

Highlights

  • create_backprojector (cubic.preprocessing) — device-agnostic NumPy/CuPy port of BackProjector.m: traditional, gaussian, butterworth, wiener, wiener-butterworth.
  • backprojector toggle on richardson_lucy_xp, threaded through decon_xpy, richardson_lucy_iter, and deconv_iter_num_finder. Faithful unmatched RL update (no H^T1, no correction<0 clip, estimate floored at 1e-3, both projectors sum-normalized); circulant only.
  • Example notebook deconvolution_wb_backprojector_3d.ipynb (+ paired script + README row).

Validation

  • Matched path byte-exact vs pre-change on CPU/GPU; CPU/GPU parity for the WB path ≤ 4e-8.
  • On real astrocyte data (A40 GPU): WB@2 reaches matched@~10 quality with sharper absolute FSC resolution.
  • ruff, ruff format, mypy, full pytest (397 passed) green.

Full Changelog: v0.8.0a1...v0.8.0a2

v0.8.0a1 — Cellpose v4 DINO model support

Choose a tag to compare

@alxndrkalinin alxndrkalinin released this 15 Jun 21:44
6dbf90e

Features

  • Cellpose v4 DINO models: the GPU-resident segmentation path now supports all four Cellpose v4 models — the SAM backbones (cpsam, cpsam_v2) and the DINOv3 backbones (cpdino, cpdino-vitb). The canonical entry point is now segment_cellpose; segment_cpsam is kept as a deprecated alias (slated for removal after 0.8).
  • Tunable tile size: segment_cellpose exposes a bsize parameter (defaults to 256 for the SAM backbone, 384 for DINO), centralized in _resolve_bsize with cellpose's SAM→256 guard and non-positive-bsize validation.

Packaging

  • Require cellpose>=4.2.0 (the release that first ships cpsam_v2/cpdino).
  • The DINO backbones additionally require dinov3, which is published only on GitHub: pip install git+https://github.com/facebookresearch/dinov3.

Notes

  • Resident masks match stock CellposeModel.eval (AP@0.5 ≥ 0.95) for all four models in 2D and 3D.

Full changes in #51.

v0.7.0

Choose a tag to compare

@alxndrkalinin alxndrkalinin released this 12 Jun 16:45

First stable release of the 0.7 series, consolidating work from 13 alpha pre-releases (v0.7.0a1–a13). 79 commits since v0.6.0.

Highlights

  • Expanded cubic.metrics — a full device-agnostic metrics suite: resolution (FRC/FSC/DCR, band-limited), image quality (PSNR, SSIM, MS-SSIM, MicroSSIM/MicroMS3IM), and Pearson correlation (PCC).
  • GPU-resident Cellpose-SAM segmentation with the v4 API.
  • Device-agnostic GLCM texture features and regionprops extra_properties forwarding.
  • torch interop in the device layer — zero-copy CUDA↔CuPy bridging.

New features

Metrics — resolution

  • DCR, histogram FSC, and band-limited metrics (#32)
  • Binomial splitting for single-image FRC/FSC (#40)

Metrics — image quality

  • MicroSSIM / MicroMS3IM port and a general-purpose ms_ssim (Wang 2003, torchmetrics-compatible), with compute_ssim_elements, an RI-factor solver (bracket + bisection on dS/dα), and image_processing helpers
  • alpha_min / alpha_max kwargs for MicroSSIM (#41)
  • pcc (Pearson correlation coefficient)
  • normalize='min_max' for psnr/nrmse; spatial_dims for 5-D SSIM inputs
  • return_iou passthrough in average_precision

Feature extraction

  • Device-agnostic GLCM texture features
  • extra_properties forwarded through regionprops / regionprops_table

Segmentation

  • GPU-resident Cellpose-SAM eval + v4 API migration (#46)

Device layer & examples

  • torch tensors recognized in asnumpy / get_device; CUDA tensors route to CuPy (zero-copy)
  • Shared device-routing helpers across the scipy/skimage proxies; proxies now scan all args for device
  • Segmentation & feature-extraction example notebooks (#33); Zenodo + pooch auto-download for example data (#48)

Notable fixes

  • SSIM mask footprint moved to the mask's device for cuCIM erosion (GPU per-cell SSIM)
  • FSC/FRC: mean-center before Hamming window (#42); return NaN when the curve never legitimately crosses threshold (#45); honor use_max_nyquist
  • Segmentation: handle empty FOVs, migrate off deprecated skimage min_size= (#44); np.isin for numpy ≥2.2 (#43)
  • average_precision counts distinct labels
  • crop_corner correctness for single-axis and bottom-right crops
  • Rank-agnostic deconvolution slicing with a faithful observer path
  • GLCM: float64 counts, empty-pair directions on CuPy, distance validation

Install

```bash
pip install cubic==0.7.0
```

Full changelog: v0.6.0...v0.7.0

v0.7.0a13 — fix GPU masked SSIM (cuCIM erosion footprint device)

Choose a tag to compare

@alxndrkalinin alxndrkalinin released this 11 Jun 05:29

Fixes

  • GPU masked SSIM: move the erosion footprint onto the mask's device before morphology.erosion. morphology.square/cube receive only an int, so the device-agnostic proxy returned a host footprint; cuCIM's erosion rejected a NumPy footprint paired with a GPU mask (footprint must be either an ndarray or Sequence). Per-cell SSIM now runs on GPU. (1cd5f08)

Full changelog: v0.7.0a12...v0.7.0a13