Spaces:
Sleeping
Sleeping
Chandra-Align
feat: scientific photogrammetric workstation - preprocessing, ground metrics, visualization, export, enhanced UI
38d2728 Download tests/test_sar_preprocessing.py from RevanShriyan/Chandra-Align: direct link, hf CLI and curl.
- Browser
- Download file 6.16 kB
-
https://huggingface.co/spaces/RevanShriyan/Chandra-Align/resolve/main/tests/test_sar_preprocessing.py
- Command line
-
hf download hf://spaces/RevanShriyan/Chandra-Align/tests/test_sar_preprocessing.py
-
curl -L -o test_sar_preprocessing.py https://huggingface.co/spaces/RevanShriyan/Chandra-Align/resolve/main/tests/test_sar_preprocessing.py
6.16 kB
| import pytest | |
| import numpy as np | |
| import cv2 | |
| from chandra_align.preprocessing.sar import ( | |
| refined_lee_filter, | |
| frost_filter, | |
| prepare_sar_intensity, | |
| ) | |
| class TestSARPreprocessing: | |
| """Unit tests for SAR preprocessing functions.""" | |
| def test_refined_lee_filter_basic(self): | |
| """Test Refined Lee filter on synthetic speckled image.""" | |
| # Create smooth base image | |
| base = np.ones((100, 100), dtype=np.float32) * 100.0 | |
| # Add multiplicative speckle (Gamma distributed with ENL=1) | |
| np.random.seed(42) | |
| speckle = np.random.gamma(shape=1.0, scale=1.0, size=(100, 100)) | |
| speckled = base * speckle | |
| filtered = refined_lee_filter(speckled, win_size=5, num_looks=1.0) | |
| # Check output shape | |
| assert filtered.shape == (100, 100) | |
| # Check output is finite | |
| assert np.all(np.isfinite(filtered)) | |
| # Check output is non-negative | |
| assert np.all(filtered >= 0) | |
| # Variance in flat region should be reduced | |
| center_region = filtered[40:60, 40:60] | |
| input_center = speckled[40:60, 40:60] | |
| assert np.var(center_region) < np.var(input_center) * 0.5 | |
| def test_refined_lee_filter_win_size_validation(self): | |
| """Test win_size validation for Refined Lee filter.""" | |
| img = np.ones((50, 50), dtype=np.float32) | |
| # Even win_size should raise | |
| with pytest.raises(ValueError, match="odd integer >= 3"): | |
| refined_lee_filter(img, win_size=4) | |
| # win_size < 3 should raise | |
| with pytest.raises(ValueError, match="odd integer >= 3"): | |
| refined_lee_filter(img, win_size=1) | |
| # Valid win_size | |
| result = refined_lee_filter(np.ones((10, 10)), win_size=3) | |
| assert result.shape == (10, 10) | |
| def test_frost_filter_basic(self): | |
| """Test Frost filter on synthetic speckled image.""" | |
| base = np.ones((50, 50), dtype=np.float32) * 100.0 | |
| np.random.seed(42) | |
| speckle = np.random.gamma(shape=1.0, scale=1.0, size=(50, 50)) | |
| speckled = base * speckle | |
| filtered = frost_filter(speckled, win_size=5, damp_factor=1.0) | |
| assert filtered.shape == (50, 50) | |
| assert np.all(np.isfinite(filtered)) | |
| assert np.all(filtered >= 0) | |
| def test_frost_filter_win_size_validation(self): | |
| """Test win_size validation for Frost filter.""" | |
| img = np.ones((50, 50), dtype=np.float32) | |
| with pytest.raises(ValueError, match="odd integer >= 3"): | |
| frost_filter(img, win_size=4) | |
| with pytest.raises(ValueError, match="odd integer >= 3"): | |
| frost_filter(img, win_size=1) | |
| def test_prepare_sar_intensity_handles_zeros_and_negatives(self): | |
| """Test log-scale preparation handles zeros and negatives safely.""" | |
| img = np.array([ | |
| [0.0, 1.0, 10.0], | |
| [-5.0, 100.0, 1000.0], | |
| [0.0, -1.0, 0.0] | |
| ], dtype=np.float32) | |
| result = prepare_sar_intensity(img) | |
| assert result.dtype == np.uint8 | |
| assert result.shape == img.shape | |
| # No NaN or Inf should be present | |
| assert np.all(np.isfinite(result.astype(np.float32))) | |
| # Values should be in valid uint8 range | |
| assert np.all(result >= 0) | |
| assert np.all(result <= 255) | |
| def test_prepare_sar_intensity_uniform_image(self): | |
| """Test preparation on uniform image.""" | |
| img = np.ones((50, 50), dtype=np.float32) * 100.0 | |
| result = prepare_sar_intensity(img) | |
| assert result.shape == (50, 50) | |
| assert result.dtype == np.uint8 | |
| # Uniform image should result in uniform output | |
| assert np.std(result) == 0 | |
| def test_refined_lee_preserves_edges(self): | |
| """Test Refined Lee preserves edges in synthetic step edge.""" | |
| # Create image with sharp edge | |
| img = np.ones((100, 100), dtype=np.float32) * 10.0 | |
| img[:, 50:] = 100.0 # Sharp step edge at column 50 | |
| # Add mild speckle | |
| np.random.seed(42) | |
| speckle = np.random.gamma(shape=4.0, scale=0.25, size=(100, 100)) | |
| speckled = img * speckle | |
| filtered = refined_lee_filter(speckled, win_size=7, num_looks=4.0) | |
| # Edge should still be detectable (gradient preserved) | |
| # Convert to uint8 for Sobel (OpenCV limitation) | |
| speckled_8u = cv2.normalize(speckled, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8) | |
| filtered_8u = cv2.normalize(filtered, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8) | |
| grad_before = cv2.Sobel(speckled_8u, cv2.CV_32F, 1, 0, ksize=3) | |
| grad_after = cv2.Sobel(filtered_8u, cv2.CV_32F, 1, 0, ksize=3) | |
| # Edge at column 50 should still have high gradient | |
| edge_grad_before = np.max(np.abs(grad_before[:, 48:52])) | |
| edge_grad_after = np.max(np.abs(grad_after[:, 48:52])) | |
| # Gradient should not be completely smoothed out | |
| assert edge_grad_after > edge_grad_before * 0.3 | |
| def test_refined_lee_homogeneous_region(self): | |
| """Test Refined Lee on homogeneous region (low variance).""" | |
| img = np.ones((50, 50), dtype=np.float32) * 100.0 | |
| np.random.seed(42) | |
| speckle = np.random.gamma(shape=1.0, scale=1.0, size=(50, 50)) | |
| speckled = img * speckle | |
| filtered = refined_lee_filter(speckled, win_size=5, num_looks=1.0) | |
| # Should not produce NaN or Inf | |
| assert np.all(np.isfinite(filtered)) | |
| # Mean should be close to original mean | |
| assert abs(np.mean(filtered) - 100.0) < 20.0 | |
| def test_frost_filter_homogeneous(self): | |
| """Test Frost filter on homogeneous region.""" | |
| img = np.ones((30, 30), dtype=np.float32) * 100.0 | |
| np.random.seed(42) | |
| speckle = np.random.gamma(shape=1.0, scale=1.0, size=(30, 30)) | |
| speckled = img * speckle | |
| filtered = frost_filter(speckled, win_size=5, damp_factor=1.0) | |
| assert np.all(np.isfinite(filtered)) | |
| assert filtered.shape == (30, 30) | |
| if __name__ == "__main__": | |
| pytest.main([__file__, "-v"]) |