diff --git a/python/python/async_tiff/_async_tiff.pyi b/python/python/async_tiff/_async_tiff.pyi index e69de29..b20bd8f 100644 --- a/python/python/async_tiff/_async_tiff.pyi +++ b/python/python/async_tiff/_async_tiff.pyi @@ -0,0 +1,3 @@ +from ._geo import GeoKeyDirectory as GeoKeyDirectory +from ._ifd import ImageFileDirectory as ImageFileDirectory +from ._tiff import TIFF as TIFF diff --git a/python/python/async_tiff/_geo.pyi b/python/python/async_tiff/_geo.pyi new file mode 100644 index 0000000..5f1e906 --- /dev/null +++ b/python/python/async_tiff/_geo.pyi @@ -0,0 +1,91 @@ +class GeoKeyDirectory: + @property + def model_type(self) -> int | None: ... + @property + def raster_type(self) -> int | None: ... + @property + def citation(self) -> str | None: ... + @property + def geographic_type(self) -> int | None: ... + @property + def geog_citation(self) -> str | None: ... + @property + def geog_geodetic_datum(self) -> int | None: ... + @property + def geog_prime_meridian(self) -> int | None: ... + @property + def geog_linear_units(self) -> int | None: ... + @property + def geog_linear_unit_size(self) -> float | None: ... + @property + def geog_angular_units(self) -> int | None: ... + @property + def geog_angular_unit_size(self) -> float | None: ... + @property + def geog_ellipsoid(self) -> int | None: ... + @property + def geog_semi_major_axis(self) -> float | None: ... + @property + def geog_semi_minor_axis(self) -> float | None: ... + @property + def geog_inv_flattening(self) -> float | None: ... + @property + def geog_azimuth_units(self) -> int | None: ... + @property + def geog_prime_meridian_long(self) -> float | None: ... + @property + def projected_type(self) -> int | None: ... + @property + def proj_citation(self) -> str | None: ... + @property + def projection(self) -> int | None: ... + @property + def proj_coord_trans(self) -> int | None: ... + @property + def proj_linear_units(self) -> int | None: ... + @property + def proj_linear_unit_size(self) -> float | None: ... + @property + def proj_std_parallel1(self) -> float | None: ... + @property + def proj_std_parallel2(self) -> float | None: ... + @property + def proj_nat_origin_long(self) -> float | None: ... + @property + def proj_nat_origin_lat(self) -> float | None: ... + @property + def proj_false_easting(self) -> float | None: ... + @property + def proj_false_northing(self) -> float | None: ... + @property + def proj_false_origin_long(self) -> float | None: ... + @property + def proj_false_origin_lat(self) -> float | None: ... + @property + def proj_false_origin_easting(self) -> float | None: ... + @property + def proj_false_origin_northing(self) -> float | None: ... + @property + def proj_center_long(self) -> float | None: ... + @property + def proj_center_lat(self) -> float | None: ... + @property + def proj_center_easting(self) -> float | None: ... + @property + def proj_center_northing(self) -> float | None: ... + @property + def proj_scale_at_nat_origin(self) -> float | None: ... + @property + def proj_scale_at_center(self) -> float | None: ... + @property + def proj_azimuth_angle(self) -> float | None: ... + @property + def proj_straight_vert_pole_long(self) -> float | None: ... + @property + def vertical(self) -> int | None: ... + @property + def vertical_citation(self) -> str | None: ... + @property + def vertical_datum(self) -> int | None: ... + @property + def vertical_units(self) -> int | None: ... diff --git a/python/python/async_tiff/_ifd.pyi b/python/python/async_tiff/_ifd.pyi new file mode 100644 index 0000000..dd62f88 --- /dev/null +++ b/python/python/async_tiff/_ifd.pyi @@ -0,0 +1,98 @@ +from .enums import ( + CompressionMethod, + PhotometricInterpretation, + PlanarConfiguration, + Predictor, + ResolutionUnit, + SampleFormat, +) +from ._geo import GeoKeyDirectory + +class ImageFileDirectory: + @property + def new_subfile_type(self) -> int | None: ... + @property + def image_width(self) -> int: + """The number of columns in the image, i.e., the number of pixels per row.""" + + @property + def image_height(self) -> int: + """The number of rows of pixels in the image.""" + + @property + def bits_per_sample(self) -> list[int]: ... + @property + def compression(self) -> CompressionMethod: ... + @property + def photometric_interpretation(self) -> PhotometricInterpretation: ... + @property + def document_name(self) -> str | None: ... + @property + def image_description(self) -> str | None: ... + @property + def strip_offsets(self) -> list[int] | None: ... + @property + def orientation(self) -> int | None: ... + @property + def samples_per_pixel(self) -> int: + """ + The number of components per pixel. + + SamplesPerPixel is usually 1 for bilevel, grayscale, and palette-color images. + SamplesPerPixel is usually 3 for RGB images. If this value is higher, + ExtraSamples should give an indication of the meaning of the additional + channels. + """ + + @property + def rows_per_strip(self) -> int | None: ... + @property + def strip_byte_counts(self) -> int | None: ... + @property + def min_sample_value(self) -> int | None: ... + @property + def max_sample_value(self) -> int | None: ... + @property + def x_resolution(self) -> float | None: + """The number of pixels per ResolutionUnit in the ImageWidth direction.""" + + @property + def y_resolution(self) -> float | None: + """The number of pixels per ResolutionUnit in the ImageLength direction.""" + + @property + def planar_configuration(self) -> PlanarConfiguration: ... + @property + def resolution_unit(self) -> ResolutionUnit | None: ... + @property + def software(self) -> str | None: ... + @property + def date_time(self) -> str | None: ... + @property + def artist(self) -> str | None: ... + @property + def host_computer(self) -> str | None: ... + @property + def predictor(self) -> Predictor | None: ... + @property + def tile_width(self) -> int: ... + @property + def tile_height(self) -> int: ... + @property + def tile_offsets(self) -> list[int]: ... + @property + def tile_byte_counts(self) -> list[int]: ... + @property + def extra_samples(self) -> bytes | None: ... + @property + def sample_format(self) -> list[SampleFormat]: ... + @property + def jpeg_tables(self) -> bytes | None: ... + @property + def copyright(self) -> str | None: ... + @property + def geo_key_directory(self) -> GeoKeyDirectory | None: ... + @property + def model_pixel_scale(self) -> list[float] | None: ... + @property + def model_tiepoint(self) -> list[float] | None: ... diff --git a/python/python/async_tiff/_tiff.pyi b/python/python/async_tiff/_tiff.pyi new file mode 100644 index 0000000..f534ec3 --- /dev/null +++ b/python/python/async_tiff/_tiff.pyi @@ -0,0 +1,11 @@ +from typing import Any + +from ._ifd import ImageFileDirectory + +class TIFF: + @classmethod + async def open( + cls, path: str, *, store: Any, prefetch: int | None = 16384 + ) -> TIFF: ... + @property + def ifds(self) -> list[ImageFileDirectory]: ... diff --git a/python/src/tiff.rs b/python/src/tiff.rs index 33d4f4b..2391c7d 100644 --- a/python/src/tiff.rs +++ b/python/src/tiff.rs @@ -37,6 +37,7 @@ impl PyTIFF { Ok(cog_reader) } + #[getter] fn ifds(&self) -> Vec { let ifds = self.0.ifds(); ifds.as_ref().iter().map(|ifd| ifd.clone().into()).collect() diff --git a/python/tests/test_cog.py b/python/tests/test_cog.py index 5fd9fb5..577a0bb 100644 --- a/python/tests/test_cog.py +++ b/python/tests/test_cog.py @@ -1,13 +1,19 @@ import async_tiff +from time import time from async_tiff import TIFF from async_tiff.store import S3Store store = S3Store("sentinel-cogs", region="us-west-2", skip_signature=True) path = "sentinel-s2-l2a-cogs/12/S/UF/2022/6/S2B_12SUF_20220609_0_L2A/B04.tif" -# 2 min, 15s tiff = await TIFF.open(path, store=store, prefetch=32768) -ifds = tiff.ifds() + +start = time() +tiff = await TIFF.open(path, store=store, prefetch=32768) +end = time() +end - start + +ifds = tiff.ifds ifd = ifds[0] ifd.compression ifd.tile_height diff --git a/src/cog.rs b/src/cog.rs index d26d4ec..e387f8b 100644 --- a/src/cog.rs +++ b/src/cog.rs @@ -66,11 +66,8 @@ mod test { let cog_reader = COGReader::try_open(Box::new(reader.clone())).await.unwrap(); let ifd = &cog_reader.ifds.as_ref()[1]; - // dbg!(ifd.geotransform()); - dbg!(ifd); let tile = ifd.get_tile(0, 0, Box::new(reader)).await.unwrap(); std::fs::write("img.buf", tile).unwrap(); - // dbg!(tile.len()); } #[ignore = "local file"] diff --git a/src/ifd.rs b/src/ifd.rs index d95fc9e..48fa572 100644 --- a/src/ifd.rs +++ b/src/ifd.rs @@ -47,7 +47,6 @@ impl ImageFileDirectories { next_ifd_offset = ifd.next_ifd_offset(); ifds.push(ifd); } - dbg!(&ifds[0].compression); Ok(Self { ifds }) } @@ -771,7 +770,6 @@ impl ImageFileDirectory { async fn read_tag(cursor: &mut AsyncCursor) -> Result<(Tag, Value)> { let code = cursor.read_u16().await?; let tag_name = Tag::from_u16_exhaustive(code); - // dbg!(&tag_name); let current_cursor_position = cursor.position(); @@ -823,8 +821,6 @@ async fn read_tag_value( // 2a: the value is 5-8 bytes and we're in BigTiff mode. // We don't support bigtiff yet - // dbg!(value_byte_length); - // dbg!(tag_type); // NOTE: we should only be reading value_byte_length when it's 4 bytes or fewer. Right now // we're reading even if it's 8 bytes, but then only using the first 4 bytes of this // buffer.