Repository navigation
Conversation
This was referenced Oct 1, 2026
lusoris
force-pushed
the
fix/adm-integer-aim-clip
branch
2 times, most recently
from
October 2, 2026 05:23
681b0f0 to
af94fc9
Compare
integer_compute_adm() normalised the AIM numerator with aim_num / den, while compute_adm() in adm.c returns MIN(aim_num / aim_den, 1.0f). On a picture whose distorted side has much more detail than the reference, the integer extractor therefore reports an AIM above 1 where the float extractor reports exactly 1. A flat grey 64x64 reference against the same picture with isolated 4x2 luma patches gives integer_aim 3.175585 and aim 1.000000; 576x324 patches give 2.602848 and 1.0; flat against uniform noise of +-24 gives 1.268618 and 1.0. The unclipped value also reaches integer adm3, which VMAF_integer_feature_adm3_score computes as MAX(adm2 * w + (1 - aim) * (1 - w), adm_min_val). With the weight 0.7 and floor 0.5 of the vmaf_v1.0.16 models it sat on the floor where the float extractor gives 0.7. Clip the integer AIM in the one place that concludes it. integer_adm_cuda has no AIM pass and no other extractor reads the score. This changes integer aim, integer adm3 and the vmaf_v1.0.16 scores only where the unclipped AIM exceeds 1. No Python golden assertion has an integer AIM above 0.02656, and the three Netflix reference pairs score identically. test_integer_adm_aim_clip checks, on a flat reference against noise of +-24 at 576x324, the clipped value (1.268618 before, 1 after) on the SIMD and the scalar path and the agreement of integer and float adm3 with the model's weight and floor, and that identical pictures give an AIM of 0 and noise of +-8 an AIM below 1 that passes unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lusoris
force-pushed
the
fix/adm-integer-aim-clip
branch
from
October 2, 2026 18:43
af94fc9 to
530be8e
Compare
Author
|
Closing this. I proposed the clip to make the integer extractor match the float one, but I could not find a case where it changes a shipped model's output: on every picture I built where integer AIM exceeds 1 (flat reference with patches or noise), the default model's VMAF is already 0 before and after the clip, and the other published models read adm3 unchanged once AIM is below 1. Since integer AIM feeds adm3, and adm3 feeds the published scores, changing it is a behaviour change that should come from you rather than from a consistency cleanup. The inconsistency stays as it is upstream (integer: unclipped ratio; float: clipped at 1). If you want the clip, it is the one line in the description and I am glad to reopen. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The integer ADM extractor can report an AIM above 1; the float extractor clips the same ratio to 1. This PR clips the integer one. It is a behaviour change, so the options are spelled out below.
What is inconsistent
The integer line came in with
966be8d5(2026-04-17) and the float line, with the clip and its comment, with4dcc2f7c(2026-04-27). AIM is the additive impairment that survives contrast masking, divided by the DLM denominator, which is the reference's own detail. When the distorted picture has far more detail than the reference, the ratio exceeds 1.Reproducer
Master
6ec23e8f2(reproduced there; the code is unchanged in 9e48141), release build, default options, default dispatch. The pictures are 8-bit 4:2:0 and generated, so anyone can rebuild them:255 0 0 0(two rows of255 0 0 0) every 16 samples from (1, 1), at both sizes;128 + state % (2 * amp + 1) - amp), for amp 8 and 24.vmaf -r flat_ref.yuv -d distorted.yuv -w 64 -h 64 -p 420 -b 8 \ --feature adm --feature float_adm --no_prediction -q --json -o out.jsoninteger_aimmasteraim(float) masterinteger_adm3masteradm3(float) masterinteger_aim,integer_adm3this branchThe integer
adm3ismax(adm2 * w + (1 - aim) * (1 - w), adm_min_val), so an AIM above 1 lowers it. The 0.506536 is0.5 * adm2with an integeradm2of 1.013; that is the scale 0 centre-tap wrap that #1602 fixes (with #1602 applied the same picture gives anadm2of 1.0), not an AIM effect.With the model the shipped
vmaf_v1.0.16files set (adm_dlm_weight0.7,adm_min_val0.5) on the noise pictures,vmaf_v1.0.16_3d0h:integer_adm3masterinteger_adm3this branchFix
*score_aim = MIN(aim_num / den, 1.0f);ininteger_compute_adm(). That is the only place the integer AIM is concluded.libvmaf/src/feature/cudahas no AIM pass (integer_adm_cudanever computesinteger_aim), and nothing else reads the score.What changes, and where
integer_aim, and everything computed from it:integer_adm3(VMAF_integer_feature_adm3_score) and the VMAF of every model that reads it. Those are the eightvmaf_v1.0.16andvmaf_v1.0.16_hfrmodels (model/vmaf_v1.0.16/*.json,model/vmaf_v1.0.16_hfr/*.json); no other file undermodel/mentionsadm3or an AIM feature.vmaf_v0.6.1and the other shipped models readadm2only.python/testasserts an integer AIM in 48 places; the largest expected value is 0.026560104166666664 (vmafexec_feature_extractor_test.py:1998). The 49 integeradm3assertions (0.9423 to 1.0526) are computed with those AIM values. None involves an AIM above 1, so no assertion changes..engagement/golden_compare.py, default dispatch and--cpumask -1): identical per-frame and pooled output on master and on this branch.vmaf_v1.0.16_3d0h,_5d0h,_1d5h_2160,_hfr_3d0h) on the 576x324 reference against noise of +-8, 12, 16, 17, 18, 19, 20, 22, 24, 28, 32 and 48 (model AIM up to 2.63): identical before and after in all 48 runs, because the score reaches 0 before the AIM reaches 1 on this family of pictures. This is not exhaustive; a picture with an AIM above 1 and a score above 0 would change.Alternative
Drop the clip from
adm.cinstead, so that both extractors return the raw ratio. That changes the floataimand the floatadm3on the same inputs, keeps the shipped models' scores as they are, and makesfloat_admandadmreport values above 1. Theadm.ccomment says the clip is deliberate there; nothing says its absence frominteger_adm.cis. I think the clip is the intended behaviour, but the models read the integer feature, so it is your call; I can turn this into the other change if you prefer.Tests
test_integer_adm_aim_clip(new): on the flat reference against noise of +-24, integer AIM is exactly 1 on the SIMD and on the scalar path, equals the float AIM, and integeradm3with weight 0.7 and floor 0.5 is within 1e-4 of the float one; identical pictures give an AIM of 0; noise of +-8 gives an AIM between 0.4 and 0.5 that matches the float one within 1e-4. On master it fails:integer aim 1.2686181234336971, expected 1. The float comparisons are skipped whenfloat_admis not built. The test uses the noise picture rather than the patches because the patches also reach the centre-tap wrap that #1602 fixes: on the 64x64 patches master gives a scalaradm2of 1.0355 against 1.0 on the SIMD paths, and UBSan reports a negative left shift inadm_cm()(integer_adm.c:1680). With #1602 applied the scalaradm2is 1.0.Validation
Rebased on master 9e48141 (2026-10-02). x86-64 Linux, GCC 16.2.1, on master
9e48141b.meson test(-Denable_float=true -Denable_checkasm=true): 25/25 on master, 26/26 here (the new test).-Db_sanitize=address,undefined -Db_lto=falsebuild: 22 pass and 3 fail on master, 23 pass and 3 fail here.test_predictandtest_pic_preallocationabort on LeakSanitizer reports andcheckasmon aheap-buffer-overflowinadm_dwt2_16on both; this change does not touch them.The workflow run on this PR needs a maintainer's approval.