mirror of
https://github.com/smittix/intercept.git
synced 2026-06-08 22:21:55 -07:00
Fix SSTV slant correction wedge artifact
This commit is contained in:
@@ -685,6 +685,40 @@ class TestImageDecoder:
|
||||
assert img is not None
|
||||
assert img.size == (320, 240)
|
||||
|
||||
def test_slant_correction_wraps_rows_without_blank_wedge(self):
|
||||
"""Slant correction should rotate rows, not introduce black fill."""
|
||||
PIL = pytest.importorskip('PIL')
|
||||
from utils.sstv.image_decoder import SSTVImageDecoder
|
||||
|
||||
decoder = SSTVImageDecoder(SCOTTIE_1)
|
||||
decoder._sync_deviations = [float(i * 4) for i in range(SCOTTIE_1.height)]
|
||||
|
||||
source = np.full((SCOTTIE_1.height, SCOTTIE_1.width, 3), 128, dtype=np.uint8)
|
||||
img = PIL.Image.fromarray(source, 'RGB')
|
||||
|
||||
corrected = decoder._apply_slant_correction(img)
|
||||
corrected_arr = np.array(corrected)
|
||||
|
||||
# If correction clips/fills, zeros appear. Circular shift should preserve all values.
|
||||
assert corrected_arr.min() == 128
|
||||
assert corrected_arr.max() == 128
|
||||
|
||||
def test_slant_correction_skips_implausible_drift(self):
|
||||
"""Very large estimated drift should be treated as a bad fit and ignored."""
|
||||
PIL = pytest.importorskip('PIL')
|
||||
from utils.sstv.image_decoder import SSTVImageDecoder
|
||||
|
||||
decoder = SSTVImageDecoder(SCOTTIE_1)
|
||||
decoder._sync_deviations = [float(i * 40) for i in range(SCOTTIE_1.height)]
|
||||
|
||||
source = np.full((SCOTTIE_1.height, SCOTTIE_1.width, 3), 177, dtype=np.uint8)
|
||||
img = PIL.Image.fromarray(source, 'RGB')
|
||||
|
||||
corrected = decoder._apply_slant_correction(img)
|
||||
|
||||
# Implausible slope should return original image unchanged.
|
||||
assert np.array_equal(np.array(corrected), source)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SSTVDecoder orchestrator tests
|
||||
|
||||
+13
-10
@@ -397,9 +397,9 @@ class SSTVImageDecoder:
|
||||
|
||||
Uses the sync deviation measurements collected during decoding to
|
||||
estimate the per-line SDR clock drift rate via linear regression,
|
||||
then shears the image to compensate. Noisy individual measurements
|
||||
are averaged out; if fewer than 10 valid measurements exist the
|
||||
image is returned unchanged.
|
||||
then circularly shifts each row to compensate. Noisy individual
|
||||
measurements are averaged out; if fewer than 10 valid measurements
|
||||
exist the image is returned unchanged.
|
||||
"""
|
||||
valid = [(i, d) for i, d in enumerate(self._sync_deviations)
|
||||
if d is not None]
|
||||
@@ -423,16 +423,19 @@ class SSTVImageDecoder:
|
||||
|
||||
arr = np.array(img)
|
||||
height, width = arr.shape[:2]
|
||||
corrected = np.zeros_like(arr)
|
||||
|
||||
# Reject clearly implausible estimates. Even with cheap SDR clocks,
|
||||
# real SSTV slant is typically modest; extreme values are usually
|
||||
# bad sync picks that would over-correct the image.
|
||||
total_shift = abs((height - 1) * pixels_per_line)
|
||||
if total_shift > width * 0.25:
|
||||
return img
|
||||
|
||||
corrected = np.empty_like(arr)
|
||||
|
||||
for row in range(height):
|
||||
shift = -int(round(row * pixels_per_line))
|
||||
if shift == 0:
|
||||
corrected[row] = arr[row]
|
||||
elif shift > 0:
|
||||
corrected[row, shift:] = arr[row, :width - shift]
|
||||
else:
|
||||
corrected[row, :width + shift] = arr[row, -shift:]
|
||||
corrected[row] = np.roll(arr[row], shift=shift, axis=0)
|
||||
|
||||
return Image.fromarray(corrected, 'RGB')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user