# VMAF

Canonical URL: https://encode.wiki/reference/metrics/vmaf/
Content version: ca42e61ede0a24f789d7287a0001244ad22f4abf
Status: current
License: CC BY-SA 4.0
Checksum (SHA-256): 3592b1cbeec65df9edade4f0fd4ea18c6217d4cc892c8d54cad56abd18daad1e

VMAF (Video Multimethod Assessment Fusion) is Netflix's full-reference video quality model. It computes a few elementary features, such as detail loss and motion, and a regression model trained on viewer ratings turns them into a score, normally 0–100, predicting how an average viewer would rate the video under one specific viewing condition. It was built for streaming: choosing bitrates and resolutions for an encoding ladder. It is good at that, but sharpening can inflate it, it barely separates high-quality encodes, and the classic models ignore colour, so a VMAF number alone won't tell you which encoder or setting looks better.

## What VMAF is good for, and what it isn't

For that job it tracks viewer opinion better than [PSNR](/reference/metrics/psnr/) or [SSIM](/reference/metrics/ssim/) when you compare encodes of the same content at different bitrates and resolutions, and it is supported almost everywhere, from FFmpeg to Av1an's target-quality mode.

It is widely criticised as a judge for encoder and settings comparisons:

- **Enhancement inflates it.** Sharpening and contrast boosts raise the score even though they move the picture away from the source. Encoders exploit this: SVT-AV1's `--tune 5` and libaom's `--tune=vmaf` sharpen the source before encoding to gain VMAF points. Netflix added "no enhancement gain" (NEG) models for codec evaluation for exactly this reason, and made NEG behaviour the default in v1.
- **It discriminates poorly near transparency.** It was trained on streaming-quality video, and differences between high-quality encodes get squeezed into the top few points. On the KonFiG-IQA image dataset, which focuses on fine, near-threshold differences, Cloudinary measured a rank correlation of 0.49 for VMAF against 0.92 for SSIMULACRA2 and Butteraugli.
- **Grain and psychovisual tuning are open problems.** Netflix itself lists film-grain noise and perceptual optimisations such as adaptive quantisation among the things v1 still doesn't handle well.
- **The classic models are luma-only.** Every v0 model ignores chroma entirely. v1 adds a chroma feature.
- **It is SDR-only.** No released model is designed for PQ or HLG; Netflix says an HDR version of v1 is planned. Feeding it HDR produces numbers, not meaningful ones.
- **Scores depend on viewing assumptions.** The default model predicts a 1080p TV viewed from three picture heights. Computed on 480p video at native size, it effectively assumes a much longer viewing distance and scores high.

For comparing encoders or settings, use [SSIMULACRA2](/reference/metrics/ssimulacra2/), [Butteraugli](/reference/metrics/butteraugli/), [XPSNR](/reference/metrics/xpsnr/) or [CVVDP](/reference/metrics/cvvdp/), with a NEG VMAF score as an optional extra, and look at the video. See [Compare encoding quality](/guides/compare-encoding-quality/).

## Choose a model

libvmaf compiles its models into the library, so you select them with `version=` and need no model files. Use `path=` for a JSON model from the repository's `model/` directory.

| Model (version=)                          | Viewing condition                           | Notes                                                                                    |
| ------------------------------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `vmaf_v0.6.1`                               | 1080p TV at 3× picture height (3H)          | Default in FFmpeg and the `vmaf` tool, and what most published numbers and tools use     |
| `vmaf_v0.6.1neg`                            | 1080p TV at 3H                              | No enhancement gain; the v0 model to use for encoder comparisons                         |
| `vmaf_4k_v0.6.1`, `vmaf_4k_v0.6.1neg`       | 4K TV at 1.5H                               | Compute at 2160p                                                                         |
| `vmaf_v0.6.1` with `enable_transform=true`  | Phone                                       | v0 phone mode; scores higher than the TV model for the same video                       |
| `vmaf_v1.0.16_3d0h`                         | 1080p at 3H                                 | v1 standard model                                                                        |
| `vmaf_v1.0.16_5d0h`                         | Phone (1080p at 5H)                         |                                                                                          |
| `vmaf_v1.0.16_1d5h_2160`                    | 4K at 1.5H                                  | The default choice for 4K                                                                |
| `vmaf_v1.0.16_3d0h_2160`                    | 4K at 3H (consumer TV)                      | Range 0–110, to show the benefit of 4K over 1080p at that distance                       |
| `vmaf_v1.0.16_hfr_*`                        | As above, for ~50–60 fps content            | Same four variants with a wider motion window                                            |

VMAF v1, released with libvmaf 3.2.0 in June 2026, adds a banding feature (CAMBI), a chroma feature and an additive-impairment term for blocking, drops VIF, and has NEG behaviour built in. Netflix recommends computing SDR v1 at 10 bits, even for 8-bit encodes, so banding is captured. v1 and v0 scores come from different models and aren't interchangeable, so don't mix them in one comparison.

> **Caution — v1 needs a recent libvmaf.**
>
> The v1 models need libvmaf 3.2.1 or newer, or 3.2.0 built with `-Denable_float=true`. FFmpeg builds linked against an older libvmaf only offer the v0 models.
## Run VMAF in FFmpeg

Your FFmpeg needs to be built with `--enable-libvmaf`. Put the distorted file first and the reference second:

```sh
ffmpeg -i distorted.mkv -i reference.mkv -lavfi libvmaf -f null -
```

This uses `vmaf_v0.6.1` and prints the mean score over all frames. A more useful run picks the model, writes per-frame scores and uses more than one thread:

```sh
ffmpeg -i distorted.mkv -i reference.mkv -lavfi "[0:v]format=yuv420p10le[d];[1:v]format=yuv420p10le[r];[d][r]libvmaf=model=version=vmaf_v1.0.16_3d0h:log_fmt=json:log_path=vmaf.json:n_threads=8" -f null -
```

- `n_threads`: libvmaf is single-threaded by default. Set it to the number of cores you can spare.
- `log_fmt=json:log_path=vmaf.json`: per-frame scores, plus the min, max, mean and harmonic mean over the clip.
- `format=yuv420p10le` on both inputs: the 10-bit measurement Netflix recommends for v1. Leave it out for v0 models.

To compute the regular and NEG scores in one pass, give each model a name:

```sh
ffmpeg -i distorted.mkv -i reference.mkv -lavfi libvmaf='model=version=vmaf_v0.6.1\\:name=vmaf|version=vmaf_v0.6.1neg\\:name=vmaf_neg' -f null -
```

**Compare at the model's resolution.** If the encode is smaller than the reference, upscale it with bicubic, as Netflix does, rather than downscaling the reference:

```sh
ffmpeg -i distorted.mkv -i reference.mkv -lavfi "[0:v]scale=1920:1080:flags=bicubic[d];[d][1:v]libvmaf" -f null -
```

For a 4K source, use a 4K model at 2160p. Both inputs need the same frame count; FFmpeg silently repeats the last frame of a shorter input. Add `setpts=PTS-STARTPTS` to both inputs only if one of them starts at a non-zero timestamp.

**Find the worst frames.** The mean hides short bad sections, which is what viewers notice. With a JSON log:

```sh
jq '.frames | sort_by(.metrics.vmaf) | .[:5] | map({frameNum, vmaf: .metrics.vmaf})' vmaf.json
```

Setting the filter's `pool` option to `harmonic_mean` or `min` makes the printed summary weigh bad frames more heavily.

> **Tip — Model paths on Windows.**
>
> Built-in `version=` models and relative paths avoid the problem. An absolute path needs forward slashes and the drive colon escaped for both libvmaf and the filtergraph. In PowerShell or cmd this works as written: `-lavfi "libvmaf=model='path=C\\\:/vmaf/model.json'"`.
## Use the standalone vmaf tool

The `vmaf` command-line tool from libvmaf reads only Y4M or raw YUV, but it doesn't need an FFmpeg build with libvmaf and exposes every libvmaf option. Prebuilt binaries for Linux and Windows are on the [libvmaf releases page](https://github.com/Netflix/vmaf/releases).

```sh
vmaf -r reference.y4m -d distorted.y4m -m version=vmaf_v1.0.16_3d0h --json -o vmaf.json
```

Raw YUV input also needs `--width`, `--height`, `--pixel_format` and `--bitdepth`. Like the filter, it is single-threaded unless you add `--threads N`. `--feature psnr`, `--feature float_ssim` and similar add extra metrics to the log.

When a v1 measurement uses an upscaled or 8-bit encode, give the banding feature the encode's real size and bit depth by appending them to the model, e.g. `-m version=vmaf_v1.0.16_3d0h:cambi.enc_width=1280:cambi.enc_height=720:cambi.enc_bitdepth=8`.

To avoid writing huge Y4M files, decode into named pipes:

```sh
mkfifo ref.y4m dist.y4m
ffmpeg -nostdin -v error -i reference.mkv -pix_fmt yuv420p10le -strict -1 -y ref.y4m &
ffmpeg -nostdin -v error -i distorted.mkv -pix_fmt yuv420p10le -strict -1 -y dist.y4m &
vmaf -r ref.y4m -d dist.y4m -m version=vmaf_v1.0.16_3d0h --json -o vmaf.json
rm ref.y4m dist.y4m
```

FFmpeg needs `-strict -1` to write Y4M above 8 bits, `-y` because the pipe file already exists, and `-nostdin` because the shell stops a background FFmpeg that touches the terminal, which hangs the pipeline.

## Further reading

- [Netflix VMAF documentation](https://github.com/Netflix/vmaf/tree/master/resource/doc)
- [VMAF FAQ](https://github.com/Netflix/vmaf/blob/master/resource/doc/faq.md), including why comparing a video with itself may not give exactly 100
- [VMAF v1: Good Is Not Good Enough](https://netflixtechblog.com/vmaf-v1-good-is-not-good-enough-60d7e4244ea8) (Netflix tech blog)
