Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Code adapted from "upfirdn" python library with permission: 

2# 

3# Copyright (c) 2009, Motorola, Inc 

4# 

5# All Rights Reserved. 

6# 

7# Redistribution and use in source and binary forms, with or without 

8# modification, are permitted provided that the following conditions are 

9# met: 

10# 

11# * Redistributions of source code must retain the above copyright notice, 

12# this list of conditions and the following disclaimer. 

13# 

14# * Redistributions in binary form must reproduce the above copyright 

15# notice, this list of conditions and the following disclaimer in the 

16# documentation and/or other materials provided with the distribution. 

17# 

18# * Neither the name of Motorola nor the names of its contributors may be 

19# used to endorse or promote products derived from this software without 

20# specific prior written permission. 

21# 

22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 

23# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 

24# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 

25# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 

26# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 

27# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 

28# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 

29# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 

30# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 

31# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 

32# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

33 

34import numpy as np 

35 

36from ._upfirdn_apply import _output_len, _apply, mode_enum 

37 

38__all__ = ['upfirdn', '_output_len'] 

39 

40_upfirdn_modes = [ 

41 'constant', 'wrap', 'edge', 'smooth', 'symmetric', 'reflect', 

42 'antisymmetric', 'antireflect', 'line', 

43] 

44 

45 

46def _pad_h(h, up): 

47 """Store coefficients in a transposed, flipped arrangement. 

48 

49 For example, suppose upRate is 3, and the 

50 input number of coefficients is 10, represented as h[0], ..., h[9]. 

51 

52 Then the internal buffer will look like this:: 

53 

54 h[9], h[6], h[3], h[0], // flipped phase 0 coefs 

55 0, h[7], h[4], h[1], // flipped phase 1 coefs (zero-padded) 

56 0, h[8], h[5], h[2], // flipped phase 2 coefs (zero-padded) 

57 

58 """ 

59 h_padlen = len(h) + (-len(h) % up) 

60 h_full = np.zeros(h_padlen, h.dtype) 

61 h_full[:len(h)] = h 

62 h_full = h_full.reshape(-1, up).T[:, ::-1].ravel() 

63 return h_full 

64 

65 

66def _check_mode(mode): 

67 mode = mode.lower() 

68 enum = mode_enum(mode) 

69 return enum 

70 

71 

72class _UpFIRDn(object): 

73 """Helper for resampling.""" 

74 

75 def __init__(self, h, x_dtype, up, down): 

76 h = np.asarray(h) 

77 if h.ndim != 1 or h.size == 0: 

78 raise ValueError('h must be 1-D with non-zero length') 

79 self._output_type = np.result_type(h.dtype, x_dtype, np.float32) 

80 h = np.asarray(h, self._output_type) 

81 self._up = int(up) 

82 self._down = int(down) 

83 if self._up < 1 or self._down < 1: 

84 raise ValueError('Both up and down must be >= 1') 

85 # This both transposes, and "flips" each phase for filtering 

86 self._h_trans_flip = _pad_h(h, self._up) 

87 self._h_trans_flip = np.ascontiguousarray(self._h_trans_flip) 

88 self._h_len_orig = len(h) 

89 

90 def apply_filter(self, x, axis=-1, mode='constant', cval=0): 

91 """Apply the prepared filter to the specified axis of N-D signal x.""" 

92 output_len = _output_len(self._h_len_orig, x.shape[axis], 

93 self._up, self._down) 

94 # Explicit use of np.int64 for output_shape dtype avoids OverflowError 

95 # when allocating large array on platforms where np.int_ is 32 bits 

96 output_shape = np.asarray(x.shape, dtype=np.int64) 

97 output_shape[axis] = output_len 

98 out = np.zeros(output_shape, dtype=self._output_type, order='C') 

99 axis = axis % x.ndim 

100 mode = _check_mode(mode) 

101 _apply(np.asarray(x, self._output_type), 

102 self._h_trans_flip, out, 

103 self._up, self._down, axis, mode, cval) 

104 return out 

105 

106 

107def upfirdn(h, x, up=1, down=1, axis=-1, mode='constant', cval=0): 

108 """Upsample, FIR filter, and downsample. 

109 

110 Parameters 

111 ---------- 

112 h : array_like 

113 1-D FIR (finite-impulse response) filter coefficients. 

114 x : array_like 

115 Input signal array. 

116 up : int, optional 

117 Upsampling rate. Default is 1. 

118 down : int, optional 

119 Downsampling rate. Default is 1. 

120 axis : int, optional 

121 The axis of the input data array along which to apply the 

122 linear filter. The filter is applied to each subarray along 

123 this axis. Default is -1. 

124 mode : str, optional 

125 The signal extension mode to use. The set 

126 ``{"constant", "symmetric", "reflect", "edge", "wrap"}`` correspond to 

127 modes provided by `numpy.pad`. ``"smooth"`` implements a smooth 

128 extension by extending based on the slope of the last 2 points at each 

129 end of the array. ``"antireflect"`` and ``"antisymmetric"`` are 

130 anti-symmetric versions of ``"reflect"`` and ``"symmetric"``. The mode 

131 `"line"` extends the signal based on a linear trend defined by the 

132 first and last points along the ``axis``. 

133 

134 .. versionadded:: 1.4.0 

135 cval : float, optional 

136 The constant value to use when ``mode == "constant"``. 

137 

138 .. versionadded:: 1.4.0 

139 

140 Returns 

141 ------- 

142 y : ndarray 

143 The output signal array. Dimensions will be the same as `x` except 

144 for along `axis`, which will change size according to the `h`, 

145 `up`, and `down` parameters. 

146 

147 Notes 

148 ----- 

149 The algorithm is an implementation of the block diagram shown on page 129 

150 of the Vaidyanathan text [1]_ (Figure 4.3-8d). 

151 

152 The direct approach of upsampling by factor of P with zero insertion, 

153 FIR filtering of length ``N``, and downsampling by factor of Q is 

154 O(N*Q) per output sample. The polyphase implementation used here is 

155 O(N/P). 

156 

157 .. versionadded:: 0.18 

158 

159 References 

160 ---------- 

161 .. [1] P. P. Vaidyanathan, Multirate Systems and Filter Banks, 

162 Prentice Hall, 1993. 

163 

164 Examples 

165 -------- 

166 Simple operations: 

167 

168 >>> from scipy.signal import upfirdn 

169 >>> upfirdn([1, 1, 1], [1, 1, 1]) # FIR filter 

170 array([ 1., 2., 3., 2., 1.]) 

171 >>> upfirdn([1], [1, 2, 3], 3) # upsampling with zeros insertion 

172 array([ 1., 0., 0., 2., 0., 0., 3., 0., 0.]) 

173 >>> upfirdn([1, 1, 1], [1, 2, 3], 3) # upsampling with sample-and-hold 

174 array([ 1., 1., 1., 2., 2., 2., 3., 3., 3.]) 

175 >>> upfirdn([.5, 1, .5], [1, 1, 1], 2) # linear interpolation 

176 array([ 0.5, 1. , 1. , 1. , 1. , 1. , 0.5, 0. ]) 

177 >>> upfirdn([1], np.arange(10), 1, 3) # decimation by 3 

178 array([ 0., 3., 6., 9.]) 

179 >>> upfirdn([.5, 1, .5], np.arange(10), 2, 3) # linear interp, rate 2/3 

180 array([ 0. , 1. , 2.5, 4. , 5.5, 7. , 8.5, 0. ]) 

181 

182 Apply a single filter to multiple signals: 

183 

184 >>> x = np.reshape(np.arange(8), (4, 2)) 

185 >>> x 

186 array([[0, 1], 

187 [2, 3], 

188 [4, 5], 

189 [6, 7]]) 

190 

191 Apply along the last dimension of ``x``: 

192 

193 >>> h = [1, 1] 

194 >>> upfirdn(h, x, 2) 

195 array([[ 0., 0., 1., 1.], 

196 [ 2., 2., 3., 3.], 

197 [ 4., 4., 5., 5.], 

198 [ 6., 6., 7., 7.]]) 

199 

200 Apply along the 0th dimension of ``x``: 

201 

202 >>> upfirdn(h, x, 2, axis=0) 

203 array([[ 0., 1.], 

204 [ 0., 1.], 

205 [ 2., 3.], 

206 [ 2., 3.], 

207 [ 4., 5.], 

208 [ 4., 5.], 

209 [ 6., 7.], 

210 [ 6., 7.]]) 

211 """ 

212 x = np.asarray(x) 

213 ufd = _UpFIRDn(h, x.dtype, up, down) 

214 # This is equivalent to (but faster than) using np.apply_along_axis 

215 return ufd.apply_filter(x, axis, mode, cval)