Skip to content

Commit 9ef70fe

Browse files
Some doc updates and cosmetics (#28)
* Some doc updates and cosmetics * Updates for PyPI build
1 parent b679e2d commit 9ef70fe

8 files changed

Lines changed: 190 additions & 83 deletions

File tree

.github/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
- name: Install package and docs tooling
2727
run: python -m pip install -v ".[test]" pdoc
2828
- name: Build docs
29-
run: pdoc bioimage_cpp -o docs
29+
run: PYTHONPATH=src pdoc bioimage_cpp -o docs
3030
- uses: actions/upload-pages-artifact@v3
3131
with:
3232
path: docs

.github/workflows/wheels.yml

Lines changed: 102 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,113 @@ on:
55
release:
66
types: [published]
77

8+
permissions:
9+
contents: read
10+
811
jobs:
9-
wheels:
10-
name: ${{ matrix.os }}
12+
build_wheels:
13+
name: wheels-${{ matrix.os }}
1114
runs-on: ${{ matrix.os }}
1215
strategy:
1316
fail-fast: false
1417
matrix:
15-
os: [ubuntu-latest, macos-13, macos-14, windows-latest]
18+
os: [ubuntu-latest, macos-latest, windows-latest]
19+
1620
steps:
1721
- uses: actions/checkout@v4
18-
- uses: pypa/cibuildwheel@v2.18
22+
23+
- name: Build wheels
24+
uses: pypa/cibuildwheel@v3.4.1
1925
env:
20-
CIBW_BUILD: "cp310-* cp311-* cp312-*"
21-
CIBW_TEST_REQUIRES: pytest
22-
CIBW_TEST_COMMAND: "python -c \"import bioimage_cpp\" && pytest {project}/tests"
26+
CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*"
27+
CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_*"
28+
CIBW_ARCHS_MACOS: "native"
29+
CIBW_TEST_REQUIRES: "pytest pooch scipy"
30+
CIBW_TEST_COMMAND: "python -c \"import bioimage_cpp; print(bioimage_cpp.__file__)\" && pytest -q {project}/tests"
31+
32+
- name: Upload wheels
33+
uses: actions/upload-artifact@v4
34+
with:
35+
name: cibw-wheels-${{ matrix.os }}
36+
path: wheelhouse/*.whl
37+
38+
build_sdist:
39+
name: sdist
40+
runs-on: ubuntu-latest
41+
42+
steps:
43+
- uses: actions/checkout@v4
44+
- uses: actions/setup-python@v5
45+
with:
46+
python-version: "3.11"
47+
48+
- name: Install build tooling
49+
run: python -m pip install build twine
50+
51+
- name: Build sdist
52+
run: python -m build --sdist
53+
54+
- name: Check sdist metadata
55+
run: python -m twine check dist/*
56+
57+
- name: Upload sdist
58+
uses: actions/upload-artifact@v4
59+
with:
60+
name: cibw-sdist
61+
path: dist/*.tar.gz
62+
63+
publish-testpypi:
64+
name: publish-to-testpypi
65+
needs: [build_wheels, build_sdist]
66+
runs-on: ubuntu-latest
67+
if: github.event_name == 'workflow_dispatch'
68+
69+
environment:
70+
name: testpypi
71+
url: https://test.pypi.org/p/bioimage-cpp
72+
73+
permissions:
74+
id-token: write
75+
76+
steps:
77+
- name: Download distributions
78+
uses: actions/download-artifact@v4
79+
with:
80+
pattern: cibw-*
81+
path: dist
82+
merge-multiple: true
83+
84+
- name: List distributions
85+
run: ls -lh dist
86+
87+
- name: Publish to TestPyPI
88+
uses: pypa/gh-action-pypi-publish@release/v1
89+
with:
90+
repository-url: https://test.pypi.org/legacy/
91+
92+
publish-pypi:
93+
name: publish-to-pypi
94+
needs: [build_wheels, build_sdist]
95+
runs-on: ubuntu-latest
96+
if: github.event_name == 'release' && github.event.action == 'published'
97+
98+
environment:
99+
name: pypi
100+
url: https://pypi.org/p/bioimage-cpp
101+
102+
permissions:
103+
id-token: write
104+
105+
steps:
106+
- name: Download distributions
107+
uses: actions/download-artifact@v4
108+
with:
109+
pattern: cibw-*
110+
path: dist
111+
merge-multiple: true
112+
113+
- name: List distributions
114+
run: ls -lh dist
115+
116+
- name: Publish to PyPI
117+
uses: pypa/gh-action-pypi-publish@release/v1

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ venv/
3030
*.npz
3131

3232
CLAUDE.md
33+
pypi-release.txt

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,10 @@ Don't add `std::chrono` snippets ad hoc; use the existing macros so future profi
192192
193193
## Documentation
194194
195-
`README.md` covers: what `bioimage-cpp` is and isn't, install/build, minimal examples, design philosophy. Public functions need concise docstrings documenting input shapes, supported dtypes, output shapes/dtypes, copy behavior, background-label behavior (if relevant), and axis/coordinate conventions.
196-
197195
Update `MIGRATION_GUIDE.md` whenever public functionality changes, so users migrating from `nifty`/`affogato` see the corresponding `bioimage-cpp` API, behavioral differences, and intentional improvements.
198196
197+
The documentation is build via pdoc, see `.github/workflow/docs.yaml`.
198+
199199
## Checklist when modifying code
200200
201201
1. Reuse existing `detail/` helpers; add new shared ones rather than duplicating.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 bioimage-cpp contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 2 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,5 @@
11
# bioimage-cpp
22

3-
Stand-alone implementation of image analysis and segmentaton functionality in C++ with minimal python bindings.
3+
Image processing and segmentation functionality in C++ with light-weight python bindings through nanobind and minimal dependencies to enable distribution via pip.
44

5-
`bioimage-cpp` is a small C++/Python package for dependency-light bioimage-analysis algorithms that are difficult to install from larger C++ libraries. It exposes NumPy-based Python APIs backed by a focused C++20 core.
6-
7-
## Install
8-
9-
```bash
10-
python -m pip install bioimage-cpp
11-
```
12-
13-
## Build from source
14-
15-
```bash
16-
python -m pip install -v ".[test]"
17-
pytest -q
18-
```
19-
20-
## Example
21-
22-
```python
23-
import numpy as np
24-
import bioimage_cpp as bic
25-
26-
labels = np.array([1, 3, 2, 1], dtype=np.uint64)
27-
relabeling = {1: 10, 2: 20, 3: 30}
28-
29-
out = bic.utils.take_dict(relabeling, labels)
30-
# array([10, 30, 20, 10], dtype=uint64)
31-
```
32-
33-
```python
34-
affinities = np.ones((2, 4, 4), dtype=np.float32)
35-
offsets = [[0, 1], [1, 0]]
36-
37-
segmentation = bic.segmentation.mutex_watershed(
38-
affinities,
39-
offsets,
40-
number_of_attractive_channels=2,
41-
)
42-
```
43-
44-
```python
45-
image = np.arange(64, dtype=np.float32).reshape(8, 8)
46-
matrix = np.array([[1.0, 0.0, 0.5], [0.0, 1.0, -1.0]])
47-
48-
transformed = bic.transformation.affine_transform(
49-
image,
50-
matrix,
51-
order=3,
52-
fill_value=0,
53-
)
54-
```
55-
56-
```python
57-
graph = bic.graph.UndirectedGraph.from_edges(4, [[0, 1], [1, 2], [2, 3]])
58-
graph.find_edge(2, 1)
59-
# 1
60-
61-
grid = bic.graph.grid_graph((64, 64))
62-
grid.number_of_edges
63-
# 8064
64-
65-
edge_weights = bic.graph.grid_boundary_features(grid, boundary_map)
66-
local_weights, valid_edges, lifted_uvs, lifted_weights, offset_ids = (
67-
bic.graph.grid_affinity_features_with_lifted(grid, affinities, offsets)
68-
)
69-
```
70-
71-
## Scope
72-
73-
The project is not a compatibility layer for `nifty`, `vigra`, or other large libraries. It keeps I/O and heavy dependencies out of the C++ core; callers should use existing Python packages for file formats and pass NumPy arrays into `bioimage-cpp`.
5+
Please refer to [the documentation](https://computational-cell-analytics.github.io/bioimage-cpp/) for details.

pyproject.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,24 @@ authors = [
1616
classifiers = [
1717
"Development Status :: 3 - Alpha",
1818
"Intended Audience :: Science/Research",
19+
"License :: OSI Approved :: MIT License",
1920
"Programming Language :: C++",
2021
"Programming Language :: C++ :: 20",
2122
"Programming Language :: Python :: 3",
2223
"Programming Language :: Python :: 3 :: Only",
24+
"Programming Language :: Python :: 3.10",
25+
"Programming Language :: Python :: 3.11",
26+
"Programming Language :: Python :: 3.12",
27+
"Programming Language :: Python :: 3.13",
28+
"Programming Language :: Python :: 3.14",
2329
"Topic :: Scientific/Engineering :: Image Processing",
2430
]
2531

32+
[project.urls]
33+
Homepage = "https://github.com/computational-cell-analytics/bioimage-cpp"
34+
Repository = "https://github.com/computational-cell-analytics/bioimage-cpp"
35+
Issues = "https://github.com/computational-cell-analytics/bioimage-cpp/issues"
36+
2637
[project.optional-dependencies]
2738
test = ["pytest", "pooch", "scipy"]
2839
data = ["pooch"]
@@ -31,6 +42,16 @@ data = ["pooch"]
3142
cmake.version = ">=3.21"
3243
cmake.build-type = "Release"
3344
wheel.packages = ["src/bioimage_cpp"]
45+
sdist.exclude = [
46+
"pypi-release.txt",
47+
"dist/",
48+
"wheelhouse/",
49+
"_skbuild/",
50+
"build/",
51+
"*.egg-info/",
52+
".pytest_cache/",
53+
"__pycache__/",
54+
]
3455

3556
[tool.pytest.ini_options]
3657
testpaths = ["tests"]

src/bioimage_cpp/__init__.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
"""`bioimage_cpp` implements functionality needed for image procesing in C++.
1+
"""`bioimage_cpp` implements image processing and segmentation functionality in C++.
22
It generates light-weight python bindings with nanobind with minimal dependencies to enable distribution via pip.
33
4-
The main goal of this library is to provide functionality that is missing from scipy or scikit-image or
5-
to provide more performant versions of functionality from these libraries.
4+
The main goal of this library is to provide functionality that is missing from scipy or scikit-image,
5+
or to provide more performant versions of functionality from these libraries.
66
77
The functionality implemented here bundles and improves algorithms etc. from:
88
- [affogato](https://github.com/constantinpape/affogato)
@@ -19,6 +19,43 @@
1919
2020
TODO: document once on pip / conda
2121
22+
## Functionality
23+
24+
This library provides the following functionality:
25+
- `affinities`: functionality for deriving affinities from segmentations.
26+
- `filters`: efficient implementation of convolutional image filters.
27+
- `graph`: graph creation and graph (partitioning) algorithms.
28+
- `segmentation`: image segmentation functionality.
29+
- `transformation`: affine transformations.
30+
- `util`: misc utility functionality.
31+
32+
## Example
33+
34+
Below is a simple example for creating and partitioning a graph with this library.
35+
For more realistic use-cases check out [the migration guide](migration-guide).
36+
37+
```python
38+
import numpy as np
39+
import bioimage_cpp as bic
40+
41+
# Create a graph with 50 nodes.
42+
graph = bic.graph.undirected_graph(number_of_nodes=50)
43+
44+
# Insert a bunch of edges forming a chain.
45+
graph.insert_edges(
46+
np.concatenate([np.arange(0, 49)[:, None], np.arange(1, 50)[:, None]], axis=1)
47+
)
48+
49+
# Create edge weights in [-1, 1].
50+
weights = 2 * np.random.rand(graph.number_of_edges) - 1
51+
52+
# Partition the graph via multicut (greedy solver).
53+
objective = bic.graph.MulticutObjective(graph, weights)
54+
solver = bic.graph.GreedyAdditiveMulticut()
55+
partition = solver.optimize(objective)
56+
print("Partitioned into", len(np.unique(partition)), "elements")
57+
```
58+
2259
.. include:: ../../MIGRATION_GUIDE.md
2360
"""
2461

0 commit comments

Comments
 (0)