PixelModel-v4-image-codec-study / tests /test_cross_codec.py
brulee-1's picture
Add files using upload-large-folder tool
901a5f5 verified
Raw
History Blame Contribute Delete
22.9 kB
"""Task 1 cross-codec adapterの契約テスト。"""
from __future__ import annotations
from dataclasses import FrozenInstanceError, replace
from io import BytesIO
import numpy as np
import pytest
from PIL import Image
from pixelmodel_robustness.cross_codec import (
CODEC_SPECS,
CONDITION_IDS,
CONDITION_REGISTRY,
CodecSpec,
ConditionSpec,
JxlCodecError,
ProtectedArtifact,
decode_l_plane,
decode_protected_high,
decode_rgb,
encode_l_plane,
encode_protected_high,
encode_rgb,
preflight_codecs,
repair_nonfinite_zero,
semantic_hash,
)
def rgb_image() -> Image.Image:
return Image.fromarray(
np.array(
[[[0, 1, 2], [20, 40, 60]], [[100, 120, 140], [240, 220, 200]]],
dtype=np.uint8,
),
"RGB",
)
def gray_plane() -> np.ndarray:
return np.arange(16, dtype=np.uint8).reshape(4, 4) * 13
def test_registry_has_exact_order_and_stable_semantic_hash() -> None:
assert CONDITION_IDS == (
"png_baseline", "webp_lossless", "jpeg_q100_raw", "jpeg_q100_repair_zero",
"jpeg_q100_protected_high", "jpeg_q80_raw", "jpeg_q80_repair_zero",
"jpeg_q80_protected_high", "webp_q80_raw", "webp_q80_repair_zero",
"webp_q80_protected_high", "avif_q70_raw", "avif_q70_repair_zero",
"avif_q70_protected_high", "jxl_d1_raw", "jxl_d1_repair_zero",
"jxl_d1_protected_high",
)
assert tuple(CONDITION_REGISTRY) == CONDITION_IDS
assert semantic_hash(CONDITION_REGISTRY) == semantic_hash(tuple(CONDITION_REGISTRY))
with pytest.raises(TypeError):
CONDITION_REGISTRY["new"] = CONDITION_REGISTRY["png_baseline"] # type: ignore[index]
def test_codec_spec_is_immutable_and_options_are_fixed() -> None:
spec = CODEC_SPECS["jpeg_q80"]
assert isinstance(spec, CodecSpec)
assert spec.quality == 80
assert spec.options["subsampling"] == 0
with pytest.raises(FrozenInstanceError):
spec.quality = 99 # type: ignore[misc]
assert CODEC_SPECS["webp_lossless"].options == {"lossless": True, "exact": True, "method": 6}
assert CODEC_SPECS["jxl_d1"].quality == 90
assert CODEC_SPECS["jxl_d1"].metadata["libjxl_distance"] == 1.0
assert "jxl:distance" not in str(CODEC_SPECS["jxl_d1"].options)
def test_all_codec_options_and_nested_registry_objects_are_immutable() -> None:
expected = {
"png": {},
"webp_lossless": {"lossless": True, "exact": True, "method": 6},
"jpeg_q100": {"quality": 100, "subsampling": 0, "optimize": False},
"jpeg_q80": {"quality": 80, "subsampling": 0, "optimize": False},
"webp_q80": {"quality": 80, "lossless": False, "method": 6, "exact": True},
"avif_q70": {"quality": 70, "subsampling": "4:4:4", "speed": 6, "range": "full", "codec": "aom", "max_threads": 1, "autotiling": False},
"jxl_d1": {"quality": 90},
}
assert {name: dict(spec.options) for name, spec in CODEC_SPECS.items()} == expected
assert all(spec.grayscale is True for spec in CODEC_SPECS.values())
for spec in CODEC_SPECS.values():
with pytest.raises(TypeError):
spec.options["quality"] = 1 # type: ignore[index]
with pytest.raises(TypeError):
spec.metadata["changed"] = True # type: ignore[index]
entry = CONDITION_REGISTRY["png_baseline"]
assert isinstance(entry, ConditionSpec)
with pytest.raises(FrozenInstanceError):
entry.mode = "tampered" # type: ignore[misc]
@pytest.mark.parametrize("codec", ("png", "webp_lossless", "jpeg_q100", "jpeg_q80", "webp_q80", "avif_q70"))
def test_rgb_encode_decode_uses_fixed_contract(codec: str) -> None:
available = {item.name: item for item in preflight_codecs()}
if not available[codec].available:
pytest.skip(available[codec].reason or "codec unavailable")
payload = encode_rgb(rgb_image(), CODEC_SPECS[codec])
decoded = decode_rgb(payload, CODEC_SPECS[codec])
assert isinstance(payload, bytes)
assert decoded.mode == "RGB"
assert decoded.size == (2, 2)
if codec in {"png", "webp_lossless"}:
np.testing.assert_array_equal(np.asarray(decoded), np.asarray(rgb_image()))
def test_grayscale_plane_round_trip_for_lossless_png() -> None:
payload = encode_l_plane(gray_plane(), CODEC_SPECS["png"])
decoded = decode_l_plane(payload, CODEC_SPECS["png"])
np.testing.assert_array_equal(decoded, gray_plane())
def test_l_plane_adapter_passes_single_channel_image_to_encoder(monkeypatch: pytest.MonkeyPatch) -> None:
import pixelmodel_robustness.cross_codec as module
modes: list[str] = []
original_save = Image.Image.save
def capture_save(self: Image.Image, *args: object, **kwargs: object) -> object:
modes.append(self.mode)
return original_save(self, *args, **kwargs)
monkeypatch.setattr(Image.Image, "save", capture_save)
encode_l_plane(gray_plane(), CODEC_SPECS["png"])
assert modes[-1] == "L"
assert module.decode_l_plane(encode_l_plane(gray_plane(), CODEC_SPECS["png"]), CODEC_SPECS["png"]).dtype == np.uint8
def test_jxl_l_plane_writes_l_mode_source_png(monkeypatch: pytest.MonkeyPatch) -> None:
import pixelmodel_robustness.cross_codec as module
source_modes: list[str] = []
seen_argv: list[list[str]] = []
def fake_jxl_run(argv: list[str]) -> object:
seen_argv.append(argv)
with Image.open(argv[2]) as source:
source_modes.append(source.mode)
Image.new("L", (4, 4)).save(argv[-1], format="PNG")
return type("Completed", (), {"returncode": 0, "stdout": "", "stderr": ""})()
monkeypatch.setattr(module, "_jxl_run", fake_jxl_run)
module._jxl_encode(Image.fromarray(gray_plane(), "L"))
assert source_modes == ["L"]
assert seen_argv[0][0:2] == ["-quality", "90"]
assert seen_argv[0][2].endswith("source.png")
assert seen_argv[0][3].endswith("payload.jxl")
def test_protected_high_is_exact_and_payload_is_combined_bytes() -> None:
values = np.array([0.0, -0.0, 1.5, -2.25, 3.125, -4.5], dtype=np.float16)
artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"])
assert isinstance(artifact, ProtectedArtifact)
decoded, metadata = decode_protected_high(artifact, (2, 3), values)
np.testing.assert_array_equal(decoded.view(np.uint16) >> 8, values.view(np.uint16) >> 8)
assert metadata["decoded_high_exact"] is True
assert metadata["high_mode"] == metadata["low_mode"] == "L"
assert metadata["dimensions"] == [2, 3]
assert metadata["combined_payload_bytes"] == len(artifact.payload)
assert metadata["combined_payload_sha256"]
with Image.open(BytesIO(artifact.low_codec_bytes)) as low_image:
assert metadata["low_mode"] == low_image.mode
assert metadata["high_encoded_bytes"] == len(artifact.high_png_bytes)
assert metadata["low_encoded_bytes"] == len(artifact.low_codec_bytes)
assert metadata["high_encoded_sha256"]
assert metadata["low_encoded_sha256"]
assert metadata["decoded_low_sha256"]
assert metadata["high_dimensions"] == metadata["low_dimensions"] == [2, 3]
@pytest.mark.parametrize("mode", ["RGB", "P"])
def test_protected_high_rejects_non_l_high_plane(mode: str) -> None:
values = np.array([0.0, 1.5, -2.25, 3.125, -4.5, 5.0], dtype=np.float16)
artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"])
high = np.asarray(Image.open(BytesIO(artifact.high_png_bytes)).convert("L"), dtype=np.uint8)
stream = BytesIO()
tampered_image = np.stack([high] * 3, axis=-1) if mode == "RGB" else high
Image.fromarray(tampered_image, mode).save(stream, format="PNG")
tampered = ProtectedArtifact(stream.getvalue(), artifact.low_codec_bytes, artifact.low_codec)
with pytest.raises(ValueError, match="high plane mode"):
decode_protected_high(tampered, (2, 3), values)
def test_protected_high_rejects_high_shape_tamper() -> None:
values = np.arange(6, dtype=np.float16)
artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"])
stream = BytesIO()
Image.fromarray(np.zeros((1, 6), dtype=np.uint8), "L").save(stream, format="PNG")
tampered = ProtectedArtifact(stream.getvalue(), artifact.low_codec_bytes, artifact.low_codec)
with pytest.raises(ValueError, match="dimensions"):
decode_protected_high(tampered, (2, 3), values)
def test_protected_high_zero_pads_to_capacity_and_checks_full_high_plane() -> None:
values = np.array([1.5, -2.25, 3.125], dtype=np.float16)
artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"])
decoded, metadata = decode_protected_high(artifact, (2, 3), values)
expected_high = np.zeros(6, dtype=np.uint16)
expected_high[:3] = values.view(np.uint16)
np.testing.assert_array_equal(decoded.view(np.uint16) >> 8, expected_high >> 8)
assert decoded.size == 6
assert metadata["source_value_count"] == 3
with pytest.raises(ValueError, match="capacity"):
encode_protected_high(np.zeros(7, dtype=np.float16), (2, 3), CODEC_SPECS["jpeg_q80"])
def test_zero_repair_changes_only_nonfinite_fp16_words() -> None:
raw = np.array([0.0, -0.0, np.nan, np.inf, -np.inf, 2.0], dtype=np.float16)
result = repair_nonfinite_zero(raw, {"layer": (0, len(raw))})
assert np.isfinite(result.values).all()
np.testing.assert_array_equal(result.values.view(np.uint16)[[0, 1, 5]], raw.view(np.uint16)[[0, 1, 5]])
assert result.repaired_value_count == 3
def test_jxl_failure_preserves_subprocess_diagnostics(monkeypatch: pytest.MonkeyPatch) -> None:
import pixelmodel_robustness.cross_codec as module
def failed_run(*args: object, **kwargs: object) -> object:
class Completed:
returncode = 7
stdout = "ImageMagick 7.1.2"
stderr = "libjxl unavailable"
failed_run.args = args # type: ignore[attr-defined]
failed_run.kwargs = kwargs # type: ignore[attr-defined]
return Completed()
monkeypatch.setattr(module.subprocess, "run", failed_run)
with pytest.raises(JxlCodecError, match="returncode=7") as error:
module._jxl_run(["/tmp/in.png", "/tmp/out.jxl"])
assert error.value.stderr == "libjxl unavailable"
assert error.value.argv == ("/etc/profiles/per-user/scratchbrulee/bin/magick", "/tmp/in.png", "/tmp/out.jxl")
assert failed_run.kwargs["shell"] is False # type: ignore[attr-defined]
def test_jxl_preflight_reports_version_and_argv_without_silent_fallback() -> None:
result = {item.name: item for item in preflight_codecs()}["jxl_d1"]
assert isinstance(result.available, bool)
if result.available:
assert result.version
assert result.argv
assert result.metadata["quality"] == 90
assert result.metadata["libjxl_distance"] == 1.0
assert result.metadata["probe_q90_bytes"] > 0
assert result.metadata["probe_q50_bytes"] > 0
assert result.metadata["probe_q90_sha256"] != result.metadata["probe_q50_sha256"]
assert result.metadata["probe_dimensions"] == (8, 8)
assert result.metadata["probe_q50_dimensions"] == (8, 8)
assert "ImageMagick" in result.version
assert result.metadata["imagemagick_full_version"] == result.version
assert result.metadata["libjxl_delegate_version"] == "0.11.2"
assert result.metadata["argv_template"] == (
"/etc/profiles/per-user/scratchbrulee/bin/magick", "-quality", "90", "<source>", "<target>"
)
with pytest.raises(TypeError):
result.metadata["argv_template"][0] = "tampered" # type: ignore[index]
else:
assert result.reason
def test_encoded_bytes_are_readable_without_temp_artifact() -> None:
payload = encode_rgb(rgb_image(), CODEC_SPECS["png"])
with Image.open(BytesIO(payload)) as image:
assert image.format == "PNG"
assert isinstance(payload, bytes)
def test_zero_repair_does_not_mutate_and_preserves_all_finite_fp16_bits() -> None:
raw = np.array([0.0, -0.0, np.nextafter(np.float16(0), np.float16(1)), np.finfo(np.float16).max, np.nan], dtype=np.float16)
before = raw.view(np.uint16).copy()
result = repair_nonfinite_zero(raw, {"layer": (0, len(raw))})
np.testing.assert_array_equal(raw.view(np.uint16), before)
np.testing.assert_array_equal(result.values.view(np.uint16)[:4], before[:4])
def test_preflight_diagnostics_are_structured_for_jxl_failure(monkeypatch: pytest.MonkeyPatch) -> None:
import pixelmodel_robustness.cross_codec as module
def failed_run(*args: object, **kwargs: object) -> object:
return type("Completed", (), {"returncode": 9, "stdout": "version out", "stderr": "format err"})()
monkeypatch.setattr(module.subprocess, "run", failed_run)
result = module._jxl_version()
assert result.available is False
assert result.diagnostics == {
"argv": (module.MAGICK_PATH, "-version"), "stdout": "version out", "stderr": "format err", "returncode": 9,
}
def test_non_jxl_encode_does_not_run_global_or_jxl_preflight(monkeypatch: pytest.MonkeyPatch) -> None:
import pixelmodel_robustness.cross_codec as module
monkeypatch.setattr(module, "preflight_codecs", lambda: (_ for _ in ()).throw(AssertionError("global preflight")))
monkeypatch.setattr(module, "_jxl_version", lambda: (_ for _ in ()).throw(AssertionError("JXL probe")))
payload = encode_rgb(rgb_image(), CODEC_SPECS["png"])
assert payload.startswith(b"\x89PNG")
def test_avif_raw_decode_is_rgb_when_available() -> None:
status = {item.name: item for item in preflight_codecs()}["avif_q70"]
if not status.available:
pytest.skip(status.reason or "AVIF unavailable")
decoded = decode_rgb(encode_rgb(rgb_image(), CODEC_SPECS["avif_q70"]), CODEC_SPECS["avif_q70"])
assert decoded.mode == "RGB"
def test_jxl_preflight_rejects_unparseable_libjxl_version(monkeypatch: pytest.MonkeyPatch) -> None:
import pixelmodel_robustness.cross_codec as module
def fake_run(args: list[str]) -> object:
if args == ["-version"]:
return type("Completed", (), {"returncode": 0, "stdout": "ImageMagick Delegates: jxl", "stderr": ""})()
if args == ["-list", "format"]:
return type("Completed", (), {"returncode": 8, "stdout": "format out", "stderr": "format err"})()
raise AssertionError(args)
monkeypatch.setattr(module, "_jxl_run", fake_run)
result = module._jxl_version()
assert result.available is False
assert result.reason
assert result.diagnostics == {
"argv": (module.MAGICK_PATH, "-list", "format"), "stdout": "format out", "stderr": "format err", "returncode": 8,
}
def test_protected_high_rejects_l_mode_non_png_formats() -> None:
values = np.arange(6, dtype=np.float16)
artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"])
for image_format in ("BMP", "TIFF"):
stream = BytesIO()
Image.fromarray(np.zeros((2, 3), dtype=np.uint8), "L").save(stream, format=image_format)
tampered = ProtectedArtifact(stream.getvalue(), artifact.low_codec_bytes, artifact.low_codec)
with pytest.raises(ValueError, match="format"):
decode_protected_high(tampered, (2, 3), values)
def test_protected_low_codec_has_immutable_lossy_allowlist() -> None:
import pixelmodel_robustness.cross_codec as module
assert module.LOSSY_CODEC_NAMES == ("jpeg_q100", "jpeg_q80", "webp_q80", "avif_q70", "jxl_d1")
values = np.arange(6, dtype=np.float16)
for name in ("png", "webp_lossless"):
with pytest.raises(ValueError, match="lossy"):
encode_protected_high(values, (2, 3), CODEC_SPECS[name])
artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"])
with pytest.raises(ValueError, match="lossy"):
decode_protected_high(replace(artifact, low_codec="png"), (2, 3), values)
with pytest.raises(TypeError):
module.LOSSY_CODEC_NAMES[0] = "png" # type: ignore[index]
def test_jxl_probe_failure_preserves_probe_diagnostics(monkeypatch: pytest.MonkeyPatch) -> None:
import pixelmodel_robustness.cross_codec as module
def fake_run(args: list[str]) -> object:
if args == ["-version"]:
return type("Completed", (), {"returncode": 0, "stdout": "ImageMagick Delegates: jxl", "stderr": "version err"})()
if args == ["-list", "format"]:
return type("Completed", (), {"returncode": 0, "stdout": "JXL* rw+ JPEG XL (libjxl 0.11.2)", "stderr": "format err"})()
raise AssertionError(args)
expected = module.JxlCodecError(
"probe failed", argv=[module.MAGICK_PATH, "-quality", "90", "source", "target"],
stdout="probe out", stderr="probe err", returncode=13,
)
monkeypatch.setattr(module, "_jxl_run", fake_run)
monkeypatch.setattr(module, "_jxl_encode", lambda *args, **kwargs: (_ for _ in ()).throw(expected))
result = module._jxl_version()
assert result.available is False
assert result.diagnostics["probe"] == {
"argv": expected.argv, "stdout": expected.stdout, "stderr": expected.stderr, "returncode": expected.returncode,
}
def test_preflight_diagnostics_are_immutable_for_pillow_results() -> None:
result = preflight_codecs()[0]
with pytest.raises(TypeError):
result.diagnostics["changed"] = True # type: ignore[index]
def test_condition_registry_has_exact_full_semantics() -> None:
expected = (
("png_baseline", "png", "raw", None, False),
("webp_lossless", "webp_lossless", "raw", None, False),
("jpeg_q100_raw", "jpeg_q100", "raw", None, False),
("jpeg_q100_repair_zero", "jpeg_q100", "repair_zero", "zero", False),
("jpeg_q100_protected_high", "jpeg_q100", "protected_high", None, True),
("jpeg_q80_raw", "jpeg_q80", "raw", None, False),
("jpeg_q80_repair_zero", "jpeg_q80", "repair_zero", "zero", False),
("jpeg_q80_protected_high", "jpeg_q80", "protected_high", None, True),
("webp_q80_raw", "webp_q80", "raw", None, False),
("webp_q80_repair_zero", "webp_q80", "repair_zero", "zero", False),
("webp_q80_protected_high", "webp_q80", "protected_high", None, True),
("avif_q70_raw", "avif_q70", "raw", None, False),
("avif_q70_repair_zero", "avif_q70", "repair_zero", "zero", False),
("avif_q70_protected_high", "avif_q70", "protected_high", None, True),
("jxl_d1_raw", "jxl_d1", "raw", None, False),
("jxl_d1_repair_zero", "jxl_d1", "repair_zero", "zero", False),
("jxl_d1_protected_high", "jxl_d1", "protected_high", None, True),
)
assert tuple((e.condition_id, e.codec, e.mode, e.repair, e.protected) for e in CONDITION_REGISTRY.values()) == expected
def test_protected_encode_decode_do_not_mutate_source_bits() -> None:
values = np.array([0.0, -0.0, 1.5, -2.25, 3.125, -4.5], dtype=np.float16)
before = values.view(np.uint16).copy()
artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"])
decode_protected_high(artifact, (2, 3), values)
np.testing.assert_array_equal(values.view(np.uint16), before)
def test_expected_formats_and_forged_codec_spec_are_binding() -> None:
import pixelmodel_robustness.cross_codec as module
expected = {
"png": "PNG", "webp_lossless": "WEBP", "jpeg_q100": "JPEG", "jpeg_q80": "JPEG",
"webp_q80": "WEBP", "avif_q70": "AVIF", "jxl_d1": "JXL",
}
assert dict(module.EXPECTED_FORMAT_BY_CODEC) == expected
with pytest.raises(TypeError):
module.EXPECTED_FORMAT_BY_CODEC["png"] = "JPEG" # type: ignore[index]
forged = replace(CODEC_SPECS["jpeg_q80"], format="PNG")
with pytest.raises(ValueError, match="semantic"):
encode_protected_high(np.arange(6, dtype=np.float16), (2, 3), forged)
@pytest.mark.parametrize("codec", ("jpeg_q100", "jpeg_q80", "webp_q80", "avif_q70"))
def test_non_jxl_decode_l_plane_rejects_payload_format_mismatch(codec: str) -> None:
png_payload = encode_l_plane(gray_plane(), CODEC_SPECS["png"])
with pytest.raises(ValueError, match="format"):
decode_l_plane(png_payload, CODEC_SPECS[codec])
@pytest.mark.parametrize("codec", ("jpeg_q100", "jpeg_q80", "webp_q80", "avif_q70", "jxl_d1"))
def test_protected_decode_rejects_png_bytes_for_every_lossy_codec(codec: str) -> None:
values = np.arange(6, dtype=np.float16)
artifact = encode_protected_high(values, (2, 3), CODEC_SPECS["jpeg_q80"])
png_payload = encode_l_plane(np.zeros((2, 3), dtype=np.uint8), CODEC_SPECS["png"])
tampered = replace(artifact, low_codec=codec, low_codec_bytes=png_payload)
with pytest.raises((ValueError, JxlCodecError)):
decode_protected_high(tampered, (2, 3), values)
@pytest.mark.parametrize("codec", ("jpeg_q100", "jpeg_q80", "webp_q80", "avif_q70", "jxl_d1"))
def test_decode_rgb_rejects_png_payload_for_every_lossy_codec(codec: str) -> None:
png_payload = encode_rgb(rgb_image(), CODEC_SPECS["png"])
with pytest.raises((ValueError, JxlCodecError)):
decode_rgb(png_payload, CODEC_SPECS[codec])
def test_verified_preflight_token_skips_jxl_probe(monkeypatch: pytest.MonkeyPatch) -> None:
import pixelmodel_robustness.cross_codec as module
token = {"jxl_d1": module.CodecPreflight("jxl_d1", True, "fixture", None, ("fixture",), {})}
monkeypatch.setattr(module, "_jxl_version", lambda: (_ for _ in ()).throw(AssertionError("probe bypassed")))
monkeypatch.setattr(module, "_jxl_encode", lambda image: b"jxl-payload")
monkeypatch.setattr(module, "_jxl_decode", lambda payload: Image.new("RGB", (2, 2)))
assert module.encode_rgb(rgb_image(), CODEC_SPECS["jxl_d1"], verified_preflight=token) == b"jxl-payload"
assert module.decode_rgb(b"jxl-payload", CODEC_SPECS["jxl_d1"], verified_preflight=token).mode == "RGB"
def test_verified_preflight_token_rejects_missing_or_unavailable_codec() -> None:
import pixelmodel_robustness.cross_codec as module
with pytest.raises(RuntimeError, match="unavailable"):
module.encode_rgb(rgb_image(), CODEC_SPECS["jxl_d1"], verified_preflight={})
token = {"jxl_d1": module.CodecPreflight("jxl_d1", False, None, "missing", (), {})}
with pytest.raises(RuntimeError, match="missing"):
module.encode_rgb(rgb_image(), CODEC_SPECS["jxl_d1"], verified_preflight=token)
def test_decode_uses_canonical_format_for_forged_and_unknown_specs() -> None:
png_payload = encode_rgb(rgb_image(), CODEC_SPECS["png"])
forged = replace(CODEC_SPECS["jpeg_q80"], format="PNG")
unknown = CodecSpec("unknown_codec", "PNG", None, {}, {})
for spec in (forged, unknown):
with pytest.raises(ValueError, match="canonical|unknown"):
decode_rgb(png_payload, spec)
with pytest.raises(ValueError, match="canonical|unknown"):
decode_l_plane(png_payload, spec)