generated from FNNDSC/python-chrisapp-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
isws.py
executable file
·97 lines (79 loc) · 3.21 KB
/
isws.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/env python
import os
import sys
from argparse import ArgumentParser, Namespace, ArgumentDefaultsHelpFormatter
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from typing import BinaryIO, Sequence, Literal, Optional
import subprocess
from loguru import logger
from chris_plugin import chris_plugin, PathMapper
from civet.extraction import Side, IrregularSurface
SIDE_OPTIONS = ('left', 'right', 'auto', 'none')
SideStr = Literal['left', 'right', 'auto', 'none']
parser = ArgumentParser(description='Resample surface mesh to have 81,920 triangles.',
formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('-s', '--side', default='auto', choices=SIDE_OPTIONS,
help='brain hemisphere side. "auto" => infer from file name')
parser.add_argument('-p', '--pattern', default='**/*.obj',
help='pattern for file names to include')
parser.add_argument('-q', '--quiet', action='store_true',
help='disable status messages')
parser.add_argument('--no-fail', action='store_true', dest='no_fail',
help='do not produce non-zero exit status on failures')
def isws(surface: Path, output: Path, side: SideStr) -> None:
log_path = output.with_suffix('.log')
try:
logger.info('Processing {} to {}, log: {}', surface, output, log_path)
with log_path.open('wb') as log:
IrregularSurface(surface)\
.interpolate_with_sphere(pick_side(surface, side))\
.save(output, shell=curry_log(log))
logger.info('Completed {}', output)
except Exception as e:
logger.exception('Failed to process {}', surface)
raise e
def curry_log(log: BinaryIO):
def run_with_log(cmd: Sequence[str | os.PathLike]) -> None:
subprocess.run(cmd, stderr=log, stdout=log, check=True)
return run_with_log
def pick_side(input_path: Path, side: SideStr) -> Optional[Side]:
if side == 'left':
return Side.LEFT
if side == 'right':
return Side.RIGHT
if side == 'auto':
path = str(input_path).lower()
if 'left' in path:
return Side.LEFT
if 'right' in path:
return Side.RIGHT
raise ValueError(f'Substring "left" nor "right" found in: {path}')
if side == 'none':
return None
raise ValueError(f'side must be one of: {SIDE_OPTIONS}')
@chris_plugin(
parser=parser,
category='MRI Processing',
min_memory_limit='100Mi',
min_cpu_limit='1000m',
)
def main(options: Namespace, inputdir: Path, outputdir: Path):
if options.quiet:
logger.remove()
logger.add(sys.stderr, level='WARNING')
nproc = len(os.sched_getaffinity(0))
logger.debug('Using {} threads.', nproc)
results = []
with ThreadPoolExecutor(max_workers=nproc) as pool:
mapper = PathMapper(inputdir, outputdir, glob=options.pattern, suffix='._81920.obj')
for mc_surface, output in mapper:
results.append(pool.submit(isws, mc_surface, output, options.side))
if options.no_fail:
return
for future in results:
e = future.exception()
if e is not None:
raise e
if __name__ == '__main__':
main()