Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .github/workflows/build_wheel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@ jobs:
uses: docker/setup-qemu-action@v1

- name: Build wheels
uses: pypa/cibuildwheel@v2.19.1
uses: joerick/cibuildwheel@v2.22.0
# to supply options, put them in 'env', like:
env:
CIBW_ARCHS_LINUX: ${{matrix.arch}}
CIBW_BEFORE_BUILD: pip install numpy setuptools wheel cython
CIBW_BEFORE_BUILD: pip install numpy pybind11 setuptools wheel pkginfo twine
CIBW_ARCHS_MACOS: "x86_64 arm64"
CIBW_ARCHS: auto64

- uses: actions/upload-artifact@v2
- name: Upload built wheels
uses: actions/upload-artifact@v4
with:
name: built-wheels-${{ matrix.os }}-${{ matrix.arch }}
path: ./wheelhouse/*.whl
if-no-files-found: warn
2 changes: 1 addition & 1 deletion .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest, windows-2019]
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ img = load_3d_em_stack()
img_pyramid = tinybrain.downsample_with_averaging(img, factor=(2,2,1), num_mips=5, sparse=False)

labels = load_3d_labels()
label_pyramid = tinybrain.downsample_segmentation(labels, factor=(2,2,1), num_mips=5, sparse=False))
label_pyramid = tinybrain.downsample_segmentation(labels, factor=(2,2,1), num_mips=5, sparse=False)

# We also have a few other types
img_pyramid = tinybrain.downsample_with_min_pooling(image, factor=(2,2,1), num_mips=5)
img_pyramid = tinybrain.downsample_with_max_pooling(image, factor=(2,2,1), num_mips=5)
img_pyramid = tinybrain.downsample_with_striding(image, factor=(2,2,1), num_mips=5)
```

## Installation
Expand All @@ -27,7 +32,7 @@ pip install tinybrain
## Motivation

Image hierarchy generation in connectomics uses a few different techniques for
visualizing data, but predominantly we create image pyramids of uint8 grayscale images using 2x2 average pooling and of uint8 to uint64 segmentation labels using 2x2 mode pooling. When images become very large and people wish to visualze upper mip levels using three axes at once, it becomes desirable to perform 2x2x2 downsamples to maintain isotropy.
visualizing data, but predominantly we create image pyramids of uint8 grayscale images using 2x2 average pooling and of uint8 to uint64 segmentation labels using 2x2 mode pooling. When images become very large and people wish to visualize upper mip levels using three axes at once, it becomes desirable to perform 2x2x2 downsamples to maintain isotropy.

It's possible to compute both of these using numpy, however as multiple packages found it useful to copy the downsample functions, it makes sense to formalize these functions into a seperate library located on PyPI.

Expand Down
6 changes: 3 additions & 3 deletions automated_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def test_even_odd2d():
assert np.array_equal(oddimg, ans3x3x3)
assert np.array_equal(oddimgf, ans3x3x3)

@pytest.mark.parametrize("dtype", (np.uint8, np.uint16, np.float32, np.float64))
@pytest.mark.parametrize("dtype", (np.int8, np.int16, np.uint8, np.uint16, np.float32, np.float64))
@pytest.mark.parametrize("sparse", [False, True])
def test_accelerated_vs_numpy_avg_pooling_2x2x1(dtype, sparse):
image = np.random.randint(0,255, size=(512, 512, 6), dtype=np.uint8).astype(dtype, copy=False)
Expand All @@ -147,7 +147,7 @@ def test_accelerated_vs_numpy_avg_pooling_2x2x1(dtype, sparse):

assert np.all(mips[-1] == npimg)

@pytest.mark.parametrize("dtype", (np.uint8, np.uint16, np.float32, np.float64))
@pytest.mark.parametrize("dtype", (np.int8, np.int16, np.uint8, np.uint16, np.float32, np.float64))
def test_accelerated_vs_numpy_avg_pooling_2x2x1_simple_sparse(dtype):
for x in [0,1]:
for y in [0,1]:
Expand Down Expand Up @@ -189,7 +189,7 @@ def test_accelerated_vs_numpy_avg_pooling_2x2x1_simple_sparse(dtype):
assert np.all(res[1] == ans)


@pytest.mark.parametrize("dtype", (np.uint8, np.uint16, np.float32, np.float64))
@pytest.mark.parametrize("dtype", (np.int8, np.int16, np.uint8, np.uint16, np.float32, np.float64))
@pytest.mark.parametrize("sx", (6,7,1024,1025))
@pytest.mark.parametrize("sy", (6,7,1024,1025))
@pytest.mark.parametrize("sz", (4,5,32,33))
Expand Down
19 changes: 14 additions & 5 deletions tinybrain/accelerated.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (C) 2019, William Silversmith
Copyright (C) 2019,2025 William Silversmith

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -359,7 +359,7 @@ T* _average_pooling_2x2_single_mip(
+ static_cast<T2>(d)
) / static_cast<T2>(
std::max(
static_cast<T>((a > 0) + (b > 0) + (c > 0) + (d > 0)),
static_cast<T>((a != 0) + (b != 0) + (c != 0) + (d != 0)),
static_cast<T>(1)
)
)
Expand All @@ -375,7 +375,7 @@ T* _average_pooling_2x2_single_mip(
+ static_cast<T2>(b)
) / static_cast<T2>(
std::max(
static_cast<T>((a > 0) + (b > 0)),
static_cast<T>((a != 0) + (b != 0)),
static_cast<T>(1)
)
));
Expand Down Expand Up @@ -931,8 +931,17 @@ U* denominator_2x2x2(

template <typename T, typename U>
inline void render_image(T* accum, U* oimg, const uint32_t bitshift, const size_t ovoxels) {
for (size_t i = 0; i < ovoxels; i++) {
oimg[i] = static_cast<U>(accum[i] >> bitshift);
if constexpr (std::is_signed<T>::value) {
for (size_t i = 0; i < ovoxels; i++) {
oimg[i] = (accum[i] < 0)
? -static_cast<U>(std::abs(accum[i]) >> bitshift)
: static_cast<U>(accum[i] >> bitshift);
}
}
else {
for (size_t i = 0; i < ovoxels; i++) {
oimg[i] = static_cast<U>(accum[i] >> bitshift);
}
}
}

Expand Down
Loading
Loading