msr-reader 0.2.0__tar.gz → 0.2.1__tar.gz
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.
- msr_reader-0.2.1/.idea/.gitignore +8 -0
- msr_reader-0.2.1/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- msr_reader-0.2.1/.idea/misc.xml +4 -0
- msr_reader-0.2.1/.idea/modules.xml +8 -0
- msr_reader-0.2.1/.idea/msr-reader.iml +12 -0
- msr_reader-0.2.1/.idea/vcs.xml +6 -0
- msr_reader-0.2.1/.idea/workspace.xml +62 -0
- {msr_reader-0.2.0 → msr_reader-0.2.1}/PKG-INFO +10 -4
- {msr_reader-0.2.0 → msr_reader-0.2.1}/README.md +8 -2
- {msr_reader-0.2.0 → msr_reader-0.2.1}/pyproject.toml +1 -1
- msr_reader-0.2.1/src/msr_reader/__init__.py +2 -0
- {msr_reader-0.2.0 → msr_reader-0.2.1}/src/msr_reader/obffile.py +76 -35
- msr_reader-0.2.1/src/msr_reader/xml_metadata.py +118 -0
- msr_reader-0.2.1/test.ipynb +114 -0
- msr_reader-0.2.0/src/msr_reader/__init__.py +0 -1
- {msr_reader-0.2.0 → msr_reader-0.2.1}/.gitignore +0 -0
- {msr_reader-0.2.0 → msr_reader-0.2.1}/LICENSE +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/msr-reader.iml" filepath="$PROJECT_DIR$/.idea/msr-reader.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="PYTHON_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$" />
|
|
5
|
+
<orderEntry type="jdk" jdkName="test2" jdkType="Python SDK" />
|
|
6
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
7
|
+
</component>
|
|
8
|
+
<component name="PyDocumentationSettings">
|
|
9
|
+
<option name="format" value="PLAIN" />
|
|
10
|
+
<option name="myDocStringFormat" value="Plain" />
|
|
11
|
+
</component>
|
|
12
|
+
</module>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="AutoImportSettings">
|
|
4
|
+
<option name="autoReloadType" value="SELECTIVE" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="ChangeListManager">
|
|
7
|
+
<list default="true" id="1f68cdb2-fdef-4f34-9f04-d5b729d53611" name="Changes" comment="">
|
|
8
|
+
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
|
9
|
+
</list>
|
|
10
|
+
<option name="SHOW_DIALOG" value="false" />
|
|
11
|
+
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
12
|
+
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
13
|
+
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
14
|
+
</component>
|
|
15
|
+
<component name="Git.Settings">
|
|
16
|
+
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
|
17
|
+
</component>
|
|
18
|
+
<component name="ProjectColorInfo"><![CDATA[{
|
|
19
|
+
"associatedIndex": 4
|
|
20
|
+
}]]></component>
|
|
21
|
+
<component name="ProjectId" id="2pABTJPszxXQEjnSgTQldRhFt07" />
|
|
22
|
+
<component name="ProjectViewState">
|
|
23
|
+
<option name="hideEmptyMiddlePackages" value="true" />
|
|
24
|
+
<option name="showLibraryContents" value="true" />
|
|
25
|
+
</component>
|
|
26
|
+
<component name="PropertiesComponent"><![CDATA[{
|
|
27
|
+
"keyToString": {
|
|
28
|
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
29
|
+
"git-widget-placeholder": "main",
|
|
30
|
+
"last_opened_file_path": "/Users/david/Documents/workspace/msr-reader",
|
|
31
|
+
"node.js.detected.package.eslint": "true",
|
|
32
|
+
"node.js.detected.package.tslint": "true",
|
|
33
|
+
"node.js.selected.package.eslint": "(autodetect)",
|
|
34
|
+
"node.js.selected.package.tslint": "(autodetect)",
|
|
35
|
+
"nodejs_package_manager_path": "npm",
|
|
36
|
+
"vue.rearranger.settings.migration": "true"
|
|
37
|
+
}
|
|
38
|
+
}]]></component>
|
|
39
|
+
<component name="SharedIndexes">
|
|
40
|
+
<attachedChunks>
|
|
41
|
+
<set>
|
|
42
|
+
<option value="bundled-js-predefined-1d06a55b98c1-74d2a5396914-JavaScript-PY-241.14494.241" />
|
|
43
|
+
<option value="bundled-python-sdk-0509580d9d50-28c9f5db9ffe-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-241.14494.241" />
|
|
44
|
+
</set>
|
|
45
|
+
</attachedChunks>
|
|
46
|
+
</component>
|
|
47
|
+
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
|
48
|
+
<component name="TaskManager">
|
|
49
|
+
<task active="true" id="Default" summary="Default task">
|
|
50
|
+
<changelist id="1f68cdb2-fdef-4f34-9f04-d5b729d53611" name="Changes" comment="" />
|
|
51
|
+
<created>1732199005423</created>
|
|
52
|
+
<option name="number" value="Default" />
|
|
53
|
+
<option name="presentableId" value="Default" />
|
|
54
|
+
<updated>1732199005423</updated>
|
|
55
|
+
<workItem from="1732199006562" duration="8317000" />
|
|
56
|
+
</task>
|
|
57
|
+
<servers />
|
|
58
|
+
</component>
|
|
59
|
+
<component name="TypeScriptGeneratedFilesManager">
|
|
60
|
+
<option name="version" value="3" />
|
|
61
|
+
</component>
|
|
62
|
+
</project>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: msr-reader
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Pure Python reader for MSR/OBF (Imspector) image data
|
|
5
5
|
Project-URL: Homepage, https://github.com/hoerlteam/msr-reader
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/hoerlteam/msr-reader/issues
|
|
@@ -33,6 +33,7 @@ pip install msr-reader
|
|
|
33
33
|
```python
|
|
34
34
|
from msr_reader import OBFFile
|
|
35
35
|
from xml.etree import ElementTree
|
|
36
|
+
import ome_types
|
|
36
37
|
|
|
37
38
|
with OBFFile('file_path.msr') as f:
|
|
38
39
|
|
|
@@ -42,13 +43,18 @@ with OBFFile('file_path.msr') as f:
|
|
|
42
43
|
|
|
43
44
|
# metadata
|
|
44
45
|
stack_shapes = f.shapes # list of stack shapes, including stack and dimension names
|
|
45
|
-
pixel_sizes = f.pixel_sizes # like
|
|
46
|
+
pixel_sizes = f.pixel_sizes # like shapes, but with pixel sizes (unit: meters)
|
|
46
47
|
|
|
47
48
|
# imspector metadata as xml string
|
|
48
|
-
xml_imspector_metadata = f.
|
|
49
|
+
xml_imspector_metadata = f.get_imspector_xml_metadata(idx)
|
|
49
50
|
|
|
50
51
|
# can be parsed with ElementTree, for example
|
|
51
52
|
et = ElementTree.fromstring(xml_imspector_metadata)
|
|
52
53
|
# find, e.g. x pixel size in XML
|
|
53
54
|
et.find('doc/ExpControl/scan/range/x/psz').text
|
|
55
|
+
|
|
56
|
+
# OME-XML metadata as xml string
|
|
57
|
+
xml_ome_metadata = f.get_ome_xml_metadata()
|
|
58
|
+
# can be parsed with ome-types, for example
|
|
59
|
+
ome_meta = ome_types.from_xml(xml_ome_metadata)
|
|
54
60
|
```
|
|
@@ -17,6 +17,7 @@ pip install msr-reader
|
|
|
17
17
|
```python
|
|
18
18
|
from msr_reader import OBFFile
|
|
19
19
|
from xml.etree import ElementTree
|
|
20
|
+
import ome_types
|
|
20
21
|
|
|
21
22
|
with OBFFile('file_path.msr') as f:
|
|
22
23
|
|
|
@@ -26,13 +27,18 @@ with OBFFile('file_path.msr') as f:
|
|
|
26
27
|
|
|
27
28
|
# metadata
|
|
28
29
|
stack_shapes = f.shapes # list of stack shapes, including stack and dimension names
|
|
29
|
-
pixel_sizes = f.pixel_sizes # like
|
|
30
|
+
pixel_sizes = f.pixel_sizes # like shapes, but with pixel sizes (unit: meters)
|
|
30
31
|
|
|
31
32
|
# imspector metadata as xml string
|
|
32
|
-
xml_imspector_metadata = f.
|
|
33
|
+
xml_imspector_metadata = f.get_imspector_xml_metadata(idx)
|
|
33
34
|
|
|
34
35
|
# can be parsed with ElementTree, for example
|
|
35
36
|
et = ElementTree.fromstring(xml_imspector_metadata)
|
|
36
37
|
# find, e.g. x pixel size in XML
|
|
37
38
|
et.find('doc/ExpControl/scan/range/x/psz').text
|
|
39
|
+
|
|
40
|
+
# OME-XML metadata as xml string
|
|
41
|
+
xml_ome_metadata = f.get_ome_xml_metadata()
|
|
42
|
+
# can be parsed with ome-types, for example
|
|
43
|
+
ome_meta = ome_types.from_xml(xml_ome_metadata)
|
|
38
44
|
```
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import struct
|
|
2
2
|
from collections import namedtuple
|
|
3
|
+
from io import BytesIO
|
|
3
4
|
from zlib import decompress
|
|
4
5
|
|
|
5
6
|
import numpy as np
|
|
@@ -52,10 +53,7 @@ def _read_stack_header(fd, position):
|
|
|
52
53
|
|
|
53
54
|
if not magic_header == MAGIC_STACK_HEADER:
|
|
54
55
|
raise ValueError('Can not parse stack, no magic header found')
|
|
55
|
-
|
|
56
|
-
if version < 5:
|
|
57
|
-
raise ValueError('stack versions <5 not implemented yet')
|
|
58
|
-
|
|
56
|
+
|
|
59
57
|
size = struct.unpack(f'<{MAXIMAL_NUMBER_OF_DIMENSIONS}L', fd.read(struct.calcsize(f'<{MAXIMAL_NUMBER_OF_DIMENSIONS}L')))[:rank]
|
|
60
58
|
length = struct.unpack(f'<{MAXIMAL_NUMBER_OF_DIMENSIONS}d', fd.read(struct.calcsize(f'<{MAXIMAL_NUMBER_OF_DIMENSIONS}d')))[:rank]
|
|
61
59
|
offset = struct.unpack(f'<{MAXIMAL_NUMBER_OF_DIMENSIONS}d', fd.read(struct.calcsize(f'<{MAXIMAL_NUMBER_OF_DIMENSIONS}d')))[:rank]
|
|
@@ -155,56 +153,86 @@ def _to_si_unit(unpack_output):
|
|
|
155
153
|
|
|
156
154
|
def _read_stack_footer(fd, stack_header: OBFStackHeader):
|
|
157
155
|
|
|
156
|
+
# seek to footer start (after binary data)
|
|
158
157
|
fd.seek(stack_header.data_position + stack_header.data_length)
|
|
159
158
|
|
|
160
|
-
|
|
159
|
+
# V1
|
|
160
|
+
if stack_header.stack_version >= 1:
|
|
161
|
+
(size,) = struct.unpack(f'<L', fd.read(struct.calcsize(f'<L')))
|
|
162
|
+
has_col_info_fmt = f'<{MAXIMAL_NUMBER_OF_DIMENSIONS}L'
|
|
163
|
+
has_col_positions = struct.unpack(has_col_info_fmt, fd.read(struct.calcsize(has_col_info_fmt)))[:len(stack_header.size)]
|
|
164
|
+
has_col_labels = struct.unpack(has_col_info_fmt, fd.read(struct.calcsize(has_col_info_fmt)))[:len(stack_header.size)]
|
|
165
|
+
(metadata_length,) = struct.unpack(f'<L', fd.read(struct.calcsize(f'<L')))
|
|
166
|
+
else:
|
|
167
|
+
size, has_col_positions, has_col_labels, metadata_length = None, None, None, None
|
|
168
|
+
|
|
169
|
+
# V2
|
|
170
|
+
if stack_header.stack_version >= 2:
|
|
171
|
+
si_fmt = '<18ld'
|
|
172
|
+
si_value = _to_si_unit(struct.unpack(si_fmt, fd.read(struct.calcsize(si_fmt))))
|
|
173
|
+
si_dimensions = []
|
|
174
|
+
for _ in range(MAXIMAL_NUMBER_OF_DIMENSIONS):
|
|
175
|
+
si_dimensions.append(_to_si_unit(struct.unpack(si_fmt, fd.read(struct.calcsize(si_fmt)))))
|
|
176
|
+
else:
|
|
177
|
+
si_value, si_dimensions = None, None
|
|
161
178
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
179
|
+
# V3
|
|
180
|
+
if stack_header.stack_version >= 3:
|
|
181
|
+
flush_fmt = '<QQ'
|
|
182
|
+
num_flush_points, flush_block_size = struct.unpack(flush_fmt, fd.read(struct.calcsize(flush_fmt)))
|
|
183
|
+
else:
|
|
184
|
+
num_flush_points, flush_block_size = 0, None
|
|
165
185
|
|
|
166
|
-
|
|
186
|
+
# V4
|
|
187
|
+
if stack_header.stack_version >= 4:
|
|
188
|
+
(tag_dictionary_length,) = struct.unpack('<Q', fd.read(struct.calcsize('<Q')))
|
|
189
|
+
else:
|
|
190
|
+
tag_dictionary_length = None
|
|
167
191
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
192
|
+
# V5
|
|
193
|
+
if stack_header.stack_version >= 5:
|
|
194
|
+
stack_end_disk, min_format_version, stack_end_used_disk = struct.unpack('<QLQ', fd.read(struct.calcsize('<QLQ')))
|
|
195
|
+
else:
|
|
196
|
+
stack_end_disk, min_format_version, stack_end_used_disk = None, None, None
|
|
173
197
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
(num_flush_points, flush_block_size, tag_dictionary_length,
|
|
178
|
-
stack_end_disk, min_format_version, stack_end_used_disk, samples_written, num_chunk_positions) = unpack_out
|
|
198
|
+
# V6
|
|
199
|
+
if stack_header.stack_version >= 6:
|
|
200
|
+
samples_written, num_chunk_positions = struct.unpack('<QQ', fd.read(struct.calcsize('<QQ')))
|
|
179
201
|
else:
|
|
180
|
-
|
|
181
|
-
unpack_out = struct.unpack(stack_footer_fmt, fd.read(struct.calcsize(stack_footer_fmt)))
|
|
182
|
-
(num_flush_points, flush_block_size, tag_dictionary_length,
|
|
183
|
-
stack_end_disk, min_format_version, stack_end_used_disk, samples_written, num_chunk_positions) = unpack_out + (None, None, None)
|
|
202
|
+
samples_written, num_chunk_positions = None, None
|
|
184
203
|
|
|
204
|
+
# in any case: seek to end of footer
|
|
205
|
+
fd.seek(stack_header.data_position + stack_header.data_length + size)
|
|
206
|
+
|
|
207
|
+
# extra info after footer
|
|
185
208
|
labels = []
|
|
186
209
|
for _ in range(len(stack_header.size)):
|
|
187
210
|
(n,) = struct.unpack(f'<L', fd.read(struct.calcsize(f'<L')))
|
|
188
211
|
(s,) = struct.unpack(f'<{n}s', fd.read(struct.calcsize(f'<{n}s')))
|
|
189
212
|
labels.append(s.decode('utf8'))
|
|
190
213
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
metadata = fd.read(metadata_length).decode('utf-8')
|
|
214
|
+
if stack_header.stack_version >= 1:
|
|
215
|
+
# TODO: handle col_positions, col_labels, chunked storage
|
|
216
|
+
if any(has_col_labels) or any(has_col_positions):
|
|
217
|
+
raise ValueError('col_label and col_position parsing not implemented yet')
|
|
218
|
+
if num_chunk_positions is not None and num_chunk_positions > 0:
|
|
219
|
+
raise ValueError('chunked storage not implemented yet')
|
|
198
220
|
|
|
199
|
-
|
|
221
|
+
metadata = fd.read(metadata_length).decode('utf-8')
|
|
222
|
+
else:
|
|
223
|
+
metadata = ""
|
|
224
|
+
|
|
225
|
+
# FLUSH POSITIONS (only >=V3, but since num_flush_points defaults to 0 this will do nothing)
|
|
200
226
|
flush_positions = []
|
|
201
227
|
for _ in range(num_flush_points):
|
|
202
228
|
(fp,) = struct.unpack(f'<Q', fd.read(struct.calcsize(f'<Q')))
|
|
203
229
|
flush_positions.append(fp)
|
|
204
230
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
231
|
+
if stack_header.stack_version >= 4:
|
|
232
|
+
tag_dict_buf = BytesIO(fd.read(tag_dictionary_length))
|
|
233
|
+
tag_dict = _read_tag_dict(tag_dict_buf)
|
|
234
|
+
else:
|
|
235
|
+
tag_dict = {}
|
|
208
236
|
|
|
209
237
|
return OBFStackFooter(size, has_col_positions, has_col_labels, metadata, si_value, si_dimensions, flush_positions,
|
|
210
238
|
flush_block_size, tag_dict, stack_end_disk, min_format_version, stack_end_used_disk,
|
|
@@ -288,7 +316,7 @@ class OBFFile:
|
|
|
288
316
|
"""
|
|
289
317
|
return self.pixel_sizes[stack_idx].sizes
|
|
290
318
|
|
|
291
|
-
def
|
|
319
|
+
def get_imspector_xml_metadata(self, stack_idx):
|
|
292
320
|
"""
|
|
293
321
|
get XML metadata string of stack with index stack_idx
|
|
294
322
|
"""
|
|
@@ -305,6 +333,19 @@ class OBFFile:
|
|
|
305
333
|
|
|
306
334
|
return xml_imspector_metadata
|
|
307
335
|
|
|
336
|
+
def get_ome_xml_metadata(self):
|
|
337
|
+
"""
|
|
338
|
+
get OME-XML metadata string
|
|
339
|
+
"""
|
|
340
|
+
|
|
341
|
+
# check that we have metadata (will be available through main header)
|
|
342
|
+
if 'ome_xml' in self.main_header.metadata:
|
|
343
|
+
ome_xml_metadata = self.main_header.metadata['ome_xml']
|
|
344
|
+
else:
|
|
345
|
+
ome_xml_metadata = ''
|
|
346
|
+
|
|
347
|
+
return ome_xml_metadata
|
|
348
|
+
|
|
308
349
|
def close(self):
|
|
309
350
|
self.fd.close()
|
|
310
351
|
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from xml.etree import ElementTree
|
|
2
|
+
|
|
3
|
+
LEAF_TYPE_INT_PREFIXES = ('int', 'uint')
|
|
4
|
+
LEAF_TYPE_FLOAT_PREFIXES = ('single', 'double')
|
|
5
|
+
LEAF_TYPE_PREFIXES = ('char', 'logical') + LEAF_TYPE_INT_PREFIXES + LEAF_TYPE_FLOAT_PREFIXES
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def startswith_any_in_collection(s, prefixes):
|
|
9
|
+
for prefix in prefixes:
|
|
10
|
+
if s.startswith(prefix):
|
|
11
|
+
return True
|
|
12
|
+
return False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def reshape_list(l, shape):
|
|
16
|
+
rows, cols = shape
|
|
17
|
+
res = []
|
|
18
|
+
row_list = []
|
|
19
|
+
for i, elem in enumerate(l):
|
|
20
|
+
row_list.append(elem)
|
|
21
|
+
if i > 0 and (i + 1) % cols == 0:
|
|
22
|
+
res.append(row_list)
|
|
23
|
+
row_list = []
|
|
24
|
+
return res
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def handle_xml_element(element: ElementTree.Element):
|
|
28
|
+
typ = element.attrib['type']
|
|
29
|
+
if startswith_any_in_collection(typ, LEAF_TYPE_PREFIXES):
|
|
30
|
+
return handle_xml_leaf(element)
|
|
31
|
+
elif typ == 'struct':
|
|
32
|
+
return handle_xml_struct(element)
|
|
33
|
+
elif typ == 'cell':
|
|
34
|
+
return handle_xml_cell(element)
|
|
35
|
+
elif typ == 'empty':
|
|
36
|
+
return None
|
|
37
|
+
else:
|
|
38
|
+
raise ValueError(f'unknown XML element of type: {typ}')
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def handle_xml_cell(element: ElementTree.Element):
|
|
42
|
+
result_list = []
|
|
43
|
+
for child in element:
|
|
44
|
+
if not child.tag == 'item':
|
|
45
|
+
raise ValueError(f'expected only item elements in cell, but got {child.tag}')
|
|
46
|
+
result_list.append(handle_xml_element(child))
|
|
47
|
+
return result_list
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def handle_xml_struct(element: ElementTree.Element):
|
|
51
|
+
result_dict = {}
|
|
52
|
+
for child in element:
|
|
53
|
+
child_result = handle_xml_element(child)
|
|
54
|
+
if child_result is not None:
|
|
55
|
+
result_dict[child.tag] = child_result
|
|
56
|
+
|
|
57
|
+
# set empty dict to None, so it will get dropped by outer call
|
|
58
|
+
if len(result_dict) == 0:
|
|
59
|
+
result_dict = None
|
|
60
|
+
|
|
61
|
+
return result_dict
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def handle_xml_leaf(element: ElementTree.Element):
|
|
65
|
+
typ = element.attrib['type']
|
|
66
|
+
if not startswith_any_in_collection(typ, LEAF_TYPE_PREFIXES):
|
|
67
|
+
raise ValueError(f'leaf node in XML of type {typ} not supported.')
|
|
68
|
+
|
|
69
|
+
# if parameters is char/str, we just return it and do not care about size
|
|
70
|
+
if typ == 'char':
|
|
71
|
+
# empty string will be None in XML, return an actual string
|
|
72
|
+
if element.text is None:
|
|
73
|
+
return ''
|
|
74
|
+
return element.text
|
|
75
|
+
|
|
76
|
+
# get size of leaf data,
|
|
77
|
+
size = element.attrib['size']
|
|
78
|
+
size = tuple(map(int, size.split(' ')))
|
|
79
|
+
|
|
80
|
+
# empty parameter: return None
|
|
81
|
+
if any((s == 0 for s in size)):
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
# check if we need to return more than one value as list
|
|
85
|
+
is_scalar = all((s == 1 for s in size))
|
|
86
|
+
|
|
87
|
+
# check if we need to return nested lists (two dimensional parameter array)
|
|
88
|
+
is_matrix = all((s > 1 for s in size))
|
|
89
|
+
|
|
90
|
+
# get function to convert str to dtype
|
|
91
|
+
if startswith_any_in_collection(typ, LEAF_TYPE_INT_PREFIXES):
|
|
92
|
+
parse_fun = int
|
|
93
|
+
elif startswith_any_in_collection(typ, LEAF_TYPE_FLOAT_PREFIXES):
|
|
94
|
+
parse_fun = float
|
|
95
|
+
else:
|
|
96
|
+
# NOTE: to parse bool, we first convert it to int (0/1), then to bool
|
|
97
|
+
# because bool('0') would return True
|
|
98
|
+
parse_fun = lambda text: bool(int(text))
|
|
99
|
+
|
|
100
|
+
# scalar: just parse
|
|
101
|
+
if is_scalar:
|
|
102
|
+
return parse_fun(element.text)
|
|
103
|
+
# matrix/2d array -> make nested list
|
|
104
|
+
elif is_matrix:
|
|
105
|
+
return reshape_list(list(map(parse_fun, element.text.split(' '))), size)
|
|
106
|
+
# vector: split on whitespace, parse individual elements, to list
|
|
107
|
+
else:
|
|
108
|
+
return list(map(parse_fun, element.text.split(' ')))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def imspector_xml_to_dict(xml_string):
|
|
112
|
+
'''
|
|
113
|
+
Convert the Imspector XML metadata available via OBFFile.get_imspector_xml_metadata
|
|
114
|
+
into a nested dictionary as used by the specpy interface
|
|
115
|
+
|
|
116
|
+
NOTE: Newer versions of Imspector may no longer save this information to .msr files
|
|
117
|
+
'''
|
|
118
|
+
return handle_xml_element(ElementTree.fromstring(xml_string))
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"execution_count": 1,
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"outputs": [
|
|
8
|
+
{
|
|
9
|
+
"data": {
|
|
10
|
+
"text/plain": [
|
|
11
|
+
"(17, 110, 120)"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"execution_count": 1,
|
|
15
|
+
"metadata": {},
|
|
16
|
+
"output_type": "execute_result"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"source": [
|
|
20
|
+
"from msr_reader import OBFFile\n",
|
|
21
|
+
"\n",
|
|
22
|
+
"path = '/Users/david/Desktop/new_pipeline_msr_files/bad44489_field_0_sted_0.msr'\n",
|
|
23
|
+
"\n",
|
|
24
|
+
"with OBFFile(path) as reader:\n",
|
|
25
|
+
" img = reader.read_stack(1)\n",
|
|
26
|
+
"\n",
|
|
27
|
+
"img.shape"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"cell_type": "code",
|
|
32
|
+
"execution_count": 2,
|
|
33
|
+
"metadata": {},
|
|
34
|
+
"outputs": [
|
|
35
|
+
{
|
|
36
|
+
"data": {
|
|
37
|
+
"text/plain": [
|
|
38
|
+
"<matplotlib.image.AxesImage at 0x110c83200>"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"execution_count": 2,
|
|
42
|
+
"metadata": {},
|
|
43
|
+
"output_type": "execute_result"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"data": {
|
|
47
|
+
"image/png": "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di3OPvtsNDc3Y/r06bj44ouxffv2WJuDBw9i2bJlmDp1KiZPnozFixejv79/lGZcPa677jpkMhmsWLEi/96xtha7du3Chz/8YUydOhVNTU047bTTsGnTpvzxKIpw9dVXY8aMGWhqakJ3dzd27NgxijNOh5GREaxevRpdXV1oamrCm970Jnzta1+L6X+O17V46KGHcNFFF6GzsxOZTAb33HNP7Ljmuvfs2YMlS5agpaUFbW1tuPTSS7Fv374qXsVRSjQGufPOO6MJEyZE//qv/xr97ne/iz7xiU9EbW1tUX9//2hPLVUWLVoU3XrrrdGWLVuiJ598MnrPe94TzZ49O9q3b1++zSc/+clo1qxZUU9PT7Rp06boHe94R3TOOeeM4qzT59FHH41OPPHE6K1vfWt0+eWX598/ltZiz5490QknnBB99KMfjXp7e6Nnn302uv/++6Pf//73+TbXXXdd1NraGt1zzz3RU089Fb3vfe+Lurq6otdee20UZx6ea6+9Npo6dWp07733Rs8991x01113RZMnT47++Z//Od9mvK7Ff/3Xf0Vf+tKXop/85CcRgOjuu++OHddc9/nnnx+dfvrp0SOPPBL9+te/jt785jdHH/rQh6p8JUcfY3KzfPvb3x4tW7Ys//fIyEjU2dkZrV27dhRnVX12794dAYg2bNgQRVEUDQwMRPX19dFdd92Vb/P0009HAKKNGzeO1jRTZe/evdGcOXOiBx54IHrXu96V3yyPtbX44he/GJ133nnO47lcLuro6Ii++c1v5t8bGBiIGhoaon//93+vxhSrxoUXXhh9/OMfj733gQ98IFqyZEkURcfOWtibpea6t27dGgGIHnvssXybX/ziF1Emk4l27dpVtbkfjYw5M+yhQ4ewefNmdHd359+rqalBd3c3Nm7cOIozqz6Dg68XOZ0yZQoAYPPmzRgeHo6tzdy5czF79uxxuzbLli3DhRdeGLtm4Nhbi5/97GeYN28ePvjBD2L69Ok488wz8b3vfS9//LnnnkNfX19sPVpbWzF//vxxtx7nnHMOenp68MwzzwAAnnrqKTz88MO44IILABxba2Giue6NGzeira0N8+bNy7fp7u5GTU0Nent7qz7no4kxJ6T+6quvYmRkBO3t7bH329vbsW3btlGaVfXJ5XJYsWIFzj33XJx66qkAgL6+PkyYMAFtbW2xtu3t7ejr6xuFWabLnXfeiccffxyPPfZY0bFjbS2effZZ3HLLLVi5ciX+4R/+AY899hg++9nPYsKECVi6dGn+mkt9b8bbelx11VXIZrOYO3cuamtrMTIygmuvvRZLliwBgGNqLUw0193X14fp06fHjtfV1WHKlCnjem00jLnNkrzOsmXLsGXLFjz88MOjPZVRYefOnbj88svxwAMPoLGxcbSnM+rkcjnMmzcPX//61wEAZ555JrZs2YL169dj6dKlozy76vLjH/8Yt99+O+644w6ccsopePLJJ7FixQp0dnYec2tBwjHmzLDTpk1DbW1tUVRjf38/Ojo6RmlW1WX58uW499578ctf/jJWsLSjowOHDh3CwMBArP14XJvNmzdj9+7deNvb3oa6ujrU1dVhw4YNuOmmm1BXV4f29vZjZi0AYMaMGTj55JNj75100kl44YUXACB/zcfC9+YLX/gCrrrqKlxyySU47bTT8JGPfARXXHEF1q5dC+DYWgsTzXV3dHRg9+7dseOHDx/Gnj17xvXaaBhzm+WECRNw1llnoaenJ/9eLpdDT08PFixYMIozS58oirB8+XLcfffdePDBB9HV1RU7ftZZZ6G+vj62Ntu3b8cLL7ww7tZm4cKF+O1vf4snn3wy/2/evHlYsmRJ/vWxshYAcO655xalET3zzDM44YQTAABdXV3o6OiIrUc2m0Vvb++4W48DBw4UFfKtra1FLpcDcGythYnmuhcsWICBgQFs3rw53+bBBx9ELpfD/Pnzqz7no4rRjjDy4c4774waGhqi2267Ldq6dWt02WWXRW1tbVFfX99oTy1VPvWpT0Wtra3Rr371q+jll1/O/ztw4EC+zSc/+clo9uzZ0YMPPhht2rQpWrBgQbRgwYJRnHX1MKNho+jYWotHH300qquri6699tpox44d0e233x5NnDgx+tGPfpRvc91110VtbW3RT3/60+h//ud/ove///3jIl3CZunSpdHxxx+fTx35yU9+Ek2bNi268sor823G61rs3bs3euKJJ6InnngiAhDdcMMN0RNPPBE9//zzURTprvv888+PzjzzzKi3tzd6+OGHozlz5jB1JBqjqSNRFEU333xzNHv27GjChAnR29/+9uiRRx4Z7SmlDoCS/2699dZ8m9deey369Kc/HR133HHRxIkTo7/+67+OXn755dGbdBWxN8tjbS1+/vOfR6eeemrU0NAQzZ07N/rud78bO57L5aLVq1dH7e3tUUNDQ7Rw4cJo+/btozTb9Mhms9Hll18ezZ49O2psbIze+MY3Rl/60peioaGhfJvxuha//OUvS/5GLF26NIoi3XX/6U9/ij70oQ9FkydPjlpaWqKPfexj0d69e0fhao4uWKKLEEIISWDM+SwJIYSQasPNkhBCCEmAmyUhhBCSADdLQgghJAFuloQQQkgC3CwJIYSQBLhZEkIIIQlwsySEEEIS4GZJCCGEJMDNkhBCCEmAmyUhhBCSADdLQgghJIH/Hzj5HGmr9jyPAAAAAElFTkSuQmCC",
|
|
48
|
+
"text/plain": [
|
|
49
|
+
"<Figure size 640x480 with 1 Axes>"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"metadata": {},
|
|
53
|
+
"output_type": "display_data"
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"source": [
|
|
57
|
+
"from matplotlib import pyplot as plt\n",
|
|
58
|
+
"\n",
|
|
59
|
+
"plt.imshow(img.max(0))"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"cell_type": "code",
|
|
64
|
+
"execution_count": 4,
|
|
65
|
+
"metadata": {},
|
|
66
|
+
"outputs": [
|
|
67
|
+
{
|
|
68
|
+
"name": "stdout",
|
|
69
|
+
"output_type": "stream",
|
|
70
|
+
"text": [
|
|
71
|
+
"/Users/david/Desktop/scratch_data/msr-test-files/20230414_yeast_pkmo_ng_mtdna_3d_well7_1.msr\n",
|
|
72
|
+
"/Users/david/Desktop/scratch_data/msr-test-files/2889b245416cd9783c627f413caa45c9_overview_1.msr\n",
|
|
73
|
+
"/Users/david/Desktop/scratch_data/msr-test-files/20230414_yeast_pkmo_ng_mtdna_3d_well6_2.msr\n",
|
|
74
|
+
"/Users/david/Desktop/scratch_data/msr-test-files/20150217_Xist_cell01_001_xyt_2D_condition1.msr\n",
|
|
75
|
+
"/Users/david/Desktop/scratch_data/msr-test-files/20230414_yeast_pkmo_ng_mtdna_3d_well3_8.msr\n",
|
|
76
|
+
"/Users/david/Desktop/scratch_data/msr-test-files/groupa_cell01.msr\n",
|
|
77
|
+
"/Users/david/Desktop/scratch_data/msr-test-files/conf01.msr\n",
|
|
78
|
+
"/Users/david/Desktop/scratch_data/msr-test-files/2889b245416cd9783c627f413caa45c9_overview_1_detail_0.msr\n",
|
|
79
|
+
"/Users/david/Desktop/scratch_data/msr-test-files/mesc_yoyo2_img_001.msr\n"
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"source": [
|
|
84
|
+
"import glob\n",
|
|
85
|
+
"\n",
|
|
86
|
+
"for file in glob.glob('/Users/david/Desktop/scratch_data/msr-test-files/*.msr'):\n",
|
|
87
|
+
" print(file)\n",
|
|
88
|
+
" with OBFFile(file) as reader:\n",
|
|
89
|
+
" img = reader.read_stack(0)"
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
"metadata": {
|
|
94
|
+
"kernelspec": {
|
|
95
|
+
"display_name": "env-py312",
|
|
96
|
+
"language": "python",
|
|
97
|
+
"name": "python3"
|
|
98
|
+
},
|
|
99
|
+
"language_info": {
|
|
100
|
+
"codemirror_mode": {
|
|
101
|
+
"name": "ipython",
|
|
102
|
+
"version": 3
|
|
103
|
+
},
|
|
104
|
+
"file_extension": ".py",
|
|
105
|
+
"mimetype": "text/x-python",
|
|
106
|
+
"name": "python",
|
|
107
|
+
"nbconvert_exporter": "python",
|
|
108
|
+
"pygments_lexer": "ipython3",
|
|
109
|
+
"version": "3.12.7"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"nbformat": 4,
|
|
113
|
+
"nbformat_minor": 2
|
|
114
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .obffile import OBFFile
|
|
File without changes
|
|
File without changes
|