Merge branch 'rank_filters' of https://github.com/odebeir/scikits-image into rank-filters

This commit is contained in:
Johannes Schönberger
2012-11-09 17:37:53 +01:00
32 changed files with 4608 additions and 4 deletions
+2 -1
View File
@@ -22,4 +22,5 @@ doc/source/auto_examples/images/plot_*.png
doc/source/auto_examples/images/thumb
doc/source/auto_examples/applications/
doc/source/_static/random.js
.idea/
*.log
+3
View File
@@ -117,3 +117,6 @@
- Petter Strandmark
Perimeter calculation in regionprops.
- Olivier Debeir
Rank filters (8- and 16-bits) using sliding window.
View File
@@ -0,0 +1,585 @@
"""
===============================================================
Rank filters
===============================================================
Rank filters are non-linear filters using the local grey levels ordering to compute the filtered value.
This ensemble of filters share a common base: the local grey-level histogram extraction computed on
the neighborhood of a pixel (defined by a 2D structuring element).
If the filtered value is taken as the middle value of the histogram, we get the classical median filter.
Rank filters can be used for several purposes such as:
* image quality enhancement
e.g. image smoothing, sharpening
* image pre-processing
e.g. noise reduction, contrast enhancement
* feature extraction
e.g. border detection, isolated point detection
* post-processing
e.g. small object removal, object grouping, contour smoothing
Some well known filters are specific cases of rank filters [1]_ e.g. morphological dilation, morphological erosion,
median filters.
The different implementation availables in ``skimage`` are compared.
In this example, we will see how to filter a grey level image using some of the linear and non-linear filters
availables in skimage. We use the ``camera`` image from ``skimage.data``.
.. [1] Pierre Soille, On morphological operators based on rank filters, Pattern Recognition 35 (2002) 527-535.
"""
import numpy as np
import matplotlib.pyplot as plt
from skimage import data
ima = data.camera()
hist = np.histogram(ima, bins=np.arange(0, 256))
plt.figure(figsize=(8, 3))
plt.subplot(121)
plt.imshow(ima, cmap=plt.cm.gray, interpolation='nearest')
plt.axis('off')
plt.subplot(122)
plt.plot(hist[1][:-1], hist[0], lw=2)
plt.title('histogram of grey values')
"""
.. image:: PLOT2RST.current_figure
Noise removal
==============
some noise is added to the image, 1% of pixels are randomly set to 255, %1% are randomly set to 0.
The **median** filter is applied to remove the noise.
.. note:: there is different implementations of median filter : ``skimage.filter.median_filter``and
`skimage.filter.rank.median``
"""
noise = np.random.random(ima.shape)
nima = data.camera()
nima[noise>.99] = 255
nima[noise<.01] = 0
from skimage.filter.rank import median
from skimage.morphology import disk
fig = plt.figure(figsize=[10,7])
lo = median(nima,disk(1))
hi = median(nima,disk(5))
ext = median(nima,disk(20))
plt.subplot(2,2,1)
plt.imshow(nima,cmap=plt.cm.gray,vmin=0,vmax=255)
plt.xlabel('noised image')
plt.subplot(2,2,2)
plt.imshow(lo,cmap=plt.cm.gray,vmin=0,vmax=255)
plt.xlabel('median $r=1$')
plt.subplot(2,2,3)
plt.imshow(hi,cmap=plt.cm.gray,vmin=0,vmax=255)
plt.xlabel('median $r=5$')
plt.subplot(2,2,4)
plt.imshow(ext,cmap=plt.cm.gray,vmin=0,vmax=255)
plt.xlabel('median $r=20$')
"""
.. image:: PLOT2RST.current_figure
The added noise is efficiently removed, as the image defaults are small (1 pixel wide), a small filter radius is
sufficient. As the radius is increasing, objects with a bigger size are filtered too such as the camera tripod.
Median filter is commonly used for noise removal because borders are preserved.
Image smoothing
================
The example hereunder shows how a local **mean** smooth the cameraman image.
"""
from skimage.filter.rank import mean
fig = plt.figure(figsize=[10,7])
loc_mean = mean(nima,disk(10))
plt.subplot(1,2,1)
plt.imshow(ima,cmap=plt.cm.gray,vmin=0,vmax=255)
plt.xlabel('original')
plt.subplot(1,2,2)
plt.imshow(loc_mean,cmap=plt.cm.gray,vmin=0,vmax=255)
plt.xlabel('local mean $r=10$')
"""
.. image:: PLOT2RST.current_figure
One may be interested in smoothing an image while preserving important borders (median filters already achieved this),
here we use the **bilateral** filter that restrict the local neighborhood to pixel having a grey level similar to the
central one.
.. note:: a different implementation is available for color images in ``skimage.filter.denoise_bilateral``.
"""
from skimage.filter.rank import bilateral_mean
ima = data.camera()
selem = disk(10)
bilat = bilateral_mean(ima.astype(np.uint16),disk(20),s0=10,s1=10)
# display results
fig = plt.figure(figsize=[10,7])
plt.subplot(2,2,1)
plt.imshow(ima,cmap=plt.cm.gray)
plt.xlabel('original')
plt.subplot(2,2,3)
plt.imshow(bilat,cmap=plt.cm.gray)
plt.xlabel('bilateral mean')
plt.subplot(2,2,2)
plt.imshow(ima[200:350,350:450],cmap=plt.cm.gray)
plt.subplot(2,2,4)
plt.imshow(bilat[200:350,350:450],cmap=plt.cm.gray)
"""
.. image:: PLOT2RST.current_figure
One can see that the large continuous part of the image (e.g.sky) are smoothed whereas other details are preserved.
Contrast enhancement
====================
We compare here how the global histogram equalization is applied locally.
The equalized image [2]_ has a roughly linear cumulative distribution function for each pixel neighborhood.
The local version [3]_ of the histogram equalization emphasized every local graylevel variations.
.. [2] http://en.wikipedia.org/wiki/Histogram_equalization
.. [3] http://en.wikipedia.org/wiki/Adaptive_histogram_equalization
"""
from skimage import exposure
from skimage.filter import rank
ima = data.camera()
# equalize globally and locally
glob = exposure.equalize(ima)*255
loc = rank.equalize(ima,disk(20))
# extract histogram for each image
hist = np.histogram(ima, bins=np.arange(0, 256))
glob_hist = np.histogram(glob, bins=np.arange(0, 256))
loc_hist = np.histogram(loc, bins=np.arange(0, 256))
plt.figure(figsize=(10, 10))
plt.subplot(321)
plt.imshow(ima, cmap=plt.cm.gray, interpolation='nearest')
plt.axis('off')
plt.subplot(322)
plt.plot(hist[1][:-1], hist[0], lw=2)
plt.title('histogram of grey values')
plt.subplot(323)
plt.imshow(glob, cmap=plt.cm.gray, interpolation='nearest')
plt.axis('off')
plt.subplot(324)
plt.plot(glob_hist[1][:-1], glob_hist[0], lw=2)
plt.title('histogram of grey values')
plt.subplot(325)
plt.imshow(loc, cmap=plt.cm.gray, interpolation='nearest')
plt.axis('off')
plt.subplot(326)
plt.plot(loc_hist[1][:-1], loc_hist[0], lw=2)
plt.title('histogram of grey values')
"""
.. image:: PLOT2RST.current_figure
an other way to maximize the number of grey level used for an image is to apply a local autoleveling,
i.e. here a pixel grey level is proportionally remapped between local minimum and local maximum.
The following example show how local autolevel enhance the camaraman picture.
"""
from skimage.filter.rank import autolevel
ima = data.camera()
selem = disk(10)
auto = autolevel(ima.astype(np.uint16),disk(20))
# display results
fig = plt.figure(figsize=[10,7])
plt.subplot(1,2,1)
plt.imshow(ima,cmap=plt.cm.gray)
plt.xlabel('original')
plt.subplot(1,2,2)
plt.imshow(auto,cmap=plt.cm.gray)
plt.xlabel('local autolevel')
"""
.. image:: PLOT2RST.current_figure
This filter is very sensitive to local outlayers, see the little white spot in the sky left part. This is due
to a local maximum which is very high comparing to the rest of the neighborhood. One can moderate this
using the percentile version of the autolevel filter which uses to given percentiles (one inferior, one superior)
in place of local minimum and maximum. The example bellow illustrate how the percentile parameters influence the
local autolevel result.
"""
from skimage.filter.rank import percentile_autolevel
image = data.camera()
selem = disk(20)
loc_autolevel = autolevel(image,selem=selem)
loc_perc_autolevel0 = percentile_autolevel(image,selem=selem,p0=.00,p1=1.0)
loc_perc_autolevel1 = percentile_autolevel(image,selem=selem,p0=.01,p1=.99)
loc_perc_autolevel2 = percentile_autolevel(image,selem=selem,p0=.05,p1=.95)
loc_perc_autolevel3 = percentile_autolevel(image,selem=selem,p0=.1,p1=.9)
fig, axes = plt.subplots(nrows=3, figsize=(7, 8))
ax0, ax1, ax2 = axes
plt.gray()
ax0.imshow(np.hstack((image,loc_autolevel)))
ax0.set_title('original / autolevel')
ax1.imshow(np.hstack((loc_perc_autolevel0,loc_perc_autolevel1)),vmin=0,vmax=255)
ax1.set_title('percentile autolevel 0%,1%')
ax2.imshow(np.hstack((loc_perc_autolevel2,loc_perc_autolevel3)),vmin=0,vmax=255)
ax2.set_title('percentile autolevel 5% and 10%')
for ax in axes:
ax.axis('off')
"""
.. image:: PLOT2RST.current_figure
Morphological contrast enhancement filter replaces the central pixel by local maximum
if the original grey level value if closest to local maximum, by the minimum local otherwise.
"""
from skimage.filter.rank import morph_contr_enh
ima = data.camera()
enh = morph_contr_enh(ima,disk(5))
# display results
fig = plt.figure(figsize=[10,7])
plt.subplot(2,2,1)
plt.imshow(ima,cmap=plt.cm.gray)
plt.xlabel('original')
plt.subplot(2,2,3)
plt.imshow(enh,cmap=plt.cm.gray)
plt.xlabel('local morphlogical contrast enhancement')
plt.subplot(2,2,2)
plt.imshow(ima[200:350,350:450],cmap=plt.cm.gray)
plt.subplot(2,2,4)
plt.imshow(enh[200:350,350:450],cmap=plt.cm.gray)
"""
.. image:: PLOT2RST.current_figure
The percentile version of the local morphological contrast enhancement, uses percentile *p0* and *p1* instead of local
minimum and local maximum.
"""
from skimage.filter.rank import percentile_morph_contr_enh
ima = data.camera()
penh = percentile_morph_contr_enh(ima,disk(5),p0=.1,p1=.9)
# display results
fig = plt.figure(figsize=[10,7])
plt.subplot(2,2,1)
plt.imshow(ima,cmap=plt.cm.gray)
plt.xlabel('original')
plt.subplot(2,2,3)
plt.imshow(penh,cmap=plt.cm.gray)
plt.xlabel('local percentile morphlogical\n contrast enhancement')
plt.subplot(2,2,2)
plt.imshow(ima[200:350,350:450],cmap=plt.cm.gray)
plt.subplot(2,2,4)
plt.imshow(penh[200:350,350:450],cmap=plt.cm.gray)
"""
.. image:: PLOT2RST.current_figure
Image morphology
================
Local maximum and local minimum are the base operators for grey level morphology.
.. note:: ``skimage.dilate`` and ``skimage.erode`` are equivalent filters (see below for comparison).
Here is an example of classical morphological grey level filters : opening, closing and morphological gradient.
"""
from skimage.filter.rank import maximum,minimum,gradient
ima = data.camera()
closing = maximum(minimum(ima,disk(5)),disk(5))
opening = minimum(maximum(ima,disk(5)),disk(5))
grad = gradient(ima,disk(5))
# display results
fig = plt.figure(figsize=[10,7])
plt.subplot(2,2,1)
plt.imshow(ima,cmap=plt.cm.gray)
plt.xlabel('original')
plt.subplot(2,2,2)
plt.imshow(closing,cmap=plt.cm.gray)
plt.xlabel('grey level closing')
plt.subplot(2,2,3)
plt.imshow(opening,cmap=plt.cm.gray)
plt.xlabel('grey level opening')
plt.subplot(2,2,4)
plt.imshow(grad,cmap=plt.cm.gray)
plt.xlabel('morphological gradient')
"""
.. image:: PLOT2RST.current_figure
Feature extraction
===================
Local histogram can be exploited to compute local entropy, which is related to the local image complexity.
Entropy is computed using base 2 logarithm i.e. the filter returns the minimum number of bits needed to encode local
greylevel distribution.
``skimage.rank.entropy`` returns local entropy on a given structuring element.
The following example shows this filter applied on 8- and 16- bit images.
.. note:: to better use the available image bit, the function returns 10x entropy for 8-bit images and 1000x entropy
for 16-bit images.
"""
from skimage import data
from skimage.filter.rank import entropy
from skimage.morphology import disk
import numpy as np
import matplotlib.pyplot as plt
# defining a 8- and a 16-bit test images
a8 = data.camera()
a16 = data.camera().astype(np.uint16)*4
ent8 = entropy(a8,disk(5)) # pixel value contain 10x the local entropy
ent16 = entropy(a16,disk(5)) # pixel value contain 1000x the local entropy
# display results
plt.figure(figsize=(10, 10))
plt.subplot(2,2,1)
plt.imshow(a8, cmap=plt.cm.gray)
plt.xlabel('8-bit image')
plt.colorbar()
plt.subplot(2,2,2)
plt.imshow(ent8, cmap=plt.cm.jet)
plt.xlabel('entropy*10')
plt.colorbar()
plt.subplot(2,2,3)
plt.imshow(a16, cmap=plt.cm.gray)
plt.xlabel('16-bit image')
plt.colorbar()
plt.subplot(2,2,4)
plt.imshow(ent16, cmap=plt.cm.jet)
plt.xlabel('entropy*1000')
plt.colorbar()
plt.show()
"""
.. image:: PLOT2RST.current_figure
Implementation
================
The central part of the ``skimage.rank`` filters is build on a sliding window that update local grey level histogram.
This approach limits the algorithm complexity to O(n) where n is the number of image pixels. The complexity is also
limited with respect to the structuring element size.
"""
from time import time
from scipy.ndimage.filters import percentile_filter
from skimage.morphology import dilation
from skimage.filter import median_filter
from skimage.filter.rank import median,maximum
def exec_and_timeit(func):
""" Decorator that returns both function results and execution time
(result, ms)
"""
def wrapper(*arg):
t1 = time()
res = func(*arg)
t2 = time()
ms = (t2-t1)*1000.0
return (res,ms)
return wrapper
@exec_and_timeit
def cr_med(image,selem):
return median(image=image,selem = selem)
@exec_and_timeit
def cr_max(image,selem):
return maximum(image=image,selem = selem)
@exec_and_timeit
def cm_dil(image,selem):
return dilation(image=image,selem = selem)
@exec_and_timeit
def ctmf_med(image,radius):
return median_filter(image=image,radius=radius)
@exec_and_timeit
def ndi_med(image,n):
return percentile_filter(image,50,size=n*2-1)
"""
Comparison between
* ``rank.maximum``
* ``cmorph.dilate``
on increasing structuring element size
"""
a = data.camera()
rec = []
e_range = range(1,20,2)
for r in e_range:
elem = disk(r+1)
rc,ms_rc = cr_max(a,elem)
rcm,ms_rcm = cm_dil(a,elem)
rec.append((ms_rc,ms_rcm))
rec = np.asarray(rec)
plt.figure()
plt.title('increasing element size')
plt.ylabel('time (ms)')
plt.xlabel('element radius')
plt.plot(e_range,rec)
plt.legend(['crank.maximum','cmorph.dilate'])
"""
and increasing image size
.. image:: PLOT2RST.current_figure
"""
r = 9
elem = disk(r+1)
rec = []
s_range = range(100,1000,100)
for s in s_range:
a = (np.random.random((s,s))*256).astype('uint8')
(rc,ms_rc) = cr_max(a,elem)
(rcm,ms_rcm) = cm_dil(a,elem)
rec.append((ms_rc,ms_rcm))
rec = np.asarray(rec)
plt.figure()
plt.title('increasing image size')
plt.ylabel('time (ms)')
plt.xlabel('image size')
plt.plot(s_range,rec)
plt.legend(['crank.maximum','cmorph.dilate'])
"""
.. image:: PLOT2RST.current_figure
Comparison between:
* ``rank.median``
* ``ctmf.median_filter``
* ``ndimage.percentile``
on increasing structuring element size
"""
a = data.camera()
rec = []
e_range = range(2,30,4)
for r in e_range:
elem = disk(r+1)
rc,ms_rc = cr_med(a,elem)
rctmf,ms_rctmf = ctmf_med(a,r)
rndi,ms_ndi = ndi_med(a,r)
rec.append((ms_rc,ms_rctmf,ms_ndi))
rec = np.asarray(rec)
plt.figure()
plt.title('increasing element size')
plt.plot(e_range,rec)
plt.legend(['rank.median','ctmf.median_filter','ndimage.percentile'])
plt.ylabel('time (ms)')
plt.xlabel('element radius')
"""
.. image:: PLOT2RST.current_figure
comparison of outcome of the three methods
"""
plt.figure()
plt.imshow(np.hstack((rc,rctmf,rndi)))
plt.xlabel('rank.median vs ctmf.median_filter vs ndimage.percentile')
"""
.. image:: PLOT2RST.current_figure
and increasing image size
"""
r = 9
elem = disk(r+1)
rec = []
s_range = [100,200,500,1000]
for s in s_range:
a = (np.random.random((s,s))*256).astype('uint8')
(rc,ms_rc) = cr_med(a,elem)
rctmf,ms_rctmf = ctmf_med(a,r)
rndi,ms_ndi = ndi_med(a,r)
rec.append((ms_rc,ms_rctmf,ms_ndi))
rec = np.asarray(rec)
plt.figure()
plt.title('increasing image size')
plt.plot(s_range,rec)
plt.legend(['rank.median','ctmf.median_filter','ndimage.percentile'])
plt.ylabel('time (ms)')
plt.xlabel('image size')
"""
.. image:: PLOT2RST.current_figure
"""
plt.show()
+47
View File
@@ -0,0 +1,47 @@
"""
==============================
Bilateral mean
==============================
This example compares
* local mean
* percentile mean
* bilateral mean
build on the local histogram distribution
local mean uses all pixels belonging to the structuring element to compute average gray level,
percentile mean uses only values between percentiles p0 and p1 (here 10% and 90%),
whereas bilateral mean uses only pixels of the structuring element having a gray level situated inside
g-s0 and g+s1 (here g-500 and g+500).
The filters are applied on a 16 bit image (actual bitdepth is 12bit).
Percentile and usual mean give here similar results, these filters smooth the complete image (background and details).
Bilateral mean exhibits a high filtering rate for continuous area (i.e. background) while image higher frequencies
remains untouched.
"""
import numpy as np
import matplotlib.pyplot as plt
from skimage import data
from skimage.morphology import disk
import skimage.filter.rank as rank
a16 = (data.coins()).astype('uint16')*16
selem = disk(20)
f1 = rank.percentile_mean(a16,selem = selem,p0=.1,p1=.9)
f2 = rank.bilateral_mean(a16,selem = selem,s0=500,s1=500)
f3 = rank.mean(a16,selem = selem)
# display results
fig, axes = plt.subplots(nrows=3, figsize=(15,10))
ax0, ax1, ax2 = axes
ax0.imshow(np.hstack((a16,f1)))
ax0.set_title('percentile mean')
ax1.imshow(np.hstack((a16,f2)))
ax1.set_title('bilateral mean')
ax2.imshow(np.hstack((a16,f3)))
ax2.set_title('local mean')
plt.show()
+44
View File
@@ -0,0 +1,44 @@
"""
===================
Entropy
===================
"""
from skimage import data
from skimage.filter.rank import entropy
from skimage.morphology import disk
import numpy as np
import matplotlib.pyplot as plt
# defining a 8- and a 16-bit test images
a8 = data.camera()
a16 = data.camera().astype(np.uint16)*4
ent8 = entropy(a8,disk(5)) # pixel value contain 10x the local entropy
ent16 = entropy(a16,disk(5)) # pixel value contain 1000x the local entropy
# display results
plt.figure(figsize=(10, 10))
plt.subplot(2,2,1)
plt.imshow(a8, cmap=plt.cm.gray)
plt.xlabel('8-bit image')
plt.colorbar()
plt.subplot(2,2,2)
plt.imshow(ent8, cmap=plt.cm.jet)
plt.xlabel('entropy*10')
plt.colorbar()
plt.subplot(2,2,3)
plt.imshow(a16, cmap=plt.cm.gray)
plt.xlabel('16-bit image')
plt.colorbar()
plt.subplot(2,2,4)
plt.imshow(ent16, cmap=plt.cm.jet)
plt.xlabel('entropy*1000')
plt.colorbar()
plt.show()
+84
View File
@@ -0,0 +1,84 @@
"""
===============================
Local Histogram Equalization
===============================
This examples enhances an image with low contrast, using a method called
*local histogram equalization*, which "spreads out the most frequent intensity
values" in an image .
The equalized image [1]_ has a roughly linear cumulative distribution function for each pixel neighborhood.
The local version [2]_ of the histogram equalization emphasized every local graylevel variations.
.. [1] http://en.wikipedia.org/wiki/Histogram_equalization
.. [2] http://en.wikipedia.org/wiki/Adaptive_histogram_equalization
"""
from skimage import data
from skimage.util.dtype import dtype_range
from skimage import exposure
from skimage.morphology import disk
import matplotlib.pyplot as plt
import numpy as np
from skimage.filter import rank
def plot_img_and_hist(img, axes, bins=256):
"""Plot an image along with its histogram and cumulative histogram.
"""
ax_img, ax_hist = axes
ax_cdf = ax_hist.twinx()
# Display image
ax_img.imshow(img, cmap=plt.cm.gray)
ax_img.set_axis_off()
# Display histogram
ax_hist.hist(img.ravel(), bins=bins)
ax_hist.ticklabel_format(axis='y', style='scientific', scilimits=(0, 0))
ax_hist.set_xlabel('Pixel intensity')
xmin, xmax = dtype_range[img.dtype.type]
ax_hist.set_xlim(xmin, xmax)
# Display cumulative distribution
img_cdf, bins = exposure.cumulative_distribution(img, bins)
ax_cdf.plot(bins, img_cdf, 'r')
return ax_img, ax_hist, ax_cdf
# Load an example image
img = data.moon()
# Contrast stretching
p2 = np.percentile(img, 2)
p98 = np.percentile(img, 98)
img_rescale = exposure.equalize(img)
# Equalization
selem = disk(30)
img_eq = rank.equalize(img,selem=selem)
# Display results
f, axes = plt.subplots(2, 3, figsize=(8, 4))
ax_img, ax_hist, ax_cdf = plot_img_and_hist(img, axes[:, 0])
ax_img.set_title('Low contrast image')
ax_hist.set_ylabel('Number of pixels')
ax_img, ax_hist, ax_cdf = plot_img_and_hist(img_rescale, axes[:, 1])
ax_img.set_title('Global equalise')
ax_img, ax_hist, ax_cdf = plot_img_and_hist(img_eq, axes[:, 2])
ax_img.set_title('Local equalize')
ax_cdf.set_ylabel('Fraction of total intensity')
# prevent overlap of y-axis labels
plt.subplots_adjust(wspace=0.4)
plt.show()
+54
View File
@@ -0,0 +1,54 @@
"""
================================
Markers for watershed transform
================================
The watershed is a classical algorithm used for **segmentation**, that
is, for separating different objects in an image.
Here a marker image is build from the region of low gradient inside the image.
See Wikipedia_ for more details on the algorithm.
.. _Wikipedia: http://en.wikipedia.org/wiki/Watershed_(image_processing)
"""
from scipy import ndimage
import matplotlib.pyplot as plt
from skimage.morphology import watershed,disk
from skimage import data
# original data
from skimage.filter import rank
image = data.camera()
# denoise image
denoised = rank.median(image,disk(2))
# find continuous region (low gradient) --> markers
markers = rank.gradient(denoised,disk(5))<10
markers = ndimage.label(markers)[0]
#local gradient
gradient = rank.gradient(denoised,disk(2))
# process the watershed
labels = watershed(gradient, markers)
# display results
fig, axes = plt.subplots(ncols=4, figsize=(8, 2.7))
ax0, ax1, ax2, ax3 = axes
ax0.imshow(image, cmap=plt.cm.gray, interpolation='nearest')
ax1.imshow(gradient, cmap=plt.cm.spectral, interpolation='nearest')
ax2.imshow(markers, cmap=plt.cm.spectral, interpolation='nearest')
ax3.imshow(image, cmap=plt.cm.gray, interpolation='nearest')
ax3.imshow(labels, cmap=plt.cm.spectral, interpolation='nearest',alpha=.7)
for ax in axes:
ax.axis('off')
plt.subplots_adjust(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0, right=1)
plt.show()
+1 -1
View File
@@ -26,7 +26,7 @@ See Wikipedia_ for more details on the algorithm.
"""
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt
from skimage.morphology import watershed, is_local_maximum
+35
View File
@@ -0,0 +1,35 @@
To use this to build your Cython file use the commandline options:
.. sourcecode:: text
$ python setup.py build_ext --inplace
**To do**
* add simple examples, adapt documentation on existing examples
* add/check existing doc
* adapting tests for each type of filter
**General remarks**
Basically these filters compute local histogram for each pixel. Histogram is build using a moving window in
order to limit redundant computation. The path followed by the moving window is given hereunder
...-----------------------\
/--------------------------/
\-------------------------- ...
A comparison is proposed with cmorph.dilate algorithm to show how computation costs evolve with respect to image size or
structuring element size. This implementation gives better results for large structuring elements.
A local histogram is update at each pixel by introducing pixel entering the structuring element border and
by removing those leaving it. The histogram size is 8bit (256 bins) for 8 bit images and 2 to 12 bit (up to 4096 bins)
for 16bit image depending on the image maximum value. Image with pixels higher than 4095 raise a ValueError.
The filter is applied up to the image border, the neighboorhood used is adjusted accordingly. The user may provide
a mask image (same size as input image) where non zero value are the part of the image participating the the
histogram computation. By default all the image is filtered.
+3
View File
@@ -0,0 +1,3 @@
from .rank import *
from .percentile_rank import *
from .bilateral_rank import *
+18
View File
@@ -0,0 +1,18 @@
cimport numpy as np
#---------------------------------------------------------------------------
# 16 bit core kernel receives extra information about data bitdepth
#---------------------------------------------------------------------------
# generic cdef functions
cdef int int_max(int a, int b)
cdef int int_min(int a, int b)
cdef _core16(
np.uint16_t kernel(Py_ssize_t * , float, np.uint16_t, Py_ssize_t, Py_ssize_t, Py_ssize_t, float, float, Py_ssize_t, Py_ssize_t),
np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask,
np.ndarray[np.uint16_t, ndim=2] out,
char shift_x, char shift_y, Py_ssize_t bitdepth,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1)
+283
View File
@@ -0,0 +1,283 @@
#cython: cdivision=True
#cython: boundscheck=False
#cython: nonecheck=False
#cython: wraparound=False
import numpy as np
cimport numpy as np
from libc.stdlib cimport malloc, free
#---------------------------------------------------------------------------
# 16 bit core kernel receives extra information about data bitdepth
#---------------------------------------------------------------------------
# generic cdef functions
cdef inline int int_max(int a, int b):
return a if a >= b else b
cdef inline int int_min(int a, int b):
return a if a <= b else b
cdef inline void histogram_increment(Py_ssize_t * histo, float * pop, np.uint16_t value):
histo[value] += 1
pop[0] += 1.
cdef inline void histogram_decrement(Py_ssize_t * histo, float * pop, np.uint16_t value):
histo[value] -= 1
pop[0] -= 1.
cdef inline np.uint8_t is_in_mask(Py_ssize_t rows, Py_ssize_t cols, Py_ssize_t r, Py_ssize_t c, np.uint8_t * mask):
""" returns 1 if given(r,c) coordinate are within the image frame ([0-rows],[0-cols]) and
inside the given mask
returns 0 otherwise
"""
if r < 0 or r > rows - 1 or c < 0 or c > cols - 1:
return 0
else:
if mask[r * cols + c]:
return 1
else:
return 0
cdef inline _core16(
np.uint16_t kernel(Py_ssize_t *, float, np.uint16_t, Py_ssize_t, Py_ssize_t, Py_ssize_t, float, float, Py_ssize_t, Py_ssize_t),
np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask,
np.ndarray[np.uint16_t, ndim=2] out,
char shift_x, char shift_y, Py_ssize_t bitdepth,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
""" Main loop, this function computes the histogram for each image point
- data is uint8
- result is uint8 casted
"""
cdef Py_ssize_t rows = image.shape[0]
cdef Py_ssize_t cols = image.shape[1]
cdef Py_ssize_t srows = selem.shape[0]
cdef Py_ssize_t scols = selem.shape[1]
cdef Py_ssize_t centre_r = int(selem.shape[0] / 2) + shift_y
cdef Py_ssize_t centre_c = int(selem.shape[1] / 2) + shift_x
# check that structuring element center is inside the element bounding box
assert centre_r >= 0
assert centre_c >= 0
assert centre_r < srows
assert centre_c < scols
assert bitdepth in range(2, 13)
maxbin_list = [0, 0, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096]
midbin_list = [0, 0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048]
#set maxbin and midbin
cdef Py_ssize_t maxbin = maxbin_list[bitdepth], midbin = midbin_list[bitdepth]
assert (image < maxbin).all()
image = np.ascontiguousarray(image)
if mask is None:
mask = np.ones((rows, cols), dtype=np.uint8)
else:
mask = np.ascontiguousarray(mask)
if image is out:
raise NotImplementedError("Cannot perform rank operation in place.")
if out is None:
out = np.zeros((rows, cols), dtype=np.uint16)
else:
out = np.ascontiguousarray(out)
mask = np.ascontiguousarray(mask)
# define pointers to the data
cdef np.uint16_t * out_data = <np.uint16_t * >out.data
cdef np.uint16_t * image_data = <np.uint16_t * >image.data
cdef np.uint8_t * mask_data = <np.uint8_t * >mask.data
# define local variable types
cdef Py_ssize_t r, c, rr, cc, s, value, local_max, i, even_row
cdef float pop # number of pixels actually inside the neighborhood (float)
# allocate memory with malloc
cdef Py_ssize_t max_se = srows * scols
# number of element in each attack border
cdef Py_ssize_t num_se_n, num_se_s, num_se_e, num_se_w
# the current local histogram distribution
cdef Py_ssize_t * histo = <Py_ssize_t * >malloc(maxbin * sizeof(Py_ssize_t))
# these lists contain the relative pixel row and column for each of the 4 attack borders
# east, west, north and south
# e.g. se_e_r lists the rows of the east structuring element border
cdef Py_ssize_t * se_e_r = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_e_c = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_w_r = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_w_c = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_n_r = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_n_c = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_s_r = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_s_c = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
# build attack and release borders
# by using difference along axis
t = np.hstack((selem, np.zeros((selem.shape[0], 1))))
t_e = np.diff(t, axis=1) == -1
t = np.hstack((np.zeros((selem.shape[0], 1)), selem))
t_w = np.diff(t, axis=1) == 1
t = np.vstack((selem, np.zeros((1, selem.shape[1]))))
t_s = np.diff(t, axis=0) == -1
t = np.vstack((np.zeros((1, selem.shape[1])), selem))
t_n = np.diff(t, axis=0) == 1
num_se_n = num_se_s = num_se_e = num_se_w = 0
for r in range(srows):
for c in range(scols):
if t_e[r, c]:
se_e_r[num_se_e] = r - centre_r
se_e_c[num_se_e] = c - centre_c
num_se_e += 1
if t_w[r, c]:
se_w_r[num_se_w] = r - centre_r
se_w_c[num_se_w] = c - centre_c
num_se_w += 1
if t_n[r, c]:
se_n_r[num_se_n] = r - centre_r
se_n_c[num_se_n] = c - centre_c
num_se_n += 1
if t_s[r, c]:
se_s_r[num_se_s] = r - centre_r
se_s_c[num_se_s] = c - centre_c
num_se_s += 1
# initial population and histogram
for i in range(maxbin):
histo[i] = 0
pop = 0
for r in range(srows):
for c in range(scols):
rr = r - centre_r
cc = c - centre_c
if selem[r, c]:
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_increment(histo, & pop, image_data[rr * cols + cc])
r = 0
c = 0
# kernel -------------------------------------------
out_data[r * cols + c] = kernel(histo, pop, image_data[r * cols + c],
bitdepth, maxbin, midbin, p0, p1, s0, s1)
# kernel -------------------------------------------
# main loop
r = 0
for even_row in range(0, rows, 2):
# ---> west to east
for c in range(1, cols):
for s in range(num_se_e):
rr = r + se_e_r[s]
cc = c + se_e_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_increment(histo, & pop, image_data[rr * cols + cc])
for s in range(num_se_w):
rr = r + se_w_r[s]
cc = c + se_w_c[s] - 1
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_decrement(histo, & pop, image_data[rr * cols + cc])
# kernel -------------------------------------------
out_data[r * cols + c] = kernel(
histo, pop, image_data[r * cols + c],
bitdepth, maxbin, midbin, p0, p1, s0, s1)
# kernel -------------------------------------------
r += 1 # pass to the next row
if r >= rows:
break
# ---> north to south
for s in range(num_se_s):
rr = r + se_s_r[s]
cc = c + se_s_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_increment(histo, & pop, image_data[rr * cols + cc])
for s in range(num_se_n):
rr = r + se_n_r[s] - 1
cc = c + se_n_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_decrement(histo, & pop, image_data[rr * cols + cc])
# kernel -------------------------------------------
out_data[r * cols + c] = kernel(histo, pop, image_data[r * cols + c],
bitdepth, maxbin, midbin, p0, p1, s0, s1)
# kernel -------------------------------------------
# ---> east to west
for c in range(cols - 2, -1, -1):
for s in range(num_se_w):
rr = r + se_w_r[s]
cc = c + se_w_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_increment(histo, & pop, image_data[rr * cols + cc])
for s in range(num_se_e):
rr = r + se_e_r[s]
cc = c + se_e_c[s] + 1
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_decrement(histo, & pop, image_data[rr * cols + cc])
# kernel -------------------------------------------
out_data[r * cols + c] = kernel(
histo, pop, image_data[r * cols + c],
bitdepth, maxbin, midbin, p0, p1, s0, s1)
# kernel -------------------------------------------
r += 1 # pass to the next row
if r >= rows:
break
# ---> north to south
for s in range(num_se_s):
rr = r + se_s_r[s]
cc = c + se_s_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_increment(histo, & pop, image_data[rr * cols + cc])
for s in range(num_se_n):
rr = r + se_n_r[s] - 1
cc = c + se_n_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_decrement(histo, & pop, image_data[rr * cols + cc])
# kernel -------------------------------------------
out_data[r * cols + c] = kernel(histo, pop, image_data[r * cols + c],
bitdepth, maxbin, midbin, p0, p1, s0, s1)
# kernel -------------------------------------------
# release memory allocated by malloc
free(se_e_r)
free(se_e_c)
free(se_w_r)
free(se_w_c)
free(se_n_r)
free(se_n_c)
free(se_s_r)
free(se_s_c)
free(histo)
return out
+17
View File
@@ -0,0 +1,17 @@
cimport numpy as np
# generic cdef functions
cdef np.uint8_t uint8_max(np.uint8_t a, np.uint8_t b)
cdef np.uint8_t uint8_min(np.uint8_t a, np.uint8_t b)
#---------------------------------------------------------------------------
# 8 bit core kernel receives extra information about data inferior and superior percentiles
#---------------------------------------------------------------------------
cdef _core8(
np.uint8_t kernel(Py_ssize_t *, float, np.uint8_t, float, float, Py_ssize_t, Py_ssize_t),
np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask,
np.ndarray[np.uint8_t, ndim=2] out,
char shift_x, char shift_y, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1)
+274
View File
@@ -0,0 +1,274 @@
#cython: cdivision=True
#cython: boundscheck=False
#cython: nonecheck=False
#cython: wraparound=False
import numpy as np
cimport numpy as np
from libc.stdlib cimport malloc, free
# generic cdef functions
cdef inline np.uint8_t uint8_max(np.uint8_t a, np.uint8_t b):
return a if a >= b else b
cdef inline np.uint8_t uint8_min(np.uint8_t a, np.uint8_t b):
return a if a <= b else b
#---------------------------------------------------------------------------
# 8 bit core kernel
#---------------------------------------------------------------------------
cdef inline void histogram_increment(Py_ssize_t * histo, float * pop, np.uint8_t value):
histo[value] += 1
pop[0] += 1.
cdef inline void histogram_decrement(Py_ssize_t * histo, float * pop, np.uint8_t value):
histo[value] -= 1
pop[0] -= 1.
cdef inline np.uint8_t is_in_mask(Py_ssize_t rows, Py_ssize_t cols, Py_ssize_t r, Py_ssize_t c, np.uint8_t * mask):
""" returns 1 if given(r,c) coordinate are within the image frame ([0-rows],[0-cols]) and
inside the given mask
returns 0 otherwise
"""
if r < 0 or r > rows - 1 or c < 0 or c > cols - 1:
return 0
else:
if mask[r * cols + c]:
return 1
else:
return 0
cdef inline _core8(
np.uint8_t kernel(Py_ssize_t * , float, np.uint8_t, float, float, Py_ssize_t, Py_ssize_t),
np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask,
np.ndarray[np.uint8_t, ndim=2] out,
char shift_x, char shift_y, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
""" Main loop, this function computes the histogram for each image point
- data is uint8
- result is uint8 casted
"""
cdef Py_ssize_t rows = image.shape[0]
cdef Py_ssize_t cols = image.shape[1]
cdef Py_ssize_t srows = selem.shape[0]
cdef Py_ssize_t scols = selem.shape[1]
cdef Py_ssize_t centre_r = int(selem.shape[0] / 2) + shift_y
cdef Py_ssize_t centre_c = int(selem.shape[1] / 2) + shift_x
# check that structuring element center is inside the element bounding box
assert centre_r >= 0
assert centre_c >= 0
assert centre_r < srows
assert centre_c < scols
image = np.ascontiguousarray(image)
if mask is None:
mask = np.ones((rows, cols), dtype=np.uint8)
else:
mask = np.ascontiguousarray(mask)
if image is out:
raise NotImplementedError("Cannot perform rank operation in place.")
if out is None:
out = np.zeros((rows, cols), dtype=np.uint8)
else:
out = np.ascontiguousarray(out)
mask = np.ascontiguousarray(mask)
# define pointers to the data
cdef np.uint8_t * out_data = <np.uint8_t * >out.data
cdef np.uint8_t * image_data = <np.uint8_t * >image.data
cdef np.uint8_t * mask_data = <np.uint8_t * >mask.data
# define local variable types
cdef Py_ssize_t r, c, rr, cc, s, value, local_max, i, even_row
# number of pixels actually inside the neighborhood (float)
cdef float pop
# allocate memory with malloc
cdef Py_ssize_t max_se = srows * scols
# number of element in each attack border
cdef Py_ssize_t num_se_n, num_se_s, num_se_e, num_se_w
# the current local histogram distribution
cdef Py_ssize_t * histo = <Py_ssize_t * >malloc(256 * sizeof(Py_ssize_t))
# these lists contain the relative pixel row and column for each of the 4 attack borders
# east, west, north and south
# e.g. se_e_r lists the rows of the east structuring element border
cdef Py_ssize_t * se_e_r = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_e_c = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_w_r = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_w_c = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_n_r = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_n_c = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_s_r = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
cdef Py_ssize_t * se_s_c = <Py_ssize_t * >malloc(max_se * sizeof(Py_ssize_t))
# build attack and release borders
# by using difference along axis
t = np.hstack((selem, np.zeros((selem.shape[0], 1))))
t_e = np.diff(t, axis=1) == -1
t = np.hstack((np.zeros((selem.shape[0], 1)), selem))
t_w = np.diff(t, axis=1) == 1
t = np.vstack((selem, np.zeros((1, selem.shape[1]))))
t_s = np.diff(t, axis=0) == -1
t = np.vstack((np.zeros((1, selem.shape[1])), selem))
t_n = np.diff(t, axis=0) == 1
num_se_n = num_se_s = num_se_e = num_se_w = 0
for r in range(srows):
for c in range(scols):
if t_e[r, c]:
se_e_r[num_se_e] = r - centre_r
se_e_c[num_se_e] = c - centre_c
num_se_e += 1
if t_w[r, c]:
se_w_r[num_se_w] = r - centre_r
se_w_c[num_se_w] = c - centre_c
num_se_w += 1
if t_n[r, c]:
se_n_r[num_se_n] = r - centre_r
se_n_c[num_se_n] = c - centre_c
num_se_n += 1
if t_s[r, c]:
se_s_r[num_se_s] = r - centre_r
se_s_c[num_se_s] = c - centre_c
num_se_s += 1
# initial population and histogram (kernel is centered on the first row and column)
for i in range(256):
histo[i] = 0
pop = 0
for r in range(srows):
for c in range(scols):
rr = r - centre_r
cc = c - centre_c
if selem[r, c]:
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_increment(histo, & pop, image_data[rr * cols + cc])
r = 0
c = 0
# kernel --------------------------------------------------------------------
out_data[r * cols + c] = kernel(histo, pop, image_data[r * cols +
c], p0, p1, s0, s1)
# kernel --------------------------------------------------------------------
# main loop
r = 0
for even_row in range(0, rows, 2):
# ---> west to east
for c in range(1, cols):
for s in range(num_se_e):
rr = r + se_e_r[s]
cc = c + se_e_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_increment(histo, & pop, image_data[rr * cols + cc])
for s in range(num_se_w):
rr = r + se_w_r[s]
cc = c + se_w_c[s] - 1
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_decrement(histo, & pop, image_data[rr * cols + cc])
# kernel --------------------------------------------------------------------
out_data[r * cols + c] = kernel(
histo, pop, image_data[r * cols + c], p0, p1, s0, s1)
# kernel --------------------------------------------------------------------
r += 1 # pass to the next row
if r >= rows:
break
# ---> north to south
for s in range(num_se_s):
rr = r + se_s_r[s]
cc = c + se_s_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_increment(histo, & pop, image_data[rr * cols + cc])
for s in range(num_se_n):
rr = r + se_n_r[s] - 1
cc = c + se_n_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_decrement(histo, & pop, image_data[rr * cols + cc])
# kernel --------------------------------------------------------------------
out_data[r * cols + c] = kernel(histo, pop, image_data[r *
cols + c], p0, p1, s0, s1)
# kernel --------------------------------------------------------------------
# ---> east to west
for c in range(cols - 2, -1, -1):
for s in range(num_se_w):
rr = r + se_w_r[s]
cc = c + se_w_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_increment(histo, & pop, image_data[rr * cols + cc])
for s in range(num_se_e):
rr = r + se_e_r[s]
cc = c + se_e_c[s] + 1
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_decrement(histo, & pop, image_data[rr * cols + cc])
# kernel --------------------------------------------------------------------
out_data[r * cols + c] = kernel(
histo, pop, image_data[r * cols + c], p0, p1, s0, s1)
# kernel --------------------------------------------------------------------
r += 1 # pass to the next row
if r >= rows:
break
# ---> north to south
for s in range(num_se_s):
rr = r + se_s_r[s]
cc = c + se_s_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_increment(histo, & pop, image_data[rr * cols + cc])
for s in range(num_se_n):
rr = r + se_n_r[s] - 1
cc = c + se_n_c[s]
if is_in_mask(rows, cols, rr, cc, mask_data):
histogram_decrement(histo, & pop, image_data[rr * cols + cc])
# kernel --------------------------------------------------------------------
out_data[r * cols + c] = kernel(histo, pop, image_data[r *
cols + c], p0, p1, s0, s1)
# kernel --------------------------------------------------------------------
# release memory allocated by malloc
free(se_e_r)
free(se_e_c)
free(se_w_r)
free(se_w_c)
free(se_n_r)
free(se_n_c)
free(se_s_r)
free(se_s_c)
free(histo)
return out
+364
View File
@@ -0,0 +1,364 @@
#cython: cdivision=True
#cython: boundscheck=False
#cython: nonecheck=False
#cython: wraparound=False
import numpy as np
cimport numpy as np
from libc.math cimport log2
# import main loop
from skimage.filter.rank._core16 cimport _core16
# -----------------------------------------------------------------
# kernels uint16 take extra parameter for defining the bitdepth
# -----------------------------------------------------------------
cdef inline np.uint16_t kernel_autolevel(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i, imin, imax, delta
if pop:
for i in range(maxbin - 1, -1, -1):
if histo[i]:
imax = i
break
for i in range(maxbin):
if histo[i]:
imin = i
break
delta = imax - imin
if delta > 0:
return < np.uint16_t > (1. * (maxbin - 1) * (g - imin) / delta)
else:
return < np.uint16_t > (imax - imin)
cdef inline np.uint16_t kernel_bottomhat(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
for i in range(maxbin):
if histo[i]:
break
return < np.uint16_t > (g - i)
cdef inline np.uint16_t kernel_equalize(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
cdef float sum = 0.
if pop:
for i in range(maxbin):
sum += histo[i]
if i >= g:
break
return < np.uint16_t > (((maxbin - 1) * sum) / pop)
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_gradient(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i, imin, imax
if pop:
for i in range(maxbin - 1, -1, -1):
if histo[i]:
imax = i
break
for i in range(maxbin):
if histo[i]:
imin = i
break
return < np.uint16_t > (imax - imin)
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_maximum(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
if pop:
for i in range(maxbin - 1, -1, -1):
if histo[i]:
return < np.uint16_t > (i)
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_mean(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
cdef float mean = 0.
if pop:
for i in range(maxbin):
mean += histo[i] * i
return < np.uint16_t > (mean / pop)
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_meansubstraction(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
cdef float mean = 0.
if pop:
for i in range(maxbin):
mean += histo[i] * i
return < np.uint16_t > ((g - mean / pop) / 2. + (midbin - 1))
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_median(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
cdef float sum = pop / 2.0
if pop:
for i in range(maxbin):
if histo[i]:
sum -= histo[i]
if sum < 0:
return < np.uint16_t > (i)
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_minimum(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
if pop:
for i in range(maxbin):
if histo[i]:
return < np.uint16_t > (i)
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_modal(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t hmax = 0, imax = 0
if pop:
for i in range(maxbin):
if histo[i] > hmax:
hmax = histo[i]
imax = i
return < np.uint16_t > (imax)
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_morph_contr_enh(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i, imin, imax
if pop:
for i in range(maxbin - 1, -1, -1):
if histo[i]:
imax = i
break
for i in range(maxbin):
if histo[i]:
imin = i
break
if imax - g < g - imin:
return < np.uint16_t > (imax)
else:
return < np.uint16_t > (imin)
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_pop(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
return < np.uint16_t > (pop)
cdef inline np.uint16_t kernel_threshold(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
cdef float mean = 0.
if pop:
for i in range(maxbin):
mean += histo[i] * i
return < np.uint16_t > (g > (mean / pop))
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_tophat(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
for i in range(maxbin - 1, -1, -1):
if histo[i]:
break
return < np.uint16_t > (i - g)
cdef inline np.uint16_t kernel_entropy(
Py_ssize_t * histo, float pop, np.uint16_t g,
Py_ssize_t bitdepth, Py_ssize_t maxbin, Py_ssize_t midbin,
float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
cdef float e,p
e = 0.
for i in range(maxbin):
p = histo[i]/pop
if p>0:
e -= p*log2(p)
return < np.uint16_t > e*1000
# -----------------------------------------------------------------
# python wrappers
# -----------------------------------------------------------------
def autolevel(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_autolevel, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def bottomhat(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_bottomhat, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def equalize(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_equalize, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def gradient(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_gradient, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def maximum(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_maximum, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def mean(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_mean, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def meansubstraction(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_meansubstraction, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def median(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_median, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def minimum(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_minimum, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def morph_contr_enh(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_morph_contr_enh, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def modal(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_modal, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def pop(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_pop, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def threshold(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_threshold, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def tophat(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_tophat, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def entropy(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, Py_ssize_t bitdepth=8):
return _core16(kernel_entropy, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
@@ -0,0 +1,72 @@
#cython: cdivision=True
#cython: boundscheck=False
#cython: nonecheck=False
#cython: wraparound=False
import numpy as np
cimport numpy as np
# import main loop
from skimage.filter.rank._core16 cimport _core16
# -----------------------------------------------------------------
# kernels uint16 take extra parameter for defining the bitdepth
# -----------------------------------------------------------------
cdef inline np.uint16_t kernel_mean(
Py_ssize_t * histo, float pop, np.uint16_t g, Py_ssize_t bitdepth,
Py_ssize_t maxbin, Py_ssize_t midbin, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef int i, bilat_pop = 0
cdef float mean = 0.
if pop:
for i in range(maxbin):
if (g > (i - s0)) and (g < (i + s1)):
bilat_pop += histo[i]
mean += histo[i] * i
if bilat_pop:
return < np.uint16_t > (mean / bilat_pop)
else:
return < np.uint16_t > (0)
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_pop(
Py_ssize_t * histo, float pop, np.uint16_t g, Py_ssize_t bitdepth, Py_ssize_t maxbin,
Py_ssize_t midbin, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef int i, bilat_pop = 0
if pop:
for i in range(maxbin):
if (g > (i - s0)) and (g < (i + s1)):
bilat_pop += histo[i]
return < np.uint16_t > (bilat_pop)
else:
return < np.uint16_t > (0)
# -----------------------------------------------------------------
# python wrappers
# -----------------------------------------------------------------
def mean(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, int bitdepth=8, int s0=1, int s1=1):
"""average gray level (clipped on uint8)
"""
return _core16(kernel_mean, image, selem, mask, out, shift_x, shift_y, bitdepth, 0., 0., s0, s1)
def pop(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, int bitdepth=8, int s0=1, int s1=1):
"""returns the number of actual pixels of the structuring element inside the mask
"""
return _core16(kernel_pop, image, selem, mask, out, shift_x, shift_y, bitdepth, .0, .0, s0, s1)
@@ -0,0 +1,297 @@
#cython: cdivision=True
#cython: boundscheck=False
#cython: nonecheck=False
#cython: wraparound=False
import numpy as np
cimport numpy as np
# import main loop
from skimage.filter.rank._core16 cimport _core16, int_min, int_max
# -----------------------------------------------------------------
# kernels uint16 (SOFT version using percentiles)
# -----------------------------------------------------------------
cdef inline np.uint16_t kernel_autolevel(
Py_ssize_t * histo, float pop, np.uint16_t g, Py_ssize_t bitdepth,
Py_ssize_t maxbin, Py_ssize_t midbin, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef int i, imin, imax, sum, delta
if pop:
sum = 0
p1 = 1.0 - p1
for i in range(maxbin):
sum += histo[i]
if sum > p0 * pop:
imin = i
break
sum = 0
for i in range(maxbin - 1, -1, -1):
sum += histo[i]
if sum > p1 * pop:
imax = i
break
delta = imax - imin
if delta > 0:
return < np.uint16_t > (1.0 * (maxbin - 1) * (int_min(int_max(imin, g), imax) - imin) / delta)
else:
return < np.uint16_t > (imax - imin)
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_gradient(
Py_ssize_t * histo, float pop, np.uint16_t g, Py_ssize_t bitdepth,
Py_ssize_t maxbin, Py_ssize_t midbin, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef int i, imin, imax, sum, delta
if pop:
sum = 0
p1 = 1.0 - p1
for i in range(maxbin):
sum += histo[i]
if sum >= p0 * pop:
imin = i
break
sum = 0
for i in range((maxbin - 1), -1, -1):
sum += histo[i]
if sum >= p1 * pop:
imax = i
break
return < np.uint16_t > (imax - imin)
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_mean(
Py_ssize_t * histo, float pop, np.uint16_t g, Py_ssize_t bitdepth,
Py_ssize_t maxbin, Py_ssize_t midbin, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef int i, sum, mean, n
if pop:
sum = 0
mean = 0
n = 0
for i in range(maxbin):
sum += histo[i]
if (sum >= p0 * pop) and (sum <= p1 * pop):
n += histo[i]
mean += histo[i] * i
if n > 0:
return < np.uint16_t > (1.0 * mean / n)
else:
return < np.uint16_t > (0)
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_mean_substraction(
Py_ssize_t * histo, float pop, np.uint16_t g, Py_ssize_t bitdepth,
Py_ssize_t maxbin, Py_ssize_t midbin, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef int i, sum, mean, n
if pop:
sum = 0
mean = 0
n = 0
for i in range(maxbin):
sum += histo[i]
if (sum >= p0 * pop) and (sum <= p1 * pop):
n += histo[i]
mean += histo[i] * i
if n > 0:
return < np.uint16_t > ((g - (mean / n)) * .5 + midbin)
else:
return < np.uint16_t > (0)
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_morph_contr_enh(
Py_ssize_t * histo, float pop, np.uint16_t g, Py_ssize_t bitdepth,
Py_ssize_t maxbin, Py_ssize_t midbin, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef int i, imin, imax, sum, delta
if pop:
sum = 0
p1 = 1.0 - p1
for i in range(maxbin):
sum += histo[i]
if sum > p0 * pop:
imin = i
break
sum = 0
for i in range((maxbin - 1), -1, -1):
sum += histo[i]
if sum > p1 * pop:
imax = i
break
if g > imax:
return < np.uint16_t > imax
if g < imin:
return < np.uint16_t > imin
if imax - g < g - imin:
return < np.uint16_t > imax
else:
return < np.uint16_t > imin
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_percentile(
Py_ssize_t * histo, float pop, np.uint16_t g, Py_ssize_t bitdepth,
Py_ssize_t maxbin, Py_ssize_t midbin, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef int i
cdef float sum = 0.
if pop:
for i in range(maxbin):
sum += histo[i]
if sum >= p0 * pop:
break
return < np.uint16_t > (i)
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_pop(
Py_ssize_t * histo, float pop, np.uint16_t g, Py_ssize_t bitdepth,
Py_ssize_t maxbin, Py_ssize_t midbin, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef int i, sum, n
if pop:
sum = 0
n = 0
for i in range(maxbin):
sum += histo[i]
if (sum >= p0 * pop) and (sum <= p1 * pop):
n += histo[i]
return < np.uint16_t > (n)
else:
return < np.uint16_t > (0)
cdef inline np.uint16_t kernel_threshold(
Py_ssize_t * histo, float pop, np.uint16_t g, Py_ssize_t bitdepth,
Py_ssize_t maxbin, Py_ssize_t midbin, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef int i
cdef float sum = 0.
if pop:
for i in range(maxbin):
sum += histo[i]
if sum >= p0 * pop:
break
return < np.uint16_t > ((maxbin - 1) * (g >= i))
else:
return < np.uint16_t > (0)
# -----------------------------------------------------------------
# python wrappers
# -----------------------------------------------------------------
def autolevel(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, int bitdepth=8, float p0=0., float p1=0.):
"""bottom hat
"""
return _core16(
kernel_autolevel, image, selem, mask, out, shift_x, shift_y, bitdepth, p0, p1,
< Py_ssize_t > 0, < Py_ssize_t > 0)
def gradient(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, int bitdepth=8, float p0=0., float p1=0.):
"""return p0,p1 percentile gradient
"""
return _core16(
kernel_gradient, image, selem, mask, out, shift_x, shift_y, bitdepth, p0, p1, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def mean(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, int bitdepth=8, float p0=0., float p1=0.):
"""return mean between [p0 and p1] percentiles
"""
return _core16(
kernel_mean, image, selem, mask, out, shift_x, shift_y, bitdepth, p0, p1, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def mean_substraction(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, int bitdepth=8, float p0=0., float p1=0.):
"""return original - mean between [p0 and p1] percentiles *.5 +127
"""
return _core16(
kernel_mean_substraction, image, selem, mask, out, shift_x, shift_y, bitdepth, p0, p1,
< Py_ssize_t > 0, < Py_ssize_t > 0)
def morph_contr_enh(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, int bitdepth=8, float p0=0., float p1=0.):
"""reforce contrast using percentiles
"""
return _core16(
kernel_morph_contr_enh, image, selem, mask, out, shift_x, shift_y, bitdepth, p0, p1,
< Py_ssize_t > 0, < Py_ssize_t > 0)
def percentile(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, int bitdepth=8, float p0=0., float p1=0.):
"""return p0 percentile
"""
return _core16(
kernel_percentile, image, selem, mask, out, shift_x, shift_y, bitdepth, p0, p1,
< Py_ssize_t > 0, < Py_ssize_t > 0)
def pop(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, int bitdepth=8, float p0=0., float p1=0.):
"""return nb of pixels between [p0 and p1]
"""
return _core16(
kernel_pop, image, selem, mask, out, shift_x, shift_y, bitdepth, p0, p1,
< Py_ssize_t > 0, < Py_ssize_t > 0)
def threshold(np.ndarray[np.uint16_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint16_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, int bitdepth=8, float p0=0., float p1=0.):
"""return (maxbin-1) if g > percentile p0
"""
return _core16(
kernel_threshold, image, selem, mask, out, shift_x, shift_y, bitdepth, p0, p1,
< Py_ssize_t > 0, < Py_ssize_t > 0)
+458
View File
@@ -0,0 +1,458 @@
#cython: cdivision=True
#cython: boundscheck=False
#cython: nonecheck=False
#cython: wraparound=False
import numpy as np
cimport numpy as np
from libc.math cimport log2
# import main loop
from skimage.filter.rank._core8 cimport _core8
# -----------------------------------------------------------------
# kernels uint8
# -----------------------------------------------------------------
cdef inline np.uint8_t kernel_autolevel(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1,
Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i, imin, imax, delta
if pop:
for i in range(255, -1, -1):
if histo[i]:
imax = i
break
for i in range(256):
if histo[i]:
imin = i
break
delta = imax - imin
if delta > 0:
return < np.uint8_t > (255. * (g - imin) / delta)
else:
return < np.uint8_t > (imax - imin)
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_bottomhat(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1,
Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
for i in range(256):
if histo[i]:
break
return < np.uint8_t > (g - i)
cdef inline np.uint8_t kernel_equalize(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1,
Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
cdef float sum = 0.
if pop:
for i in range(256):
sum += histo[i]
if i >= g:
break
return < np.uint8_t > ((255 * sum) / pop)
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_gradient(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef Py_ssize_t i, imin, imax
if pop:
for i in range(255, -1, -1):
if histo[i]:
imax = i
break
for i in range(256):
if histo[i]:
imin = i
break
return < np.uint8_t > (imax - imin)
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_maximum(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef Py_ssize_t i
if pop:
for i in range(255, -1, -1):
if histo[i]:
return < np.uint8_t > (i)
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_mean(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef Py_ssize_t i
cdef float mean = 0.
if pop:
for i in range(256):
mean += histo[i] * i
return < np.uint8_t > (mean / pop)
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_meansubstraction(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1,
Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i
cdef float mean = 0.
if pop:
for i in range(256):
mean += histo[i] * i
return < np.uint8_t > ((g - mean / pop) / 2. + 127)
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_median(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef Py_ssize_t i
cdef float sum = pop / 2.0
if pop:
for i in range(256):
if histo[i]:
sum -= histo[i]
if sum < 0:
return < np.uint8_t > (i)
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_minimum(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef Py_ssize_t i
if pop:
for i in range(256):
if histo[i]:
return < np.uint8_t > (i)
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_modal(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t hmax = 0, imax = 0
if pop:
for i in range(256):
if histo[i] > hmax:
hmax = histo[i]
imax = i
return < np.uint8_t > (imax)
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_morph_contr_enh(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1,
Py_ssize_t s0, Py_ssize_t s1):
cdef Py_ssize_t i, imin, imax
if pop:
for i in range(255, -1, -1):
if histo[i]:
imax = i
break
for i in range(256):
if histo[i]:
imin = i
break
if imax - g < g - imin:
return < np.uint8_t > (imax)
else:
return < np.uint8_t > (imin)
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_pop(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
return < np.uint8_t > (pop)
cdef inline np.uint8_t kernel_threshold(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef Py_ssize_t i
cdef float mean = 0.
if pop:
for i in range(256):
mean += histo[i] * i
return < np.uint8_t > (g > (mean / pop))
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_tophat(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef Py_ssize_t i
for i in range(255, -1, -1):
if histo[i]:
break
return < np.uint8_t > (i - g)
cdef inline np.uint8_t kernel_noise_filter(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef Py_ssize_t i
cdef Py_ssize_t min_i
# early stop if at least one pixel of the neighborhood has the same g
if histo[g]>0:
return < np.uint8_t > 0
for i in range(g, -1, -1):
if histo[i]:
break
min_i = g-i
for i in range(g, 256):
if histo[i]:
break
if i-g < min_i:
return < np.uint8_t > (i-g)
else:
return < np.uint8_t > min_i
cdef inline np.uint8_t kernel_entropy(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef Py_ssize_t i
cdef float e,p
e = 0.
for i in range(256):
p = histo[i]/pop
if p>0:
e -= p*log2(p)
return < np.uint8_t > e*10
cdef inline np.uint8_t kernel_otsu(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef Py_ssize_t i
cdef Py_ssize_t max_i
cdef float P, mu1, mu2, q1,new_q1, sigma_b, max_sigma_b
cdef float mu = 0.
# compute local mean
if pop:
for i in range(256):
mu += histo[i] * i
mu = (mu / pop)
else:
return < np.uint8_t > (0)
# maximizing the between class variance
max_i = 0
q1 = histo[0]/pop
m1 = 0.
max_sigma_b = 0.
for i in range(1,256):
P = histo[i]/pop
new_q1 = q1 + P
if new_q1>0:
mu1 = (q1*mu1 + i*P)/new_q1
mu2 = (mu-new_q1*mu1)/(1.-new_q1)
sigma_b = new_q1*(1.-new_q1)*(mu1-mu2)**2
if sigma_b>max_sigma_b:
max_sigma_b = sigma_b
max_i = i
q1 = new_q1
if g>max_i:
return < np.uint8_t > 255
else:
return < np.uint8_t > 0
# -----------------------------------------------------------------
# python wrappers
# used only internally
# -----------------------------------------------------------------
def autolevel(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(
kernel_autolevel, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def bottomhat(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(
kernel_bottomhat, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def equalize(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(
kernel_equalize, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def gradient(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(
kernel_gradient, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def maximum(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(kernel_maximum, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def mean(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(kernel_mean, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def meansubstraction(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(
kernel_meansubstraction, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def median(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(kernel_median, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def minimum(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(kernel_minimum, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def morph_contr_enh(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(
kernel_morph_contr_enh, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def modal(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(kernel_modal, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def pop(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(kernel_pop, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def threshold(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(
kernel_threshold, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def tophat(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(kernel_tophat, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def noise_filter(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(kernel_noise_filter, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def entropy(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(kernel_entropy, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
def otsu(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0):
return _core8(kernel_otsu, image, selem, mask, out, shift_x, shift_y, .0, .0, < Py_ssize_t > 0, < Py_ssize_t > 0)
+286
View File
@@ -0,0 +1,286 @@
#cython: cdivision=True
#cython: boundscheck=False
#cython: nonecheck=False
#cython: wraparound=False
import numpy as np
cimport numpy as np
# import main loop
from skimage.filter.rank._core8 cimport _core8, uint8_max, uint8_min
# -----------------------------------------------------------------
# kernels uint8 (SOFT version using percentiles)
# -----------------------------------------------------------------
cdef inline np.uint8_t kernel_autolevel(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef int i, imin, imax, sum, delta
if pop:
sum = 0
p1 = 1.0 - p1
imin = 0
imax = 255
for i in range(256):
sum += histo[i]
if sum > (p0 * pop):
imin = i
break
sum = 0
for i in range(255, -1, -1):
sum += histo[i]
if sum > (p1 * pop):
imax = i
break
delta = imax - imin
if delta > 0:
return < np.uint8_t > (255 * (uint8_min(uint8_max(imin, g), imax) - imin) / delta)
else:
return < np.uint8_t > (imax - imin)
else:
return < np.uint8_t > (128)
cdef inline np.uint8_t kernel_gradient(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef int i, imin, imax, sum, delta
if pop:
sum = 0
p1 = 1.0 - p1
for i in range(256):
sum += histo[i]
if sum >= p0 * pop:
imin = i
break
sum = 0
for i in range(255, -1, -1):
sum += histo[i]
if sum >= p1 * pop:
imax = i
break
return < np.uint8_t > (imax - imin)
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_mean(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef int i, sum, mean, n
if pop:
sum = 0
mean = 0
n = 0
for i in range(256):
sum += histo[i]
if (sum >= p0 * pop) and (sum <= p1 * pop):
n += histo[i]
mean += histo[i] * i
if n > 0:
return < np.uint8_t > (1.0 * mean / n)
else:
return < np.uint8_t > (0)
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_mean_substraction(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1,
Py_ssize_t s0, Py_ssize_t s1):
cdef int i, sum, mean, n
if pop:
sum = 0
mean = 0
n = 0
for i in range(256):
sum += histo[i]
if (sum >= p0 * pop) and (sum <= p1 * pop):
n += histo[i]
mean += histo[i] * i
if n > 0:
return < np.uint8_t > ((g - (mean / n)) * .5 + 127)
else:
return < np.uint8_t > (0)
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_morph_contr_enh(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1,
Py_ssize_t s0, Py_ssize_t s1):
cdef int i, imin, imax, sum, delta
if pop:
sum = 0
p1 = 1.0 - p1
for i in range(256):
sum += histo[i]
if sum >= p0 * pop:
imin = i
break
sum = 0
for i in range(255, -1, -1):
sum += histo[i]
if sum >= p1 * pop:
imax = i
break
if g > imax:
return < np.uint8_t > imax
if g < imin:
return < np.uint8_t > imin
if imax - g < g - imin:
return < np.uint8_t > imax
else:
return < np.uint8_t > imin
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_percentile(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1,
Py_ssize_t s0, Py_ssize_t s1):
cdef int i
cdef float sum = 0.
if pop:
for i in range(256):
sum += histo[i]
if sum >= p0 * pop:
break
return < np.uint8_t > (i)
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_pop(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1,
Py_ssize_t s0, Py_ssize_t s1):
cdef int i, sum, n
if pop:
sum = 0
n = 0
for i in range(256):
sum += histo[i]
if (sum >= p0 * pop) and (sum <= p1 * pop):
n += histo[i]
return < np.uint8_t > (n)
else:
return < np.uint8_t > (0)
cdef inline np.uint8_t kernel_threshold(
Py_ssize_t * histo, float pop, np.uint8_t g, float p0, float p1, Py_ssize_t s0,
Py_ssize_t s1):
cdef int i
cdef float sum = 0.
if pop:
for i in range(256):
sum += histo[i]
if sum >= p0 * pop:
break
return < np.uint8_t > (255 * (g >= i))
else:
return < np.uint8_t > (0)
# -----------------------------------------------------------------
# python wrappers
# -----------------------------------------------------------------
def autolevel(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, float p0=0., float p1=0.):
"""autolevel
"""
return _core8(
kernel_autolevel, image, selem, mask, out, shift_x, shift_y, p0, p1, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def gradient(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, float p0=0., float p1=0.):
"""return p0,p1 percentile gradient
"""
return _core8(
kernel_gradient, image, selem, mask, out, shift_x, shift_y, p0, p1, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def mean(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, float p0=0., float p1=0.):
"""return mean between [p0 and p1] percentiles
"""
return _core8(kernel_mean, image, selem, mask, out, shift_x, shift_y, p0, p1, < Py_ssize_t > 0, < Py_ssize_t > 0)
def mean_substraction(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, float p0=0., float p1=0.):
"""return original - mean between [p0 and p1] percentiles *.5 +127
"""
return _core8(
kernel_mean_substraction, image, selem, mask, out, shift_x, shift_y, p0, p1, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def morph_contr_enh(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, float p0=0., float p1=0.):
"""reforce contrast using percentiles
"""
return _core8(
kernel_morph_contr_enh, image, selem, mask, out, shift_x, shift_y, p0, p1, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def percentile(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, float p0=0., float p1=0.):
"""return p0 percentile
"""
return _core8(
kernel_percentile, image, selem, mask, out, shift_x, shift_y, p0, p1, < Py_ssize_t > 0,
< Py_ssize_t > 0)
def pop(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, float p0=0., float p1=0.):
"""return nb of pixels between [p0 and p1]
"""
return _core8(kernel_pop, image, selem, mask, out, shift_x, shift_y, p0, p1, < Py_ssize_t > 0, < Py_ssize_t > 0)
def threshold(np.ndarray[np.uint8_t, ndim=2] image,
np.ndarray[np.uint8_t, ndim=2] selem,
np.ndarray[np.uint8_t, ndim=2] mask=None,
np.ndarray[np.uint8_t, ndim=2] out=None,
char shift_x=0, char shift_y=0, float p0=0., float p1=0.):
"""return 255 if g > percentile p0
"""
return _core8(
kernel_threshold, image, selem, mask, out, shift_x, shift_y, p0, p1, < Py_ssize_t > 0,
< Py_ssize_t > 0)
+165
View File
@@ -0,0 +1,165 @@
"""bilateral_rank.py - approximate bilateral rankfilter for local (custom kernel) mean
The local histogram is computed using a sliding window similar to the method described in
Reference: Huang, T. ,Yang, G. ; Tang, G.. "A fast two-dimensional median filtering algorithm",
IEEE Transactions on Acoustics, Speech and Signal Processing, Feb 1979. Volume: 27 , Issue: 1, Page(s): 13 - 18.
input image can be 8 bit or 16 bit with a value < 4096 (i.e. 12 bit),
8 bit images are casted in 16 bit
the number of histogram bins is determined from the maximum value present in the image
The pixel neighborhood is defined by:
* the given structuring element
* an interval [g-s0,g+s1] in gray level around g the processed pixel gray level
The kernel is flat (i.e. each pixel belonging to the neighborhood contributes equally)
result image is 16 bit with respect to the input image
"""
from skimage import img_as_ubyte
import numpy as np
from skimage.filter.rank import _crank16_bilateral
from skimage.filter.rank.generic import find_bitdepth
__all__ = ['bilateral_mean', 'bilateral_pop']
def _apply(func8, func16, image, selem, out, mask, shift_x, shift_y, s0, s1):
selem = img_as_ubyte(selem)
if mask is not None:
mask = img_as_ubyte(mask)
if image.dtype == np.uint8:
image = image.astype(np.uint16)
elif image.dtype == np.uint16:
pass
else:
raise TypeError("only uint8 and uint16 image supported!")
bitdepth = find_bitdepth(image)
if bitdepth > 11:
raise ValueError("only uint16 <4096 image (12bit) supported!")
return func16(
image, selem, shift_x=shift_x, shift_y=shift_y, mask=mask, bitdepth=bitdepth + 1, out=out,
s0=s0, s1=s1)
def bilateral_mean(image, selem, out=None, mask=None, shift_x=False, shift_y=False, s0=10, s1=10):
"""Apply a flat kernel bilateral filter.
This is an edge-preserving and noise reducing denoising filter. It averages
pixels based on their spatial closeness and radiometric similarity.
Spatial closeness is measured by considering only the local pixel neighborhood given by a
structuring element (selem).
Radiometric similarity is defined by the gray level interval [g-s0,g+s1] where g is the current pixel gray level.
Only pixels belonging to the structuring element AND having a gray level inside this interval are averaged.
Return greyscale local bilateral_mean of an image.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, as the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
s0, s1 : int
define the [s0,s1] interval to be considered for computing the value.
Returns
-------
out : uint16 array (uint8 image are casted to uint16)
The result of the local bilateral mean.
See also
--------
skimage.filter.denoise_bilateral() for a gaussian bilateral filter.
Notes
-----
* input image can be 8 bit or 16 bit with a value < 4096 (i.e. 12 bit)
* 8 bit images are casted in 16 bit
Examples
--------
>>> from skimage import data
>>> from skimage.morphology import disk
>>> from skimage.filter.rank import bilateral_mean
>>> # Load test image
>>> ima = data.camera()
>>> # bilateral filtering of cameraman image using a flat kernel
>>> bilat_ima = bilateral_mean(ima, disk(20), s0=10,s1=10)
"""
return _apply(
None, _crank16_bilateral.mean, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y,
s0=s0, s1=s1)
def bilateral_pop(image, selem, out=None, mask=None, shift_x=False, shift_y=False, s0=10, s1=10):
"""Return the number (population) of pixels actually inside the bilateral neighborhood,
i.e. being inside the structuring element AND having a gray level inside the interval [g-s0,g+s1].
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, as the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
s0, s1 : int
define the [s0,s1] interval to be considered for computing the value.
Returns
-------
out : uint16 array (uint8 image are casted to uint16)
the local number of pixels inside the bilateral neighborhood
Examples
--------
>>> # Local mean
>>> from skimage.morphology import square
>>> import skimage.filter.rank as rank
>>> ima8 = 255*np.array([[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]], dtype=np.uint8)
>>> rank.bilateral_pop(ima8, square(3), s0=10,s1=10)
array([[3, 4, 3, 4, 3],
[4, 4, 6, 4, 4],
[3, 6, 9, 6, 3],
[4, 4, 6, 4, 4],
[3, 4, 3, 4, 3]], dtype=uint16)
"""
return _apply(
None, _crank16_bilateral.pop, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y,
s0=s0, s1=s1)
+77
View File
@@ -0,0 +1,77 @@
import matplotlib.pyplot as plt
from pprint import pprint
from skimage import data
from skimage.morphology.selem import disk
import skimage.filter.rank as rank
def plot_all():
a8 = data.camera()
a16 = a8.astype('uint16')*16
selem = disk(5)
name_list = sorted([n for n in dir(rank) if n[0] is not '_'])
print name_list
for n in name_list:
if n.rfind('bilateral')==0:
print n
method = eval('rank.%s'%n)
if type(method) == type(rank.maximum):
print method
f8 = method(a8,selem = selem,s0=10,s1=10)
f16 = method(a16,selem = selem,s0=10,s1=10)
plt.figure()
plt.subplot(2,2,1)
plt.imshow(a8)
plt.colorbar()
plt.subplot(2,2,2)
plt.imshow(f8)
plt.colorbar()
plt.subplot(2,2,3)
plt.imshow(f16)
plt.colorbar()
plt.title(method)
for n in name_list:
if n.rfind('percentile')==0:
print n
method = eval('rank.%s'%n)
if type(method) == type(rank.maximum):
print method
f8 = method(a8,selem = selem,p0=.1,p1=.9)
f16 = method(a16,selem = selem,p0=.1,p1=.9)
plt.figure()
plt.subplot(2,2,1)
plt.imshow(a8)
plt.colorbar()
plt.subplot(2,2,2)
plt.imshow(f8)
plt.colorbar()
plt.subplot(2,2,3)
plt.imshow(f16)
plt.colorbar()
plt.title(method)
for n in name_list:
if n.find('percentile')==-1 and n.find('bilateral')==-1:
print n
method = eval('rank.%s'%n)
if type(method) == type(rank.maximum):
print method
f8 = method(a8,selem = selem)
f16 = method(a16,selem = selem)
plt.figure()
plt.subplot(2,2,1)
plt.imshow(a8)
plt.colorbar()
plt.subplot(2,2,2)
plt.imshow(f8)
plt.colorbar()
plt.subplot(2,2,3)
plt.imshow(f16)
plt.colorbar()
plt.title(method)
plt.show()
if __name__ == '__main__':
plot_all()
pprint(dir(rank))
+32
View File
@@ -0,0 +1,32 @@
import numpy as np
import matplotlib.pyplot as plt
from skimage import data
from skimage.morphology.selem import disk
import skimage.filter.rank as rank
from skimage.filter import denoise_bilateral
if __name__ == '__main__':
a8 = data.camera()
a16 = data.camera().astype(np.uint16)*4
p8 = data.page()
selem = disk(20)
otsu = rank.otsu(p8,selem)
plt.figure()
plt.subplot(1,2,1)
plt.imshow(p8)
plt.colorbar()
plt.subplot(1,2,2)
plt.imshow(otsu)
plt.colorbar()
plt.show()
Binary file not shown.
@@ -0,0 +1,28 @@
import numpy as np
import matplotlib.pyplot as plt
import gdal
from skimage.morphology import disk
import skimage.filter.rank as rank
filename = 'iko_pan_Ja1.tif'
im16 = gdal.Open(filename).ReadAsArray().astype(np.uint16)
plt.figure()
plt.imshow(im16,cmap=plt.cm.gray)
plt.colorbar()
f0 = rank.median(im16,disk(1))
f1 = rank.bilateral_mean(im16,disk(20),s0=200,s1=200)
f2 = rank.equalize(f1,disk(10))
f3 = rank.bottomhat(f1,disk(1))
plt.figure()
plt.imshow(f2,cmap=plt.cm.gray,interpolation='nearest')
plt.colorbar()
plt.show()
+42
View File
@@ -0,0 +1,42 @@
import numpy as np
import matplotlib.pyplot as plt
from skimage import data
from skimage.morphology.selem import disk
import skimage.filter.rank as rank
print dir(rank)
print rank.mean
print rank.percentile_mean
print rank.bilateral_mean
a8 = data.camera()
a16 = a8.astype('uint16')*16
selem = disk(10)
f8 = rank.mean(a8,selem)
f16 = rank.mean(a16,selem)
plt.figure()
plt.imshow(np.hstack((a8,f8)))
plt.colorbar()
plt.figure()
plt.imshow(np.hstack((a16,f16)))
plt.colorbar()
f8 = rank.percentile_mean(a8,selem,p0=.1,p1=.9)
f16 = rank.percentile_mean(a16,selem,p0=.1,p1=.9)
plt.figure()
plt.imshow(np.hstack((a8,f8)))
plt.colorbar()
plt.figure()
plt.imshow(np.hstack((a16,f16)))
plt.colorbar()
plt.show()
+11
View File
@@ -0,0 +1,11 @@
import numpy as np
def find_bitdepth(image):
"""returns the max bith depth of a uint16 image
"""
umax = np.max(image)
if umax > 2:
return int(np.log2(umax))
else:
return 1
+337
View File
@@ -0,0 +1,337 @@
"""percentile_rank.py - inferior and superior ranks, provided by the user, are passed to the kernel function
to provide a softer version of the rank filters. E.g. percentile_autolevel will stretch image levels between
percentile [p0,p1] instead of using [min,max]. It means that isolate bright or dark pixels will not produce halos.
The local histogram is computed using a sliding window similar to the method described in
Reference: Huang, T. ,Yang, G. ; Tang, G.. "A fast two-dimensional median filtering algorithm",
IEEE Transactions on Acoustics, Speech and Signal Processing, Feb 1979. Volume: 27 , Issue: 1, Page(s): 13 - 18.
input image can be 8 bit or 16 bit with a value < 4096 (i.e. 12 bit),
for 16 bit input images, the number of histogram bins is determined from the maximum value present in the image
result image is 8 or 16 bit with respect to the input image
"""
from skimage import img_as_ubyte
import numpy as np
from skimage.filter.rank.generic import find_bitdepth
from skimage.filter.rank import _crank16_percentiles, _crank8_percentiles
__all__ = ['percentile_autolevel', 'percentile_gradient',
'percentile_mean', 'percentile_mean_substraction',
'percentile_morph_contr_enh', 'percentile', 'percentile_pop', 'percentile_threshold']
def _apply(func8, func16, image, selem, out, mask, shift_x, shift_y, p0, p1):
selem = img_as_ubyte(selem)
if mask is not None:
mask = img_as_ubyte(mask)
if image.dtype == np.uint8:
return func8(image, selem, shift_x=shift_x, shift_y=shift_y, mask=mask, out=out, p0=p0, p1=p1)
elif image.dtype == np.uint16:
bitdepth = find_bitdepth(image)
if bitdepth > 11:
raise ValueError("only uint16 <4096 image (12bit) supported!")
return func16(
image, selem, shift_x=shift_x, shift_y=shift_y, mask=mask, bitdepth=bitdepth + 1, out=out,
p0=p0, p1=p1)
else:
raise TypeError("only uint8 and uint16 image supported!")
def percentile_autolevel(image, selem, out=None, mask=None, shift_x=False, shift_y=False, p0=.0, p1=1.):
"""Return greyscale local autolevel of an image.
Autolevel is computed on the given structuring element. Only levels between percentiles [p0,p1] ,are used.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, as the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
p0, p1 : float in [0.,...,1.]
define the [p0,p1] percentile interval to be considered for computing the value.
Returns
-------
local autolevel : uint8 array or uint16 array depending on input image
The result of the local autolevel.
"""
return _apply(
_crank8_percentiles.autolevel, _crank16_percentiles.autolevel, image, selem, out=out, mask=mask,
shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1)
def percentile_gradient(image, selem, out=None, mask=None, shift_x=False, shift_y=False, p0=.0, p1=1.):
"""Return greyscale local percentile_gradient of an image.
percentile_gradient is computed on the given structuring element. Only levels between percentiles [p0,p1] ,are used.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, as the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
p0, p1 : float in [0.,...,1.]
define the [p0,p1] percentile interval to be considered for computing the value.
Returns
-------
local percentile_gradient : uint8 array or uint16 array depending on input image
The result of the local percentile_gradient.
"""
return _apply(
_crank8_percentiles.gradient, _crank16_percentiles.gradient, image, selem, out=out, mask=mask,
shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1)
def percentile_mean(image, selem, out=None, mask=None, shift_x=False, shift_y=False, p0=.0, p1=1.):
"""Return greyscale local mean of an image.
Mean is computed on the given structuring element. Only levels between percentiles [p0,p1] ,are used.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, as the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
p0, p1 : float in [0.,...,1.]
define the [p0,p1] percentile interval to be considered for computing the value.
Returns
-------
local mean : uint8 array or uint16 array depending on input image
The result of the local mean.
"""
return _apply(
_crank8_percentiles.mean, _crank16_percentiles.mean, image, selem, out=out, mask=mask,
shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1)
def percentile_mean_substraction(image, selem, out=None, mask=None, shift_x=False, shift_y=False, p0=.0, p1=1.):
"""Return greyscale local mean_substraction of an image.
mean_substraction is computed on the given structuring element. Only levels between percentiles [p0,p1] ,are used.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, as the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
p0, p1 : float in [0.,...,1.]
define the [p0,p1] percentile interval to be considered for computing the value.
Returns
-------
local mean_substraction : uint8 array or uint16 array depending on input image
The result of the local mean_substraction.
"""
return _apply(
_crank8_percentiles.mean_substraction, _crank16_percentiles.mean_substraction, image, selem, out=out,
mask=mask, shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1)
def percentile_morph_contr_enh(image, selem, out=None, mask=None, shift_x=False, shift_y=False, p0=.0, p1=1.):
"""Return greyscale local morph_contr_enh of an image.
morph_contr_enh is computed on the given structuring element. Only levels between percentiles [p0,p1] ,are used.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, as the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
p0, p1 : float in [0.,...,1.]
define the [p0,p1] percentile interval to be considered for computing the value.
Returns
-------
local morph_contr_enh : uint8 array or uint16 array depending on input image
The result of the local morph_contr_enh.
"""
return _apply(
_crank8_percentiles.morph_contr_enh, _crank16_percentiles.morph_contr_enh, image, selem, out=out,
mask=mask, shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1)
def percentile(image, selem, out=None, mask=None, shift_x=False, shift_y=False, p0=.0, p1=1.):
"""Return greyscale local percentile of an image.
percentile is computed on the given structuring element. Only levels between percentiles [p0,p1] ,are used.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, as the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
p0, p1 : float in [0.,...,1.]
define the [p0,p1] percentile interval to be considered for computing the value.
Returns
-------
local percentile : uint8 array or uint16 array depending on input image
The result of the local percentile.
"""
return _apply(
_crank8_percentiles.percentile, _crank16_percentiles.percentile, image, selem, out=out, mask=mask,
shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1)
def percentile_pop(image, selem, out=None, mask=None, shift_x=False, shift_y=False, p0=.0, p1=1.):
"""Return greyscale local pop of an image.
pop is computed on the given structuring element. Only levels between percentiles [p0,p1] ,are used.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, as the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
p0, p1 : float in [0.,...,1.]
define the [p0,p1] percentile interval to be considered for computing the value.
Returns
-------
local pop : uint8 array or uint16 array depending on input image
The result of the local pop.
"""
return _apply(
_crank8_percentiles.pop, _crank16_percentiles.pop, image, selem, out=out, mask=mask, shift_x=shift_x,
shift_y=shift_y, p0=p0, p1=p1)
def percentile_threshold(image, selem, out=None, mask=None, shift_x=False, shift_y=False, p0=.0, p1=1.):
"""Return greyscale local threshold of an image.
threshold is computed on the given structuring element. Only levels between percentiles [p0,p1] ,are used.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, as the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
p0, p1 : float in [0.,...,1.]
define the [p0,p1] percentile interval to be considered for computing the value.
Returns
-------
local threshold : uint8 array or uint16 array depending on input image
The result of the local threshold.
"""
return _apply(
_crank8_percentiles.threshold, _crank16_percentiles.threshold, image, selem, out=out, mask=mask,
shift_x=shift_x, shift_y=shift_y, p0=p0, p1=p1)
+703
View File
@@ -0,0 +1,703 @@
"""rank.py - rankfilter for local (custom kernel) maximum, minimum, median, mean, auto-level, equalization, etc
The local histogram is computed using a sliding window similar to the method described in
Reference: Huang, T. ,Yang, G. ; Tang, G.. "A fast two-dimensional median filtering algorithm",
IEEE Transactions on Acoustics, Speech and Signal Processing, Feb 1979. Volume: 27 , Issue: 1, Page(s): 13 - 18.
input image can be 8 bit or 16 bit with a value < 4096 (i.e. 12 bit),
for 16 bit input images, the number of histogram bins is determined from the maximum value present in the image
result image is 8 or 16 bit with respect to the input image
"""
from skimage import img_as_ubyte
import numpy as np
from skimage.filter.rank import _crank8, _crank16
from skimage.filter.rank.generic import find_bitdepth
__all__ = ['autolevel', 'bottomhat', 'equalize', 'gradient', 'maximum', 'mean', 'meansubstraction', 'median', 'minimum',
'modal', 'morph_contr_enh', 'pop', 'threshold', 'tophat','noise_filter','entropy','otsu']
def _apply(func8, func16, image, selem, out, mask, shift_x, shift_y):
selem = img_as_ubyte(selem)
if mask is not None:
mask = img_as_ubyte(mask)
if image.dtype == np.uint8:
if func8 is None:
raise TypeError("not implemented for uint8 image")
return func8(image, selem, shift_x=shift_x, shift_y=shift_y, mask=mask, out=out)
elif image.dtype == np.uint16:
if func16 is None:
raise TypeError("not implemented for uint16 image")
bitdepth = find_bitdepth(image)
if bitdepth > 11:
raise ValueError("only uint16 <4096 image (12bit) supported!")
return func16(image, selem, shift_x=shift_x, shift_y=shift_y, mask=mask, bitdepth=bitdepth + 1, out=out)
else:
raise TypeError("only uint8 and uint16 image supported!")
def autolevel(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Autolevel image using local histogram.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The result of the local autolevel.
Examples
--------
>>> from skimage import data
>>> from skimage.morphology import disk
>>> from skimage.filter.rank import autolevel
>>> # Load test image
>>> ima = data.camera()
>>> # Stretch image contrast locally
>>> auto = autolevel(ima, disk(20))
"""
return _apply(
_crank8.autolevel, _crank16.autolevel, image, selem, out=out, mask=mask, shift_x=shift_x,
shift_y=shift_y)
def bottomhat(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Returns greyscale local bottomhat of an image.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
local bottomhat : uint8 array or uint16 array depending on input image
The result of the local bottomhat.
"""
return _apply(
_crank8.bottomhat, _crank16.bottomhat, image, selem, out=out, mask=mask, shift_x=shift_x,
shift_y=shift_y)
def equalize(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Equalize image using local histogram.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The result of the local equalize.
Examples
--------
>>> from skimage import data
>>> from skimage.morphology import disk
>>> from skimage.filter.rank import equalize
>>> # Load test image
>>> ima = data.camera()
>>> # Local equalization
>>> equ = equalize(ima, disk(20))
"""
return _apply(
_crank8.equalize, _crank16.equalize, image, selem, out=out, mask=mask, shift_x=shift_x,
shift_y=shift_y)
def gradient(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Return greyscale local gradient of an image (i.e. local maximum - local minimum).
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The local gradient.
"""
return _apply(
_crank8.gradient, _crank16.gradient, image, selem, out=out, mask=mask, shift_x=shift_x,
shift_y=shift_y)
def maximum(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Return greyscale local maximum of an image.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The local maximum.
See also
--------
skimage.morphology.dilation()
Note
----
* input image can be 8 bit or 16 bit with a value < 4096 (i.e. 12 bit)
* the lower algorithm complexity makes the rank.maximum() more efficient for larger images and structuring elements
"""
return _apply(_crank8.maximum, _crank16.maximum, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y)
def mean(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Return greyscale local mean of an image.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The local mean.
Examples
--------
>>> from skimage import data
>>> from skimage.morphology import disk
>>> from skimage.filter.rank import mean
>>> # Load test image
>>> ima = data.camera()
>>> # Local mean
>>> avg = mean(ima, disk(20))
"""
return _apply(_crank8.mean, _crank16.mean, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y)
def meansubstraction(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Return image substracted from its local mean.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The result of the local meansubstraction.
"""
return _apply(
_crank8.meansubstraction, _crank16.meansubstraction, image, selem, out=out, mask=mask,
shift_x=shift_x, shift_y=shift_y)
def median(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Return greyscale local median of an image.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The local median.
Examples
--------
>>> from skimage import data
>>> from skimage.morphology import disk
>>> from skimage.filter.rank import median
>>> # Load test image
>>> ima = data.camera()
>>> # Local mean
>>> avg = median(ima, disk(20))
"""
return _apply(_crank8.median, _crank16.median, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y)
def minimum(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Return greyscale local minimum of an image.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The local minimum.
See also
--------
skimage.morphology.erosion()
Note
----
* input image can be 8 bit or 16 bit with a value < 4096 (i.e. 12 bit)
* the lower algorithm complexity makes the rank.minimum() more efficient for larger images and structuring elements
"""
return _apply(_crank8.minimum, _crank16.minimum, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y)
def modal(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Return greyscale local mode of an image.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The local modal.
"""
return _apply(_crank8.modal, _crank16.modal, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y)
def morph_contr_enh(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Enhance an image replacing each pixel by the local maximum if pixel graylevel is closest to maximimum
than local minimum OR local minimum otherwise.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The result of the local morph_contr_enh.
Examples
--------
>>> from skimage import data
>>> from skimage.morphology import disk
>>> from skimage.filter.rank import morph_contr_enh
>>> # Load test image
>>> ima = data.camera()
>>> # Local mean
>>> avg = morph_contr_enh(ima, disk(20))
"""
return _apply(
_crank8.morph_contr_enh, _crank16.morph_contr_enh, image, selem, out=out, mask=mask, shift_x=shift_x,
shift_y=shift_y)
def pop(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Return the number (population) of pixels actually inside the neighborhood.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The number of pixels belonging to the neighborhood.
Examples
--------
>>> # Local mean
>>> from skimage.morphology import square
>>> import skimage.filter.rank as rank
>>> ima = 255*np.array([[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]], dtype=np.uint8)
>>> rank.pop(ima, square(3))
array([[4, 6, 6, 6, 4],
[6, 9, 9, 9, 6],
[6, 9, 9, 9, 6],
[6, 9, 9, 9, 6],
[4, 6, 6, 6, 4]], dtype=uint8)
"""
return _apply(_crank8.pop, _crank16.pop, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y)
def threshold(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Return greyscale local threshold of an image.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The result of the local threshold.
Examples
--------
>>> # Local threshold
>>> from skimage.morphology import square
>>> from skimage.filter.rank import threshold
>>> ima = 255*np.array([[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]], dtype=np.uint8)
>>> threshold(ima, square(3))
array([[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]], dtype=uint8)
"""
return _apply(
_crank8.threshold, _crank16.threshold, image, selem, out=out, mask=mask, shift_x=shift_x,
shift_y=shift_y)
def tophat(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Return greyscale local tophat of an image.
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
The image tophat.
"""
return _apply(_crank8.tophat, _crank16.tophat, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y)
def noise_filter(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Returns the noise feature as described in [Hashimoto12]_
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's. Central element is removed during the filtering.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Reference
----------
.. [Hashimoto12] N. Hashimoto et al. Referenceless image quality evaluation for whole slide imaging. J Pathol Inform 2012;3:9.
Returns
-------
out : uint8 array or uint16 array (same as input image)
The image noise .
"""
# ensure that the central pixel in the structuring element is empty
centre_r = int(selem.shape[0] / 2) + shift_y
centre_c = int(selem.shape[1] / 2) + shift_x
# make a local copy
selem_cpy = selem.copy()
selem_cpy[centre_r,centre_c] = 0
return _apply(_crank8.noise_filter, None, image, selem_cpy, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y)
def entropy(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Returns the entropy [wiki_entropy]_ computed locally. Entropy is computed using base 2 logarithm i.e.
the filter returns the minimum number of bits needed to encode local greylevel distribution.
References
----------
.. [wiki_entropy] http://en.wikipedia.org/wiki/Entropy_(information_theory)
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
entropy x10 (uint8 images) and entropy x1000 (uint16 images)
Examples
--------
>>> # Local entropy
>>> from skimage import data
>>> from skimage.filter.rank import entropy
>>> from skimage.morphology import disk
>>> # defining a 8- and a 16-bit test images
>>> a8 = data.camera()
>>> a16 = data.camera().astype(np.uint16)*4
>>> ent8 = entropy(a8,disk(5)) # pixel value contain 10x the local entropy
>>> ent16 = entropy(a16,disk(5)) # pixel value contain 1000x the local entropy
"""
return _apply(_crank8.entropy, _crank16.entropy, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y)
def otsu(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
"""Returns the image threshold using a the Otsu [otsu]_ locally .
References
----------
.. [otsu] http://en.wikipedia.org/wiki/Otsu's_method
Parameters
----------
image : ndarray
Image array (uint8 array or uint16). If image is uint16, the algorithm uses max. 12bit histogram,
an exception will be raised if image has a value > 4095
selem : ndarray
The neighborhood expressed as a 2-D array of 1's and 0's.
out : ndarray
If None, a new array will be allocated.
mask : ndarray (uint8)
Mask array that defines (>0) area of the image included in the local neighborhood.
If None, the complete image is used (default).
shift_x, shift_y : (int)
Offset added to the structuring element center point.
Shift is bounded to the structuring element sizes (center must be inside the given structuring element).
Returns
-------
out : uint8 array or uint16 array (same as input image)
threshold image
Examples
--------
>>> # Local entropy
>>> from skimage import data
>>> from skimage.filter.rank import otsu
>>> from skimage.morphology import disk
>>> # defining a 8- and a 16-bit test images
>>> a8 = data.camera()
>>> loc_otsu = otsu(a8,disk(5))
"""
return _apply(_crank8.otsu, None, image, selem, out=out, mask=mask, shift_x=shift_x, shift_y=shift_y)
+57
View File
@@ -0,0 +1,57 @@
import sys
print sys.path
import skimage
print skimage
import unittest
import numpy as np
from skimage.filter import rank
from skimage import data
from skimage.morphology import cmorph,disk
from skimage.filter.rank import _crank8, _crank16
from skimage.filter.rank import _crank16_percentiles
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
pass
def test_trivial_selem(self):
# check that min, max and mean returns identity if structuring element contains only central pixel
a = np.zeros((5,5),dtype='uint8')
a[2,2] = 255
a[2,3] = 128
a[1,2] = 16
elem = np.asarray([[0,0,0],[0,1,0],[0,0,0]],dtype='uint8')
f = _crank8.mean(image=a,selem = elem,shift_x=0,shift_y=0)
np.testing.assert_array_equal(a,f)
f = _crank8.minimum(image=a,selem = elem,shift_x=0,shift_y=0)
np.testing.assert_array_equal(a,f)
f = _crank8.maximum(image=a,selem = elem,shift_x=0,shift_y=0)
np.testing.assert_array_equal(a,f)
def test_smallest_selem(self):
# check that min, max and mean returns identity if structuring element contains only central pixel
a = np.zeros((5,5),dtype='uint8')
a[2,2] = 255
a[2,3] = 128
a[1,2] = 16
elem = np.asarray([[1]],dtype='uint8')
f = _crank8.mean(image=a,selem = elem,shift_x=0,shift_y=0)
np.testing.assert_array_equal(a,f)
f = _crank8.minimum(image=a,selem = elem,shift_x=0,shift_y=0)
np.testing.assert_array_equal(a,f)
f = _crank8.maximum(image=a,selem = elem,shift_x=0,shift_y=0)
np.testing.assert_array_equal(a,f)
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
unittest.TextTestRunner(verbosity=2).run(suite)
+191
View File
@@ -0,0 +1,191 @@
import sys
print sys.path
import skimage
print skimage
import unittest
import numpy as np
from skimage.filter import rank
from skimage import data
from skimage.morphology import cmorph,disk
from skimage.filter.rank import _crank8, _crank16
from skimage.filter.rank import _crank16_percentiles
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
pass
def test_random_sizes(self):
# make sure the size is not a problem
niter = 10
elem = np.asarray([[1,1,1],[1,1,1],[1,1,1]],dtype='uint8')
for m,n in np.random.random_integers(1,100,size=(10,2)):
a8 = np.ones((m,n),dtype='uint8')
r = _crank8.mean(image=a8,selem = elem,shift_x=0,shift_y=0)
self.assertTrue(a8.shape == r.shape)
r = _crank8.mean(image=a8,selem = elem,shift_x=+1,shift_y=+1)
self.assertTrue(a8.shape == r.shape)
for m,n in np.random.random_integers(1,100,size=(10,2)):
a16 = np.ones((m,n),dtype='uint16')
r = _crank16.mean(image=a16,selem = elem,shift_x=0,shift_y=0)
self.assertTrue(a16.shape == r.shape)
r = _crank16.mean(image=a16,selem = elem,shift_x=+1,shift_y=+1)
self.assertTrue(a16.shape == r.shape)
for m,n in np.random.random_integers(1,100,size=(10,2)):
a16 = np.ones((m,n),dtype='uint16')
r = _crank16_percentiles.mean(image=a16,selem = elem,shift_x=0,shift_y=0,p0=.1,p1=.9)
self.assertTrue(a16.shape == r.shape)
r = _crank16_percentiles.mean(image=a16,selem = elem,shift_x=+1,shift_y=+1,p0=.1,p1=.9)
self.assertTrue(a16.shape == r.shape)
def test_compare_with_cmorph_dilate(self):
#compare the result of maximum filter with dilate
a = (np.random.random((500,500))*256).astype('uint8')
for r in range(1,20,1):
elem = np.ones((r,r),dtype='uint8')
# elem = (np.random.random((r,r))>.5).astype('uint8')
rc = _crank8.maximum(image=a,selem = elem)
cm = cmorph.dilate(image=a,selem = elem)
self.assertTrue((rc==cm).all())
def test_compare_with_cmorph_erode(self):
#compare the result of maximum filter with erode
a = (np.random.random((500,500))*256).astype('uint8')
for r in range(1,20,1):
elem = np.ones((r,r),dtype='uint8')
# elem = (np.random.random((r,r))>.5).astype('uint8')
rc = _crank8.minimum(image=a,selem = elem)
cm = cmorph.erode(image=a,selem = elem)
self.assertTrue((rc==cm).all())
def test_bitdepth(self):
# test the different bit depth for rank16
elem = np.ones((3,3),dtype='uint8')
a16 = np.ones((100,100),dtype='uint16')*255
r = _crank16_percentiles.mean(image=a16,selem = elem,shift_x=0,shift_y=0,p0=.1,p1=.9,bitdepth=8)
a16 = np.ones((100,100),dtype='uint16')*255*2
r = _crank16_percentiles.mean(image=a16,selem = elem,shift_x=0,shift_y=0,p0=.1,p1=.9,bitdepth=9)
a16 = np.ones((100,100),dtype='uint16')*255*4
r = _crank16_percentiles.mean(image=a16,selem = elem,shift_x=0,shift_y=0,p0=.1,p1=.9,bitdepth=10)
a16 = np.ones((100,100),dtype='uint16')*255*8
r = _crank16_percentiles.mean(image=a16,selem = elem,shift_x=0,shift_y=0,p0=.1,p1=.9,bitdepth=11)
a16 = np.ones((100,100),dtype='uint16')*255*16
r = _crank16_percentiles.mean(image=a16,selem = elem,shift_x=0,shift_y=0,p0=.1,p1=.9,bitdepth=12)
def test_population(self):
# check the number of valid pixels in the neighborhood
a = np.zeros((5,5),dtype='uint8')
elem = np.ones((3,3),dtype='uint8')
p = _crank8.pop(image=a,selem = elem)
r = np.asarray([[4, 6, 6, 6, 4],
[6, 9, 9, 9, 6],
[6, 9, 9, 9, 6],
[6, 9, 9, 9, 6],
[4, 6, 6, 6, 4]])
np.testing.assert_array_equal(r,p)
def test_structuring_element(self):
# check the output for a custom structuring element
a = np.zeros((6,6),dtype='uint8')
a[2,2] = 255
elem = np.asarray([[1,1,0],[1,1,1],[0,0,1]],dtype='uint8')
f = _crank8.maximum(image=a,selem = elem,shift_x=1,shift_y=1)
r = np.asarray([[ 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0],
[ 0, 0, 255, 0, 0, 0],
[ 0, 0, 255, 255, 255, 0],
[ 0, 0, 0, 255, 255, 0],
[ 0, 0, 0, 0, 0, 0]])
np.testing.assert_array_equal(r,f)
@unittest.expectedFailure
def test_fail_on_bitdepth(self):
# should fail because data bitdepth is too high for the function
a16 = np.ones((100,100),dtype='uint16')*255
elem = np.ones((3,3),dtype='uint8')
f = _crank16_percentiles.mean(image=a16,selem = elem,shift_x=0,shift_y=0,p0=.1,p1=.9,bitdepth=4)
def test_output(self):
#check rank function with external OUT output array
selem = disk(20)
a = (np.random.random((500,500))*256).astype('uint8')
out = np.zeros_like(a)
f1 = rank.mean(a,selem,out=out)
f2 = rank.mean(a,selem)
np.testing.assert_array_equal(f1,f2)
np.testing.assert_array_equal(out,f2)
@unittest.expectedFailure
def test_inplace_output(self):
#rank filters are not supposed to filter inplace
selem = disk(20)
a = (np.random.random((500,500))*256).astype('uint8')
out = a
f = rank.mean(a,selem,out=out)
np.testing.assert_array_equal(f,out)
def test_compare_autolevels(self):
# compare autolevel and percentile autolevel with p0=0.0 and p1=1.0
# should returns the same arrays
image = data.camera()
selem = disk(20)
loc_autolevel = rank.autolevel(image,selem=selem)
loc_perc_autolevel = rank.percentile_autolevel(image,selem=selem,p0=.0,p1=1.)
assert (loc_autolevel==loc_perc_autolevel).all()
def test_compare_autolevels_16bit(self):
# compare autolevel(16bit) and percentile autolevel(16bit) with p0=0.0 and p1=1.0
# should returns the same arrays
image = data.camera().astype(np.uint16)*4
selem = disk(20)
loc_autolevel = rank.autolevel(image,selem=selem)
loc_perc_autolevel = rank.percentile_autolevel(image,selem=selem,p0=.0,p1=1.)
assert (loc_autolevel==loc_perc_autolevel).all()
def test_compare_8bit_vs_16bit(self):
# filters applied on 8bit image ore 16bit image (having only real 8bit of dynamic)
# should be identical
i8 = data.camera()
i16 = i8.astype(np.uint16)
assert (i8==i16).all()
methods = ['autolevel','bottomhat','equalize','gradient','maximum','mean'
,'meansubstraction','median','minimum','modal','morph_contr_enh','pop','threshold', 'tophat']
for method in methods:
func = eval('rank.%s'%method)
f8 = func(i8,disk(3))
f16 = func(i16,disk(3))
assert (f8==f16).all()
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
unittest.TextTestRunner(verbosity=2).run(suite)
+38 -2
View File
@@ -14,11 +14,47 @@ def configuration(parent_package='', top_path=None):
cython(['_ctmf.pyx'], working_path=base_path)
cython(['_denoise.pyx'], working_path=base_path)
cython(['rank/_core8.pyx'], working_path=base_path)
cython(['rank/_core16.pyx'], working_path=base_path)
cython(['rank/_crank8.pyx'], working_path=base_path)
cython(['rank/_crank8_percentiles.pyx'], working_path=base_path)
cython(['rank/_crank16.pyx'], working_path=base_path)
cython(['rank/_crank16_percentiles.pyx'], working_path=base_path)
cython(['rank/_crank16_bilateral.pyx'], working_path=base_path)
cython(['rank/rank.pyx'], working_path=base_path)
cython(['rank/percentile_rank.pyx'], working_path=base_path)
cython(['rank/bilateral_rank.pyx'], working_path=base_path)
config.add_extension('_ctmf', sources=['_ctmf.c'],
include_dirs=[get_numpy_include_dirs()])
include_dirs=[get_numpy_include_dirs()])
config.add_extension('_denoise', sources=['_denoise.c'],
include_dirs=[get_numpy_include_dirs(), '../_shared'])
include_dirs=[get_numpy_include_dirs(), '../_shared'])
config.add_extension('rank/_core8', sources=['rank/_core8.c'],
include_dirs=[get_numpy_include_dirs()])
config.add_extension('rank/_core16', sources=['rank/_core16.c'],
include_dirs=[get_numpy_include_dirs()])
config.add_extension('rank/_crank8', sources=['rank/_crank8.c'],
include_dirs=[get_numpy_include_dirs()])
config.add_extension(
'rank/_crank8_percentiles', sources=['rank/_crank8_percentiles.c'],
include_dirs=[get_numpy_include_dirs()])
config.add_extension('rank/_crank16', sources=['rank/_crank16.c'],
include_dirs=[get_numpy_include_dirs()])
config.add_extension(
'rank/_crank16_percentiles', sources=['rank/_crank16_percentiles.c'],
include_dirs=[get_numpy_include_dirs()])
config.add_extension(
'rank/_crank16_bilateral', sources=['rank/_crank16_bilateral.c'],
include_dirs=[get_numpy_include_dirs()])
config.add_extension(
'rank/rank', sources=['rank/rank.c'],
include_dirs=[get_numpy_include_dirs()])
config.add_extension(
'rank/percentile_rank', sources=['rank/percentile_rank.c'],
include_dirs=[get_numpy_include_dirs()])
config.add_extension(
'rank/bilateral_rank', sources=['rank/bilateral_rank.c'],
include_dirs=[get_numpy_include_dirs()])
return config