pyisyntax 0.1.4__tar.gz → 0.1.6__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.
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/CHANGELOG.md +13 -1
- {pyisyntax-0.1.4/pyisyntax.egg-info → pyisyntax-0.1.6}/PKG-INFO +27 -22
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/README.md +23 -11
- pyisyntax-0.1.6/isyntax/_pyisyntax.pyi +4 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax/lowlevel/io_management.py +11 -8
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax/lowlevel/libisyntax.py +68 -46
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax/wrapper.py +41 -5
- pyisyntax-0.1.6/isyntax_build/builder.py +112 -0
- pyisyntax-0.1.6/isyntax_build/vendor/libisyntax/src/examples/isyntax_dirwalk.c +91 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax.c +58 -33
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax.h +6 -3
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_reader.c +16 -8
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_reader.h +3 -2
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_streamer.c +4 -1
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/libisyntax.c +97 -39
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/libisyntax.h +11 -1
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/block_allocator.c +22 -13
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/memrw.c +24 -15
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/timerutils.c +24 -15
- {pyisyntax-0.1.4 → pyisyntax-0.1.6/pyisyntax.egg-info}/PKG-INFO +27 -22
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/pyisyntax.egg-info/SOURCES.txt +3 -0
- pyisyntax-0.1.6/pyisyntax.egg-info/requires.txt +2 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/pyproject.toml +39 -22
- pyisyntax-0.1.6/tests/__init__.py +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/tests/conftest.py +2 -2
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/tests/test_lowlevel.py +33 -7
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/tests/test_wrapper.py +9 -1
- pyisyntax-0.1.4/isyntax_build/builder.py +0 -98
- pyisyntax-0.1.4/pyisyntax.egg-info/requires.txt +0 -11
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/LICENSE +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/MANIFEST.in +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax/__init__.py +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax/lowlevel/__init__.py +0 -0
- /pyisyntax-0.1.4/isyntax_build/__init__.py → /pyisyntax-0.1.6/isyntax/py.typed +0 -0
- {pyisyntax-0.1.4/tests → pyisyntax-0.1.6/isyntax_build}/__init__.py +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/pyisyntax.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/src/python_platform_utils.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/src/python_platform_utils.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/src/win32_utils.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/LICENSE.txt +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/README.md +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/examples/isyntax_example.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/examples/isyntax_to_tiff.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_dwt.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_streamer.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/platform/arena.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/platform/common.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/platform/intrinsics.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/platform/linux_platform.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/platform/linux_utils.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/platform/platform.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/platform/platform.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/platform/win32_utils.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/platform/win32_utils.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/platform/work_queue.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/platform/work_queue.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/third_party/ltalloc.cc +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/third_party/ltalloc.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/third_party/stb_ds.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/third_party/stb_image.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/third_party/stb_image_write.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/third_party/stb_sprintf.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/third_party/yxml.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/third_party/yxml.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/benaphore.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/benaphore.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/block_allocator.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/libtiff_api.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/mathutils.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/mathutils.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/memrw.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/stringutils.c +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/stringutils.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/isyntax_build/vendor/libisyntax/src/utils/timerutils.h +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/pyisyntax.egg-info/dependency_links.txt +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/pyisyntax.egg-info/not-zip-safe +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/pyisyntax.egg-info/top_level.txt +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/setup.cfg +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/setup.py +0 -0
- {pyisyntax-0.1.4 → pyisyntax-0.1.6}/tests/data/DOWNLOAD.txt +0 -0
|
@@ -7,6 +7,17 @@ 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
|
+
|
|
10
21
|
## [0.1.4] - 2024-08-31
|
|
11
22
|
|
|
12
23
|
### Added
|
|
@@ -61,7 +72,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
61
72
|
|
|
62
73
|
- This is the initial release.
|
|
63
74
|
|
|
64
|
-
[unreleased]: https://github.com/anibali/pyisyntax/compare/v0.1.
|
|
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
|
|
65
77
|
[0.1.4]: https://github.com/anibali/pyisyntax/compare/v0.1.3...v0.1.4
|
|
66
78
|
[0.1.3]: https://github.com/anibali/pyisyntax/compare/v0.1.2...v0.1.3
|
|
67
79
|
[0.1.2]: https://github.com/anibali/pyisyntax/compare/v0.1.1...v0.1.2
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pyisyntax
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: Python bindings for libisyntax
|
|
5
5
|
Author-email: Aiden Nibali <dismaldenizen@gmail.com>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/anibali/pyisyntax
|
|
8
8
|
Project-URL: Source, https://github.com/anibali/pyisyntax
|
|
9
9
|
Project-URL: Download, https://pypi.org/project/pyisyntax/#files
|
|
@@ -14,14 +14,7 @@ Description-Content-Type: text/markdown
|
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
Requires-Dist: cffi>=1.12
|
|
16
16
|
Requires-Dist: numpy
|
|
17
|
-
|
|
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,34 @@ 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
|
-
|
|
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
|
-
$
|
|
72
|
+
$ uv sync --frozen
|
|
82
73
|
```
|
|
83
|
-
|
|
74
|
+
|
|
75
|
+
To modify pyisyntax project dependencies:
|
|
76
|
+
|
|
77
|
+
1. Edit dependencies in `pyproject.toml`.
|
|
78
|
+
2. Update the lock file using uv:
|
|
84
79
|
```console
|
|
85
|
-
$
|
|
80
|
+
$ uv lock
|
|
86
81
|
```
|
|
87
82
|
|
|
88
|
-
|
|
83
|
+
### Cutting a new release
|
|
89
84
|
|
|
90
|
-
1.
|
|
91
|
-
2.
|
|
85
|
+
1. Ensure that tests are passing and everything is ready for release.
|
|
86
|
+
2. Create and push a Git tag:
|
|
92
87
|
```console
|
|
93
|
-
$
|
|
88
|
+
$ git tag v0.1.6
|
|
89
|
+
$ git push --tags
|
|
94
90
|
```
|
|
91
|
+
3. Download the artifacts from GitHub Actions, which will include the source distribution tarball and binary wheels.
|
|
92
|
+
4. Create a new release on GitHub from the tagged commit and upload the packages as attachments to the release.
|
|
93
|
+
5. Also upload the packages to PyPI using Twine:
|
|
94
|
+
```console
|
|
95
|
+
$ uv run twine upload pyisyntax-*.tar.gz pyisyntax-*.whl
|
|
96
|
+
```
|
|
97
|
+
6. Bump the version number in `pyproject.toml` and create a commit, signalling the start of development on the next version.
|
|
98
|
+
|
|
99
|
+
These files should also be added to a GitHub release.
|
|
@@ -48,22 +48,34 @@ 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
|
-
|
|
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
|
-
$
|
|
54
|
+
$ uv sync --frozen
|
|
57
55
|
```
|
|
58
|
-
|
|
56
|
+
|
|
57
|
+
To modify pyisyntax project dependencies:
|
|
58
|
+
|
|
59
|
+
1. Edit dependencies in `pyproject.toml`.
|
|
60
|
+
2. Update the lock file using uv:
|
|
59
61
|
```console
|
|
60
|
-
$
|
|
62
|
+
$ uv lock
|
|
61
63
|
```
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
### Cutting a new release
|
|
64
66
|
|
|
65
|
-
1.
|
|
66
|
-
2.
|
|
67
|
+
1. Ensure that tests are passing and everything is ready for release.
|
|
68
|
+
2. Create and push a Git tag:
|
|
67
69
|
```console
|
|
68
|
-
$
|
|
70
|
+
$ git tag v0.1.6
|
|
71
|
+
$ git push --tags
|
|
69
72
|
```
|
|
73
|
+
3. Download the artifacts from GitHub Actions, which will include the source distribution tarball and binary wheels.
|
|
74
|
+
4. Create a new release on GitHub from the tagged commit and upload the packages as attachments to the release.
|
|
75
|
+
5. Also upload the packages to PyPI using Twine:
|
|
76
|
+
```console
|
|
77
|
+
$ uv run twine upload pyisyntax-*.tar.gz pyisyntax-*.whl
|
|
78
|
+
```
|
|
79
|
+
6. Bump the version number in `pyproject.toml` and create a commit, signalling the start of development on the next version.
|
|
80
|
+
|
|
81
|
+
These files should also be added to a GitHub release.
|
|
@@ -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
|
-
|
|
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
|
|
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(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
|
@@ -131,6 +133,14 @@ def image_get_level(wsi_image: ISyntaxImagePtr, index: int) -> ISyntaxLevelPtr:
|
|
|
131
133
|
return lib.libisyntax_image_get_level(wsi_image, index)
|
|
132
134
|
|
|
133
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
|
+
|
|
134
144
|
def level_get_scale(level: ISyntaxLevelPtr) -> int:
|
|
135
145
|
return lib.libisyntax_level_get_scale(level)
|
|
136
146
|
|
|
@@ -165,11 +175,13 @@ def cache_create(debug_name: str | None, cache_size: int) -> ISyntaxCachePtr:
|
|
|
165
175
|
debug_name_or_null = ffi.NULL
|
|
166
176
|
else:
|
|
167
177
|
debug_name_or_null = ffi.new("char[]", debug_name.encode("utf-8"))
|
|
168
|
-
check_error(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
178
|
+
check_error(
|
|
179
|
+
lib.libisyntax_cache_create(
|
|
180
|
+
debug_name_or_null,
|
|
181
|
+
cache_size,
|
|
182
|
+
isyntax_cache,
|
|
183
|
+
),
|
|
184
|
+
)
|
|
173
185
|
return isyntax_cache[0]
|
|
174
186
|
|
|
175
187
|
|
|
@@ -192,15 +204,17 @@ def tile_read(
|
|
|
192
204
|
pixels_buffer: "Buffer | FFI.buffer",
|
|
193
205
|
pixel_format: ISyntaxPixelFormat,
|
|
194
206
|
) -> None:
|
|
195
|
-
check_error(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
+
)
|
|
204
218
|
|
|
205
219
|
|
|
206
220
|
def read_region(
|
|
@@ -214,27 +228,31 @@ def read_region(
|
|
|
214
228
|
pixels_buffer: "Buffer | FFI.buffer",
|
|
215
229
|
pixel_format: ISyntaxPixelFormat,
|
|
216
230
|
) -> None:
|
|
217
|
-
check_error(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
+
)
|
|
228
244
|
|
|
229
245
|
|
|
230
246
|
def read_label_image_jpeg(isyntax: ISyntaxPtr) -> memoryview:
|
|
231
247
|
jpeg_buffer_ptr = ffi.new("uint8_t**")
|
|
232
248
|
jpeg_size_ptr = ffi.new("uint32_t*")
|
|
233
|
-
check_error(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
249
|
+
check_error(
|
|
250
|
+
lib.libisyntax_read_label_image_jpeg(
|
|
251
|
+
isyntax,
|
|
252
|
+
jpeg_buffer_ptr,
|
|
253
|
+
jpeg_size_ptr,
|
|
254
|
+
),
|
|
255
|
+
)
|
|
238
256
|
jpeg_buffer = ffi.gc(jpeg_buffer_ptr[0], free)
|
|
239
257
|
jpeg_size = jpeg_size_ptr[0]
|
|
240
258
|
return memoryview(ffi.buffer(jpeg_buffer, jpeg_size))
|
|
@@ -243,11 +261,13 @@ def read_label_image_jpeg(isyntax: ISyntaxPtr) -> memoryview:
|
|
|
243
261
|
def read_macro_image_jpeg(isyntax: ISyntaxPtr) -> memoryview:
|
|
244
262
|
jpeg_buffer_ptr = ffi.new("uint8_t**")
|
|
245
263
|
jpeg_size_ptr = ffi.new("uint32_t*")
|
|
246
|
-
check_error(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
264
|
+
check_error(
|
|
265
|
+
lib.libisyntax_read_macro_image_jpeg(
|
|
266
|
+
isyntax,
|
|
267
|
+
jpeg_buffer_ptr,
|
|
268
|
+
jpeg_size_ptr,
|
|
269
|
+
),
|
|
270
|
+
)
|
|
251
271
|
jpeg_buffer = ffi.gc(jpeg_buffer_ptr[0], free)
|
|
252
272
|
jpeg_size = jpeg_size_ptr[0]
|
|
253
273
|
return memoryview(ffi.buffer(jpeg_buffer, jpeg_size))
|
|
@@ -256,12 +276,14 @@ def read_macro_image_jpeg(isyntax: ISyntaxPtr) -> memoryview:
|
|
|
256
276
|
def read_icc_profile(isyntax: ISyntaxPtr, image: ISyntaxImagePtr) -> memoryview:
|
|
257
277
|
profile_buffer_ptr = ffi.new("uint8_t**")
|
|
258
278
|
profile_size_ptr = ffi.new("uint32_t*")
|
|
259
|
-
check_error(
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
279
|
+
check_error(
|
|
280
|
+
lib.libisyntax_read_icc_profile(
|
|
281
|
+
isyntax,
|
|
282
|
+
image,
|
|
283
|
+
profile_buffer_ptr,
|
|
284
|
+
profile_size_ptr,
|
|
285
|
+
),
|
|
286
|
+
)
|
|
265
287
|
profile_buffer = ffi.gc(profile_buffer_ptr[0], free)
|
|
266
288
|
profile_size = profile_size_ptr[0]
|
|
267
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(
|
|
137
|
-
|
|
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(
|
|
156
|
-
|
|
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:
|
|
@@ -224,6 +250,16 @@ class ISyntax:
|
|
|
224
250
|
level0 = self.wsi.get_level(0)
|
|
225
251
|
return level0.mpp_y
|
|
226
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
|
+
|
|
227
263
|
def __enter__(self) -> "ISyntax":
|
|
228
264
|
return self
|
|
229
265
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import platform
|
|
3
|
+
from importlib import resources
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from cffi import FFI
|
|
7
|
+
|
|
8
|
+
import isyntax_build
|
|
9
|
+
|
|
10
|
+
cdef_extra = """
|
|
11
|
+
extern "Python" {
|
|
12
|
+
bool python_file_set_pos(int, int64_t);
|
|
13
|
+
int64_t python_file_read_into(int, void*, size_t);
|
|
14
|
+
int64_t python_file_get_size(int);
|
|
15
|
+
void python_file_close(int);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
void free(void *ptr);
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def paths_to_strings(*paths: Path) -> list[str]:
|
|
23
|
+
return [str(path) for path in paths]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def create_ffibuilder() -> FFI:
|
|
27
|
+
ffibuilder = FFI()
|
|
28
|
+
|
|
29
|
+
project_dir = Path(__file__).parent.parent
|
|
30
|
+
|
|
31
|
+
vendor = project_dir / "isyntax_build" / "vendor"
|
|
32
|
+
src = project_dir / "isyntax_build" / "src"
|
|
33
|
+
libisyntax_src = vendor / "libisyntax" / "src"
|
|
34
|
+
|
|
35
|
+
if os.name == "nt":
|
|
36
|
+
platform_sources = [
|
|
37
|
+
src / "win32_utils.c",
|
|
38
|
+
]
|
|
39
|
+
extra_compile_args = ["/std:c11"]
|
|
40
|
+
libraries = ["winmm"]
|
|
41
|
+
else:
|
|
42
|
+
platform_sources = []
|
|
43
|
+
extra_compile_args = []
|
|
44
|
+
libraries = []
|
|
45
|
+
# The vendored libisyntax guards its SIMD/atomics on x86-centric macros
|
|
46
|
+
# that GCC does not define on aarch64 Linux, so the arm build otherwise
|
|
47
|
+
# fails to compile / links x86-only intrinsics. Select the correct paths:
|
|
48
|
+
# __ARM_NEON__ -> intrinsics.h includes <arm_neon.h> for the NEON code
|
|
49
|
+
# __arm64 -> ltalloc spin-wait uses sched_yield, not x86 `pause`
|
|
50
|
+
# the last two map x86-only bit intrinsics onto portable GCC builtins
|
|
51
|
+
if platform.machine() in ("aarch64", "arm64"):
|
|
52
|
+
extra_compile_args += [
|
|
53
|
+
"-D__ARM_NEON__=1",
|
|
54
|
+
"-D__arm64=1",
|
|
55
|
+
"-D_bit_scan_forward=__builtin_ctz",
|
|
56
|
+
"-D_popcnt32=__builtin_popcount",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
ffibuilder.set_source(
|
|
60
|
+
"isyntax._pyisyntax",
|
|
61
|
+
resources.read_text(isyntax_build, "pyisyntax.c"),
|
|
62
|
+
sources=paths_to_strings(
|
|
63
|
+
libisyntax_src / "libisyntax.c",
|
|
64
|
+
libisyntax_src / "isyntax" / "isyntax.c",
|
|
65
|
+
libisyntax_src / "isyntax" / "isyntax_reader.c",
|
|
66
|
+
libisyntax_src / "utils" / "timerutils.c",
|
|
67
|
+
libisyntax_src / "utils" / "block_allocator.c",
|
|
68
|
+
libisyntax_src / "utils" / "benaphore.c",
|
|
69
|
+
libisyntax_src / "platform" / "platform.c",
|
|
70
|
+
libisyntax_src / "platform" / "work_queue.c",
|
|
71
|
+
libisyntax_src / "third_party" / "yxml.c",
|
|
72
|
+
libisyntax_src / "third_party" / "ltalloc.cc",
|
|
73
|
+
src / "python_platform_utils.c",
|
|
74
|
+
*platform_sources,
|
|
75
|
+
),
|
|
76
|
+
include_dirs=paths_to_strings(
|
|
77
|
+
libisyntax_src,
|
|
78
|
+
libisyntax_src / "isyntax",
|
|
79
|
+
libisyntax_src / "platform",
|
|
80
|
+
libisyntax_src / "third_party",
|
|
81
|
+
libisyntax_src / "utils",
|
|
82
|
+
src,
|
|
83
|
+
),
|
|
84
|
+
extra_compile_args=extra_compile_args,
|
|
85
|
+
libraries=libraries,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
header_lines = []
|
|
89
|
+
with (libisyntax_src / "libisyntax.h").open() as f:
|
|
90
|
+
for line in f:
|
|
91
|
+
if line.startswith("#"):
|
|
92
|
+
continue
|
|
93
|
+
header_lines.append(line)
|
|
94
|
+
header_lines.append("\n")
|
|
95
|
+
with (src / "python_platform_utils.h").open() as f:
|
|
96
|
+
for line in f:
|
|
97
|
+
if line.startswith("#"):
|
|
98
|
+
continue
|
|
99
|
+
header_lines.append(line)
|
|
100
|
+
header_text = "".join(header_lines)
|
|
101
|
+
header_text += "\n\n"
|
|
102
|
+
header_text += cdef_extra
|
|
103
|
+
|
|
104
|
+
ffibuilder.cdef(header_text)
|
|
105
|
+
|
|
106
|
+
return ffibuilder
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
ffibuilder = create_ffibuilder()
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
ffibuilder.compile(verbose=True)
|
|
@@ -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
|
+
}
|