You need to agree to share your contact information to access this model
This repository is publicly accessible, but you have to accept the conditions to access its files and content.
These weights are a fine-tune of Meta's DINOv3 and are distributed under the DINOv3 License, a copy of which is in this repository as LICENSE. You are accepting Meta's terms, not ours. Read LICENSE before accepting: the points below are a summary and the licence text governs.
Section 1(a) grants you a royalty-free licence to use, reproduce, distribute, copy, create derivative works of and modify the materials, with no limit on commercial use. Section 5(a) says any derivative you make is yours.
The conditions that come with that: a derivative may only be distributed under the same Agreement with a copy attached (§1(b)(i)), published research must acknowledge the materials (§1(b)(ii)), no reverse engineering (§1(b)(iv)), and no military, nuclear, espionage or weapons use, with trade controls applying (§1(b)(v)).
The training data is Lantmäteriet's, all under CC BY 4.0. Attribution travels with anything you build on these weights; the citation block on the model card has the wording.
Log in or Sign Up to review the conditions and access this model content.
TIC Forestry v1
Land cover and canopy height from Swedish aerial photos, at 1 m per pixel.
Give it an aerial photo. For every square metre it returns a land cover class and a canopy height.
512 m of ground, 2024 imagery. Top left is the input. The other three panels are the output.
It is a DINOv3 ViT-L/16 satellite backbone with a multi-scale dense decoder. We fine-tuned it on Lantmäteriet aerial photos, and took the height it had to learn from airborne laser.
Why it predicts height. Photos are reflown about every two years. Laser is much slower. Sweden's forest laser scan started in 2018 and is still going, and covers about 221,000 km2. Where ground has been scanned twice, the gap is usually six or seven years. This model reads canopy height off each new photo, so you do not have to wait for the next laser pass.
| parameters | 305,625,963. Backbone 303.39 M, head 2.23 M. 465 tensors, bf16 |
| input | 512 x 512 at 0.5 m. 4 bands (R, G, B, NIR), uint8 |
| output | 256 x 256 at 1 m. 10-class logits, plus canopy height in metres |
| cross-county mIoU | 0.79 |
| height RMSE against laser | 3.6 m |
| VRAM | 1,982 MiB in total while serving, driver-reported. Card size is not the constraint |
What it is for
Mapping stands, measuring canopy, counting up land cover over an area, and comparing two flights to see what changed. It works at stand scale, which in Sweden means 1 to 5 hectares.
The class means land use, not tree cover
If you read one thing on this page, read this.
The labels come from Lantmäteriet's Topografi 10 mark layer. That is a land use map. On it, a
clearfelled stand is still Barr- och blandskog. The model learned the map.
We checked this against laser over 3,568,378 pixels:
| predicted class | laser canopy under 2 m |
|---|---|
| conifer_or_mixed_forest | 41.3 % |
| deciduous_forest | 47.8 % |
| water | 98.2 % |
| arable | 96.1 % |
| open_land | 83.1 % |
So 41 % of the pixels the model calls conifer have nothing over 2 m growing on them. For a land use map that is correct. For a tree cover map it is wrong.
If you want to know whether there are trees, look at the height layer. If you want to know what kind of land it is, look at the class layer.
What it cannot do
- Find individual trees. One ViT patch covers 8 m of ground.
- Tell species apart beyond three groups: conifer or mixed, deciduous, mountain birch.
- Say why something changed. A fire and a clearfelling look the same to it.
- Handle imagery unlike its training data. See Limitations.
Architecture
| backbone | DINOv3 ViT-L/16 sat493m. Last 6 of 24 blocks unfrozen, 78.9 M trainable |
| decoder | tokens from depths 6, 12, 18 and 24, fused and upsampled 32 to 256. Widths 256, 128, 64, 32 |
| heads | 10-class logits and a canopy height regressor. Both dense at 1 m |
| fourth band | near-infrared is trained from the start. The patch embedding is 4-channel |
Why the input is 0.5 m
The sharpest Swedish imagery is 0.16 m, so 0.5 m looks like a waste. Three reasons it is not.
A chip has to be wider than a stand. At 0.5 m a 512 px chip covers 256 m, which holds a stand and its edges. At 0.16 m the same chip covers 82 m and sits inside one stand.
The backbone was pretrained on 0.6 m satellite imagery. 0.5 m is close to what it already knows.
Sweden also has a 0.40 m product, which covers most of the north. 0.5 m is the only grid both products reach by downsampling. Asking for 0.16 m would mean upsampling half the country.
We resample by area-weighted mean. 0.16 m into 0.5 m is 3.125 to 1, so a pixel on a boundary contributes its overlap.
Classes
| code | name | mark.objekttyp |
wooded | colour |
|---|---|---|---|---|
| 0 | unlabelled | transparent | ||
| 1 | water | Sjö, Hav, Vattendragsyta, Anlagt vatten | no | #2b5f9e |
| 2 | arable | Åker, Fruktodling | no | #e8d58a |
| 3 | built | Låg/Hög/Sluten bebyggelse, Industri- och handelsbebyggelse, Torg | no | #b0403a |
| 4 | conifer_or_mixed_forest | Barr- och blandskog | yes | #1f6b3a |
| 5 | deciduous_forest | Lövskog | yes | #7fb84f |
| 6 | mountain_birch | Fjällbjörkskog | yes | #a8c47a |
| 7 | open_land | Öppen mark | no | #d9c99a |
| 8 | bare_mountain | Kalfjäll | no | #9a9a94 |
| 9 | glacier | Glaciär | no | #e6f2f7 |
The palette is fixed, so two maps of different ground can be compared. Our own GeoTIFF output embeds it as a TIFF colour table.
Training data
| chips | 287,612 over 2,743 cells of 10 km, in 17 counties |
| class labels | Lantmäteriet Topografi 10 mark, 3,494,541 polygons |
| height target | our own 1 m canopy model: highest laser return per square metre, minus the terrain model |
| imagery | Lantmäteriet orthophotos, 0.16 m and 0.40 m, 4-band, 2019 onward |
| schedule | AdamW, layer-wise LR decay, cosine over 147,160 steps, 5 % warmup, EMA 0.999 |
| run | 20 epochs of 7,358 batches of 32, about 27 min per epoch. One GPU |
Three things we decided, and why.
We did not train on NMD. Naturvårdsverkets Nationella Marktäckedata splits forest more finely. We compared it against our labels over 125 million cells of 10 m. NMD took water and arable from the same Lantmäteriet vectors we use, and those agree 100.0 % and 94.2 %. Its forest classes are modelled, and those agree 82.4 % for conifer or mixed and 80.4 % for deciduous. Training on a model's output teaches you that model's mistakes, so we use NMD to score and not to train.
We kept the edges. Every polygon in Topografi 10 carries its own declared positional uncertainty. For the three forest classes the median is 3.0 m and the 90th percentile 4.5 m, against 0.6 m for water and arable. Lantmäteriet's specification only requires 20 m on a forest boundary, so the delivered data is well inside its own ceiling, but a forest edge is still five times looser than a shoreline. The obvious fix is to shrink every labelled area by 3 m and throw the border away. That also throws away every example of an edge, and finding where one stand ends and the next begins is most of the job. So we draw the classes exactly as the polygons are, and put a second raster next to it saying how far each pixel is from the nearest border.
We kept stale labels. Each chip records how much of its canopy agrees with its class. A low number means the stand was felled after the polygon was drawn. We left those in rather than rewrite the label.
Evaluation
We kept whole counties out of training, with a 10 km margin around them, instead of picking random tiles. Trees are shorter in the north: chips in Norrbotten have a median canopy of 2.1 m, chips in Skåne 5.5 m. With random tiles the model would have seen the ground next door and the score would flatter it.
- validation: Östergötland, Blekinge
- test: Örebro, Västerbotten
| test | val | |
|---|---|---|
| mIoU | 0.8102 (8 classes) | 0.8013 (6 classes) |
| pixel accuracy | 0.9141 | 0.9119 |
| canopy height R² | 0.6498 | 0.5915 |
| height RMSE / MAE | 3.64 m / 2.05 m | 4.76 m |
Those two mIoUs are not comparable. The southern counties have six of the classes. Västerbotten adds mountain birch and bare mountain, and both score better than average, which lifts the test number. Comparing only the six classes both sides have, val is 0.8013 and test is 0.7903.
Use 0.79.
| class | val | test |
|---|---|---|
| water | 0.975 | 0.906 |
| bare mountain | 0.925 | |
| arable | 0.901 | 0.916 |
| built | 0.874 | 0.901 |
| conifer or mixed | 0.894 | 0.894 |
| mountain birch | 0.815 | |
| open land | 0.564 | 0.580 |
| deciduous | 0.599 | 0.545 |
Checked against laser
The scores above use the same targets the model trained on. So we also scored the height head against laser, over 14 random 512 m windows and 3,568,378 pixels.
| R² against laser | 0.647 (test split gave 0.6498) |
| RMSE | 3.65 m (test split gave 3.64 m) |
| bias, model minus laser | +0.33 m |
The photo is on average 2.6 years newer than the laser. Trees grow in that gap, which is the likely reason the bias is positive.
Limitations
- Deciduous and open land are weak. 0.545 and 0.580, against 0.81 to 0.93 for everything else. They fail for different reasons. Deciduous is only 5.0 % of labelled pixels, and 12x rarer than conifer. Open land is 8.8 % of pixels, but only 29.4 % of it forms blocks, so it is nearly all boundary.
- The class is land use. See above.
- Forest edges are the loosest part of the labels. Median declared uncertainty is 3.0 m on the forest classes and 0.6 m on water and arable, so a stand boundary is about where the map says and not exactly.
- 2019 onward only. The Swedish archive has no near-infrared band before 2019, so change analysis starts there.
- Satellite imagery is untested. The backbone was pretrained on satellite images, but this head only ever saw 0.5 m aerial photos. Sentinel-2 is 20x coarser. We have not tried it, so we do not know how it does.
- Sweden only. Swedish classes, Swedish laser, Swedish test counties.
- Put a threshold on your change maps. Not because the model is unstable: the same chip gives bit-identical classes and heights whatever else is in the batch, measured. But pixels near a class boundary flip easily. Move the cut by 128 m and 22.8 % of the overlap changes class, and two implementations of this model agree on 99.87 % of pixels rather than 100 %.
Serving geometry
The answer depends on where you cut the image. Across a tile join, 33.24 % of pixels change class. Away from a join it is 1.50 %. Centre a chip on the join instead and it drops to 3.23 %.
So we serve a 128 m cell cut from the middle of a 256 m chip. Every pixel we answer sat at least 64 m from the edge of what the model saw.
This is not only an edge problem. 74.7 % of all class flips are two pairs: conifer to deciduous (44.9 %) and built to open land (29.8 %). Those are the same pairs the IoU table is worst on.
Running it
config.json has the band order, the normalisation statistics, the class names and the EPSG code.
Scale uint8 to 0-1, then normalise with input.mean_0_1 and input.std_0_1.
The architecture is custom (TicForestryDinoV3), so AutoModel will not load it.
What it costs to hold
Read off the driver while the runner was serving, at pass width 8:
| MiB | |
|---|---|
| weights (f16 planes, f32 norms) | 587 |
| forward workspace | 785 |
| CUDA context, kernel modules, allocator slack | ~610 |
| total, as nvidia-smi sees it | 1,982 |
Under 2 GB all in, and more than half of that is fixed cost rather than the model. A bigger pass width grows the workspace row and nothing else.
The serving API
We serve this on paddock, our own inference engine. Paddock is open source, dual-licensed MIT or Apache-2.0.
Here is the wire format. It is the one we used to make the reference outputs in fixtures/.
POST /v1/segmentations?response_format=binary
Authorization: Bearer <key>
Content-Type: application/octet-stream
<chip><chip>...
A chip is 512 x 512 x 4 uint8, HWC, interleaved R, G, B, NIR. That is 1,048,576 bytes. Put as many
chips in one body as you like. Add &logits=true if you want the raw logits back.
The response holds one block per chip, in the order you sent them:
| field | bytes | layout |
|---|---|---|
| classes | 65,536 | 256 x 256 uint8. Argmax over the ten classes, row-major |
| height | 262,144 | 256 x 256 float32 little-endian, in metres |
| logits | 1,310,720 | 256 x 256 x 10 float16 little-endian. Only with logits=true |
That is 327,680 bytes per chip, or 1,638,400 with logits.
Two things to get right.
The logits come back pixel-major, as [y][x][class]. Our fixtures are class-major. Read the
wrong way they still decode, and still look like logits. The maximum error is 34, against 0.77 for
the right reading. You will not get an error.
Height is float32 metres, and the head can return negatives. You will see small ones over water and at building edges. That is the model being wrong, not a nodata value.
response_format=json returns the same rasters base64-encoded. It costs about a third more bytes.
Send about 24 chips per request.
Measured 2026-09-20 on paddock, one NVIDIA RTX PRO 6000 Max-Q, over a LAN. One request per row, median of three.
| chips | time | chips/s |
|---|---|---|
| 1 | 0.051 s | 19.4 |
| 4 | 0.096 s | 41.6 |
| 12 | 0.277 s | 43.4 |
| 24 | 0.502 s | 47.8 |
| 48 | 0.967 s | 49.6 |
| 96 | 1.882 s | 51.0 |
One chip per request wastes two thirds of the throughput. Four fixes most of that. Past about 24 the curve is nearly flat, so request size stops being the thing that matters, and larger requests cost you upload and latency: 96 chips is 96 MB and almost two seconds.
Your own numbers will differ with the GPU, the network and whatever else the card is serving.
Reference outputs
fixtures/ holds twelve chips from the test counties. Between them they cover all eight classes
those counties contain. Each chip comes two ways: a raw 512 x 512 x 4 uint8 block, and a georeferenced
4-band GeoTIFF in SWEREF 99 TM. Both come with the reference class argmax and height raster. The
first three chips also carry full logits.
We ported the model to another runtime and checked it against these. It agreed on 99.87 % of pixels, and three chips matched exactly. Every pixel that differed was a near-tie. The median top-1 margin was 0.0156, against 3.383 across the chip. 53 of the 57 sat on a class boundary.
Compare logits with a tolerance, not for equality.
Licence
These weights ship under the DINOv3 License. A copy is in this repository as LICENSE.
Section 1(a) grants a "non-exclusive, worldwide, non-transferable and royalty-free limited license ... to use, reproduce, distribute, copy, create derivative works of, and make modifications to the DINO Materials". There is no limit on commercial use. Section 5(a) says your derivative is yours.
You may redistribute a fine-tuned version. The conditions:
- §1(b)(i). A derivative goes out under the same Agreement, with a copy attached. So these weights cannot be relicensed MIT or Apache, and neither can anything built on them.
- §1(b)(ii). Acknowledge the materials in published research.
- §1(b)(iv). No reverse engineering.
- §1(b)(v). No military, nuclear, espionage or weapons use. Trade controls apply.
- §6 to §8. Meta can terminate on breach, California law applies, and Meta can amend the Agreement.
You do not have to put "DINOv3" in the name.
Training data
All four Lantmäteriet sources are CC BY 4.0. You can use them commercially and build on them. You have to credit them.
| source | where the licence is stated |
|---|---|
| Ortofoto Nedladdning | declared on all 734 collections of api.lantmateriet.se/stac-bild/v1/ |
| Markhöjdmodell | declared in api.lantmateriet.se/stac-hojd/v1/ |
| Laserdata Skog | the STAC says other and links its terms. §3.1 of those terms names CC BY 4.0 and allows commercial use |
| Topografi 10 Nedladdning, vektor | Lantmäteriet product documentation |
Citation
TIC Forestry v1: land cover and canopy height from Swedish orthophotos.
The Intelligence Company, 2026.
DINOv3 ViT-L/16 sat493m backbone (Meta AI), fine-tuned on Lantmäteriet
orthophotos, Topografi 10 and Laserdata Skog.
Contains data from Lantmäteriet, processed:
- Ortofoto Nedladdning, © Lantmäteriet, CC BY 4.0
- Markhöjdmodell, © Lantmäteriet, CC BY 4.0
- Laserdata Nedladdning, skog, © Lantmäteriet, CC BY 4.0
- Topografi 10 Nedladdning, vektor, © Lantmäteriet, CC BY 4.0
Built on DINOv3 (Meta AI), used under the DINOv3 License.
- Downloads last month
- 12
Model tree for tic-oss/tic-forestry-v1
Base model
facebook/dinov3-vit7b16-pretrain-sat493m