Skip to content

Commit 30046a9

Browse files
use uint8 datatype for automatic output image format (#1281)
* use uint8 datatype for automatic output image format * update changelog
1 parent 26a4b45 commit 30046a9

3 files changed

Lines changed: 55 additions & 6 deletions

File tree

CHANGES.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@
55
### Misc
66

77
* update rio-tiler requirement to `>=8.0,<9.0`
8+
* return `UINT8` datatype JPEG/PNG when no output format is specified **breaking change**
89

910
### titiler.core
1011

11-
* add `band_description` attribute to `Point` output model (returned by /point endpoints) **breaking change**
12+
* add `band_description` attribute to `Point` output model (returned by /point endpoints) **breaking change**
1213

1314
### titiler.extensions
1415

1516
* update rio-cogeo requirement to `7.0,<8.0`
1617

1718
### titiler.mosaic
1819

19-
* change Response model for `/point` endpoint **breaking change**
20+
* change Response model for `/point` endpoint **breaking change**
2021

2122
```python
2223
# before

src/titiler/core/tests/test_rendering.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,46 @@ def test_rendering():
115115
assert data_converted[:, 2, 2].tolist() == [100, 100, 100, 50]
116116
# Non-masked from CMAP
117117
assert data_converted[:, 3, 3].tolist() == [255, 255, 255, 255]
118+
119+
120+
def test_rendering_auto_dtype():
121+
"""Test Automatic format selection and dtype"""
122+
data = numpy.ma.zeros((1, 5, 5), dtype="uint16") + 1
123+
data.mask = False
124+
# add a masked value
125+
data.mask[0, 0, 0] = True
126+
im = ImageData(data)
127+
with pytest.warns(InvalidDatatypeWarning):
128+
content, media = render_image(im)
129+
assert media == "image/png"
130+
131+
with MemoryFile(content) as mem:
132+
with mem.open() as dst:
133+
assert dst.count == 2
134+
assert dst.dtypes == ("uint8", "uint8")
135+
136+
# Not Masked
137+
data = numpy.ma.zeros((1, 5, 5), dtype="uint16") + 1
138+
data.mask = False
139+
im = ImageData(data)
140+
with pytest.warns(InvalidDatatypeWarning):
141+
content, media = render_image(im)
142+
assert media == "image/jpeg"
143+
144+
with MemoryFile(content) as mem:
145+
with mem.open() as dst:
146+
assert dst.count == 1
147+
assert dst.dtypes == ("uint8",)
148+
149+
# Full Masked
150+
data = numpy.ma.zeros((1, 5, 5), dtype="uint16") + 1
151+
data.mask = True
152+
im = ImageData(data)
153+
with pytest.warns(InvalidDatatypeWarning):
154+
content, media = render_image(im)
155+
assert media == "image/png"
156+
157+
with MemoryFile(content) as mem:
158+
with mem.open() as dst:
159+
assert dst.count == 2
160+
assert dst.dtypes == ("uint8", "uint8")

src/titiler/core/titiler/core/utils.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,6 @@ def render_image( # noqa: C901
8787
mask != input_range[0], alpha_from_cmap, output_range[0][0]
8888
).astype(data.dtype)
8989

90-
# If output_format is not set, we choose between JPEG and PNG
91-
if not output_format:
92-
output_format = ImageType.jpeg if mask.all() else ImageType.png
93-
9490
# format-specific valid dtypes
9591
format_dtypes = {
9692
ImageType.png: ["uint8", "uint16"],
@@ -100,6 +96,15 @@ def render_image( # noqa: C901
10096
ImageType.jp2: ["uint8", "int16", "uint16"],
10197
}
10298

99+
# If output_format is not set, we choose between JPEG and PNG
100+
if not output_format:
101+
# Check if any alpha value == min datatype value (== Masked)
102+
is_masked = (mask == dtype_ranges[str(mask.dtype)][0]).any()
103+
output_format = ImageType.png if is_masked else ImageType.jpeg
104+
# For automatic format we make sure the output datatype
105+
# will be the same for both JPEG and PNG
106+
format_dtypes[ImageType.png] = ["uint8"]
107+
103108
valid_dtypes = format_dtypes.get(output_format, [])
104109
if valid_dtypes and data.dtype not in valid_dtypes:
105110
warnings.warn(

0 commit comments

Comments
 (0)