Add registered SynthID phase probing

This commit is contained in:
Victor Kuznetsov
2026-08-10 11:07:32 -07:00
parent 391e4c1e7c
commit 5e5a3976ba
8 changed files with 556 additions and 75 deletions
+27
View File
@@ -118,3 +118,30 @@ def test_scoring_can_canonicalize_geometry(tmp_path: Path) -> None:
assert score.path == str(mismatch)
assert score.peak_count == 4
def test_translation_search_recovers_shifted_carrier(tmp_path: Path) -> None:
positives: list[Path] = []
for index in range(4):
path = tmp_path / f"positive-{index}.png"
_write_image(path, phase=0.4, seed=index)
positives.append(path)
heldout = tmp_path / "heldout.png"
shifted = tmp_path / "shifted.png"
_write_image(heldout, phase=0.4, seed=10)
with Image.open(heldout) as source:
pixels = np.asarray(source).copy()
Image.fromarray(np.roll(pixels, shift=(1, 1), axis=(0, 1)), mode="RGB").save(shifted)
model = carrier.discover_model(positives, peak_count=8, min_radius=1.0)
fixed = carrier.score_image(shifted, model)
unregistered = carrier.score_translations(shifted, model, max_shift=0)
registered = carrier.score_translations(shifted, model, max_shift=2)
assert unregistered.score == pytest.approx(fixed.score)
assert unregistered.active_weight_fraction == pytest.approx(fixed.active_weight_fraction)
assert registered.score > fixed.score
assert abs(registered.row_shift) <= 2
assert abs(registered.column_shift) <= 2
with pytest.raises(ValueError, match="between 0 and 32"):
carrier.score_translations(shifted, model, max_shift=33)
+57
View File
@@ -30,6 +30,29 @@ def _write_codebook(path: Path, *, height: int, width: int, phase: float) -> Non
np.savez(path, **payload)
def _write_dense_codebook(path: Path, *, height: int, width: int, phase: float) -> None:
half_width = width // 2 + 1
magnitudes = np.zeros((height, half_width, 3), dtype=np.float16)
phases = np.zeros_like(magnitudes)
coherence = np.zeros_like(magnitudes, dtype=np.uint8)
rows = np.asarray([7, 11, 13, 17])
columns = np.asarray([5, 9, 12, 15])
for channel in range(3):
magnitudes[rows, columns, channel] = np.log2(1.0 + np.asarray([1000.0, 10.0, 10.0, 10.0]))
phases[rows, columns, channel] = phase
coherence[rows, columns, channel] = 255
np.savez(
path,
format_version=np.asarray(2),
**{
f"{height}x{width}/sparse": np.asarray(0),
f"{height}x{width}/mag": magnitudes,
f"{height}x{width}/phase": phases,
f"{height}x{width}/cons": coherence,
},
)
def _write_carrier(path: Path, *, height: int, width: int, phase: float) -> None:
yy, xx = np.mgrid[:height, :width]
carrier = 80.0 + 20.0 * np.cos(2.0 * np.pi * (7.0 * yy / height + 5.0 * xx / width) + phase)
@@ -51,6 +74,40 @@ def test_numeric_codebook_scores_matching_phase(tmp_path: Path) -> None:
assert score.phase_score > 0.0
def test_dense_numeric_codebook_scores_matching_phase(tmp_path: Path) -> None:
height = width = 64
codebook = tmp_path / "dense-codebook.npz"
image = tmp_path / "image.png"
_write_dense_codebook(codebook, height=height, width=width, phase=0.4)
_write_carrier(image, height=height, width=width, phase=0.4)
model = probe.load_v3_model(codebook, height=height, width=width, peak_count=4, min_radius=1.0)
score = probe.score_image(image, model)
assert score.peak_count == 4
assert score.phase_score > 0.0
def test_translation_search_recovers_shifted_carrier(tmp_path: Path) -> None:
height = width = 64
codebook = tmp_path / "codebook.npz"
image = tmp_path / "image.png"
shifted = tmp_path / "shifted.png"
_write_codebook(codebook, height=height, width=width, phase=0.4)
_write_carrier(image, height=height, width=width, phase=0.4)
with Image.open(image) as source:
pixels = np.asarray(source).copy()
Image.fromarray(np.roll(pixels, shift=(1, 1), axis=(0, 1)), mode="RGB").save(shifted)
model = probe.load_v3_model(codebook, height=height, width=width, peak_count=4, min_radius=1.0)
fixed = probe.score_image(shifted, model)
registered = probe.score_translations(shifted, model, max_shift=2)
assert registered.phase_score > fixed.phase_score
assert abs(registered.row_shift) <= 2
assert abs(registered.column_shift) <= 2
def test_rejects_wrong_format(tmp_path: Path) -> None:
artifact = tmp_path / "bad.npz"
np.savez(artifact, format_version=np.asarray(1))