How to Parse FFmpeg VMAF XML or JSON Output
When running video quality assessments using the FFmpeg
libvmaf filter, the output is typically saved as an XML or
JSON file containing aggregate metrics and per-frame quality scores.
This article provides straightforward methods to parse both output
formats using Python and standard command-line tools, allowing you to
extract overall scores and frame-by-frame data efficiently.
Generating the Output Files
To generate the files, pass the log_fmt and
log_path options to the libvmaf filter:
For JSON:
ffmpeg -i distorted.mp4 -i reference.mp4 -filter_complex "libvmaf=log_fmt=json:log_path=vmaf.json" -f null -For XML:
ffmpeg -i distorted.mp4 -i reference.mp4 -filter_complex "libvmaf=log_fmt=xml:log_path=vmaf.xml" -f null -Parsing JSON Output
The JSON output contains a pooled_metrics object for
aggregated statistics (such as mean, min, and harmonic mean) and a
frames array containing per-frame scores.
Using Python
import json
with open("vmaf.json", "r") as f:
data = json.load(f)
# Extract pooled (overall) VMAF scores
pooled_vmaf = data["pooled_metrics"]["vmaf"]
print(f"Mean VMAF: {pooled_vmaf['mean']}")
print(f"Min VMAF: {pooled_vmaf['min']}")
# Extract per-frame scores
frame_scores = [frame["metrics"]["vmaf"] for frame in data["frames"]]
print(f"First frame VMAF: {frame_scores[0]}")
print(f"Total evaluated frames: {len(frame_scores)}")Using the Command Line
(jq)
Extract the aggregate mean VMAF score:
jq '.pooled_metrics.vmaf.mean' vmaf.jsonExtract a list of per-frame scores:
jq '[.frames[].metrics.vmaf]' vmaf.jsonParsing XML Output
The XML output contains <metric> nodes inside the
<pooled_metrics> parent, and individual frame records
inside <frames> using <f> elements
with metric attributes.
Using Python
(xml.etree.ElementTree)
import xml.etree.ElementTree as ET
tree = ET.parse("vmaf.xml")
root = tree.getroot()
# Extract pooled metrics
for metric in root.findall(".//pooled_metrics/metric"):
if metric.attrib.get("name") == "vmaf":
print(f"Mean VMAF: {metric.attrib.get('mean')}")
print(f"Min VMAF: {metric.attrib.get('min')}")
# Extract per-frame scores
frame_scores = []
for frame in root.findall(".//frames/f"):
frame_scores.append(float(frame.attrib["vmaf"]))
print(f"First frame VMAF: {frame_scores[0]}")
print(f"Total evaluated frames: {len(frame_scores)}")Using the Command Line
(xmlstarlet)
Extract the aggregate mean VMAF score:
xmlstarlet sel -t -v "//pooled_metrics/metric[@name='vmaf']/@mean" -n vmaf.xmlExtract all per-frame scores separated by newlines:
xmlstarlet sel -t -m "//frames/f" -v "@vmaf" -n vmaf.xml