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.
Files changed (101) hide show
  1. pynibs/__init__.py +26 -14
  2. pynibs/coil/__init__.py +6 -0
  3. pynibs/{coil.py → coil/coil.py} +213 -543
  4. pynibs/coil/export.py +508 -0
  5. pynibs/congruence/__init__.py +4 -1
  6. pynibs/congruence/congruence.py +37 -45
  7. pynibs/congruence/ext_metrics.py +40 -11
  8. pynibs/congruence/stimulation_threshold.py +1 -2
  9. pynibs/expio/Mep.py +120 -370
  10. pynibs/expio/__init__.py +10 -0
  11. pynibs/expio/brainsight.py +34 -37
  12. pynibs/expio/cobot.py +25 -25
  13. pynibs/expio/exp.py +10 -7
  14. pynibs/expio/fit_funs.py +3 -0
  15. pynibs/expio/invesalius.py +70 -0
  16. pynibs/expio/localite.py +190 -91
  17. pynibs/expio/neurone.py +139 -0
  18. pynibs/expio/signal_ced.py +345 -2
  19. pynibs/expio/visor.py +16 -15
  20. pynibs/freesurfer.py +34 -33
  21. pynibs/hdf5_io/hdf5_io.py +149 -132
  22. pynibs/hdf5_io/xdmf.py +35 -31
  23. pynibs/mesh/__init__.py +1 -1
  24. pynibs/mesh/mesh_struct.py +77 -92
  25. pynibs/mesh/transformations.py +121 -21
  26. pynibs/mesh/utils.py +191 -99
  27. pynibs/models/_TMS.py +2 -1
  28. pynibs/muap.py +1 -2
  29. pynibs/neuron/__init__.py +10 -0
  30. pynibs/neuron/models/mep.py +566 -0
  31. pynibs/neuron/neuron_regression.py +98 -8
  32. pynibs/optimization/__init__.py +12 -2
  33. pynibs/optimization/{optimization.py → coil_opt.py} +157 -133
  34. pynibs/optimization/multichannel.py +1174 -24
  35. pynibs/optimization/workhorses.py +7 -8
  36. pynibs/regression/__init__.py +4 -2
  37. pynibs/regression/dual_node_detection.py +229 -219
  38. pynibs/regression/regression.py +92 -61
  39. pynibs/roi/__init__.py +4 -1
  40. pynibs/roi/roi_structs.py +19 -21
  41. pynibs/roi/{roi.py → roi_utils.py} +56 -33
  42. pynibs/subject.py +24 -14
  43. pynibs/util/__init__.py +20 -4
  44. pynibs/util/dosing.py +4 -5
  45. pynibs/util/quality_measures.py +39 -38
  46. pynibs/util/rotations.py +116 -9
  47. pynibs/util/{simnibs.py → simnibs_io.py} +29 -19
  48. pynibs/util/{util.py → utils.py} +20 -22
  49. pynibs/visualization/para.py +4 -4
  50. pynibs/visualization/render_3D.py +4 -4
  51. pynibs-0.2026.1.dist-info/METADATA +105 -0
  52. pynibs-0.2026.1.dist-info/RECORD +69 -0
  53. {pyNIBS-0.2024.8.dist-info → pynibs-0.2026.1.dist-info}/WHEEL +1 -1
  54. pyNIBS-0.2024.8.dist-info/METADATA +0 -723
  55. pyNIBS-0.2024.8.dist-info/RECORD +0 -107
  56. pynibs/data/configuration_exp0.yaml +0 -59
  57. pynibs/data/configuration_linear_MEP.yaml +0 -61
  58. pynibs/data/configuration_linear_RT.yaml +0 -61
  59. pynibs/data/configuration_sigmoid4.yaml +0 -68
  60. pynibs/data/network mapping configuration/configuration guide.md +0 -238
  61. pynibs/data/network mapping configuration/configuration_TEMPLATE.yaml +0 -42
  62. pynibs/data/network mapping configuration/configuration_for_testing.yaml +0 -43
  63. pynibs/data/network mapping configuration/configuration_modelTMS.yaml +0 -43
  64. pynibs/data/network mapping configuration/configuration_reg_isi_05.yaml +0 -43
  65. pynibs/data/network mapping configuration/output_documentation.md +0 -185
  66. pynibs/data/network mapping configuration/recommendations_for_accuracy_threshold.md +0 -77
  67. pynibs/data/neuron/models/L23_PC_cADpyr_biphasic_v1.csv +0 -1281
  68. pynibs/data/neuron/models/L23_PC_cADpyr_monophasic_v1.csv +0 -1281
  69. pynibs/data/neuron/models/L4_LBC_biphasic_v1.csv +0 -1281
  70. pynibs/data/neuron/models/L4_LBC_monophasic_v1.csv +0 -1281
  71. pynibs/data/neuron/models/L4_NBC_biphasic_v1.csv +0 -1281
  72. pynibs/data/neuron/models/L4_NBC_monophasic_v1.csv +0 -1281
  73. pynibs/data/neuron/models/L4_SBC_biphasic_v1.csv +0 -1281
  74. pynibs/data/neuron/models/L4_SBC_monophasic_v1.csv +0 -1281
  75. pynibs/data/neuron/models/L5_TTPC2_cADpyr_biphasic_v1.csv +0 -1281
  76. pynibs/data/neuron/models/L5_TTPC2_cADpyr_monophasic_v1.csv +0 -1281
  77. pynibs/tests/data/InstrumentMarker20200225163611937.xml +0 -19
  78. pynibs/tests/data/TriggerMarkers_Coil0_20200225163443682.xml +0 -14
  79. pynibs/tests/data/TriggerMarkers_Coil1_20200225170337572.xml +0 -6373
  80. pynibs/tests/data/Xdmf.dtd +0 -89
  81. pynibs/tests/data/brainsight_niiImage_nifticoord.txt +0 -145
  82. pynibs/tests/data/brainsight_niiImage_nifticoord_largefile.txt +0 -1434
  83. pynibs/tests/data/brainsight_niiImage_niifticoord_mixedtargets.txt +0 -47
  84. pynibs/tests/data/create_subject_testsub.py +0 -332
  85. pynibs/tests/data/data.hdf5 +0 -0
  86. pynibs/tests/data/geo.hdf5 +0 -0
  87. pynibs/tests/test_coil.py +0 -474
  88. pynibs/tests/test_elements2nodes.py +0 -100
  89. pynibs/tests/test_hdf5_io/test_xdmf.py +0 -61
  90. pynibs/tests/test_mesh_transformations.py +0 -123
  91. pynibs/tests/test_mesh_utils.py +0 -143
  92. pynibs/tests/test_nnav_imports.py +0 -101
  93. pynibs/tests/test_quality_measures.py +0 -117
  94. pynibs/tests/test_regressdata.py +0 -289
  95. pynibs/tests/test_roi.py +0 -17
  96. pynibs/tests/test_rotations.py +0 -86
  97. pynibs/tests/test_subject.py +0 -71
  98. pynibs/tests/test_util.py +0 -24
  99. /pynibs/{regression/score_types.py → neuron/models/m1_montbrio.py} +0 -0
  100. {pyNIBS-0.2024.8.dist-info → pynibs-0.2026.1.dist-info/licenses}/LICENSE +0 -0
  101. {pyNIBS-0.2024.8.dist-info → pynibs-0.2026.1.dist-info}/top_level.txt +0 -0
@@ -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, type="cfs")
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 = current_time_string = csv_parser.data[0][24] # 25. column of CSv data = time stamp
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
- fn_conform_nii=nii_conform_path,
426
- m_nnav=m_nnav,
427
- nnav_system="brainsight",
428
- mesh_approach="headreco",
429
- fiducials=None,
430
- orientation='RAS',
431
- fsl_cmd=None,
432
- target='simnibs',
433
- temp_dir=temp_dir,
434
- rem_tmp=True,
435
- verbose=verbose)
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.NaN)
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
- tms_pulse_time=d_bs[f"Offset"][i],
524
- sampling_rate=1000 / d_bs["EMG Res."][i],
525
- start_mep=start_mep, end_mep=end_mep,
526
- measurement_start_time=float(d["EMG Start"][i]),
527
- fn_plot=fn_plot)
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
- fn_geo_hdf5=fn_mesh_hdf5,
562
- remove_coil_skin_distance_outlier=remove_coil_skin_distance_outlier,
563
- fn_plot=os.path.split(fn_exp_hdf5)[0])
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
- as 'Targets'.
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
- systems.
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 (matsimnibs_samples is None or matsimnibs_samples.size == 0):
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
- round(event["POS"] * cfs_header["Samplingrate"])
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
- cfs_emg[trigger_event_idcs[0]:, c_idx],
99
- (num_sweeps, samples_per_sweep)
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
- 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
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
- fn_mesh_hdf5=fn_mesh_hdf5,
174
- distance=1,
175
- remove_coil_skin_distance_outlier=remove_coil_skin_distance_outlier)
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
- 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
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. ´´".../fs_subjectID/subjectID_T1fs_conform.nii.gz"´´.
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.: ´´"/data/pt_01756/probands/33791.b8/exp/1/33791.b8_recording/MRI/33791.b8_recording.mri"´´
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) # TODO: remove hardcoded dists
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
@@ -183,6 +183,9 @@ def linear_log(x, m, n):
183
183
  return y
184
184
 
185
185
 
186
+ def step(x, a): return (x > a).astype(int)
187
+
188
+
186
189
  def exp(x, x0, r, y0):
187
190
  """
188
191
  Parametrized exponential function.
@@ -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