Scheduled service maintenance on November 22


On Friday, November 22, 2024, between 06:00 CET and 18:00 CET, GIN services will undergo planned maintenance. Extended service interruptions should be expected. We will try to keep downtimes to a minimum, but recommend that users avoid critical tasks, large data uploads, or DOI requests during this time.

We apologize for any inconvenience.

FiltFiltM.m 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. function Y = FiltFiltM(b, a, X, Dim)
  2. % FiltFiltM - zero phase-shift digital filter
  3. % The signal is filtered in forward and reverse direction for a zero phase
  4. % distortion. This is equivalent to FILTFILT of Matlab's Signal Processing
  5. % Toolbox, but the direct processing of arrays let this function run 10% to 90%
  6. % faster. With the fast Mex function FilterX, FiltFiltM is 80% to 95% faster
  7. % than FILTFILT and uses much less temporary memory.
  8. %
  9. % Y = FiltFiltM(b, a, X, Dim)
  10. % INPUT:
  11. % b, a: Filter coefficients as vectors. See FILTER.
  12. % X: Signal as DOUBLE or SINGLE array.
  13. % Dim: Dimension to operate on. If Dim is the empty matrix [], the filter is
  14. % applied to the 1st non-singelton dimension. Filtering along the 1st
  15. % dimension is faster than for the other dimensions.
  16. % Optional, default: 1.
  17. % OUTPUT:
  18. % Y: Array of same size and type as X. The values are filtered in forward
  19. % and backward direction for a zero phase shift.
  20. % The result equals the output of FILTFILT.
  21. %
  22. % NOTES:
  23. % - In opposite to FILTFILT the initial and final phases are treated separately
  24. % instead of enlarging the signal array. This reduces the memory consumption.
  25. % - The results for signals of type SINGLE are more accurate, because FilterX
  26. % uses DOUBLE values for computing the filter.
  27. %
  28. % EXAMPLES:
  29. % b = [0.0675, 0.1349, 0.0675]; % Low pass Butterworth filter
  30. % a = [1.0, -1.1430, 0.4128];
  31. % X = rand(16, 8);
  32. % Y = FiltFiltM(b, a, X); % Along the 1st dimension
  33. % figure, subplot(1,2,1); plot(Y);
  34. % Y = FiltFiltM(b, a, X, 2); % Along the 2nd dimension
  35. % subplot(1,2,2), plot(Y.');
  36. %
  37. % INSTALLATION:
  38. % This function runs without installation. But for more speed the C-mex
  39. % FilterX.c can be compiled: Call FilterM() without inputs. See there for
  40. % further instructions.
  41. %
  42. % See also FILTER, FILTER2, FILTFILT, CONV, FilterM.
  43. %
  44. % Tested: Matlab 6.5, 7.7, 7.8, WinXP
  45. % Author: Jan Simon, Heidelberg, 2009-2011 matlab.THISYEAR(a)nMINUSsimon.de
  46. % References:
  47. % [1] Sanjit K. Mitra, Digital Signal Processing, 2nd ed., McGraw-Hill, 2001
  48. % [2] Fredrik Gustafsson, Determining the initial states in forward-backward
  49. % filtering, IEEE Transactions on Signal Processing, pp. 988--992,
  50. % April 1996, Volume 44, Issue 4
  51. % $JRev: R-t V:019 Sum:flRzmua2/Vps Date:20-Jul-2011 10:29:21 $
  52. % $License: BSD (use/copy/change/redistribute on own risk, mention the author) $
  53. % $UnitTest: uTest_FiltFiltM $
  54. % $File: Tools\GLMath\FiltFiltM.m $
  55. % History:
  56. % 009: 08-May-2011 16:45, Get initial and final parts separately: 10% faster.
  57. % Joining the reflected initial and final parts to the signal wastes
  58. % memory. Now the reflected edge parts are filtered separately just to get
  59. % the initial conditions for filtering the original signal.
  60. % 011: 12-Jul-2011 16:40, 4th input [Dim]. Rewritten from scratch.
  61. % Now this function does not contain any parts of the original FILTFILT
  62. % anymore. The initial conditions are determined following Gustafsson's
  63. % article.
  64. % Initialize: ==================================================================
  65. % Global Interface: ------------------------------------------------------------
  66. % Use the much faster C-Mex replacement for FILTER:
  67. persistent hasFilterX
  68. if isempty(hasFilterX)
  69. hasFilterX = ~isempty(which('FilterX'));
  70. if ~hasFilterX && ~isempty(which('FilterM'));
  71. fprintf(['::: %s: Matlab''s FILTER is used.\n', ...
  72. ' Run FilterM without inputs to compile FilterX.c\n'], ...
  73. mfilename);
  74. end
  75. end
  76. % Initial values: --------------------------------------------------------------
  77. % Program Interface: -----------------------------------------------------------
  78. nArg = nargin;
  79. if nArg == 3
  80. Dim = []; % Default is specified later: 1st or 1st non-singelton dimension
  81. elseif nArg == 4
  82. if numel(Dim) > 1 || ~isequal(Dim, round(abs(Dim)))
  83. error(['JSimon:', mfilename, ':BadDim'], ...
  84. ['*** ', mfilename, ': Dim must be a valid dimension.']);
  85. end
  86. else
  87. error(['JSimon:', mfilename, ':BadNInput'], ...
  88. ['*** ', mfilename, ': 3 or 4 inputs required.']);
  89. end
  90. if isempty(b) || isempty(a) || isempty(X)
  91. Y = X([]);
  92. return;
  93. end
  94. % Get filter parameter vectors as row vectors and padded with zeros on demand:
  95. nb = numel(b);
  96. na = numel(a);
  97. b = b(:);
  98. a = a(:);
  99. Order = max(nb, na);
  100. if nb < Order
  101. b(Order, 1) = 0;
  102. end
  103. if na < Order
  104. a(Order, 1) = 0;
  105. end
  106. nEdge = 3 * (Order - 1); % Number of edge elements
  107. % Move dimension to operate on to dim 1: ---------------------------------------
  108. % Specifying the dimension in the FILTER command is not faster. It seems like
  109. % the permutation is performed inside FILTER also instead of operating on the
  110. % original data.
  111. sizeX = size(X);
  112. ndimsX = ndims(X);
  113. if ndimsX == 2 % Vector or matrix
  114. if isempty(Dim) % Default: First non-singelton dimension
  115. doPermute = false;
  116. doReshape = (sizeX(1) == 1);
  117. if doReshape
  118. X = X(:);
  119. end
  120. elseif Dim < 3
  121. doReshape = false;
  122. doPermute = (Dim == 2);
  123. if doPermute
  124. perm = [2, 1];
  125. X = transpose(X);
  126. end
  127. else
  128. % Usually Matlab functions allow appending arbitrary trailing singelton
  129. % dimensions, but then the (xLen<=nEdge) check fails anyway:
  130. error(['JSimon:', mfilename, ':BadDim'], ...
  131. ['*** ', mfilename, ': Dim out of range.']);
  132. end
  133. else % Signal has more than 2 dimensions: -----------------------
  134. doReshape = true;
  135. if isempty(Dim) % First non-singleton dimension:
  136. doPermute = false;
  137. nonSingle = find(sizeX ~= 1);
  138. if ~isempty(nonSingle) && nonSingle(1) > 1 % Short-circuit needed
  139. X = reshape(X, sizeX(nonSingle(1)), []);
  140. end
  141. elseif Dim <= ndimsX % Dim specified:
  142. if Dim == 1
  143. doPermute = false;
  144. else
  145. doPermute = true;
  146. perm = [Dim, 1:Dim - 1, Dim + 1:ndims(X)];
  147. X = reshape(permute(X, perm), sizeX(Dim), []);
  148. sizeX = sizeX(perm);
  149. end
  150. else
  151. error(['JSimon:', mfilename, ':BadDim'], ...
  152. ['*** ', mfilename, ': Dim out of range.']);
  153. end
  154. end
  155. xLen = size(X, 1); % Not sizeX - would fail for row vectors!
  156. if xLen <= nEdge % Input data too short
  157. error(['JSimon:', mfilename, ':BadDataLen'], ...
  158. ['*** ', mfilename, ...
  159. ': Signal must be longer than 3 times the filter order.']);
  160. end
  161. % User Interface: --------------------------------------------------------------
  162. % Do the work: =================================================================
  163. % Create initial conditions to treat offsets at beginning and end:
  164. K = eye(Order - 1);
  165. K(:, 1) = a(2:Order);
  166. K(1) = K(1) + 1;
  167. K(Order:Order:numel(K)) = -1.0;
  168. IC = K \ (b(2:Order) - a(2:Order) * b(1));
  169. % Use a reflection to extrapolate signal at beginning and end to reduce edge
  170. % effects (BSXFUN would be some micro-seconds faster, but it is not available in
  171. % Matlab 6.5):
  172. x1_2 = 2 * X(1, :);
  173. xf_2 = 2 * X(xLen, :);
  174. Xi = x1_2(ones(1, nEdge), :) - X((nEdge + 1):-1:2, :);
  175. Xf = xf_2(ones(1, nEdge), :) - X((xLen - 1):-1:(xLen - nEdge), :);
  176. if hasFilterX % Use the faster C-mex filter function: -------------------------
  177. % Filter initial reflected signal:
  178. [dum, Zi] = FilterX(b, a, Xi, IC * Xi(1, :)); %#ok<ASGLU>
  179. % Use the final conditions of the initial part for the actual signal:
  180. [Ys, Zs] = FilterX(b, a, X, Zi); % "s"teady state
  181. Yf = FilterX(b, a, Xf, Zs); % "f"inal conditions
  182. % Filter signal again in reverse order:
  183. [dum, Zf] = FilterX(b, a, Yf, IC * Yf(nEdge, :), 'reverse'); %#ok<ASGLU>
  184. Y = FilterX(b, a, Ys, Zf, 'reverse');
  185. else % Use the slower built-in FILTER function: -------------------------------
  186. [dum, Zi] = filter(b, a, Xi, IC * Xi(1, :)); %#ok<ASGLU>
  187. [Ys, Zs] = filter(b, a, X, Zi); % "s"teady state
  188. Yf = filter(b, a, Xf, Zs); % "f"inal conditions
  189. Yf = Yf(nEdge:-1:1, :);
  190. [dum, Zf] = filter(b, a, Yf, IC * Yf(1, :)); %#ok<ASGLU>
  191. Y = filter(b, a, Ys(xLen:-1:1, :), Zf); % Filter reverted signal
  192. Y = Y(xLen:-1:1, :); % Re-revert
  193. end
  194. % Reconstruct original array dimension: ----------------------------------------
  195. if doReshape
  196. Y = reshape(Y, sizeX);
  197. end
  198. if doPermute
  199. Y = ipermute(Y, perm);
  200. end
  201. % return;