From 34802c8aeca50225a716ecd22880bd0758297ff0 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Tue, 11 Mar 2014 19:01:34 -0400 Subject: [PATCH] Added pg.gaussianFilter, removed all dependency on gaussian_filter --- examples/DataSlicing.py | 1 - examples/FlowchartCustomNode.py | 7 ++--- examples/GLImageItem.py | 5 ++-- examples/GLSurfacePlot.py | 5 ++-- examples/HistogramLUT.py | 3 +- examples/ImageView.py | 3 +- examples/VideoSpeedTest.py | 8 +++-- examples/crosshair.py | 5 ++-- examples/isocurve.py | 3 +- pyqtgraph/flowchart/library/Filters.py | 3 +- pyqtgraph/functions.py | 41 +++++++++++++++++++++++++- 11 files changed, 59 insertions(+), 25 deletions(-) diff --git a/examples/DataSlicing.py b/examples/DataSlicing.py index bd201832..d766e7e3 100644 --- a/examples/DataSlicing.py +++ b/examples/DataSlicing.py @@ -11,7 +11,6 @@ a 2D plane and interpolate data along that plane to generate a slice image import initExample import numpy as np -import scipy from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph as pg diff --git a/examples/FlowchartCustomNode.py b/examples/FlowchartCustomNode.py index 25ea5c77..54c56622 100644 --- a/examples/FlowchartCustomNode.py +++ b/examples/FlowchartCustomNode.py @@ -12,7 +12,6 @@ from pyqtgraph.flowchart.library.common import CtrlNode from pyqtgraph.Qt import QtGui, QtCore import pyqtgraph as pg import numpy as np -import scipy.ndimage app = QtGui.QApplication([]) @@ -44,7 +43,7 @@ win.show() ## generate random input data data = np.random.normal(size=(100,100)) -data = 25 * scipy.ndimage.gaussian_filter(data, (5,5)) +data = 25 * pg.gaussianFilter(data, (5,5)) data += np.random.normal(size=(100,100)) data[40:60, 40:60] += 15.0 data[30:50, 30:50] += 15.0 @@ -90,7 +89,7 @@ class ImageViewNode(Node): ## CtrlNode is just a convenience class that automatically creates its ## control widget based on a simple data structure. class UnsharpMaskNode(CtrlNode): - """Return the input data passed through scipy.ndimage.gaussian_filter.""" + """Return the input data passed through pg.gaussianFilter.""" nodeName = "UnsharpMask" uiTemplate = [ ('sigma', 'spin', {'value': 1.0, 'step': 1.0, 'range': [0.0, None]}), @@ -110,7 +109,7 @@ class UnsharpMaskNode(CtrlNode): # CtrlNode has created self.ctrls, which is a dict containing {ctrlName: widget} sigma = self.ctrls['sigma'].value() strength = self.ctrls['strength'].value() - output = dataIn - (strength * scipy.ndimage.gaussian_filter(dataIn, (sigma,sigma))) + output = dataIn - (strength * pg.gaussianFilter(dataIn, (sigma,sigma))) return {'dataOut': output} diff --git a/examples/GLImageItem.py b/examples/GLImageItem.py index dfdaad0c..581474fd 100644 --- a/examples/GLImageItem.py +++ b/examples/GLImageItem.py @@ -12,7 +12,6 @@ from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph.opengl as gl import pyqtgraph as pg import numpy as np -import scipy.ndimage as ndi app = QtGui.QApplication([]) w = gl.GLViewWidget() @@ -22,8 +21,8 @@ w.setWindowTitle('pyqtgraph example: GLImageItem') ## create volume data set to slice three images from shape = (100,100,70) -data = ndi.gaussian_filter(np.random.normal(size=shape), (4,4,4)) -data += ndi.gaussian_filter(np.random.normal(size=shape), (15,15,15))*15 +data = pg.gaussianFilter(np.random.normal(size=shape), (4,4,4)) +data += pg.gaussianFilter(np.random.normal(size=shape), (15,15,15))*15 ## slice out three planes, convert to RGBA for OpenGL texture levels = (-0.08, 0.08) diff --git a/examples/GLSurfacePlot.py b/examples/GLSurfacePlot.py index 963cf4cf..e9896e07 100644 --- a/examples/GLSurfacePlot.py +++ b/examples/GLSurfacePlot.py @@ -10,7 +10,6 @@ import initExample from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph as pg import pyqtgraph.opengl as gl -import scipy.ndimage as ndi import numpy as np ## Create a GL View widget to display data @@ -29,7 +28,7 @@ w.addItem(g) ## Simple surface plot example ## x, y values are not specified, so assumed to be 0:50 -z = ndi.gaussian_filter(np.random.normal(size=(50,50)), (1,1)) +z = pg.gaussianFilter(np.random.normal(size=(50,50)), (1,1)) p1 = gl.GLSurfacePlotItem(z=z, shader='shaded', color=(0.5, 0.5, 1, 1)) p1.scale(16./49., 16./49., 1.0) p1.translate(-18, 2, 0) @@ -46,7 +45,7 @@ w.addItem(p2) ## Manually specified colors -z = ndi.gaussian_filter(np.random.normal(size=(50,50)), (1,1)) +z = pg.gaussianFilter(np.random.normal(size=(50,50)), (1,1)) x = np.linspace(-12, 12, 50) y = np.linspace(-12, 12, 50) colors = np.ones((50,50,4), dtype=float) diff --git a/examples/HistogramLUT.py b/examples/HistogramLUT.py index 5d66cb5d..4d89dd3f 100644 --- a/examples/HistogramLUT.py +++ b/examples/HistogramLUT.py @@ -7,7 +7,6 @@ Use a HistogramLUTWidget to control the contrast / coloration of an image. import initExample import numpy as np -import scipy.ndimage as ndi from pyqtgraph.Qt import QtGui, QtCore import pyqtgraph as pg @@ -34,7 +33,7 @@ l.addWidget(v, 0, 0) w = pg.HistogramLUTWidget() l.addWidget(w, 0, 1) -data = ndi.gaussian_filter(np.random.normal(size=(256, 256)), (20, 20)) +data = pg.gaussianFilter(np.random.normal(size=(256, 256)), (20, 20)) for i in range(32): for j in range(32): data[i*8, j*8] += .1 diff --git a/examples/ImageView.py b/examples/ImageView.py index 44821f42..22168409 100644 --- a/examples/ImageView.py +++ b/examples/ImageView.py @@ -14,7 +14,6 @@ displaying and analyzing 2D and 3D data. ImageView provides: import initExample import numpy as np -import scipy.ndimage from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph as pg @@ -29,7 +28,7 @@ win.show() win.setWindowTitle('pyqtgraph example: ImageView') ## Create random 3D data set with noisy signals -img = scipy.ndimage.gaussian_filter(np.random.normal(size=(200, 200)), (5, 5)) * 20 + 100 +img = pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 20 + 100 img = img[np.newaxis,:,:] decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis] data = np.random.normal(size=(100, 200, 200)) diff --git a/examples/VideoSpeedTest.py b/examples/VideoSpeedTest.py index 7449c2cd..19c49107 100644 --- a/examples/VideoSpeedTest.py +++ b/examples/VideoSpeedTest.py @@ -13,7 +13,6 @@ import initExample ## Add path to library (just for examples; you do not need th from pyqtgraph.Qt import QtGui, QtCore, USE_PYSIDE import numpy as np import pyqtgraph as pg -import scipy.ndimage as ndi import pyqtgraph.ptime as ptime if USE_PYSIDE: @@ -95,10 +94,13 @@ def mkData(): if ui.rgbCheck.isChecked(): data = np.random.normal(size=(frames,width,height,3), loc=loc, scale=scale) - data = ndi.gaussian_filter(data, (0, 6, 6, 0)) + data = pg.gaussianFilter(data, (0, 6, 6, 0)) else: data = np.random.normal(size=(frames,width,height), loc=loc, scale=scale) - data = ndi.gaussian_filter(data, (0, 6, 6)) + print frames, width, height, loc, scale + data = pg.gaussianFilter(data, (0, 6, 6)) + print data[0] + pg.image(data) if dtype[0] != 'float': data = np.clip(data, 0, mx) data = data.astype(dt) diff --git a/examples/crosshair.py b/examples/crosshair.py index 67d3cc5f..076fab49 100644 --- a/examples/crosshair.py +++ b/examples/crosshair.py @@ -7,7 +7,6 @@ the mouse. import initExample ## Add path to library (just for examples; you do not need this) import numpy as np -import scipy.ndimage as ndi import pyqtgraph as pg from pyqtgraph.Qt import QtGui, QtCore from pyqtgraph.Point import Point @@ -33,8 +32,8 @@ p1.setAutoVisible(y=True) #create numpy arrays #make the numbers large to show that the xrange shows data from 10000 to all the way 0 -data1 = 10000 + 15000 * ndi.gaussian_filter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000) -data2 = 15000 + 15000 * ndi.gaussian_filter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000) +data1 = 10000 + 15000 * pg.gaussianFilter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000) +data2 = 15000 + 15000 * pg.gaussianFilter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000) p1.plot(data1, pen="r") p1.plot(data2, pen="g") diff --git a/examples/isocurve.py b/examples/isocurve.py index fa451063..b401dfe1 100644 --- a/examples/isocurve.py +++ b/examples/isocurve.py @@ -10,7 +10,6 @@ import initExample ## Add path to library (just for examples; you do not need th from pyqtgraph.Qt import QtGui, QtCore import numpy as np import pyqtgraph as pg -import scipy.ndimage as ndi app = QtGui.QApplication([]) @@ -18,7 +17,7 @@ app = QtGui.QApplication([]) frames = 200 data = np.random.normal(size=(frames,30,30), loc=0, scale=100) data = np.concatenate([data, data], axis=0) -data = ndi.gaussian_filter(data, (10, 10, 10))[frames/2:frames + frames/2] +data = pg.gaussianFilter(data, (10, 10, 10))[frames/2:frames + frames/2] data[:, 15:16, 15:17] += 1 win = pg.GraphicsWindow() diff --git a/pyqtgraph/flowchart/library/Filters.py b/pyqtgraph/flowchart/library/Filters.py index e5bb2453..b72fbca5 100644 --- a/pyqtgraph/flowchart/library/Filters.py +++ b/pyqtgraph/flowchart/library/Filters.py @@ -2,6 +2,7 @@ from ...Qt import QtCore, QtGui from ..Node import Node from . import functions +from ... import functions as pgfn from .common import * import numpy as np @@ -161,7 +162,7 @@ class Gaussian(CtrlNode): import scipy.ndimage except ImportError: raise Exception("GaussianFilter node requires the package scipy.ndimage.") - return scipy.ndimage.gaussian_filter(data, self.ctrls['sigma'].value()) + return pgfn.gaussianFilter(data, self.ctrls['sigma'].value()) class Derivative(CtrlNode): diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index 57d9a685..f76a71c9 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -1120,6 +1120,45 @@ def colorToAlpha(data, color): #raise Exception() return np.clip(output, 0, 255).astype(np.ubyte) + +def gaussianFilter(data, sigma): + """ + Drop-in replacement for scipy.ndimage.gaussian_filter. + + (note: results are only approximately equal to the output of + gaussian_filter) + """ + if np.isscalar(sigma): + sigma = (sigma,) * data.ndim + + baseline = data.mean() + filtered = data - baseline + for ax in range(data.ndim): + s = sigma[ax] + if s == 0: + continue + + # generate 1D gaussian kernel + ksize = int(s * 6) + x = np.arange(-ksize, ksize) + kernel = np.exp(-x**2 / (2*s**2)) + kshape = [1,] * data.ndim + kshape[ax] = len(kernel) + kernel = kernel.reshape(kshape) + + # convolve as product of FFTs + shape = data.shape[ax] + ksize + scale = 1.0 / (abs(s) * (2*np.pi)**0.5) + filtered = scale * np.fft.irfft(np.fft.rfft(filtered, shape, axis=ax) * + np.fft.rfft(kernel, shape, axis=ax), + axis=ax) + + # clip off extra data + sl = [slice(None)] * data.ndim + sl[ax] = slice(filtered.shape[ax]-data.shape[ax],None,None) + filtered = filtered[sl] + return filtered + baseline + def downsample(data, n, axis=0, xvals='subsample'): """Downsample by averaging points together across axis. @@ -1556,7 +1595,7 @@ def traceImage(image, values, smooth=0.5): paths = [] for i in range(diff.shape[-1]): d = (labels==i).astype(float) - d = ndi.gaussian_filter(d, (smooth, smooth)) + d = gaussianFilter(d, (smooth, smooth)) lines = isocurve(d, 0.5, connected=True, extendToEdge=True) path = QtGui.QPainterPath() for line in lines: