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.
@@ -0,0 +1,8 @@
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Editor-based HTTP Client requests
5
+ /httpRequests/
6
+ # Datasource local storage ignored files
7
+ /dataSources/
8
+ /dataSources.local.xml
@@ -0,0 +1,6 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectRootManager" version="2" project-jdk-name="test2" project-jdk-type="Python SDK" />
4
+ </project>
@@ -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,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
@@ -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.3
1
+ Metadata-Version: 2.4
2
2
  Name: msr-reader
3
- Version: 0.2.0
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 sizes, but with pixel sizes (unit: meters)
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.get_xml_metadata(idx)
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 sizes, but with pixel sizes (unit: meters)
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.get_xml_metadata(idx)
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
  ```
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "msr-reader"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  authors = [
9
9
  { name="David Hörl", email="hoerldavid@gmail.com" },
10
10
  ]
@@ -0,0 +1,2 @@
1
+ from .obffile import OBFFile
2
+ from .xml_metadata import imspector_xml_to_dict
@@ -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
- (size,) = struct.unpack(f'<L', fd.read(struct.calcsize(f'<L')))
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
- 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)]
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
- (metadata_length,) = struct.unpack(f'<L', fd.read(struct.calcsize(f'<L')))
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
- si_fmt = '<18ld'
169
- si_value = _to_si_unit(struct.unpack(si_fmt, fd.read(struct.calcsize(si_fmt))))
170
- si_dimensions = []
171
- for _ in range(MAXIMAL_NUMBER_OF_DIMENSIONS):
172
- si_dimensions.append(_to_si_unit(struct.unpack(si_fmt, fd.read(struct.calcsize(si_fmt)))))
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
- if stack_header.stack_version > 5:
175
- stack_footer_fmt = '<QQQQLQQQ'
176
- unpack_out = struct.unpack(stack_footer_fmt, fd.read(struct.calcsize(stack_footer_fmt)))
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
- stack_footer_fmt = '<QQQQL'
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
- # TODO: handle col_positions, col_labels, chunked storage
192
- if any(has_col_labels) or any(has_col_positions):
193
- raise ValueError('col_label and col_position parsing not implemented yet')
194
- if num_chunk_positions is not None and num_chunk_positions > 0:
195
- raise ValueError('chunked storage not implemented yet')
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
- # FLUSH POSITIONS
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
- # tag_dict_buf = BytesIO(fd.read(tag_dictionary_length))
206
- tag_dict = _read_tag_dict(fd)
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 get_xml_metadata(self, stack_idx):
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