Repository navigation
Description
The CPU writer in xrspatial/geotiff/_writer.py assumes any array with 3 or more samples per pixel is RGB, and that a 4th band is an alpha channel:
photometric = 2 if samples_per_pixel >= 3 else 1
...
if photometric == 2 and samples_per_pixel > 3:
n_extra = samples_per_pixel - 3
extra_vals = [2] + [0] * (n_extra - 1) # 2 = unassociated alpha
tags.append((TAG_EXTRA_SAMPLES, SHORT, n_extra, extra_vals))That is wrong for scientific multispectral data. A 4-band Landsat or Sentinel raster (R, G, B, NIR) is written with Photometric=RGB and ExtraSamples=[unassociated alpha]. Downstream tools then composite the NIR band as transparency.
The same pattern lives in write_streaming (lines ~1463-1479), so the dask path has the same bug.
A second problem makes it worse: a user who tries to override via extra_tags=[(TAG_EXTRA_SAMPLES, ...)] cannot. The writer adds its auto tag first, and the existing dedup loop drops the user tag because the tag id is already present.
Repro:
import numpy as np
import xarray as xr
from xrspatial.geotiff import to_geotiff
from xrspatial.geotiff._header import (
TAG_PHOTOMETRIC, TAG_EXTRA_SAMPLES, parse_header, parse_ifd,
)
# 4-band NIR raster: red, green, blue, near-infrared
data = np.zeros((64, 64, 4), dtype=np.uint16)
da = xr.DataArray(data, dims=("y", "x", "band"))
to_geotiff(da, "nir.tif")
with open("nir.tif", "rb") as f:
raw = f.read()
hdr = parse_header(raw[:8])
ifd, _ = parse_ifd(raw, hdr.ifd_offset, hdr.bigtiff)
print("Photometric:", ifd.get_value(TAG_PHOTOMETRIC, 1)) # 2 (RGB) -- wrong
print("ExtraSamples:", ifd.get_value(TAG_EXTRA_SAMPLES, 1)) # 2 (alpha) -- wrongProposed fix:
- Add a
photometrickwarg toto_geotiffaccepting'auto'(default),'rgb','rgba','minisblack', or an explicit integer. In'auto'mode default to MinIsBlack for any band count, with ExtraSamples=[0]*N_extra (unspecified). RGB becomes opt-in viaphotometric='rgb'. - Let user-supplied
extra_tagsoverride the auto-generated Photometric and ExtraSamples tags. Build the auto tags, drop any whose tag id appears in userextra_tags, then append the user tags. Restrict this to PHOTOMETRIC and EXTRA_SAMPLES so users cannot clobber IMAGE_WIDTH or strip offsets by accident. - Document the kwarg and the override behavior in
to_geotiff's docstring.
This is a behavior change. 4-band data that used to come out as RGB+alpha will come out as MinIsBlack + ExtraSamples=unspecified. That is the point: the old default was producing wrong files. Users with actual RGBA data can pass photometric='rgba' to opt back in.
Activity
geotiff tests: consolidate kwarg / signature cluster (Sub-PR A) (#2458)