[Numpy-discussion] Bilateral filter
Nadav Horesh
nadavh at visionsense.com
Wed Aug 6 07:59:11 EDT 2008
I made the following modification to the source code, I hope it is ready
to be included in scipy.
1. Added a BSD licence declaration.
2. Small optimisation.
3. The code is split into a cython back-end and a python
front-end.
All remarks are welcome,
Nadav.
On Tue, 2008-08-05 at 16:08 -0500, Travis E. Oliphant wrote:
> Nadav Horesh wrote:
> > Attached here my cython implementation of the bilateral filter, which
> > is my first cython program. I would ask for the following:
> >
> > 1. Is there any way to speed up the code just by "cosmetic"
> > modifications?
> > 2. In particular I use the unportable gcc function __builtin_abs:
> > Is there any way to access this in a portable way?
> > 3. I would like to donate this code to scipy or any other suitable
> > project. What can/should I do to realise it?
> >
> >
> > Remarks:
> >
> > The code contains 3 end-user routines:
> >
> > 1. A pure python implementation: Easy to read and modify --- it can
> > be cut out into a python source code.
> > 2. A straight forward cython implementation: About 4 times as fast
> > as the python implementation.
> > 3. An optimised cython implementation earning another factor of 2
> > in speed (depends on the parameters used).
> >
> > I see this code as a "research grade" that would evolve in the near
> > future, as I am working on a related project, and hopefully following
> > your comments.
> This would be a very welcome addition to SciPy. Thank you.
>
> -Travis
>
> _______________________________________________
> Numpy-discussion mailing list
> Numpy-discussion at scipy.org
> http://projects.scipy.org/mailman/listinfo/numpy-discussion
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/numpy-discussion/attachments/20080806/5cfe20d7/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: bilateral.py
Type: text/x-python
Size: 1803 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/numpy-discussion/attachments/20080806/5cfe20d7/attachment.py>
-------------- next part --------------
# Copyright 2008, Nadav Horesh
# nadavh at visionsense dot com
#
# The software is licenced under BSD licence.
'''
A cython implementation of the plain (and slow) algorithm for bilateral
filtering.
The class Bilat_fcn exposes methods to be called by nd_image.generic_filter
function in orde to render the actual filter.
'''
import numpy as np
cdef extern from "arrayobject.h":
ctypedef int intp
ctypedef extern class numpy.ndarray [object PyArrayObject]:
cdef char *data
cdef int nd
cdef intp *dimensions
cdef intp *strides
cdef int flags
cdef extern from "math.h":
double exp(double x)
cdef extern:
int abs(int x)
cdef int GAUSS_SAMP = 32
cdef int GAUSS_IDX_MAX = GAUSS_SAMP - 1
class Bilat_fcn(object):
'''
The class provides the bilaterl filter function to be called by
generic_filter.
initialization parameters:
spat_sig: The sigma of the spatial Gaussian filter
inten_sig: The sigma of the gray-levels Gaussian filter
filter_size: (int) The size of the spatial convolution kernel. If
not set, it is set to ~ 4*spat_sig.
'''
def __init__(self, spat_sig, inten_sig, filter_size=None):
if filter_size is not None and filter_size >= 2:
self.xy_size = int(filter_size)
else:
self.xy_size = int(round(spat_sig*4))
# Make filter size odd
self.xy_size += 1-self.xy_size%2
x = np.arange(self.xy_size, dtype=float)
x = (x-x.mean())**2
#xy_ker: Spatial convolution kernel
self.xy_ker = np.exp(-np.add.outer(x,x)/(2*spat_sig**2)).ravel()
self.xy_ker /= self.xy_ker.sum()
self.inten_sig = 2 * inten_sig**2
self.index = self.xy_size**2 // 2
## An initialization for LUT instead of a Gaussian function call
## (for the fc_filter method)
x = np.linspace(0,3.0, GAUSS_SAMP)
self.gauss_lut = np.exp(-x**2/2)
self.x_quant = 3*inten_sig / GAUSS_IDX_MAX
##
## Filtering functions
##
def __call__ (self, data):
'An unoptimized (pure python) implementation'
weight = np.exp(-(data-data[self.index])**2/self.inten_sig) * self.xy_ker
return np.dot(data, weight) / weight.sum()
def cfilter(self, ndarray data):
'An optimized implementation'
cdef ndarray kernel = self.xy_ker
cdef double sigma = self.inten_sig
cdef double weight_i, weight, result, centre, dat_i
cdef double *pdata=<double *>data.data, *pker=<double *>kernel.data
cdef int i, dim = data.dimensions[0]
centre = pdata[self.index]
weight = 0.0
result = 0.0
for i from 0 <= i < dim:
dat_i = pdata[i]
weight_i = exp(-(dat_i-centre)**2 / sigma) * pker[i]
weight += weight_i;
result += dat_i * weight_i
return result / weight
def fc_filter(self, ndarray data):
'Use further optimisation by replacing exp functions calls by a LUT'
cdef ndarray kernel = self.xy_ker
cdef ndarray gauss_lut_arr = self.gauss_lut
cdef double sigma = self.inten_sig
cdef double weight_i, weight, result, centre, dat_i
cdef double *pdata=<double *>data.data, *pker=<double *>kernel.data
cdef int i, dim = data.dimensions[0]
cdef int exp_i # Entry index for the LUT
cdef double x_quant = self.x_quant
cdef double *gauss_lut = <double *>gauss_lut_arr.data
centre = pdata[self.index]
weight = 0.0
result = 0.0
for i from 0 <= i < dim:
dat_i = pdata[i]
exp_i = abs(<int>((dat_i-centre) / x_quant))
if exp_i > GAUSS_IDX_MAX:
#exp_i = GAUSS_IDX_MAX
continue
weight_i = gauss_lut[exp_i] * pker[i]
weight += weight_i;
result += dat_i * weight_i
return result / weight
#
# Filtering functions to be called from outsize
#
More information about the NumPy-Discussion
mailing list