All scipy imports in the library are now optional (need to test each of these changes)

Several examples still require scipy.
This commit is contained in:
Luke Campagnola 2013-11-23 14:35:09 -05:00
parent 00418e4921
commit 816069c020
8 changed files with 68 additions and 93 deletions

View File

@ -14,7 +14,6 @@ import initExample
## This example uses a ViewBox to create a PlotWidget-like interface ## This example uses a ViewBox to create a PlotWidget-like interface
#from scipy import random
import numpy as np import numpy as np
from pyqtgraph.Qt import QtGui, QtCore from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg import pyqtgraph as pg

View File

@ -4,7 +4,6 @@ from .Vector import Vector
from .Transform3D import Transform3D from .Transform3D import Transform3D
from .Vector import Vector from .Vector import Vector
import numpy as np import numpy as np
import scipy.linalg
class SRTTransform3D(Transform3D): class SRTTransform3D(Transform3D):
"""4x4 Transform matrix that can always be represented as a combination of 3 matrices: scale * rotate * translate """4x4 Transform matrix that can always be represented as a combination of 3 matrices: scale * rotate * translate
@ -118,6 +117,7 @@ class SRTTransform3D(Transform3D):
The input matrix must be affine AND have no shear, The input matrix must be affine AND have no shear,
otherwise the conversion will most likely fail. otherwise the conversion will most likely fail.
""" """
import numpy.linalg
for i in range(4): for i in range(4):
self.setRow(i, m.row(i)) self.setRow(i, m.row(i))
m = self.matrix().reshape(4,4) m = self.matrix().reshape(4,4)
@ -134,7 +134,7 @@ class SRTTransform3D(Transform3D):
## rotation axis is the eigenvector with eigenvalue=1 ## rotation axis is the eigenvector with eigenvalue=1
r = m[:3, :3] / scale[:, np.newaxis] r = m[:3, :3] / scale[:, np.newaxis]
try: try:
evals, evecs = scipy.linalg.eig(r) evals, evecs = numpy.linalg.eig(r)
except: except:
print("Rotation matrix: %s" % str(r)) print("Rotation matrix: %s" % str(r))
print("Scale: %s" % str(scale)) print("Scale: %s" % str(scale))

View File

@ -1,5 +1,4 @@
import numpy as np import numpy as np
import scipy.interpolate
from .Qt import QtGui, QtCore from .Qt import QtGui, QtCore
class ColorMap(object): class ColorMap(object):
@ -84,6 +83,11 @@ class ColorMap(object):
qcolor Values are returned as an array of QColor objects. qcolor Values are returned as an array of QColor objects.
=========== =============================================================== =========== ===============================================================
""" """
try:
import scipy.interpolate
except:
raise Exception("Colormap.map() requires the package scipy.interpolate, but it could not be imported.")
if isinstance(mode, basestring): if isinstance(mode, basestring):
mode = self.enumMap[mode.lower()] mode = self.enumMap[mode.lower()]

View File

@ -1,9 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ...Qt import QtCore, QtGui from ...Qt import QtCore, QtGui
from ..Node import Node from ..Node import Node
from scipy.signal import detrend
from scipy.ndimage import median_filter, gaussian_filter
#from ...SignalProxy import SignalProxy
from . import functions from . import functions
from .common import * from .common import *
import numpy as np import numpy as np
@ -119,7 +116,11 @@ class Median(CtrlNode):
@metaArrayWrapper @metaArrayWrapper
def processData(self, data): def processData(self, data):
return median_filter(data, self.ctrls['n'].value()) try:
import scipy.ndimage
except ImportError:
raise Exception("MedianFilter node requires the package scipy.ndimage.")
return scipy.ndimage.median_filter(data, self.ctrls['n'].value())
class Mode(CtrlNode): class Mode(CtrlNode):
"""Filters data by taking the mode (histogram-based) of a sliding window""" """Filters data by taking the mode (histogram-based) of a sliding window"""
@ -156,7 +157,11 @@ class Gaussian(CtrlNode):
@metaArrayWrapper @metaArrayWrapper
def processData(self, data): def processData(self, data):
return gaussian_filter(data, self.ctrls['sigma'].value()) try:
import scipy.ndimage
except ImportError:
raise Exception("GaussianFilter node requires the package scipy.ndimage.")
return scipy.ndimage.gaussian_filter(data, self.ctrls['sigma'].value())
class Derivative(CtrlNode): class Derivative(CtrlNode):
@ -189,6 +194,10 @@ class Detrend(CtrlNode):
@metaArrayWrapper @metaArrayWrapper
def processData(self, data): def processData(self, data):
try:
from scipy.signal import detrend
except ImportError:
raise Exception("DetrendFilter node requires the package scipy.signal.")
return detrend(data) return detrend(data)

View File

@ -1,4 +1,3 @@
import scipy
import numpy as np import numpy as np
from ...metaarray import MetaArray from ...metaarray import MetaArray
@ -47,6 +46,11 @@ def downsample(data, n, axis=0, xvals='subsample'):
def applyFilter(data, b, a, padding=100, bidir=True): def applyFilter(data, b, a, padding=100, bidir=True):
"""Apply a linear filter with coefficients a, b. Optionally pad the data before filtering """Apply a linear filter with coefficients a, b. Optionally pad the data before filtering
and/or run the filter in both directions.""" and/or run the filter in both directions."""
try:
import scipy.signal
except ImportError:
raise Exception("applyFilter() requires the package scipy.signal.")
d1 = data.view(np.ndarray) d1 = data.view(np.ndarray)
if padding > 0: if padding > 0:
@ -67,6 +71,11 @@ def applyFilter(data, b, a, padding=100, bidir=True):
def besselFilter(data, cutoff, order=1, dt=None, btype='low', bidir=True): def besselFilter(data, cutoff, order=1, dt=None, btype='low', bidir=True):
"""return data passed through bessel filter""" """return data passed through bessel filter"""
try:
import scipy.signal
except ImportError:
raise Exception("besselFilter() requires the package scipy.signal.")
if dt is None: if dt is None:
try: try:
tvals = data.xvals('Time') tvals = data.xvals('Time')
@ -85,6 +94,11 @@ def besselFilter(data, cutoff, order=1, dt=None, btype='low', bidir=True):
def butterworthFilter(data, wPass, wStop=None, gPass=2.0, gStop=20.0, order=1, dt=None, btype='low', bidir=True): def butterworthFilter(data, wPass, wStop=None, gPass=2.0, gStop=20.0, order=1, dt=None, btype='low', bidir=True):
"""return data passed through bessel filter""" """return data passed through bessel filter"""
try:
import scipy.signal
except ImportError:
raise Exception("butterworthFilter() requires the package scipy.signal.")
if dt is None: if dt is None:
try: try:
tvals = data.xvals('Time') tvals = data.xvals('Time')
@ -175,6 +189,11 @@ def denoise(data, radius=2, threshold=4):
def adaptiveDetrend(data, x=None, threshold=3.0): def adaptiveDetrend(data, x=None, threshold=3.0):
"""Return the signal with baseline removed. Discards outliers from baseline measurement.""" """Return the signal with baseline removed. Discards outliers from baseline measurement."""
try:
import scipy.signal
except ImportError:
raise Exception("adaptiveDetrend() requires the package scipy.signal.")
if x is None: if x is None:
x = data.xvals(0) x = data.xvals(0)

View File

@ -34,17 +34,6 @@ import decimal, re
import ctypes import ctypes
import sys, struct import sys, struct
try:
import scipy.ndimage
HAVE_SCIPY = True
if getConfigOption('useWeave'):
try:
import scipy.weave
except ImportError:
setConfigOptions(useWeave=False)
except ImportError:
HAVE_SCIPY = False
from . import debug from . import debug
def siScale(x, minVal=1e-25, allowUnicode=True): def siScale(x, minVal=1e-25, allowUnicode=True):
@ -422,7 +411,9 @@ def affineSlice(data, shape, origin, vectors, axes, order=1, returnCoords=False,
affineSlice(data, shape=(20,20), origin=(40,0,0), vectors=((-1, 1, 0), (-1, 0, 1)), axes=(1,2,3)) affineSlice(data, shape=(20,20), origin=(40,0,0), vectors=((-1, 1, 0), (-1, 0, 1)), axes=(1,2,3))
""" """
if not HAVE_SCIPY: try:
import scipy.ndimage
except ImportError:
raise Exception("This function requires the scipy library, but it does not appear to be importable.") raise Exception("This function requires the scipy library, but it does not appear to be importable.")
# sanity check # sanity check
@ -579,15 +570,14 @@ def solve3DTransform(points1, points2):
Find a 3D transformation matrix that maps points1 onto points2. Find a 3D transformation matrix that maps points1 onto points2.
Points must be specified as a list of 4 Vectors. Points must be specified as a list of 4 Vectors.
""" """
if not HAVE_SCIPY: import numpy.linalg
raise Exception("This function depends on the scipy library, but it does not appear to be importable.")
A = np.array([[points1[i].x(), points1[i].y(), points1[i].z(), 1] for i in range(4)]) A = np.array([[points1[i].x(), points1[i].y(), points1[i].z(), 1] for i in range(4)])
B = np.array([[points2[i].x(), points2[i].y(), points2[i].z(), 1] for i in range(4)]) B = np.array([[points2[i].x(), points2[i].y(), points2[i].z(), 1] for i in range(4)])
## solve 3 sets of linear equations to determine transformation matrix elements ## solve 3 sets of linear equations to determine transformation matrix elements
matrix = np.zeros((4,4)) matrix = np.zeros((4,4))
for i in range(3): for i in range(3):
matrix[i] = scipy.linalg.solve(A, B[:,i]) ## solve Ax = B; x is one row of the desired transformation matrix matrix[i] = numpy.linalg.solve(A, B[:,i]) ## solve Ax = B; x is one row of the desired transformation matrix
return matrix return matrix
@ -600,8 +590,7 @@ def solveBilinearTransform(points1, points2):
mapped = np.dot(matrix, [x*y, x, y, 1]) mapped = np.dot(matrix, [x*y, x, y, 1])
""" """
if not HAVE_SCIPY: import numpy.linalg
raise Exception("This function depends on the scipy library, but it does not appear to be importable.")
## A is 4 rows (points) x 4 columns (xy, x, y, 1) ## A is 4 rows (points) x 4 columns (xy, x, y, 1)
## B is 4 rows (points) x 2 columns (x, y) ## B is 4 rows (points) x 2 columns (x, y)
A = np.array([[points1[i].x()*points1[i].y(), points1[i].x(), points1[i].y(), 1] for i in range(4)]) A = np.array([[points1[i].x()*points1[i].y(), points1[i].x(), points1[i].y(), 1] for i in range(4)])
@ -610,7 +599,7 @@ def solveBilinearTransform(points1, points2):
## solve 2 sets of linear equations to determine transformation matrix elements ## solve 2 sets of linear equations to determine transformation matrix elements
matrix = np.zeros((2,4)) matrix = np.zeros((2,4))
for i in range(2): for i in range(2):
matrix[i] = scipy.linalg.solve(A, B[:,i]) ## solve Ax = B; x is one row of the desired transformation matrix matrix[i] = numpy.linalg.solve(A, B[:,i]) ## solve Ax = B; x is one row of the desired transformation matrix
return matrix return matrix
@ -629,6 +618,10 @@ def rescaleData(data, scale, offset, dtype=None):
try: try:
if not getConfigOption('useWeave'): if not getConfigOption('useWeave'):
raise Exception('Weave is disabled; falling back to slower version.') raise Exception('Weave is disabled; falling back to slower version.')
try:
import scipy.weave
except ImportError:
raise Exception('scipy.weave is not importable; falling back to slower version.')
## require native dtype when using weave ## require native dtype when using weave
if not data.dtype.isnative: if not data.dtype.isnative:
@ -671,68 +664,13 @@ def applyLookupTable(data, lut):
Uses values in *data* as indexes to select values from *lut*. Uses values in *data* as indexes to select values from *lut*.
The returned data has shape data.shape + lut.shape[1:] The returned data has shape data.shape + lut.shape[1:]
Uses scipy.weave to improve performance if it is available.
Note: color gradient lookup tables can be generated using GradientWidget. Note: color gradient lookup tables can be generated using GradientWidget.
""" """
if data.dtype.kind not in ('i', 'u'): if data.dtype.kind not in ('i', 'u'):
data = data.astype(int) data = data.astype(int)
## using np.take appears to be faster than even the scipy.weave method and takes care of clipping as well.
return np.take(lut, data, axis=0, mode='clip') return np.take(lut, data, axis=0, mode='clip')
### old methods:
#data = np.clip(data, 0, lut.shape[0]-1)
#try:
#if not USE_WEAVE:
#raise Exception('Weave is disabled; falling back to slower version.')
### number of values to copy for each LUT lookup
#if lut.ndim == 1:
#ncol = 1
#else:
#ncol = sum(lut.shape[1:])
### output array
#newData = np.empty((data.size, ncol), dtype=lut.dtype)
### flattened input arrays
#flatData = data.flatten()
#flatLut = lut.reshape((lut.shape[0], ncol))
#dataSize = data.size
### strides for accessing each item
#newStride = newData.strides[0] / newData.dtype.itemsize
#lutStride = flatLut.strides[0] / flatLut.dtype.itemsize
#dataStride = flatData.strides[0] / flatData.dtype.itemsize
### strides for accessing individual values within a single LUT lookup
#newColStride = newData.strides[1] / newData.dtype.itemsize
#lutColStride = flatLut.strides[1] / flatLut.dtype.itemsize
#code = """
#for( int i=0; i<dataSize; i++ ) {
#for( int j=0; j<ncol; j++ ) {
#newData[i*newStride + j*newColStride] = flatLut[flatData[i*dataStride]*lutStride + j*lutColStride];
#}
#}
#"""
#scipy.weave.inline(code, ['flatData', 'flatLut', 'newData', 'dataSize', 'ncol', 'newStride', 'lutStride', 'dataStride', 'newColStride', 'lutColStride'])
#newData = newData.reshape(data.shape + lut.shape[1:])
##if np.any(newData != lut[data]):
##print "mismatch!"
#data = newData
#except:
#if USE_WEAVE:
#debug.printExc("Error; disabling weave.")
#USE_WEAVE = False
#data = lut[data]
#return data
def makeRGBA(*args, **kwds): def makeRGBA(*args, **kwds):
"""Equivalent to makeARGB(..., useRGBA=True)""" """Equivalent to makeARGB(..., useRGBA=True)"""
@ -1473,7 +1411,11 @@ def traceImage(image, values, smooth=0.5):
If image is RGB or RGBA, then the shape of values should be (nvals, 3/4) If image is RGB or RGBA, then the shape of values should be (nvals, 3/4)
The parameter *smooth* is expressed in pixels. The parameter *smooth* is expressed in pixels.
""" """
try:
import scipy.ndimage as ndi import scipy.ndimage as ndi
except ImportError:
raise Exception("traceImage() requires the package scipy.ndimage, but it is not importable.")
if values.ndim == 2: if values.ndim == 2:
values = values.T values = values.T
values = values[np.newaxis, np.newaxis, ...].astype(float) values = values[np.newaxis, np.newaxis, ...].astype(float)
@ -1967,14 +1909,16 @@ def invertQTransform(tr):
bugs in that method. (specifically, Qt has floating-point precision issues bugs in that method. (specifically, Qt has floating-point precision issues
when determining whether a matrix is invertible) when determining whether a matrix is invertible)
""" """
if not HAVE_SCIPY: try:
import numpy.linalg
arr = np.array([[tr.m11(), tr.m12(), tr.m13()], [tr.m21(), tr.m22(), tr.m23()], [tr.m31(), tr.m32(), tr.m33()]])
inv = numpy.linalg.inv(arr)
return QtGui.QTransform(inv[0,0], inv[0,1], inv[0,2], inv[1,0], inv[1,1], inv[1,2], inv[2,0], inv[2,1])
except ImportError:
inv = tr.inverted() inv = tr.inverted()
if inv[1] is False: if inv[1] is False:
raise Exception("Transform is not invertible.") raise Exception("Transform is not invertible.")
return inv[0] return inv[0]
arr = np.array([[tr.m11(), tr.m12(), tr.m13()], [tr.m21(), tr.m22(), tr.m23()], [tr.m31(), tr.m32(), tr.m33()]])
inv = scipy.linalg.inv(arr)
return QtGui.QTransform(inv[0,0], inv[0,1], inv[0,2], inv[1,0], inv[1,1], inv[1,2], inv[2,0], inv[2,1])
def pseudoScatter(data, spacing=None, shuffle=True, bidir=False): def pseudoScatter(data, spacing=None, shuffle=True, bidir=False):

View File

@ -655,7 +655,10 @@ class PlotDataItem(GraphicsObject):
dx = np.diff(x) dx = np.diff(x)
uniform = not np.any(np.abs(dx-dx[0]) > (abs(dx[0]) / 1000.)) uniform = not np.any(np.abs(dx-dx[0]) > (abs(dx[0]) / 1000.))
if not uniform: if not uniform:
try:
import scipy.interpolate as interp import scipy.interpolate as interp
except:
raise Exception('Fourier transform of irregularly-sampled data requires the package scipy.interpolate.')
x2 = np.linspace(x[0], x[-1], len(x)) x2 = np.linspace(x[0], x[-1], len(x))
y = interp.griddata(x, y, x2, method='linear') y = interp.griddata(x, y, x2, method='linear')
x = x2 x = x2

View File

@ -13,11 +13,8 @@ of how to build an ROI at the bottom of the file.
""" """
from ..Qt import QtCore, QtGui from ..Qt import QtCore, QtGui
#if not hasattr(QtCore, 'Signal'):
#QtCore.Signal = QtCore.pyqtSignal
import numpy as np import numpy as np
from numpy.linalg import norm #from numpy.linalg import norm
import scipy.ndimage as ndimage
from ..Point import * from ..Point import *
from ..SRTTransform import SRTTransform from ..SRTTransform import SRTTransform
from math import cos, sin from math import cos, sin