Skip to content

Commit

Permalink
Merge pull request #214 from clEsperanto/release-0.11.0
Browse files Browse the repository at this point in the history
Release 0.11.0
  • Loading branch information
StRigaud authored Jul 10, 2024
2 parents c2adad1 + 17e9117 commit f3c0f8c
Show file tree
Hide file tree
Showing 10 changed files with 471 additions and 4 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ option(BUILD_SHARED_LIBS OFF)


## CLIc dependency
set(CLIC_REPO_TAG 0.10.3) # branch name for dev
set(CLIC_REPO_TAG 0.11.0) # branch name for dev
set(CLIC_REPO_URL https://github.com/clEsperanto/CLIc.git)
set(BUILD_OPENCL_BACKEND ON CACHE BOOL "Build with OCL if FOUND" FORCE)
set(BUILD_CUDA_BACKEND ON CACHE BOOL "Build with CUDA if FOUND" FORCE)
Expand Down
58 changes: 58 additions & 0 deletions pyclesperanto/_tier1.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,64 @@ def binary_xor(
return op(device=device, src0=input_image0, src1=input_image1, dst=output_image)


@plugin_function(category=["filter", "binary processing"])
def binary_supinf(
input_image: Image,
output_image: Optional[Image] = None,
device: Optional[Device] = None,
) -> Image:
"""Compute the maximum of the erosion with plannar structuring elements. Warning:
This operation is only supported BINARY data type images.
Parameters
----------
input_image: Image
The binary input image to be processed.
output_image: Optional[Image] = None
The output image where results are written into.
device: Optional[Device] = None
Device to perform the operation on.
Returns
-------
Image
"""

from ._pyclesperanto import _binary_supinf as op

return op(device=device, src=input_image, dst=output_image)


@plugin_function(category=["filter", "binary processing"])
def binary_infsup(
input_image: Image,
output_image: Optional[Image] = None,
device: Optional[Device] = None,
) -> Image:
"""Compute the minimum of the dilation with plannar structuring elements. Warning:
This operation is only supported BINARY data type images.
Parameters
----------
input_image: Image
The binary input image to be processed.
output_image: Optional[Image] = None
The output image where results are written into.
device: Optional[Device] = None
Device to perform the operation on.
Returns
-------
Image
"""

from ._pyclesperanto import _binary_infsup as op

return op(device=device, src=input_image, dst=output_image)


@plugin_function
def block_enumerate(
input_image0: Image,
Expand Down
50 changes: 50 additions & 0 deletions pyclesperanto/_tier3.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,3 +464,53 @@ def minimum_position(input_image: Image, device: Optional[Device] = None) -> lis
from ._pyclesperanto import _minimum_position as op

return op(device=device, src=input_image)


@plugin_function
def morphological_chan_vese(
input_image: Image,
output_image: Optional[Image] = None,
num_iter: int = 100,
smoothing: int = 1,
lambda1: float = 1,
lambda2: float = 1,
device: Optional[Device] = None,
) -> Image:
"""Compute an active contour model using the Chan-Vese morphological algorithm. The
output image (dst) should also be initialisation of the contour. If not provided
(nullptr), the function will use a checkboard pattern initialisation.
Parameters
----------
input_image: Image
Input image to process.
output_image: Optional[Image] = None
Output contour, can also be use to provide initialisation.
num_iter: int = 100
Number of iterations.
smoothing: int = 1
Number of
lambda1: float = 1
Lambda1.
lambda2: float = 1
Lambda2.
device: Optional[Device] = None
Device to perform the operation on.
Returns
-------
Image
"""

from ._pyclesperanto import _morphological_chan_vese as op

return op(
device=device,
src=input_image,
dst=output_image,
num_iter=int(num_iter),
smoothing=int(smoothing),
lambda1=float(lambda1),
lambda2=float(lambda2),
)
4 changes: 2 additions & 2 deletions pyclesperanto/_version.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# pyclesperanto version
VERSION_CODE = 0, 10, 3
VERSION_CODE = 0, 11, 0
VERSION_STATUS = ""
VERSION = ".".join(str(x) for x in VERSION_CODE) + VERSION_STATUS

# clic version
CLIC_VERSION_CODE = 0, 10, 3
CLIC_VERSION_CODE = 0, 11, 0
CLIC_VERSION_STATUS = ""
CLIC_VERSION = ".".join(str(x) for x in CLIC_VERSION_CODE) + CLIC_VERSION_STATUS

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ description = "GPU-accelerated image processing in python using OpenCL"
name = "pyclesperanto"
readme = "README.md"
requires-python = ">=3.7"
version = "0.10.3"
version = "0.11.0"

[project.urls]
Documentation = "https://clesperanto.github.io/pyclesperanto/"
Expand Down
10 changes: 10 additions & 0 deletions src/wrapper/tier1_.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ m.def("_binary_xor", &cle::tier1::binary_xor_func, "Call binary_xor from C++.",
py::arg("device"), py::arg("src0"), py::arg("src1"), py::arg("dst"));


m.def("_binary_supinf", &cle::tier1::binary_supinf_func, "Call binary_supinf from C++.",
py::return_value_policy::take_ownership,
py::arg("device"), py::arg("src"), py::arg("dst"));


m.def("_binary_infsup", &cle::tier1::binary_infsup_func, "Call binary_infsup from C++.",
py::return_value_policy::take_ownership,
py::arg("device"), py::arg("src"), py::arg("dst"));


m.def("_block_enumerate", &cle::tier1::block_enumerate_func, "Call block_enumerate from C++.",
py::return_value_policy::take_ownership,
py::arg("device"), py::arg("src0"), py::arg("src1"), py::arg("dst"), py::arg("blocksize"));
Expand Down
5 changes: 5 additions & 0 deletions src/wrapper/tier3_.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,9 @@ m.def("_minimum_position", &cle::tier3::minimum_position_func, "Call minimum_pos
py::arg("device"), py::arg("src"));


m.def("_morphological_chan_vese", &cle::tier3::morphological_chan_vese_func, "Call morphological_chan_vese from C++.",
py::return_value_policy::take_ownership,
py::arg("device"), py::arg("src"), py::arg("dst"), py::arg("num_iter"), py::arg("smoothing"), py::arg("lambda1"), py::arg("lambda2"));


}
118 changes: 118 additions & 0 deletions tests/test_infsup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import numpy as np
from skimage.segmentation.morphsnakes import inf_sup, sup_inf

import pyclesperanto as cle

cle.select_device("TX")


def test_inferior_superior_2d():
test = cle.push(
np.asarray(
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 0, 0, 0, 0, 1, 1, 0],
[0, 1, 1, 0, 0, 0, 0, 1, 1, 0],
[0, 1, 1, 0, 0, 0, 0, 1, 1, 0],
[0, 1, 1, 0, 0, 0, 0, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
).astype(np.uint8)
)

reference = cle.push(
np.asarray(
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
[0, 1, 1, 0, 0, 0, 0, 1, 1, 0],
[0, 1, 1, 0, 0, 0, 0, 1, 1, 0],
[0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
).astype(np.uint8)
)

result = cle.create(test)
cle.binary_infsup(test, result)

print(result)

a = cle.pull(result)
b = cle.pull(reference)
assert np.array_equal(a, b)


def test_inferior_superior_3d():
test = cle.push(
np.asarray(
[
[
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
],
[
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 0, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
],
[
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
],
]
).astype(np.uint8)
)

reference = cle.push(
np.asarray(
[
[
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
],
[
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
],
[
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
],
]
).astype(np.uint8)
)

result = cle.create(test)
cle.binary_infsup(test, result)

print(result)

a = cle.pull(result)
b = cle.pull(reference)
assert np.array_equal(a, b)
42 changes: 42 additions & 0 deletions tests/test_morphological_chan-vese.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import numpy as np
from skimage import morphology, segmentation

import pyclesperanto as cle

# def test_morphological_chan_vese():
# image = generate_disk((25, 25), 5).astype(np.float32)
# reference = cle.push(segmentation.morphological_chan_vese(image, num_iter=1, smoothing=1, lambda1=1, lambda2=1))
# result = cle.morphological_chan_vese(image, num_iter=1, smoothing=, lambda1=1, lambda2=1)
# a = cle.pull(result).astype(np.uint8)
# b = cle.pull(reference).astype(np.uint8)
# print(a)
# print(b)
# assert (np.array_equal(a, b))


# def test_morphological_chan_vese_on_membranes_2d():
# from skimage.data import cells3d
# from skimage.segmentation import morphological_chan_vese
# import pyclesperanto as cle
# image2d = cells3d()[30, 0, ...]
# result_gpu = cle.morphological_chan_vese(image2d, num_iter=20, smoothing=10)
# result_cpu = morphological_chan_vese(image2d, num_iter=20, smoothing=10)
# assert cle.array_equal(result_cpu, result_gpu)

# def test_morphological_chan_vese_on_membranes_3d():
# from skimage.data import cells3d
# from skimage.segmentation import morphological_chan_vese
# import pyclesperanto as cle
# image3d = cells3d()[10:50:, 0, :40, :40]
# result_gpu = cle.morphological_chan_vese(image3d, num_iter=20, smoothing=10)
# result_cpu = morphological_chan_vese(image3d, num_iter=20, smoothing=10)
# cle.array_equal(result_cpu, result_gpu)


def generate_disk(shape, radius):
image = np.zeros(shape)
image[
image.shape[0] // 2 - radius : image.shape[0] // 2 + radius + 1,
image.shape[1] // 2 - radius : image.shape[1] // 2 + radius + 1,
] = morphology.disk(radius)
return image
Loading

0 comments on commit f3c0f8c

Please sign in to comment.