Add segmentation post-processing: dark water fix + wetland reclassify
Two heuristic rules applied after SegEarth-OV3 inference: 1. Dark water: if background pixels have mean_rgb < 0.24 and std < 0.08, reclassify as water. Fixes GTA-UAV satellite dark ocean (57% → ~15% bg). 2. Wetland reclassify (GTA-UAV only): split false-positive wetland pixels by color — green-dominant → vegetation, else → bare soil. Fixes 14.3% muddy/wetland false positives on GTA-V hillside terrain. Config flags: seg_fix_dark_water (default True), seg_reclassify_wetland (default False, enabled in run_gta_uav.py). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ from src.augmentor.inference import (
|
||||
infer_chmv2_batch,
|
||||
infer_depth_batch,
|
||||
infer_segmentation_batch,
|
||||
postprocess_segmentation,
|
||||
_SOBEL_X,
|
||||
_SOBEL_Y,
|
||||
)
|
||||
@@ -220,3 +221,59 @@ class TestInferSegmentationBatch:
|
||||
result = infer_segmentation_batch(pipeline, seg_config, sample_batch, torch.device("cpu"))
|
||||
assert result.shape == (B, 1, H, W)
|
||||
assert (result == 0).all()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Post-processing heuristics
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestPostprocessSegmentation:
|
||||
def test_dark_water_reclassified(self) -> None:
|
||||
"""Dark uniform background → water."""
|
||||
# Dark image (mean ~0.15, std ~0.02)
|
||||
rgb = torch.full((1, 3, 32, 32), 0.15)
|
||||
rgb += torch.randn_like(rgb) * 0.02
|
||||
rgb.clamp_(0, 1)
|
||||
seg = torch.zeros(1, 1, 32, 32, dtype=torch.uint8) # all background
|
||||
result = postprocess_segmentation(seg, rgb)
|
||||
assert (result == 4).all() # water
|
||||
|
||||
def test_bright_background_unchanged(self) -> None:
|
||||
"""Bright background should NOT become water."""
|
||||
rgb = torch.full((1, 3, 32, 32), 0.6)
|
||||
seg = torch.zeros(1, 1, 32, 32, dtype=torch.uint8)
|
||||
result = postprocess_segmentation(seg, rgb)
|
||||
assert (result == 0).all() # stays background
|
||||
|
||||
def test_non_background_unchanged(self) -> None:
|
||||
"""Non-background classes untouched even if dark."""
|
||||
rgb = torch.full((1, 3, 32, 32), 0.1)
|
||||
seg = torch.full((1, 1, 32, 32), 3, dtype=torch.uint8) # vegetation
|
||||
result = postprocess_segmentation(seg, rgb)
|
||||
assert (result == 3).all()
|
||||
|
||||
def test_wetland_reclassify_green(self) -> None:
|
||||
"""Green wetland → vegetation when reclassify_wetland=True."""
|
||||
rgb = torch.zeros(1, 3, 32, 32)
|
||||
rgb[0, 1] = 0.6 # green dominant
|
||||
rgb[0, 0] = 0.2 # low red
|
||||
seg = torch.full((1, 1, 32, 32), 14, dtype=torch.uint8) # wetland
|
||||
result = postprocess_segmentation(seg, rgb, reclassify_wetland=True)
|
||||
assert (result == 3).all() # vegetation
|
||||
|
||||
def test_wetland_reclassify_brown(self) -> None:
|
||||
"""Brown wetland → bare soil when reclassify_wetland=True."""
|
||||
rgb = torch.zeros(1, 3, 32, 32)
|
||||
rgb[0, 0] = 0.5 # red dominant
|
||||
rgb[0, 1] = 0.3 # low green
|
||||
seg = torch.full((1, 1, 32, 32), 14, dtype=torch.uint8)
|
||||
result = postprocess_segmentation(seg, rgb, reclassify_wetland=True)
|
||||
assert (result == 11).all() # bare soil
|
||||
|
||||
def test_wetland_unchanged_without_flag(self) -> None:
|
||||
"""Wetland stays if reclassify_wetland=False."""
|
||||
rgb = torch.zeros(1, 3, 32, 32)
|
||||
rgb[0, 1] = 0.6
|
||||
seg = torch.full((1, 1, 32, 32), 14, dtype=torch.uint8)
|
||||
result = postprocess_segmentation(seg, rgb, reclassify_wetland=False)
|
||||
assert (result == 14).all()
|
||||
|
||||
Reference in New Issue
Block a user