pyNIBS 0.2024.8__py3-none-any.whl → 0.2026.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pynibs/__init__.py +26 -14
- pynibs/coil/__init__.py +6 -0
- pynibs/{coil.py → coil/coil.py} +213 -543
- pynibs/coil/export.py +508 -0
- pynibs/congruence/__init__.py +4 -1
- pynibs/congruence/congruence.py +37 -45
- pynibs/congruence/ext_metrics.py +40 -11
- pynibs/congruence/stimulation_threshold.py +1 -2
- pynibs/expio/Mep.py +120 -370
- pynibs/expio/__init__.py +10 -0
- pynibs/expio/brainsight.py +34 -37
- pynibs/expio/cobot.py +25 -25
- pynibs/expio/exp.py +10 -7
- pynibs/expio/fit_funs.py +3 -0
- pynibs/expio/invesalius.py +70 -0
- pynibs/expio/localite.py +190 -91
- pynibs/expio/neurone.py +139 -0
- pynibs/expio/signal_ced.py +345 -2
- pynibs/expio/visor.py +16 -15
- pynibs/freesurfer.py +34 -33
- pynibs/hdf5_io/hdf5_io.py +149 -132
- pynibs/hdf5_io/xdmf.py +35 -31
- pynibs/mesh/__init__.py +1 -1
- pynibs/mesh/mesh_struct.py +77 -92
- pynibs/mesh/transformations.py +121 -21
- pynibs/mesh/utils.py +191 -99
- pynibs/models/_TMS.py +2 -1
- pynibs/muap.py +1 -2
- pynibs/neuron/__init__.py +10 -0
- pynibs/neuron/models/mep.py +566 -0
- pynibs/neuron/neuron_regression.py +98 -8
- pynibs/optimization/__init__.py +12 -2
- pynibs/optimization/{optimization.py → coil_opt.py} +157 -133
- pynibs/optimization/multichannel.py +1174 -24
- pynibs/optimization/workhorses.py +7 -8
- pynibs/regression/__init__.py +4 -2
- pynibs/regression/dual_node_detection.py +229 -219
- pynibs/regression/regression.py +92 -61
- pynibs/roi/__init__.py +4 -1
- pynibs/roi/roi_structs.py +19 -21
- pynibs/roi/{roi.py → roi_utils.py} +56 -33
- pynibs/subject.py +24 -14
- pynibs/util/__init__.py +20 -4
- pynibs/util/dosing.py +4 -5
- pynibs/util/quality_measures.py +39 -38
- pynibs/util/rotations.py +116 -9
- pynibs/util/{simnibs.py → simnibs_io.py} +29 -19
- pynibs/util/{util.py → utils.py} +20 -22
- pynibs/visualization/para.py +4 -4
- pynibs/visualization/render_3D.py +4 -4
- pynibs-0.2026.1.dist-info/METADATA +105 -0
- pynibs-0.2026.1.dist-info/RECORD +69 -0
- {pyNIBS-0.2024.8.dist-info → pynibs-0.2026.1.dist-info}/WHEEL +1 -1
- pyNIBS-0.2024.8.dist-info/METADATA +0 -723
- pyNIBS-0.2024.8.dist-info/RECORD +0 -107
- pynibs/data/configuration_exp0.yaml +0 -59
- pynibs/data/configuration_linear_MEP.yaml +0 -61
- pynibs/data/configuration_linear_RT.yaml +0 -61
- pynibs/data/configuration_sigmoid4.yaml +0 -68
- pynibs/data/network mapping configuration/configuration guide.md +0 -238
- pynibs/data/network mapping configuration/configuration_TEMPLATE.yaml +0 -42
- pynibs/data/network mapping configuration/configuration_for_testing.yaml +0 -43
- pynibs/data/network mapping configuration/configuration_modelTMS.yaml +0 -43
- pynibs/data/network mapping configuration/configuration_reg_isi_05.yaml +0 -43
- pynibs/data/network mapping configuration/output_documentation.md +0 -185
- pynibs/data/network mapping configuration/recommendations_for_accuracy_threshold.md +0 -77
- pynibs/data/neuron/models/L23_PC_cADpyr_biphasic_v1.csv +0 -1281
- pynibs/data/neuron/models/L23_PC_cADpyr_monophasic_v1.csv +0 -1281
- pynibs/data/neuron/models/L4_LBC_biphasic_v1.csv +0 -1281
- pynibs/data/neuron/models/L4_LBC_monophasic_v1.csv +0 -1281
- pynibs/data/neuron/models/L4_NBC_biphasic_v1.csv +0 -1281
- pynibs/data/neuron/models/L4_NBC_monophasic_v1.csv +0 -1281
- pynibs/data/neuron/models/L4_SBC_biphasic_v1.csv +0 -1281
- pynibs/data/neuron/models/L4_SBC_monophasic_v1.csv +0 -1281
- pynibs/data/neuron/models/L5_TTPC2_cADpyr_biphasic_v1.csv +0 -1281
- pynibs/data/neuron/models/L5_TTPC2_cADpyr_monophasic_v1.csv +0 -1281
- pynibs/tests/data/InstrumentMarker20200225163611937.xml +0 -19
- pynibs/tests/data/TriggerMarkers_Coil0_20200225163443682.xml +0 -14
- pynibs/tests/data/TriggerMarkers_Coil1_20200225170337572.xml +0 -6373
- pynibs/tests/data/Xdmf.dtd +0 -89
- pynibs/tests/data/brainsight_niiImage_nifticoord.txt +0 -145
- pynibs/tests/data/brainsight_niiImage_nifticoord_largefile.txt +0 -1434
- pynibs/tests/data/brainsight_niiImage_niifticoord_mixedtargets.txt +0 -47
- pynibs/tests/data/create_subject_testsub.py +0 -332
- pynibs/tests/data/data.hdf5 +0 -0
- pynibs/tests/data/geo.hdf5 +0 -0
- pynibs/tests/test_coil.py +0 -474
- pynibs/tests/test_elements2nodes.py +0 -100
- pynibs/tests/test_hdf5_io/test_xdmf.py +0 -61
- pynibs/tests/test_mesh_transformations.py +0 -123
- pynibs/tests/test_mesh_utils.py +0 -143
- pynibs/tests/test_nnav_imports.py +0 -101
- pynibs/tests/test_quality_measures.py +0 -117
- pynibs/tests/test_regressdata.py +0 -289
- pynibs/tests/test_roi.py +0 -17
- pynibs/tests/test_rotations.py +0 -86
- pynibs/tests/test_subject.py +0 -71
- pynibs/tests/test_util.py +0 -24
- /pynibs/{regression/score_types.py → neuron/models/m1_montbrio.py} +0 -0
- {pyNIBS-0.2024.8.dist-info → pynibs-0.2026.1.dist-info/licenses}/LICENSE +0 -0
- {pyNIBS-0.2024.8.dist-info → pynibs-0.2026.1.dist-info}/top_level.txt +0 -0
pynibs/expio/brainsight.py
CHANGED
|
@@ -32,7 +32,6 @@ class BrainsightCSVParser:
|
|
|
32
32
|
parse_line(line)
|
|
33
33
|
Parses a line and updates the corresponding attributes.
|
|
34
34
|
"""
|
|
35
|
-
|
|
36
35
|
class State:
|
|
37
36
|
FILE_HEADER = 0
|
|
38
37
|
SKIPPING = 1
|
|
@@ -116,7 +115,6 @@ def write_targets_brainsight(targets, fn_out, names=None, overwrite=True):
|
|
|
116
115
|
-------
|
|
117
116
|
<file> .txt file containing the targets for import into Brainsight
|
|
118
117
|
"""
|
|
119
|
-
|
|
120
118
|
targets = np.atleast_3d(targets)
|
|
121
119
|
|
|
122
120
|
if names is None:
|
|
@@ -228,7 +226,7 @@ def create_merged_nnav_emg_file_brainsight(fn_brainsight_nnav_info, fn_emg_info,
|
|
|
228
226
|
|
|
229
227
|
num_stim = emg_data.shape[0]
|
|
230
228
|
num_channels = emg_data.shape[1]
|
|
231
|
-
num_emg_samples = emg_data.shape[2]
|
|
229
|
+
# num_emg_samples = emg_data.shape[2]
|
|
232
230
|
|
|
233
231
|
emg_timestamps_h5_ref = data_h5[f"/{keys[0]}/frameinfo/start"][:]
|
|
234
232
|
emg_timestamps = []
|
|
@@ -241,7 +239,8 @@ def create_merged_nnav_emg_file_brainsight(fn_brainsight_nnav_info, fn_emg_info,
|
|
|
241
239
|
|
|
242
240
|
elif ext == ".cfs":
|
|
243
241
|
emg_data, emg_timestamps, num_stim, num_channels, num_emg_samples, sampling_rate = \
|
|
244
|
-
pynibs.read_biosig_emg_data(fn_data=fn_emg_info, include_first_trigger=True,
|
|
242
|
+
pynibs.expio.signal_ced.read_biosig_emg_data(fn_data=fn_emg_info, include_first_trigger=True,
|
|
243
|
+
filetype="cfs")
|
|
245
244
|
emg_res = 1000 / sampling_rate # [ms]
|
|
246
245
|
emg_start = 0 # EMG start time not included in CSF file / always 0 for CED Signal (?)
|
|
247
246
|
else:
|
|
@@ -259,7 +258,7 @@ def create_merged_nnav_emg_file_brainsight(fn_brainsight_nnav_info, fn_emg_info,
|
|
|
259
258
|
csv_parser.parse_line(line)
|
|
260
259
|
|
|
261
260
|
nnav_timestamps = []
|
|
262
|
-
start_time_string =
|
|
261
|
+
start_time_string = csv_parser.data[0][24] # 25. column of CSv data = time stamp
|
|
263
262
|
start_time = datetime.datetime.strptime(start_time_string, '%H:%M:%S.%f')
|
|
264
263
|
for c_idx in range(0, num_stim):
|
|
265
264
|
current_time_string = csv_parser.data[c_idx][24] # 25. column of CSv data = time stamp
|
|
@@ -421,18 +420,18 @@ def merge_exp_data_brainsight(subject, exp_idx, mesh_idx, coil_outlier_corr_cond
|
|
|
421
420
|
print(f"Transforming coil positions from Brainsight to SimNIBS space")
|
|
422
421
|
print(nii_conform_path)
|
|
423
422
|
|
|
424
|
-
m_simnibs = pynibs.nnav2simnibs(fn_exp_nii=nii_exp_path_lst[0][0],
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
423
|
+
m_simnibs = pynibs.expio.nnav2simnibs(fn_exp_nii=nii_exp_path_lst[0][0],
|
|
424
|
+
fn_conform_nii=nii_conform_path,
|
|
425
|
+
m_nnav=m_nnav,
|
|
426
|
+
nnav_system="brainsight",
|
|
427
|
+
mesh_approach="headreco",
|
|
428
|
+
fiducials=None,
|
|
429
|
+
orientation='RAS',
|
|
430
|
+
fsl_cmd=None,
|
|
431
|
+
target='simnibs',
|
|
432
|
+
temp_dir=temp_dir,
|
|
433
|
+
rem_tmp=True,
|
|
434
|
+
verbose=verbose)
|
|
436
435
|
|
|
437
436
|
# create dictionary containing stimulation and physiological data
|
|
438
437
|
if verbose:
|
|
@@ -456,7 +455,7 @@ def merge_exp_data_brainsight(subject, exp_idx, mesh_idx, coil_outlier_corr_cond
|
|
|
456
455
|
|
|
457
456
|
for i in range(n_stims):
|
|
458
457
|
d['coil_0'].append(m_simnibs[:, :, i])
|
|
459
|
-
d['coil_1'].append(np.zeros((4, 4)) * np.
|
|
458
|
+
d['coil_1'].append(np.zeros((4, 4)) * np.nan)
|
|
460
459
|
d['coil_mean'].append(np.nanmean(np.stack((d['coil_0'][-1],
|
|
461
460
|
d['coil_1'][-1]), axis=2), axis=2))
|
|
462
461
|
d['number'].append(d_bs['Index'][i])
|
|
@@ -519,12 +518,12 @@ def merge_exp_data_brainsight(subject, exp_idx, mesh_idx, coil_outlier_corr_cond
|
|
|
519
518
|
os.makedirs(fn_channel, exist_ok=True)
|
|
520
519
|
|
|
521
520
|
# filter data and calculate p2p values
|
|
522
|
-
p2p, mep_filt_data, latency = pynibs.calc_p2p(sweep=d_bs[f"EMG Data {c_idx}"][i],
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
521
|
+
p2p, mep_filt_data, latency = pynibs.expio.calc_p2p(sweep=d_bs[f"EMG Data {c_idx}"][i],
|
|
522
|
+
tms_pulse_time=d_bs[f"Offset"][i],
|
|
523
|
+
sampling_rate=1000 / d_bs["EMG Res."][i],
|
|
524
|
+
start_mep=start_mep, end_mep=end_mep,
|
|
525
|
+
measurement_start_time=float(d["EMG Start"][i]),
|
|
526
|
+
fn_plot=fn_plot)
|
|
528
527
|
|
|
529
528
|
d[f"mep_raw_data_time_{chan_name}"].append(np.arange(d_bs["EMG Start"][i],
|
|
530
529
|
d_bs["EMG End"][i], d_bs["EMG Res."][i]))
|
|
@@ -551,16 +550,16 @@ def merge_exp_data_brainsight(subject, exp_idx, mesh_idx, coil_outlier_corr_cond
|
|
|
551
550
|
if coil_outlier_corr_cond:
|
|
552
551
|
if verbose:
|
|
553
552
|
print("Removing coil position outliers")
|
|
554
|
-
d = pynibs.coil_outlier_correction_cond(exp=d, outlier_angle=5., outlier_loc=3., fn_exp_out=fn_exp_hdf5)
|
|
553
|
+
d = pynibs.expio.coil_outlier_correction_cond(exp=d, outlier_angle=5., outlier_loc=3., fn_exp_out=fn_exp_hdf5)
|
|
555
554
|
|
|
556
555
|
# perform coil <-> head distance correction
|
|
557
556
|
if coil_distance_corr:
|
|
558
557
|
if verbose:
|
|
559
558
|
print("Performing coil <-> head distance correction")
|
|
560
|
-
d = pynibs.coil_distance_correction(exp=d,
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
559
|
+
d = pynibs.expio.coil_distance_correction(exp=d,
|
|
560
|
+
fn_geo_hdf5=fn_mesh_hdf5,
|
|
561
|
+
remove_coil_skin_distance_outlier=remove_coil_skin_distance_outlier,
|
|
562
|
+
fn_plot=os.path.split(fn_exp_hdf5)[0])
|
|
564
563
|
|
|
565
564
|
d_avg = dict()
|
|
566
565
|
for key in d.keys():
|
|
@@ -753,15 +752,13 @@ def transform_brainsight(data, col_names):
|
|
|
753
752
|
data: list of lists with positions
|
|
754
753
|
col_names: list of str
|
|
755
754
|
|
|
756
|
-
Returns
|
|
757
|
-
|
|
755
|
+
Returns
|
|
756
|
+
-------
|
|
758
757
|
names : list of str
|
|
759
758
|
Samples/target names. len(names) = n_pos
|
|
760
759
|
matsimnibs: np.ndarray
|
|
761
760
|
Coil position/orientation in matsimnibs style with Brainsight coil axes definition
|
|
762
761
|
shape: (n_pos, 4, 4)
|
|
763
|
-
|
|
764
|
-
Written by Ole Numssen, numssen@cbs.mpg.de, 2022.
|
|
765
762
|
"""
|
|
766
763
|
matsimnibs = np.zeros((len(data), 4, 4))
|
|
767
764
|
pos_names = []
|
|
@@ -899,11 +896,11 @@ def read_triggermarker(fn):
|
|
|
899
896
|
Read instrument-marker and conditions from Brainsight session.
|
|
900
897
|
|
|
901
898
|
BrainSight stores recorded coil placements as 'Samples' and predefined coil placements
|
|
902
|
-
|
|
899
|
+
as 'Targets'.
|
|
903
900
|
|
|
904
901
|
Coil axes definitions as defined by Brainsight.
|
|
905
902
|
dI/dt is returned as list of [-1] to keep consistent with read_triggermarker functions for other neuronavigation
|
|
906
|
-
|
|
903
|
+
systems.
|
|
907
904
|
|
|
908
905
|
Parameters
|
|
909
906
|
----------
|
|
@@ -913,7 +910,7 @@ def read_triggermarker(fn):
|
|
|
913
910
|
Returns
|
|
914
911
|
-------
|
|
915
912
|
matsimnibs : np.ndarray of float
|
|
916
|
-
(4, 4, N) Instrument marker matrices
|
|
913
|
+
(4, 4, N) Instrument marker matrices.
|
|
917
914
|
didt : np.ndarray of float
|
|
918
915
|
(N) Rate of change of coil current in (A/us).
|
|
919
916
|
stim_int : np.ndarray of float
|
|
@@ -963,7 +960,7 @@ def read_triggermarker(fn):
|
|
|
963
960
|
else:
|
|
964
961
|
names_samples, matsimnibs_samples = [], None
|
|
965
962
|
|
|
966
|
-
if
|
|
963
|
+
if matsimnibs_samples is None or matsimnibs_samples.size == 0:
|
|
967
964
|
raise ValueError(f"Could not find any targets in {fn}.")
|
|
968
965
|
|
|
969
966
|
# get timings for samples
|
pynibs/expio/cobot.py
CHANGED
|
@@ -80,7 +80,7 @@ def merge_exp_data_cobot(subject, exp_idx, mesh_idx, coil_outlier_corr_cond=Fals
|
|
|
80
80
|
# compute indices in data block corresponding to the events
|
|
81
81
|
if event["TYP"] == "0x7ffe":
|
|
82
82
|
trigger_event_idcs.append(
|
|
83
|
-
|
|
83
|
+
round(event["POS"] * cfs_header["Samplingrate"])
|
|
84
84
|
)
|
|
85
85
|
|
|
86
86
|
num_sweeps = min(num_sweeps, len(trigger_event_idcs))
|
|
@@ -95,8 +95,8 @@ def merge_exp_data_cobot(subject, exp_idx, mesh_idx, coil_outlier_corr_cond=Fals
|
|
|
95
95
|
# beginning) we would need to adhere to the entire trigger_event_indices
|
|
96
96
|
# list.
|
|
97
97
|
channel_emg = np.reshape(
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
cfs_emg[trigger_event_idcs[0]:, c_idx],
|
|
99
|
+
(num_sweeps, samples_per_sweep)
|
|
100
100
|
)
|
|
101
101
|
|
|
102
102
|
d[f"mep_raw_data_time_{c_idx}"] = []
|
|
@@ -113,14 +113,14 @@ def merge_exp_data_cobot(subject, exp_idx, mesh_idx, coil_outlier_corr_cond=Fals
|
|
|
113
113
|
os.makedirs(fn_channel, exist_ok=True)
|
|
114
114
|
|
|
115
115
|
# filter data and calculate p2p values
|
|
116
|
-
p2p, mep_filt_data, latency = pynibs.calc_p2p(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
p2p, mep_filt_data, latency = pynibs.expio.calc_p2p(
|
|
117
|
+
sweep=channel_emg[s_idx],
|
|
118
|
+
tms_pulse_time=tms_pulse_time,
|
|
119
|
+
sampling_rate=sampling_rate,
|
|
120
|
+
start_mep=start_mep_ms,
|
|
121
|
+
end_mep=end_mep_ms,
|
|
122
|
+
measurement_start_time=0,
|
|
123
|
+
fn_plot=fn_plot
|
|
124
124
|
)
|
|
125
125
|
|
|
126
126
|
d[f"mep_raw_data_time_{c_idx}"].append(time)
|
|
@@ -169,10 +169,10 @@ def merge_exp_data_cobot(subject, exp_idx, mesh_idx, coil_outlier_corr_cond=Fals
|
|
|
169
169
|
if coil_distance_corr:
|
|
170
170
|
if verbose:
|
|
171
171
|
print("Performing coil <-> head distance correction")
|
|
172
|
-
matsimnibs_t = pynibs.coil_distance_correction_matsimnibs(matsimnibs=matsimnibs_t,
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
matsimnibs_t = pynibs.expio.coil_distance_correction_matsimnibs(matsimnibs=matsimnibs_t,
|
|
173
|
+
fn_mesh_hdf5=fn_mesh_hdf5,
|
|
174
|
+
distance=1,
|
|
175
|
+
remove_coil_skin_distance_outlier=remove_coil_skin_distance_outlier)
|
|
176
176
|
|
|
177
177
|
d["coil_0"] = []
|
|
178
178
|
for i in range(centers.shape[0]):
|
|
@@ -182,13 +182,13 @@ def merge_exp_data_cobot(subject, exp_idx, mesh_idx, coil_outlier_corr_cond=Fals
|
|
|
182
182
|
|
|
183
183
|
# 3) save in "experiment.hdf5"
|
|
184
184
|
coil_0_np = np.array(d['coil_0'])
|
|
185
|
-
pynibs.write_coil_pos_hdf5(
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
185
|
+
pynibs.coil.write_coil_pos_hdf5(
|
|
186
|
+
fn_hdf=out_coil_pos_fn,
|
|
187
|
+
centers=coil_0_np[:, :3, 3], # np.array(centers),
|
|
188
|
+
m0=coil_0_np[:, :3, 0], # np.array(m0),
|
|
189
|
+
m1=coil_0_np[:, :3, 1], # np.array(m1),
|
|
190
|
+
m2=coil_0_np[:, :3, 2], # np.array(m2),
|
|
191
|
+
overwrite=True
|
|
192
192
|
)
|
|
193
193
|
|
|
194
194
|
# create dictionary of stimulation data
|
|
@@ -232,8 +232,8 @@ def merge_exp_data_cobot(subject, exp_idx, mesh_idx, coil_outlier_corr_cond=Fals
|
|
|
232
232
|
df_phys_data_postproc = pd.DataFrame.from_dict(d_phys_data_postproc)
|
|
233
233
|
|
|
234
234
|
# save in .hdf5 file
|
|
235
|
-
df_stim_data.to_hdf(fn_exp_hdf5, "stim_data")
|
|
236
|
-
df_phys_data_raw.to_hdf(fn_exp_hdf5, "phys_data/raw/EMG")
|
|
237
|
-
df_phys_data_postproc.to_hdf(fn_exp_hdf5, "phys_data/postproc/EMG")
|
|
235
|
+
df_stim_data.to_hdf(fn_exp_hdf5, key="stim_data")
|
|
236
|
+
df_phys_data_raw.to_hdf(fn_exp_hdf5, key="phys_data/raw/EMG")
|
|
237
|
+
df_phys_data_postproc.to_hdf(fn_exp_hdf5, key="phys_data/postproc/EMG")
|
|
238
238
|
|
|
239
239
|
print("Done.")
|
pynibs/expio/exp.py
CHANGED
|
@@ -409,7 +409,7 @@ def nnav2simnibs(fn_exp_nii, fn_conform_nii, m_nnav, nnav_system, mesh_approach=
|
|
|
409
409
|
fn_exp_nii : str
|
|
410
410
|
Filename of .nii file the experiments were conducted with.
|
|
411
411
|
fn_conform_nii : str
|
|
412
|
-
Filename of .nii file from SimNIBS mri2msh function, e.g.
|
|
412
|
+
Filename of .nii file from SimNIBS mri2msh function, e.g. ``".../fs_subjectID/subjectID_T1fs_conform.nii.gz"``.
|
|
413
413
|
m_nnav : np.ndarray
|
|
414
414
|
(4, 4, N) Position matrices from neuronavigation.
|
|
415
415
|
nnav_system : str
|
|
@@ -422,16 +422,19 @@ def nnav2simnibs(fn_exp_nii, fn_conform_nii, m_nnav, nnav_system, mesh_approach=
|
|
|
422
422
|
Approach the mesh is generated with (``"headreco"`` or ``"mri2mesh"``).
|
|
423
423
|
fiducials : np.ndarray of float, optional
|
|
424
424
|
(3, 3) Fiducial points in ANT nifti space from file,
|
|
425
|
-
e.g.:
|
|
425
|
+
e.g.: ``"/data/pt_01756/probands/33791.b8/exp/1/33791.b8_recording/MRI/33791.b8_recording.mri"``
|
|
426
426
|
(x frontal-occipital, y right-left, z inferior-superior).
|
|
427
427
|
|
|
428
428
|
.. code-block:: sh
|
|
429
|
+
|
|
429
430
|
VoxelOnPositiveXAxis (Nasion, first row)
|
|
430
431
|
221 131 127
|
|
431
432
|
VoxelOnPositiveYAxis (left ear, second row)
|
|
432
433
|
121 203 105
|
|
433
434
|
VoxelOnNegativeYAxis (right ear, third row)
|
|
434
435
|
121 57 105
|
|
436
|
+
|
|
437
|
+
|
|
435
438
|
orientation : str, default: 'RAS'
|
|
436
439
|
Orientation convention (``'RAS'`` or ``'LPS'``), can be read from neuronavigation .xml
|
|
437
440
|
file under ``coordinateSpace="RAS"``.
|
|
@@ -1097,7 +1100,7 @@ def coil_outlier_correction_cond(exp=None, fn_exp=None, fn_exp_out=None, outlier
|
|
|
1097
1100
|
"""
|
|
1098
1101
|
if exp is not None:
|
|
1099
1102
|
if type(exp) is list:
|
|
1100
|
-
exp = pynibs.list2dict(exp)
|
|
1103
|
+
exp = pynibs.util.utils.list2dict(exp)
|
|
1101
1104
|
elif fn_exp is not None:
|
|
1102
1105
|
exp = read_csv(fn_exp)
|
|
1103
1106
|
else:
|
|
@@ -1519,7 +1522,7 @@ def coil_distance_correction(exp=None, fn_exp=None, fn_exp_out=None, fn_geo_hdf5
|
|
|
1519
1522
|
|
|
1520
1523
|
if exp is not None:
|
|
1521
1524
|
if type(exp) is list:
|
|
1522
|
-
exp = pynibs.list2dict(exp)
|
|
1525
|
+
exp = pynibs.util.utils.list2dict(exp)
|
|
1523
1526
|
elif fn_exp is not None:
|
|
1524
1527
|
exp = read_csv(fn_exp)
|
|
1525
1528
|
else:
|
|
@@ -1534,7 +1537,7 @@ def coil_distance_correction(exp=None, fn_exp=None, fn_exp_out=None, fn_geo_hdf5
|
|
|
1534
1537
|
n_conditions = len(exp_cond)
|
|
1535
1538
|
|
|
1536
1539
|
# read head mesh and extract skin surface
|
|
1537
|
-
msh = pynibs.load_mesh_hdf5(fn_geo_hdf5)
|
|
1540
|
+
msh = pynibs.hdf5_io.load_mesh_hdf5(fn_geo_hdf5)
|
|
1538
1541
|
triangles = msh.triangles[msh.triangles_regions == 1005]
|
|
1539
1542
|
point_idx_unique = np.unique(triangles)
|
|
1540
1543
|
points = msh.points[point_idx_unique, :]
|
|
@@ -1563,7 +1566,7 @@ def coil_distance_correction(exp=None, fn_exp=None, fn_exp_out=None, fn_geo_hdf5
|
|
|
1563
1566
|
if remove_coil_skin_distance_outlier:
|
|
1564
1567
|
distance_mean = np.median(distance)
|
|
1565
1568
|
distance_zm = distance - distance_mean
|
|
1566
|
-
coil_pos_selected = np.logical_and(min_dist < distance_zm, distance_zm < max_dist)
|
|
1569
|
+
coil_pos_selected = np.logical_and(min_dist < distance_zm, distance_zm < max_dist)
|
|
1567
1570
|
|
|
1568
1571
|
# distance distribution (original)
|
|
1569
1572
|
plt.hist(distance, bins=50, density=True)
|
|
@@ -1686,7 +1689,7 @@ def coil_distance_correction_matsimnibs(matsimnibs, fn_mesh_hdf5, distance=0, re
|
|
|
1686
1689
|
matsimnibs_corrected = copy.deepcopy(matsimnibs)
|
|
1687
1690
|
|
|
1688
1691
|
# read head mesh and extract skin surface
|
|
1689
|
-
msh = pynibs.load_mesh_hdf5(fn_mesh_hdf5)
|
|
1692
|
+
msh = pynibs.hdf5_io.load_mesh_hdf5(fn_mesh_hdf5)
|
|
1690
1693
|
triangles = msh.triangles[msh.triangles_regions == 1005] # this is skin
|
|
1691
1694
|
point_idx_unique = np.unique(triangles)
|
|
1692
1695
|
points = msh.points[point_idx_unique, :]
|
pynibs/expio/fit_funs.py
CHANGED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
""" Functions to import data from Invesalius """
|
|
2
|
+
|
|
3
|
+
import transformations as tr
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import numpy as np
|
|
6
|
+
import datetime
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def read_triggermarker(fn_mkss):
|
|
10
|
+
"""
|
|
11
|
+
Read instrument-marker and conditions from neuronavigator .xml-file.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
fn_mkss : str
|
|
16
|
+
Path to TriggerMarker.fn_mkss file
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
m_nnav : np.ndarray of float
|
|
21
|
+
(4, 4, N) Neuronavigation coordinates of N stimuli (4x4 matsimnibs matrices).
|
|
22
|
+
didt : np.ndarray of float
|
|
23
|
+
(N) Rate of change of coil current in (A/us). At the moment: NAN.
|
|
24
|
+
stim_int : np.ndarray of float
|
|
25
|
+
(N) Stimulator intensity in (% MSO). At the moment: NAN.
|
|
26
|
+
descr : list of str
|
|
27
|
+
(N) Labels of the instrument-marker-conditions.
|
|
28
|
+
rec_time : list of str
|
|
29
|
+
(N) Recording time in ms.
|
|
30
|
+
"""
|
|
31
|
+
data = pd.read_csv(fn_mkss, skiprows=1, sep="\t")
|
|
32
|
+
data = data[data['marker_type'] == 4]
|
|
33
|
+
if 'is_tracked' not in data.columns:
|
|
34
|
+
print("is_tracked column not found, assuming all markers are tracked")
|
|
35
|
+
tracking_missing = True
|
|
36
|
+
else:
|
|
37
|
+
tracking_missing = False
|
|
38
|
+
print(f"{data[data['is_tracked'] == 0].shape[0]} untracked markers found.")
|
|
39
|
+
# data = data[data['is_tracked'] == 1]
|
|
40
|
+
print(f"{data.shape[0]} coil placements found in {fn_mkss}")
|
|
41
|
+
|
|
42
|
+
# build matsimnibs from data
|
|
43
|
+
matsimnibs = []
|
|
44
|
+
desc = []
|
|
45
|
+
rec_time = []
|
|
46
|
+
start_time = data['timestamp'].iloc[0]
|
|
47
|
+
dt_format = "%Y-%m-%d %H:%M:%S.%f"
|
|
48
|
+
start_time = datetime.datetime.strptime(start_time, dt_format)
|
|
49
|
+
for idx, row in data.iterrows():
|
|
50
|
+
if not tracking_missing and row[data['is_tracked'] == 0]:
|
|
51
|
+
print(f"Skipping untracked marker {idx}")
|
|
52
|
+
r = np.eye(4)
|
|
53
|
+
r[:3, :4] = np.nan
|
|
54
|
+
r = tr.euler_matrix(row["alpha_world"], row["beta_world"], row["gamma_world"], "sxyz")
|
|
55
|
+
r[:3,3] = row[["x_world", "y_world", "z_world"]].values
|
|
56
|
+
matsimnibs.append(r)
|
|
57
|
+
|
|
58
|
+
desc.append(row["marker_id"])
|
|
59
|
+
rec_time.append((datetime.datetime.strptime(row["timestamp"], dt_format) - start_time).total_seconds() * 1000)
|
|
60
|
+
matsimnibs = np.array(matsimnibs)
|
|
61
|
+
matsimnibs = np.moveaxis(matsimnibs, 0, -1)
|
|
62
|
+
|
|
63
|
+
didt, stim_int, descr = (np.zeros(matsimnibs.shape[-1]),
|
|
64
|
+
np.zeros(matsimnibs.shape[-1]),
|
|
65
|
+
np.zeros(matsimnibs.shape[-1]))
|
|
66
|
+
|
|
67
|
+
# currently not storted by Invesalius
|
|
68
|
+
didt[:] = np.nan
|
|
69
|
+
stim_int[:] = np.nan
|
|
70
|
+
return matsimnibs, didt, stim_int, desc, rec_time
|