[SciPy-User] Gaussian Filter
Sturla Molden
sturla at molden.no
Sat Sep 5 21:59:18 EDT 2009
Brian Thorne skrev:
> But I am curious as to what causes the edges to do that in the IIR
> filter version?
>
> I made a pretty (IMHO) plot of the diff image seperating each channel:
> http://1.bp.blogspot.com/_lewp47C9PZI/SqJSUG7MJEI/AAAAAAAAAgs/s8aSE0FBCwI/s1600-h/lena_diff_ndfilt_iir.png
In your code you save to JPEG (lossy compression) before taking the
difference. Don't do that, you incur some error from the compression.
Mind you that neither filter are 'correct'. The Gaussian filters in
ndimage and OpenCV truncate the Gaussian, my version use a recursive
approximation. The Gaussian filter in ndimage and OpenCV have larger
truncation errors near the edges: As the kernel overlaps with an edge,
the kernel is truncated further and truncation error increase. Thus, you
were looking at the discreapancy between two approximations. You cannot
attribute the edge difference to error in the IIR approximation from
this result.
First, it seems it helps to pad with some zeros (I forgot to do that,
sorry!), e.g. setting padding=3*sigma.
def gaussian2D(image, sigma, padding=None):
if padding is None: padding = 3*sigma
n,m = image.shape[0],image.shape[1]
tmp = zeros((n + 2*padding, m + 2*padding))
if tmp.shape[0] < 4: raise ValueError, \
'Image and padding too small'
if tmp.shape[1] < 4: raise ValueError, \
'Image and padding too small'
B,A = __gausscoeff(sigma)
tmp[padding:n+padding,padding:m+padding] = image
tmp = lfilter(B, A, tmp, axis=0)
tmp = flipud(tmp)
tmp = lfilter(B, A, tmp, axis=0)
tmp = flipud(tmp)
tmp = lfilter(B, A, tmp, axis=1)
tmp = fliplr(tmp)
tmp = lfilter(B, A, tmp, axis=1)
tmp = fliplr(tmp)
return tmp[padding:n+padding,padding:m+padding]
Now I get a maximum illumination difference of 9 (range 0-255) between
ndimage and IIR. Another image (Forest.jpg, sample image in Windows
Vista) gave a maximum of 5. The biggest discrepancy was near the edges
here as well.
There is still discrepancies at the edges. Who are the culprit?
We can easily find 'facit' using an FFT. By truncating at 3 standard
deviations, the truncation error from FFT convolution will be very small:
def gaussian2D_FFT(image, sigma, padding=None):
if padding is None: padding = 3*sigma
n,m = image.shape[0],image.shape[1]
tmp = zeros((n + 2*padding, m + 2*padding))
if tmp.shape[0] < 4: raise ValueError, \
'Image and padding too small'
if tmp.shape[1] < 4: raise ValueError, \
'Image and padding too small'
tmp[:n,:m] = image
x,y = meshgrid(range(tmp.shape[1]),range(tmp.shape[0]))
B = exp(-(((x-padding)**2) + ((y-padding)**2))/(2.*sigma*sigma))
B[:] *= 1./B.sum()
retv = irfft2(rfft2(B)*rfft2(tmp))
return retv[padding:n+padding,padding:m+padding]
This FFT-based filter also have loss of gain near the edges due to
padding, so we adjust it accordingly:
img = np.fromstring(Image.open('lenna.png').tostring(), dtype=np.uint8)\
.reshape((512,512,3)).astype(float)
gain = gaussian2D_FFT(np.ones(img.shape[:2]), 10)
for i in range(3):
img[:,:,i] = gaussian2D_FFT(img[:,:,i], 10) / (gain + 1E-100)
Now, comparing what we get from scipy.ndimage.filters.gaussian_filter
and IIR with the FFT 'facit' (very small truncation error) :
* IIR vs. FFT: maximum illumination difference: 3
* ndimage vs. FFT: maximum illumination difference: 9 (close to edges)
* ndimage vs. IIR: maximum illumination difference: 9 (close to edges)
Which in fact means that the IIR approximation is more accurate than
OpenCV and ndimage's Gaussian filter! The edge difference in fact comes
from truncation error in ndimage and OpenVC, not from the IIR being
inaccurate. It is due to increased truncation error near the edges.
I have prepared similar images like yours:
IIR vs. FFT: http://hostdump.com/images/iirvsfft.png
ndimage vs. FFT: http://hostdump.com/images/ndarrayvsf.png
ndimage vs. IIR: http://hostdump.com/images/ndarrayvsi.png
Thus, the IIR is the better approximation to the Gaussian filter.
Regards,
Sturla Molden
More information about the SciPy-User
mailing list