fix(v5): average noisy copies from clean source

This commit is contained in:
2026-07-09 19:55:26 +08:00
parent da390b8bd9
commit 428b9a3e7f
2 changed files with 59 additions and 0 deletions

View File

@@ -165,6 +165,8 @@ def image_state_apply_operation(*, state, operation, params):
return image_state_fft_spectrum_create(state=state, params=params or {}) return image_state_fft_spectrum_create(state=state, params=params or {})
if operation == "inverse_fft_reconstruction": if operation == "inverse_fft_reconstruction":
return image_state_inverse_fft_create(state=state, params=params or {}) return image_state_inverse_fft_create(state=state, params=params or {})
if operation == "average_noisy_copies":
return image_state_average_noisy_copies_create(state=state, params=params or {})
source = load_image_array(state.image) source = load_image_array(state.image)
result = apply_registered_operation(source, operation, params or {}) result = apply_registered_operation(source, operation, params or {})
@@ -180,6 +182,40 @@ def image_state_apply_operation(*, state, operation, params):
return image_state_payload(state=new_state, include_image=True) return image_state_payload(state=new_state, include_image=True)
def image_state_average_noisy_copies_create(*, state, params):
"""Create averaged noisy copies from the clean source behind a Gaussian-noise state.
If the active state is already a Gaussian-noisy image, the averaging must regenerate independent
noisy copies from its parent image, not from the already-noisy pixels.
"""
source_state = state
if (
state.operation == "noise_filter"
and isinstance(state.params, dict)
and state.params.get("kind", "gaussian") == "gaussian"
and state.parent is not None
):
source_state = state.parent
operation_params = dict(params or {})
if source_state.id != state.id:
operation_params["source_state_id"] = str(source_state.id)
source = load_image_array(source_state.image)
result = apply_registered_operation(source, "average_noisy_copies", operation_params)
new_state = image_state_create(
session=state.session,
parent=state,
image=result,
operation="average_noisy_copies",
params=operation_params,
label=None,
prefix="state-average_noisy_copies",
)
return image_state_payload(state=new_state, include_image=True)
def image_state_fft_spectrum_create(*, state, params): def image_state_fft_spectrum_create(*, state, params):
"""Create an FFT visualization state and persist the actual complex spectrum. """Create an FFT visualization state and persist the actual complex spectrum.

View File

@@ -233,6 +233,29 @@ class ApiTests(TestCase):
self.assertEqual(result.ndim, 2) self.assertEqual(result.ndim, 2)
self.assertEqual(int(result[0, 0]), 96) self.assertEqual(int(result[0, 0]), 96)
def test_average_noisy_copies_uses_clean_parent_after_gaussian_noise_state(self):
upload = self.client.post("/api/images/", {"image": grayscale_png_upload(value=96)}, format="multipart")
s0_id = upload.data["states"][0]["state_id"]
noisy = self.client.post(
f"/api/states/{s0_id}/operations/",
{"operation": "noise_filter", "params": {"kind": "gaussian", "mean": 0.2, "variance": 0}},
format="json",
)
noisy_image = load_image_array(ImageState.objects.get(id=noisy.data["state_id"]).image)
self.assertGreater(int(noisy_image[0, 0]), 96)
averaged = self.client.post(
f"/api/states/{noisy.data['state_id']}/operations/",
{"operation": "average_noisy_copies", "params": {"N": 10, "kind": "gaussian", "mean": 0, "variance": 0}},
format="json",
)
self.assertEqual(averaged.status_code, 201)
self.assertEqual(averaged.data["parent_state_id"], noisy.data["state_id"])
self.assertEqual(averaged.data["params"]["source_state_id"], s0_id)
result = load_image_array(ImageState.objects.get(id=averaged.data["state_id"]).image)
self.assertEqual(int(result[0, 0]), 96)
def test_periodic_noise_preserves_grayscale_and_zero_amplitude(self): def test_periodic_noise_preserves_grayscale_and_zero_amplitude(self):
upload = self.client.post("/api/images/", {"image": grayscale_png_upload(value=96)}, format="multipart") upload = self.client.post("/api/images/", {"image": grayscale_png_upload(value=96)}, format="multipart")
s0_id = upload.data["states"][0]["state_id"] s0_id = upload.data["states"][0]["state_id"]