Skip to content

Apply MinIsWhite inversion across GeoTIFF read backends#1804

Merged
brendancol merged 3 commits into
mainfrom
issue-1795
May 13, 2026
Merged

Apply MinIsWhite inversion across GeoTIFF read backends#1804
brendancol merged 3 commits into
mainfrom
issue-1795

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

@brendancol brendancol commented May 13, 2026

Closes #1797.

Shares MinIsWhite handling across eager HTTP, remote dask, and GPU read paths so backend values agree.

Tested: pytest xrspatial/geotiff/tests/test_miniswhite_backend_parity_1797.py

@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 13, 2026
@brendancol brendancol requested a review from Copilot May 13, 2026 14:56
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes TIFF Photometric=MinIsWhite (inversion) handling consistent across GeoTIFF read backends so that eager HTTP reads, dask windowed reads (HTTP/fsspec-prefetch path), and GPU reads produce the same pixel values.

Changes:

  • Centralized MinIsWhite inversion into _reader._apply_photometric_miniswhite() and applied it to the HTTP COG reader and the dask HTTP-prefetch window reader.
  • Added MinIsWhite inversion handling to the GPU read path for non-CPU-decoded GPU outputs.
  • Added new regression tests for HTTP eager and HTTP+dask parity for MinIsWhite images.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
xrspatial/geotiff/tests/test_miniswhite_backend_parity_1795.py Adds HTTP + HTTP-dask tests asserting MinIsWhite inversion matches expected output.
xrspatial/geotiff/_reader.py Introduces _apply_photometric_miniswhite() and applies it in HTTP and local read paths for consistent inversion.
xrspatial/geotiff/__init__.py Applies MinIsWhite inversion in the dask HTTP-prefetch path and in the GPU decode path for backend parity.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -0,0 +1,86 @@
"""MinIsWhite photometric handling must be backend-consistent (#1795)."""
Comment on lines +3304 to +3310
if (ifd.photometric == 0 and samples == 1 and not arr_was_cpu_decoded):
gpu_dtype = np.dtype(str(arr_gpu.dtype))
if gpu_dtype.kind == 'u':
arr_gpu = np.iinfo(gpu_dtype).max - arr_gpu
elif gpu_dtype.kind == 'f':
arr_gpu = -arr_gpu

Comment on lines +16 to +55
class _RangeHandler(http.server.BaseHTTPRequestHandler):
payload: bytes = b''

def do_GET(self): # noqa: N802
rng = self.headers.get('Range')
if rng and rng.startswith('bytes='):
spec = rng[len('bytes='):]
start_s, _, end_s = spec.partition('-')
start = int(start_s)
end = int(end_s) if end_s else len(self.payload) - 1
chunk = self.payload[start:end + 1]
self.send_response(206)
self.send_header('Content-Type', 'application/octet-stream')
self.send_header(
'Content-Range',
f'bytes {start}-{start + len(chunk) - 1}/{len(self.payload)}',
)
self.send_header('Content-Length', str(len(chunk)))
self.end_headers()
self.wfile.write(chunk)
return
self.send_response(200)
self.send_header('Content-Type', 'application/octet-stream')
self.send_header('Content-Length', str(len(self.payload)))
self.end_headers()
self.wfile.write(self.payload)

def log_message(self, *_args, **_kwargs):
pass


def _serve(payload: bytes):
handler_cls = type(
'RangeHandler1795', (_RangeHandler,), {'payload': payload}
)
httpd = socketserver.TCPServer(('127.0.0.1', 0), handler_cls)
port = httpd.server_address[1]
thread = threading.Thread(target=httpd.serve_forever, daemon=True)
thread.start()
return httpd, port
@brendancol brendancol merged commit a5d78e4 into main May 13, 2026
1 of 11 checks passed
brendancol added a commit that referenced this pull request May 13, 2026
Resolves conflict in xrspatial/geotiff/__init__.py: keeps the
`_read_vrt_dask` dispatch hook from the PR branch. All other
geotiff changes from main (#1791, #1793, #1801, #1802, #1803, #1804,
#1805, #1806) were already integrated into the working tree by the
prior 7329dd9 commit; this merge just records the parent so git
recognises the reconciliation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MinIsWhite GeoTIFF inversion is inconsistent across read backends

2 participants