nmcp-precomputed 3.0.4__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.
- nmcp/__init__.py +4 -0
- nmcp/__main__.py +13 -0
- nmcp/data/__init__.py +2 -0
- nmcp/data/precomputed_entry.py +10 -0
- nmcp/data/remote_data_client.py +466 -0
- nmcp/from_json.py +34 -0
- nmcp/from_service.py +32 -0
- nmcp/list_skeletons.py +24 -0
- nmcp/precomputed/__init__.py +5 -0
- nmcp/precomputed/nmcp_precomputed.py +252 -0
- nmcp/precomputed/nmcp_skeleton.py +142 -0
- nmcp/precomputed/segment_info.py +61 -0
- nmcp/precomputed/segment_property.py +33 -0
- nmcp/precomputed/segment_tag_property.py +85 -0
- nmcp/precomputed_worker.py +273 -0
- nmcp/remove_skeleton.py +21 -0
- nmcp_precomputed-3.0.4.dist-info/METADATA +17 -0
- nmcp_precomputed-3.0.4.dist-info/RECORD +19 -0
- nmcp_precomputed-3.0.4.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import threading
|
|
4
|
+
|
|
5
|
+
from .data import RemoteDataClient, PrecomputedEntry
|
|
6
|
+
from .precomputed import ensure_bucket_folders, create_from_data, extract_neuron_properties, SkeletonComponents
|
|
7
|
+
|
|
8
|
+
logging.basicConfig(level=logging.WARNING)
|
|
9
|
+
logging.getLogger("nmcp").setLevel(logging.DEBUG)
|
|
10
|
+
logging.getLogger(__name__).setLevel(logging.DEBUG)
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
process_interval: int = 10 # seconds
|
|
15
|
+
heartbeat_interval: int = 3600 # seconds
|
|
16
|
+
|
|
17
|
+
heartbeat_count_limit: int = int(heartbeat_interval / process_interval)
|
|
18
|
+
heartbeat_current_count: int = 0
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def load_specimen_space_reconstruction(client: RemoteDataClient, pending: PrecomputedEntry):
|
|
22
|
+
reconstruction_id = pending.reconstructionId
|
|
23
|
+
skeleton_id = pending.skeletonId
|
|
24
|
+
|
|
25
|
+
data = client.get_specimen_space_reconstruction_data(reconstruction_id)
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
# Extract properties once from header
|
|
29
|
+
properties = extract_neuron_properties(data)
|
|
30
|
+
|
|
31
|
+
axon_components = SkeletonComponents.create(data["axon"])
|
|
32
|
+
|
|
33
|
+
dendrite_components = SkeletonComponents.create(data["dendrite"])
|
|
34
|
+
|
|
35
|
+
return axon_components, dendrite_components, properties
|
|
36
|
+
|
|
37
|
+
except Exception as ex:
|
|
38
|
+
logger.error(f"error loading specimen space reconstruction {reconstruction_id}: {ex}")
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def load_reconstruction(client: RemoteDataClient, pending: PrecomputedEntry):
|
|
43
|
+
header_data = client.get_reconstruction_header(pending.reconstructionId)
|
|
44
|
+
|
|
45
|
+
reconstruction_id = pending.reconstructionId
|
|
46
|
+
skeleton_id = pending.skeletonId
|
|
47
|
+
|
|
48
|
+
if header_data is None or reconstruction_id is None or skeleton_id is None:
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
logger.info(f"{reconstruction_id} with skeleton id {skeleton_id} has header data available")
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
# Extract properties once from header
|
|
55
|
+
properties = extract_neuron_properties(header_data)
|
|
56
|
+
|
|
57
|
+
# Process axon data in true chunks
|
|
58
|
+
logger.info(f"retrieving axon data in chunks for {reconstruction_id}")
|
|
59
|
+
axon_components = None
|
|
60
|
+
chunk_size = 25000
|
|
61
|
+
axon_offset = 0
|
|
62
|
+
axon_total_points = 0
|
|
63
|
+
|
|
64
|
+
while True:
|
|
65
|
+
logger.debug(f"fetching axon chunk at offset {axon_offset} with size {chunk_size}")
|
|
66
|
+
axon_result = client.get_axon_chunks(
|
|
67
|
+
reconstruction_id,
|
|
68
|
+
chunk_size=chunk_size,
|
|
69
|
+
offset=axon_offset,
|
|
70
|
+
limit=chunk_size
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if not axon_result or not axon_result["data"]:
|
|
74
|
+
logger.debug("no more axon data available")
|
|
75
|
+
break
|
|
76
|
+
|
|
77
|
+
chunk_points = axon_result["data"]
|
|
78
|
+
chunk_count = len(chunk_points)
|
|
79
|
+
|
|
80
|
+
if axon_components is None:
|
|
81
|
+
# First chunk - create new SkeletonComponents
|
|
82
|
+
logger.debug(f"creating axon components with {chunk_count} points")
|
|
83
|
+
axon_components = SkeletonComponents.create(chunk_points)
|
|
84
|
+
else:
|
|
85
|
+
# Subsequent chunks - append to existing
|
|
86
|
+
logger.debug(f"appending {chunk_count} points to axon components")
|
|
87
|
+
axon_components.append(chunk_points)
|
|
88
|
+
|
|
89
|
+
axon_total_points += chunk_count
|
|
90
|
+
|
|
91
|
+
# Check if we got less than requested (end of data)
|
|
92
|
+
if chunk_count < chunk_size:
|
|
93
|
+
logger.debug(f"received {chunk_count} < {chunk_size}, end of axon data")
|
|
94
|
+
break
|
|
95
|
+
|
|
96
|
+
axon_offset += chunk_count
|
|
97
|
+
|
|
98
|
+
if axon_components is not None:
|
|
99
|
+
logger.info(f"assembled axon with {axon_total_points} total points")
|
|
100
|
+
|
|
101
|
+
# Process dendrite data in true chunks
|
|
102
|
+
logger.info(f"retrieving dendrite data in chunks for {reconstruction_id}")
|
|
103
|
+
dendrite_components = None
|
|
104
|
+
|
|
105
|
+
dendrite_offset = 0
|
|
106
|
+
dendrite_total_points = 0
|
|
107
|
+
|
|
108
|
+
while True:
|
|
109
|
+
logger.debug(f"fetching dendrite chunk at offset {dendrite_offset} with size {chunk_size}")
|
|
110
|
+
dendrite_result = client.get_dendrite_chunks(
|
|
111
|
+
reconstruction_id,
|
|
112
|
+
chunk_size=chunk_size,
|
|
113
|
+
offset=dendrite_offset,
|
|
114
|
+
limit=chunk_size
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if not dendrite_result or not dendrite_result["data"]:
|
|
118
|
+
logger.debug("no more dendrite data available")
|
|
119
|
+
break
|
|
120
|
+
|
|
121
|
+
chunk_points = dendrite_result["data"]
|
|
122
|
+
chunk_count = len(chunk_points)
|
|
123
|
+
|
|
124
|
+
if dendrite_components is None:
|
|
125
|
+
# First chunk - create new SkeletonComponents
|
|
126
|
+
logger.debug(f"creating dendrite components with {chunk_count} points")
|
|
127
|
+
dendrite_components = SkeletonComponents.create(chunk_points)
|
|
128
|
+
else:
|
|
129
|
+
# Subsequent chunks - append to existing
|
|
130
|
+
logger.debug(f"appending {chunk_count} points to dendrite components")
|
|
131
|
+
dendrite_components.append(chunk_points)
|
|
132
|
+
|
|
133
|
+
dendrite_total_points += chunk_count
|
|
134
|
+
|
|
135
|
+
# Check if we got less than requested (end of data)
|
|
136
|
+
if chunk_count < chunk_size:
|
|
137
|
+
logger.debug(f"received {chunk_count} < {chunk_size}, end of dendrite data")
|
|
138
|
+
break
|
|
139
|
+
|
|
140
|
+
dendrite_offset += chunk_count
|
|
141
|
+
|
|
142
|
+
if dendrite_components is not None:
|
|
143
|
+
logger.info(f"assembled dendrite with {dendrite_total_points} total points")
|
|
144
|
+
|
|
145
|
+
return axon_components, dendrite_components, properties
|
|
146
|
+
|
|
147
|
+
except Exception as ex:
|
|
148
|
+
logger.error(f"error loading reconstruction {reconstruction_id}: {ex}")
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def save_specimen_precomputed(output, skeleton_id, properties, axon_components, dendrite_components):
|
|
153
|
+
logger.info(f"creating specimen space precomputed skeleton {skeleton_id}")
|
|
154
|
+
create_from_data(axon_components, dendrite_components, properties, f"{output}/specimen", skeleton_id)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def save_atlas_precomputed(output, skeleton_id, properties, axon_components, dendrite_components):
|
|
158
|
+
# Create the full reconstruction (both axon and dendrite)
|
|
159
|
+
logger.info(f"creating full reconstruction for skeleton {skeleton_id}")
|
|
160
|
+
create_from_data(axon_components, dendrite_components, properties, f"{output}/full", skeleton_id)
|
|
161
|
+
|
|
162
|
+
# Create axon-only reconstruction
|
|
163
|
+
logger.info(f"creating axon-only reconstruction for skeleton {skeleton_id}")
|
|
164
|
+
create_from_data(axon_components, None, properties, f"{output}/axon", skeleton_id)
|
|
165
|
+
|
|
166
|
+
# Create dendrite-only reconstruction
|
|
167
|
+
logger.info(f"creating dendrite-only reconstruction for skeleton {skeleton_id}")
|
|
168
|
+
create_from_data(None, dendrite_components, properties, f"{output}/dendrite", skeleton_id)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def process_pending(client: RemoteDataClient, output: str):
|
|
172
|
+
global heartbeat_current_count, heartbeat_count_limit
|
|
173
|
+
|
|
174
|
+
processed_atlas = process_atlas_pending(client, output)
|
|
175
|
+
processed_specimen = process_specimen_pending(client, output)
|
|
176
|
+
|
|
177
|
+
if processed_atlas or processed_specimen:
|
|
178
|
+
heartbeat_current_count = 0
|
|
179
|
+
else:
|
|
180
|
+
heartbeat_current_count += 1
|
|
181
|
+
if heartbeat_current_count >= heartbeat_count_limit:
|
|
182
|
+
logger.info("There are no pending precomputed entries")
|
|
183
|
+
heartbeat_current_count = 0
|
|
184
|
+
|
|
185
|
+
t1 = threading.Timer(process_interval, process_pending, (client, output))
|
|
186
|
+
t1.start()
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def process_specimen_pending(client: RemoteDataClient, output: str) -> bool:
|
|
190
|
+
try:
|
|
191
|
+
pending = client.find_specimen_space_pending()
|
|
192
|
+
|
|
193
|
+
if len(pending) > 0:
|
|
194
|
+
logger.info(f"{len(pending)} pending specimen space precomputed entries")
|
|
195
|
+
|
|
196
|
+
for pend in pending:
|
|
197
|
+
try:
|
|
198
|
+
reconstruction = load_specimen_space_reconstruction(client, pend)
|
|
199
|
+
|
|
200
|
+
if reconstruction is None:
|
|
201
|
+
client.mark_specimen_failed_load(pend.id)
|
|
202
|
+
continue
|
|
203
|
+
|
|
204
|
+
axon_components, dendrite_components, properties = reconstruction
|
|
205
|
+
|
|
206
|
+
save_specimen_precomputed(output, pend.skeletonId, properties, axon_components,
|
|
207
|
+
dendrite_components)
|
|
208
|
+
|
|
209
|
+
client.mark_specimen_generated(pend.id)
|
|
210
|
+
except Exception as ex:
|
|
211
|
+
logger.error("error", None, ex, True)
|
|
212
|
+
client.mark_specimen_failed_generate(pend.id)
|
|
213
|
+
|
|
214
|
+
return True
|
|
215
|
+
except Exception as ex:
|
|
216
|
+
logger.error("process error", None, ex, True)
|
|
217
|
+
|
|
218
|
+
return False
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def process_atlas_pending(client: RemoteDataClient, output: str) -> bool:
|
|
222
|
+
try:
|
|
223
|
+
pending = client.find_atlas_pending()
|
|
224
|
+
|
|
225
|
+
if len(pending) > 0:
|
|
226
|
+
logger.info(f"{len(pending)} pending atlas precomputed entries")
|
|
227
|
+
|
|
228
|
+
for pend in pending:
|
|
229
|
+
try:
|
|
230
|
+
reconstruction = load_reconstruction(client, pend)
|
|
231
|
+
|
|
232
|
+
if reconstruction is None:
|
|
233
|
+
client.mark_atlas_failed_load(pend.id)
|
|
234
|
+
continue
|
|
235
|
+
|
|
236
|
+
axon_components, dendrite_components, properties = reconstruction
|
|
237
|
+
|
|
238
|
+
save_atlas_precomputed(output, pend.skeletonId, properties, axon_components,
|
|
239
|
+
dendrite_components)
|
|
240
|
+
|
|
241
|
+
client.mark_atlas_generated(pend.id)
|
|
242
|
+
except Exception as ex:
|
|
243
|
+
logger.error("error", None, ex, True)
|
|
244
|
+
client.mark_atlas_failed_generate(pend.id)
|
|
245
|
+
|
|
246
|
+
return True
|
|
247
|
+
except Exception as ex:
|
|
248
|
+
logger.error("process error", None, ex, True)
|
|
249
|
+
|
|
250
|
+
return False
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def main(url: str, auth_key: str, output: str):
|
|
254
|
+
logger.info(f"starting data client for url: {url}")
|
|
255
|
+
logger.info(f"output base url: {output}")
|
|
256
|
+
|
|
257
|
+
ensure_bucket_folders(output)
|
|
258
|
+
|
|
259
|
+
client = RemoteDataClient(url, auth_key)
|
|
260
|
+
|
|
261
|
+
process_pending(client, output)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
if __name__ == '__main__':
|
|
265
|
+
parser = argparse.ArgumentParser()
|
|
266
|
+
|
|
267
|
+
parser.add_argument("-u", "--url", help="URL of the GraphQL service")
|
|
268
|
+
parser.add_argument("-a", "--authkey", help="authorization header for GraphQL service")
|
|
269
|
+
parser.add_argument("-o", "--output", help="the output cloud volume location")
|
|
270
|
+
|
|
271
|
+
args = parser.parse_args()
|
|
272
|
+
|
|
273
|
+
main(args.url, args.authkey, args.output)
|
nmcp/remove_skeleton.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from .precomputed import remove_skeleton
|
|
5
|
+
|
|
6
|
+
logging.basicConfig(level=logging.WARNING)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
parser = argparse.ArgumentParser()
|
|
11
|
+
|
|
12
|
+
parser.add_argument("-o", "--output", help="the output cloud volume location")
|
|
13
|
+
parser.add_argument("-s", "--skeleton", help="the is of the skeleton to remove", type=int)
|
|
14
|
+
|
|
15
|
+
args = parser.parse_args()
|
|
16
|
+
|
|
17
|
+
remove_skeleton(args.skeleton, args.output)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
main()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nmcp-precomputed
|
|
3
|
+
Version: 3.0.4
|
|
4
|
+
Summary: Neuron Morphology Community Portal Precomputed Toolbox
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: <3.11,>=3.10
|
|
7
|
+
Requires-Dist: allensdk
|
|
8
|
+
Requires-Dist: cloud-files
|
|
9
|
+
Requires-Dist: cloud-volume
|
|
10
|
+
Requires-Dist: gql[requests]
|
|
11
|
+
Requires-Dist: numpy<1.24
|
|
12
|
+
Requires-Dist: pandas
|
|
13
|
+
Provides-Extra: publish
|
|
14
|
+
Requires-Dist: build; extra == 'publish'
|
|
15
|
+
Requires-Dist: twine; extra == 'publish'
|
|
16
|
+
Provides-Extra: test
|
|
17
|
+
Requires-Dist: pytest==8.2.0; extra == 'test'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
nmcp/__init__.py,sha256=77zzZ079YOR9DYewHmWMd0Z7qKHt3fya2aDyTc2d02s,386
|
|
2
|
+
nmcp/__main__.py,sha256=Sr015AWnIyp3eyb1Yz2x1YFmIFhZM5Py5XHEhdTOCEk,399
|
|
3
|
+
nmcp/from_json.py,sha256=U-YBM5UXEOzM001dnopqGChomfLYa8RORJXNxfVsdDE,685
|
|
4
|
+
nmcp/from_service.py,sha256=nM0WNDZZm-t0Xa0_vZxCcFVBPm9UyqVvsZihKZmMFo0,844
|
|
5
|
+
nmcp/list_skeletons.py,sha256=UJ7AhBaM8oqGn4IXX7sI2ff0k_EMM7AlGhxzB-f_w3w,433
|
|
6
|
+
nmcp/precomputed_worker.py,sha256=tFEzBLFKeXOcLh7kWa_CsIGo0WHf1Oydjdbu8EFytHc,10130
|
|
7
|
+
nmcp/remove_skeleton.py,sha256=VCyVO_jiAFz94L_LglNXLH5Zkjwn6YgedizLu1YULU4,470
|
|
8
|
+
nmcp/data/__init__.py,sha256=RUdff4AivqMyOdMy2S1atG8yXF9VEXD1Yx6P2R19l6Y,97
|
|
9
|
+
nmcp/data/precomputed_entry.py,sha256=y7VCR5ZetQdoWmH7034n6bEeSyzcXpwjrAeB0YBNxZU,183
|
|
10
|
+
nmcp/data/remote_data_client.py,sha256=sqyr95St8stwYPlZYdpElrY1GI2rUDG4GnmTHJa--aE,15585
|
|
11
|
+
nmcp/precomputed/__init__.py,sha256=v-t9pM8MF2rErRz5IEG4ZngH669VIBBFq9C2DKZypwY,406
|
|
12
|
+
nmcp/precomputed/nmcp_precomputed.py,sha256=3Id2OAG12hlq2yHaOdMR1f6xyBad7iiv83yTKeyhifs,8080
|
|
13
|
+
nmcp/precomputed/nmcp_skeleton.py,sha256=d3FnKPCwLnW43KMJIHGhVrjF9504gINrf_7mdwqiLfY,4646
|
|
14
|
+
nmcp/precomputed/segment_info.py,sha256=WyqHvicK2tQ1iAd2BWaNFop8Zl46hxhVBsLssskZcuI,2165
|
|
15
|
+
nmcp/precomputed/segment_property.py,sha256=s2cUIoBJBmX0eyKS0oyQjIYD4bPL68ABo8eSPqC43BY,943
|
|
16
|
+
nmcp/precomputed/segment_tag_property.py,sha256=Ict-wMtfH0ej3V6dOWi8ndxDqFs64J4d_mJy43jcYrs,2553
|
|
17
|
+
nmcp_precomputed-3.0.4.dist-info/METADATA,sha256=ktMUxjnep73NaNVqU3hi0ChflFHWhvXkzMZcoC-UrnA,496
|
|
18
|
+
nmcp_precomputed-3.0.4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
19
|
+
nmcp_precomputed-3.0.4.dist-info/RECORD,,
|