pyisyntax 0.1.3__tar.gz → 0.1.5__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.
Files changed (77) hide show
  1. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/CHANGELOG.md +25 -1
  2. {pyisyntax-0.1.3/pyisyntax.egg-info → pyisyntax-0.1.5}/PKG-INFO +15 -28
  3. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/README.md +6 -12
  4. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax/lowlevel/io_management.py +11 -8
  5. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax/lowlevel/libisyntax.py +72 -46
  6. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax/wrapper.py +45 -5
  7. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/builder.py +21 -21
  8. pyisyntax-0.1.5/isyntax_build/vendor/libisyntax/src/examples/isyntax_dirwalk.c +91 -0
  9. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/examples/isyntax_to_tiff.c +4 -3
  10. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax.c +220 -102
  11. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax.h +16 -4
  12. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_reader.c +16 -8
  13. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_reader.h +3 -2
  14. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_streamer.c +81 -24
  15. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/libisyntax.c +101 -39
  16. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/libisyntax.h +13 -2
  17. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/third_party/ltalloc.cc +12 -3
  18. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/block_allocator.c +22 -13
  19. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/mathutils.c +5 -3
  20. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/mathutils.h +14 -7
  21. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/memrw.c +24 -15
  22. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/stringutils.c +2 -2
  23. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/timerutils.c +24 -15
  24. {pyisyntax-0.1.3 → pyisyntax-0.1.5/pyisyntax.egg-info}/PKG-INFO +15 -28
  25. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/pyisyntax.egg-info/SOURCES.txt +1 -0
  26. pyisyntax-0.1.5/pyisyntax.egg-info/requires.txt +2 -0
  27. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/pyproject.toml +39 -26
  28. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/tests/conftest.py +2 -2
  29. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/tests/test_lowlevel.py +38 -7
  30. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/tests/test_wrapper.py +13 -1
  31. pyisyntax-0.1.3/pyisyntax.egg-info/requires.txt +0 -11
  32. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/LICENSE +0 -0
  33. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/MANIFEST.in +0 -0
  34. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax/__init__.py +0 -0
  35. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax/lowlevel/__init__.py +0 -0
  36. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/__init__.py +0 -0
  37. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/pyisyntax.c +0 -0
  38. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/src/python_platform_utils.c +0 -0
  39. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/src/python_platform_utils.h +0 -0
  40. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/src/win32_utils.c +0 -0
  41. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/LICENSE.txt +0 -0
  42. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/README.md +0 -0
  43. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/examples/isyntax_example.c +0 -0
  44. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_dwt.c +0 -0
  45. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_streamer.h +0 -0
  46. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/platform/arena.h +0 -0
  47. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/platform/common.h +0 -0
  48. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/platform/intrinsics.h +0 -0
  49. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/platform/linux_platform.c +0 -0
  50. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/platform/linux_utils.c +0 -0
  51. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/platform/platform.c +0 -0
  52. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/platform/platform.h +0 -0
  53. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/platform/win32_utils.c +0 -0
  54. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/platform/win32_utils.h +0 -0
  55. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/platform/work_queue.c +0 -0
  56. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/platform/work_queue.h +0 -0
  57. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/third_party/ltalloc.h +0 -0
  58. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/third_party/stb_ds.h +0 -0
  59. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/third_party/stb_image.h +0 -0
  60. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/third_party/stb_image_write.h +0 -0
  61. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/third_party/stb_sprintf.h +0 -0
  62. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/third_party/yxml.c +0 -0
  63. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/third_party/yxml.h +0 -0
  64. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/benaphore.c +0 -0
  65. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/benaphore.h +0 -0
  66. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/block_allocator.h +0 -0
  67. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/libtiff_api.h +0 -0
  68. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/memrw.h +0 -0
  69. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/stringutils.h +0 -0
  70. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/isyntax_build/vendor/libisyntax/src/utils/timerutils.h +0 -0
  71. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/pyisyntax.egg-info/dependency_links.txt +0 -0
  72. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/pyisyntax.egg-info/not-zip-safe +0 -0
  73. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/pyisyntax.egg-info/top_level.txt +0 -0
  74. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/setup.cfg +0 -0
  75. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/setup.py +0 -0
  76. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/tests/__init__.py +0 -0
  77. {pyisyntax-0.1.3 → pyisyntax-0.1.5}/tests/data/DOWNLOAD.txt +0 -0
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.5] - 2025-04-18
11
+
12
+ ### Added
13
+
14
+ - Properties `offset_x`and `offset_y` for the WSI image offset in relation to the macro image.
15
+
16
+ ### Changed
17
+
18
+ - Bump the `libisyntax` submodule commit.
19
+ - Use `uv` for development environment management (was previously `conda-lock`).
20
+
21
+ ## [0.1.4] - 2024-08-31
22
+
23
+ ### Added
24
+
25
+ - Support for reading the slide barcode. The barcode string can be accessed via
26
+ the `barcode` property of an `ISyntax` instance.
27
+
28
+ ### Changed
29
+
30
+ - Bump the `libisyntax` submodule commit.
31
+
10
32
  ## [0.1.3] - 2024-03-28
11
33
 
12
34
  ### Added
@@ -50,7 +72,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
50
72
 
51
73
  - This is the initial release.
52
74
 
53
- [unreleased]: https://github.com/anibali/pyisyntax/compare/v0.1.3...HEAD
75
+ [unreleased]: https://github.com/anibali/pyisyntax/compare/v0.1.5...HEAD
76
+ [0.1.4]: https://github.com/anibali/pyisyntax/compare/v0.1.4...v0.1.5
77
+ [0.1.4]: https://github.com/anibali/pyisyntax/compare/v0.1.3...v0.1.4
54
78
  [0.1.3]: https://github.com/anibali/pyisyntax/compare/v0.1.2...v0.1.3
55
79
  [0.1.2]: https://github.com/anibali/pyisyntax/compare/v0.1.1...v0.1.2
56
80
  [0.1.1]: https://github.com/anibali/pyisyntax/compare/v0.1.0...v0.1.1
@@ -1,27 +1,20 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pyisyntax
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: Python bindings for libisyntax
5
5
  Author-email: Aiden Nibali <dismaldenizen@gmail.com>
6
- License: MIT
7
- Project-URL: homepage, https://github.com/anibali/pyisyntax
8
- Project-URL: source, https://github.com/anibali/pyisyntax
9
- Project-URL: download, https://pypi.org/project/pyisyntax/#files
10
- Project-URL: tracker, https://github.com/anibali/pyisyntax/issues
11
- Project-URL: release notes, https://github.com/anibali/pyisyntax/blob/master/CHANGELOG.md
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/anibali/pyisyntax
8
+ Project-URL: Source, https://github.com/anibali/pyisyntax
9
+ Project-URL: Download, https://pypi.org/project/pyisyntax/#files
10
+ Project-URL: Tracker, https://github.com/anibali/pyisyntax/issues
11
+ Project-URL: Release notes, https://github.com/anibali/pyisyntax/blob/master/CHANGELOG.md
12
12
  Requires-Python: >=3.10
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
  Requires-Dist: cffi>=1.12
16
16
  Requires-Dist: numpy
17
- Provides-Extra: dev
18
- Requires-Dist: conda-lock; extra == "dev"
19
- Requires-Dist: pytest; extra == "dev"
20
- Requires-Dist: pytest-mock; extra == "dev"
21
- Requires-Dist: ruff==0.2.1; extra == "dev"
22
- Requires-Dist: tox>=4.0; extra == "dev"
23
- Requires-Dist: twine; extra == "dev"
24
- Requires-Dist: typing_extensions; extra == "dev"
17
+ Dynamic: license-file
25
18
 
26
19
  # pyisyntax
27
20
 
@@ -73,22 +66,16 @@ with ISyntax.open("my_file.isyntax") as isyntax:
73
66
 
74
67
  To set up a development environment from the lock file:
75
68
 
76
- 1. Ensure that you have
77
- [micromamba](https://mamba.readthedocs.io/en/latest/user_guide/micromamba.html)
78
- installed.
79
- 2. Create the environment:
69
+ 1. Ensure that you have [uv](https://docs.astral.sh/uv/) installed.
70
+ 2. Create the virtual environment:
80
71
  ```console
81
- $ micromamba create -n pyisyntax -f conda-lock.yml --category main --category dev
82
- ```
83
- 3. Activate the environment:
84
- ```console
85
- $ micromamba activate pyisyntax
72
+ $ uv sync --frozen
86
73
  ```
87
74
 
88
75
  To modify pyisyntax project dependencies:
89
76
 
90
- 1. Edit pyproject.toml.
91
- 2. Update the lock file using conda-lock:
77
+ 1. Edit dependencies in `pyproject.toml`.
78
+ 2. Update the lock file using uv:
92
79
  ```console
93
- $ conda-lock lock -f pyproject.toml -p linux-64 --micromamba
80
+ $ uv lock
94
81
  ```
@@ -48,22 +48,16 @@ with ISyntax.open("my_file.isyntax") as isyntax:
48
48
 
49
49
  To set up a development environment from the lock file:
50
50
 
51
- 1. Ensure that you have
52
- [micromamba](https://mamba.readthedocs.io/en/latest/user_guide/micromamba.html)
53
- installed.
54
- 2. Create the environment:
51
+ 1. Ensure that you have [uv](https://docs.astral.sh/uv/) installed.
52
+ 2. Create the virtual environment:
55
53
  ```console
56
- $ micromamba create -n pyisyntax -f conda-lock.yml --category main --category dev
57
- ```
58
- 3. Activate the environment:
59
- ```console
60
- $ micromamba activate pyisyntax
54
+ $ uv sync --frozen
61
55
  ```
62
56
 
63
57
  To modify pyisyntax project dependencies:
64
58
 
65
- 1. Edit pyproject.toml.
66
- 2. Update the lock file using conda-lock:
59
+ 1. Edit dependencies in `pyproject.toml`.
60
+ 2. Update the lock file using uv:
67
61
  ```console
68
- $ conda-lock lock -f pyproject.toml -p linux-64 --micromamba
62
+ $ uv lock
69
63
  ```
@@ -27,12 +27,15 @@ class ByHandleRegistry(Generic[T]):
27
27
  Handles start at 1, autoincrement, and will automatically be recycled
28
28
  once objects are removed from the registry.
29
29
  """
30
- self._list = []
30
+ self._list: list[T | ByHandleRegistry._EmptySlot] = []
31
31
  self._n_free = 0
32
32
  self._empty_slot = self._EmptySlot()
33
33
 
34
34
  def pop(self, handle: int) -> T:
35
35
  element = self._list[handle - 1]
36
+ if isinstance(element, ByHandleRegistry._EmptySlot):
37
+ msg = "empty slot"
38
+ raise IndexError(msg)
36
39
  self._list[handle - 1] = self._empty_slot
37
40
  self._n_free += 1
38
41
  if len(self._list) == handle:
@@ -52,11 +55,15 @@ class ByHandleRegistry(Generic[T]):
52
55
  return index + 1
53
56
 
54
57
  def __getitem__(self, handle: int) -> T:
55
- return self._list[handle - 1]
58
+ element = self._list[handle - 1]
59
+ if isinstance(element, ByHandleRegistry._EmptySlot):
60
+ msg = "empty slot"
61
+ raise IndexError(msg)
62
+ return element
56
63
 
57
64
  def items(self) -> Iterator[tuple[int, T]]:
58
65
  for i, element in enumerate(self._list):
59
- if element is not self._empty_slot:
66
+ if not isinstance(element, ByHandleRegistry._EmptySlot):
60
67
  yield i + 1, element
61
68
 
62
69
 
@@ -69,7 +76,6 @@ def init_python_io_hooks() -> None:
69
76
  _io_registry[handle].f.seek(offset)
70
77
  return True
71
78
 
72
-
73
79
  @ffi.def_extern()
74
80
  def python_file_read_into(handle: int, dest: VoidPtr, bytes_to_read: int) -> int:
75
81
  bytes_read = _io_registry[handle].f.readinto(ffi.buffer(dest, bytes_to_read))
@@ -77,17 +83,14 @@ def init_python_io_hooks() -> None:
77
83
  raise RuntimeError
78
84
  return max(bytes_read, 1)
79
85
 
80
-
81
86
  @ffi.def_extern()
82
87
  def python_file_get_size(handle: int) -> int:
83
88
  return _io_registry[handle].n_bytes
84
89
 
85
-
86
90
  @ffi.def_extern()
87
91
  def python_file_close(handle: int) -> None:
88
92
  _io_registry.pop(handle).f.close()
89
93
 
90
-
91
94
  lib.init_python_platform_utils(
92
95
  lib.python_file_set_pos,
93
96
  lib.python_file_read_into,
@@ -109,7 +112,7 @@ def register_io(f: RawIOBase | BufferedIOBase, n_bytes: int) -> int:
109
112
  Returns:
110
113
  New handle assigned to the IO object.
111
114
  """
112
- if not f.readable:
115
+ if not f.readable():
113
116
  msg = "IO object must be readable"
114
117
  raise RuntimeError(msg)
115
118
  return _io_registry.add(SizedIO(f, n_bytes))
@@ -77,11 +77,13 @@ def open_from_registered_handle(handle: int, *, is_init_allocators: bool = False
77
77
  init()
78
78
 
79
79
  isyntax = ffi.new("isyntax_t**")
80
- check_error(lib.libisyntax_open(
81
- ffi.new("char[]", str(handle).encode("utf-8")),
82
- is_init_allocators,
83
- isyntax,
84
- ))
80
+ check_error(
81
+ lib.libisyntax_open(
82
+ ffi.new("char[]", str(handle).encode("utf-8")),
83
+ is_init_allocators,
84
+ isyntax,
85
+ ),
86
+ )
85
87
  return isyntax[0]
86
88
 
87
89
 
@@ -119,6 +121,10 @@ def get_macro_image(isyntax: ISyntaxPtr) -> ISyntaxImagePtr:
119
121
  return lib.libisyntax_get_macro_image(isyntax)
120
122
 
121
123
 
124
+ def get_barcode(isyntax: ISyntaxPtr) -> bytes:
125
+ return ffi.string(lib.libisyntax_get_barcode(isyntax))
126
+
127
+
122
128
  def image_get_level_count(wsi_image: ISyntaxImagePtr) -> int:
123
129
  return lib.libisyntax_image_get_level_count(wsi_image)
124
130
 
@@ -127,6 +133,14 @@ def image_get_level(wsi_image: ISyntaxImagePtr, index: int) -> ISyntaxLevelPtr:
127
133
  return lib.libisyntax_image_get_level(wsi_image, index)
128
134
 
129
135
 
136
+ def image_get_offset_x(wsi_image: ISyntaxImagePtr) -> int:
137
+ return lib.libisyntax_image_get_offset_x(wsi_image)
138
+
139
+
140
+ def image_get_offset_y(wsi_image: ISyntaxImagePtr) -> int:
141
+ return lib.libisyntax_image_get_offset_y(wsi_image)
142
+
143
+
130
144
  def level_get_scale(level: ISyntaxLevelPtr) -> int:
131
145
  return lib.libisyntax_level_get_scale(level)
132
146
 
@@ -161,11 +175,13 @@ def cache_create(debug_name: str | None, cache_size: int) -> ISyntaxCachePtr:
161
175
  debug_name_or_null = ffi.NULL
162
176
  else:
163
177
  debug_name_or_null = ffi.new("char[]", debug_name.encode("utf-8"))
164
- check_error(lib.libisyntax_cache_create(
165
- debug_name_or_null,
166
- cache_size,
167
- isyntax_cache,
168
- ))
178
+ check_error(
179
+ lib.libisyntax_cache_create(
180
+ debug_name_or_null,
181
+ cache_size,
182
+ isyntax_cache,
183
+ ),
184
+ )
169
185
  return isyntax_cache[0]
170
186
 
171
187
 
@@ -188,15 +204,17 @@ def tile_read(
188
204
  pixels_buffer: "Buffer | FFI.buffer",
189
205
  pixel_format: ISyntaxPixelFormat,
190
206
  ) -> None:
191
- check_error(lib.libisyntax_tile_read(
192
- isyntax,
193
- isyntax_cache,
194
- level,
195
- tile_x,
196
- tile_y,
197
- ffi.from_buffer("uint32_t[]", pixels_buffer, require_writable=True),
198
- pixel_format,
199
- ))
207
+ check_error(
208
+ lib.libisyntax_tile_read(
209
+ isyntax,
210
+ isyntax_cache,
211
+ level,
212
+ tile_x,
213
+ tile_y,
214
+ ffi.from_buffer("uint32_t[]", pixels_buffer, require_writable=True),
215
+ pixel_format,
216
+ ),
217
+ )
200
218
 
201
219
 
202
220
  def read_region(
@@ -210,27 +228,31 @@ def read_region(
210
228
  pixels_buffer: "Buffer | FFI.buffer",
211
229
  pixel_format: ISyntaxPixelFormat,
212
230
  ) -> None:
213
- check_error(lib.libisyntax_read_region(
214
- isyntax,
215
- isyntax_cache,
216
- level,
217
- x,
218
- y,
219
- width,
220
- height,
221
- ffi.from_buffer("uint32_t[]", pixels_buffer, require_writable=True),
222
- pixel_format,
223
- ))
231
+ check_error(
232
+ lib.libisyntax_read_region(
233
+ isyntax,
234
+ isyntax_cache,
235
+ level,
236
+ x,
237
+ y,
238
+ width,
239
+ height,
240
+ ffi.from_buffer("uint32_t[]", pixels_buffer, require_writable=True),
241
+ pixel_format,
242
+ ),
243
+ )
224
244
 
225
245
 
226
246
  def read_label_image_jpeg(isyntax: ISyntaxPtr) -> memoryview:
227
247
  jpeg_buffer_ptr = ffi.new("uint8_t**")
228
248
  jpeg_size_ptr = ffi.new("uint32_t*")
229
- check_error(lib.libisyntax_read_label_image_jpeg(
230
- isyntax,
231
- jpeg_buffer_ptr,
232
- jpeg_size_ptr,
233
- ))
249
+ check_error(
250
+ lib.libisyntax_read_label_image_jpeg(
251
+ isyntax,
252
+ jpeg_buffer_ptr,
253
+ jpeg_size_ptr,
254
+ ),
255
+ )
234
256
  jpeg_buffer = ffi.gc(jpeg_buffer_ptr[0], free)
235
257
  jpeg_size = jpeg_size_ptr[0]
236
258
  return memoryview(ffi.buffer(jpeg_buffer, jpeg_size))
@@ -239,11 +261,13 @@ def read_label_image_jpeg(isyntax: ISyntaxPtr) -> memoryview:
239
261
  def read_macro_image_jpeg(isyntax: ISyntaxPtr) -> memoryview:
240
262
  jpeg_buffer_ptr = ffi.new("uint8_t**")
241
263
  jpeg_size_ptr = ffi.new("uint32_t*")
242
- check_error(lib.libisyntax_read_macro_image_jpeg(
243
- isyntax,
244
- jpeg_buffer_ptr,
245
- jpeg_size_ptr,
246
- ))
264
+ check_error(
265
+ lib.libisyntax_read_macro_image_jpeg(
266
+ isyntax,
267
+ jpeg_buffer_ptr,
268
+ jpeg_size_ptr,
269
+ ),
270
+ )
247
271
  jpeg_buffer = ffi.gc(jpeg_buffer_ptr[0], free)
248
272
  jpeg_size = jpeg_size_ptr[0]
249
273
  return memoryview(ffi.buffer(jpeg_buffer, jpeg_size))
@@ -252,12 +276,14 @@ def read_macro_image_jpeg(isyntax: ISyntaxPtr) -> memoryview:
252
276
  def read_icc_profile(isyntax: ISyntaxPtr, image: ISyntaxImagePtr) -> memoryview:
253
277
  profile_buffer_ptr = ffi.new("uint8_t**")
254
278
  profile_size_ptr = ffi.new("uint32_t*")
255
- check_error(lib.libisyntax_read_icc_profile(
256
- isyntax,
257
- image,
258
- profile_buffer_ptr,
259
- profile_size_ptr,
260
- ))
279
+ check_error(
280
+ lib.libisyntax_read_icc_profile(
281
+ isyntax,
282
+ image,
283
+ profile_buffer_ptr,
284
+ profile_size_ptr,
285
+ ),
286
+ )
261
287
  profile_buffer = ffi.gc(profile_buffer_ptr[0], free)
262
288
  profile_size = profile_size_ptr[0]
263
289
  return memoryview(ffi.buffer(profile_buffer, profile_size))
@@ -76,6 +76,16 @@ class ISyntaxImage:
76
76
  for i in range(self.level_count):
77
77
  yield self.get_level(i)
78
78
 
79
+ @property
80
+ def offset_x(self) -> int:
81
+ """X-offset in pixels of the WSI image origin from the macro image origin."""
82
+ return libisyntax.image_get_offset_x(self.ptr)
83
+
84
+ @property
85
+ def offset_y(self) -> int:
86
+ """Y-offset in pixels of the WSI image origin from the macro image origin."""
87
+ return libisyntax.image_get_offset_y(self.ptr)
88
+
79
89
 
80
90
  class ISyntax:
81
91
  def __init__(self, f: RawIOBase | BufferedIOBase, n_bytes: int, cache_size: int = 2000) -> None:
@@ -86,7 +96,7 @@ class ISyntax:
86
96
  self.ptr = libisyntax.open_from_registered_handle(self.io_handle, is_init_allocators=False)
87
97
  self.closed = False
88
98
  self._cache_size = cache_size
89
- self._cache = None
99
+ self._cache: ISyntaxCache | None = None
90
100
 
91
101
  @classmethod
92
102
  def open(cls: type["ISyntax"], filename: str | Path, cache_size: int = 2000) -> "ISyntax":
@@ -133,8 +143,15 @@ class ISyntax:
133
143
  """
134
144
  cache = self.get_cache()
135
145
  buf = np.empty((self.tile_height, self.tile_width, 4), dtype=np.uint8)
136
- libisyntax.tile_read(self.ptr, cache.ptr, level, tile_x, tile_y,
137
- buf.data, libisyntax.ISyntaxPixelFormat.RGBA)
146
+ libisyntax.tile_read(
147
+ self.ptr,
148
+ cache.ptr,
149
+ level,
150
+ tile_x,
151
+ tile_y,
152
+ buf.data,
153
+ libisyntax.ISyntaxPixelFormat.RGBA,
154
+ )
138
155
  return buf
139
156
 
140
157
  def read_region(self, x: int, y: int, width: int, height: int, level: int = 0) -> np.ndarray:
@@ -152,8 +169,17 @@ class ISyntax:
152
169
  """
153
170
  cache = self.get_cache()
154
171
  buf = np.empty((height, width, 4), dtype=np.uint8)
155
- libisyntax.read_region(self.ptr, cache.ptr, level, x, y, width, height,
156
- buf.data, libisyntax.ISyntaxPixelFormat.RGBA)
172
+ libisyntax.read_region(
173
+ self.ptr,
174
+ cache.ptr,
175
+ level,
176
+ x,
177
+ y,
178
+ width,
179
+ height,
180
+ buf.data,
181
+ libisyntax.ISyntaxPixelFormat.RGBA,
182
+ )
157
183
  return buf
158
184
 
159
185
  def read_label_image_jpeg(self) -> memoryview | None:
@@ -178,6 +204,10 @@ class ISyntax:
178
204
  except libisyntax.LibISyntaxFatalError:
179
205
  return None
180
206
 
207
+ @property
208
+ def barcode(self) -> str:
209
+ return libisyntax.get_barcode(self.ptr).decode("ascii")
210
+
181
211
  def read_icc_profile(self) -> memoryview | None:
182
212
  """Reads the ICC color profile for an image.
183
213
 
@@ -220,6 +250,16 @@ class ISyntax:
220
250
  level0 = self.wsi.get_level(0)
221
251
  return level0.mpp_y
222
252
 
253
+ @property
254
+ def offset_x(self) -> int:
255
+ """X-offset in pixels of the WSI image origin from the macro image origin."""
256
+ return self.wsi.offset_x
257
+
258
+ @property
259
+ def offset_y(self) -> int:
260
+ """Y-offset in pixels of the WSI image origin from the macro image origin."""
261
+ return self.wsi.offset_y
262
+
223
263
  def __enter__(self) -> "ISyntax":
224
264
  return self
225
265
 
@@ -27,13 +27,13 @@ def create_ffibuilder() -> FFI:
27
27
 
28
28
  project_dir = Path(__file__).parent.parent
29
29
 
30
- vendor = project_dir/"isyntax_build"/"vendor"
31
- src = project_dir/"isyntax_build"/"src"
32
- libisyntax_src = vendor/"libisyntax"/"src"
30
+ vendor = project_dir / "isyntax_build" / "vendor"
31
+ src = project_dir / "isyntax_build" / "src"
32
+ libisyntax_src = vendor / "libisyntax" / "src"
33
33
 
34
34
  if os.name == "nt":
35
35
  platform_sources = [
36
- src/"win32_utils.c",
36
+ src / "win32_utils.c",
37
37
  ]
38
38
  extra_compile_args = ["/std:c11"]
39
39
  libraries = ["winmm"]
@@ -46,25 +46,25 @@ def create_ffibuilder() -> FFI:
46
46
  "isyntax._pyisyntax",
47
47
  resources.read_text(isyntax_build, "pyisyntax.c"),
48
48
  sources=paths_to_strings(
49
- libisyntax_src/"libisyntax.c",
50
- libisyntax_src/"isyntax"/"isyntax.c",
51
- libisyntax_src/"isyntax"/"isyntax_reader.c",
52
- libisyntax_src/"utils"/"timerutils.c",
53
- libisyntax_src/"utils"/"block_allocator.c",
54
- libisyntax_src/"utils"/"benaphore.c",
55
- libisyntax_src/"platform"/"platform.c",
56
- libisyntax_src/"platform"/"work_queue.c",
57
- libisyntax_src/"third_party"/"yxml.c",
58
- libisyntax_src/"third_party"/"ltalloc.cc",
59
- src/"python_platform_utils.c",
49
+ libisyntax_src / "libisyntax.c",
50
+ libisyntax_src / "isyntax" / "isyntax.c",
51
+ libisyntax_src / "isyntax" / "isyntax_reader.c",
52
+ libisyntax_src / "utils" / "timerutils.c",
53
+ libisyntax_src / "utils" / "block_allocator.c",
54
+ libisyntax_src / "utils" / "benaphore.c",
55
+ libisyntax_src / "platform" / "platform.c",
56
+ libisyntax_src / "platform" / "work_queue.c",
57
+ libisyntax_src / "third_party" / "yxml.c",
58
+ libisyntax_src / "third_party" / "ltalloc.cc",
59
+ src / "python_platform_utils.c",
60
60
  *platform_sources,
61
61
  ),
62
62
  include_dirs=paths_to_strings(
63
63
  libisyntax_src,
64
- libisyntax_src/"isyntax",
65
- libisyntax_src/"platform",
66
- libisyntax_src/"third_party",
67
- libisyntax_src/"utils",
64
+ libisyntax_src / "isyntax",
65
+ libisyntax_src / "platform",
66
+ libisyntax_src / "third_party",
67
+ libisyntax_src / "utils",
68
68
  src,
69
69
  ),
70
70
  extra_compile_args=extra_compile_args,
@@ -72,13 +72,13 @@ def create_ffibuilder() -> FFI:
72
72
  )
73
73
 
74
74
  header_lines = []
75
- with (libisyntax_src/"libisyntax.h").open() as f:
75
+ with (libisyntax_src / "libisyntax.h").open() as f:
76
76
  for line in f:
77
77
  if line.startswith("#"):
78
78
  continue
79
79
  header_lines.append(line)
80
80
  header_lines.append("\n")
81
- with (src/"python_platform_utils.h").open() as f:
81
+ with (src / "python_platform_utils.h").open() as f:
82
82
  for line in f:
83
83
  if line.startswith("#"):
84
84
  continue
@@ -0,0 +1,91 @@
1
+ #include "libisyntax.h"
2
+
3
+ #include <stdint.h>
4
+ #include <stdio.h>
5
+ #include <dirent.h>
6
+ #include <sys/stat.h>
7
+ #include <sys/types.h>
8
+ #include <malloc.h>
9
+
10
+
11
+ #define CHECK_LIBISYNTAX_OK(_libisyntax_call) do { \
12
+ isyntax_error_t result = _libisyntax_call; \
13
+ assert(result == LIBISYNTAX_OK); \
14
+ } while(0)
15
+
16
+
17
+ #define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var)
18
+
19
+ #ifdef _WIN32
20
+ #define PATH_SEP "\\"
21
+ #else
22
+ #define PATH_SEP "/";
23
+ #endif
24
+
25
+ // This program loops through all the .isyntax files in a folder (including subfolders),
26
+ // and outputs the barcode for each file to stdout (in comma-separated format).
27
+ // This may be useful for large collections of WSIs where you can't tell which is which from the filename.
28
+
29
+ // NOTE: the barcode/label information must be preserved for this to work.
30
+ // This information might be deleted when using the Philips IMS export function (unless that setting is changed).
31
+
32
+ int read_barcode_of_isyntax_files_in_directory(const char* dir, const char* subdir_prefix) {
33
+ // Looping through files in a folder.
34
+ // Reference: https://stackoverflow.com/questions/1271064/how-do-i-loop-through-all-files-in-a-folder-using-c
35
+ struct dirent *dp;
36
+ DIR *dfd;
37
+ if ((dfd = opendir(dir)) == NULL) {
38
+ fprintf(stderr, "Can't open %s\n", dir);
39
+ return 0;
40
+ }
41
+
42
+ char filename[260] ;
43
+ while ((dp = readdir(dfd)) != NULL) {
44
+ struct stat stbuf ;
45
+ sprintf(filename, "%s" PATH_SEP "%s", dir, dp->d_name);
46
+ if(stat(filename, &stbuf) == -1) {
47
+ printf("Unable to stat file: %s\n" , filename);
48
+ continue ;
49
+ }
50
+ if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
51
+ if (dp->d_name[0] != '.') {
52
+ char* subdir_path = malloc(260);
53
+ snprintf(subdir_path, 260, "%s" PATH_SEP "%s", dir, dp->d_name);
54
+ read_barcode_of_isyntax_files_in_directory(subdir_path, dp->d_name); // recurse into subdirectories
55
+ free(subdir_path);
56
+ }
57
+ continue; // Skip directories
58
+ } else {
59
+ size_t filename_len = strlen(dp->d_name);
60
+ const char* suffix = ".isyntax";
61
+ size_t suffix_len = strlen(suffix);
62
+ if (strncmp(dp->d_name + filename_len - suffix_len, suffix, suffix_len) == 0) {
63
+ // Open the iSyntax file in a special mode where the header is only read partially (for speed).
64
+ isyntax_t* isyntax;
65
+ if (libisyntax_open(filename, LIBISYNTAX_OPEN_FLAG_READ_BARCODE_ONLY, &isyntax) == LIBISYNTAX_OK) {
66
+ // Print out the path of the .isyntax file + the barcode in .csv format.
67
+ if (subdir_prefix) {
68
+ printf("%s" PATH_SEP "%s,%s\n", subdir_prefix, dp->d_name, libisyntax_get_barcode(isyntax));
69
+ } else {
70
+ printf("%s,%s\n", dp->d_name, libisyntax_get_barcode(isyntax));
71
+ }
72
+ libisyntax_close(isyntax);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ return 0;
78
+ }
79
+
80
+ int main(int argc, char** argv) {
81
+ if (argc <= 1) {
82
+ printf("Usage: %s <directory_path> - output filename and barcode (comma-separated) for each iSyntax file in the directory and its subdirectories\n",
83
+ argv[0], argv[0], argv[0], argv[0]);
84
+ return 0;
85
+ }
86
+
87
+ libisyntax_init();
88
+
89
+ char* dir = argv[1];
90
+ return read_barcode_of_isyntax_files_in_directory(dir, NULL);
91
+ }
@@ -37,6 +37,7 @@
37
37
  #endif
38
38
 
39
39
  #include <stdint.h>
40
+ #include <inttypes.h>
40
41
  #include <assert.h>
41
42
  #include <stdlib.h>
42
43
  #include <string.h>
@@ -221,7 +222,7 @@ uint64_t parse_cache_size(const char *size_str) {
221
222
  uint64_t size;
222
223
  char unit;
223
224
 
224
- if (sscanf(size_str, "%lld%c", &size, &unit) == 2) {
225
+ if (sscanf(size_str, "%"PRIu64"%c", &size, &unit) == 2) {
225
226
  if (unit == 'M') {
226
227
  if (size > INT64_MAX / (1024)) {
227
228
  printf("Error: Cache size too large.\n");
@@ -238,7 +239,7 @@ uint64_t parse_cache_size(const char *size_str) {
238
239
  printf("Error: Invalid unit for cache size. Use 'M' for megabytes or 'G' for gigabytes.\n");
239
240
  return -1;
240
241
  }
241
- } else if (sscanf(size_str, "%lld", &size) != 1) {
242
+ } else if (sscanf(size_str, "%"PRIu64, &size) != 1) {
242
243
  printf("Error: Invalid cache size format.\n");
243
244
  return -1;
244
245
  }
@@ -426,7 +427,7 @@ int main(int argc, char **argv) {
426
427
 
427
428
  isyntax_cache_t *isyntax_cache = NULL;
428
429
  if (libisyntax_cache_create("isyntax-to-tiff cache", cache_size, &isyntax_cache) != LIBISYNTAX_OK) {
429
- fprintf(stderr, "Failed to create iSyntax cache with size %llu.\n", cache_size);
430
+ fprintf(stderr, "Failed to create iSyntax cache with size %"PRIu64".\n", cache_size);
430
431
  libisyntax_close(isyntax);
431
432
  return -1;
432
433
  }