pyisyntax 0.1.0__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.0/LICENSE +20 -0
- pyisyntax-0.1.0/MANIFEST.in +14 -0
- pyisyntax-0.1.0/PKG-INFO +81 -0
- pyisyntax-0.1.0/README.md +63 -0
- pyisyntax-0.1.0/isyntax/__init__.py +5 -0
- pyisyntax-0.1.0/isyntax/lowlevel/__init__.py +0 -0
- pyisyntax-0.1.0/isyntax/lowlevel/io_management.py +115 -0
- pyisyntax-0.1.0/isyntax/lowlevel/libisyntax.py +260 -0
- pyisyntax-0.1.0/isyntax/wrapper.py +228 -0
- pyisyntax-0.1.0/isyntax_build/__init__.py +0 -0
- pyisyntax-0.1.0/isyntax_build/builder.py +98 -0
- pyisyntax-0.1.0/isyntax_build/pyisyntax.c +2 -0
- pyisyntax-0.1.0/isyntax_build/src/python_platform_utils.c +83 -0
- pyisyntax-0.1.0/isyntax_build/src/python_platform_utils.h +12 -0
- pyisyntax-0.1.0/isyntax_build/src/win32_utils.c +57 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/LICENSE.txt +24 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/README.md +3 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/examples/isyntax_example.c +118 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/examples/isyntax_to_tiff.c +490 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/isyntax/isyntax.c +3430 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/isyntax/isyntax.h +416 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_dwt.c +594 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_reader.c +418 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_reader.h +30 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_streamer.c +1097 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_streamer.h +90 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/libisyntax.c +494 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/libisyntax.h +93 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/platform/arena.h +92 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/platform/common.h +379 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/platform/intrinsics.h +262 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/platform/linux_platform.c +52 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/platform/linux_utils.c +106 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/platform/platform.c +142 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/platform/platform.h +188 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/platform/win32_utils.c +264 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/platform/win32_utils.h +49 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/platform/work_queue.c +238 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/platform/work_queue.h +102 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/third_party/ltalloc.cc +1342 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/third_party/ltalloc.h +17 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/third_party/stb_ds.h +1895 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/third_party/stb_image.h +7987 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/third_party/stb_image_write.h +1724 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/third_party/stb_sprintf.h +1906 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/third_party/yxml.c +1060 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/third_party/yxml.h +162 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/benaphore.c +76 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/benaphore.h +60 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/block_allocator.c +133 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/block_allocator.h +88 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/libtiff_api.h +202 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/mathutils.c +328 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/mathutils.h +182 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/memrw.c +135 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/memrw.h +62 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/stringutils.c +163 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/stringutils.h +48 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/timerutils.c +99 -0
- pyisyntax-0.1.0/isyntax_build/vendor/libisyntax/src/utils/timerutils.h +48 -0
- pyisyntax-0.1.0/pyisyntax.egg-info/PKG-INFO +81 -0
- pyisyntax-0.1.0/pyisyntax.egg-info/SOURCES.txt +72 -0
- pyisyntax-0.1.0/pyisyntax.egg-info/dependency_links.txt +1 -0
- pyisyntax-0.1.0/pyisyntax.egg-info/not-zip-safe +1 -0
- pyisyntax-0.1.0/pyisyntax.egg-info/requires.txt +9 -0
- pyisyntax-0.1.0/pyisyntax.egg-info/top_level.txt +2 -0
- pyisyntax-0.1.0/pyproject.toml +65 -0
- pyisyntax-0.1.0/setup.cfg +4 -0
- pyisyntax-0.1.0/setup.py +13 -0
- pyisyntax-0.1.0/tests/__init__.py +0 -0
- pyisyntax-0.1.0/tests/conftest.py +16 -0
- pyisyntax-0.1.0/tests/data/DOWNLOAD.txt +1 -0
- pyisyntax-0.1.0/tests/test_lowlevel.py +189 -0
- pyisyntax-0.1.0/tests/test_wrapper.py +113 -0
pyisyntax-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2024 Aiden Nibali
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
include LICENSE
|
|
3
|
+
|
|
4
|
+
graft isyntax_build
|
|
5
|
+
prune isyntax_build/vendor/libisyntax
|
|
6
|
+
graft isyntax_build/vendor/libisyntax/src
|
|
7
|
+
include isyntax_build/vendor/libisyntax/LICENSE.txt
|
|
8
|
+
include isyntax_build/vendor/libisyntax/README.md
|
|
9
|
+
|
|
10
|
+
graft tests
|
|
11
|
+
recursive-exclude tests *.isyntax
|
|
12
|
+
|
|
13
|
+
prune */.pytest_cache
|
|
14
|
+
prune */__pycache__
|
pyisyntax-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pyisyntax
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python bindings for libisyntax
|
|
5
|
+
Author-email: Aiden Nibali <dismaldenizen@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: cffi>=1.12
|
|
11
|
+
Requires-Dist: numpy
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: conda-lock; extra == "dev"
|
|
14
|
+
Requires-Dist: pytest; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
16
|
+
Requires-Dist: ruff==0.2.1; extra == "dev"
|
|
17
|
+
Requires-Dist: twine; extra == "dev"
|
|
18
|
+
|
|
19
|
+
# pyisyntax
|
|
20
|
+
|
|
21
|
+
A Python library for working with pathology images in the iSyntax file format,
|
|
22
|
+
powered by [libisyntax](https://github.com/amspath/libisyntax).
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Read and display a region of the WSI via
|
|
27
|
+
[Pillow](https://pillow.readthedocs.io/).
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from isyntax import ISyntax
|
|
31
|
+
import PIL.Image
|
|
32
|
+
|
|
33
|
+
with ISyntax.open("my_file.isyntax") as isyntax:
|
|
34
|
+
# Read pixels from the specified region into a numpy array
|
|
35
|
+
pixels = isyntax.read_region(500, 500, 400, 200, level=4)
|
|
36
|
+
# Convert numpy array into a PIL image
|
|
37
|
+
pil_image = PIL.Image.fromarray(pixels)
|
|
38
|
+
# Show the image
|
|
39
|
+
pil_image.show()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Extract and save the associated macro image.
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from isyntax import ISyntax
|
|
46
|
+
|
|
47
|
+
with ISyntax.open("my_file.isyntax") as isyntax:
|
|
48
|
+
# The macro image will be returned as compressed JPEG data.
|
|
49
|
+
jpeg_data = isyntax.read_macro_image_jpeg()
|
|
50
|
+
# This JPEG data can be written directly to a file.
|
|
51
|
+
with open("macro_image.jpg", "wb") as f:
|
|
52
|
+
f.write(jpeg_data)
|
|
53
|
+
# Alternatively, you could decompress the data using Pillow:
|
|
54
|
+
# pil_image = PIL.Image.open(io.BytesIO(jpeg_data), formats=["JPEG"])
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Development
|
|
58
|
+
|
|
59
|
+
### Dependency management
|
|
60
|
+
|
|
61
|
+
To set up a development environment from the lock file:
|
|
62
|
+
|
|
63
|
+
1. Ensure that you have
|
|
64
|
+
[micromamba](https://mamba.readthedocs.io/en/latest/user_guide/micromamba.html)
|
|
65
|
+
installed.
|
|
66
|
+
2. Create the environment:
|
|
67
|
+
```console
|
|
68
|
+
$ micromamba create -n pyisyntax -f conda-lock.yml --category main --category dev
|
|
69
|
+
```
|
|
70
|
+
3. Activate the environment:
|
|
71
|
+
```console
|
|
72
|
+
$ micromamba activate pyisyntax
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
To modify pyisyntax project dependencies:
|
|
76
|
+
|
|
77
|
+
1. Edit pyproject.toml.
|
|
78
|
+
2. Update the lock file using conda-lock:
|
|
79
|
+
```console
|
|
80
|
+
$ conda-lock lock -f pyproject.toml -p linux-64 --micromamba
|
|
81
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# pyisyntax
|
|
2
|
+
|
|
3
|
+
A Python library for working with pathology images in the iSyntax file format,
|
|
4
|
+
powered by [libisyntax](https://github.com/amspath/libisyntax).
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
Read and display a region of the WSI via
|
|
9
|
+
[Pillow](https://pillow.readthedocs.io/).
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from isyntax import ISyntax
|
|
13
|
+
import PIL.Image
|
|
14
|
+
|
|
15
|
+
with ISyntax.open("my_file.isyntax") as isyntax:
|
|
16
|
+
# Read pixels from the specified region into a numpy array
|
|
17
|
+
pixels = isyntax.read_region(500, 500, 400, 200, level=4)
|
|
18
|
+
# Convert numpy array into a PIL image
|
|
19
|
+
pil_image = PIL.Image.fromarray(pixels)
|
|
20
|
+
# Show the image
|
|
21
|
+
pil_image.show()
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Extract and save the associated macro image.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from isyntax import ISyntax
|
|
28
|
+
|
|
29
|
+
with ISyntax.open("my_file.isyntax") as isyntax:
|
|
30
|
+
# The macro image will be returned as compressed JPEG data.
|
|
31
|
+
jpeg_data = isyntax.read_macro_image_jpeg()
|
|
32
|
+
# This JPEG data can be written directly to a file.
|
|
33
|
+
with open("macro_image.jpg", "wb") as f:
|
|
34
|
+
f.write(jpeg_data)
|
|
35
|
+
# Alternatively, you could decompress the data using Pillow:
|
|
36
|
+
# pil_image = PIL.Image.open(io.BytesIO(jpeg_data), formats=["JPEG"])
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Development
|
|
40
|
+
|
|
41
|
+
### Dependency management
|
|
42
|
+
|
|
43
|
+
To set up a development environment from the lock file:
|
|
44
|
+
|
|
45
|
+
1. Ensure that you have
|
|
46
|
+
[micromamba](https://mamba.readthedocs.io/en/latest/user_guide/micromamba.html)
|
|
47
|
+
installed.
|
|
48
|
+
2. Create the environment:
|
|
49
|
+
```console
|
|
50
|
+
$ micromamba create -n pyisyntax -f conda-lock.yml --category main --category dev
|
|
51
|
+
```
|
|
52
|
+
3. Activate the environment:
|
|
53
|
+
```console
|
|
54
|
+
$ micromamba activate pyisyntax
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
To modify pyisyntax project dependencies:
|
|
58
|
+
|
|
59
|
+
1. Edit pyproject.toml.
|
|
60
|
+
2. Update the lock file using conda-lock:
|
|
61
|
+
```console
|
|
62
|
+
$ conda-lock lock -f pyproject.toml -p linux-64 --micromamba
|
|
63
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from collections.abc import Iterator
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from io import BufferedIOBase, RawIOBase
|
|
4
|
+
from typing import Generic, NewType, TypeVar
|
|
5
|
+
|
|
6
|
+
from _pyisyntax import ffi, lib
|
|
7
|
+
|
|
8
|
+
VoidPtr = NewType("VoidPtr", object)
|
|
9
|
+
T = TypeVar("T")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class SizedIO:
|
|
14
|
+
#: The IO object.
|
|
15
|
+
f: RawIOBase | BufferedIOBase
|
|
16
|
+
#: The complete length of the IO object's data (in bytes).
|
|
17
|
+
n_bytes: int
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ByHandleRegistry(Generic[T]):
|
|
21
|
+
class _EmptySlot:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
def __init__(self) -> None:
|
|
25
|
+
"""Creates a registry of objects keyed by integer handles.
|
|
26
|
+
|
|
27
|
+
Handles start at 1, autoincrement, and will automatically be recycled
|
|
28
|
+
once objects are removed from the registry.
|
|
29
|
+
"""
|
|
30
|
+
self._list = []
|
|
31
|
+
self._n_free = 0
|
|
32
|
+
self._empty_slot = self._EmptySlot()
|
|
33
|
+
|
|
34
|
+
def pop(self, handle: int) -> T:
|
|
35
|
+
element = self._list[handle - 1]
|
|
36
|
+
self._list[handle - 1] = self._empty_slot
|
|
37
|
+
self._n_free += 1
|
|
38
|
+
if len(self._list) == handle:
|
|
39
|
+
while len(self._list) > 0 and self._list[-1] is self._empty_slot:
|
|
40
|
+
self._list.pop()
|
|
41
|
+
self._n_free -= 1
|
|
42
|
+
return element
|
|
43
|
+
|
|
44
|
+
def add(self, element: T) -> int:
|
|
45
|
+
if self._n_free == 0:
|
|
46
|
+
self._list.append(element)
|
|
47
|
+
return len(self._list)
|
|
48
|
+
index = self._list.index(self._empty_slot)
|
|
49
|
+
self._list[index] = element
|
|
50
|
+
self._n_free -= 1
|
|
51
|
+
# Handles start at 1.
|
|
52
|
+
return index + 1
|
|
53
|
+
|
|
54
|
+
def __getitem__(self, handle: int) -> T:
|
|
55
|
+
return self._list[handle - 1]
|
|
56
|
+
|
|
57
|
+
def items(self) -> Iterator[tuple[int, T]]:
|
|
58
|
+
for i, element in enumerate(self._list):
|
|
59
|
+
if element is not self._empty_slot:
|
|
60
|
+
yield i + 1, element
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
_io_registry = ByHandleRegistry[SizedIO]()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def init_python_io_hooks() -> None:
|
|
67
|
+
@ffi.def_extern()
|
|
68
|
+
def python_file_set_pos(handle: int, offset: int) -> bool:
|
|
69
|
+
_io_registry[handle].f.seek(offset)
|
|
70
|
+
return True
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@ffi.def_extern()
|
|
74
|
+
def python_file_read_into(handle: int, dest: VoidPtr, bytes_to_read: int) -> int:
|
|
75
|
+
bytes_read = _io_registry[handle].f.readinto(ffi.buffer(dest, bytes_to_read))
|
|
76
|
+
if bytes_read is None:
|
|
77
|
+
raise RuntimeError
|
|
78
|
+
return max(bytes_read, 1)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@ffi.def_extern()
|
|
82
|
+
def python_file_get_size(handle: int) -> int:
|
|
83
|
+
return _io_registry[handle].n_bytes
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@ffi.def_extern()
|
|
87
|
+
def python_file_close(handle: int) -> None:
|
|
88
|
+
_io_registry.pop(handle).f.close()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
lib.init_python_platform_utils(
|
|
92
|
+
lib.python_file_set_pos,
|
|
93
|
+
lib.python_file_read_into,
|
|
94
|
+
lib.python_file_get_size,
|
|
95
|
+
lib.python_file_close,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def register_io(f: RawIOBase | BufferedIOBase, n_bytes: int) -> int:
|
|
100
|
+
"""Registers a Python IO object for use with the underlying C library.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
f: IO object to register.
|
|
104
|
+
n_bytes: Size of the IO object's data (in bytes).
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
RuntimeError: When the IO object is not readable.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
New handle assigned to the IO object.
|
|
111
|
+
"""
|
|
112
|
+
if not f.readable:
|
|
113
|
+
msg = "IO object must be readable"
|
|
114
|
+
raise RuntimeError(msg)
|
|
115
|
+
return _io_registry.add(SizedIO(f, n_bytes))
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from enum import IntEnum
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TYPE_CHECKING, NewType
|
|
5
|
+
|
|
6
|
+
from _pyisyntax import ffi, lib
|
|
7
|
+
|
|
8
|
+
from isyntax.lowlevel.io_management import init_python_io_hooks, register_io
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from cffi import FFI
|
|
12
|
+
from typing_extensions import Buffer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Pointer = NewType("Pointer", "FFI.CData")
|
|
16
|
+
ISyntaxPtr = NewType("ISyntaxPtr", Pointer)
|
|
17
|
+
ISyntaxImagePtr = NewType("ISyntaxImagePtr", Pointer)
|
|
18
|
+
ISyntaxLevelPtr = NewType("ISyntaxLevelPtr", Pointer)
|
|
19
|
+
ISyntaxCachePtr = NewType("ISyntaxCachePtr", Pointer)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
LIBISYNTAX_OK = 0
|
|
23
|
+
# Generic error that the user should not expect to recover from.
|
|
24
|
+
LIBISYNTAX_FATAL = 1
|
|
25
|
+
# One of the arguments passed to a function is invalid.
|
|
26
|
+
LIBISYNTAX_INVALID_ARGUMENT = 2
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ISyntaxPixelFormat(IntEnum):
|
|
30
|
+
RGBA = lib.LIBISYNTAX_PIXEL_FORMAT_RGBA
|
|
31
|
+
BGRA = lib.LIBISYNTAX_PIXEL_FORMAT_BGRA
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class LibISyntaxError(Exception):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LibISyntaxFatalError(LibISyntaxError):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class LibISyntaxInvalidArgumentError(LibISyntaxError):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class LibISyntaxUnknownError(LibISyntaxError):
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class NullPointerError(Exception):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def check_error(status: int) -> None:
|
|
55
|
+
if status == LIBISYNTAX_OK:
|
|
56
|
+
return
|
|
57
|
+
if status == LIBISYNTAX_FATAL:
|
|
58
|
+
raise LibISyntaxFatalError
|
|
59
|
+
if status == LIBISYNTAX_INVALID_ARGUMENT:
|
|
60
|
+
raise LibISyntaxInvalidArgumentError
|
|
61
|
+
raise LibISyntaxUnknownError
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def free(ptr: Pointer) -> None:
|
|
65
|
+
lib.free(ptr)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _do_init() -> None:
|
|
69
|
+
check_error(lib.libisyntax_init())
|
|
70
|
+
init_python_io_hooks()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def init() -> None:
|
|
74
|
+
ffi.init_once(_do_init, "libisyntax_init")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def open_from_registered_handle(handle: int, *, is_init_allocators: bool = False) -> ISyntaxPtr:
|
|
78
|
+
init()
|
|
79
|
+
|
|
80
|
+
isyntax = ffi.new("isyntax_t**")
|
|
81
|
+
check_error(lib.libisyntax_open(
|
|
82
|
+
ffi.new("char[]", str(handle).encode("utf-8")),
|
|
83
|
+
is_init_allocators,
|
|
84
|
+
isyntax,
|
|
85
|
+
))
|
|
86
|
+
return isyntax[0]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def open_from_filename(filename: str | Path, *, is_init_allocators: bool = False) -> ISyntaxPtr:
|
|
90
|
+
filename = Path(filename)
|
|
91
|
+
f = filename.open("rb")
|
|
92
|
+
handle = register_io(f, os.fstat(f.fileno()).st_size)
|
|
93
|
+
return open_from_registered_handle(handle, is_init_allocators=is_init_allocators)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def close(isyntax: ISyntaxPtr) -> None:
|
|
97
|
+
# If a null pointer gets through the program will segfault.
|
|
98
|
+
if isyntax == ffi.NULL:
|
|
99
|
+
raise NullPointerError
|
|
100
|
+
lib.libisyntax_close(isyntax)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_tile_width(isyntax: ISyntaxPtr) -> int:
|
|
104
|
+
return lib.libisyntax_get_tile_width(isyntax)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_tile_height(isyntax: ISyntaxPtr) -> int:
|
|
108
|
+
return lib.libisyntax_get_tile_height(isyntax)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_wsi_image_index(isyntax: ISyntaxPtr) -> int:
|
|
112
|
+
return lib.libisyntax_get_wsi_image_index(isyntax)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_image(isyntax: ISyntaxPtr, wsi_image_index: int) -> ISyntaxImagePtr:
|
|
116
|
+
return lib.libisyntax_get_image(isyntax, wsi_image_index)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def image_get_level_count(wsi_image: ISyntaxImagePtr) -> int:
|
|
120
|
+
return lib.libisyntax_image_get_level_count(wsi_image)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def image_get_level(wsi_image: ISyntaxImagePtr, index: int) -> ISyntaxLevelPtr:
|
|
124
|
+
return lib.libisyntax_image_get_level(wsi_image, index)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def level_get_scale(level: ISyntaxLevelPtr) -> int:
|
|
128
|
+
return lib.libisyntax_level_get_scale(level)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def level_get_width_in_tiles(level: ISyntaxLevelPtr) -> int:
|
|
132
|
+
return lib.libisyntax_level_get_width_in_tiles(level)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def level_get_height_in_tiles(level: ISyntaxLevelPtr) -> int:
|
|
136
|
+
return lib.libisyntax_level_get_height_in_tiles(level)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def level_get_width(level: ISyntaxLevelPtr) -> int:
|
|
140
|
+
return lib.libisyntax_level_get_width(level)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def level_get_height(level: ISyntaxLevelPtr) -> int:
|
|
144
|
+
return lib.libisyntax_level_get_height(level)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def level_get_mpp_x(level: ISyntaxLevelPtr) -> float:
|
|
148
|
+
return lib.libisyntax_level_get_mpp_x(level)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def level_get_mpp_y(level: ISyntaxLevelPtr) -> float:
|
|
152
|
+
return lib.libisyntax_level_get_mpp_y(level)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def cache_create(debug_name: str | None, cache_size: int) -> ISyntaxCachePtr:
|
|
156
|
+
isyntax_cache = ffi.new("isyntax_cache_t**")
|
|
157
|
+
if debug_name is None:
|
|
158
|
+
debug_name_or_null = ffi.NULL
|
|
159
|
+
else:
|
|
160
|
+
debug_name_or_null = ffi.new("char[]", debug_name.encode("utf-8"))
|
|
161
|
+
check_error(lib.libisyntax_cache_create(
|
|
162
|
+
debug_name_or_null,
|
|
163
|
+
cache_size,
|
|
164
|
+
isyntax_cache,
|
|
165
|
+
))
|
|
166
|
+
return isyntax_cache[0]
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def cache_inject(isyntax_cache: ISyntaxCachePtr, isyntax: ISyntaxPtr) -> None:
|
|
170
|
+
check_error(lib.libisyntax_cache_inject(isyntax_cache, isyntax))
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def cache_destroy(isyntax_cache: ISyntaxCachePtr) -> None:
|
|
174
|
+
if isyntax_cache == ffi.NULL:
|
|
175
|
+
raise NullPointerError
|
|
176
|
+
lib.libisyntax_cache_destroy(isyntax_cache)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def tile_read(
|
|
180
|
+
isyntax: ISyntaxPtr,
|
|
181
|
+
isyntax_cache: ISyntaxCachePtr,
|
|
182
|
+
level: int,
|
|
183
|
+
tile_x: int,
|
|
184
|
+
tile_y: int,
|
|
185
|
+
pixels_buffer: "Buffer | FFI.buffer",
|
|
186
|
+
pixel_format: ISyntaxPixelFormat,
|
|
187
|
+
) -> None:
|
|
188
|
+
check_error(lib.libisyntax_tile_read(
|
|
189
|
+
isyntax,
|
|
190
|
+
isyntax_cache,
|
|
191
|
+
level,
|
|
192
|
+
tile_x,
|
|
193
|
+
tile_y,
|
|
194
|
+
ffi.from_buffer("uint32_t[]", pixels_buffer, require_writable=True),
|
|
195
|
+
pixel_format,
|
|
196
|
+
))
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def read_region(
|
|
200
|
+
isyntax: ISyntaxPtr,
|
|
201
|
+
isyntax_cache: ISyntaxCachePtr,
|
|
202
|
+
level: int,
|
|
203
|
+
x: int,
|
|
204
|
+
y: int,
|
|
205
|
+
width: int,
|
|
206
|
+
height: int,
|
|
207
|
+
pixels_buffer: "Buffer | FFI.buffer",
|
|
208
|
+
pixel_format: ISyntaxPixelFormat,
|
|
209
|
+
) -> None:
|
|
210
|
+
check_error(lib.libisyntax_read_region(
|
|
211
|
+
isyntax,
|
|
212
|
+
isyntax_cache,
|
|
213
|
+
level,
|
|
214
|
+
x,
|
|
215
|
+
y,
|
|
216
|
+
width,
|
|
217
|
+
height,
|
|
218
|
+
ffi.from_buffer("uint32_t[]", pixels_buffer, require_writable=True),
|
|
219
|
+
pixel_format,
|
|
220
|
+
))
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def read_label_image_jpeg(isyntax: ISyntaxPtr) -> memoryview:
|
|
224
|
+
jpeg_buffer_ptr = ffi.new("uint8_t**")
|
|
225
|
+
jpeg_size_ptr = ffi.new("uint32_t*")
|
|
226
|
+
check_error(lib.libisyntax_read_label_image_jpeg(
|
|
227
|
+
isyntax,
|
|
228
|
+
jpeg_buffer_ptr,
|
|
229
|
+
jpeg_size_ptr,
|
|
230
|
+
))
|
|
231
|
+
jpeg_buffer = ffi.gc(jpeg_buffer_ptr[0], free)
|
|
232
|
+
jpeg_size = jpeg_size_ptr[0]
|
|
233
|
+
return memoryview(ffi.buffer(jpeg_buffer, jpeg_size))
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def read_macro_image_jpeg(isyntax: ISyntaxPtr) -> memoryview:
|
|
237
|
+
jpeg_buffer_ptr = ffi.new("uint8_t**")
|
|
238
|
+
jpeg_size_ptr = ffi.new("uint32_t*")
|
|
239
|
+
check_error(lib.libisyntax_read_macro_image_jpeg(
|
|
240
|
+
isyntax,
|
|
241
|
+
jpeg_buffer_ptr,
|
|
242
|
+
jpeg_size_ptr,
|
|
243
|
+
))
|
|
244
|
+
jpeg_buffer = ffi.gc(jpeg_buffer_ptr[0], free)
|
|
245
|
+
jpeg_size = jpeg_size_ptr[0]
|
|
246
|
+
return memoryview(ffi.buffer(jpeg_buffer, jpeg_size))
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def read_icc_profile(isyntax: ISyntaxPtr, image: ISyntaxImagePtr) -> memoryview:
|
|
250
|
+
profile_buffer_ptr = ffi.new("uint8_t**")
|
|
251
|
+
profile_size_ptr = ffi.new("uint32_t*")
|
|
252
|
+
check_error(lib.libisyntax_read_icc_profile(
|
|
253
|
+
isyntax,
|
|
254
|
+
image,
|
|
255
|
+
profile_buffer_ptr,
|
|
256
|
+
profile_size_ptr,
|
|
257
|
+
))
|
|
258
|
+
profile_buffer = ffi.gc(profile_buffer_ptr[0], free)
|
|
259
|
+
profile_size = profile_size_ptr[0]
|
|
260
|
+
return memoryview(ffi.buffer(profile_buffer, profile_size))
|