mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-29 01:11:56 +08:00
211 lines
6.5 KiB
Cython
211 lines
6.5 KiB
Cython
#cython: cdivision=True
|
|
#cython: boundscheck=False
|
|
#cython: nonecheck=False
|
|
#cython: wraparound=False
|
|
|
|
'''
|
|
Originally part of CellProfiler, code licensed under both GPL and BSD licenses.
|
|
Website: http://www.cellprofiler.org
|
|
|
|
Copyright (c) 2003-2009 Massachusetts Institute of Technology
|
|
Copyright (c) 2009-2011 Broad Institute
|
|
All rights reserved.
|
|
|
|
Original author: Lee Kamentsky
|
|
'''
|
|
|
|
import numpy as np
|
|
|
|
cimport numpy as cnp
|
|
|
|
|
|
def _skeletonize_loop(cnp.uint8_t[:, ::1] result,
|
|
Py_ssize_t[::1] i, Py_ssize_t[::1] j,
|
|
cnp.int32_t[::1] order, cnp.uint8_t[::1] table):
|
|
"""
|
|
Inner loop of skeletonize function
|
|
|
|
Parameters
|
|
----------
|
|
|
|
result : ndarray of uint8
|
|
On input, the image to be skeletonized, on output the skeletonized
|
|
image.
|
|
|
|
i, j : ndarrays
|
|
The coordinates of each foreground pixel in the image
|
|
|
|
order : ndarray
|
|
The index of each pixel, in the order of processing (order[0] is
|
|
the first pixel to process, etc.)
|
|
|
|
table : ndarray
|
|
The 512-element lookup table of values after transformation
|
|
(whether to keep or not each configuration in a binary 3x3 array)
|
|
|
|
Notes
|
|
-----
|
|
|
|
The loop determines whether each pixel in the image can be removed without
|
|
changing the Euler number of the image. The pixels are ordered by
|
|
increasing distance from the background which means a point nearer to
|
|
the quench-line of the brushfire will be evaluated later than a
|
|
point closer to the edge.
|
|
|
|
Note that the neighbourhood of a pixel may evolve before the loop
|
|
arrives at this pixel. This is why it is possible to compute the
|
|
skeleton in only one pass, thanks to an adapted ordering of the
|
|
pixels.
|
|
"""
|
|
cdef:
|
|
Py_ssize_t accumulator
|
|
Py_ssize_t index, order_index
|
|
Py_ssize_t ii, jj
|
|
Py_ssize_t rows = result.shape[0]
|
|
Py_ssize_t cols = result.shape[1]
|
|
|
|
for index in range(order.shape[0]):
|
|
accumulator = 16
|
|
order_index = order[index]
|
|
ii = i[order_index]
|
|
jj = j[order_index]
|
|
# Compute the configuration around the pixel
|
|
if ii > 0:
|
|
if jj > 0 and result[ii - 1, jj - 1]:
|
|
accumulator += 1
|
|
if result[ii - 1, jj]:
|
|
accumulator += 2
|
|
if jj < cols - 1 and result[ii - 1, jj + 1]:
|
|
accumulator += 4
|
|
if jj > 0 and result[ii, jj - 1]:
|
|
accumulator += 8
|
|
if jj < cols - 1 and result[ii, jj + 1]:
|
|
accumulator += 32
|
|
if ii < rows - 1:
|
|
if jj > 0 and result[ii + 1, jj - 1]:
|
|
accumulator += 64
|
|
if result[ii + 1, jj]:
|
|
accumulator += 128
|
|
if jj < cols - 1 and result[ii + 1, jj + 1]:
|
|
accumulator += 256
|
|
# Assign the value of table corresponding to the configuration
|
|
result[ii, jj] = table[accumulator]
|
|
|
|
|
|
|
|
def _table_lookup_index(cnp.uint8_t[:, ::1] image):
|
|
"""
|
|
Return an index into a table per pixel of a binary image
|
|
|
|
Take the sum of true neighborhood pixel values where the neighborhood
|
|
looks like this::
|
|
|
|
1 2 4
|
|
8 16 32
|
|
64 128 256
|
|
|
|
This code could be replaced by a convolution with the kernel::
|
|
|
|
256 128 64
|
|
32 16 8
|
|
4 2 1
|
|
|
|
but this runs about twice as fast because of inlining and the
|
|
hardwired kernel.
|
|
"""
|
|
cdef:
|
|
Py_ssize_t[:, ::1] indexer
|
|
Py_ssize_t *p_indexer
|
|
cnp.uint8_t *p_image
|
|
Py_ssize_t i_stride
|
|
Py_ssize_t i_shape
|
|
Py_ssize_t j_shape
|
|
Py_ssize_t i
|
|
Py_ssize_t j
|
|
Py_ssize_t offset
|
|
|
|
i_shape = image.shape[0]
|
|
j_shape = image.shape[1]
|
|
indexer = np.zeros((i_shape, j_shape), dtype=np.intp)
|
|
p_indexer = &indexer[0, 0]
|
|
p_image = &image[0, 0]
|
|
i_stride = image.strides[0]
|
|
assert i_shape >= 3 and j_shape >= 3, \
|
|
"Please use the slow method for arrays < 3x3"
|
|
|
|
for i in range(1, i_shape-1):
|
|
offset = i_stride* i + 1
|
|
for j in range(1, j_shape - 1):
|
|
if p_image[offset]:
|
|
p_indexer[offset + i_stride + 1] += 1
|
|
p_indexer[offset + i_stride] += 2
|
|
p_indexer[offset + i_stride - 1] += 4
|
|
p_indexer[offset + 1] += 8
|
|
p_indexer[offset] += 16
|
|
p_indexer[offset - 1] += 32
|
|
p_indexer[offset - i_stride + 1] += 64
|
|
p_indexer[offset - i_stride] += 128
|
|
p_indexer[offset - i_stride - 1] += 256
|
|
offset += 1
|
|
#
|
|
# Do the corner cases (literally)
|
|
#
|
|
if image[0, 0]:
|
|
indexer[0, 0] += 16
|
|
indexer[0, 1] += 8
|
|
indexer[1, 0] += 2
|
|
indexer[1, 1] += 1
|
|
|
|
if image[0, j_shape - 1]:
|
|
indexer[0, j_shape - 2] += 32
|
|
indexer[0, j_shape - 1] += 16
|
|
indexer[1, j_shape - 2] += 4
|
|
indexer[1, j_shape - 1] += 2
|
|
|
|
if image[i_shape - 1, 0]:
|
|
indexer[i_shape - 2, 0] += 128
|
|
indexer[i_shape - 2, 1] += 64
|
|
indexer[i_shape - 1, 0] += 16
|
|
indexer[i_shape - 1, 1] += 8
|
|
|
|
if image[i_shape - 1, j_shape - 1]:
|
|
indexer[i_shape - 2, j_shape - 2] += 256
|
|
indexer[i_shape - 2, j_shape - 1] += 128
|
|
indexer[i_shape - 1, j_shape - 2] += 32
|
|
indexer[i_shape - 1, j_shape - 1] += 16
|
|
#
|
|
# Do the edges
|
|
#
|
|
for j in range(1, j_shape - 1):
|
|
if image[0, j]:
|
|
indexer[0, j - 1] += 32
|
|
indexer[0, j] += 16
|
|
indexer[0, j + 1] += 8
|
|
indexer[1, j - 1] += 4
|
|
indexer[1, j] += 2
|
|
indexer[1, j + 1] += 1
|
|
if image[i_shape - 1, j]:
|
|
indexer[i_shape - 2, j - 1] += 256
|
|
indexer[i_shape - 2, j] += 128
|
|
indexer[i_shape - 2, j + 1] += 64
|
|
indexer[i_shape - 1, j - 1] += 32
|
|
indexer[i_shape - 1, j] += 16
|
|
indexer[i_shape - 1, j + 1] += 8
|
|
|
|
for i in range(1, i_shape - 1):
|
|
if image[i, 0]:
|
|
indexer[i - 1, 0] += 128
|
|
indexer[i, 0] += 16
|
|
indexer[i + 1, 0] += 2
|
|
indexer[i - 1, 1] += 64
|
|
indexer[i, 1] += 8
|
|
indexer[i + 1, 1] += 1
|
|
if image[i, j_shape - 1]:
|
|
indexer[i - 1, j_shape - 2] += 256
|
|
indexer[i, j_shape - 2] += 32
|
|
indexer[i + 1, j_shape - 2] += 4
|
|
indexer[i - 1, j_shape - 1] += 128
|
|
indexer[i, j_shape - 1] += 16
|
|
indexer[i + 1, j_shape - 1] += 2
|
|
return np.asarray(indexer)
|