diff --git a/.gitattributes b/.gitattributes index 88462dc3023ac34945130f2d1495f2a11411877d..bf8bfbfb2eaff3712d5b6937b15f64b98c79471f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,99 @@ weights/**/*.jpg filter=lfs diff=lfs merge=lfs -text weights/**/*.webp filter=lfs diff=lfs merge=lfs -text weights/**/*.avif filter=lfs diff=lfs merge=lfs -text weights/**/*.jxl filter=lfs diff=lfs merge=lfs -text +samples/jpeg_q100_repair_zero/pm4-003-46e10145.png filter=lfs diff=lfs merge=lfs -text +samples/jpeg_q100_repair_zero/pm4-091-c428ceb2.png filter=lfs diff=lfs merge=lfs -text +samples/jpeg_q100_repair_zero/pm4-020-d8e94e65.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-043-cde8ad90.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-033-b5e58e7f.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-013-d58e7b01.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-098-b03251b5.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-095-13e2d4a2.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-036-71932e79.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-010-97841ad3.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-077-7fa689ad.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-030-2b63f9ba.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-050-c3e45c01.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-049-85579bcd.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-026-ad193361.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-048-e270315e.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-012-98171122.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-052-03bbf4ac.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-045-25fa16bb.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-044-93deb087.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-021-2da79c09.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-070-691aa003.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-018-4ea91357.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-061-3e32cfca.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-008-54647723.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-037-9515174e.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-029-95eb9f34.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-072-4a0cff0c.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-031-844a8814.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-034-a383c38f.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-066-da57563c.png filter=lfs diff=lfs merge=lfs -text +samples/avif_q70_protected_high/pm4-004-ab14afb8.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-081-5b17a851.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-055-ac372623.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-079-5e8f0ede.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-069-cacdab19.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-094-dd71e13d.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-038-335a27fa.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-032-42ee9a0f.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-022-b8b11cb8.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-086-90e23232.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-007-5003533d.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-028-b56d212e.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-085-84317ff5.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-042-f7a87ffa.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-076-a577a15d.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-024-56de999a.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-017-7f335742.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-071-b9fe92b0.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-011-c900b313.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-074-796fd1d3.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-019-f7070d02.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-006-44a2087e.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-065-1cab116c.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-041-7ac2f16a.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-005-5e9b5a50.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-083-02dc5c51.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-068-fbe210a2.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-001-cca08f5b.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-009-e51903e1.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-092-cc02ddf9.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-064-2264c7eb.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-073-6ec086e0.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-099-d947f488.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-063-a4e09271.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-047-fc09cfd4.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-100-5e269fc4.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-087-34bf26a1.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-039-066bcab1.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-046-bec80a2b.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-027-becf10ea.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-023-0eb7f66d.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-035-f3b99568.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-084-06ed218a.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-067-ef873e98.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-040-eca2f1c2.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-057-8ac12393.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-059-7d5da4fb.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-056-ee005598.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-015-8fe08e47.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-014-8876ccfb.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-053-0c5b1381.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-054-92b0a67d.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-088-92a9c223.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-058-b655d991.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-025-ef6349d3.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-096-95b0203e.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-093-53b39ad0.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-062-4e57bc1c.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-091-c428ceb2.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-003-46e10145.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-043-cde8ad90.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-020-d8e94e65.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-095-13e2d4a2.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-033-b5e58e7f.png filter=lfs diff=lfs merge=lfs -text +samples/webp_lossless/pm4-013-d58e7b01.png filter=lfs diff=lfs merge=lfs -text diff --git a/samples/avif_q70_protected_high/pm4-004-ab14afb8.png b/samples/avif_q70_protected_high/pm4-004-ab14afb8.png new file mode 100644 index 0000000000000000000000000000000000000000..2e66ed32737e12181a39c2d831c59a1e95ccf842 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-004-ab14afb8.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58253941e0d8417717e284670dcdf98e32d23cae4ca2973d44169dc75a7b5fac +size 118312 diff --git a/samples/avif_q70_protected_high/pm4-008-54647723.png b/samples/avif_q70_protected_high/pm4-008-54647723.png new file mode 100644 index 0000000000000000000000000000000000000000..75ba0b656463d84abdfe9142b9cabdc3f1145cb9 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-008-54647723.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bdeb63731e4a41871fe3c9aff7190a9cb41ccb6ead0542a2654fa56fa294cdff +size 110777 diff --git a/samples/avif_q70_protected_high/pm4-010-97841ad3.png b/samples/avif_q70_protected_high/pm4-010-97841ad3.png new file mode 100644 index 0000000000000000000000000000000000000000..ebd199636577ac6161074b022c60e80767a1156c --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-010-97841ad3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6899caad76d689367fc7099d28a912669ac3b990cae50a698f28f54e6b435df +size 102313 diff --git a/samples/avif_q70_protected_high/pm4-012-98171122.png b/samples/avif_q70_protected_high/pm4-012-98171122.png new file mode 100644 index 0000000000000000000000000000000000000000..a0160fbb0c007b4fbd597fb52c4c34e2127289e6 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-012-98171122.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:899aa9d75e222a2dccd57dec48e815f5e49fc9c3f8cdfe4385761546f92bc4a1 +size 107725 diff --git a/samples/avif_q70_protected_high/pm4-013-d58e7b01.png b/samples/avif_q70_protected_high/pm4-013-d58e7b01.png new file mode 100644 index 0000000000000000000000000000000000000000..d1d6567fcec89a2556b3a676e0fdbe511a954482 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-013-d58e7b01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96a2212b4ca9f2b1bfcfb40ed6fdb7e743debcb02e8aa6b3e59fba3fbf0268c7 +size 122676 diff --git a/samples/avif_q70_protected_high/pm4-018-4ea91357.png b/samples/avif_q70_protected_high/pm4-018-4ea91357.png new file mode 100644 index 0000000000000000000000000000000000000000..86471d3f048f845c02a16bf4218b50a23a8a9352 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-018-4ea91357.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91808cc021293555b94ac6de7e16ade8c5f402712cd3e6064dfbb3acea3e516c +size 120002 diff --git a/samples/avif_q70_protected_high/pm4-021-2da79c09.png b/samples/avif_q70_protected_high/pm4-021-2da79c09.png new file mode 100644 index 0000000000000000000000000000000000000000..3653182860c1e07fffbff8227680e867fe33be73 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-021-2da79c09.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:000aca47439e97de957505104306e89bcebc558f2df2cbaffe6c16557fce6aac +size 139308 diff --git a/samples/avif_q70_protected_high/pm4-026-ad193361.png b/samples/avif_q70_protected_high/pm4-026-ad193361.png new file mode 100644 index 0000000000000000000000000000000000000000..802fecfb31d1fd86ed0ffd7f8246da2c13050992 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-026-ad193361.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:653bc3cdaee22f4133e6fe9167070c9ed9947f3aa3167a452e8e6ba305625b61 +size 103308 diff --git a/samples/avif_q70_protected_high/pm4-029-95eb9f34.png b/samples/avif_q70_protected_high/pm4-029-95eb9f34.png new file mode 100644 index 0000000000000000000000000000000000000000..5312d3e1e869004d61c0d53bbc605aaa023dd0b8 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-029-95eb9f34.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67042bdab46ff4de0eb510ad8a902bc90cfe792d555deee381612ec97db21498 +size 104968 diff --git a/samples/avif_q70_protected_high/pm4-030-2b63f9ba.png b/samples/avif_q70_protected_high/pm4-030-2b63f9ba.png new file mode 100644 index 0000000000000000000000000000000000000000..ac113a6027f566aa765a5066ca00de477493044f --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-030-2b63f9ba.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff0584a98a947b7031ada5ec9e8b23e27d057f59b6fe4cb834a4dcf0880f18ad +size 122224 diff --git a/samples/avif_q70_protected_high/pm4-031-844a8814.png b/samples/avif_q70_protected_high/pm4-031-844a8814.png new file mode 100644 index 0000000000000000000000000000000000000000..1f53e46003cfb3b10c42225999477ef27b3f040b --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-031-844a8814.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:507e99731d610cd831610b8ac0515c76522dd233cc4f3fa3ef3482f98cc59c13 +size 119116 diff --git a/samples/avif_q70_protected_high/pm4-033-b5e58e7f.png b/samples/avif_q70_protected_high/pm4-033-b5e58e7f.png new file mode 100644 index 0000000000000000000000000000000000000000..f4ca33a0c1ca5283190a5325a85eee5b623571b9 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-033-b5e58e7f.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f8ce3a535c7d5847a21242a0f4f7527294371dc342c8174c3df875ad81f406b +size 108412 diff --git a/samples/avif_q70_protected_high/pm4-034-a383c38f.png b/samples/avif_q70_protected_high/pm4-034-a383c38f.png new file mode 100644 index 0000000000000000000000000000000000000000..a961f304222ca67451351c5c4f938b20e64033e4 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-034-a383c38f.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06a9c4ebd0f9c5791a694205c15675cf58ac5bead4888f4dfaf7111d1590fc28 +size 110164 diff --git a/samples/avif_q70_protected_high/pm4-036-71932e79.png b/samples/avif_q70_protected_high/pm4-036-71932e79.png new file mode 100644 index 0000000000000000000000000000000000000000..abb140f2e2e3b35d59e1ec5c885a7e90ceaa15f4 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-036-71932e79.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2cc9d06e292a7d0e7195f5b9d078e285fc0b71bf7c318f83340d6ff655d6f1d +size 112526 diff --git a/samples/avif_q70_protected_high/pm4-037-9515174e.png b/samples/avif_q70_protected_high/pm4-037-9515174e.png new file mode 100644 index 0000000000000000000000000000000000000000..86b0c010552a5ad89bbe9deb24ca4994731bf1c0 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-037-9515174e.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf669387c5d606214a296effd6bf5660bd5f8d6f4e4750950c5bd5a059e8a1e0 +size 120626 diff --git a/samples/avif_q70_protected_high/pm4-043-cde8ad90.png b/samples/avif_q70_protected_high/pm4-043-cde8ad90.png new file mode 100644 index 0000000000000000000000000000000000000000..92a5d355ebc808e1be29b510479a19b8ff94e411 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-043-cde8ad90.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f030cd237fc2c212d3eb60cf689d188f02b1eef845c1e26d2defdbc1e776c955 +size 107954 diff --git a/samples/avif_q70_protected_high/pm4-044-93deb087.png b/samples/avif_q70_protected_high/pm4-044-93deb087.png new file mode 100644 index 0000000000000000000000000000000000000000..f49304af083dd2a6056c8f0dd5a22ba34a0820bf --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-044-93deb087.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a9935b1f47a672e68123d3aa718ab99aae0c94b447e7cc9198abbd233336188 +size 112736 diff --git a/samples/avif_q70_protected_high/pm4-045-25fa16bb.png b/samples/avif_q70_protected_high/pm4-045-25fa16bb.png new file mode 100644 index 0000000000000000000000000000000000000000..664051c5ef37bcb83378bde69941cc7f13006d7f --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-045-25fa16bb.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eff399eb69b19b4ee1e4b4b385cdcfec2592a4dcbb246dfbf191cf56be841578 +size 113222 diff --git a/samples/avif_q70_protected_high/pm4-048-e270315e.png b/samples/avif_q70_protected_high/pm4-048-e270315e.png new file mode 100644 index 0000000000000000000000000000000000000000..6a6933ddbf060d42e1f14b890cff519cc0c0da23 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-048-e270315e.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ac19a306007415f2c90acc4e612047bff5de9edeb1fdc2029fc00c3ebd6bfbb +size 120196 diff --git a/samples/avif_q70_protected_high/pm4-049-85579bcd.png b/samples/avif_q70_protected_high/pm4-049-85579bcd.png new file mode 100644 index 0000000000000000000000000000000000000000..3c55c531c1c0b8aa4667162ad02a5db2b7ef3e94 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-049-85579bcd.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5c0e179e7c91fd1c65d02242b391a7747c2cbe34eae62f5eccdf597462f1758 +size 114132 diff --git a/samples/avif_q70_protected_high/pm4-050-c3e45c01.png b/samples/avif_q70_protected_high/pm4-050-c3e45c01.png new file mode 100644 index 0000000000000000000000000000000000000000..bb7d51c6ea580452a0b8224c662d726bc745b171 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-050-c3e45c01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5593f8a02cb27fa6cc9c7404d51d7d9261ed9e124db48e1b5f95161a767ddc04 +size 117182 diff --git a/samples/avif_q70_protected_high/pm4-052-03bbf4ac.png b/samples/avif_q70_protected_high/pm4-052-03bbf4ac.png new file mode 100644 index 0000000000000000000000000000000000000000..72922783e47e74c9dbd6abc76c1f1f5635d87f77 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-052-03bbf4ac.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:525c799640436576a61859adb54a8988a7e418197032b4483b8d4926126c59e1 +size 110172 diff --git a/samples/avif_q70_protected_high/pm4-061-3e32cfca.png b/samples/avif_q70_protected_high/pm4-061-3e32cfca.png new file mode 100644 index 0000000000000000000000000000000000000000..976b80480b5b3d0efca96cdcf2af76234513c981 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-061-3e32cfca.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32efb98b58cd7850ff90294f22dce6a88c34e1340a78d86e1cb9a06241b5842c +size 110147 diff --git a/samples/avif_q70_protected_high/pm4-066-da57563c.png b/samples/avif_q70_protected_high/pm4-066-da57563c.png new file mode 100644 index 0000000000000000000000000000000000000000..cbcfd692fab5f50c9c09da2c0291e62303a1ae29 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-066-da57563c.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b56da02fe22df4d94772d47e85badd2f5fc187341cf5e243d39bd4832d7e7b6 +size 102940 diff --git a/samples/avif_q70_protected_high/pm4-070-691aa003.png b/samples/avif_q70_protected_high/pm4-070-691aa003.png new file mode 100644 index 0000000000000000000000000000000000000000..350cc5df48cd103e63caddcb3cbf9d32f6ef8542 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-070-691aa003.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7deb3234b6f6ca71594970e48623839dac6e103160b68f16468730e21e0478cd +size 119184 diff --git a/samples/avif_q70_protected_high/pm4-072-4a0cff0c.png b/samples/avif_q70_protected_high/pm4-072-4a0cff0c.png new file mode 100644 index 0000000000000000000000000000000000000000..696f5158a5c47083cf4e1baadeb7ced2f6ffb3b5 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-072-4a0cff0c.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cff756fbd9d92c4eededae09efaee17935aea73793e9775c1b327fb33c3211b5 +size 122617 diff --git a/samples/avif_q70_protected_high/pm4-077-7fa689ad.png b/samples/avif_q70_protected_high/pm4-077-7fa689ad.png new file mode 100644 index 0000000000000000000000000000000000000000..884e4acedd5adb8ef3aa4f5fc30ccd49106e640e --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-077-7fa689ad.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:865da8bc45257adf7158efeb1fb445f835f020c43f8dbb83180249bd9b096ddc +size 103245 diff --git a/samples/avif_q70_protected_high/pm4-095-13e2d4a2.png b/samples/avif_q70_protected_high/pm4-095-13e2d4a2.png new file mode 100644 index 0000000000000000000000000000000000000000..651e6dd2546c3bc5fd936b059b7b972dfde53051 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-095-13e2d4a2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c249e088f53a61d7532aa1898f36109b68d3a868ebb00ad74963446d9364f210 +size 106559 diff --git a/samples/avif_q70_protected_high/pm4-098-b03251b5.png b/samples/avif_q70_protected_high/pm4-098-b03251b5.png new file mode 100644 index 0000000000000000000000000000000000000000..f67102e4f2c388e4ba8e83e8b527f3a9bf272311 --- /dev/null +++ b/samples/avif_q70_protected_high/pm4-098-b03251b5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6414c397bd8a541ce6a12c5a2e071ad463942e5263b16a78ffaa73a2b31bfa9b +size 101249 diff --git a/samples/jpeg_q100_repair_zero/pm4-003-46e10145.png b/samples/jpeg_q100_repair_zero/pm4-003-46e10145.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb25a45860a2a3ea2e5983c249637ea21e25082 --- /dev/null +++ b/samples/jpeg_q100_repair_zero/pm4-003-46e10145.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec796f501b70c36d1f3d3b75d8657fe5848004ccc17882f7812b198818a49e77 +size 122666 diff --git a/samples/jpeg_q100_repair_zero/pm4-020-d8e94e65.png b/samples/jpeg_q100_repair_zero/pm4-020-d8e94e65.png new file mode 100644 index 0000000000000000000000000000000000000000..d67e2aef2e30f45ff7e19ecde01334fdd74b2806 --- /dev/null +++ b/samples/jpeg_q100_repair_zero/pm4-020-d8e94e65.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aecbb1e6f1c8559b86622d84759ca844437f8d316f54eff0fb5a323572f5edc8 +size 123161 diff --git a/samples/jpeg_q100_repair_zero/pm4-091-c428ceb2.png b/samples/jpeg_q100_repair_zero/pm4-091-c428ceb2.png new file mode 100644 index 0000000000000000000000000000000000000000..f7d522f54781f7f8d86392e94a0a99dccc455575 --- /dev/null +++ b/samples/jpeg_q100_repair_zero/pm4-091-c428ceb2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33b856fafb01bbe1eb0796161aeb8dec3e8f3d386abcb9f3ee8a2ef8c57f7978 +size 133939 diff --git a/samples/webp_lossless/pm4-001-cca08f5b.png b/samples/webp_lossless/pm4-001-cca08f5b.png new file mode 100644 index 0000000000000000000000000000000000000000..70e3b31bc724f1d618b2278e36bd70c3e062f045 --- /dev/null +++ b/samples/webp_lossless/pm4-001-cca08f5b.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0fcd59b963f7d0c16ca56e492e55eb470a0fa51f809e301f2bcf4551ccf2c428 +size 103098 diff --git a/samples/webp_lossless/pm4-003-46e10145.png b/samples/webp_lossless/pm4-003-46e10145.png new file mode 100644 index 0000000000000000000000000000000000000000..b359aa656f069abd7c6645c56c08389614e7bcf5 --- /dev/null +++ b/samples/webp_lossless/pm4-003-46e10145.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d601ac568438f5a53f2778809dbed56d5ae1fd818359d2bd1831faa007449a2d +size 100422 diff --git a/samples/webp_lossless/pm4-005-5e9b5a50.png b/samples/webp_lossless/pm4-005-5e9b5a50.png new file mode 100644 index 0000000000000000000000000000000000000000..200ba69f8b9177d46fc2a4c27b7ea01a330ba5fb --- /dev/null +++ b/samples/webp_lossless/pm4-005-5e9b5a50.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e73ea094ae3d9a159ede6517d767339de6e2016d2f786ec6e898412253d4a24b +size 112457 diff --git a/samples/webp_lossless/pm4-006-44a2087e.png b/samples/webp_lossless/pm4-006-44a2087e.png new file mode 100644 index 0000000000000000000000000000000000000000..fdcae243736ecd85a01cdac8bbab577c537c831e --- /dev/null +++ b/samples/webp_lossless/pm4-006-44a2087e.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e58762332507564f7402c108b89539e493ecd3a409317a60e316344f132ae00b +size 101289 diff --git a/samples/webp_lossless/pm4-007-5003533d.png b/samples/webp_lossless/pm4-007-5003533d.png new file mode 100644 index 0000000000000000000000000000000000000000..b02619a0c36ec305a56c333e8d46e7d025543f92 --- /dev/null +++ b/samples/webp_lossless/pm4-007-5003533d.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06430e40dc1a1e5fe69386bf56ffe7604528d6c4e84870708f5e43520cd3e2f0 +size 110707 diff --git a/samples/webp_lossless/pm4-009-e51903e1.png b/samples/webp_lossless/pm4-009-e51903e1.png new file mode 100644 index 0000000000000000000000000000000000000000..da666b7b880807365a3f507f2b7983d67f577416 --- /dev/null +++ b/samples/webp_lossless/pm4-009-e51903e1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a450c66b950685b8b4d29ef3120276a687afd0b1608f700e3e39e7cfe6c3715 +size 113938 diff --git a/samples/webp_lossless/pm4-011-c900b313.png b/samples/webp_lossless/pm4-011-c900b313.png new file mode 100644 index 0000000000000000000000000000000000000000..ce6ea9a4006f243910c9bf2a88a0998260bbdca1 --- /dev/null +++ b/samples/webp_lossless/pm4-011-c900b313.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e043efe30b021d155d9e85bafb584f36b7892109d8ccfd15336414d340a582a0 +size 116408 diff --git a/samples/webp_lossless/pm4-013-d58e7b01.png b/samples/webp_lossless/pm4-013-d58e7b01.png new file mode 100644 index 0000000000000000000000000000000000000000..2c40823e7be824f542bf5fe6257a79b1de548957 --- /dev/null +++ b/samples/webp_lossless/pm4-013-d58e7b01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78e6516a07d9138059d14b4ef44177992c2ccc1a87e119d0a854f5b4539fbb2f +size 122276 diff --git a/samples/webp_lossless/pm4-014-8876ccfb.png b/samples/webp_lossless/pm4-014-8876ccfb.png new file mode 100644 index 0000000000000000000000000000000000000000..728eca9446c47e4e371859030c34d1edc210d9b3 --- /dev/null +++ b/samples/webp_lossless/pm4-014-8876ccfb.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d4afd16c27dc524dcc9b17d6d12ee99cc7ea996b8bc1155a8a2182b1ff00bce +size 116836 diff --git a/samples/webp_lossless/pm4-015-8fe08e47.png b/samples/webp_lossless/pm4-015-8fe08e47.png new file mode 100644 index 0000000000000000000000000000000000000000..efd2b92f64ff2e7879672f370b66f158e1857e61 --- /dev/null +++ b/samples/webp_lossless/pm4-015-8fe08e47.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64509596c3136b38ad2f2cc41f6613060d5ecf87b248d5a1e2fa352a63be70b0 +size 108082 diff --git a/samples/webp_lossless/pm4-017-7f335742.png b/samples/webp_lossless/pm4-017-7f335742.png new file mode 100644 index 0000000000000000000000000000000000000000..239e814c9edcecb150f064f6c786f8930e2351df --- /dev/null +++ b/samples/webp_lossless/pm4-017-7f335742.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b070bbd481b06300bea2c873cdab7050d179133da077cc88c61e7edd52d41c24 +size 104878 diff --git a/samples/webp_lossless/pm4-019-f7070d02.png b/samples/webp_lossless/pm4-019-f7070d02.png new file mode 100644 index 0000000000000000000000000000000000000000..e8a81db6c345d3ec1a7a35520e317c90983ad918 --- /dev/null +++ b/samples/webp_lossless/pm4-019-f7070d02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd76611f13d144554125c9c69e59df65506ff65b40a4666d4fd46179f147ebc8 +size 123937 diff --git a/samples/webp_lossless/pm4-020-d8e94e65.png b/samples/webp_lossless/pm4-020-d8e94e65.png new file mode 100644 index 0000000000000000000000000000000000000000..27f08cc99e175c60d180a6f9b9137ab1e8836922 --- /dev/null +++ b/samples/webp_lossless/pm4-020-d8e94e65.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e9e022fbb5c6fbda46c5a2bd8016cb142d30ed121a41af02b8e21ab82fe1cbc +size 113683 diff --git a/samples/webp_lossless/pm4-022-b8b11cb8.png b/samples/webp_lossless/pm4-022-b8b11cb8.png new file mode 100644 index 0000000000000000000000000000000000000000..4cc61e059376c207662402f0366b0622f24ce9d8 --- /dev/null +++ b/samples/webp_lossless/pm4-022-b8b11cb8.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fcd0feba56d85cef6caf9cb97ce98bce01f254e15d197a16ad0978be6fb1d17 +size 114240 diff --git a/samples/webp_lossless/pm4-023-0eb7f66d.png b/samples/webp_lossless/pm4-023-0eb7f66d.png new file mode 100644 index 0000000000000000000000000000000000000000..9860da3d9d5d97dcc2a8ac8326ea55db04e566b1 --- /dev/null +++ b/samples/webp_lossless/pm4-023-0eb7f66d.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6edf2c672479518ad1d19906c5c16c80328e5dd0cd26d968704d6bfe610c7e90 +size 108902 diff --git a/samples/webp_lossless/pm4-024-56de999a.png b/samples/webp_lossless/pm4-024-56de999a.png new file mode 100644 index 0000000000000000000000000000000000000000..8db07bf14da391898f7bef8247d393a876b0fb02 --- /dev/null +++ b/samples/webp_lossless/pm4-024-56de999a.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd1de29d83b189895960c8b1d8f8fe66058036d5050b962ca639303d01468dbd +size 120111 diff --git a/samples/webp_lossless/pm4-025-ef6349d3.png b/samples/webp_lossless/pm4-025-ef6349d3.png new file mode 100644 index 0000000000000000000000000000000000000000..290e1296bf0bbd596957e858b827a1bd235200cc --- /dev/null +++ b/samples/webp_lossless/pm4-025-ef6349d3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0dc507fecfcf685d49f054cd3a2159911c3e459fbfbc0fce72a758aa7ee5269 +size 111354 diff --git a/samples/webp_lossless/pm4-027-becf10ea.png b/samples/webp_lossless/pm4-027-becf10ea.png new file mode 100644 index 0000000000000000000000000000000000000000..f8083677eff30664e18a98e15f5731a8463a858e --- /dev/null +++ b/samples/webp_lossless/pm4-027-becf10ea.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24ba9fefc62748ea1b45ba5d7c241da1bf1d2896c8f18604e68a1dda9fb4f688 +size 114797 diff --git a/samples/webp_lossless/pm4-028-b56d212e.png b/samples/webp_lossless/pm4-028-b56d212e.png new file mode 100644 index 0000000000000000000000000000000000000000..952e62161ab92d6da1c7757262568b062b7883e5 --- /dev/null +++ b/samples/webp_lossless/pm4-028-b56d212e.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af53cbdb8ba96c936b8ff88ba82019b7a2d55d34f53261e7edc31ead980d6a43 +size 112446 diff --git a/samples/webp_lossless/pm4-032-42ee9a0f.png b/samples/webp_lossless/pm4-032-42ee9a0f.png new file mode 100644 index 0000000000000000000000000000000000000000..fcda42c82dd9e25c5979d4430d41678ba3fdf906 --- /dev/null +++ b/samples/webp_lossless/pm4-032-42ee9a0f.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5bb4493c664b3c944a0a848adfbbc9f47d133f44ea3ce5533b038c9596c1057 +size 112648 diff --git a/samples/webp_lossless/pm4-033-b5e58e7f.png b/samples/webp_lossless/pm4-033-b5e58e7f.png new file mode 100644 index 0000000000000000000000000000000000000000..92e18f5b1c5da4071c6bad06e45cea6482eea707 --- /dev/null +++ b/samples/webp_lossless/pm4-033-b5e58e7f.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66f2d2287adae22b471409076aebb6c5109583f049e4f8af1ce2587f572a5074 +size 108331 diff --git a/samples/webp_lossless/pm4-035-f3b99568.png b/samples/webp_lossless/pm4-035-f3b99568.png new file mode 100644 index 0000000000000000000000000000000000000000..4b92ef559f4fd20688ae876330868539d5afda03 --- /dev/null +++ b/samples/webp_lossless/pm4-035-f3b99568.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9d488b4f9ee7207a03d52428a8fcb144f76cba3869591f9aa33db0011d25060 +size 110674 diff --git a/samples/webp_lossless/pm4-038-335a27fa.png b/samples/webp_lossless/pm4-038-335a27fa.png new file mode 100644 index 0000000000000000000000000000000000000000..810c8356bccaa847d52434d6bed9ce0110740518 --- /dev/null +++ b/samples/webp_lossless/pm4-038-335a27fa.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f4bdb8c09522704bdbba7909a1d3f922045a9471e767cf5ea75b8444630000f +size 111716 diff --git a/samples/webp_lossless/pm4-039-066bcab1.png b/samples/webp_lossless/pm4-039-066bcab1.png new file mode 100644 index 0000000000000000000000000000000000000000..4ed9ef79a91fb35ac8915798dad62590602e893d --- /dev/null +++ b/samples/webp_lossless/pm4-039-066bcab1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8807251f32fde85e810f75d41c2ecaf567add89896e87d58f2c48765a7f22e7d +size 110245 diff --git a/samples/webp_lossless/pm4-040-eca2f1c2.png b/samples/webp_lossless/pm4-040-eca2f1c2.png new file mode 100644 index 0000000000000000000000000000000000000000..69bd757fcce0ca7acf31d5f9b0f3752848637724 --- /dev/null +++ b/samples/webp_lossless/pm4-040-eca2f1c2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9d3987940947f19b6239f26332a00c3db423ef707a43592656f6a65821c9772 +size 118044 diff --git a/samples/webp_lossless/pm4-041-7ac2f16a.png b/samples/webp_lossless/pm4-041-7ac2f16a.png new file mode 100644 index 0000000000000000000000000000000000000000..448221ff0332dd59de2a1e0e18ea8db8b14a3ebe --- /dev/null +++ b/samples/webp_lossless/pm4-041-7ac2f16a.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1556f076b29f6b80d2fa78d58b3a87d96c560dec42ccee26f0b0c62a589dc90f +size 117722 diff --git a/samples/webp_lossless/pm4-042-f7a87ffa.png b/samples/webp_lossless/pm4-042-f7a87ffa.png new file mode 100644 index 0000000000000000000000000000000000000000..202424e470c66f0b1d6e39626f370a75c5482d44 --- /dev/null +++ b/samples/webp_lossless/pm4-042-f7a87ffa.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd8ba206ff1d69664cfae40c1b0123f2ae4a36f0d4e95a4109583cb818d22302 +size 128922 diff --git a/samples/webp_lossless/pm4-043-cde8ad90.png b/samples/webp_lossless/pm4-043-cde8ad90.png new file mode 100644 index 0000000000000000000000000000000000000000..1b1dbf469edaa6fe72ede1fbe7bf940e2da83e27 --- /dev/null +++ b/samples/webp_lossless/pm4-043-cde8ad90.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7cee1c1ac5204ceb7830f7a84baba8b82553eb6c15f6f1aa4f886c4eaa398078 +size 108200 diff --git a/samples/webp_lossless/pm4-046-bec80a2b.png b/samples/webp_lossless/pm4-046-bec80a2b.png new file mode 100644 index 0000000000000000000000000000000000000000..1bbc1aab2fd287634c945fb99430fa6852060c26 --- /dev/null +++ b/samples/webp_lossless/pm4-046-bec80a2b.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:298777b4b0d2be6fd6aa00173666a01d2a596541a5df239c6eca906de38863d6 +size 108657 diff --git a/samples/webp_lossless/pm4-047-fc09cfd4.png b/samples/webp_lossless/pm4-047-fc09cfd4.png new file mode 100644 index 0000000000000000000000000000000000000000..52499ef5d6d837614a96e517d78f618b8fa5bbe3 --- /dev/null +++ b/samples/webp_lossless/pm4-047-fc09cfd4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85aeb211ec43a40f73c1fe9265650f555c6a144e1064c41cda5c7192793549e3 +size 115978 diff --git a/samples/webp_lossless/pm4-053-0c5b1381.png b/samples/webp_lossless/pm4-053-0c5b1381.png new file mode 100644 index 0000000000000000000000000000000000000000..d53a5d3d7de7a258444f804ca0780dbae01a97ea --- /dev/null +++ b/samples/webp_lossless/pm4-053-0c5b1381.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:951c959837fb14f404b9e5661550001500ac7e5de138af42c36d896d90ce6911 +size 116271 diff --git a/samples/webp_lossless/pm4-054-92b0a67d.png b/samples/webp_lossless/pm4-054-92b0a67d.png new file mode 100644 index 0000000000000000000000000000000000000000..7dca0b6b613f4cd69cf2a728b272b43cb65c2e7d --- /dev/null +++ b/samples/webp_lossless/pm4-054-92b0a67d.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f6ab18bc339c7990d640e44d1d20933967245fe064e93f36c2056ae78b72c41b +size 126362 diff --git a/samples/webp_lossless/pm4-055-ac372623.png b/samples/webp_lossless/pm4-055-ac372623.png new file mode 100644 index 0000000000000000000000000000000000000000..56c84ac92e613c62ccb1cb7b56fb43dcf23933c7 --- /dev/null +++ b/samples/webp_lossless/pm4-055-ac372623.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5e1b894dda863ffbc2f4321590b1328071fd6a07069e9d5bba6af8190f974cd +size 106257 diff --git a/samples/webp_lossless/pm4-056-ee005598.png b/samples/webp_lossless/pm4-056-ee005598.png new file mode 100644 index 0000000000000000000000000000000000000000..dfd6c7aa7fe3abf83d4620a8756f436284b6bb22 --- /dev/null +++ b/samples/webp_lossless/pm4-056-ee005598.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:289efb8d1d24a2e206d2aad932d8c4568e30417d09fb85c7acb6e7d48c8e5dd8 +size 105685 diff --git a/samples/webp_lossless/pm4-057-8ac12393.png b/samples/webp_lossless/pm4-057-8ac12393.png new file mode 100644 index 0000000000000000000000000000000000000000..b4c528473075d92ddc175fa2797d46b78d2736b6 --- /dev/null +++ b/samples/webp_lossless/pm4-057-8ac12393.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a1fa8242529d341736c78a942814034579cbe556ab1e6a57ca5baedeb5dc8f8 +size 120368 diff --git a/samples/webp_lossless/pm4-058-b655d991.png b/samples/webp_lossless/pm4-058-b655d991.png new file mode 100644 index 0000000000000000000000000000000000000000..1125ae4760ccd0a43624f985fb507fe795b9ac6c --- /dev/null +++ b/samples/webp_lossless/pm4-058-b655d991.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:053f0e1adc2e5d8c3adf638c3cadb7070dff7d330bf0fcb6e15352b2557e23fe +size 106366 diff --git a/samples/webp_lossless/pm4-059-7d5da4fb.png b/samples/webp_lossless/pm4-059-7d5da4fb.png new file mode 100644 index 0000000000000000000000000000000000000000..af8d50571e3997ad3f1261c9aa068e2e8fe578a6 --- /dev/null +++ b/samples/webp_lossless/pm4-059-7d5da4fb.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5f7615176664c300fcd681a8cfce97f47786938364c3e07882830825c35ebf5 +size 108010 diff --git a/samples/webp_lossless/pm4-062-4e57bc1c.png b/samples/webp_lossless/pm4-062-4e57bc1c.png new file mode 100644 index 0000000000000000000000000000000000000000..df547df1f6cde92c600524861b6ea10b7f7e070a --- /dev/null +++ b/samples/webp_lossless/pm4-062-4e57bc1c.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:410654eaba77572f690cca2624fdff6dad9a625988b912731d18ed30ba1370be +size 116237 diff --git a/samples/webp_lossless/pm4-063-a4e09271.png b/samples/webp_lossless/pm4-063-a4e09271.png new file mode 100644 index 0000000000000000000000000000000000000000..c4f3c97c92898465ef935400a35fd345723bb977 --- /dev/null +++ b/samples/webp_lossless/pm4-063-a4e09271.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2b511a867c0ed130040d4769f8a51a89e7e7bca7c0cb6ecc4852fe6144d8e41f +size 101491 diff --git a/samples/webp_lossless/pm4-064-2264c7eb.png b/samples/webp_lossless/pm4-064-2264c7eb.png new file mode 100644 index 0000000000000000000000000000000000000000..e66b4f2e6eb42916816aebbf44d35296533e22d8 --- /dev/null +++ b/samples/webp_lossless/pm4-064-2264c7eb.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b31985a931527141c1dbb9b5d97ba740149e3ffb6fd6875dc3c0d11b35cbd76 +size 123988 diff --git a/samples/webp_lossless/pm4-065-1cab116c.png b/samples/webp_lossless/pm4-065-1cab116c.png new file mode 100644 index 0000000000000000000000000000000000000000..877293f6318db27d67d8f5744ddfc43b71a5e890 --- /dev/null +++ b/samples/webp_lossless/pm4-065-1cab116c.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1019fe92d633511be3b482adfb30ac974fc43aa916ef0b1650fb43073bd313b +size 121489 diff --git a/samples/webp_lossless/pm4-067-ef873e98.png b/samples/webp_lossless/pm4-067-ef873e98.png new file mode 100644 index 0000000000000000000000000000000000000000..935b4d93781a4dac68fd0d0b222705e84fccb5ff --- /dev/null +++ b/samples/webp_lossless/pm4-067-ef873e98.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f58425fc2007a13a0e0f250d16074debd212cf86f224154cb5bc4762f381bbe +size 123594 diff --git a/samples/webp_lossless/pm4-068-fbe210a2.png b/samples/webp_lossless/pm4-068-fbe210a2.png new file mode 100644 index 0000000000000000000000000000000000000000..cfbbe9c745e18072d8d8e5711892deb93a00778b --- /dev/null +++ b/samples/webp_lossless/pm4-068-fbe210a2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5239864081131ae58b1d0a244eb3a677267bc5c89e41dcafe661d27d891e4933 +size 118386 diff --git a/samples/webp_lossless/pm4-069-cacdab19.png b/samples/webp_lossless/pm4-069-cacdab19.png new file mode 100644 index 0000000000000000000000000000000000000000..f34e6202f10acdf11708bf70b266d4f2e8f6d189 --- /dev/null +++ b/samples/webp_lossless/pm4-069-cacdab19.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff0502f0ec00e8ef36a8fafb3d5a862982ae00c66cfb674dccd42e06ff150a7e +size 122354 diff --git a/samples/webp_lossless/pm4-071-b9fe92b0.png b/samples/webp_lossless/pm4-071-b9fe92b0.png new file mode 100644 index 0000000000000000000000000000000000000000..12fe51688f9fbd0b8175ff9c9335b8d1f5b7cf83 --- /dev/null +++ b/samples/webp_lossless/pm4-071-b9fe92b0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8624329020cce2c1677bdc2ddc5e3f998ee7cc427f14956eb8073b4f680b9ad9 +size 115060 diff --git a/samples/webp_lossless/pm4-073-6ec086e0.png b/samples/webp_lossless/pm4-073-6ec086e0.png new file mode 100644 index 0000000000000000000000000000000000000000..8fae7e7a8edd6174e82ba5d6d1a150e4cc9a5c43 --- /dev/null +++ b/samples/webp_lossless/pm4-073-6ec086e0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:483769187bdce6595e1129c8f63d63c724d5ee84c96c5b279246bd4946d88d46 +size 103759 diff --git a/samples/webp_lossless/pm4-074-796fd1d3.png b/samples/webp_lossless/pm4-074-796fd1d3.png new file mode 100644 index 0000000000000000000000000000000000000000..652e22cf534ed1473ae3e6e204585c25cba65c80 --- /dev/null +++ b/samples/webp_lossless/pm4-074-796fd1d3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ba3d9caeaed8d39fd40ecd127c1663f6ba1771e853e2f36a2362d6ebc47ce80 +size 113680 diff --git a/samples/webp_lossless/pm4-076-a577a15d.png b/samples/webp_lossless/pm4-076-a577a15d.png new file mode 100644 index 0000000000000000000000000000000000000000..ef12f117fda47b0e8809a8a2f8bd6937b5521311 --- /dev/null +++ b/samples/webp_lossless/pm4-076-a577a15d.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47db78e98d2d475ac8c9ddbb19cfaa7d7e47a5910c46ca6cf82db0ac765bf31b +size 107522 diff --git a/samples/webp_lossless/pm4-079-5e8f0ede.png b/samples/webp_lossless/pm4-079-5e8f0ede.png new file mode 100644 index 0000000000000000000000000000000000000000..b9ffb9dea16788bc5ea1bf5d51dad0f1a5388f68 --- /dev/null +++ b/samples/webp_lossless/pm4-079-5e8f0ede.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff9a30bb54d86be04df77c5cf815d30307341d748382e0a4c9282847ae46dafc +size 108917 diff --git a/samples/webp_lossless/pm4-081-5b17a851.png b/samples/webp_lossless/pm4-081-5b17a851.png new file mode 100644 index 0000000000000000000000000000000000000000..78a92112374324b517ea7bc371b46c715fc76974 --- /dev/null +++ b/samples/webp_lossless/pm4-081-5b17a851.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:628afcde189ce2f39666a9e68ae37b435622a22007028884ff3e2c88bdecd2d4 +size 105568 diff --git a/samples/webp_lossless/pm4-083-02dc5c51.png b/samples/webp_lossless/pm4-083-02dc5c51.png new file mode 100644 index 0000000000000000000000000000000000000000..b8bf621484eb15f8bd9f81ad4729dab2477678b3 --- /dev/null +++ b/samples/webp_lossless/pm4-083-02dc5c51.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ee65e471640e3b5638313b055cac9867cb34df2b8150a46c8757476f2ebdfca +size 121772 diff --git a/samples/webp_lossless/pm4-084-06ed218a.png b/samples/webp_lossless/pm4-084-06ed218a.png new file mode 100644 index 0000000000000000000000000000000000000000..dd49424631f1ed248c50882fbb8a1cd2e1fcf87d --- /dev/null +++ b/samples/webp_lossless/pm4-084-06ed218a.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b151d11d8b311fb05fda059b4397251aeea64467a7b02880c82192ea3dcd1dd8 +size 115564 diff --git a/samples/webp_lossless/pm4-085-84317ff5.png b/samples/webp_lossless/pm4-085-84317ff5.png new file mode 100644 index 0000000000000000000000000000000000000000..29bf9f81a616c2d63cdcf4e732e387a8921ba8fc --- /dev/null +++ b/samples/webp_lossless/pm4-085-84317ff5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9d5e161fe584f102dddecf6c99ea36367028c445b281df0c4fa78d156eb4cd5 +size 121910 diff --git a/samples/webp_lossless/pm4-086-90e23232.png b/samples/webp_lossless/pm4-086-90e23232.png new file mode 100644 index 0000000000000000000000000000000000000000..3e4d951fd9e9fc3fd75ff1801549fad64ee402d5 --- /dev/null +++ b/samples/webp_lossless/pm4-086-90e23232.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3cb2578180aa31d47e4566d0b369a71c880bd78ac72f9c48294fbec37eed0429 +size 103723 diff --git a/samples/webp_lossless/pm4-087-34bf26a1.png b/samples/webp_lossless/pm4-087-34bf26a1.png new file mode 100644 index 0000000000000000000000000000000000000000..94301d1d1a12eadd62d53d622cbae0d0c0a92832 --- /dev/null +++ b/samples/webp_lossless/pm4-087-34bf26a1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d20a8c23c2537231026a2a66fb3f9d5e5d0bc6ba29e3fa3645a924f2154c43f4 +size 100822 diff --git a/samples/webp_lossless/pm4-088-92a9c223.png b/samples/webp_lossless/pm4-088-92a9c223.png new file mode 100644 index 0000000000000000000000000000000000000000..9d003110db0389b262c05f8a9164a0caefa6ec77 --- /dev/null +++ b/samples/webp_lossless/pm4-088-92a9c223.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ddaf73ad42ada3a4f3cedc7c63cc2961ddb348cd072a3c696b7b6a09f9abc321 +size 110191 diff --git a/samples/webp_lossless/pm4-091-c428ceb2.png b/samples/webp_lossless/pm4-091-c428ceb2.png new file mode 100644 index 0000000000000000000000000000000000000000..4551552db83967a5fb4307e3fd3b9b36eaa074b0 --- /dev/null +++ b/samples/webp_lossless/pm4-091-c428ceb2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b95e665931de52b3c7e874ef925027f4eaab81e96d302b9cfc795c2ce45ed6a3 +size 132448 diff --git a/samples/webp_lossless/pm4-092-cc02ddf9.png b/samples/webp_lossless/pm4-092-cc02ddf9.png new file mode 100644 index 0000000000000000000000000000000000000000..5f550a610761b04b339fc27bf46780c199890af5 --- /dev/null +++ b/samples/webp_lossless/pm4-092-cc02ddf9.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4596d75bb4de3efe20ea1df5840f24445f211155152f41a6a12342d354b66706 +size 110845 diff --git a/samples/webp_lossless/pm4-093-53b39ad0.png b/samples/webp_lossless/pm4-093-53b39ad0.png new file mode 100644 index 0000000000000000000000000000000000000000..a7d589ba73add5b011ba130e48121f3ef7613f64 --- /dev/null +++ b/samples/webp_lossless/pm4-093-53b39ad0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16bbc4a917be6c29849b94597d40fc93fb56c2a019a23b351b419cbde411f06a +size 129169 diff --git a/samples/webp_lossless/pm4-094-dd71e13d.png b/samples/webp_lossless/pm4-094-dd71e13d.png new file mode 100644 index 0000000000000000000000000000000000000000..a73eeac12090dda04e2ed576ea423c55179b3333 --- /dev/null +++ b/samples/webp_lossless/pm4-094-dd71e13d.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10ec7b2e09fb1676cc042f112b0836f27a22bebeb96edfb3f3e2d09b2ac0eef8 +size 107201 diff --git a/samples/webp_lossless/pm4-095-13e2d4a2.png b/samples/webp_lossless/pm4-095-13e2d4a2.png new file mode 100644 index 0000000000000000000000000000000000000000..abf304891291a3478081c09ad4812d724428fd68 --- /dev/null +++ b/samples/webp_lossless/pm4-095-13e2d4a2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80212df1c8e7e08ebd767f128faed183ec98b2634bbd26c193a9b2d5fb7d3919 +size 106499 diff --git a/samples/webp_lossless/pm4-096-95b0203e.png b/samples/webp_lossless/pm4-096-95b0203e.png new file mode 100644 index 0000000000000000000000000000000000000000..ffe713a4e69c9669b2b4736b027277184c33c94e --- /dev/null +++ b/samples/webp_lossless/pm4-096-95b0203e.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:424b83b33b0a604e413afa4a97eb4b446b57838f8d7e8c9ef6650f6692d27198 +size 117603 diff --git a/samples/webp_lossless/pm4-099-d947f488.png b/samples/webp_lossless/pm4-099-d947f488.png new file mode 100644 index 0000000000000000000000000000000000000000..2a435c67d9ded4bf6578095990760d5865c400ad --- /dev/null +++ b/samples/webp_lossless/pm4-099-d947f488.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96eaf23a5754fe8e45444e46d6f09792c2b105ebcf43badaca92c29f2685f353 +size 115308 diff --git a/samples/webp_lossless/pm4-100-5e269fc4.png b/samples/webp_lossless/pm4-100-5e269fc4.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae4bf58c3669b90117d9dc4367047397d6fb2bc --- /dev/null +++ b/samples/webp_lossless/pm4-100-5e269fc4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f6a3de60cd0e00fd0d74ddd89a5d25ad34ffbbd840ad45c7e87896a00098856 +size 106384 diff --git a/scripts/pixelmodel_robustness/codec.py b/scripts/pixelmodel_robustness/codec.py new file mode 100644 index 0000000000000000000000000000000000000000..e16a1429a0c9fa19022227e6209e4e14cda6a5fb --- /dev/null +++ b/scripts/pixelmodel_robustness/codec.py @@ -0,0 +1,85 @@ +"""Official PNG codec compatible weight reconstruction adapters.""" + +from __future__ import annotations + +import hashlib +import json +from collections.abc import Mapping, Sequence +from pathlib import Path + +import numpy as np +from PIL import Image + + +def decode_weight_image(image_path: str | Path, manifest_path: str | Path) -> tuple[np.ndarray, list[str]]: + """公式manifest順でRGBのR/Gをfp16 bit patternとしてfloat32へ復元する。""" + metadata = json.loads(Path(manifest_path).read_text()) + pixels = np.asarray(Image.open(image_path).convert("RGB"), dtype=np.uint8).reshape(-1, 3) + total = int(metadata["total_parameters"]) + if len(pixels) < total: + raise ValueError(f"weight image has {len(pixels)} pixels, needs {total}") + bits = (pixels[:total, 0].astype(np.uint16) << 8) | pixels[:total, 1].astype(np.uint16) + return bits.view(np.float16).astype(np.float32), [str(item["name"]) for item in metadata["params"]] + + +def flatten_manifest(arrays: Mapping[str, np.ndarray]) -> tuple[np.ndarray, list[tuple[str, int, int]]]: + """mappingの挿入順を保持し、float16 quantization後のfloat32 flatを返す。""" + parts: list[np.ndarray] = [] + manifest: list[tuple[str, int, int]] = [] + offset = 0 + for name, value in arrays.items(): + part = np.asarray(value).reshape(-1).astype(np.float16).astype(np.float32) + parts.append(part) + manifest.append((name, offset, offset + len(part))) + offset += len(part) + return np.concatenate(parts) if parts else np.empty(0, dtype=np.float32), manifest + + +def layer_offsets(names: Sequence[str], sizes: Sequence[int]) -> dict[str, tuple[int, int]]: + """layer名とnumelからmetric用offsetを構築する。""" + offsets: dict[str, tuple[int, int]] = {} + offset = 0 + for name, size in zip(names, sizes): + offsets[name] = (offset, offset + int(size)) + offset += int(size) + return offsets + + +def sha256_file(path: str | Path) -> str: + """ファイルのSHA-256を返す。""" + digest = hashlib.sha256() + with Path(path).open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def reconstruct_dit(image_path: str | Path, manifest_path: str | Path, device: str = "cpu", config_path: str | Path | None = None): + """model_png.jsonのname/shape順でDiTを再構築し、copy後の全parameterをfinite gateする。""" + import torch + + metadata = json.loads(Path(manifest_path).read_text(encoding="utf-8")) + config = json.loads(Path(config_path).read_text(encoding="utf-8"))["dit"] if config_path is not None else metadata["cfg"] + from dit import DiT + + model = DiT(dim=int(config["dim"]), depth=int(config["depth"]), heads=int(config["heads"])) + named = dict(model.named_parameters()) + pixels = np.asarray(Image.open(image_path).convert("RGB"), dtype=np.uint8).reshape(-1, 3) + total = int(metadata["total_parameters"]) + if pixels.shape[0] < total: + raise ValueError("weight image is shorter than manifest") + bits = (pixels[:total, 0].astype(np.uint16) << 8) | pixels[:total, 1].astype(np.uint16) + flat = bits.view(np.float16) + offset = 0 + with torch.no_grad(): + for item in metadata["params"]: + name = str(item["name"]) + shape = tuple(int(value) for value in item["shape"]) + numel = int(item["numel"]) + if name not in named or tuple(named[name].shape) != shape or numel != int(np.prod(shape)): + raise ValueError(f"manifest mismatch for {name}") + named[name].copy_(torch.from_numpy(flat[offset:offset + numel].copy()).view(shape).float()) + offset += numel + if offset != total or any(not bool(torch.isfinite(parameter).all()) for parameter in model.parameters()): + raise ValueError("finite gate failed after model parameter copy") + return model.to(device).eval() diff --git a/scripts/pixelmodel_robustness/inference.py b/scripts/pixelmodel_robustness/inference.py new file mode 100644 index 0000000000000000000000000000000000000000..0784a10bcc77766f459b98cd774e50f9d59d45f3 --- /dev/null +++ b/scripts/pixelmodel_robustness/inference.py @@ -0,0 +1,121 @@ +"""公式DiTの再現可能な推論と、テスト用runtime依存性注入。""" + +from __future__ import annotations + +import math +import os +from contextlib import nullcontext +from typing import Any + +os.environ.setdefault("TOKENIZERS_PARALLELISM", "false") + + +def resolve_device(requested: str = "auto") -> str: + """cpu/mps/cudaを環境に応じて解決する。""" + import torch + if requested != "auto": + if requested == "cuda" and not torch.cuda.is_available(): + raise RuntimeError("CUDA was requested but is unavailable") + if requested == "mps" and not torch.backends.mps.is_available(): + raise RuntimeError("MPS was requested but is unavailable") + return requested + if torch.cuda.is_available(): + return "cuda" + if torch.backends.mps.is_available(): + return "mps" + return "cpu" + + +def autocast_context(device: str) -> Any: + """GPUではautocast、CPUではnullcontextを返す。""" + import torch + if device == "cuda": + return torch.autocast(device_type="cuda", dtype=torch.bfloat16) + if device == "mps": + return torch.autocast(device_type="mps", dtype=torch.float16) + return nullcontext() + + +def finite_state_dict(state_dict: dict[str, Any]) -> bool: + """state dict全tensorのfinite gate。""" + return all(bool(value.isfinite().all()) for value in state_dict.values() if hasattr(value, "isfinite")) + + +def euler_rectified_flow(model: Any, conditional: Any, unconditional: Any, latent: Any, steps: int, cfg: float, device: str) -> Any: + """公式と同じ50-step Euler rectified-flow/CFGを実行する。""" + if steps < 1: + raise ValueError("steps must be positive") + dt = 1.0 / steps + x = latent.clone() + for index in range(steps): + t = x.new_full((x.shape[0],), index * dt, device=device) + with autocast_context(device): + vc = model(x, t, conditional[0], conditional[1]) + vu = model(x, t, unconditional[0], unconditional[1]) + x = x + (vu + cfg * (vc - vu)).float() * dt + return x + + +def generate_with_runtime(runtime: Any, model: Any, prompt: str, seed: int, steps: int, cfg: float, device: str) -> tuple[Any, float | None]: + """text encode→seed固定latent→Euler→VAE decode→CLIPをruntime経由で行う。""" + import torch + with torch.inference_mode(): + conditional = runtime.encode([prompt], device) + unconditional = runtime.encode([""], device) + latent = runtime.initial_latent(seed, device) + result = euler_rectified_flow(model, conditional, unconditional, latent, steps, cfg, device) + image = runtime.decode(result, device) + if not bool(torch.isfinite(image).all()): + raise FloatingPointError("nonfinite generated image tensor") + score = runtime.clip_score(image, prompt, device) + if score is not None and not math.isfinite(float(score)): + raise FloatingPointError("nonfinite CLIP score") + return image, float(score) if score is not None else None + + +def clip_score_once(metric: Any, image: Any, prompt: str, target: str, torch_module: Any) -> float: + """stateful torchmetrics CLIPScoreを一サンプル単位で隔離して評価する。""" + metric.reset() + try: + metric((image.unsqueeze(0) * 255).to(torch_module.uint8), [prompt]) + return float(metric.compute().item()) + finally: + metric.reset() + + +def build_official_runtime(args: Any, device: str) -> tuple[Any, Any]: + """公式依存を一度だけロードし、model loaderとruntimeを返す。""" + import torch + from diffusers import AutoencoderKL + from torchmetrics.multimodal.clip_score import CLIPScore + from transformers import CLIPTextModel, CLIPTokenizer + vae = AutoencoderKL.from_pretrained(args.vae).to(device).half().eval() + tokenizer = CLIPTokenizer.from_pretrained(args.clip) + text = CLIPTextModel.from_pretrained(args.clip).to(device).half().eval() + clip_metric = CLIPScore(model_name_or_path=args.clip).to(device).eval() + + class Runtime: + def encode(self, strings: list[str], target: str) -> tuple[Any, Any]: + tokens = tokenizer(strings, padding="max_length", max_length=40, truncation=True, return_tensors="pt").to(target) + output = text(**tokens) + return output.last_hidden_state.float(), output.pooler_output.float() + + def initial_latent(self, seed: int, target: str) -> Any: + generator = torch.Generator(device=target).manual_seed(seed) + return torch.randn((1, 4, 32, 32), generator=generator, device=target) + + def decode(self, latent: Any, target: str) -> Any: + return ((vae.decode((latent / 0.18215).half()).sample.float().clamp(-1, 1) + 1) / 2)[0] + + def clip_score(self, image: Any, prompt: str, target: str) -> float | None: + return clip_score_once(clip_metric, image, prompt, target, torch) + + from pixelmodel_robustness.codec import reconstruct_dit + + def loader(weight_path: str) -> Any: + model = reconstruct_dit(weight_path, args.manifest, device, getattr(args, "config", None)) + if not finite_state_dict(dict(model.named_parameters())): + raise ValueError("finite gate failed after PNG model reconstruction") + return model + + return loader, Runtime() diff --git a/scripts/pixelmodel_robustness/jpeg_repair.py b/scripts/pixelmodel_robustness/jpeg_repair.py new file mode 100644 index 0000000000000000000000000000000000000000..f9d2cee292ac477df19e8e0b64f160528ae1bca1 --- /dev/null +++ b/scripts/pixelmodel_robustness/jpeg_repair.py @@ -0,0 +1,451 @@ +"""JPEG weight repairのTask 1-2用codec、修復、clip、fp16診断。""" + +from __future__ import annotations + +import hashlib +import json +import logging +import zlib +from collections.abc import Mapping, Sequence +from dataclasses import asdict, dataclass +from io import BytesIO +from typing import Any, Final + +import numpy as np +from PIL import Image, features +from PIL import __version__ as PILLOW_VERSION + +LOGGER = logging.getLogger(__name__) + +CONDITION_IDS: Final[tuple[str, ...]] = ( + "png_baseline", + "jpeg_q100_raw", + "jpeg_q100_repair_zero", + "jpeg_q100_repair_layer_median", + "jpeg_q100_repair_layer_mean", + "jpeg_q100_repair_zero_clip_layer_mean_std_6", + "jpeg_q100_repair_zero_clip_layer_mean_std_5", + "jpeg_q100_repair_zero_clip_layer_pct_0.01_99.99", + "jpeg_q100_repair_zero_clip_layer_pct_0.1_99.9", + "jpeg_q100_repair_layer_median_clip_layer_mean_std_6", + "jpeg_q100_repair_layer_median_clip_layer_mean_std_5", + "jpeg_q100_repair_layer_median_clip_layer_pct_0.01_99.99", + "jpeg_q100_repair_layer_median_clip_layer_pct_0.1_99.9", + "jpeg_q100_repair_layer_mean_clip_layer_mean_std_6", + "jpeg_q100_repair_layer_mean_clip_layer_mean_std_5", + "jpeg_q100_repair_layer_mean_clip_layer_pct_0.01_99.99", + "jpeg_q100_repair_layer_mean_clip_layer_pct_0.1_99.9", + "protected_high_png_low_jpeg_q100", +) +CLIP_STRATEGIES: Final[tuple[str, ...]] = ( + "layer_mean_std_6", "layer_mean_std_5", "layer_pct_0.01_99.99", "layer_pct_0.1_99.9" +) + + +@dataclass(frozen=True) +class JpegCondition: + """JPEG studyの不変condition entry。""" + + condition_id: str + source: str + repair: str | None + clip: str | None + payload_kind: str + parent_raw_sha256: str + + +@dataclass(frozen=True) +class LayerClipRecord: + """一つのlayerに対するclip結果。""" + + layer: str + strategy: str + lower: float + upper: float + pre_max_abs: float + post_max_abs: float + changed_count: int + + +@dataclass(frozen=True) +class RepairResult: + """非有限修復の値と監査用count。""" + + values: np.ndarray + method: str + nan_count: int + posinf_count: int + neginf_count: int + repaired_value_count: int + layer_records: tuple[LayerRepairRecord, ...] + repaired_histogram: dict[str, Any] + + +@dataclass(frozen=True) +class LayerRepairRecord: + """一つのlayerにおける非有限値修復の監査record。""" + + layer: str + replacement: float + nan_count: int + posinf_count: int + neginf_count: int + repaired_count: int + + +@dataclass(frozen=True) +class ClipResult: + """layerwise clipの値とlayer record。""" + + values: np.ndarray + strategy: str + layers: tuple[LayerClipRecord, ...] + total_clipped_count: int + changed_pre_histogram: dict[str, Any] + changed_post_histogram: dict[str, Any] + changed_delta_histogram: dict[str, Any] + + +@dataclass(frozen=True) +class ProtectedArtifact: + """high PNG + low JPEGのpayload。""" + + high_png_bytes: bytes + low_jpeg_bytes: bytes + + @property + def payload(self) -> bytes: + """推論用protected payloadを返す。""" + return self.high_png_bytes + self.low_jpeg_bytes + + +def _sha256(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +def encode_jpeg_q100(image: Image.Image | np.ndarray) -> bytes: + """RGB sourceをJPEG quality=100/subsampling=0で一度だけencodeする。""" + rgb = image if isinstance(image, Image.Image) else Image.fromarray(np.asarray(image, dtype=np.uint8), "RGB") + stream = BytesIO() + rgb.convert("RGB").save(stream, format="JPEG", quality=100, subsampling=0, optimize=False) + return stream.getvalue() + + +def decode_jpeg_rgb(payload: bytes) -> np.ndarray: + """JPEG bytesをRGB uint8へdecodeする。""" + with Image.open(BytesIO(payload)) as image: + return np.asarray(image.convert("RGB"), dtype=np.uint8).copy() + + +def flatten_fp16_layers(arrays: Mapping[str, np.ndarray]) -> tuple[np.ndarray, dict[str, tuple[int, int]]]: + """mappingの順序を保ったfp16 flat値とlayer offsetsを作る。""" + parts: list[np.ndarray] = [] + offsets: dict[str, tuple[int, int]] = {} + start = 0 + for name, array in arrays.items(): + part = np.asarray(array, dtype=np.float16).reshape(-1) + end = start + part.size + parts.append(part) + offsets[str(name)] = (start, end) + start = end + return (np.concatenate(parts) if parts else np.empty(0, dtype=np.float16), offsets) + + +def layer_offsets(names: Sequence[str], sizes: Sequence[int]) -> dict[str, tuple[int, int]]: + """layer名とnumelからordered offsetsを構築する。""" + if len(names) != len(sizes): + raise ValueError("names and sizes must have equal length") + offsets: dict[str, tuple[int, int]] = {} + start = 0 + for name, size in zip(names, sizes): + end = start + int(size) + if int(size) < 0: + raise ValueError("layer size must be non-negative") + offsets[str(name)] = (start, end) + start = end + return offsets + + +def validate_layer_offsets(layers: Mapping[str, tuple[int, int]], total_size: int) -> dict[str, tuple[int, int]]: + """layer offsetsが全体を隙間なく一度だけ覆うことを検証する。""" + if total_size < 0: + raise ValueError("total_size must be non-negative") + normalized = {str(name): (int(bounds[0]), int(bounds[1])) for name, bounds in layers.items()} + expected_start = 0 + for name, (start, end) in normalized.items(): + if start < 0 or end < start or end > total_size: + raise ValueError(f"layer {name} is out of bounds") + if start < expected_start: + raise ValueError(f"layer {name} has overlap") + if start > expected_start: + raise ValueError(f"layer {name} is not contiguous") + expected_start = end + if expected_start != total_size: + raise ValueError("layers do not end at total size") + return normalized + + +def _condition_fields(condition_id: str) -> tuple[str, str | None, str | None, str]: + if condition_id == "png_baseline": + return "canonical_png", None, None, "materialized_rgb_png" + if condition_id == "jpeg_q100_raw": + return "jpeg_q100", None, None, "raw_jpeg" + if condition_id == "protected_high_png_low_jpeg_q100": + return "protected", None, None, "high_png_low_jpeg" + body = condition_id.removeprefix("jpeg_q100_repair_") + repair, _, clip = body.partition("_clip_") + return "jpeg_q100", repair, clip, "materialized_rgb_png" + + +def build_jpeg_registry(raw_jpeg_bytes: bytes | None = None) -> tuple[JpegCondition, ...]: + """計画で固定された18条件をordered immutable tupleとして構築する。""" + raw_parent = _sha256(raw_jpeg_bytes) if raw_jpeg_bytes is not None else _sha256(b"jpeg_q100:quality=100:subsampling=0:optimize=False") + baseline_parent = _sha256(b"canonical_png:png_baseline") + protected_parent = _sha256(b"protected:high_png_low_jpeg_q100") + entries: list[JpegCondition] = [] + for condition_id in CONDITION_IDS: + parent = raw_parent if 1 <= CONDITION_IDS.index(condition_id) <= 16 else baseline_parent if condition_id == "png_baseline" else protected_parent + entries.append(JpegCondition(condition_id, *_condition_fields(condition_id), parent)) + return tuple(entries) + + +def semantic_hash(registry: Sequence[JpegCondition]) -> str: + """registryの順序・全semantic fieldsをcanonical JSONでhashする。""" + encoded = json.dumps([asdict(entry) for entry in registry], sort_keys=True, separators=(",", ":")).encode() + return _sha256(encoded) + + +def materialize_fp16_png(values: np.ndarray, *, width: int | None = None) -> bytes: + """flat fp16 bit patternをRGB PNGへmaterializeする(R=high,G=low,B=0)。""" + flat = np.asarray(values, dtype=np.float16).reshape(-1) + if not flat.size: + raise ValueError("values must not be empty") + width_value = int(width or max(1, min(flat.size, 1024))) + height = (flat.size + width_value - 1) // width_value + pixels = np.zeros((height * width_value, 3), dtype=np.uint8) + bits = flat.view(np.uint16) + pixels[: flat.size, 0] = (bits >> 8).astype(np.uint8) + pixels[: flat.size, 1] = bits.astype(np.uint8) + stream = BytesIO() + Image.fromarray(pixels.reshape(height, width_value, 3), "RGB").save(stream, format="PNG") + return stream.getvalue() + + +def _planes(values: np.ndarray, shape: tuple[int, int]) -> tuple[np.ndarray, np.ndarray]: + flat = np.asarray(values, dtype=np.float16).reshape(-1) + capacity = int(np.prod(shape)) + if flat.size > capacity: + raise ValueError("shape is smaller than values") + bits = np.zeros(capacity, dtype=np.uint16) + bits[: flat.size] = flat.view(np.uint16) + return (bits >> 8).astype(np.uint8).reshape(shape), bits.astype(np.uint8).reshape(shape) + + +def encode_protected(values: np.ndarray, shape: tuple[int, int]) -> ProtectedArtifact: + """high byteをlossless PNG、low byteをJPEG Q100 grayscaleでencodeする。""" + high, low = _planes(values, shape) + high_stream, low_stream = BytesIO(), BytesIO() + Image.fromarray(high, "L").save(high_stream, format="PNG") + Image.fromarray(low, "L").save(low_stream, format="JPEG", quality=100, subsampling=0, optimize=False) + return ProtectedArtifact(high_stream.getvalue(), low_stream.getvalue()) + + +def decode_protected( + payload: bytes | ProtectedArtifact, + shape: tuple[int, int], + *, + source_values: np.ndarray | None = None, +) -> tuple[np.ndarray, dict[str, Any]]: + """protected payloadをfp16へ再構築し、plane metadataを返す。""" + artifact = payload if isinstance(payload, ProtectedArtifact) else _split_payload(payload) + with Image.open(BytesIO(artifact.high_png_bytes)) as high_image, Image.open(BytesIO(artifact.low_jpeg_bytes)) as low_image: + high_mode = high_image.mode + low_mode = low_image.mode + high_dimensions = [int(high_image.height), int(high_image.width)] + low_dimensions = [int(low_image.height), int(low_image.width)] + high = np.asarray(high_image.convert("L"), dtype=np.uint8) + low = np.asarray(low_image.convert("L"), dtype=np.uint8) + if high.shape != shape or low.shape != shape: + raise ValueError("protected plane shape mismatch") + bits = (high.reshape(-1).astype(np.uint16) << 8) | low.reshape(-1).astype(np.uint16) + source_high: np.ndarray | None = None + source_low: np.ndarray | None = None + if source_values is not None: + source_high, source_low = _planes(source_values, shape) + source_high = source_high.reshape(-1) + source_low = source_low.reshape(-1) + decoded_high_bytes = high.tobytes() + decoded_low_bytes = low.tobytes() + low_changed_count = int(np.count_nonzero(low.reshape(-1) != source_low)) if source_low is not None else None + total = int(np.prod(shape)) + metadata = { + "dimensions": list(shape), + "high_mode": high_mode, + "low_mode": low_mode, + "high_dimensions": high_dimensions, + "low_dimensions": low_dimensions, + "high_bytes": len(artifact.high_png_bytes), + "low_bytes": len(artifact.low_jpeg_bytes), + "high_sha256": _sha256(artifact.high_png_bytes), + "low_sha256": _sha256(artifact.low_jpeg_bytes), + "payload_sha256": _sha256(artifact.payload), + "payload_size_bytes": len(artifact.payload), + "jpeg_options": {"quality": 100, "subsampling": 0, "optimize": False}, + "decoded_high_sha256": _sha256(decoded_high_bytes), + "decoded_low_sha256": _sha256(decoded_low_bytes), + "decoded_high_exact": bool(source_high is not None and np.array_equal(high.reshape(-1), source_high)), + "decoded_low_changed_count": low_changed_count, + "decoded_low_changed_rate": (float(low_changed_count / total) if low_changed_count is not None and total else None), + "pillow_version": PILLOW_VERSION, + "jpeg_codec_version": _jpeg_codec_version(), + "combined_payload_sha256": _sha256(artifact.high_png_bytes + artifact.low_jpeg_bytes), + "combined_payload_bytes": len(artifact.high_png_bytes) + len(artifact.low_jpeg_bytes), + "combined_payload_exact": artifact.payload == (payload if isinstance(payload, bytes) else artifact.payload), + } + return bits.view(np.float16), metadata + + +def _jpeg_codec_version() -> str | None: + """Pillowが報告できるJPEG codec versionを取得する。""" + try: + return features.version_codec("jpg") + except (AttributeError, RuntimeError, ValueError): + return None + + +def _split_payload(payload: bytes) -> ProtectedArtifact: + """PNG chunkをsignature/length/type/data/CRC順に検証してcombined payloadを分割する。""" + signature = b"\x89PNG\r\n\x1a\n" + if len(payload) < len(signature) or payload[: len(signature)] != signature: + raise ValueError("protected payload has invalid PNG signature") + position = len(signature) + while True: + if position + 12 > len(payload): + raise ValueError("PNG chunk header is out of bounds") + length = int.from_bytes(payload[position:position + 4], "big") + chunk_start = position + position += 8 + end_data = position + length + end_chunk = end_data + 4 + if end_chunk > len(payload): + raise ValueError("PNG chunk data is out of bounds") + chunk_type = payload[position - 4:position] + data = payload[position:end_data] + expected_crc = int.from_bytes(payload[end_data:end_chunk], "big") + actual_crc = zlib.crc32(chunk_type + data) & 0xFFFFFFFF + if expected_crc != actual_crc: + raise ValueError("PNG chunk CRC mismatch") + position = end_chunk + if chunk_type == b"IEND": + if length != 0: + raise ValueError("PNG IEND chunk must be empty") + return ProtectedArtifact(payload[:position], payload[position:]) + if chunk_start == len(payload): + raise ValueError("PNG has no IEND chunk") + + +def repair_nonfinite(values: np.ndarray, method: str, layers: Mapping[str, tuple[int, int]]) -> RepairResult: + """NaN/Infだけをfloat64 layer statisticで置換し、最後に一度だけfp16化する。""" + if method not in {"zero", "layer_median", "layer_mean"}: + raise ValueError(f"unknown repair method: {method}") + source = np.asarray(values, dtype=np.float16).reshape(-1) + normalized_layers = validate_layer_offsets(layers, source.size) + with np.errstate(invalid="ignore"): + output = source.astype(np.float64) + finite = np.isfinite(source) + layer_records: list[LayerRepairRecord] = [] + for name, (start, end) in normalized_layers.items(): + layer_finite = output[start:end][finite[start:end]] + if method != "zero" and layer_finite.size == 0: + raise ValueError(f"layer {name} has no finite values") + mask = ~finite[start:end] + if method == "zero": + replacement = 0.0 + elif method == "layer_median": + replacement = float(np.median(layer_finite)) + else: + replacement = float(np.mean(layer_finite, dtype=np.float64)) + output[start:end][mask] = replacement + raw_layer = source[start:end] + layer_records.append(LayerRepairRecord(name, float(replacement), int(np.isnan(raw_layer).sum()), int(np.isposinf(raw_layer).sum()), int(np.isneginf(raw_layer).sum()), int(mask.sum()))) + repaired = output.astype(np.float16) + repaired_values = repaired[~finite] + LOGGER.info("repaired nonfinite values: method=%s count=%d", method, int((~finite).sum())) + return RepairResult(repaired, method, int(np.isnan(source).sum()), int(np.isposinf(source).sum()), int(np.isneginf(source).sum()), int((~finite).sum()), tuple(layer_records), _histogram(repaired_values)) + + +def _clip_bounds(data: np.ndarray, strategy: str) -> tuple[float, float]: + finite = data[np.isfinite(data)] + if not finite.size: + raise ValueError("layer has no finite values") + finite = finite.astype(np.float64) + if strategy.startswith("layer_mean_std_"): + scale = float(strategy.removeprefix("layer_mean_std_")) + mean, std = float(np.mean(finite)), float(np.std(finite, ddof=0)) + return mean - scale * std, mean + scale * std + if strategy.startswith("layer_pct_"): + low, high = strategy.removeprefix("layer_pct_").split("_") + return float(np.percentile(finite, float(low), method="linear")), float(np.percentile(finite, float(high), method="linear")) + raise ValueError(f"unknown clip strategy: {strategy}") + + +def clip_candidate(values: np.ndarray, layers: Mapping[str, tuple[int, int]], strategy: str) -> ClipResult: + """repair candidateだけからlayer boundsを計算し、finite値をclipする。""" + if strategy not in CLIP_STRATEGIES: + raise ValueError(f"unknown clip strategy: {strategy}") + source = np.asarray(values, dtype=np.float16).reshape(-1) + if not np.isfinite(source).all(): + raise ValueError("clip candidate must be finite") + normalized_layers = validate_layer_offsets(layers, source.size) + output64 = source.astype(np.float64) + records: list[LayerClipRecord] = [] + for name, (start, end) in normalized_layers.items(): + before = output64[start:end] + lower, upper = _clip_bounds(before, strategy) + after = np.clip(before, lower, upper) + records.append(LayerClipRecord(name, strategy, lower, upper, float(np.max(np.abs(before))), float(np.max(np.abs(after))), int(np.count_nonzero(after.astype(np.float16).view(np.uint16) != source[start:end].view(np.uint16))))) + output64[start:end] = after + clipped = output64.astype(np.float16) + changed = clipped.view(np.uint16) != source.view(np.uint16) + return ClipResult(clipped, strategy, tuple(records), int(changed.sum()), _histogram(source[changed]), _histogram(clipped[changed]), _histogram(clipped[changed].astype(np.float64) - source[changed].astype(np.float64))) + + +def _histogram(values: np.ndarray, bins: int = 32) -> dict[str, Any]: + finite = np.asarray(values, dtype=np.float64) + finite = finite[np.isfinite(finite)] + if not finite.size: + return {"edges": [], "counts": []} + edges = np.histogram_bin_edges(finite, bins=min(bins, max(1, finite.size))) + counts, edges = np.histogram(finite, bins=edges) + return {"edges": edges.tolist(), "counts": counts.astype(int).tolist()} + + +def diagnostics(baseline: np.ndarray, candidate: np.ndarray, layers: Mapping[str, tuple[int, int]]) -> dict[str, Any]: + """fp16 wordのbit差分、global/layer stats、bounded histogramを返す。""" + left = np.asarray(baseline, dtype=np.float16).reshape(-1) + right = np.asarray(candidate, dtype=np.float16).reshape(-1) + if left.shape != right.shape: + raise ValueError("baseline and candidate shapes differ") + normalized_layers = validate_layer_offsets(layers, left.size) + a, b = left.view(np.uint16), right.view(np.uint16) + sign = ((a ^ b) & 0x8000) != 0 + exponent = ((a ^ b) & 0x7C00) != 0 + mantissa = ((a ^ b) & 0x03FF) != 0 + to31 = (((a >> 10) & 0x1F) != 31) & (((b >> 10) & 0x1F) == 31) + finite_extreme = np.zeros(left.size, dtype=bool) + layer_stats: list[dict[str, Any]] = [] + for name, (start, end) in normalized_layers.items(): + baseline_layer = left[start:end] + candidate_layer = right[start:end] + baseline_finite = np.isfinite(baseline_layer) + candidate_finite = np.isfinite(candidate_layer) + baseline_max = float(np.max(np.abs(baseline_layer[baseline_finite]))) if baseline_finite.any() else 0.0 + candidate_abs = np.abs(candidate_layer.astype(np.float64)) + threshold = baseline_max * 10.0 + if baseline_max == 0.0: + layer_extreme = candidate_finite & (candidate_abs > 0.0) + else: + layer_extreme = candidate_finite & (candidate_abs > threshold) + finite_extreme[start:end] = layer_extreme + layer_stats.append({"layer": name, "sign_mismatch_count": int(sign[start:end].sum()), "exponent_mismatch_count": int(exponent[start:end].sum()), "mantissa_mismatch_count": int(mantissa[start:end].sum()), "exponent_to31_count": int(to31[start:end].sum()), "finite_extreme_outlier_count": int(layer_extreme.sum()), "nan_count": int(np.isnan(candidate_layer).sum()), "posinf_count": int(np.isposinf(candidate_layer).sum()), "neginf_count": int(np.isneginf(candidate_layer).sum()), "finite_count": int(candidate_finite.sum()), "max_abs_weight": float(np.max(candidate_abs[candidate_finite])) if candidate_finite.any() else 0.0}) + return {"total_count": int(left.size), "sign_mismatch_count": int(sign.sum()), "exponent_mismatch_count": int(exponent.sum()), "mantissa_mismatch_count": int(mantissa.sum()), "exponent_to31_count": int(to31.sum()), "finite_extreme_outlier_count": int(finite_extreme.sum()), "nan_count": int(np.isnan(right).sum()), "posinf_count": int(np.isposinf(right).sum()), "neginf_count": int(np.isneginf(right).sum()), "finite_count": int(np.isfinite(right).sum()), "max_abs_weight": float(np.max(np.abs(right[np.isfinite(right)]))) if np.isfinite(right).any() else 0.0, "layers": layer_stats, "histograms": {"baseline": _histogram(left), "candidate": _histogram(right), "delta": _histogram(right.astype(np.float64) - left.astype(np.float64))}} diff --git a/scripts/pixelmodel_robustness/report.py b/scripts/pixelmodel_robustness/report.py new file mode 100644 index 0000000000000000000000000000000000000000..7b72e5d7e92f79f3cea4d7c41ffedb51c7de93e7 --- /dev/null +++ b/scripts/pixelmodel_robustness/report.py @@ -0,0 +1,99 @@ +"""Study plots and report helpers.""" + +from __future__ import annotations + +import csv +from collections.abc import Sequence +from pathlib import Path + +import numpy as np +from PIL import Image, ImageDraw + + +def write_metrics_csv(path: Path, rows: Sequence[dict[str, object]]) -> None: + """raw rowsからmetrics.csvを生成する。""" + path.parent.mkdir(parents=True, exist_ok=True) + keys = sorted({key for row in rows for key in row}) + with path.open("w", newline="", encoding="utf-8") as stream: + writer = csv.DictWriter(stream, fieldnames=keys, extrasaction="ignore") + writer.writeheader() + writer.writerows(rows) + + +def write_layer_metrics_csv(path: Path, rows: Sequence[dict[str, object]]) -> None: + """rawのlayers配列を正規化してlayer_metrics.csvへ保存する。""" + flattened: list[dict[str, object]] = [] + for row in rows: + for layer in row.get("layers", []): + flattened.append({"condition": row.get("condition"), **layer}) + write_metrics_csv(path, flattened) + + +def make_layer_plot(rows: Sequence[dict[str, object]], path: Path) -> None: + """layer-wise L2/MSE/cosine/changed plotを生成する。""" + import matplotlib.pyplot as plt + values = [(str(row.get("condition")), layer) for row in rows for layer in row.get("layers", [])] + if not values: + return + figure, axes = plt.subplots(2, 2, figsize=(12, 8), dpi=300) + for axis, key in zip(axes.flat, ("l2", "mse", "cosine", "changed_percent")): + for condition in sorted({name for name, _ in values}): + points = [float(layer[key]) for name, layer in values if name == condition and layer.get(key) is not None and np.isfinite(float(layer[key]))] + if points: + layer_names = [str(layer.get("layer")) for name, layer in values if name == condition and layer.get(key) is not None and np.isfinite(float(layer[key]))] + axis.plot(points, marker=".", label=condition) + axis.set_xticks(range(len(layer_names)), layer_names, rotation=60, ha="right", fontsize=5) + axis.set_title(key) + axis.grid(alpha=0.2) + axes[0, 0].legend(fontsize=6) + figure.tight_layout() + path.parent.mkdir(parents=True, exist_ok=True) + figure.savefig(path, dpi=300) + plt.close(figure) + + +def make_grid(images: Sequence[tuple[Image.Image, str]], path: Path, columns: int = 4) -> None: + """画像とラベルから比較 grid を作成する。""" + if not images: + return + cell = max(max(image.width, image.height) for image, _ in images) + label_height = 28 + rows = (len(images) + columns - 1) // columns + canvas = Image.new("RGB", (columns * cell, rows * (cell + label_height)), "white") + draw = ImageDraw.Draw(canvas) + for index, (image, label) in enumerate(images): + row, column = divmod(index, columns) + resized = image.convert("RGB").resize((cell, cell)) + x, y = column * cell, row * (cell + label_height) + canvas.paste(resized, (x, y)) + draw.text((x + 3, y + cell + 4), label[:36], fill="black") + canvas.save(path, dpi=(300, 300)) + + +def make_diff_maps(reference: np.ndarray, candidate: np.ndarray, stem: Path) -> None: + """signed/absolute pixel difference heatmapを保存する。""" + import matplotlib.pyplot as plt + diff = candidate.astype(float) - reference.astype(float) + for suffix, data, cmap in (("signed", diff.mean(axis=2), "coolwarm"), ("absolute", np.abs(diff).mean(axis=2), "magma")): + figure, axis = plt.subplots(figsize=(5, 5), dpi=300) + axis.imshow(data, cmap=cmap) + axis.set_axis_off() + figure.tight_layout(pad=0) + figure.savefig(stem.with_name(f"{stem.name}_{suffix}.png"), dpi=300, bbox_inches="tight", pad_inches=0) + plt.close(figure) + + +def make_quality_plot(rows: Sequence[dict[str, object]], path: Path) -> None: + """PSNR/storageのpublication-quality plotを保存する。""" + import matplotlib.pyplot as plt + valid = [row for row in rows if row.get("success") and row.get("psnr") is not None] + if not valid: + return + figure, axis = plt.subplots(figsize=(8, 5), dpi=300) + axis.scatter([float(row["compression_ratio"]) for row in valid], [float(row["psnr"]) for row in valid], s=24) + axis.set_xlabel("Compression ratio (PNG bytes / candidate bytes)") + axis.set_ylabel("Image PSNR (dB)") + axis.grid(alpha=0.25) + figure.tight_layout() + figure.savefig(path, dpi=300) + plt.close(figure)