File size: 9,005 Bytes
81dc469
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99116d2
81dc469
 
 
99116d2
81dc469
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99116d2
81dc469
 
 
 
 
 
 
 
 
 
 
 
 
99116d2
 
81dc469
 
 
 
 
 
 
 
 
 
 
 
99116d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81dc469
99116d2
 
 
81dc469
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
---
license: other
license_name: nutrient-commercial
pipeline_tag: image-text-to-text
language:
  - en
tags:
  - chart-understanding
  - chart-to-table
  - structured-extraction
  - document-ai
  - vision-language-model
metrics:
  - f1
model-index:
  - name: Nutrient Chart Parsing
    results:
      - task:
          type: image-text-to-text
          name: Chart parsing
        dataset:
          type: nutrientdocs/chart-parsing-benchmark
          name: Chart Parsing Benchmark
          split: test
        metrics:
          - type: f1
            name: Mean per-chart cell F1 (3% tolerance)
            value: 0.888
            verified: false
          - type: f1
            name: Mean per-chart cell F1 (strict)
            value: 0.797
            verified: false
---

# Nutrient Chart Parsing · _commercial_

**Read a chart image and return its data and structure as one JSON object.** The Nutrient chart-parsing
model takes a rendered chart (bar, line, pie, scatter, area, box, bubble, heatmap, histogram, treemap,
funnel, radar, violin, stem, step, candlestick, combo) and returns the title, chart type, data table,
value representation, encoding, axes, series, legend, and data-label state in a single pass. Values are
what a reader can recover from the pixels: printed labels verbatim, shares for pie and donut charts,
axis-scaled estimates otherwise.

The runnable weights are commercial and are not downloadable from this repository. This public page is the
product specification and scorecard; see [License and access](#license-and-access) to evaluate or deploy it.

- 🧪 **Try it:** [`nutrientdocs/chart-parsing-demo`](https://huggingface.co/spaces/nutrientdocs/chart-parsing-demo)
- 🏆 **Leaderboard:** [`nutrientdocs/chart-parsing-leaderboard`](https://huggingface.co/spaces/nutrientdocs/chart-parsing-leaderboard)
- 📊 **Benchmark:** [`nutrientdocs/chart-parsing-benchmark`](https://huggingface.co/datasets/nutrientdocs/chart-parsing-benchmark)

## Headline result

Systems are ranked by **mean per-chart cell F1 at 3% tolerance** on the frozen 2,500-chart benchmark:
every chart's data cells are matched by row identity and column, a value counts when it is
within 3% of the chart's value range (3 percentage points on share charts, exact when the chart prints
data labels), and the per-chart F1 is averaged so every chart counts once. **Strict F1** demands values
within 0.1%. Every entry sees the same charts and the same prompt.

| System | F1 · 3% | Strict F1 | F1 · 5% | Shape | Type | Title | sec/chart |
|---|---:|---:|---:|---:|---:|---:|---:|
| **Nutrient Chart Parsing** | **0.888** | 0.797 | 0.897 | 0.963 | 0.983 | 0.989 | 0.27 |
| Gemini 3.8 Flash | 0.710 | 0.640 | 0.713 | 0.796 | 0.960 | 0.998 | — |
| Claude Opus 5 | 0.697 | 0.617 | 0.703 | 0.792 | 0.967 | 0.998 | — |
| GPT-6 Astra | 0.675 | 0.628 | 0.676 | 0.802 | 0.964 | 0.997 | — |
| Gemini 3.5 Flash Lite | 0.663 | 0.600 | 0.672 | 0.771 | 0.947 | 0.992 | — |
| Claude Sonnet 5 | 0.657 | 0.586 | 0.664 | 0.741 | 0.973 | 0.998 | — |
| GPT-5.6 Terra | 0.630 | 0.569 | 0.634 | 0.709 | 0.960 | 0.998 | — |
| Gemini 3.1 Pro | 0.628 | 0.567 | 0.633 | 0.726 | 0.950 | 0.996 | — |
| Claude Haiku 4.5 | 0.570 | 0.469 | 0.596 | 0.708 | 0.943 | 0.995 | — |
| GPT-5.6 Sol | 0.566 | 0.514 | 0.569 | 0.658 | 0.954 | 0.997 | — |
| Qwen3.8-27B | 0.524 | 0.445 | 0.531 | 0.625 | 0.793 | 0.826 | 2.80 |
| Qwen3-VL-4B-Instruct | 0.518 | 0.443 | 0.536 | 0.623 | 0.908 | 0.984 | 0.30 |
| Qwen3.5-4B | 0.169 | 0.143 | 0.171 | 0.200 | 0.290 | 0.312 | 1.00 |

The Nutrient model reaches **0.888**, ahead of the strongest cloud model here, **Gemini 3.8 Flash (0.710)**,
and it recovers the right table shape on **96.3%** of charts against 80% for the best cloud model. Its lead
is in the data cells, not in the labels: cloud models read titles and chart types about as well. Seconds per
chart are measured on one NVIDIA A100 80GB with vLLM at 64-way concurrency and compare only within that
setup; provider APIs give no comparable latency. Full per-field and per-type breakdowns are on the
[leaderboard](https://huggingface.co/spaces/nutrientdocs/chart-parsing-leaderboard).

### Result by chart type

Mean per-chart cell F1 for the Nutrient model at the three tolerance levels, with the number of benchmark
charts of each type.

| Chart type | n | Strict | 3% | 5% |
|---|---:|---:|---:|---:|
| line | 552 | 0.831 | 0.924 | 0.928 |
| bar | 463 | 0.857 | 0.940 | 0.946 |
| pie | 249 | 0.762 | 0.765 | 0.765 |
| scatter | 235 | 0.819 | 0.859 | 0.860 |
| area | 224 | 0.822 | 0.889 | 0.912 |
| box | 171 | 0.753 | 0.980 | 0.991 |
| bubble | 164 | 0.665 | 0.750 | 0.761 |
| heatmap | 100 | 0.868 | 0.878 | 0.898 |
| histogram | 54 | 0.875 | 0.927 | 0.946 |
| treemap | 54 | 0.341 | 0.961 | 0.994 |
| combo | 51 | 0.784 | 0.937 | 0.945 |
| funnel | 49 | 0.953 | 0.953 | 0.953 |
| radar | 46 | 0.549 | 0.651 | 0.682 |
| violin | 33 | 0.691 | 0.958 | 0.980 |
| stem | 25 | 0.772 | 0.844 | 0.846 |
| step | 15 | 0.983 | 0.983 | 0.983 |
| candlestick | 15 | 0.693 | 0.807 | 0.813 |

Per-field accuracy on the same set: chart type 0.983, title 0.989, x and y axis labels 0.987 and 0.986,
axis scales 0.975 and 0.985, legend visibility 0.990, data-label visibility 0.989, column types 0.989,
column roles 0.988, axis bounds 0.835. Set F1 over encoding pairs 0.974 and over series pairs 0.993.

## Output

One JSON object per chart. `data.rows` is the table; every other key describes how the chart shows it.

```json
{
  "title": "Programming Language Popularity",
  "chartType": "pie",
  "data": {
    "columns": [
      {"name": "Language", "type": "categorical", "role": "category", "unit": null},
      {"name": "Popularity", "type": "numeric", "role": "value", "unit": null}
    ],
    "rows": [["Python", 21.6], ["Java", 19.4], ["C++", 17.7]]
  },
  "valueRepresentation": {"mode": "percentage_of_total", "format": "0.0%"},
  "encoding": {"category": "Language", "value": "Popularity", "color": "Language"},
  "axes": null,
  "series": [{"name": "Popularity", "field": "Popularity"}],
  "legend": {"visible": false, "position": null, "entries": null},
  "dataLabels": {"visible": true, "format": "percentage"}
}
```

- `chartType` is one of `bar, line, area, pie, scatter, bubble, histogram, box, violin, heatmap, treemap, funnel, radar, candlestick, step, stem, combo`.
- Column `type` is `categorical`, `numeric`, or `temporal`; column `role` is one of `category, value, series, x, y, z, size, color`.
- `valueRepresentation.mode` is `percentage_of_total` for pie, donut, treemap, and 100%-stacked charts, otherwise `absolute`.
- `axes` holds `{label, unit, scale, min, max}` per axis with the visible limits, or `null` for charts without axes.
- Any field the image does not support is `null`.

The `data` block converts to CSV, HTML, or a spreadsheet with plain code; the model is not asked to emit
those formats.

## Intended use and limits

- **Use it for:** turning rendered chart images (exports from plotting libraries, report figures, dashboard
  screenshots) into a data table and a description of how the chart shows it, for indexing, analysis,
  re-plotting, or conversion to spreadsheets.
- **Values are visual estimates.** When a chart prints no data labels the model reads values off the axis
  scale; expect errors within a few percent of the value range, and check the strict versus 3% columns
  above for how much that costs per chart type. Pie and donut charts return shares of the total, never the
  underlying absolute values.
- **Weaker chart types:** bubble (size legends are hard to invert), radar, and candlestick charts, and axis
  bounds in general (0.747 field accuracy). Pie charts read printed shares well but estimating unlabeled
  wedges is imprecise.
- **Validate downstream.** The output is well-formed JSON in practice, but parse it defensively and check
  that `data.rows` has the width `data.columns` declares before feeding it to automated pipelines.

## License and access

The model weights are offered under a **commercial Nutrient license** and are not published here. The
benchmark charts derive from ChartGen-200K, CC BY 4.0.
The benchmark is public and reproducible through
[`nutrientdocs/chart-parsing-benchmark`](https://huggingface.co/datasets/nutrientdocs/chart-parsing-benchmark).

> ### 📩 Get access
>
> Nutrient Chart Parsing is commercial. To evaluate or deploy it on-prem, with your documents staying in
> your infrastructure, **contact Nutrient:
> [nutrient.io/contact-sales](https://www.nutrient.io/contact-sales/).**

## About the author

<a href="https://nutrient.io/">
  <img src="https://avatars2.githubusercontent.com/u/1527679?v=3&s=200" height="80" />
</a>

This project is maintained and funded by [Nutrient](https://nutrient.io/) - The deterministic document infrastructure enterprises run their highest-stakes workflows on: replayable output, clear exceptions, and full audit trails on the messy, regulated documents where AI alone breaks.