async-tiff 0.5.0b2__cp311-abi3-musllinux_1_2_x86_64.whl
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.
- async_tiff/__init__.py +31 -0
- async_tiff/_array.pyi +45 -0
- async_tiff/_async_tiff.abi3.so +0 -0
- async_tiff/_async_tiff.pyi +19 -0
- async_tiff/_colormap.pyi +14 -0
- async_tiff/_decoder.pyi +19 -0
- async_tiff/_decoder_runtime.py +21 -0
- async_tiff/_geo.pyi +102 -0
- async_tiff/_ifd.pyi +150 -0
- async_tiff/_input.py +8 -0
- async_tiff/_thread_pool.pyi +4 -0
- async_tiff/_tiff.pyi +78 -0
- async_tiff/_tile.pyi +58 -0
- async_tiff/enums.py +64 -0
- async_tiff/py.typed +0 -0
- async_tiff-0.5.0b2.dist-info/METADATA +103 -0
- async_tiff-0.5.0b2.dist-info/RECORD +19 -0
- async_tiff-0.5.0b2.dist-info/WHEEL +4 -0
- async_tiff.libs/libgcc_s-f685abf1.so.1 +0 -0
async_tiff/__init__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from . import enums, store
|
|
2
|
+
from ._async_tiff import (
|
|
3
|
+
TIFF,
|
|
4
|
+
Array,
|
|
5
|
+
Colormap,
|
|
6
|
+
DecoderRegistry,
|
|
7
|
+
GeoKeyDirectory,
|
|
8
|
+
ImageFileDirectory,
|
|
9
|
+
ThreadPool,
|
|
10
|
+
Tile,
|
|
11
|
+
___version, # noqa: F403 # pyright:ignore[reportAttributeAccessIssue]
|
|
12
|
+
)
|
|
13
|
+
from ._decoder_runtime import Decoder
|
|
14
|
+
from ._input import ObspecInput
|
|
15
|
+
|
|
16
|
+
__version__: str = ___version()
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"enums",
|
|
20
|
+
"store",
|
|
21
|
+
"Array",
|
|
22
|
+
"Colormap",
|
|
23
|
+
"Decoder",
|
|
24
|
+
"DecoderRegistry",
|
|
25
|
+
"GeoKeyDirectory",
|
|
26
|
+
"ImageFileDirectory",
|
|
27
|
+
"ThreadPool",
|
|
28
|
+
"TIFF",
|
|
29
|
+
"ObspecInput",
|
|
30
|
+
"Tile",
|
|
31
|
+
]
|
async_tiff/_array.pyi
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
if sys.version_info >= (3, 12):
|
|
4
|
+
from collections.abc import Buffer
|
|
5
|
+
else:
|
|
6
|
+
from typing_extensions import Buffer
|
|
7
|
+
|
|
8
|
+
class Array(Buffer):
|
|
9
|
+
"""A 3D array that implements Python's buffer protocol.
|
|
10
|
+
|
|
11
|
+
This allows zero-copy interoperability with numpy via `np.asarray(arr)`.
|
|
12
|
+
The array is immutable and exposes a read-only buffer.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import numpy as np
|
|
18
|
+
from async_tiff import Array
|
|
19
|
+
|
|
20
|
+
# Create from raw bytes
|
|
21
|
+
data = bytes([1, 2, 3, 4, 5, 6])
|
|
22
|
+
arr = Array(data, shape=(1, 2, 3), format="<B") # 1x2x3 uint8 array
|
|
23
|
+
|
|
24
|
+
# Convert to numpy (zero-copy view)
|
|
25
|
+
np_arr = np.asarray(arr)
|
|
26
|
+
assert np_arr.shape == (1, 2, 3)
|
|
27
|
+
assert np_arr.dtype == np.uint8
|
|
28
|
+
```
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
# This is intended only for tests
|
|
32
|
+
# def __init__(
|
|
33
|
+
# self, data: Buffer, shape: tuple[int, int, int], format: str
|
|
34
|
+
# ) -> None: ...
|
|
35
|
+
def __buffer__(self, flags: int) -> memoryview[int]: ...
|
|
36
|
+
@property
|
|
37
|
+
def shape(self) -> tuple[int, int, int]:
|
|
38
|
+
"""The shape of the array.
|
|
39
|
+
|
|
40
|
+
The interpretation depends on the PlanarConfiguration:
|
|
41
|
+
|
|
42
|
+
- PlanarConfiguration=1 (chunky): (height, width, bands)
|
|
43
|
+
- PlanarConfiguration=2 (planar): (bands, height, width)
|
|
44
|
+
"""
|
|
45
|
+
...
|
|
Binary file
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from ._array import Array
|
|
2
|
+
from ._colormap import Colormap
|
|
3
|
+
from ._decoder import DecoderRegistry
|
|
4
|
+
from ._geo import GeoKeyDirectory
|
|
5
|
+
from ._ifd import ImageFileDirectory
|
|
6
|
+
from ._thread_pool import ThreadPool
|
|
7
|
+
from ._tiff import TIFF
|
|
8
|
+
from ._tile import Tile
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Array",
|
|
12
|
+
"Colormap",
|
|
13
|
+
"DecoderRegistry",
|
|
14
|
+
"GeoKeyDirectory",
|
|
15
|
+
"ImageFileDirectory",
|
|
16
|
+
"ThreadPool",
|
|
17
|
+
"TIFF",
|
|
18
|
+
"Tile",
|
|
19
|
+
]
|
async_tiff/_colormap.pyi
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
if sys.version_info >= (3, 12):
|
|
4
|
+
from collections.abc import Buffer
|
|
5
|
+
else:
|
|
6
|
+
from typing_extensions import Buffer
|
|
7
|
+
|
|
8
|
+
class Colormap(Buffer):
|
|
9
|
+
"""A 1D array of u16 values representing a TIFF colormap.
|
|
10
|
+
|
|
11
|
+
Implements Python's buffer protocol for zero-copy access via `np.asarray()`.
|
|
12
|
+
"""
|
|
13
|
+
def __buffer__(self, flags: int) -> memoryview[int]: ...
|
|
14
|
+
def __len__(self) -> int: ...
|
async_tiff/_decoder.pyi
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from ._decoder_runtime import Decoder
|
|
2
|
+
from .enums import Compression
|
|
3
|
+
|
|
4
|
+
class DecoderRegistry:
|
|
5
|
+
"""A registry holding multiple decoder methods."""
|
|
6
|
+
def __init__(
|
|
7
|
+
self, custom_decoders: dict[Compression | int, Decoder] | None = None
|
|
8
|
+
) -> None:
|
|
9
|
+
"""Construct a new decoder registry.
|
|
10
|
+
|
|
11
|
+
By default, pure-Rust decoders will be used for any recognized and supported
|
|
12
|
+
compression types. Only the supplied decoders will override Rust-native
|
|
13
|
+
decoders.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
custom_decoders: any custom decoder methods to use. This will be applied
|
|
17
|
+
_after_ (and override) any default provided Rust decoders. Defaults to
|
|
18
|
+
None.
|
|
19
|
+
"""
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from typing import TYPE_CHECKING, Protocol
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
if sys.version_info >= (3, 12):
|
|
8
|
+
from collections.abc import Buffer
|
|
9
|
+
else:
|
|
10
|
+
from typing_extensions import Buffer
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Decoder(Protocol):
|
|
14
|
+
"""A custom Python-provided decompression algorithm."""
|
|
15
|
+
|
|
16
|
+
# In the future, we could pass in photometric interpretation and jpeg tables as
|
|
17
|
+
# well.
|
|
18
|
+
@staticmethod
|
|
19
|
+
def __call__(buffer: Buffer) -> Buffer:
|
|
20
|
+
"""A callback to decode compressed data."""
|
|
21
|
+
...
|
async_tiff/_geo.pyi
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
class GeoKeyDirectory:
|
|
5
|
+
def keys(self) -> list[str]:
|
|
6
|
+
"""A list of string keys representing the GeoKey fields."""
|
|
7
|
+
def __eq__(self, value: object) -> bool: ...
|
|
8
|
+
def __iter__(self) -> Iterable[str]:
|
|
9
|
+
"""An iterable of string keys representing the GeoKey fields."""
|
|
10
|
+
def __getitem__(self, key: str) -> Any:
|
|
11
|
+
"""Access GeoKey fields by string key."""
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def model_type(self) -> int | None: ...
|
|
15
|
+
@property
|
|
16
|
+
def raster_type(self) -> int | None: ...
|
|
17
|
+
@property
|
|
18
|
+
def citation(self) -> str | None: ...
|
|
19
|
+
@property
|
|
20
|
+
def geographic_type(self) -> int | None: ...
|
|
21
|
+
@property
|
|
22
|
+
def geog_citation(self) -> str | None: ...
|
|
23
|
+
@property
|
|
24
|
+
def geog_geodetic_datum(self) -> int | None: ...
|
|
25
|
+
@property
|
|
26
|
+
def geog_prime_meridian(self) -> int | None: ...
|
|
27
|
+
@property
|
|
28
|
+
def geog_linear_units(self) -> int | None: ...
|
|
29
|
+
@property
|
|
30
|
+
def geog_linear_unit_size(self) -> float | None: ...
|
|
31
|
+
@property
|
|
32
|
+
def geog_angular_units(self) -> int | None: ...
|
|
33
|
+
@property
|
|
34
|
+
def geog_angular_unit_size(self) -> float | None: ...
|
|
35
|
+
@property
|
|
36
|
+
def geog_ellipsoid(self) -> int | None: ...
|
|
37
|
+
@property
|
|
38
|
+
def geog_semi_major_axis(self) -> float | None: ...
|
|
39
|
+
@property
|
|
40
|
+
def geog_semi_minor_axis(self) -> float | None: ...
|
|
41
|
+
@property
|
|
42
|
+
def geog_inv_flattening(self) -> float | None: ...
|
|
43
|
+
@property
|
|
44
|
+
def geog_azimuth_units(self) -> int | None: ...
|
|
45
|
+
@property
|
|
46
|
+
def geog_prime_meridian_long(self) -> float | None: ...
|
|
47
|
+
@property
|
|
48
|
+
def projected_type(self) -> int | None: ...
|
|
49
|
+
@property
|
|
50
|
+
def proj_citation(self) -> str | None: ...
|
|
51
|
+
@property
|
|
52
|
+
def projection(self) -> int | None: ...
|
|
53
|
+
@property
|
|
54
|
+
def proj_coord_trans(self) -> int | None: ...
|
|
55
|
+
@property
|
|
56
|
+
def proj_linear_units(self) -> int | None: ...
|
|
57
|
+
@property
|
|
58
|
+
def proj_linear_unit_size(self) -> float | None: ...
|
|
59
|
+
@property
|
|
60
|
+
def proj_std_parallel1(self) -> float | None: ...
|
|
61
|
+
@property
|
|
62
|
+
def proj_std_parallel2(self) -> float | None: ...
|
|
63
|
+
@property
|
|
64
|
+
def proj_nat_origin_long(self) -> float | None: ...
|
|
65
|
+
@property
|
|
66
|
+
def proj_nat_origin_lat(self) -> float | None: ...
|
|
67
|
+
@property
|
|
68
|
+
def proj_false_easting(self) -> float | None: ...
|
|
69
|
+
@property
|
|
70
|
+
def proj_false_northing(self) -> float | None: ...
|
|
71
|
+
@property
|
|
72
|
+
def proj_false_origin_long(self) -> float | None: ...
|
|
73
|
+
@property
|
|
74
|
+
def proj_false_origin_lat(self) -> float | None: ...
|
|
75
|
+
@property
|
|
76
|
+
def proj_false_origin_easting(self) -> float | None: ...
|
|
77
|
+
@property
|
|
78
|
+
def proj_false_origin_northing(self) -> float | None: ...
|
|
79
|
+
@property
|
|
80
|
+
def proj_center_long(self) -> float | None: ...
|
|
81
|
+
@property
|
|
82
|
+
def proj_center_lat(self) -> float | None: ...
|
|
83
|
+
@property
|
|
84
|
+
def proj_center_easting(self) -> float | None: ...
|
|
85
|
+
@property
|
|
86
|
+
def proj_center_northing(self) -> float | None: ...
|
|
87
|
+
@property
|
|
88
|
+
def proj_scale_at_nat_origin(self) -> float | None: ...
|
|
89
|
+
@property
|
|
90
|
+
def proj_scale_at_center(self) -> float | None: ...
|
|
91
|
+
@property
|
|
92
|
+
def proj_azimuth_angle(self) -> float | None: ...
|
|
93
|
+
@property
|
|
94
|
+
def proj_straight_vert_pole_long(self) -> float | None: ...
|
|
95
|
+
@property
|
|
96
|
+
def vertical(self) -> int | None: ...
|
|
97
|
+
@property
|
|
98
|
+
def vertical_citation(self) -> str | None: ...
|
|
99
|
+
@property
|
|
100
|
+
def vertical_datum(self) -> int | None: ...
|
|
101
|
+
@property
|
|
102
|
+
def vertical_units(self) -> int | None: ...
|
async_tiff/_ifd.pyi
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
from collections.abc import Iterable, Sequence
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from ._colormap import Colormap
|
|
5
|
+
from ._geo import GeoKeyDirectory
|
|
6
|
+
from ._tile import Tile
|
|
7
|
+
from .enums import (
|
|
8
|
+
Compression,
|
|
9
|
+
PhotometricInterpretation,
|
|
10
|
+
PlanarConfiguration,
|
|
11
|
+
Predictor,
|
|
12
|
+
ResolutionUnit,
|
|
13
|
+
SampleFormat,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
Value = int | float | str | tuple[int, int] | list[Value]
|
|
17
|
+
|
|
18
|
+
class ImageFileDirectory:
|
|
19
|
+
def keys(self) -> list[str]:
|
|
20
|
+
"""A list of string keys representing the IFD fields."""
|
|
21
|
+
def __eq__(self, value: object) -> bool: ...
|
|
22
|
+
def __iter__(self) -> Iterable[str]:
|
|
23
|
+
"""An iterable of string keys representing the IFD fields."""
|
|
24
|
+
def __getitem__(self, key: str) -> Any:
|
|
25
|
+
"""Access IFD fields by string key."""
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def new_subfile_type(self) -> int | None: ...
|
|
29
|
+
@property
|
|
30
|
+
def image_width(self) -> int:
|
|
31
|
+
"""The number of columns in the image, i.e., the number of pixels per row."""
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def image_height(self) -> int:
|
|
35
|
+
"""The number of rows of pixels in the image."""
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def bits_per_sample(self) -> list[int]: ...
|
|
39
|
+
@property
|
|
40
|
+
def compression(self) -> Compression | int:
|
|
41
|
+
"""Access the compression tag.
|
|
42
|
+
|
|
43
|
+
An `int` will be returned if the compression is not one of the values in
|
|
44
|
+
`Compression`.
|
|
45
|
+
"""
|
|
46
|
+
@property
|
|
47
|
+
def photometric_interpretation(self) -> PhotometricInterpretation: ...
|
|
48
|
+
@property
|
|
49
|
+
def document_name(self) -> str | None: ...
|
|
50
|
+
@property
|
|
51
|
+
def image_description(self) -> str | None: ...
|
|
52
|
+
@property
|
|
53
|
+
def strip_offsets(self) -> list[int] | None: ...
|
|
54
|
+
@property
|
|
55
|
+
def orientation(self) -> int | None: ...
|
|
56
|
+
@property
|
|
57
|
+
def samples_per_pixel(self) -> int:
|
|
58
|
+
"""
|
|
59
|
+
The number of components per pixel.
|
|
60
|
+
|
|
61
|
+
SamplesPerPixel is usually 1 for bilevel, grayscale, and palette-color images.
|
|
62
|
+
SamplesPerPixel is usually 3 for RGB images. If this value is higher,
|
|
63
|
+
ExtraSamples should give an indication of the meaning of the additional
|
|
64
|
+
channels.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def rows_per_strip(self) -> int | None: ...
|
|
69
|
+
@property
|
|
70
|
+
def strip_byte_counts(self) -> int | None: ...
|
|
71
|
+
@property
|
|
72
|
+
def min_sample_value(self) -> int | None: ...
|
|
73
|
+
@property
|
|
74
|
+
def max_sample_value(self) -> int | None: ...
|
|
75
|
+
@property
|
|
76
|
+
def x_resolution(self) -> float | None:
|
|
77
|
+
"""The number of pixels per ResolutionUnit in the ImageWidth direction."""
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def y_resolution(self) -> float | None:
|
|
81
|
+
"""The number of pixels per ResolutionUnit in the ImageLength direction."""
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def planar_configuration(self) -> PlanarConfiguration: ...
|
|
85
|
+
@property
|
|
86
|
+
def resolution_unit(self) -> ResolutionUnit | None: ...
|
|
87
|
+
@property
|
|
88
|
+
def software(self) -> str | None: ...
|
|
89
|
+
@property
|
|
90
|
+
def date_time(self) -> str | None: ...
|
|
91
|
+
@property
|
|
92
|
+
def artist(self) -> str | None: ...
|
|
93
|
+
@property
|
|
94
|
+
def host_computer(self) -> str | None: ...
|
|
95
|
+
@property
|
|
96
|
+
def predictor(self) -> Predictor | None: ...
|
|
97
|
+
@property
|
|
98
|
+
def tile_width(self) -> int | None: ...
|
|
99
|
+
@property
|
|
100
|
+
def tile_height(self) -> int | None: ...
|
|
101
|
+
@property
|
|
102
|
+
def tile_offsets(self) -> list[int] | None: ...
|
|
103
|
+
@property
|
|
104
|
+
def tile_byte_counts(self) -> list[int] | None: ...
|
|
105
|
+
@property
|
|
106
|
+
def extra_samples(self) -> list[int] | None: ...
|
|
107
|
+
@property
|
|
108
|
+
def sample_format(self) -> list[SampleFormat]: ...
|
|
109
|
+
@property
|
|
110
|
+
def jpeg_tables(self) -> bytes | None: ...
|
|
111
|
+
@property
|
|
112
|
+
def copyright(self) -> str | None: ...
|
|
113
|
+
@property
|
|
114
|
+
def geo_key_directory(self) -> GeoKeyDirectory | None: ...
|
|
115
|
+
@property
|
|
116
|
+
def model_pixel_scale(self) -> list[float] | None: ...
|
|
117
|
+
@property
|
|
118
|
+
def model_tiepoint(self) -> list[float] | None: ...
|
|
119
|
+
@property
|
|
120
|
+
def model_transformation(self) -> list[float] | None: ...
|
|
121
|
+
@property
|
|
122
|
+
def gdal_nodata(self) -> str | None: ...
|
|
123
|
+
@property
|
|
124
|
+
def gdal_metadata(self) -> str | None: ...
|
|
125
|
+
@property
|
|
126
|
+
def other_tags(self) -> dict[int, Value]: ...
|
|
127
|
+
@property
|
|
128
|
+
def colormap(self) -> Colormap | None:
|
|
129
|
+
"""The colormap for palette-color images."""
|
|
130
|
+
...
|
|
131
|
+
async def fetch_tile(self, x: int, y: int) -> Tile:
|
|
132
|
+
"""Fetch a single tile.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
x: The column index within the ifd to read from.
|
|
136
|
+
y: The row index within the ifd to read from.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Tile response.
|
|
140
|
+
"""
|
|
141
|
+
async def fetch_tiles(self, x: Sequence[int], y: Sequence[int]) -> list[Tile]:
|
|
142
|
+
"""Fetch multiple tiles concurrently.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
x: The column indexes within the ifd to read from.
|
|
146
|
+
y: The row indexes within the ifd to read from.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Tile responses.
|
|
150
|
+
"""
|
async_tiff/_input.py
ADDED
async_tiff/_tiff.pyi
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from typing import Sequence
|
|
2
|
+
|
|
3
|
+
from ._ifd import ImageFileDirectory
|
|
4
|
+
from ._input import ObspecInput
|
|
5
|
+
from ._tile import Tile
|
|
6
|
+
from .enums import Endianness
|
|
7
|
+
from .store import ObjectStore
|
|
8
|
+
|
|
9
|
+
class TIFF:
|
|
10
|
+
@classmethod
|
|
11
|
+
async def open(
|
|
12
|
+
cls,
|
|
13
|
+
path: str,
|
|
14
|
+
*,
|
|
15
|
+
store: ObjectStore | ObspecInput,
|
|
16
|
+
prefetch: int = 32768,
|
|
17
|
+
multiplier: int | float = 2.0,
|
|
18
|
+
) -> TIFF:
|
|
19
|
+
"""Open a new TIFF.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
path: The path within the store to read from.
|
|
23
|
+
store: The backend to use for data fetching.
|
|
24
|
+
prefetch: The number of initial bytes to read up front.
|
|
25
|
+
multiplier: The multiplier to use for readahead size growth. Must be
|
|
26
|
+
greater than 1.0. For example, for a value of `2.0`, the first metadata
|
|
27
|
+
read will be of size `prefetch`, and then the next read will be of size
|
|
28
|
+
`prefetch * 2`.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
A TIFF instance.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def endianness(self) -> Endianness:
|
|
36
|
+
"""The endianness of this TIFF file."""
|
|
37
|
+
|
|
38
|
+
def ifd(self, index: int) -> ImageFileDirectory:
|
|
39
|
+
"""Access a specific IFD by index.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
index: The IFD index to access.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
The requested IFD.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def ifds(self) -> list[ImageFileDirectory]:
|
|
50
|
+
"""Access the underlying IFDs of this TIFF.
|
|
51
|
+
|
|
52
|
+
Each ImageFileDirectory (IFD) represents one of the internal "sub images" of
|
|
53
|
+
this file.
|
|
54
|
+
"""
|
|
55
|
+
async def fetch_tile(self, x: int, y: int, z: int) -> Tile:
|
|
56
|
+
"""Fetch a single tile.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
x: The column index within the ifd to read from.
|
|
60
|
+
y: The row index within the ifd to read from.
|
|
61
|
+
z: The IFD index to read from.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Tile response.
|
|
65
|
+
"""
|
|
66
|
+
async def fetch_tiles(
|
|
67
|
+
self, x: Sequence[int], y: Sequence[int], z: int
|
|
68
|
+
) -> list[Tile]:
|
|
69
|
+
"""Fetch multiple tiles concurrently.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
x: The column indexes within the ifd to read from.
|
|
73
|
+
y: The row indexes within the ifd to read from.
|
|
74
|
+
z: The IFD index to read from.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Tile responses.
|
|
78
|
+
"""
|
async_tiff/_tile.pyi
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from collections.abc import Buffer
|
|
2
|
+
|
|
3
|
+
from ._array import Array
|
|
4
|
+
from ._decoder import DecoderRegistry
|
|
5
|
+
from ._thread_pool import ThreadPool
|
|
6
|
+
from .enums import Compression
|
|
7
|
+
|
|
8
|
+
class Tile:
|
|
9
|
+
"""A representation of a TIFF image tile."""
|
|
10
|
+
@property
|
|
11
|
+
def x(self) -> int:
|
|
12
|
+
"""The column index this tile represents."""
|
|
13
|
+
@property
|
|
14
|
+
def y(self) -> int:
|
|
15
|
+
"""The row index this tile represents."""
|
|
16
|
+
@property
|
|
17
|
+
def compressed_bytes(self) -> Buffer:
|
|
18
|
+
"""The compressed bytes underlying this tile."""
|
|
19
|
+
@property
|
|
20
|
+
def compression_method(self) -> Compression | int:
|
|
21
|
+
"""The compression method used by this tile."""
|
|
22
|
+
def decode_sync(
|
|
23
|
+
self,
|
|
24
|
+
*,
|
|
25
|
+
decoder_registry: DecoderRegistry | None = None,
|
|
26
|
+
) -> Array:
|
|
27
|
+
"""Decode this tile's data.
|
|
28
|
+
|
|
29
|
+
**Note**: This is a blocking function and will perform the tile decompression on
|
|
30
|
+
the current thread. Prefer using the asynchronous `decode` method, which will
|
|
31
|
+
offload decompression to a thread pool.
|
|
32
|
+
|
|
33
|
+
Keyword Args:
|
|
34
|
+
decoder_registry: the decoders to use for decompression. Defaults to None, in which case a default decoder registry is used.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Decoded tile data as an Array instance.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
async def decode(
|
|
41
|
+
self,
|
|
42
|
+
*,
|
|
43
|
+
decoder_registry: DecoderRegistry | None = None,
|
|
44
|
+
pool: ThreadPool | None = None,
|
|
45
|
+
) -> Array:
|
|
46
|
+
"""Decode this tile's data.
|
|
47
|
+
|
|
48
|
+
This is an asynchronous function that will offload the tile decompression to a
|
|
49
|
+
thread pool.
|
|
50
|
+
|
|
51
|
+
Keyword Args:
|
|
52
|
+
decoder_registry: the decoders to use for decompression. Defaults to None, in which case a default decoder registry is used.
|
|
53
|
+
pool: the thread pool on which to run decompression. Defaults to None, in
|
|
54
|
+
which case, a default thread pool is used.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Decoded tile data as an Array instance.
|
|
58
|
+
"""
|
async_tiff/enums.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from enum import IntEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Compression(IntEnum):
|
|
5
|
+
"""
|
|
6
|
+
See [TIFF compression
|
|
7
|
+
tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html) for
|
|
8
|
+
reference.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
Uncompressed = 1
|
|
12
|
+
Huffman = 2
|
|
13
|
+
Fax3 = 3
|
|
14
|
+
Fax4 = 4
|
|
15
|
+
LZW = 5
|
|
16
|
+
JPEG = 6
|
|
17
|
+
# // "Extended JPEG" or "new JPEG" style
|
|
18
|
+
ModernJPEG = 7
|
|
19
|
+
Deflate = 8
|
|
20
|
+
OldDeflate = 0x80B2
|
|
21
|
+
PackBits = 0x8005
|
|
22
|
+
WebP = 50001
|
|
23
|
+
JPEG2k = 34712
|
|
24
|
+
ZSTD = 0xC350
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Endianness(IntEnum):
|
|
28
|
+
LittleEndian = 0
|
|
29
|
+
BigEndian = 1
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class PhotometricInterpretation(IntEnum):
|
|
33
|
+
WhiteIsZero = 0
|
|
34
|
+
BlackIsZero = 1
|
|
35
|
+
RGB = 2
|
|
36
|
+
RGBPalette = 3
|
|
37
|
+
TransparencyMask = 4
|
|
38
|
+
CMYK = 5
|
|
39
|
+
YCbCr = 6
|
|
40
|
+
CIELab = 8
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class PlanarConfiguration(IntEnum):
|
|
44
|
+
Chunky = 1
|
|
45
|
+
Planar = 2
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Predictor(IntEnum):
|
|
49
|
+
Unknown = 1
|
|
50
|
+
Horizontal = 2
|
|
51
|
+
FloatingPoint = 3
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ResolutionUnit(IntEnum):
|
|
55
|
+
Unknown = 1
|
|
56
|
+
Inch = 2
|
|
57
|
+
Centimeter = 3
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class SampleFormat(IntEnum):
|
|
61
|
+
Uint = 1
|
|
62
|
+
Int = 2
|
|
63
|
+
Float = 3
|
|
64
|
+
Void = 4
|
async_tiff/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: async-tiff
|
|
3
|
+
Version: 0.5.0b2
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Requires-Dist: obspec>=0.1.0
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
10
|
+
|
|
11
|
+
# async-tiff
|
|
12
|
+
|
|
13
|
+
[![PyPI][pypi_badge]][pypi_link]
|
|
14
|
+
|
|
15
|
+
[pypi_badge]: https://badge.fury.io/py/async-tiff.svg
|
|
16
|
+
[pypi_link]: https://pypi.org/project/async-tiff/
|
|
17
|
+
|
|
18
|
+
Fast, low-level async TIFF reader for Python.
|
|
19
|
+
|
|
20
|
+
This documentation is for the Python bindings. [Refer here for the Rust crate documentation](https://docs.rs/async-tiff).
|
|
21
|
+
|
|
22
|
+
For a higher-level API to read GeoTIFF files, visit [`async-geotiff`][async-geotiff].
|
|
23
|
+
|
|
24
|
+
[async-geotiff]: https://developmentseed.org/async-geotiff
|
|
25
|
+
|
|
26
|
+
## Examples
|
|
27
|
+
|
|
28
|
+
### Reading NAIP
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
from async_tiff import TIFF
|
|
32
|
+
from async_tiff.store import S3Store
|
|
33
|
+
|
|
34
|
+
# You'll also need to provide credentials to access a requester pays bucket
|
|
35
|
+
store = S3Store("naip-visualization", region="us-west-2", request_payer=True)
|
|
36
|
+
path = "ny/2022/60cm/rgb/40073/m_4007307_sw_18_060_20220803.tif"
|
|
37
|
+
|
|
38
|
+
tiff = await TIFF.open(path, store=store)
|
|
39
|
+
primary_ifd = tiff.ifds[0]
|
|
40
|
+
|
|
41
|
+
primary_ifd.geo_key_directory.citation
|
|
42
|
+
# 'NAD83 / UTM zone 18N'
|
|
43
|
+
|
|
44
|
+
primary_ifd.geo_key_directory.projected_type
|
|
45
|
+
# 26918
|
|
46
|
+
# (EPSG code)
|
|
47
|
+
|
|
48
|
+
primary_ifd.sample_format
|
|
49
|
+
# [<SampleFormat.Uint: 1>, <SampleFormat.Uint: 1>, <SampleFormat.Uint: 1>]
|
|
50
|
+
|
|
51
|
+
primary_ifd.bits_per_sample
|
|
52
|
+
# [8, 8, 8]
|
|
53
|
+
|
|
54
|
+
tile = await tiff.fetch_tile(0, 0, 4)
|
|
55
|
+
array = await tile.decode()
|
|
56
|
+
|
|
57
|
+
# Use rasterio and matplotlib for visualization
|
|
58
|
+
import numpy as np
|
|
59
|
+
from rasterio.plot import reshape_as_raster, show
|
|
60
|
+
|
|
61
|
+
# Zero-copy conversion of the rust array into a numpy array
|
|
62
|
+
np_array = np.asarray(array)
|
|
63
|
+
|
|
64
|
+
# Then we need to reshape the "image" axes into "raster" axes
|
|
65
|
+
# https://rasterio.readthedocs.io/en/stable/topics/image_processing.html
|
|
66
|
+
show(reshape_as_raster(np_array), adjust=True)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+

|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
### Reading Sentinel 2 L2A
|
|
73
|
+
|
|
74
|
+
```py
|
|
75
|
+
import numpy as np
|
|
76
|
+
from async_tiff import TIFF
|
|
77
|
+
from async_tiff.store import S3Store
|
|
78
|
+
|
|
79
|
+
store = S3Store("sentinel-cogs", region="us-west-2", skip_signature=True)
|
|
80
|
+
path = "sentinel-s2-l2a-cogs/12/S/UF/2022/6/S2B_12SUF_20220609_0_L2A/B04.tif"
|
|
81
|
+
|
|
82
|
+
tiff = await TIFF.open(path, store=store)
|
|
83
|
+
primary_ifd = tiff.ifds[0]
|
|
84
|
+
# Text readable citation
|
|
85
|
+
primary_ifd.geo_key_directory.citation
|
|
86
|
+
# EPSG code
|
|
87
|
+
primary_ifd.geo_key_directory.projected_type
|
|
88
|
+
|
|
89
|
+
primary_ifd.sample_format[0]
|
|
90
|
+
# <SampleFormat.Uint: 1>
|
|
91
|
+
primary_ifd.bits_per_sample[0]
|
|
92
|
+
# 16
|
|
93
|
+
|
|
94
|
+
tile = await tiff.fetch_tile(0, 0, 0)
|
|
95
|
+
array = await tile.decode()
|
|
96
|
+
|
|
97
|
+
# Zero-copy conversion of the rust array into a numpy array
|
|
98
|
+
np_array = np.asarray(array)
|
|
99
|
+
np_array.shape
|
|
100
|
+
# (1024, 1024, 1)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
async_tiff/__init__.py,sha256=hFou5CyaCeqAHIpsxyfNv28t15tehoFv7iLbgchVW54,579
|
|
2
|
+
async_tiff/_array.pyi,sha256=muitV2BmObMPBF1O1mgo9a89posx7-ubsl4XftG5WO4,1257
|
|
3
|
+
async_tiff/_async_tiff.abi3.so,sha256=d196ogZJQgtl3U20dyE8yL2kPSRpb-bV11txPtzClzU,9519873
|
|
4
|
+
async_tiff/_async_tiff.pyi,sha256=I0Z9ey5GYZYfjJB4ZEyPUzHTsw0GcA5KBrAbTiThMc8,410
|
|
5
|
+
async_tiff/_colormap.pyi,sha256=NrXLqUq3U81bI0zG8OLgQwv_JM0YS-MrVgx-1nD6j3g,402
|
|
6
|
+
async_tiff/_decoder.pyi,sha256=tRHzZbZj1V6lDireFpNQ-f2h2tPFqh2ExYu4L6v0Cqc,709
|
|
7
|
+
async_tiff/_decoder_runtime.py,sha256=N2IC7XTgC1TmGmB7ZXCJbrVVSbn6G85cWgJ6k87XpT8,553
|
|
8
|
+
async_tiff/_geo.pyi,sha256=h71Y9zZ5vYxXaXAR4QAYmBJ0FOhLfXMMJA85p474eTE,3451
|
|
9
|
+
async_tiff/_ifd.pyi,sha256=eKH2FAboDE3B5kK2U45j0RsN5MwD3cUeDVF5WTgu0as,4755
|
|
10
|
+
async_tiff/_input.py,sha256=f6FCam4uyKaWXy-npUQ-nePaXeOsWMdXt2WcyH5fhq0,203
|
|
11
|
+
async_tiff/_thread_pool.pyi,sha256=fbDu9kbo8RNlq7bBBxuuu_6ub20L5cAfXBM-J7Y0PC8,181
|
|
12
|
+
async_tiff/_tiff.pyi,sha256=8MtZcmOPjr72lo8ohNuSwRi9N8YRZBtcjZwwJxEAO0Q,2249
|
|
13
|
+
async_tiff/_tile.pyi,sha256=NBUciMH6nan6qQLt30p9msUiY4BKh4ZmW4r9CsqtYho,1961
|
|
14
|
+
async_tiff/enums.py,sha256=C1XnevNtV_Hp5gTU7pLZRsNAfNx-4nu_f1NpbE1dN0U,1030
|
|
15
|
+
async_tiff/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
async_tiff-0.5.0b2.dist-info/METADATA,sha256=50rXi50a5Ku3aism82VG4fsq0XisM-DsVDykkRdjGWQ,2741
|
|
17
|
+
async_tiff-0.5.0b2.dist-info/WHEEL,sha256=TYQL8ukwJ_ep9kIEzr9pEuvjwsu8H5iF24nWE7u_TGo,107
|
|
18
|
+
async_tiff.libs/libgcc_s-f685abf1.so.1,sha256=rZiUpi-nw8tNSOJeN-vr3RTEAMCMaHf73gTNGVqRdKs,536145
|
|
19
|
+
async_tiff-0.5.0b2.dist-info/RECORD,,
|
|
Binary file
|