Skip to content

Commit 04f0d52

Browse files
committed
adding to changelog
1 parent b8d22b7 commit 04f0d52

File tree

2 files changed

+31
-16
lines changed

2 files changed

+31
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5656
- Removed an erroneous connection to AFNI 3dTProject in nuisance denoising that would unnecessarily send a spike regressor as a censor. This would sometimes cause TRs to unnecessarily be dropped from the timeseries as if scrubbing were being performed.
5757
- Lingering calls to `cpac_outputs.csv` (was changed to `cpac_outputs.tsv` in v1.8.1).
5858
- A bug in the `freesurfer_abcd_preproc` nodeblock where the `Template` image was incorrectly used as `reference` during the `inverse_warp` step. Replacing it with the subject-specific `T1w` image resolved the issue of the `desc-restoreBrain_T1w` being chipped off.
59+
- A bug in `ideal_bandpass` where the frequency mask was incorrectly applied, which caused filter to fail in certain cases.
5960

6061
### Removed
6162

CPAC/nuisance/bandpass.py

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,55 @@
88

99

1010
def ideal_bandpass(data, sample_period, bandpass_freqs):
11-
# Derived from YAN Chao-Gan 120504 based on REST.
11+
"""
12+
Apply ideal bandpass filtering to a 1D time series data using FFT. Derived from YAN Chao-Gan 120504 based on REST.
13+
14+
Parameters
15+
----------
16+
data : NDArray
17+
1D time series data to be filtered.
18+
sample_period : float
19+
Length of sampling period in seconds.
20+
bandpass_freqs : tuple
21+
Tuple containing the bandpass frequencies (LowCutoff, HighCutoff).
22+
23+
Returns
24+
-------
25+
NDArray
26+
Filtered time series data.
27+
28+
"""
1229
sample_freq = 1.0 / sample_period
1330
sample_length = data.shape[0]
31+
nyquist_freq = sample_freq / 2.0
1432

15-
data_p = np.zeros(int(2 ** np.ceil(np.log2(sample_length))))
33+
# Length of zero-padded data for efficient FFT
34+
N = int(2 ** np.ceil(np.log2(len(data))))
35+
data_p = np.zeros(N)
1636
data_p[:sample_length] = data
1737

1838
LowCutoff, HighCutoff = bandpass_freqs
1939

2040
if LowCutoff is None: # No lower cutoff (low-pass filter)
2141
low_cutoff_i = 0
22-
elif LowCutoff > sample_freq / 2.0:
42+
elif LowCutoff > nyquist_freq:
2343
# Cutoff beyond fs/2 (all-stop filter)
24-
low_cutoff_i = int(data_p.shape[0] / 2)
44+
low_cutoff_i = int(N / 2)
2545
else:
26-
low_cutoff_i = np.ceil(LowCutoff * data_p.shape[0] * sample_period).astype(
27-
"int"
28-
)
46+
low_cutoff_i = np.ceil(LowCutoff * N * sample_period).astype("int")
2947

30-
if HighCutoff > sample_freq / 2.0 or HighCutoff is None:
48+
if HighCutoff > nyquist_freq or HighCutoff is None:
3149
# Cutoff beyond fs/2 or unspecified (become a highpass filter)
32-
high_cutoff_i = int(data_p.shape[0] / 2)
50+
high_cutoff_i = int(N / 2)
3351
else:
34-
high_cutoff_i = np.fix(HighCutoff * data_p.shape[0] * sample_period).astype(
35-
"int"
36-
)
52+
high_cutoff_i = np.fix(HighCutoff * N * sample_period).astype("int")
3753

3854
freq_mask = np.zeros_like(data_p, dtype="bool")
3955
freq_mask[low_cutoff_i : high_cutoff_i + 1] = True
40-
freq_mask[data_p.shape[0] - high_cutoff_i : data_p.shape[0] + 1 - low_cutoff_i] = (
41-
True
42-
)
56+
freq_mask[N - high_cutoff_i : N + 1 - low_cutoff_i] = True
4357

4458
f_data = fft(data_p)
45-
f_data[freq_mask is not True] = 0.0
59+
f_data[~freq_mask] = 0.0
4660
return np.real_if_close(ifft(f_data)[:sample_length])
4761

4862

0 commit comments

Comments
 (0)