Skip to content

Fix rasterize accuracy: GPU ceil, half-open fill, all_touched pairing (#995)#996

Merged
brendancol merged 2 commits into
masterfrom
issue-995
Mar 10, 2026
Merged

Fix rasterize accuracy: GPU ceil, half-open fill, all_touched pairing (#995)#996
brendancol merged 2 commits into
masterfrom
issue-995

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

@brendancol brendancol commented Mar 9, 2026

Summary

Three rasterize bugs fixed, plus 41 new tests that check mathematical invariants instead of spot-checking pixels.

Bug fixes

GPU ceil approximation — The GPU scanline kernel used int(x + 0.999999) as a ceil substitute. This gives wrong results when an edge x-intersection lands within 1e-6 of an integer boundary (CPU uses np.ceil, gets a different answer). Replaced with exact int-based ceil.

Closed-interval boundary — The scanline fill range [ceil(x_start), floor(x_end)] double-counts pixels at shared polygon edges and fills pixels whose centers are outside the polygon. Switched to half-open [ceil(x_start), ceil(x_end)-1]. Fixes watertight tiling, rectangle area counts, and rasterio comparisons for axis-aligned edges.

all_touched edge pairingall_touched=True expanded edge y-ranges, which could create an odd active edge count on certain rows, breaking even-odd fill pairing. The backwards result: all_touched=True sometimes filled fewer pixels than False. Fixed by keeping normal scanline fill and drawing polygon boundaries via Bresenham separately, so the touched set is always a superset.

Accuracy tests (41 new)

  • Watertight tiling: non-overlapping polygon grids counted exactly once
  • Area conservation: pixel count converges to analytic area
  • Point-in-polygon: shapely.contains agrees with rasterized grid (interior)
  • Annulus: polygon with circular hole tracks pi*(R^2 - r^2)
  • Symmetry: axis-aligned shapes perfectly symmetric
  • Merge mode identities: sum-with-ones equals count, etc.
  • all_touched monotonicity: always a superset of normal fill
  • rasterio reference: exact for axis-aligned, <3% diff for slanted edges
  • Edge cases: thin polygons, concave shapes, degenerate triangles

Test plan

  • 55 existing rasterize tests pass
  • 41 new accuracy tests pass
  • GPU tests (needs CUDA hardware)

Closes #995

…d edge pairing (#995)

Three rasterize accuracy fixes:

1. GPU scanline used int(x + 0.999999) as a ceil approximation. This
   diverges from CPU's np.ceil for values within 1e-6 of an integer.
   Replaced with exact int-based ceil logic.

2. Scanline fill used closed interval [ceil(x_start), floor(x_end)]
   for column ranges. This double-counts boundary pixels when adjacent
   polygons share an edge, and includes pixels whose centers fall outside
   the polygon. Changed to half-open [ceil(x_start), ceil(x_end)-1],
   matching GDAL's convention for axis-aligned edges.

3. all_touched mode expanded edge y-ranges, which broke even-odd edge
   pairing on rows where the expansion created an odd active edge count.
   This caused all_touched=True to fill fewer pixels than all_touched=False.
   Fixed by keeping normal scanline fill and drawing polygon boundaries
   via Bresenham separately, guaranteeing the result is a superset.
41 tests exercising mathematical properties of correct rasterization:

- Watertight tiling: non-overlapping polygons counted exactly once
- Area conservation: rasterized area converges to analytic area
- Point-in-polygon cross-check: shapely.contains vs rasterized grid
- Annulus: polygon with hole matches pi*(R^2 - r^2)
- Symmetry: axis-aligned shapes perfectly symmetric, others bounded
- Merge mode identities: sum-with-ones equals count, etc.
- all_touched monotonicity: always a superset of normal fill
- rasterio reference: exact match for axis-aligned, <3% diff for slanted
- Edge cases: thin polygons, concave shapes, nearly-degenerate triangles
@github-actions github-actions Bot added the performance PR touches performance-sensitive code label Mar 9, 2026
@brendancol brendancol merged commit 4125b34 into master Mar 10, 2026
11 checks passed
@brendancol brendancol deleted the issue-995 branch May 4, 2026 13:06
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.

Fix GPU ceil approximation in scanline fill; add rasterize accuracy test suite

1 participant