caps
tit.caps ¶
EEG caps on an existing head model, without re-running charm.
Two jobs, both plain functions:
- :func:
warp_capsdoes exactly what charm does at the end of a head-model build (SimNIBS 4.6segmentation/charm_main.py, "Transforming EEG positions"): crop the head mesh to its skin triangles and callsimnibs.utils.transformations.warp_coordinates(cap, m2m, "mni2subject", out_name=<eeg_positions>/<cap>.csv, out_geo=<...>.geo, mesh_in=skin, skin_tag=1005)for each cap. Sources are SimNIBS'sresources/ElectrodeCaps_MNI(which in the image includes TI-Toolbox's caps) and the project's own MNI caps undercode/ti-toolbox/eeg_caps/. - :func:
net_stats/ :func:compare_netsinspect a net already on the head: nearest-neighbour spacing, distance to the scalp, electrodes that collapsed onto each other (the failure a cap in the wrong space produces -- 43 GSN-128 electrodes on one scalp point before 6361c2b2), duplicate labels, and the nearest electrode of a second net.
simnibs is imported inside the functions that need it: the route module
that calls these must import nothing heavy (dev/route_import_guard.py).
CapError ¶
Bases: ValueError
An MNI cap file that cannot be warped as it stands.
simnibs_caps_dir ¶
simnibs_caps_dir() -> Path
SimNIBS's resources/ElectrodeCaps_MNI -- the folder charm warps.
Located with find_spec rather than by importing
simnibs.utils.file_finder (which defines it the same way, from the
package directory): that import costs ~4 s in the image and was most of
the caps list's first answer.
Source code in tit/caps.py
has_electrodes ¶
Whether the cap file at path has at least one Electrode row.
The catalog's rule (tit.catalog._eeg_caps_with_electrodes), shared so
the caps panel and every net picker agree: SimNIBS's Fiducials.csv
(Nz/Iz/LPA/RPA, which charm also writes to every eeg_positions) is a
landmark file, not a cap. A file that is not text has no electrodes.
:func:validate_mni_cap refuses an upload this rule would hide.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike
|
A cap CSV, MNI or warped. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
landmark-only or ReferenceElectrode-only file, a missing file or one |
bool
|
that is not UTF-8 text. |
Source code in tit/caps.py
source_caps ¶
{cap file name: (source, path)} for every MNI cap that can be warped.
source is "ti-toolbox" (a cap TI-Toolbox ships, even where it
replaces a SimNIBS file of that name), "simnibs" or "project". A
project cap never shadows an image one: the upload route refuses the name.
A file with no electrode (:func:has_electrodes) is not a cap.
Source code in tit/caps.py
available_caps ¶
Every source cap plus every net already on sid, with their status.
subject_mtime is when the subject's copy was written (applied). A file
on the subject with no electrode (Fiducials.csv) is not a net.
Source code in tit/caps.py
warp_caps ¶
Warp each MNI cap onto the head, as charm does; one mesh read for all.
Each cap is written to a hidden temporary name and renamed into place, so a warp that fails half-way leaves the previous file (or none) rather than a truncated net that the rest of the app would read.
Source code in tit/caps.py
normalise_mni_cap ¶
text as the upload route stores it: no byte-order mark, no blank rows, \n ends.
SimNIBS's read_csv_positions reads row[0] of every row, so a blank
row in the middle of a cap raises IndexError; it refuses a file whose
last row has fewer than three cells, so one blank line too many at the end
raises OSError; and a byte-order mark (Excel's "CSV UTF-8") becomes
part of the first type cell, so that row is dropped. Removing them changes
no electrode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The cap file's text as uploaded. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The same rows, cell for cell, re-serialised by :func: |
Raises:
| Type | Description |
|---|---|
CapError
|
The text is not a readable CSV file. |
Source code in tit/caps.py
validate_mni_cap ¶
Number of electrodes in an MNI cap CSV, or raise :class:CapError.
The format charm reads, as SimNIBS's read_csv_positions reads it:
Type,x,y,z,name[,...] (the Type,x,y,z,x2,y2,z2,name form SimNIBS
also reads is refused: every TI-Toolbox reader takes the name from cell 5),
with Type spelled exactly Electrode / ReferenceElectrode /
Fiducial. Untrusted input: the :func:normalise_mni_cap form of
text is checked row by row, and nothing is accepted that the warp would
skip, crash on or file under another name -- a type in another case or
with a space (skipped), a name with spaces around it (kept with them, so
Cz is not found), a repeated electrode name (the net keeps one), a
later Fiducial row that takes an electrode's name at another point
(_get_eeg_positions keys every row by name, the last one wins, so the
electrode would be placed on the fiducial). A file with no Electrode
row is refused too: :func:has_electrodes would not list it as a cap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The cap file's text. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The number of |
Raises:
| Type | Description |
|---|---|
CapError
|
The first rule the file breaks, naming its row. |
Source code in tit/caps.py
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | |
nearest_distance ¶
Distance from each point to its nearest cloud point.
ponytail: brute force as one matrix product (|p|^2 + |c|^2 - 2 p.c), so
256 electrodes x 35 k skin nodes is one BLAS call instead of a Python
loop of broadcasts; the nearest node is then re-measured exactly, so the
product's round-off never reaches the answer. A KD-tree if a cloud gets
much larger (scipy is mocked in the host suite).
Source code in tit/caps.py
skin_nodes ¶
net_stats ¶
net_stats(names: list[str], world: ndarray, skin: ndarray | None = None, collapse_mm: float = COLLAPSE_MM) -> dict[str, Any]
Per-electrode spacing and scalp distance, collapsed pairs, duplicate labels.
Two electrodes closer than collapse_mm are collapsed, unless their
labels are one of the 10-20 :data:ALIAS_PAIRS; those are listed under
aliases instead, since a cap carrying both names puts them on one point.
Source code in tit/caps.py
net_points ¶
Names and world positions of a parsed net's electrodes and reference.
parsed is :func:tit.scene.build.parse_electrode_csv's split. The
reference is a physical electrode on the head, so it counts for spacing;
fiducials are landmarks, not electrodes, and do not.
Source code in tit/caps.py
collapsed_pairs ¶
The electrode pairs of a parsed net that sit on one point (aliases excluded).
net_file_collapsed ¶
:func:collapsed_pairs of the net CSV at path; [] if it cannot be read.
Source code in tit/caps.py
collapsed_summary ¶
What is wrong with a collapsed net: how many pairs, and the first three.
Source code in tit/caps.py
collapsed_message ¶
:func:collapsed_summary and the fix, as one message for an error.
compare_nets ¶
For each electrode of net A, the nearest electrode of net B and its distance.