From 410ffd9a820f03686f0da484746b829d82d73e57 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Sat, 22 Aug 2009 15:04:27 -0700 Subject: [PATCH] Pull in LPI filters and add image reading (via PIL for now). --- scikits/image/filter/__init__.py | 2 + scikits/image/filter/lpi_filter.py | 158 ++++++++++++++++++ scikits/image/filter/tests/data/camera.png | Bin 0 -> 48793 bytes scikits/image/filter/tests/test_lpi_filter.py | 56 +++++++ scikits/image/io/__init__.py | 1 + scikits/image/io/pil_imread.py | 34 ++++ 6 files changed, 251 insertions(+) create mode 100644 scikits/image/filter/__init__.py create mode 100644 scikits/image/filter/lpi_filter.py create mode 100644 scikits/image/filter/tests/data/camera.png create mode 100644 scikits/image/filter/tests/test_lpi_filter.py create mode 100644 scikits/image/io/__init__.py create mode 100644 scikits/image/io/pil_imread.py diff --git a/scikits/image/filter/__init__.py b/scikits/image/filter/__init__.py new file mode 100644 index 00000000..4bdff030 --- /dev/null +++ b/scikits/image/filter/__init__.py @@ -0,0 +1,2 @@ +from lpi_filter import * + diff --git a/scikits/image/filter/lpi_filter.py b/scikits/image/filter/lpi_filter.py new file mode 100644 index 00000000..57564921 --- /dev/null +++ b/scikits/image/filter/lpi_filter.py @@ -0,0 +1,158 @@ +""" +:author: Stefan van der Walt, 2008 +:license: modified BSD +""" + +__all__ = ['LPIFilter2D'] +__docformat__ = 'restructuredtext en' + +import numpy as np +from scipy.fftpack import fftshift, ifftshift + +eps = np.finfo(float).eps + +class LPIFilter2D(object): + """Linear Position-Invariant Filter (2-dimensional) + + """ + def __init__(self,impulse_response,**filter_params): + """ + *Parameters*: + impulse_response : callable f(r,c,**filter_params) + Function that yields the impulse response. `r` and + `c` are 1-dimensional vectors that represent row and + column positions, in other words coordinates are + (r[0],c[0]),(r[0],c[1]) etc. `**filter_params` are + passed through. + + In other words, example would be called like this: + + r = [0,0,0,1,1,1,2,2,2] + c = [0,1,2,0,1,2,0,1,2] + impulse_response(r,c,**filter_params) + + *Example*: + + Gaussian filter: + + >>> def filt_func(r,c): + return np.exp(-np.hypot(r,c)/1) + + >>> filter = LPIFilter2D(filt_func) + + + """ + self.impulse_response = impulse_response + self.filter_params = filter_params + self._cache = None + + def _pad(self,data,shape): + """Pad the data to the given shape with zeros. + + *Parameters*: + data : 2-d ndarray + Input data + shape : (2,) tuple + + """ + out = np.zeros(shape) + out[[slice(0,n) for n in data.shape]] = data + return out + + def _prepare(self,data): + """Calculate filter and data FFT in preparation for filtering. + + """ + dshape = np.array(data.shape) + dshape += (dshape %2 == 0) # all filter dimensions must be uneven + oshape = np.array(data.shape)*2-1 + + if self._cache is None or np.any(self._cache.shape != oshape): + coords = np.mgrid[[slice(0,float(n)) for n in dshape]] + # this steps over two sets of coordinates, + # not over the coordinates individually + for k,coord in enumerate(coords): + coord -= (dshape[k]-1)/2. + coords = coords.reshape(2,-1).T # coordinate pairs (r,c) + + f = self.impulse_response(coords[:,0],coords[:,1], + **self.filter_params).reshape(dshape) + + f = self._pad(f,oshape) + F = np.dual.fftn(f) + self._cache = F + else: + F = self._cache + + data = self._pad(data,oshape) + G = np.dual.fftn(data) + + return F,G + + def _min_limit(self,x,val=eps): + mask = np.abs(x) < eps + x[mask] = np.sign(x[mask])*eps + + def _centre(self,x,oshape): + """Return an array of oshape from the centre of x. + + """ + start = (np.array(x.shape) - np.array(oshape))/2.+1 + out = x[[slice(s,s+n) for s,n in zip(start,oshape)]] + return out + + def __call__(self,data): + """Apply the filter to the given data. + + *Parameters*: + data : (M,N) ndarray + + """ + F,G = self._prepare(data) + out = np.dual.ifftn(F*G) + out = np.abs(self._centre(out,data.shape)) + return out + + def inverse(self,data,max_gain=2): + """Apply the filter in reverse to the given data. + + *Parameters*: + data : (M,N) ndarray + Input data. + max_gain : float + Limit the filter gain. Often, the filter contains + zeros, which would cause the inverse filter to have + infinite gain. High gain causes amplification of + artefacts, so a conservative limit is recommended. + + """ + F,G = self._prepare(data) + self._min_limit(F) + + F = 1/F + mask = np.abs(F) > max_gain + F[mask] = np.sign(F[mask])*max_gain + + return self._centre(np.abs(ifftshift(np.dual.ifftn(G*F))),data.shape) + + def wiener(self,data,K=0.25): + """Minimum Mean Square Error (Wiener) inverse filter. + + *Parameters*: + data : (M,N) ndarray + Input data. + K : float or (M,N) ndarray + Ratio between power spectrum of noise and undegraded + image. + + """ + F,G = self._prepare(data) + self._min_limit(F) + + H_mag_sqr = np.abs(F)**2 + F = 1/F * H_mag_sqr / (H_mag_sqr + K) + + return self._centre(np.abs(ifftshift(np.dual.ifftn(G*F))),data.shape) + + def constrained_least_squares(self,data,lam): + pass diff --git a/scikits/image/filter/tests/data/camera.png b/scikits/image/filter/tests/data/camera.png new file mode 100644 index 0000000000000000000000000000000000000000..a196b8355ca0f2e001f697b37d4fad7f560ee5b6 GIT binary patch literal 48793 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

cS*Dv8ah)QD9kMWbGi5 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 XhPS|DuqW{R00000NkvXXu0mjfjJa9? literal 0 HcmV?d00001 diff --git a/scikits/image/filter/tests/test_lpi_filter.py b/scikits/image/filter/tests/test_lpi_filter.py new file mode 100644 index 00000000..6cb804f9 --- /dev/null +++ b/scikits/image/filter/tests/test_lpi_filter.py @@ -0,0 +1,56 @@ +import os.path + +import numpy as np +from numpy.testing import * + +from scikits.image.io import * +from scikits.image.filter import * + +data_dir = os.path.join(os.path.dirname(__file__), './data/') + +class TestLPIFilter2D(): + img = imread(os.path.join(data_dir + 'camera.png'), + flatten=True)[:-101,:-100] + + def filt_func(self,r,c): + return np.exp(-np.hypot(r,c)/1) + + def setUp(self): + self.f = LPIFilter2D(self.filt_func) + + def tst_shape(self, x): + X = self.f(x) + assert_equal(X.shape,x.shape) + + def test_ip_shape(self): + rows,columns = self.img.shape[:2] + + for c_slice in [slice(0,columns),slice(0,columns-5), + slice(0,columns-100)]: + yield (self.tst_shape,self.img[:,c_slice]) + + def test_inverse(self): + F = self.f(self.img) + g = self.f.inverse(F) + assert_equal(g.shape,self.img.shape) + + g1 = self.f.inverse(F[::-1,::-1]) + assert ((g-g1[::-1,::-1]).sum() < 55) + + # test cache + g1 = self.f.inverse(F[::-1,::-1]) + assert ((g-g1[::-1,::-1]).sum() < 55) + + + def test_wiener(self): + F = self.f(self.img) + g = self.f.wiener(F) + assert_equal(g.shape,self.img.shape) + + g1 = self.f.wiener(F[::-1,::-1]) + assert ((g-g1[::-1,::-1]).sum() < 1) + + +if __name__ == "__main__": + run_module_suite() + diff --git a/scikits/image/io/__init__.py b/scikits/image/io/__init__.py new file mode 100644 index 00000000..0d5394a5 --- /dev/null +++ b/scikits/image/io/__init__.py @@ -0,0 +1 @@ +from pil_imread import * diff --git a/scikits/image/io/pil_imread.py b/scikits/image/io/pil_imread.py new file mode 100644 index 00000000..d57348a8 --- /dev/null +++ b/scikits/image/io/pil_imread.py @@ -0,0 +1,34 @@ +__all__ = ['imread'] + +import numpy as np + +def imread(fname, flatten=False): + """Load an image from file. + + Parameters + ---------- + fname : string + Image file name, e.g. ``test.jpg``. + flatten : bool + If true, convert the output to grey-scale. + + Returns + ------- + img_array : ndarray + The different colour bands/channels are stored in the + third dimension, such that a grey-image is MxN, an + RGB-image MxNx3 and an RGBA-image MxNx4. + + """ + try: + from PIL import Image + except ImportError: + raise ImportError("Could not import the Python Imaging Library (PIL)" + " required to load image files. Please refer to" + " http://pypi.python.org/pypi/PIL/ for installation" + " instructions.") + + im = Image.open(fname) + if flatten: + im = im.convert('F') + return np.array(im)