Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

20x fast convex_hull_image for 2D #63

Merged
merged 43 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
88d02f4
convex hull 2d
Nov 4, 2024
653a406
a bit faster, more precise
Nov 16, 2024
9dac9df
proposal
Nov 16, 2024
cb9267b
fix
Nov 16, 2024
c26a491
offset + unique fused
Nov 16, 2024
f36a10d
test convex_hull_2d
Nov 16, 2024
df8f580
naming
Nov 16, 2024
bf8058a
black + isort
Nov 16, 2024
78bc0ea
fix import
Nov 16, 2024
4ae8e0d
quotes
Nov 16, 2024
933c67a
style
Nov 16, 2024
e3fbd0b
style
Nov 16, 2024
750a047
bfix build
Nov 16, 2024
2faa7af
skimage_warn -> warn, style
Nov 17, 2024
b169344
fix import for old scipy
Nov 17, 2024
461aa6f
fix
Nov 17, 2024
e01ace7
test fix for py3.7
AnihilatorGun Nov 17, 2024
d9bc10b
test offset_coordinates, test corner cases
AnihilatorGun Nov 18, 2024
567da9a
fix
AnihilatorGun Nov 18, 2024
780df14
style...
AnihilatorGun Nov 18, 2024
190d677
fix :bonk:
AnihilatorGun Nov 18, 2024
bd4bce2
mb fix?..
AnihilatorGun Nov 20, 2024
67842e0
revert
AnihilatorGun Nov 20, 2024
73fdd64
mb macos-13
vovaf709 Nov 20, 2024
cdfd4df
Revert "mb macos-13"
vovaf709 Nov 20, 2024
5182ac8
mb this
vovaf709 Nov 20, 2024
a6f02ca
maybe this
vovaf709 Nov 21, 2024
f9f34fe
Fix
vovaf709 Nov 21, 2024
239416f
Fix
vovaf709 Nov 22, 2024
ffad7ac
fixes for macos
Nov 22, 2024
4d5c22e
Fix `normalize_num_threads` for macos
vovaf709 Nov 22, 2024
0106fa0
Fix
vovaf709 Nov 22, 2024
170eee4
some :nail_care:
vovaf709 Nov 23, 2024
db8f9a8
improved package structure
maxme1 Nov 30, 2024
9eee515
fixed compat
maxme1 Nov 30, 2024
091fe01
morphology
AnihilatorGun Dec 2, 2024
109fa12
fix tests
AnihilatorGun Dec 2, 2024
92e50e8
randomized data test
AnihilatorGun Dec 2, 2024
52dcb78
Merge branch 'convex_hull_2d' of https://github.com/neuro-ml/imops in…
AnihilatorGun Dec 2, 2024
8fd1d56
a bit cooler hull with smaller error
AnihilatorGun Dec 4, 2024
1aa32ab
No warn on empty image
AnihilatorGun Dec 4, 2024
b8c2d72
3.13
vovaf709 Dec 4, 2024
aa5deac
revert
vovaf709 Dec 4, 2024
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
17 changes: 5 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
needs: [ check_version ]
strategy:
matrix:
os: [ ubuntu-22.04, windows-2019, macOS-12 ]
os: [ ubuntu-22.04, windows-2019, macOS-13 ]

name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
Expand All @@ -47,17 +47,10 @@ jobs:
python-version: '3.9'
- name: Install cibuildwheel
run: python -m pip install cibuildwheel==2.17.0
- name: Install gcc for mac
if: matrix.os == 'macOS-12'
- name: Install llvm for mac
if: matrix.os == 'macOS-13'
run: |
brew install llvm libomp
echo $PATH
ln -sf /usr/local/bin/gcc-11 /usr/local/bin/gcc
ln -sf /usr/local/bin/g++-11 /usr/local/bin/g++
ls /usr/local/bin/gcc*
ls /usr/local/bin/g++*
gcc --version
g++ --version
brew install llvm
- name: Install g++-11 for ubuntu
if: matrix.os == 'ubuntu-22.04'
id: install_cc
Expand All @@ -79,7 +72,7 @@ jobs:
python -m cibuildwheel --output-dir wheelhouse
env:
CIBW_ENVIRONMENT_MACOS: >
PATH="/usr/local/opt/llvm/bin:$PATH" LDFLAGS="-L/usr/local/opt/llvm/lib" CPPFLAGS="-I/usr/local/opt/llvm/include"
CC="$(brew --prefix llvm)/bin/clang" CXX="$(brew --prefix llvm)/bin/clang++"
CIBW_BUILD: cp37-* cp38-* cp39-* cp310-* cp311-* cp312-*
CIBW_BEFORE_BUILD_LINUX: 'if [ $(python -c "import sys; print(sys.version_info[1])") -ge 9 ]; then python -m pip install "numpy<3.0.0" --config-settings=setup-args="-Dallow-noblas=true"; fi'
- uses: actions/upload-artifact@v3
Expand Down
17 changes: 5 additions & 12 deletions .github/workflows/test_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
build_wheels:
strategy:
matrix:
os: [ubuntu-22.04, windows-2019, macOS-12 ]
os: [ubuntu-22.04, windows-2019, macOS-13 ]

name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
Expand All @@ -21,17 +21,10 @@ jobs:
python-version: '3.9'
- name: Install cibuildwheel
run: python -m pip install cibuildwheel==2.17.0
- name: Install gcc for mac
if: matrix.os == 'macOS-12'
- name: Install llvm for mac
if: matrix.os == 'macOS-13'
run: |
brew install llvm libomp
echo $PATH
ln -sf /usr/local/bin/gcc-11 /usr/local/bin/gcc
ln -sf /usr/local/bin/g++-11 /usr/local/bin/g++
ls /usr/local/bin/gcc*
ls /usr/local/bin/g++*
gcc --version
g++ --version
brew install llvm
- name: Install g++-11 for ubuntu
if: matrix.os == 'ubuntu-22.04'
id: install_cc
Expand All @@ -53,7 +46,7 @@ jobs:
python -m cibuildwheel --output-dir wheelhouse
env:
CIBW_ENVIRONMENT_MACOS: >
PATH="/usr/local/opt/llvm/bin:$PATH" LDFLAGS="-L/usr/local/opt/llvm/lib" CPPFLAGS="-I/usr/local/opt/llvm/include"
CC="$(brew --prefix llvm)/bin/clang" CXX="$(brew --prefix llvm)/bin/clang++"
CIBW_BUILD: cp37-* cp39-* cp312-*
CIBW_SKIP: "*manylinux_x86_64"
CIBW_BEFORE_BUILD_LINUX: 'if [ $(python -c "import sys; print(sys.version_info[1])") -ge 9 ]; then python -m pip install "numpy<3.0.0" --config-settings=setup-args="-Dallow-noblas=true"; fi'
2 changes: 1 addition & 1 deletion _build_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def get_ext_modules():
'/O3' if on_windows else '-O3',
] # FIXME: account for higher gcc versions

modules = ['backprojection', 'measure', 'morphology', 'numeric', 'radon', 'zoom']
modules = ['backprojection', 'measure', 'morphology', 'numeric', 'radon', 'zoom', 'convex_hull']
modules_to_link_against_numpy_core_math_lib = ['numeric']

src_dir = Path(__file__).parent / name / 'src'
Expand Down
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pip install imops[numba] # additionally install Numba backend

::: imops.morphology.distance_transform_edt

::: imops.morphology.convex_hull_image

::: imops.measure.label

::: imops.measure.center_of_mass
Expand Down
2 changes: 1 addition & 1 deletion imops/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.9.0'
__version__ = '0.9.1'
84 changes: 84 additions & 0 deletions imops/morphology.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,28 @@
from edt import edt
from scipy.ndimage import distance_transform_edt as scipy_distance_transform_edt, generate_binary_structure
from scipy.ndimage._nd_image import euclidean_feature_transform
from scipy.spatial import ConvexHull


try:
from scipy.spatial import QhullError
except ImportError:
from scipy.spatial.qhull import QhullError # Old scipy has another structure

from skimage.morphology import (
binary_closing as scipy_binary_closing,
binary_dilation as scipy_binary_dilation,
binary_erosion as scipy_binary_erosion,
binary_opening as scipy_binary_opening,
)
from skimage.util import unique_rows

from .backend import BackendLike, Cython, Scipy, resolve_backend
from .box import add_margin, box_to_shape, mask_to_box, shape_to_box
from .compat import _ni_support
from .crop import crop_to_box
from .pad import restore_crop
from .src._convex_hull import _grid_points_in_poly, _left_right_bounds, _offset_unique
from .src._fast_morphology import (
_binary_dilation as cython_fast_binary_dilation,
_binary_erosion as cython_fast_binary_erosion,
Expand Down Expand Up @@ -517,3 +527,77 @@ def distance_transform_edt(
return result[0]

return None


def convex_hull_image(image, offset_coordinates=True):
"""
Fast convex hull of an image. Similar to skimage.morphology.convex_hull_image with include_borders=True

Parameters
----------
image: np.ndarray
input image, must be 2D
offset_coordinates: bool
If True, a pixel at coordinate, e.g., (4, 7) will be represented by coordinates
(3.5, 7), (4.5, 7), (4, 6.5), and (4, 7.5).
This adds some “extent” to a pixel when computing the hull.

Returns
-------
output: np.ndarray
resulting convex hull of the input image

Examples
--------
```python
chull = convex_hull_image(x)
```
"""

ndim = image.ndim
if ndim != 2:
raise RuntimeError(f'convex_hull_image is currently implemented only for 2D arrays, got {ndim}D array')
vovaf709 marked this conversation as resolved.
Show resolved Hide resolved

if np.count_nonzero(image) == 0:
warn(
'Input image is entirely zero, no valid convex hull. ' 'Returning empty image',
UserWarning,
)
return np.zeros(image.shape, dtype=bool)

# In 2D, we do an optimisation by choosing only pixels that are
# the starting or ending pixel of a row or column. This vastly
# limits the number of coordinates to examine for the virtual hull.
image = np.ascontiguousarray(image, dtype=np.uint8)

coords = _left_right_bounds(image)

if offset_coordinates:
coords = _offset_unique(coords)
else:
coords = unique_rows(coords)

# Find the convex hull
try:
hull = ConvexHull(coords)
except QhullError as err:
warn(f'Failed to get convex hull image. ' f'Returning empty image, see error message below: \n' f'{err}')
return np.zeros(image.shape, dtype=bool)

vertices = hull.points[hull.vertices]

# return vertices

# If 2D, use fast Cython function to locate convex hull pixels
labels = _grid_points_in_poly(
np.ascontiguousarray(vertices[:, 0], dtype=np.float32),
np.ascontiguousarray(vertices[:, 1], dtype=np.float32),
image.shape[0],
image.shape[1],
len(vertices),
)

# edge points and vertices are included
mask = labels.view(bool)

return mask
Loading