Skip to content

Commit 75dc052

Browse files
authored
Merge pull request #72 from khanlab/chain-multiple-labels
Add a feature to merge 3 labels simultaneously
2 parents 7c76534 + 968fd10 commit 75dc052

File tree

6 files changed

+224
-7
lines changed

6 files changed

+224
-7
lines changed

.github/workflows/wetrun_test.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,13 @@ jobs:
3737
./labelmerge/run.py test_data/bids_wetrun_testing/tpl-MNI152NLin2009cAsym test_out participant \
3838
--base-desc 100Parcels7Networks --overlay_bids_dir test_data/bids_wetrun_testing/tpl-MNI152NLin2009cAsym \
3939
--overlay_desc tn --cores all --force-output --conda-frontend mamba | tee labelmerge_output.log
40+
<<<<<<< chain-multiple-labels
41+
- name: Run wet-run test for merging 3 labels
42+
shell: bash -l {0}
43+
run: |
44+
conda activate snakebids-env
45+
./labelmerge/run.py test_data/bids_wetrun_testing/tpl-MNI152NLin2009cAsym test_out participant \
46+
--base-desc 100Parcels7Networks --overlay_bids_dir test_data/bids_wetrun_testing/tpl-MNI152NLin2009cAsym \
47+
--overlay_desc tn --overlay2_bids_dir test_data/bids_wetrun_testing/tpl-MNI152NLin2009cAsym/ --overlay2_desc carpet --cores all --force-output --conda-frontend mamba | tee labelmerge_output.log
48+
=======
49+
>>>>>>> integrate-conda

labelmerge/config/snakebids.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,24 +70,37 @@ parse_args:
7070
help: Directory containing labelmaps to be overlaid on the base input labelmaps.
7171
nargs: '?'
7272
type: Path
73+
--overlay2_bids_dir:
74+
help: Directory containing labelmaps to be overlaid on the combined labelmaps.
75+
nargs: '?'
76+
type: Path
7377
--base_desc:
7478
help: Description entity for base labelmaps
7579
nargs: '?'
7680
--overlay_desc:
7781
help: Description entity for overlay labelmaps
7882
nargs: '?'
83+
--overlay2_desc:
84+
help: Description entity for second overlay labelmaps
85+
nargs: '?'
7986
--base_exceptions:
8087
help: Space separated integer labels from the base labelmap to keep over overlay labels at the same voxels.
8188
nargs: '*'
8289
--overlay_exceptions:
8390
help: Space separated integer labels from the overlay image to be overwritten by base labels at the same voxels.
8491
nargs: '*'
92+
--overlay2_exceptions:
93+
help: Space separated integer labels from the overlay image 2 to be overwritten by the combined labels at the same voxels.
94+
nargs: '*'
8595
--base_drops:
8696
help: Space separated integer labels from the base image to drop from the output.
8797
nargs: '*'
8898
--overlay_drops:
8999
help: Space separated integer labels from the overlay image to drop from the output.
90100
nargs: '*'
101+
--overlay2_drops:
102+
help: Space separated integer labels from the second overlay image to drop from the output.
103+
nargs: '*'
91104

92105
# Workflow specific config
93106

labelmerge/workflow/Snakefile

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ base_config = deepcopy(config["pybids_inputs"])
3131
base_config["labelmap"]["filters"]["desc"] = config["base_desc"]
3232
overlay_config = deepcopy(config["pybids_inputs"])
3333
overlay_config["labelmap"]["filters"]["desc"] = config["overlay_desc"]
34+
overlay2_config = deepcopy(config["pybids_inputs"])
35+
overlay2_config["labelmap"]["filters"]["desc"] = config["overlay2_desc"]
3436

3537
base_inputs = generate_inputs(
3638
bids_dir=config["bids_dir"],
@@ -46,6 +48,17 @@ overlay_inputs = generate_inputs(
4648
pybids_inputs=overlay_config,
4749
participant_label=config["participant_label"],
4850
)
51+
overlay2_inputs = (
52+
generate_inputs(
53+
bids_dir=config.get("overlay2_bids_dir", config["bids_dir"]),
54+
pybids_config=["bids", "derivatives"],
55+
derivatives=config["derivatives"],
56+
pybids_inputs=overlay2_config,
57+
participant_label=config["participant_label"],
58+
)
59+
if config.get("overlay2_bids_dir")
60+
else None
61+
)
4962

5063

5164
# this adds constraints to the bids naming
@@ -60,12 +73,29 @@ wildcard_constraints:
6073
include: "rules/label_harmonization.smk"
6174

6275

76+
all_targets = []
77+
78+
if config.get("overlay2_bids_dir"):
79+
80+
# when overlay2 is present, the *only* final targets you care about
81+
# are the merge_labels_again outputs
82+
all_targets += overlay2_inputs["labelmap"].expand(
83+
rules.merge_labels_again.output["merged_map"],
84+
)
85+
all_targets += overlay2_inputs["labelmap"].expand(
86+
rules.merge_labels_again.output["merged_metadata"],
87+
)
88+
else:
89+
# when overlay2 is *not* present, your finals are the simple two-way merge
90+
all_targets += base_inputs["labelmap"].expand(
91+
rules.merge_labels.output["merged_map"],
92+
)
93+
all_targets += base_inputs["labelmap"].expand(
94+
rules.merge_labels.output["merged_metadata"],
95+
)
96+
97+
6398
rule all:
6499
input:
65-
base_inputs["labelmap"].expand(
66-
rules.merge_labels.output["merged_map"],
67-
),
68-
base_inputs["labelmap"].expand(
69-
rules.merge_labels.output["merged_metadata"],
70-
),
100+
all_targets,
71101
default_target: True

labelmerge/workflow/rules/label_harmonization.smk

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,16 @@ def build_metadata_path(wildcards):
5252
Path(expand(overlay_inputs["labelmap"].path, **wildcards)[0]),
5353
config["overlay_bids_dir"],
5454
)
55-
return {
55+
out = {
5656
"base_metadata": base_metadata,
5757
"overlay_metadata": overlay_metadata,
5858
}
59+
if config.get("overlay2_bids_dir"):
60+
out["overlay2_metadata"] = load_metadata(
61+
Path(expand(overlay2_inputs["labelmap"].path, **wildcards)[0]),
62+
config["overlay2_bids_dir"],
63+
)
64+
return out
5965

6066

6167
rule merge_labels:
@@ -99,3 +105,43 @@ rule merge_labels:
99105
"{output.merged_map} {output.merged_metadata} "
100106
"{params.base_exceptions} {params.overlay_exceptions} "
101107
"{params.base_drops} {params.overlay_drops}"
108+
109+
110+
rule merge_labels_again:
111+
input:
112+
unpack(build_metadata_path),
113+
base2_metadata=rules.merge_labels.output.merged_metadata,
114+
base_map=rules.merge_labels.output.merged_map,
115+
overlay_map=overlay2_inputs["labelmap"].path
116+
if config.get("overlay2_bids_dir")
117+
else "",
118+
output:
119+
merged_map=bids(
120+
root=str(Path(config["output_dir"]) / "combined2"),
121+
suffix="dseg.nii.gz",
122+
desc="combined2",
123+
**base_inputs["labelmap"].wildcards,
124+
),
125+
merged_metadata=bids(
126+
root=str(Path(config["output_dir"]) / "combined2"),
127+
suffix="dseg.tsv",
128+
desc="combined2",
129+
**base_inputs["labelmap"].wildcards,
130+
),
131+
params:
132+
overlay_exceptions=f"--overlay2_exceptions {' '.join(config['overlay2_exceptions'])}"
133+
if config.get("overlay2_exceptions")
134+
else "",
135+
overlay_drops=f"--overlay2_drops {' '.join(config['overlay2_drops'])}"
136+
if config.get("overlay2_drops")
137+
else "",
138+
resources:
139+
script=str(Path(workflow.basedir) / "scripts" / "labelmerge.py"),
140+
conda:
141+
"../envs/merge_labels.yaml"
142+
shell:
143+
"python3 {resources.script} {input.base_map} {input.base2_metadata} "
144+
"{input.overlay_map} {input.overlay2_metadata} "
145+
"{output.merged_map} {output.merged_metadata} "
146+
"{params.overlay_exceptions} "
147+
"{params.overlay_drops}"
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
index name xyz
2+
0 background 48, 57, 48
3+
1 Left Cerebral White Matter 35, 58, 48
4+
2 Right Cerebral White Matter 62, 59, 48
5+
3 Left Lateral Ventricle 42, 60, 46
6+
4 Right Lateral Ventricle 55, 60, 46
7+
5 Brain-Stem 48, 50, 22
8+
34 Left Thalamus 43, 56, 42
9+
35 Left Caudate 42, 71, 44
10+
36 Left Putamen 36, 66, 39
11+
37 Left Pallidum 38, 64, 38
12+
39 Left Hippocampus 36, 55, 32
13+
40 Left Amygdala 37, 64, 30
14+
41 Left Accumbens 43, 72, 35
15+
45 Right Thalamus 53, 57, 42
16+
46 Right Caudate 55, 71, 44
17+
47 Right Putamen 61, 67, 39
18+
48 Right Pallidum 58, 64, 38
19+
49 Right Hippocampus 61, 56, 32
20+
50 Right Amygdala 60, 64, 30
21+
51 Right Accumbens 53, 72, 36
22+
101 Left Frontal Pole 35, 92, 43
23+
102 Right Frontal Pole 61, 92, 43
24+
103 Left Insular Cortex 30, 67, 39
25+
104 Right Insular Cortex 67, 67, 39
26+
105 Left Superior Frontal Gyrus 41, 76, 67
27+
106 Right Superior Frontal Gyrus 56, 75, 68
28+
107 Left Middle Frontal Gyrus 29, 75, 60
29+
108 Right Middle Frontal Gyrus 68, 75, 61
30+
109 Left Inferior Frontal Gyrus pars triangularis 23, 80, 43
31+
110 Right Inferior Frontal Gyrus pars triangularis 74, 80, 43
32+
111 Left Inferior Frontal Gyrus pars opercularis 22, 73, 47
33+
112 Right Inferior Frontal Gyrus pars opercularis 75, 74, 47
34+
113 Left Precentral Gyrus 31, 60, 64
35+
114 Right Precentral Gyrus 66, 61, 64
36+
115 Left Temporal Pole 28, 72, 24
37+
116 Right Temporal Pole 68, 73, 24
38+
117 Left Superior Temporal Gyrus anterior division 20, 64, 35
39+
118 Right Superior Temporal Gyrus anterior division 77, 65, 34
40+
119 Left Superior Temporal Gyrus posterior division 17, 52, 41
41+
120 Right Superior Temporal Gyrus posterior division 79, 54, 40
42+
121 Left Middle temporal Gyrus anterior division 19, 64, 28
43+
122 Right Middle temporal Gyrus anterior division 77, 65, 27
44+
123 Left Middle temporal Gyrus posterior division 17, 52, 33
45+
124 Right Middle temporal Gyrus posterior division 79, 55, 33
46+
125 Left Middle Temporal Gyrus temporooccipital part 19, 39, 39
47+
126 Right Middle Temporal Gyrus temporooccipital part 78, 41, 40
48+
127 Left Inferior Temporal Gyrus anterior division 24, 63, 19
49+
128 Right Inferior Temporal Gyrus anterior division 71, 65, 18
50+
129 Left Inferior Temporal Gyrus posterior division 21, 52, 25
51+
130 Right Inferior Temporal Gyrus posterior division 75, 54, 24
52+
131 Left Inferior Temporal Gyrus temporooccipital part 22, 39, 30
53+
132 Right Inferior Temporal Gyrus temporooccipital part 75, 41, 30
54+
133 Left Postcentral Gyrus 29, 52, 65
55+
134 Right Postcentral Gyrus 67, 53, 66
56+
135 Left Superior Parietal Lobule 33, 41, 68
57+
136 Right Superior Parietal Lobule 63, 42, 69
58+
137 Left Supramarginal Gyrus anterior division 19, 50, 58
59+
138 Right Supramarginal Gyrus anterior division 78, 52, 58
60+
139 Left Supramarginal Gyrus posterior division 20, 43, 56
61+
140 Right Supramarginal Gyrus posterior division 76, 46, 56
62+
141 Left Angular Gyrus 23, 38, 54
63+
142 Right Angylar Gyrus 75, 40, 56
64+
143 Left Lateral Occipital Cortex superior division 32, 29, 59
65+
144 Right Lateral Occipital Cortext superior division 65, 30, 59
66+
145 Left Lateral Occipital Cortex inferior division 25, 28, 38
67+
146 Right Lateral Occipital Cortext inferior division 71, 29, 38
68+
147 Left Intracalcarine Cortex 44, 29, 43
69+
148 Right Intracalcarine Cortex 53, 29, 43
70+
149 Left Frontal Medial Cortex 46, 88, 30
71+
150 Right Frontal Medial Cortex 50, 88, 29
72+
151 Left Juxtapositional Lobule Cortex formerly Supplmentary Motor Cortex 46, 65, 68
73+
152 Right Juxtapositional Lobule Cortex formerly Supplmentary Motor Cortex 51, 65, 68
74+
153 Left Subcalosal Cortex 46, 75, 32
75+
154 Right Subcallosal Cortex 50, 76, 31
76+
155 Left Paracingulate Gyrus 45, 84, 50
77+
156 Right Paracingulate Gyrus 51, 84, 50
78+
157 Left Cingulate Gyrus anterior division 46, 75, 51
79+
158 Right Cingulate Gyrus anterior division 51, 76, 51
80+
159 Left Cingulate Gyrus posterior division 46, 47, 54
81+
160 Right Cingulate Gyrus posterior division 51, 48, 54
82+
161 Left Precuneous Cortex 45, 36, 58
83+
162 Right Precuneous Cortex 52, 37, 59
84+
163 Left Cuneal Cortex 45, 26, 53
85+
164 Right Cuneal Cortex 52, 27, 53
86+
165 Left Frontal Orbital Cortex 33, 77, 30
87+
166 Right Frontal Orbital Cortex 63, 77, 31
88+
167 Left Parahippocampal Gyrus anterior division 37, 62, 24
89+
168 Right Parahippocampal Gyrus anterior division 59, 62, 24
90+
169 Left Parahippocampal Gyrus posterior division 37, 50, 31
91+
170 Right Parahippocampal Gyrus posterior division 59, 51, 30
92+
171 Left Lingual Gyrus 43, 33, 36
93+
172 Right Lingual Gyrus 55, 34, 36
94+
173 Left Temporal Fusiform Cortex anterior division 32, 64, 18
95+
174 Right Temporal Fusiform Cortex anterior division 63, 64, 17
96+
175 Left Temporal Fusiform Cortex posterior division 30, 52, 26
97+
176 Right Temporal Fusiform Cortex posterior division 66, 54, 25
98+
177 Left Temporal Occipital Fusiform Cortex 31, 39, 31
99+
178 Right Temporal Occipital Fusiform Cortex 66, 41, 30
100+
179 Left Occipital Fusiform Gyrus 35, 27, 31
101+
180 Right Occipital Fusiform Gyrus 61, 28, 32
102+
181 Left Frontal Operculum Cortex 28, 75, 41
103+
182 Right Frontal Operculum Cortex 69, 75, 41
104+
183 Left Central Opercular Cortex 24, 62, 45
105+
184 Right Central Opercular Cortex 73, 63, 44
106+
185 Left Parietal Operculum Cortex 23, 50, 49
107+
186 Right Parietal Operculum Cortex 73, 52, 50
108+
187 Left Planum Polare 24, 63, 36
109+
188 Right Planum Polare 72, 65, 36
110+
189 Left Heschls Gyrus 25, 56, 43
111+
190 Right Heschls Gyrus 71, 58, 43
112+
191 Left Planum Temporale 21, 52, 44
113+
192 Right Planum Temporale 76, 54, 45
114+
193 Left Supracalcarine Cortex 45, 28, 46
115+
194 Right Supracalcarine Cortex 52, 29, 46
116+
195 Left Occipital Pole 40, 18, 42
117+
196 Right Occipital Pole 57, 18, 43
118+
255 Cerebellum and Midbrain 48, 36, 21

0 commit comments

Comments
 (0)