oct-to-tiff 0.5.0__tar.gz → 0.6.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.
@@ -1,63 +1,36 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: oct-to-tiff
3
- Version: 0.5.0
3
+ Version: 0.6.1
4
4
  Summary: A command line tool for converting optical coherence tomography angiography (OCTA) data.
5
- Author-email: Cameron Lloyd <lloyd@med.unideb.hu>
6
- Maintainer-email: Cameron Lloyd <lloyd@med.unideb.hu>
7
- License: BSD 3-Clause License
8
-
9
- Copyright (c) 2021, Cameron Lloyd
10
- All rights reserved.
11
-
12
- Redistribution and use in source and binary forms, with or without
13
- modification, are permitted provided that the following conditions are met:
14
-
15
- 1. Redistributions of source code must retain the above copyright notice, this
16
- list of conditions and the following disclaimer.
17
-
18
- 2. Redistributions in binary form must reproduce the above copyright notice,
19
- this list of conditions and the following disclaimer in the documentation
20
- and/or other materials provided with the distribution.
21
-
22
- 3. Neither the name of the copyright holder nor the names of its
23
- contributors may be used to endorse or promote products derived from
24
- this software without specific prior written permission.
25
-
26
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
30
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
-
37
- Project-URL: Homepage, https://github.com/camlloyd/oct-to-tiff
38
- Project-URL: Bug Tracker, https://github.com/camlloyd/oct-to-tiff/issues
39
- Project-URL: Changelog, https://github.com/camlloyd/oct-to-tiff/blob/main/CHANGELOG.md
40
5
  Keywords: angiography,cli,oct,octa
6
+ Author: Cameron Lloyd
7
+ Author-email: Cameron Lloyd <lloyd@med.unideb.hu>
8
+ License-Expression: BSD-3-Clause
9
+ License-File: LICENSE.txt
41
10
  Classifier: Development Status :: 3 - Alpha
42
11
  Classifier: Intended Audience :: Science/Research
43
- Classifier: License :: OSI Approved :: BSD License
44
12
  Classifier: Natural Language :: English
45
13
  Classifier: Operating System :: OS Independent
46
14
  Classifier: Programming Language :: Python :: 3
47
- Classifier: Programming Language :: Python :: 3.10
48
15
  Classifier: Programming Language :: Python :: 3.11
49
16
  Classifier: Programming Language :: Python :: 3.12
50
17
  Classifier: Programming Language :: Python :: 3.13
51
- Requires-Python: >=3.10
52
- Description-Content-Type: text/markdown
53
- License-File: LICENSE.txt
18
+ Classifier: Programming Language :: Python :: 3.14
54
19
  Requires-Dist: defusedxml
55
20
  Requires-Dist: numpy
21
+ Requires-Dist: roifile
56
22
  Requires-Dist: tifffile
23
+ Requires-Dist: mypy ; extra == 'dev'
24
+ Requires-Dist: ruff ; extra == 'dev'
25
+ Requires-Dist: types-defusedxml ; extra == 'dev'
26
+ Maintainer: Cameron Lloyd
27
+ Maintainer-email: Cameron Lloyd <lloyd@med.unideb.hu>
28
+ Requires-Python: >=3.11
29
+ Project-URL: Homepage, https://github.com/camlloyd/oct-to-tiff
30
+ Project-URL: Bug Tracker, https://github.com/camlloyd/oct-to-tiff/issues
31
+ Project-URL: Changelog, https://github.com/camlloyd/oct-to-tiff/blob/main/CHANGELOG.md
57
32
  Provides-Extra: dev
58
- Requires-Dist: mypy; extra == "dev"
59
- Requires-Dist: ruff; extra == "dev"
60
- Requires-Dist: types-defusedxml; extra == "dev"
33
+ Description-Content-Type: text/markdown
61
34
 
62
35
  # oct-to-tiff
63
36
 
@@ -74,9 +47,9 @@ via pip:
74
47
 
75
48
  pip install oct-to-tiff
76
49
 
77
- via mamba:
50
+ via conda:
78
51
 
79
- mamba install -c conda-forge oct-to-tiff
52
+ conda install conda-forge::oct-to-tiff
80
53
 
81
54
  ## Getting started
82
55
  oct-to-tiff /path/to/image.OCT
@@ -214,4 +187,4 @@ This project uses [Ruff](https://github.com/astral-sh/ruff) for linting and form
214
187
 
215
188
  ## Requirements
216
189
 
217
- Requires Python 3.10 or higher.
190
+ Requires Python 3.11 or higher.
@@ -13,9 +13,9 @@ via pip:
13
13
 
14
14
  pip install oct-to-tiff
15
15
 
16
- via mamba:
16
+ via conda:
17
17
 
18
- mamba install -c conda-forge oct-to-tiff
18
+ conda install conda-forge::oct-to-tiff
19
19
 
20
20
  ## Getting started
21
21
  oct-to-tiff /path/to/image.OCT
@@ -153,4 +153,4 @@ This project uses [Ruff](https://github.com/astral-sh/ruff) for linting and form
153
153
 
154
154
  ## Requirements
155
155
 
156
- Requires Python 3.10 or higher.
156
+ Requires Python 3.11 or higher.
@@ -1,33 +1,34 @@
1
1
  [build-system]
2
- requires = ["setuptools>=61.0"]
3
- build-backend = "setuptools.build_meta"
2
+ requires = ["uv_build>=0.9.26,<0.10.0"]
3
+ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "oct-to-tiff"
7
- version = "0.5.0"
7
+ version = "0.6.1"
8
8
  dependencies = [
9
9
  "defusedxml",
10
10
  "numpy",
11
+ "roifile",
11
12
  "tifffile",
12
13
  ]
13
- requires-python = ">=3.10"
14
+ requires-python = ">=3.11"
14
15
  authors = [{name = "Cameron Lloyd", email = "lloyd@med.unideb.hu"}]
15
16
  maintainers = [{name = "Cameron Lloyd", email = "lloyd@med.unideb.hu"}]
16
17
  description = "A command line tool for converting optical coherence tomography angiography (OCTA) data."
17
18
  readme = "README.md"
18
- license = {file = "LICENSE.txt"}
19
+ license = "BSD-3-Clause"
20
+ license-files = ["LICEN[CS]E.*"]
19
21
  keywords = ["angiography", "cli", "oct", "octa"]
20
22
  classifiers = [
21
23
  "Development Status :: 3 - Alpha",
22
24
  "Intended Audience :: Science/Research",
23
- "License :: OSI Approved :: BSD License",
24
25
  "Natural Language :: English",
25
26
  "Operating System :: OS Independent",
26
27
  "Programming Language :: Python :: 3",
27
- "Programming Language :: Python :: 3.10",
28
28
  "Programming Language :: Python :: 3.11",
29
29
  "Programming Language :: Python :: 3.12",
30
30
  "Programming Language :: Python :: 3.13",
31
+ "Programming Language :: Python :: 3.14",
31
32
  ]
32
33
 
33
34
  [project.optional-dependencies]
@@ -45,11 +46,12 @@ Changelog = "https://github.com/camlloyd/oct-to-tiff/blob/main/CHANGELOG.md"
45
46
  [project.scripts]
46
47
  oct-to-tiff = "oct_to_tiff.cli:main"
47
48
 
48
- [tool.setuptools.packages.find]
49
- where = ["src"]
49
+ [tool.semantic_release]
50
+ allow_zero_version = "true"
51
+ version_toml = ["pyproject.toml:project.version"]
50
52
 
51
53
  [tool.ruff.lint]
52
54
  select = [
53
55
  "I", # isort
54
56
  "S", # flake8-bandit
55
- ]
57
+ ]
@@ -6,24 +6,25 @@ from typing import Any
6
6
 
7
7
  import defusedxml.ElementTree as DET
8
8
  import numpy as np
9
+ import numpy.typing as npt
9
10
  import tifffile
10
- from numpy.typing import NDArray
11
+ from roifile import ROI_TYPE, ImagejRoi, roiwrite
11
12
 
12
13
  logger = logging.getLogger(__name__)
13
14
 
14
15
 
15
16
  def reshape_volume(
16
- volume: NDArray[Any],
17
+ volume: npt.NDArray[Any],
17
18
  frames_per_data_group: int,
18
19
  total_data_groups: int,
19
20
  oct_window_height: int,
20
21
  xy_scan_length: int,
21
- ) -> NDArray[Any]:
22
+ ) -> npt.NDArray[Any]:
22
23
  """Reshape a 1-dimensional array to a 3-dimensional array.
23
24
 
24
25
  Parameters
25
26
  ----------
26
- volume : NDArray[Any]
27
+ volume : npt.NDArray[Any]
27
28
  A 1-dimensional array.
28
29
  frames_per_data_group : int
29
30
  The number of frames per data group.
@@ -36,7 +37,7 @@ def reshape_volume(
36
37
 
37
38
  Returns
38
39
  -------
39
- volume : NDArray[Any]
40
+ volume : npt.NDArray[Any]
40
41
  A 3-dimensional array.
41
42
 
42
43
  """
@@ -51,32 +52,47 @@ def reshape_volume(
51
52
  return volume
52
53
 
53
54
 
54
- def rotate_volume(
55
- volume: NDArray[Any],
56
- ) -> NDArray[Any]:
57
- """Rotate a 3-dimensional array 90 degrees left (anti-clockwise) about the z-axis.
55
+ def volume_metadata(
56
+ pixel_size_x: float | None,
57
+ pixel_size_y: float | None,
58
+ pixel_size_z: float | None,
59
+ ) -> dict[str, Any]:
60
+ """Build a dictionary of metadata.
58
61
 
59
62
  Parameters
60
63
  ----------
61
- volume : NDArray[Any]
62
- A 3-dimensional array.
64
+ pixel_size_x : float | None
65
+ The pixel (voxel) width in mm.
66
+ pixel_size_y : float | None
67
+ The pixel (voxel) height in mm.
68
+ pixel_size_z : float | None
69
+ The pixel (voxel) depth in mm.
63
70
 
64
71
  Returns
65
72
  -------
66
- volume : NDArray[Any]
67
- A rotated version of the input volume.
73
+ metadata : dict[str, Any]
74
+ A dictionary of metadata.
68
75
 
69
76
  """
70
- volume = np.rot90(volume, k=1, axes=(1, 2))
71
- return volume
77
+ metadata: dict[str, Any] = {"axes": "ZYX"}
78
+ if pixel_size_x is not None:
79
+ metadata["PhysicalSizeX"] = pixel_size_x
80
+ metadata["PhysicalSizeXUnit"] = "mm"
81
+ if pixel_size_y is not None:
82
+ metadata["PhysicalSizeY"] = pixel_size_y
83
+ metadata["PhysicalSizeYUnit"] = "mm"
84
+ if pixel_size_z is not None:
85
+ metadata["PhysicalSizeZ"] = pixel_size_z
86
+ metadata["PhysicalSizeZUnit"] = "mm"
87
+ return metadata
72
88
 
73
89
 
74
90
  def write_volume(
75
91
  output_path: Path,
76
- volume: NDArray[Any],
77
- pixel_size_x: float,
78
- pixel_size_y: float,
79
- pixel_size_z: float,
92
+ volume: npt.NDArray[Any],
93
+ pixel_size_x: float | None,
94
+ pixel_size_y: float | None,
95
+ pixel_size_z: float | None,
80
96
  ) -> None:
81
97
  """Write a 3-dimensional array to the output path as an OME-TIFF file, including voxel size in the metadata.
82
98
 
@@ -84,7 +100,7 @@ def write_volume(
84
100
  ----------
85
101
  output_path : Path
86
102
  The specified output path.
87
- volume : NDArray[Any]
103
+ volume : npt.NDArray[Any]
88
104
  A 3-dimensional array.
89
105
  pixel_size_x : float
90
106
  The pixel (voxel) width in mm.
@@ -98,19 +114,11 @@ def write_volume(
98
114
  output_path,
99
115
  volume,
100
116
  photometric="minisblack",
101
- metadata={
102
- "axes": "ZYX",
103
- "PhysicalSizeX": pixel_size_x,
104
- "PhysicalSizeXUnit": "mm",
105
- "PhysicalSizeY": pixel_size_y,
106
- "PhysicalSizeYUnit": "mm",
107
- "PhysicalSizeZ": pixel_size_z,
108
- "PhysicalSizeZUnit": "mm",
109
- },
117
+ metadata=volume_metadata(pixel_size_x, pixel_size_y, pixel_size_z),
110
118
  )
111
119
 
112
120
 
113
- def extract_boundaries(input_path: str | Path) -> None:
121
+ def boundaries_to_arrays(input_path: str | Path) -> list[npt.NDArray[np.int_]]:
114
122
  """Extract segmentation lines.
115
123
 
116
124
  Parameters
@@ -118,6 +126,10 @@ def extract_boundaries(input_path: str | Path) -> None:
118
126
  input_path : str | Path
119
127
  The specified input path.
120
128
 
129
+ Returns
130
+ -------
131
+ arrays : list[npt.NDArray[np.int_]]
132
+ A list of 2-dimensional arrays.
121
133
  """
122
134
  input_path = Path(input_path)
123
135
  tree = DET.parse(input_path)
@@ -128,14 +140,36 @@ def extract_boundaries(input_path: str | Path) -> None:
128
140
  int(point.text) if point.text else 0
129
141
  for point in root.findall("./Curve_Set/Image/Curve/D")
130
142
  ]
131
- scan_length = np.arange(len(data_points))
132
- num_files = len(data_points) // array_size
133
- for i in range(num_files):
143
+ num_arrays = len(data_points) // array_size
144
+
145
+ arrays = []
146
+ for i in range(num_arrays):
134
147
  start = i * array_size
135
148
  end = start + array_size
136
- table = np.column_stack([scan_length[start:end], data_points[start:end]])
137
- table_path = f"{input_path.parent}/{input_path.stem}_{i+1}.txt"
138
- np.savetxt(table_path, table, delimiter="\t", fmt="%d")
149
+ array = np.column_stack([np.arange(array_size), data_points[start:end]])
150
+ arrays.append(array)
151
+
152
+ return arrays
153
+
154
+
155
+ def arrays_to_rois(arrays: list[npt.NDArray[np.int_]], output_path: Path) -> None:
156
+ """
157
+ Convert a list of 2-dimensional arrays to ImageJ ROIs (ZIP file).
158
+
159
+ Parameters
160
+ ----------
161
+ arrays : list[npt.NDArray[np.int_]]
162
+ A list of 2-dimensional arrays.
163
+ output_path : Path
164
+ The specified output path.
165
+ """
166
+ rois = []
167
+ for array in arrays:
168
+ roi = ImagejRoi.frompoints(array)
169
+ roi.roitype = ROI_TYPE(4) # FREELINE
170
+ rois.append(roi)
171
+
172
+ roiwrite(output_path, rois, mode="w")
139
173
 
140
174
 
141
175
  def main() -> None:
@@ -202,18 +236,22 @@ def main() -> None:
202
236
  else:
203
237
  dir_name = input_path.parent
204
238
  file_name = input_path.stem
205
- file_extension = ".ome.tif"
239
+ if args.boundaries:
240
+ file_extension = "_rois.zip"
241
+ else:
242
+ file_extension = ".ome.tif"
206
243
  output_path = dir_name / (file_name + file_extension)
207
244
 
208
245
  if Path.is_file(output_path):
209
246
  if args.overwrite:
210
- pass
247
+ logger.warning(f"Overwriting {output_path}")
211
248
  else:
212
249
  logger.error(f"{output_path} already exists.")
213
250
  return
214
251
 
215
252
  if args.boundaries:
216
- extract_boundaries(input_path)
253
+ arrays = boundaries_to_arrays(input_path)
254
+ arrays_to_rois(arrays, output_path)
217
255
  return
218
256
 
219
257
  with open(input_path, "rb") as f:
@@ -234,7 +272,7 @@ def main() -> None:
234
272
  xy_scan_length = int(len(volume) ** 0.5)
235
273
  pixel_size_x = args.size / oct_window_height
236
274
  pixel_size_y = args.size / xy_scan_length
237
- pixel_size_z = 1
275
+ pixel_size_z = None
238
276
  elif args.seg_curve:
239
277
  volume = np.frombuffer(f.read(), dtype=np.single)
240
278
  if len(volume) == 1280000 or len(volume) == 1120000:
@@ -245,9 +283,9 @@ def main() -> None:
245
283
  oct_window_height = 304
246
284
  total_data_groups = 1
247
285
  xy_scan_length = len(volume) // (frames_per_data_group * oct_window_height)
248
- pixel_size_x = 1
249
- pixel_size_y = 1
250
- pixel_size_z = 1
286
+ pixel_size_x = None
287
+ pixel_size_y = None
288
+ pixel_size_z = None
251
289
  elif "3D Cornea" in file_name:
252
290
  volume = np.frombuffer(f.read(), dtype=np.single)
253
291
  frames_per_data_group = 106
@@ -257,6 +295,32 @@ def main() -> None:
257
295
  pixel_size_x = 0.007797
258
296
  pixel_size_y = 0.003071
259
297
  pixel_size_z = 0.040000
298
+
299
+ volume = reshape_volume(
300
+ volume,
301
+ frames_per_data_group,
302
+ total_data_groups,
303
+ oct_window_height,
304
+ xy_scan_length,
305
+ )
306
+ volume = np.rot90(volume, k=1, axes=(1, 2))
307
+ volume_main = volume[:101]
308
+ volume_align = volume[101:frames_per_data_group]
309
+ align_path = dir_name / (file_name + "_Align.ome.tif")
310
+ pixel_size_x_align = 0.003899
311
+ pixel_size_y_align = 0.003071
312
+ pixel_size_z_align = 0.040000
313
+ write_volume(
314
+ output_path, volume_main, pixel_size_x, pixel_size_y, pixel_size_z
315
+ )
316
+ write_volume(
317
+ align_path,
318
+ volume_align,
319
+ pixel_size_x_align,
320
+ pixel_size_y_align,
321
+ pixel_size_z_align,
322
+ )
323
+ return
260
324
  elif "3D Disc" in file_name:
261
325
  volume = np.frombuffer(f.read(), dtype=np.single)
262
326
  frames_per_data_group = 106
@@ -301,7 +365,7 @@ def main() -> None:
301
365
  xy_scan_length = 1020
302
366
  pixel_size_x = 0.002941
303
367
  pixel_size_y = 0.003071
304
- pixel_size_z = 1
368
+ pixel_size_z = None
305
369
  elif "Cornea Cross Line" in file_name:
306
370
  volume = np.frombuffer(f.read(), dtype=np.single)
307
371
  frames_per_data_group = 2
@@ -310,7 +374,7 @@ def main() -> None:
310
374
  xy_scan_length = 941
311
375
  pixel_size_x = 0.008502
312
376
  pixel_size_y = 0.003071
313
- pixel_size_z = 1
377
+ pixel_size_z = None
314
378
  elif "Cornea Line" in file_name:
315
379
  volume = np.frombuffer(f.read(), dtype=np.single)
316
380
  frames_per_data_group = 1
@@ -319,7 +383,7 @@ def main() -> None:
319
383
  xy_scan_length = 1020
320
384
  pixel_size_x = 0.007843
321
385
  pixel_size_y = 0.003071
322
- pixel_size_z = 1
386
+ pixel_size_z = None
323
387
  elif "Cross Line" in file_name:
324
388
  volume = np.frombuffer(f.read(), dtype=np.single)
325
389
  frames_per_data_group = 2
@@ -328,7 +392,7 @@ def main() -> None:
328
392
  xy_scan_length = 1020
329
393
  pixel_size_x = 0.009804
330
394
  pixel_size_y = 0.003071
331
- pixel_size_z = 1
395
+ pixel_size_z = None
332
396
  elif "Enhanced HD Line" in file_name:
333
397
  volume = np.frombuffer(f.read(), dtype=np.single)
334
398
  frames_per_data_group = 1
@@ -337,7 +401,7 @@ def main() -> None:
337
401
  xy_scan_length = 998
338
402
  pixel_size_x = 0.012024
339
403
  pixel_size_y = 0.003071
340
- pixel_size_z = 1
404
+ pixel_size_z = None
341
405
  elif "GCC" in file_name:
342
406
  volume = np.frombuffer(f.read(), dtype=np.single)
343
407
  frames_per_data_group = 16
@@ -346,7 +410,7 @@ def main() -> None:
346
410
  xy_scan_length = 933
347
411
  pixel_size_x = 0.007503
348
412
  pixel_size_y = 0.003071
349
- pixel_size_z = 1
413
+ pixel_size_z = None
350
414
  elif "Grid" in file_name:
351
415
  volume = np.frombuffer(f.read(), dtype=np.single)
352
416
  frames_per_data_group = 10
@@ -355,7 +419,7 @@ def main() -> None:
355
419
  xy_scan_length = 1020
356
420
  pixel_size_x = 0.005882
357
421
  pixel_size_y = 0.003071
358
- pixel_size_z = 1
422
+ pixel_size_z = None
359
423
  elif "HD Angio Disc" in file_name:
360
424
  volume = np.frombuffer(f.read(), dtype=np.single)
361
425
  frames_per_data_group = 400
@@ -409,7 +473,7 @@ def main() -> None:
409
473
  xy_scan_length = 1024
410
474
  pixel_size_x = 0.009766
411
475
  pixel_size_y = 0.003071
412
- pixel_size_z = 1
476
+ pixel_size_z = None
413
477
  elif "Line" in file_name:
414
478
  volume = np.frombuffer(f.read(), dtype=np.single)
415
479
  frames_per_data_group = 1
@@ -418,7 +482,7 @@ def main() -> None:
418
482
  xy_scan_length = 1020
419
483
  pixel_size_x = 0.008824
420
484
  pixel_size_y = 0.003071
421
- pixel_size_z = 1
485
+ pixel_size_z = None
422
486
  elif "ONH" in file_name:
423
487
  volume = np.frombuffer(f.read(), dtype=np.single, count=2223360)
424
488
  frames_per_data_group = 3
@@ -427,7 +491,7 @@ def main() -> None:
427
491
  xy_scan_length = 965
428
492
  pixel_size_x = 0.015952
429
493
  pixel_size_y = 0.003071
430
- pixel_size_z = 1
494
+ pixel_size_z = None
431
495
  elif "PachymetryWide" in file_name:
432
496
  volume = np.frombuffer(f.read(), dtype=np.single)
433
497
  frames_per_data_group = 16
@@ -436,7 +500,7 @@ def main() -> None:
436
500
  xy_scan_length = 1536
437
501
  pixel_size_x = 0.005859
438
502
  pixel_size_y = 0.003071
439
- pixel_size_z = 1
503
+ pixel_size_z = None
440
504
  elif "Raster" in file_name:
441
505
  volume = np.frombuffer(f.read(), dtype=np.single)
442
506
  frames_per_data_group = 21
@@ -445,7 +509,7 @@ def main() -> None:
445
509
  xy_scan_length = 1020
446
510
  pixel_size_x = 0.011765
447
511
  pixel_size_y = 0.003071
448
- pixel_size_z = 1
512
+ pixel_size_z = None
449
513
  elif "Retina Map" in file_name:
450
514
  volume = np.frombuffer(f.read(), dtype=np.single, count=6680960)
451
515
  frames_per_data_group = 13
@@ -454,7 +518,7 @@ def main() -> None:
454
518
  xy_scan_length = 803
455
519
  pixel_size_x = 0.007472
456
520
  pixel_size_y = 0.003071
457
- pixel_size_z = 1
521
+ pixel_size_z = None
458
522
 
459
523
  volume = reshape_volume(
460
524
  volume,
@@ -465,7 +529,7 @@ def main() -> None:
465
529
  )
466
530
 
467
531
  if not args.en_face and not args.seg_curve:
468
- volume = rotate_volume(volume)
532
+ volume = np.rot90(volume, k=1, axes=(1, 2))
469
533
 
470
534
  write_volume(output_path, volume, pixel_size_x, pixel_size_y, pixel_size_z)
471
535
 
@@ -1,217 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: oct-to-tiff
3
- Version: 0.5.0
4
- Summary: A command line tool for converting optical coherence tomography angiography (OCTA) data.
5
- Author-email: Cameron Lloyd <lloyd@med.unideb.hu>
6
- Maintainer-email: Cameron Lloyd <lloyd@med.unideb.hu>
7
- License: BSD 3-Clause License
8
-
9
- Copyright (c) 2021, Cameron Lloyd
10
- All rights reserved.
11
-
12
- Redistribution and use in source and binary forms, with or without
13
- modification, are permitted provided that the following conditions are met:
14
-
15
- 1. Redistributions of source code must retain the above copyright notice, this
16
- list of conditions and the following disclaimer.
17
-
18
- 2. Redistributions in binary form must reproduce the above copyright notice,
19
- this list of conditions and the following disclaimer in the documentation
20
- and/or other materials provided with the distribution.
21
-
22
- 3. Neither the name of the copyright holder nor the names of its
23
- contributors may be used to endorse or promote products derived from
24
- this software without specific prior written permission.
25
-
26
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
30
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
-
37
- Project-URL: Homepage, https://github.com/camlloyd/oct-to-tiff
38
- Project-URL: Bug Tracker, https://github.com/camlloyd/oct-to-tiff/issues
39
- Project-URL: Changelog, https://github.com/camlloyd/oct-to-tiff/blob/main/CHANGELOG.md
40
- Keywords: angiography,cli,oct,octa
41
- Classifier: Development Status :: 3 - Alpha
42
- Classifier: Intended Audience :: Science/Research
43
- Classifier: License :: OSI Approved :: BSD License
44
- Classifier: Natural Language :: English
45
- Classifier: Operating System :: OS Independent
46
- Classifier: Programming Language :: Python :: 3
47
- Classifier: Programming Language :: Python :: 3.10
48
- Classifier: Programming Language :: Python :: 3.11
49
- Classifier: Programming Language :: Python :: 3.12
50
- Classifier: Programming Language :: Python :: 3.13
51
- Requires-Python: >=3.10
52
- Description-Content-Type: text/markdown
53
- License-File: LICENSE.txt
54
- Requires-Dist: defusedxml
55
- Requires-Dist: numpy
56
- Requires-Dist: tifffile
57
- Provides-Extra: dev
58
- Requires-Dist: mypy; extra == "dev"
59
- Requires-Dist: ruff; extra == "dev"
60
- Requires-Dist: types-defusedxml; extra == "dev"
61
-
62
- # oct-to-tiff
63
-
64
- [![DOI](https://zenodo.org/badge/382486199.svg)](https://zenodo.org/badge/latestdoi/382486199)
65
- [![PyPI - Version](https://img.shields.io/pypi/v/oct-to-tiff)](https://pypi.org/project/oct-to-tiff)
66
- [![PyPI - License](https://img.shields.io/pypi/l/oct-to-tiff)](https://github.com/camlloyd/oct-to-tiff/blob/main/LICENSE.txt)
67
- [![PyPI Downloads](https://static.pepy.tech/badge/oct-to-tiff)](https://pepy.tech/projects/oct-to-tiff)
68
-
69
-
70
- A command line tool for converting optical coherence tomography angiography (OCTA) data.
71
-
72
- ## Installation
73
- via pip:
74
-
75
- pip install oct-to-tiff
76
-
77
- via mamba:
78
-
79
- mamba install -c conda-forge oct-to-tiff
80
-
81
- ## Getting started
82
- oct-to-tiff /path/to/image.OCT
83
-
84
- will read an OCT volume and write to a single OME-TIFF file, including voxel size in the metadata.
85
-
86
- By default, the output file will be written with the same name as the input file and to the same directory:
87
-
88
-
89
- tree /path/to/images
90
- ├── image.OCT
91
- └── image.ome.tif
92
-
93
- To specify a custom output directory, see [Optional arguments](#optional-arguments) below.
94
-
95
- ## Batch processing
96
- ``` bash
97
- for file in *.OCT; do oct-to-tiff "${file}"; done
98
- ```
99
- will convert all OCT volumes in the current directory to OME-TIFF files, including voxel size in the metadata.
100
-
101
- ## Supported scan patterns
102
-
103
- This tool has been developed by reverse engineering data from the Optovue RTVue XR Avanti System.
104
-
105
- Due to limited test data, only the following scan patterns are currently supported:
106
-
107
- ### Structural OCT
108
- - 3D Cornea
109
- - 3D Disc
110
- - 3D Retina
111
- - 3D Widefield
112
- - 3D Widefield MCT
113
- - Angle
114
- - Cornea Cross Line
115
- - Cornea Line
116
- - Cross Line
117
- - Enhanced HD Line
118
- - GCC
119
- - Grid
120
- - Line
121
- - ONH (Partial)
122
- - Pachymetry Wide
123
- - Radial Lines
124
- - Raster
125
- - Retina Map (Partial)
126
-
127
- ### OCT Angiography
128
- - Angio Disc
129
- - Angio Retina
130
- - HD Angio Disc
131
- - HD Angio Retina
132
-
133
-
134
- ## Optional arguments
135
-
136
- To view these options at any time, run `oct-to-tiff --help`.
137
-
138
- #### `--output OUTPUT`
139
- **Description**: specify a custom output directory.
140
-
141
- If the path to the output directory does not exist, a new directory (and parent directories) will be created.
142
-
143
- **Usage**:
144
-
145
- oct-to-tiff /path/to/image.OCT --output /path/to/output/directory
146
-
147
- #### `--overwrite`
148
- **Description**: overwrite output file if it exists.
149
-
150
- **Usage**:
151
-
152
- oct-to-tiff /path/to/image.OCT --overwrite
153
-
154
- #### `--size SIZE`
155
- **Description**: scan size in mm.
156
-
157
- Sets the correct voxel size for scan patterns with adjustable length.
158
-
159
- **Usage**:
160
-
161
- oct-to-tiff /path/to/image.OCT --size 4.5
162
-
163
- #### `--log-level LEVEL`
164
- **Description**: sets the logging level (default: `WARNING`)
165
-
166
- **Usage**:
167
-
168
- oct-to-tiff /path/to/image.OCT --log-level INFO
169
-
170
- #### `--version`
171
- **Description**: show program's version number and exit.
172
-
173
- **Usage**:
174
-
175
- oct-to-tiff --version
176
-
177
- #### The following options are mutually exclusive:
178
-
179
- #### `--angio`
180
- **Description**: convert extracted OCTA data.
181
-
182
- Requires `--size SIZE`.
183
-
184
- **Usage**:
185
-
186
- oct-to-tiff /path/to/data --angio --size 4.5
187
-
188
- #### `--en-face`
189
- **Description**: convert extracted en face data.
190
-
191
- Requires `--size SIZE`.
192
-
193
- **Usage**:
194
-
195
- oct-to-tiff /path/to/data --en-face --size 4.5
196
-
197
- #### `--seg-curve`
198
- **Description**: convert extracted segmentation data.
199
-
200
- **Usage**:
201
-
202
- oct-to-tiff /path/to/data --seg-curve
203
-
204
- #### `--boundaries`
205
- **Description**: extract segmentation lines.
206
-
207
- **Usage**:
208
-
209
- oct-to-tiff /path/to/curve.xml --boundaries
210
-
211
- ## Contributing
212
-
213
- This project uses [Ruff](https://github.com/astral-sh/ruff) for linting and formatting.
214
-
215
- ## Requirements
216
-
217
- Requires Python 3.10 or higher.
@@ -1,4 +0,0 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
@@ -1,11 +0,0 @@
1
- LICENSE.txt
2
- README.md
3
- pyproject.toml
4
- src/oct_to_tiff/__init__.py
5
- src/oct_to_tiff/cli.py
6
- src/oct_to_tiff.egg-info/PKG-INFO
7
- src/oct_to_tiff.egg-info/SOURCES.txt
8
- src/oct_to_tiff.egg-info/dependency_links.txt
9
- src/oct_to_tiff.egg-info/entry_points.txt
10
- src/oct_to_tiff.egg-info/requires.txt
11
- src/oct_to_tiff.egg-info/top_level.txt
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- oct-to-tiff = oct_to_tiff.cli:main
@@ -1,8 +0,0 @@
1
- defusedxml
2
- numpy
3
- tifffile
4
-
5
- [dev]
6
- mypy
7
- ruff
8
- types-defusedxml
@@ -1 +0,0 @@
1
- oct_to_tiff
File without changes