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
MAINT: Rewrote scipy.ndimage.find_object function in cython #5006
base: main
Are you sure you want to change the base?
Changes from 5 commits
a83b747
7b63879
caf51f6
049ac58
6af1c34
86208e5
49214c1
22f64f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
###################################################################### | ||
# Cython version of scipy.ndimage.measurement.find_objects() function. | ||
###################################################################### | ||
|
||
cimport cython | ||
from cython cimport sizeof | ||
import numpy as np | ||
cimport numpy as np | ||
|
||
np.import_array() | ||
|
||
cdef extern from *: | ||
ctypedef int Py_intptr_t | ||
|
||
ctypedef void (*PyArray_CopySwapFunc)(void *, void *, int, void *) | ||
|
||
cdef extern from "numpy/arrayobject.h" nogil: | ||
ctypedef struct PyArrayIterObject: | ||
np.intp_t *coordinates | ||
char *dataptr | ||
np.npy_bool contiguous | ||
|
||
ctypedef struct PyArrayObject: | ||
int nd | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not being used anywhere right now. If there is an advantage in using this over |
||
|
||
void *PyDataMem_NEW(size_t) | ||
void PyDataMem_FREE(void *) | ||
|
||
|
||
###################################################################### | ||
# Use of Cython's type templates for type specialization | ||
###################################################################### | ||
|
||
# Only integer values are allowed. | ||
ctypedef fused data_t: | ||
np.int8_t | ||
np.int16_t | ||
np.int32_t | ||
np.int64_t | ||
np.uint8_t | ||
np.uint16_t | ||
np.uint32_t | ||
np.uint64_t | ||
|
||
##################################################################### | ||
# Function Specializers and asociate function for using fused type | ||
##################################################################### | ||
|
||
ctypedef void (*func_p)(void *data, np.flatiter iti, np.intp_t max_label, | ||
int *regions, int rank) nogil | ||
|
||
def get_funcs(np.ndarray[data_t] input): | ||
return (<Py_intptr_t> findObjectsPoint[data_t]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the casting here? You are converting a function pointer to an integer type? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaimefrio That is required. Removing it from line 53 gives error while compiling the code and, in line 103 removing it gives segmentation fault. |
||
|
||
###################################################################### | ||
# Update Regions According to Input Data Type | ||
###################################################################### | ||
|
||
cdef inline findObjectsPoint(data_t *data, np.flatiter _iti, np.intp_t max_label, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the right approach is to do:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaimefrio Compiler crashes if I declare the argument type as void * giving warning: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaimefrio ... cdef inline findObjectsPoint(void *data, np.flatiter _iti, np.intp_t max_label, int *regions, int rank, np.ndarray[data_t] input): ... compiles successfully but when I run it over test cases, compiler crashes. ` There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of |
||
int *regions, int rank): | ||
cdef int kk =0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing space after |
||
cdef np.intp_t cc | ||
|
||
# only integer or boolean values are allowed, since s_index is being used in indexing | ||
cdef np.intp_t s_index = <np.intp_t> (<data_t *> data)[0] - 1 | ||
|
||
if s_index >=0 and s_index < max_label: | ||
if rank > 0: | ||
s_index *= 2 * rank | ||
if regions[s_index] < 0: | ||
for kk in range(rank): | ||
cc = (<PyArrayIterObject *>_iti).coordinates[kk] | ||
regions[s_index + kk] = cc | ||
regions[s_index + kk + rank] = cc + 1 | ||
|
||
else: | ||
for kk in range(rank): | ||
cc = (<PyArrayIterObject *>_iti).coordinates[kk] | ||
if cc < regions[s_index + kk]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you make this condition |
||
regions[s_index + kk] = cc | ||
if cc + 1 > regions[s_index + kk + rank]: | ||
regions[s_index + kk + rank] = cc + 1 | ||
else: | ||
regions[s_index] = 1 | ||
|
||
return 1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Function is void, should have no return. |
||
|
||
###################################################################### | ||
# Implementaion of find_Objects function:- | ||
###################################################################### | ||
|
||
cpdef _findObjects(np.ndarray input_raw, np.intp_t max_label): | ||
cdef: | ||
funcs = get_funcs(input_raw.take([0])) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't you need to specify the type of funcs here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not:
If this works, you can get rid of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaimefrio No.It is not working. It gives error while compiling: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also in line 94 |
||
|
||
int ii, rank, size_regions | ||
int start, jj, idx, end | ||
int *regions = NULL | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most of these should be |
||
|
||
np.flatiter _iti | ||
PyArrayIterObject *iti | ||
|
||
func_p findObjectsPoint = <func_p> <void *> <Py_intptr_t> funcs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaimefrio This casting is required. Without it cython compiler gives error: Python objects can't be casted to void *. |
||
|
||
int flags = np.NPY_CONTIGUOUS | np.NPY_NOTSWAPPED | np.NPY_ALIGNED | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think requiring the array to be contiguous is too strict: if we are going to make it unconditionally contiguous, we can probably get rid of using the iterator altogether. |
||
|
||
input = np.PyArray_FROM_OF(input_raw, flags) | ||
|
||
rank = input.ndim | ||
|
||
# Array Iterator defining and Initialization: | ||
_iti = np.PyArray_IterNew(input) | ||
iti = <PyArrayIterObject *> _iti | ||
|
||
# If iterator is contiguous, PyArray_ITER_NEXT will treat it as 1D Array | ||
iti.contiguous = 0 | ||
|
||
# Declaring output array | ||
size_regions = 0 | ||
|
||
if max_label < 0: | ||
max_label = 0 | ||
elif max_label > 0: | ||
if rank > 0: | ||
size_regions = 2 * max_label * rank | ||
|
||
else: | ||
size_regions = max_label | ||
|
||
regions = <int *> PyDataMem_NEW(size_regions * sizeof(int)) | ||
if regions == NULL: | ||
raise MemoryError() | ||
|
||
else: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you need this branch? |
||
regions = NULL | ||
|
||
try: | ||
with nogil: | ||
if rank > 0: | ||
for jj in range(size_regions): | ||
regions[jj] = -1 | ||
|
||
# Iteration over array: | ||
while np.PyArray_ITER_NOTDONE(_iti): | ||
findObjectsPoint(np.PyArray_ITER_DATA(_iti), _iti, max_label, regions, rank) | ||
np.PyArray_ITER_NEXT(_iti) | ||
|
||
result = [] | ||
|
||
for ii in range(max_label): | ||
if rank > 0: | ||
idx = 2 * rank * ii | ||
|
||
else: | ||
idx = ii | ||
|
||
if regions[idx] >= 0: | ||
slc = () | ||
for jj in range(rank): | ||
start = regions[idx + jj] | ||
end = regions[idx + jj + rank] | ||
|
||
slc += (slice(start, end),) | ||
result.append(slc) | ||
|
||
else: | ||
result.append(None) | ||
except: | ||
# clean up and re-raise | ||
PyDataMem_FREE(regions) | ||
raise | ||
|
||
return result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't
input = input.view(np.uint8)
work too? It would save a copy of the array.