feat(v5): add periodic noise and fft reconstruction
This commit is contained in:
@@ -3,6 +3,7 @@ from io import BytesIO
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import numpy as np
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.test import TestCase, override_settings
|
||||
from PIL import Image
|
||||
@@ -69,12 +70,19 @@ class ApiTests(TestCase):
|
||||
self.assertIn("median_filter", operation_ids)
|
||||
self.assertIn("noise_filter", operation_ids)
|
||||
self.assertIn("average_noisy_copies", operation_ids)
|
||||
self.assertIn("periodic_noise", operation_ids)
|
||||
self.assertIn("inverse_fft_reconstruction", operation_ids)
|
||||
self.assertIn("rgb_to_gray", operation_ids)
|
||||
self.assertEqual(operations["histeq"]["params"], {})
|
||||
self.assertEqual(operations["noise_filter"]["label"], "Noise Filter")
|
||||
self.assertEqual(operations["average_noisy_copies"]["label"], "Average N Noisy Copies")
|
||||
self.assertEqual(operations["average_noisy_copies"]["params"]["N"]["default"], 100)
|
||||
self.assertFalse(operations["average_noisy_copies"]["repeatable"])
|
||||
self.assertEqual(operations["periodic_noise"]["label"], "Periodic Noise")
|
||||
self.assertEqual(operations["periodic_noise"]["params"]["A"]["default"], 0.2)
|
||||
self.assertEqual(operations["periodic_noise"]["params"]["T"]["default"], 100)
|
||||
self.assertEqual(operations["inverse_fft_reconstruction"]["label"], "Inverse FFT Reconstruction")
|
||||
self.assertFalse(operations["inverse_fft_reconstruction"]["repeatable"])
|
||||
self.assertEqual(operations["box_filter"]["label"], "Average / Box Filter")
|
||||
self.assertEqual(operations["gaussian_filter"]["label"], "Gaussian Filter")
|
||||
self.assertFalse(operations["negative"]["repeatable"])
|
||||
@@ -225,6 +233,76 @@ class ApiTests(TestCase):
|
||||
self.assertEqual(result.ndim, 2)
|
||||
self.assertEqual(int(result[0, 0]), 96)
|
||||
|
||||
def test_periodic_noise_preserves_grayscale_and_zero_amplitude(self):
|
||||
upload = self.client.post("/api/images/", {"image": grayscale_png_upload(value=96)}, format="multipart")
|
||||
s0_id = upload.data["states"][0]["state_id"]
|
||||
response = self.client.post(
|
||||
f"/api/states/{s0_id}/operations/",
|
||||
{"operation": "periodic_noise", "params": {"A": 0, "T": 100}},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(response.data["channels"], 1)
|
||||
self.assertEqual(response.data["color_mode"], "L")
|
||||
result = load_image_array(ImageState.objects.get(id=response.data["state_id"]).image)
|
||||
self.assertEqual(result.ndim, 2)
|
||||
self.assertTrue(np.all(result == 96))
|
||||
|
||||
def test_periodic_noise_rgb_uses_shared_channel_mask(self):
|
||||
upload = self.client.post("/api/images/", {"image": png_upload(color=(80, 80, 80), size=(8, 8))}, format="multipart")
|
||||
s0_id = upload.data["states"][0]["state_id"]
|
||||
response = self.client.post(
|
||||
f"/api/states/{s0_id}/operations/",
|
||||
{"operation": "periodic_noise", "params": {"A": 0.1, "T": 4}},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 201)
|
||||
result = load_image_array(ImageState.objects.get(id=response.data["state_id"]).image)
|
||||
np.testing.assert_array_equal(result[:, :, 0], result[:, :, 1])
|
||||
np.testing.assert_array_equal(result[:, :, 1], result[:, :, 2])
|
||||
|
||||
def test_periodic_noise_rejects_invalid_period(self):
|
||||
upload = self.client.post("/api/images/", {"image": grayscale_png_upload()}, format="multipart")
|
||||
s0_id = upload.data["states"][0]["state_id"]
|
||||
response = self.client.post(
|
||||
f"/api/states/{s0_id}/operations/",
|
||||
{"operation": "periodic_noise", "params": {"A": 0.2, "T": 0}},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_inverse_fft_reconstruction_matches_input_shape_and_values(self):
|
||||
upload = self.client.post("/api/images/", {"image": png_upload(color=(32, 64, 128), size=(4, 4))}, format="multipart")
|
||||
s0_id = upload.data["states"][0]["state_id"]
|
||||
response = self.client.post(
|
||||
f"/api/states/{s0_id}/operations/",
|
||||
{"operation": "inverse_fft_reconstruction", "params": {}},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(response.data["channels"], 3)
|
||||
result = load_image_array(ImageState.objects.get(id=response.data["state_id"]).image)
|
||||
expected = load_image_array(ImageState.objects.get(id=s0_id).image)
|
||||
np.testing.assert_allclose(result, expected, atol=1)
|
||||
|
||||
def test_inverse_fft_reconstruction_preserves_grayscale(self):
|
||||
upload = self.client.post("/api/images/", {"image": grayscale_png_upload(value=96)}, format="multipart")
|
||||
s0_id = upload.data["states"][0]["state_id"]
|
||||
response = self.client.post(
|
||||
f"/api/states/{s0_id}/operations/",
|
||||
{"operation": "inverse_fft_reconstruction", "params": {}},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(response.data["channels"], 1)
|
||||
result = load_image_array(ImageState.objects.get(id=response.data["state_id"]).image)
|
||||
self.assertEqual(result.ndim, 2)
|
||||
self.assertTrue(np.allclose(result, 96, atol=1))
|
||||
|
||||
def test_grayscale_operation_creates_state(self):
|
||||
upload = self.client.post("/api/images/", {"image": png_upload()}, format="multipart")
|
||||
s0_id = upload.data["states"][0]["state_id"]
|
||||
|
||||
Reference in New Issue
Block a user