Image Segmentation
Transformers
English
semantic-segmentation
segformer
agriculture
orchard
apple
outdoor
Instructions to use WEN0256/Segformer85Mv1 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use WEN0256/Segformer85Mv1 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-segmentation", model="WEN0256/Segformer85Mv1")# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("WEN0256/Segformer85Mv1", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Add Segformer85Mv2 (fine-tuned on Orchard Navigation, autumn+different camera). v1 unchanged.
Browse files- .gitattributes +3 -0
- README.md +78 -21
- Segformer85Mv2.pt +3 -0
- finetune_v7.py +240 -0
- history_v7.json +146 -0
- predict.py +1 -1
- samples_v6_vs_v7/cmp_0_Annotation_Data_Sep16_T_m.jpg +3 -0
- samples_v6_vs_v7/cmp_4_Annotation_Data_Sep16_T_m.jpg +3 -0
- samples_v6_vs_v7/cmp_5_Annotation_Data_Sep16_T_m.jpg +3 -0
.gitattributes
CHANGED
|
@@ -37,3 +37,6 @@ samples/sample_00_frame_2575.jpg filter=lfs diff=lfs merge=lfs -text
|
|
| 37 |
samples/sample_05_frame_3371.jpg filter=lfs diff=lfs merge=lfs -text
|
| 38 |
samples/sample_09_frame_4009.jpg filter=lfs diff=lfs merge=lfs -text
|
| 39 |
v6_OOD_full_res.mp4 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
samples/sample_05_frame_3371.jpg filter=lfs diff=lfs merge=lfs -text
|
| 38 |
samples/sample_09_frame_4009.jpg filter=lfs diff=lfs merge=lfs -text
|
| 39 |
v6_OOD_full_res.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
samples_v6_vs_v7/cmp_0_Annotation_Data_Sep16_T_m.jpg filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
samples_v6_vs_v7/cmp_4_Annotation_Data_Sep16_T_m.jpg filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
samples_v6_vs_v7/cmp_5_Annotation_Data_Sep16_T_m.jpg filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -14,9 +14,16 @@ pipeline_tag: image-segmentation
|
|
| 14 |
base_model: nvidia/segformer-b5-finetuned-ade-640-640
|
| 15 |
---
|
| 16 |
|
| 17 |
-
#
|
| 18 |
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
## Quick Use
|
| 22 |
|
|
@@ -26,8 +33,10 @@ from transformers import SegformerForSemanticSegmentation
|
|
| 26 |
import torch, cv2, numpy as np
|
| 27 |
import torch.nn.functional as F
|
| 28 |
|
| 29 |
-
# 1. Download weights
|
| 30 |
-
ckpt_path = hf_hub_download(repo_id="
|
|
|
|
|
|
|
| 31 |
|
| 32 |
# 2. Init architecture from base + load fine-tuned weights
|
| 33 |
NAMES = ["tree","ground","person","sky","road","mountain","building","background"]
|
|
@@ -65,7 +74,7 @@ A ready-to-use `predict.py` is included in this repo.
|
|
| 65 |
| 2 | person | Workers in scene |
|
| 66 |
| 3 | sky | |
|
| 67 |
| 4 | road | Path between rows |
|
| 68 |
-
| 5 | mountain | Distant terrain
|
| 69 |
| 6 | building | Sheds, equipment shelters |
|
| 70 |
| 7 | background | Unknown / unlabeled regions (model output rare) |
|
| 71 |
|
|
@@ -81,12 +90,12 @@ A ready-to-use `predict.py` is included in this repo.
|
|
| 81 |
| `std` | `[0.229, 0.224, 0.225]` |
|
| 82 |
| Input resolution | Any HΓW where both are multiples of 32 |
|
| 83 |
| Trained at | 1024Γ576 (native 16:9) |
|
| 84 |
-
| Recommended inference | 1280Γ704 or original native (snap to 32-multiple) |
|
| 85 |
-
| Precision | bfloat16 fine β model weights stored in fp32 |
|
| 86 |
|
| 87 |
-
## Performance
|
|
|
|
|
|
|
| 88 |
|
| 89 |
-
Validated on a temporally-disjoint hold-out (frames 4501+
|
| 90 |
|
| 91 |
| Metric | Value |
|
| 92 |
|---|---|
|
|
@@ -94,7 +103,25 @@ Validated on a temporally-disjoint hold-out (frames 4501+ from training set):
|
|
| 94 |
| **mIoU (7 real classes)** | **0.714** |
|
| 95 |
| **Pixel accuracy** | **0.834** |
|
| 96 |
|
| 97 |
-
###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
| Class | IoU | Precision | Recall |
|
| 99 |
|---|---|---|---|
|
| 100 |
| tree | 0.742 | 0.79 | 0.93 |
|
|
@@ -105,20 +132,25 @@ Validated on a temporally-disjoint hold-out (frames 4501+ from training set):
|
|
| 105 |
| mountain | 0.437 | 0.62 | 0.66 |
|
| 106 |
| building | 0.711 | 0.84 | 0.83 |
|
| 107 |
|
| 108 |
-
(Reported values from epoch 21 best-tree checkpoint on the no-leak validation split.)
|
| 109 |
-
|
| 110 |
-
### OOD evaluation
|
| 111 |
-
On a completely held-out recording (1912 frames from `oak_0415_twoRadar_1`, never seen in training), mean prediction confidence is **0.939**, with model predicting `tree` on 41.8% of pixels and falling back to `background` on only 7.4% β indicating strong out-of-distribution generalization.
|
| 112 |
-
|
| 113 |
## Training Data
|
| 114 |
|
| 115 |
-
|
|
|
|
| 116 |
- Initial annotations from 3 separate Roboflow projects (SAM-assisted polygons), merged + class-aligned (`vines`β`tree`, `moutain`β`mountain` typo fixed)
|
| 117 |
- Pseudo-labels generated by an earlier model to fill SAM annotation gaps
|
| 118 |
- Temporal split: frames `<=4500` train (5177 samples), frames `>4500` validation (155 samples) β **no neighbor leakage**
|
| 119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
## Training Recipe
|
| 121 |
|
|
|
|
| 122 |
| Hyperparameter | Value |
|
| 123 |
|---|---|
|
| 124 |
| Optimizer | AdamW, weight_decay 0.01 |
|
|
@@ -131,16 +163,41 @@ On a completely held-out recording (1912 frames from `oak_0415_twoRadar_1`, neve
|
|
| 131 |
| Class weights | tree 1.5, ground 0.5, person 1.5, sky 1.0, road 1.0, mountain 1.0, building 1.0, background 0.1 |
|
| 132 |
| Hardware | RTX 5090 (32 GB), ~2.3 hours |
|
| 133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
## Files in This Repo
|
| 135 |
|
| 136 |
| File | Purpose |
|
| 137 |
|---|---|
|
| 138 |
-
| `Segformer85Mv1.pt` |
|
| 139 |
-
| `
|
|
|
|
| 140 |
| `README.md` | This file |
|
| 141 |
-
| `samples/*.jpg` |
|
| 142 |
-
| `
|
| 143 |
-
| `
|
|
|
|
|
|
|
|
|
|
| 144 |
| `v6_OOD_full_res.mp4` | 1-minute OOD inference video at native resolution |
|
| 145 |
|
| 146 |
## License
|
|
|
|
| 14 |
base_model: nvidia/segformer-b5-finetuned-ade-640-640
|
| 15 |
---
|
| 16 |
|
| 17 |
+
# Segformer85M β Apple Orchard Semantic Segmentation
|
| 18 |
|
| 19 |
+
Segformer-B5 (85M parameters) fine-tuned for **8-class semantic segmentation** of outdoor apple orchard scenes captured from a robotic platform.
|
| 20 |
+
|
| 21 |
+
This repo contains **two checkpoints**:
|
| 22 |
+
|
| 23 |
+
| File | When to use |
|
| 24 |
+
|------|-------------|
|
| 25 |
+
| **`Segformer85Mv1.pt`** | Original v1, trained only on the spring oak_0415 dataset. Best baseline. |
|
| 26 |
+
| **`Segformer85Mv2.pt`** β | v1 + fine-tuned on a second dataset (different camera, autumn season). **Use this for general deployment** β same accuracy on the original orchard, dramatically better generalization to new cameras / new seasons. |
|
| 27 |
|
| 28 |
## Quick Use
|
| 29 |
|
|
|
|
| 33 |
import torch, cv2, numpy as np
|
| 34 |
import torch.nn.functional as F
|
| 35 |
|
| 36 |
+
# 1. Download weights β pick v1 OR v2
|
| 37 |
+
ckpt_path = hf_hub_download(repo_id="WEN0256/Segformer85Mv1", filename="Segformer85Mv2.pt")
|
| 38 |
+
# ^^^^^^^^^^^^^^^^^
|
| 39 |
+
# use v2 by default
|
| 40 |
|
| 41 |
# 2. Init architecture from base + load fine-tuned weights
|
| 42 |
NAMES = ["tree","ground","person","sky","road","mountain","building","background"]
|
|
|
|
| 74 |
| 2 | person | Workers in scene |
|
| 75 |
| 3 | sky | |
|
| 76 |
| 4 | road | Path between rows |
|
| 77 |
+
| 5 | mountain | Distant terrain |
|
| 78 |
| 6 | building | Sheds, equipment shelters |
|
| 79 |
| 7 | background | Unknown / unlabeled regions (model output rare) |
|
| 80 |
|
|
|
|
| 90 |
| `std` | `[0.229, 0.224, 0.225]` |
|
| 91 |
| Input resolution | Any HΓW where both are multiples of 32 |
|
| 92 |
| Trained at | 1024Γ576 (native 16:9) |
|
|
|
|
|
|
|
| 93 |
|
| 94 |
+
## Performance
|
| 95 |
+
|
| 96 |
+
### v1 (Segformer85Mv1.pt) β original training only
|
| 97 |
|
| 98 |
+
Validated on a temporally-disjoint hold-out from the same recording (frames 4501+, no leakage):
|
| 99 |
|
| 100 |
| Metric | Value |
|
| 101 |
|---|---|
|
|
|
|
| 103 |
| **mIoU (7 real classes)** | **0.714** |
|
| 104 |
| **Pixel accuracy** | **0.834** |
|
| 105 |
|
| 106 |
+
### v2 (Segformer85Mv2.pt) β v1 + Orchard Navigation fine-tune β
|
| 107 |
+
|
| 108 |
+
Same v1 hold-out β no regression on old domain:
|
| 109 |
+
|
| 110 |
+
| Metric | v1 | **v2** |
|
| 111 |
+
|---|---|---|
|
| 112 |
+
| Tree IoU (orig orchard, no leak) | 0.742 | **0.742** β
|
|
| 113 |
+
| mIoU (orig orchard) | 0.714 | 0.712 |
|
| 114 |
+
|
| 115 |
+
NEW orchard hold-out (different camera, autumn season β Aug+Sep capture):
|
| 116 |
+
|
| 117 |
+
| Metric | v1 | **v2** |
|
| 118 |
+
|---|---|---|
|
| 119 |
+
| Tree recall on new orchard | ~0.55 (estimated) | **0.999** π |
|
| 120 |
+
|
| 121 |
+
**Visual qualitative**: v1 sometimes misclassifies autumn foliage as `person` (red); v2 cleanly segments it as `tree`. See `samples/` for side-by-side examples.
|
| 122 |
+
|
| 123 |
+
### v1 per-class IoU (8-class, no leak)
|
| 124 |
+
|
| 125 |
| Class | IoU | Precision | Recall |
|
| 126 |
|---|---|---|---|
|
| 127 |
| tree | 0.742 | 0.79 | 0.93 |
|
|
|
|
| 132 |
| mountain | 0.437 | 0.62 | 0.66 |
|
| 133 |
| building | 0.711 | 0.84 | 0.83 |
|
| 134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
## Training Data
|
| 136 |
|
| 137 |
+
### v1 base
|
| 138 |
+
- ~5300 frames from a single oak_0415_oneRadar_1 recording (spring, single camera)
|
| 139 |
- Initial annotations from 3 separate Roboflow projects (SAM-assisted polygons), merged + class-aligned (`vines`β`tree`, `moutain`β`mountain` typo fixed)
|
| 140 |
- Pseudo-labels generated by an earlier model to fill SAM annotation gaps
|
| 141 |
- Temporal split: frames `<=4500` train (5177 samples), frames `>4500` validation (155 samples) β **no neighbor leakage**
|
| 142 |
|
| 143 |
+
### v2 fine-tune (NEW)
|
| 144 |
+
- **+311 images** from "Orchard Navigation" dataset:
|
| 145 |
+
- 178 frames from a Sep-16 recording (autumn season)
|
| 146 |
+
- 134 frames from a Windows webcam capture (Aug 23, different camera/sensor)
|
| 147 |
+
- Tree-only polygon annotations
|
| 148 |
+
- Mixed with 500 sampled v1 images (full 8-class masks) to prevent forgetting
|
| 149 |
+
- Non-tree pixels in new images set to `ignore_index=255` so the model only adapts its tree decisions, leaving other classes untouched
|
| 150 |
+
|
| 151 |
## Training Recipe
|
| 152 |
|
| 153 |
+
### v1
|
| 154 |
| Hyperparameter | Value |
|
| 155 |
|---|---|
|
| 156 |
| Optimizer | AdamW, weight_decay 0.01 |
|
|
|
|
| 163 |
| Class weights | tree 1.5, ground 0.5, person 1.5, sky 1.0, road 1.0, mountain 1.0, building 1.0, background 0.1 |
|
| 164 |
| Hardware | RTX 5090 (32 GB), ~2.3 hours |
|
| 165 |
|
| 166 |
+
### v2 fine-tune (delta from v1)
|
| 167 |
+
| Hyperparameter | Value |
|
| 168 |
+
|---|---|
|
| 169 |
+
| LR | **5e-6** (10Γ lower for safe fine-tune) |
|
| 170 |
+
| Epochs | **8** (best at epoch 3) |
|
| 171 |
+
| `ignore_index` | **255** (for unlabeled pixels in new data) |
|
| 172 |
+
| Everything else | Same as v1 |
|
| 173 |
+
| Hardware | RTX 5090, ~13 minutes |
|
| 174 |
+
|
| 175 |
+
## Limitations
|
| 176 |
+
|
| 177 |
+
This model was trained on a **single Korean apple orchard** (spring 2024) with a **single robot platform**, plus a small fine-tune on a second autumn capture. Expect degradation on:
|
| 178 |
+
|
| 179 |
+
- β οΈ Different orchards (different tree species, layouts, training systems)
|
| 180 |
+
- β οΈ Different cameras (different FOV, color profiles, sensors)
|
| 181 |
+
- π Different seasons not in training (winter dormant trees)
|
| 182 |
+
- π Different lighting (rain, dawn/dusk, night)
|
| 183 |
+
- π Aerial / drone perspectives
|
| 184 |
+
|
| 185 |
+
For deployment in a new context, plan to fine-tune on 100-300 in-domain images.
|
| 186 |
+
|
| 187 |
## Files in This Repo
|
| 188 |
|
| 189 |
| File | Purpose |
|
| 190 |
|---|---|
|
| 191 |
+
| `Segformer85Mv1.pt` | Original v1 weights (339 MB) |
|
| 192 |
+
| `Segformer85Mv2.pt` | v1 + Orchard Navigation fine-tune (339 MB) β |
|
| 193 |
+
| `predict.py` | Standalone inference script (defaults to v2) |
|
| 194 |
| `README.md` | This file |
|
| 195 |
+
| `samples/*.jpg` | v1 prediction examples (in-domain) |
|
| 196 |
+
| `samples_v6_vs_v7/*.jpg` | **v1 vs v2 side-by-side** on new orchard (showcases v2 improvement) |
|
| 197 |
+
| `train_v6_5090.py` | v1 training script |
|
| 198 |
+
| `finetune_v7.py` | v2 fine-tune script |
|
| 199 |
+
| `history_v6.json` | v1 per-epoch training history |
|
| 200 |
+
| `history_v7.json` | v2 fine-tune history |
|
| 201 |
| `v6_OOD_full_res.mp4` | 1-minute OOD inference video at native resolution |
|
| 202 |
|
| 203 |
## License
|
Segformer85Mv2.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ca217b62e6b1adcb1bc03571761ea12ada7a2e752fb2e2fce19ce6f14edd838f
|
| 3 |
+
size 338883907
|
finetune_v7.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""V7 β fine-tune Segformer85Mv1 on Orchard Navigation dataset (tree-only).
|
| 2 |
+
|
| 3 |
+
Strategy:
|
| 4 |
+
- Mix 311 NEW images (tree=0, everything-else=255 ignore) with a SAMPLE of
|
| 5 |
+
500 OLD images (full 8-class masks) β keeps non-tree classes from drifting.
|
| 6 |
+
- Very low LR (5e-6) and few epochs (8) to nudge tree decisions toward the
|
| 7 |
+
new domain (different camera, different season) without catastrophic
|
| 8 |
+
forgetting of old classes.
|
| 9 |
+
- Temporal val from old data (frame > 4500) gives apples-to-apples vs v6.
|
| 10 |
+
- Also reports tree IoU on a held-out chunk of NEW data.
|
| 11 |
+
"""
|
| 12 |
+
from __future__ import annotations
|
| 13 |
+
import json, re, time, random
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
import numpy as np, cv2, torch
|
| 16 |
+
import torch.nn as nn
|
| 17 |
+
import torch.nn.functional as F
|
| 18 |
+
from torch.utils.data import Dataset, DataLoader, ConcatDataset
|
| 19 |
+
from torch.amp import autocast
|
| 20 |
+
import albumentations as A
|
| 21 |
+
from transformers import SegformerForSemanticSegmentation
|
| 22 |
+
|
| 23 |
+
# βββββββββββββ config βββββββββββββ
|
| 24 |
+
ROOT = Path("/workspace/agmotree")
|
| 25 |
+
OLD_IMG = ROOT / "old_data/images"
|
| 26 |
+
OLD_MSK = ROOT / "old_data/masks_pseudo"
|
| 27 |
+
NEW_IMG = ROOT / "new_data/images"
|
| 28 |
+
NEW_MSK = ROOT / "new_data/masks"
|
| 29 |
+
CKPT_IN = ROOT / "Segformer85Mv1.pt"
|
| 30 |
+
OUT_DIR = ROOT / "v7_output"
|
| 31 |
+
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
| 32 |
+
|
| 33 |
+
NAMES = ["tree","ground","person","sky","road","mountain","building","background"]
|
| 34 |
+
NUM_CLASSES = 8
|
| 35 |
+
IGNORE_INDEX = 255
|
| 36 |
+
|
| 37 |
+
IMG_W = 1024
|
| 38 |
+
IMG_H = 576
|
| 39 |
+
BATCH = 2
|
| 40 |
+
GRAD_ACCUM = 4
|
| 41 |
+
EPOCHS = 8
|
| 42 |
+
LR = 5e-6
|
| 43 |
+
N_OLD_SAMPLE = 500 # how many old images to mix in
|
| 44 |
+
SEED = 42
|
| 45 |
+
|
| 46 |
+
# Class weights β heavier on tree, normal on others
|
| 47 |
+
WEIGHTS = np.array([1.5, 0.5, 1.5, 1.0, 1.0, 1.0, 1.0, 0.1])
|
| 48 |
+
|
| 49 |
+
# βββββββββββββ data βββββββββββββ
|
| 50 |
+
def frame_num(p):
|
| 51 |
+
m = re.match(r"frame_(\d+)", p.stem); return int(m.group(1)) if m else -1
|
| 52 |
+
|
| 53 |
+
random.seed(SEED); np.random.seed(SEED); torch.manual_seed(SEED)
|
| 54 |
+
|
| 55 |
+
old_all = sorted(OLD_IMG.glob("*.jpg"))
|
| 56 |
+
old_train = [p for p in old_all if frame_num(p) <= 4500]
|
| 57 |
+
old_val = [p for p in old_all if frame_num(p) > 4500]
|
| 58 |
+
old_train_sampled = random.sample(old_train, min(N_OLD_SAMPLE, len(old_train)))
|
| 59 |
+
|
| 60 |
+
new_all = sorted(NEW_IMG.glob("*.jpg"))
|
| 61 |
+
random.shuffle(new_all)
|
| 62 |
+
n_new_val = max(20, len(new_all) // 10)
|
| 63 |
+
new_val = new_all[:n_new_val]
|
| 64 |
+
new_train = new_all[n_new_val:]
|
| 65 |
+
|
| 66 |
+
print(f"=== V7 FINE-TUNE ===")
|
| 67 |
+
print(f" old train (sampled): {len(old_train_sampled)} (8-class masks)")
|
| 68 |
+
print(f" new train: {len(new_train)} (tree-only, rest ignore)")
|
| 69 |
+
print(f" old val (no leak): {len(old_val)} (frames > 4500)")
|
| 70 |
+
print(f" new val: {len(new_val)} (held-out new data)")
|
| 71 |
+
|
| 72 |
+
train_tf = A.Compose([
|
| 73 |
+
A.Resize(IMG_H, IMG_W),
|
| 74 |
+
A.HorizontalFlip(p=0.5),
|
| 75 |
+
A.RandomBrightnessContrast(0.2, 0.2, p=0.5),
|
| 76 |
+
A.HueSaturationValue(10, 15, 10, p=0.3),
|
| 77 |
+
A.GaussianBlur(blur_limit=(3,5), p=0.2),
|
| 78 |
+
A.Normalize(mean=(0.485,0.456,0.406), std=(0.229,0.224,0.225)),
|
| 79 |
+
])
|
| 80 |
+
val_tf = A.Compose([
|
| 81 |
+
A.Resize(IMG_H, IMG_W),
|
| 82 |
+
A.Normalize(mean=(0.485,0.456,0.406), std=(0.229,0.224,0.225)),
|
| 83 |
+
])
|
| 84 |
+
|
| 85 |
+
class SegDS(Dataset):
|
| 86 |
+
def __init__(self, paths, mask_dir, tf):
|
| 87 |
+
self.paths = paths; self.mask_dir = mask_dir; self.tf = tf
|
| 88 |
+
def __len__(self): return len(self.paths)
|
| 89 |
+
def __getitem__(self, i):
|
| 90 |
+
ip = self.paths[i]
|
| 91 |
+
img = cv2.cvtColor(cv2.imread(str(ip)), cv2.COLOR_BGR2RGB)
|
| 92 |
+
msk = cv2.imread(str(self.mask_dir / (ip.stem + ".png")), cv2.IMREAD_GRAYSCALE)
|
| 93 |
+
out = self.tf(image=img, mask=msk)
|
| 94 |
+
return (torch.from_numpy(out["image"]).permute(2,0,1).float(),
|
| 95 |
+
torch.from_numpy(out["mask"]).long())
|
| 96 |
+
|
| 97 |
+
old_train_ds = SegDS(old_train_sampled, OLD_MSK, train_tf)
|
| 98 |
+
new_train_ds = SegDS(new_train, NEW_MSK, train_tf)
|
| 99 |
+
old_val_ds = SegDS(old_val, OLD_MSK, val_tf)
|
| 100 |
+
new_val_ds = SegDS(new_val, NEW_MSK, val_tf)
|
| 101 |
+
|
| 102 |
+
# βββββββββββββ eval helpers βββββββββββββ
|
| 103 |
+
def confusion(preds, ys, n):
|
| 104 |
+
cm = np.zeros((n, n), dtype=np.int64)
|
| 105 |
+
for tc in range(n):
|
| 106 |
+
mt = (ys == tc)
|
| 107 |
+
if not mt.any(): continue
|
| 108 |
+
for pc in range(n):
|
| 109 |
+
cm[tc, pc] += int(((preds == pc) & mt).sum())
|
| 110 |
+
return cm
|
| 111 |
+
|
| 112 |
+
def iou_from_cm(cm):
|
| 113 |
+
n = cm.shape[0]; ious = np.zeros(n)
|
| 114 |
+
for c in range(n):
|
| 115 |
+
tp = cm[c,c]; fp = cm[:,c].sum()-tp; fn = cm[c,:].sum()-tp
|
| 116 |
+
ious[c] = tp / (tp+fp+fn) if (tp+fp+fn) > 0 else float("nan")
|
| 117 |
+
return ious
|
| 118 |
+
|
| 119 |
+
# βββββββββββββ train βββββββββββββ
|
| 120 |
+
log_path = OUT_DIR / "log_v7.txt"
|
| 121 |
+
def log(m):
|
| 122 |
+
print(m, flush=True)
|
| 123 |
+
with log_path.open("a", encoding="utf-8") as f: f.write(m + "\n")
|
| 124 |
+
|
| 125 |
+
def main():
|
| 126 |
+
log_path.write_text("")
|
| 127 |
+
train_ds = ConcatDataset([old_train_ds, new_train_ds])
|
| 128 |
+
train_loader = DataLoader(train_ds, batch_size=BATCH, shuffle=True,
|
| 129 |
+
num_workers=8, pin_memory=True, drop_last=True,
|
| 130 |
+
persistent_workers=True)
|
| 131 |
+
old_val_loader = DataLoader(old_val_ds, batch_size=BATCH, shuffle=False,
|
| 132 |
+
num_workers=4, pin_memory=True, persistent_workers=True)
|
| 133 |
+
new_val_loader = DataLoader(new_val_ds, batch_size=BATCH, shuffle=False,
|
| 134 |
+
num_workers=4, pin_memory=True, persistent_workers=True)
|
| 135 |
+
|
| 136 |
+
log(f"=== V7 FINE-TUNE ===")
|
| 137 |
+
log(f"old train sampled={len(old_train_sampled)} new train={len(new_train)}")
|
| 138 |
+
log(f"old val (no-leak frames>4500): {len(old_val)} new val: {len(new_val)}")
|
| 139 |
+
log(f"loading {CKPT_IN} ...")
|
| 140 |
+
model = SegformerForSemanticSegmentation.from_pretrained(
|
| 141 |
+
"nvidia/segformer-b5-finetuned-ade-640-640", num_labels=NUM_CLASSES,
|
| 142 |
+
id2label={i:n for i,n in enumerate(NAMES)},
|
| 143 |
+
label2id={n:i for i,n in enumerate(NAMES)},
|
| 144 |
+
ignore_mismatched_sizes=True,
|
| 145 |
+
).cuda()
|
| 146 |
+
ckpt = torch.load(CKPT_IN, map_location="cuda", weights_only=False)
|
| 147 |
+
model.load_state_dict(ckpt["model"])
|
| 148 |
+
log(f" loaded v6 ckpt: epoch {ckpt['epoch']}, prev tree IoU {ckpt['tree_iou']:.3f}")
|
| 149 |
+
|
| 150 |
+
cw = torch.tensor(WEIGHTS, dtype=torch.float32, device="cuda")
|
| 151 |
+
loss_fn = nn.CrossEntropyLoss(weight=cw, ignore_index=IGNORE_INDEX)
|
| 152 |
+
optim = torch.optim.AdamW(model.parameters(), lr=LR, weight_decay=1e-2)
|
| 153 |
+
sched = torch.optim.lr_scheduler.CosineAnnealingLR(optim, T_max=EPOCHS*len(train_loader))
|
| 154 |
+
|
| 155 |
+
log(f"train batches: {len(train_loader)}")
|
| 156 |
+
|
| 157 |
+
best_avg_tree = -1.0
|
| 158 |
+
history = []
|
| 159 |
+
for epoch in range(1, EPOCHS+1):
|
| 160 |
+
model.train()
|
| 161 |
+
t0 = time.time()
|
| 162 |
+
epoch_loss = 0.0
|
| 163 |
+
optim.zero_grad()
|
| 164 |
+
for step, (x, y) in enumerate(train_loader):
|
| 165 |
+
x = x.cuda(non_blocking=True); y = y.cuda(non_blocking=True)
|
| 166 |
+
with autocast("cuda", dtype=torch.bfloat16):
|
| 167 |
+
out = model(pixel_values=x)
|
| 168 |
+
logits = F.interpolate(out.logits, size=y.shape[-2:], mode="bilinear", align_corners=False)
|
| 169 |
+
loss = loss_fn(logits, y) / GRAD_ACCUM
|
| 170 |
+
loss.backward()
|
| 171 |
+
if (step+1) % GRAD_ACCUM == 0:
|
| 172 |
+
optim.step(); optim.zero_grad(); sched.step()
|
| 173 |
+
epoch_loss += loss.item() * GRAD_ACCUM
|
| 174 |
+
train_loss = epoch_loss / len(train_loader)
|
| 175 |
+
|
| 176 |
+
# βββ eval on old (8-class) and new (tree-only) βββ
|
| 177 |
+
model.eval()
|
| 178 |
+
cm_old = np.zeros((NUM_CLASSES, NUM_CLASSES), dtype=np.int64)
|
| 179 |
+
cm_new = np.zeros((NUM_CLASSES, NUM_CLASSES), dtype=np.int64)
|
| 180 |
+
with torch.no_grad():
|
| 181 |
+
for x, y in old_val_loader:
|
| 182 |
+
x = x.cuda(); y = y.cuda()
|
| 183 |
+
with autocast("cuda", dtype=torch.bfloat16):
|
| 184 |
+
out = model(pixel_values=x)
|
| 185 |
+
logits = F.interpolate(out.logits, size=y.shape[-2:], mode="bilinear", align_corners=False)
|
| 186 |
+
cm_old += confusion(logits.argmax(1).cpu().numpy(), y.cpu().numpy(), NUM_CLASSES)
|
| 187 |
+
tree_tp = tree_fn = 0 # only tree class is meaningful (others not labeled)
|
| 188 |
+
for x, y in new_val_loader:
|
| 189 |
+
x = x.cuda(); y = y.cuda()
|
| 190 |
+
with autocast("cuda", dtype=torch.bfloat16):
|
| 191 |
+
out = model(pixel_values=x)
|
| 192 |
+
logits = F.interpolate(out.logits, size=y.shape[-2:], mode="bilinear", align_corners=False)
|
| 193 |
+
pred = logits.argmax(1).cpu().numpy()
|
| 194 |
+
ys = y.cpu().numpy()
|
| 195 |
+
# Tree recall = TP / (TP+FN) over labeled tree pixels only.
|
| 196 |
+
# Ignored pixels (ys==255) are excluded entirely.
|
| 197 |
+
tree_mask = (ys == 0)
|
| 198 |
+
tree_tp += int(((pred == 0) & tree_mask).sum())
|
| 199 |
+
tree_fn += int(((pred != 0) & tree_mask).sum())
|
| 200 |
+
|
| 201 |
+
iou_old = iou_from_cm(cm_old)
|
| 202 |
+
miou_old = float(np.nanmean(iou_old[:7]))
|
| 203 |
+
tree_old = float(iou_old[0])
|
| 204 |
+
# NEW val: only tree-recall is meaningful (other classes unlabeled)
|
| 205 |
+
tree_recall_new = tree_tp / (tree_tp + tree_fn) if (tree_tp + tree_fn) > 0 else float("nan")
|
| 206 |
+
avg_tree = (tree_old + tree_recall_new) / 2
|
| 207 |
+
|
| 208 |
+
elapsed = time.time() - t0
|
| 209 |
+
log(f"epoch {epoch:02d}/{EPOCHS} tloss={train_loss:.4f} ({elapsed:.0f}s)")
|
| 210 |
+
log(f" OLD val (8-class): mIoU(7)={miou_old:.3f} tree IoU={tree_old:.3f}")
|
| 211 |
+
log(f" NEW val: tree RECALL={tree_recall_new:.3f} (TP={tree_tp:,} FN={tree_fn:,})")
|
| 212 |
+
log(f" per-class OLD: " + ", ".join(f"{n}={v:.3f}" for n,v in zip(NAMES, iou_old)))
|
| 213 |
+
|
| 214 |
+
history.append({
|
| 215 |
+
"epoch": epoch, "train_loss": float(train_loss),
|
| 216 |
+
"tree_iou_old": tree_old, "tree_recall_new": tree_recall_new,
|
| 217 |
+
"miou_old_7": miou_old, "avg_tree": avg_tree,
|
| 218 |
+
"per_class_iou_old": {n: float(v) for n, v in zip(NAMES, iou_old)},
|
| 219 |
+
})
|
| 220 |
+
|
| 221 |
+
torch.save({"model": model.state_dict(), "epoch": epoch,
|
| 222 |
+
"tree_iou_old": tree_old, "tree_recall_new": tree_recall_new,
|
| 223 |
+
"miou_old_7": miou_old},
|
| 224 |
+
OUT_DIR / "v7_last.pt")
|
| 225 |
+
if avg_tree > best_avg_tree:
|
| 226 |
+
best_avg_tree = avg_tree
|
| 227 |
+
torch.save({"model": model.state_dict(), "epoch": epoch,
|
| 228 |
+
"tree_iou_old": tree_old, "tree_recall_new": tree_recall_new,
|
| 229 |
+
"miou_old_7": miou_old},
|
| 230 |
+
OUT_DIR / "v7_best.pt")
|
| 231 |
+
log(f" saved v7_best.pt (avg {avg_tree:.3f})")
|
| 232 |
+
|
| 233 |
+
(OUT_DIR / "history_v7.json").write_text(json.dumps(history, indent=2))
|
| 234 |
+
|
| 235 |
+
log(f"\n=== DONE ===")
|
| 236 |
+
log(f"best avg tree IoU (old+new mean): {best_avg_tree:.3f}")
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
if __name__ == "__main__":
|
| 240 |
+
main()
|
history_v7.json
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"epoch": 1,
|
| 4 |
+
"train_loss": 0.1445474764559036,
|
| 5 |
+
"tree_iou_old": 0.7276111213384829,
|
| 6 |
+
"tree_recall_new": 0.9964709949014234,
|
| 7 |
+
"miou_old_7": 0.7071341866953114,
|
| 8 |
+
"avg_tree": 0.8620410581199531,
|
| 9 |
+
"per_class_iou_old": {
|
| 10 |
+
"tree": 0.7276111213384829,
|
| 11 |
+
"ground": 0.8545028283913517,
|
| 12 |
+
"person": 0.7197452556695519,
|
| 13 |
+
"sky": 0.7665161031217024,
|
| 14 |
+
"road": 0.7716666106060096,
|
| 15 |
+
"mountain": 0.4105051777602567,
|
| 16 |
+
"building": 0.6993922099798241,
|
| 17 |
+
"background": 0.30209167992875857
|
| 18 |
+
}
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"epoch": 2,
|
| 22 |
+
"train_loss": 0.09120039182344977,
|
| 23 |
+
"tree_iou_old": 0.7348800379399489,
|
| 24 |
+
"tree_recall_new": 0.9992896559386689,
|
| 25 |
+
"miou_old_7": 0.7107307502748205,
|
| 26 |
+
"avg_tree": 0.8670848469393089,
|
| 27 |
+
"per_class_iou_old": {
|
| 28 |
+
"tree": 0.7348800379399489,
|
| 29 |
+
"ground": 0.853819505788582,
|
| 30 |
+
"person": 0.7223546582340064,
|
| 31 |
+
"sky": 0.7675516469276799,
|
| 32 |
+
"road": 0.7742544234364183,
|
| 33 |
+
"mountain": 0.41988628752707635,
|
| 34 |
+
"building": 0.7023686920700309,
|
| 35 |
+
"background": 0.32039096359921654
|
| 36 |
+
}
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"epoch": 3,
|
| 40 |
+
"train_loss": 0.08554688886655733,
|
| 41 |
+
"tree_iou_old": 0.74153728943059,
|
| 42 |
+
"tree_recall_new": 0.9995758181795736,
|
| 43 |
+
"miou_old_7": 0.71243266621694,
|
| 44 |
+
"avg_tree": 0.8705565538050819,
|
| 45 |
+
"per_class_iou_old": {
|
| 46 |
+
"tree": 0.74153728943059,
|
| 47 |
+
"ground": 0.8535114953849786,
|
| 48 |
+
"person": 0.721128950704959,
|
| 49 |
+
"sky": 0.7701010375444943,
|
| 50 |
+
"road": 0.7727083269046633,
|
| 51 |
+
"mountain": 0.4298014657280298,
|
| 52 |
+
"building": 0.6982400978208655,
|
| 53 |
+
"background": 0.34584085232597234
|
| 54 |
+
}
|
| 55 |
+
},
|
| 56 |
+
{
|
| 57 |
+
"epoch": 4,
|
| 58 |
+
"train_loss": 0.08495914258924014,
|
| 59 |
+
"tree_iou_old": 0.7386737697019637,
|
| 60 |
+
"tree_recall_new": 0.9998306235571522,
|
| 61 |
+
"miou_old_7": 0.7108516336566473,
|
| 62 |
+
"avg_tree": 0.869252196629558,
|
| 63 |
+
"per_class_iou_old": {
|
| 64 |
+
"tree": 0.7386737697019637,
|
| 65 |
+
"ground": 0.8530840272060696,
|
| 66 |
+
"person": 0.7226071760588065,
|
| 67 |
+
"sky": 0.7702586411872955,
|
| 68 |
+
"road": 0.7661165021954849,
|
| 69 |
+
"mountain": 0.42538260855725873,
|
| 70 |
+
"building": 0.699838710689653,
|
| 71 |
+
"background": 0.3418799912840437
|
| 72 |
+
}
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"epoch": 5,
|
| 76 |
+
"train_loss": 0.0824889050444397,
|
| 77 |
+
"tree_iou_old": 0.7374752361770358,
|
| 78 |
+
"tree_recall_new": 0.999875807068874,
|
| 79 |
+
"miou_old_7": 0.7075837051390604,
|
| 80 |
+
"avg_tree": 0.8686755216229549,
|
| 81 |
+
"per_class_iou_old": {
|
| 82 |
+
"tree": 0.7374752361770358,
|
| 83 |
+
"ground": 0.8517622487225316,
|
| 84 |
+
"person": 0.7170282863817369,
|
| 85 |
+
"sky": 0.7688366303765454,
|
| 86 |
+
"road": 0.7567630529761218,
|
| 87 |
+
"mountain": 0.42263572337088734,
|
| 88 |
+
"building": 0.6985847579685641,
|
| 89 |
+
"background": 0.3276835145430565
|
| 90 |
+
}
|
| 91 |
+
},
|
| 92 |
+
{
|
| 93 |
+
"epoch": 6,
|
| 94 |
+
"train_loss": NaN,
|
| 95 |
+
"tree_iou_old": 0.7397202992344901,
|
| 96 |
+
"tree_recall_new": 0.9999024727479229,
|
| 97 |
+
"miou_old_7": 0.7127320425514964,
|
| 98 |
+
"avg_tree": 0.8698113859912064,
|
| 99 |
+
"per_class_iou_old": {
|
| 100 |
+
"tree": 0.7397202992344901,
|
| 101 |
+
"ground": 0.853263524499737,
|
| 102 |
+
"person": 0.7202039547444198,
|
| 103 |
+
"sky": 0.7715238413193277,
|
| 104 |
+
"road": 0.7738255330134594,
|
| 105 |
+
"mountain": 0.4198599823783031,
|
| 106 |
+
"building": 0.7107271626707372,
|
| 107 |
+
"background": 0.34450614720925465
|
| 108 |
+
}
|
| 109 |
+
},
|
| 110 |
+
{
|
| 111 |
+
"epoch": 7,
|
| 112 |
+
"train_loss": NaN,
|
| 113 |
+
"tree_iou_old": 0.7398512261769766,
|
| 114 |
+
"tree_recall_new": 0.9999254348604373,
|
| 115 |
+
"miou_old_7": 0.7132055573889754,
|
| 116 |
+
"avg_tree": 0.869888330518707,
|
| 117 |
+
"per_class_iou_old": {
|
| 118 |
+
"tree": 0.7398512261769766,
|
| 119 |
+
"ground": 0.8539561450004414,
|
| 120 |
+
"person": 0.7196386093776438,
|
| 121 |
+
"sky": 0.7709864078362132,
|
| 122 |
+
"road": 0.7727917702136876,
|
| 123 |
+
"mountain": 0.4276638710715956,
|
| 124 |
+
"building": 0.707550872046269,
|
| 125 |
+
"background": 0.36019225517621223
|
| 126 |
+
}
|
| 127 |
+
},
|
| 128 |
+
{
|
| 129 |
+
"epoch": 8,
|
| 130 |
+
"train_loss": 0.07825338785261966,
|
| 131 |
+
"tree_iou_old": 0.7386024885258515,
|
| 132 |
+
"tree_recall_new": 0.9999464217374665,
|
| 133 |
+
"miou_old_7": 0.7114400608211693,
|
| 134 |
+
"avg_tree": 0.869274455131659,
|
| 135 |
+
"per_class_iou_old": {
|
| 136 |
+
"tree": 0.7386024885258515,
|
| 137 |
+
"ground": 0.8543745147138091,
|
| 138 |
+
"person": 0.72068353262178,
|
| 139 |
+
"sky": 0.7693893529932668,
|
| 140 |
+
"road": 0.7702198236853051,
|
| 141 |
+
"mountain": 0.4261176724920308,
|
| 142 |
+
"building": 0.700693040716142,
|
| 143 |
+
"background": 0.34337551501723146
|
| 144 |
+
}
|
| 145 |
+
}
|
| 146 |
+
]
|
predict.py
CHANGED
|
@@ -21,7 +21,7 @@ from transformers import SegformerForSemanticSegmentation
|
|
| 21 |
|
| 22 |
# βββ config βββ
|
| 23 |
BASE_MODEL = "nvidia/segformer-b5-finetuned-ade-640-640"
|
| 24 |
-
WEIGHTS_PATH = os.environ.get("
|
| 25 |
NAMES = ["tree", "ground", "person", "sky", "road", "mountain", "building", "background"]
|
| 26 |
PALETTE = np.array([
|
| 27 |
[60, 220, 60], # tree - green
|
|
|
|
| 21 |
|
| 22 |
# βββ config βββ
|
| 23 |
BASE_MODEL = "nvidia/segformer-b5-finetuned-ade-640-640"
|
| 24 |
+
WEIGHTS_PATH = os.environ.get("SEGFORMER85M_WEIGHTS", "Segformer85Mv2.pt") # default v2 (best generalization); set env var or --weights to use v1
|
| 25 |
NAMES = ["tree", "ground", "person", "sky", "road", "mountain", "building", "background"]
|
| 26 |
PALETTE = np.array([
|
| 27 |
[60, 220, 60], # tree - green
|
samples_v6_vs_v7/cmp_0_Annotation_Data_Sep16_T_m.jpg
ADDED
|
Git LFS Details
|
samples_v6_vs_v7/cmp_4_Annotation_Data_Sep16_T_m.jpg
ADDED
|
Git LFS Details
|
samples_v6_vs_v7/cmp_5_Annotation_Data_Sep16_T_m.jpg
ADDED
|
Git LFS Details
|