File size: 11,561 Bytes
901a5f5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
"""JPEG repair Task 1-2の契約テスト。"""

from __future__ import annotations

from io import BytesIO

import numpy as np
import pytest
from PIL import Image

from pixelmodel_robustness.jpeg_repair import (
    CLIP_STRATEGIES,
    CONDITION_IDS,
    build_jpeg_registry,
    clip_candidate,
    decode_jpeg_rgb,
    decode_protected,
    diagnostics,
    encode_jpeg_q100,
    encode_protected,
    materialize_fp16_png,
    repair_nonfinite,
    semantic_hash,
    validate_layer_offsets,
)


def _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 test_registry_is_exact_ordered_and_semantically_hashed() -> None:
    registry = build_jpeg_registry()
    assert tuple(entry.condition_id for entry in registry) == CONDITION_IDS
    assert len(registry) == 18
    assert len({entry.parent_raw_sha256 for entry in registry[1:17]}) == 1
    assert semantic_hash(registry) == semantic_hash(build_jpeg_registry())
    assert tuple(entry.condition_id for entry in registry) == CONDITION_IDS


def test_jpeg_q100_source_is_one_reusable_decode() -> None:
    raw = encode_jpeg_q100(_image())
    first = decode_jpeg_rgb(raw)
    second = decode_jpeg_rgb(raw)
    assert raw.startswith(b"\xff\xd8")
    np.testing.assert_array_equal(first, second)
    assert first.shape == (2, 2, 3)


def test_fp16_flat_materialization_preserves_bits_and_png_contract() -> None:
    values = np.array([0.0, -0.0, 1.5, -2.25, np.float16("inf")], dtype=np.float16)
    png = materialize_fp16_png(values)
    pixels = np.asarray(Image.open(BytesIO(png)).convert("RGB"), dtype=np.uint8).reshape(-1, 3)
    np.testing.assert_array_equal((pixels[: len(values), 0].astype(np.uint16) << 8) | pixels[: len(values), 1], values.view(np.uint16))
    assert pixels[:, 2].tolist() == [0] * len(values)


def test_finite_repair_materialize_decode_preserves_original_uint16_bits() -> None:
    original = np.array([0.0, -0.0, 1.5, -2.25, 3.125, -4.5], dtype=np.float16)
    repaired = repair_nonfinite(original, "layer_mean", {"layer": (0, len(original))}).values
    png = materialize_fp16_png(repaired)
    pixels = np.asarray(Image.open(BytesIO(png)).convert("RGB"), dtype=np.uint8).reshape(-1, 3)
    decoded_bits = (pixels[: len(original), 0].astype(np.uint16) << 8) | pixels[: len(original), 1].astype(np.uint16)
    np.testing.assert_array_equal(decoded_bits, original.view(np.uint16))


def test_protected_high_png_is_exact_low_jpeg_only_changes() -> None:
    values = np.array([0.0, -0.0, 1.5, -2.25, 3.125, -4.5], dtype=np.float16)
    artifact = encode_protected(values, (2, 3))
    decoded, metadata = decode_protected(artifact.payload, (2, 3))
    np.testing.assert_array_equal(decoded.view(np.uint16), values.view(np.uint16))
    high = np.asarray(Image.open(BytesIO(artifact.high_png_bytes)).convert("L"), dtype=np.uint8)
    np.testing.assert_array_equal(high.reshape(-1), (values.view(np.uint16) >> 8).astype(np.uint8))
    assert metadata["payload_size_bytes"] == len(artifact.high_png_bytes) + len(artifact.low_jpeg_bytes)
    assert metadata["high_mode"] == metadata["low_mode"] == "L"


def test_repair_replaces_only_nonfinite_and_reports_signed_zero_bits() -> None:
    raw = np.array([0.0, -0.0, np.nan, np.inf, -np.inf, 2.0], dtype=np.float16)
    result = repair_nonfinite(raw, "layer_mean", {"layer": (0, len(raw))})
    assert result.repaired_value_count == 3
    assert result.nan_count == result.posinf_count == result.neginf_count == 1
    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]])


def test_zero_repair_accepts_all_nonfinite_layer_and_preserves_finite_bits() -> None:
    raw = np.array([np.nan, np.inf, -np.inf, 1.5, -0.0, np.nan], dtype=np.float16)
    original_bits = raw.view(np.uint16).copy()

    result = repair_nonfinite(raw, "zero", {"all_nonfinite": (0, 3), "mixed": (3, 6)})

    assert np.isfinite(result.values).all()
    assert result.repaired_value_count == 4
    assert result.nan_count == 2
    assert result.posinf_count == 1
    assert result.neginf_count == 1
    assert [record.repaired_count for record in result.layer_records] == [3, 1]
    assert [record.nan_count for record in result.layer_records] == [1, 1]
    assert [record.posinf_count for record in result.layer_records] == [1, 0]
    assert [record.neginf_count for record in result.layer_records] == [1, 0]
    finite = np.isfinite(raw)
    np.testing.assert_array_equal(result.values.view(np.uint16)[finite], original_bits[finite])


def test_repair_rejects_empty_finite_layer() -> None:
    raw = np.array([np.nan, np.inf], dtype=np.float16)
    with pytest.raises(ValueError, match="finite values"):
        repair_nonfinite(raw, "layer_median", {"empty": (0, 2)})


@pytest.mark.parametrize("method", ["layer_median", "layer_mean"])
def test_repair_rejects_empty_finite_layer_for_statistical_methods(method: str) -> None:
    raw = np.array([np.nan, np.inf, -np.inf], dtype=np.float16)
    with pytest.raises(ValueError, match="finite values"):
        repair_nonfinite(raw, method, {"empty": (0, 3)})


@pytest.mark.parametrize("strategy", CLIP_STRATEGIES)
def test_clip_strategies_use_candidate_only_and_record_changes(strategy: str) -> None:
    candidate = np.array([-10.0, -1.0, 0.0, 1.0, 10.0], dtype=np.float16)
    result = clip_candidate(candidate, {"layer": (0, 5)}, strategy)
    assert result.values.dtype == np.float16
    assert result.layers[0].strategy == strategy
    assert result.layers[0].changed_count == int(np.count_nonzero(result.values.view(np.uint16) != candidate.view(np.uint16)))
    assert np.isfinite(result.values).all()
    assert result.layers[0].lower <= result.layers[0].upper


def test_clip_uses_population_std_and_linear_percentile() -> None:
    candidate = np.array([0.0, 1.0, 2.0, 100.0], dtype=np.float16)
    result = clip_candidate(candidate, {"layer": (0, 4)}, "layer_mean_std_6")
    expected = float(np.mean(candidate.astype(np.float64)) + 6 * np.std(candidate.astype(np.float64), ddof=0))
    assert result.layers[0].upper == pytest.approx(expected)
    percentile = clip_candidate(candidate, {"layer": (0, 4)}, "layer_pct_0.1_99.9")
    expected_percentile = float(np.percentile(candidate.astype(np.float64), 99.9, method="linear"))
    assert percentile.layers[0].upper == pytest.approx(expected_percentile)


def test_clip_degenerate_and_threshold_equal_are_unchanged() -> None:
    candidate = np.array([2.0, 2.0], dtype=np.float16)
    result = clip_candidate(candidate, {"one": (0, 2)}, "layer_mean_std_5")
    np.testing.assert_array_equal(result.values.view(np.uint16), candidate.view(np.uint16))
    assert result.layers[0].changed_count == 0


def test_fp16_bit_diagnostics_and_bounded_histogram() -> None:
    baseline = np.array([1.0, -1.0, 0.0, 1.0], dtype=np.float16)
    candidate = np.array([20.0, -1.0, np.float16("inf"), np.float16("nan")], dtype=np.float16)
    result = diagnostics(baseline, candidate, {"layer": (0, 4)})
    assert result["sign_mismatch_count"] == 0
    assert result["exponent_mismatch_count"] == 3
    assert result["mantissa_mismatch_count"] == 2
    assert result["exponent_to31_count"] == 2
    assert result["finite_extreme_outlier_count"] == 1
    assert set(result["histograms"]) == {"baseline", "candidate", "delta"}
    assert all(len(item["counts"]) <= 64 for item in result["histograms"].values())


def test_registry_parent_identity_is_only_shared_by_raw_jpeg_conditions() -> None:
    raw = encode_jpeg_q100(_image())
    registry = build_jpeg_registry(raw)
    assert len({entry.parent_raw_sha256 for entry in registry[1:17]}) == 1
    assert registry[0].parent_raw_sha256 != registry[1].parent_raw_sha256
    assert registry[17].parent_raw_sha256 != registry[1].parent_raw_sha256
    assert registry[0].parent_raw_sha256 != registry[17].parent_raw_sha256


def test_layer_offsets_are_contiguous_and_fail_closed() -> None:
    assert validate_layer_offsets({"a": (0, 2), "b": (2, 4)}, 4) == {"a": (0, 2), "b": (2, 4)}
    with pytest.raises(ValueError, match="contiguous"):
        validate_layer_offsets({"a": (0, 2), "b": (3, 4)}, 4)
    with pytest.raises(ValueError, match="overlap"):
        validate_layer_offsets({"a": (0, 3), "b": (2, 4)}, 4)


def test_diagnostics_extreme_uses_layer_baseline_max_and_reports_finite_stats() -> None:
    baseline = np.array([1.0, 2.0, 0.0, 0.0], dtype=np.float16)
    candidate = np.array([25.0, 2.0, 1.0, 0.0], dtype=np.float16)
    result = diagnostics(baseline, candidate, {"first": (0, 2), "zero": (2, 4)})
    assert result["finite_extreme_outlier_count"] == 2
    assert result["finite_count"] == 4
    assert result["max_abs_weight"] == pytest.approx(25.0)
    assert result["nan_count"] == result["posinf_count"] == result["neginf_count"] == 0
    assert result["layers"][0]["finite_extreme_outlier_count"] == 1
    assert result["layers"][1]["finite_extreme_outlier_count"] == 1
    assert result["layers"][1]["max_abs_weight"] == pytest.approx(1.0)


def test_repair_has_layer_records_and_bounded_repaired_histogram() -> None:
    raw = np.array([np.nan, np.inf, -np.inf, 1.0], dtype=np.float16)
    result = repair_nonfinite(raw, "zero", {"layer": (0, 4)})
    record = result.layer_records[0]
    assert record.layer == "layer"
    assert record.repaired_count == 3
    assert record.nan_count == record.posinf_count == record.neginf_count == 1
    assert sum(result.repaired_histogram["counts"]) == 3
    assert len(result.repaired_histogram["counts"]) <= 64


def test_clip_has_total_count_and_changed_value_histograms() -> None:
    candidate = np.array([-100.0, -1.0, 0.0, 1.0, 100.0], dtype=np.float16)
    result = clip_candidate(candidate, {"layer": (0, 5)}, "layer_mean_std_5")
    assert result.total_clipped_count == result.layers[0].changed_count
    assert sum(result.changed_pre_histogram["counts"]) == result.total_clipped_count
    assert sum(result.changed_post_histogram["counts"]) == result.total_clipped_count
    assert sum(result.changed_delta_histogram["counts"]) == result.total_clipped_count


def test_protected_metadata_reports_decoded_planes_and_low_changes() -> None:
    rng = np.random.default_rng(20260727)
    values = rng.integers(0, 65536, size=32 * 32, dtype=np.uint16).view(np.float16)
    artifact = encode_protected(values, (32, 32))
    decoded, metadata = decode_protected(artifact.payload, (32, 32), source_values=values)
    assert metadata["decoded_high_exact"] is True
    assert metadata["decoded_high_sha256"]
    assert metadata["decoded_low_sha256"]
    assert metadata["decoded_low_changed_count"] > 0
    assert 0.0 < metadata["decoded_low_changed_rate"] <= 1.0
    assert metadata["pillow_version"]
    assert metadata["combined_payload_exact"] is True
    assert metadata["high_mode"] == metadata["low_mode"] == "L"
    assert metadata["high_dimensions"] == metadata["low_dimensions"] == [32, 32]
    assert metadata["combined_payload_sha256"]
    assert metadata["combined_payload_bytes"] == metadata["high_bytes"] + metadata["low_bytes"]
    assert "jpeg_codec_version" in metadata
    assert decoded.shape == values.shape


def test_combined_payload_parser_rejects_invalid_png_chunk_bounds() -> None:
    values = np.arange(16, dtype=np.float16)
    artifact = encode_protected(values, (4, 4))
    broken = artifact.payload[:16] + b"\xff"
    with pytest.raises(ValueError, match="PNG"):
        decode_protected(broken, (4, 4))