vuong69 commited on
Commit
a62bc30
·
verified ·
1 Parent(s): dcc7280

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/loss/base.yaml +3 -0
  2. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/anyup.yaml +2 -0
  3. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/bilinear.yaml +2 -0
  4. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/featup.yaml +5 -0
  5. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/ircnn.yaml +5 -0
  6. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/jafar.yaml +10 -0
  7. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/jbf.yaml +5 -0
  8. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/jbu.yaml +5 -0
  9. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/naf.yaml +16 -0
  10. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/nearest.yaml +3 -0
  11. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/rednet.yaml +5 -0
  12. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/restormer.yaml +4 -0
  13. Pixal3D/torch_hub/hub/valeoai_NAF_main/config/optimizer/adamw.yaml +4 -0
  14. Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/DATASETS.md +25 -0
  15. Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/EVALUATIONS.md +8 -0
  16. Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/INSTALL.md +39 -0
  17. Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/README.md +18 -0
  18. Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/TRAINING.md +23 -0
  19. Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/data.sh +51 -0
  20. Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/ade20k.py +231 -0
  21. Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/cityscapes.py +115 -0
  22. Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/coco.py +317 -0
  23. Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/davis.py +61 -0
  24. Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/image_dataset.py +118 -0
  25. Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/kitti360.py +196 -0
  26. Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/kitti360/val_split.json +0 -0
  27. Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/voc.py +113 -0
  28. Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/eval_depth_probing.py +448 -0
  29. Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/eval_seg_probing.py +393 -0
  30. Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/eval_video_seg.py +816 -0
  31. Pixal3D/torch_hub/hub/valeoai_NAF_main/hydra_plugins/resolvers.py +41 -0
  32. Pixal3D/torch_hub/hub/valeoai_NAF_main/notebooks/attention_maps.ipynb +389 -0
  33. Pixal3D/torch_hub/hub/valeoai_NAF_main/notebooks/inference.ipynb +294 -0
  34. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/backbone/__init__.py +1 -0
  35. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/backbone/vit_wrapper.py +180 -0
  36. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/__init__.py +3 -0
  37. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/__pycache__/__init__.cpython-311.pyc +0 -0
  38. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/__pycache__/attentions.cpython-311.pyc +0 -0
  39. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/__pycache__/convolutions.cpython-311.pyc +0 -0
  40. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/__pycache__/rope.cpython-311.pyc +0 -0
  41. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/attentions.py +75 -0
  42. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/convolutions.py +92 -0
  43. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/rope.py +174 -0
  44. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/loss.py +45 -0
  45. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__init__.py +12 -0
  46. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__pycache__/__init__.cpython-311.pyc +0 -0
  47. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__pycache__/anyup.cpython-311.pyc +0 -0
  48. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__pycache__/base.cpython-311.pyc +0 -0
  49. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__pycache__/bilinear.cpython-311.pyc +0 -0
  50. Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__pycache__/featup.cpython-311.pyc +0 -0
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/loss/base.yaml ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ mse:
2
+ _target_: src.loss.Loss
3
+ loss_type: mse
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/anyup.yaml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ _target_: src.model.AnyUpsampler
2
+ name: anyup
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/bilinear.yaml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ _target_: src.model.Bilinear
2
+ name: bilinear
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/featup.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ _target_: src.model.FeatUp
2
+ feature_dim: ${get_feature:${backbone.name}}
3
+ backbone_name: ${backbone.name}
4
+ name: featup
5
+ ratio: 16
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/ircnn.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ _target_: src.model.IRCNN
2
+ in_nc: 3
3
+ out_nc: 3
4
+ nc: 64
5
+ name: ircnn
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/jafar.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+
4
+ _target_: src.model.JAFAR
5
+ dim: 128
6
+ v_dim: ${model.feature_dim}
7
+ feature_dim: ${get_feature:${backbone.name}}
8
+ kernel_size: 1
9
+ num_heads: 4
10
+ name: jafar
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/jbf.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ _target_: src.model.JBF
2
+ kernel_size: 5
3
+ sigma_color: 0.1
4
+ sigma_spatial: 1.5
5
+ name: jbf
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/jbu.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ _target_: src.model.JBU
2
+ dim: 256
3
+ radius: 5
4
+ combine: False
5
+ name: jbu
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/naf.yaml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+
4
+ _target_: src.model.naf.NAF
5
+ dim: 256
6
+ heads_attn: 4
7
+ heads_rope: 4
8
+ kernel_size: 9
9
+
10
+ # ImageEncoder options
11
+ use_semencoder: true
12
+ use_encoder: true
13
+ img_layers: 2
14
+ rope_rescale: 2
15
+
16
+ name: naf
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/nearest.yaml ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ _target_: src.model.Nearest
2
+ feature_dim: ${get_feature:${backbone.name}}
3
+ name: nearest
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/rednet.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ _target_: src.model.REDNet
2
+ num_layers: 15
3
+ num_features: 64
4
+ input_dim: 3
5
+ name: dncnn
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/model/restormer.yaml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ _target_: src.model.Restormer
2
+ inp_channels: 3
3
+ out_channels: 3
4
+ name: restormer
Pixal3D/torch_hub/hub/valeoai_NAF_main/config/optimizer/adamw.yaml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ _target_: torch.optim.AdamW
2
+ lr: 2e-4
3
+ betas: [0.9, 0.999]
4
+ weight_decay: 1e-5
Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/DATASETS.md ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Datasets
2
+
3
+ Create a data folder corresponding to the root data directory represented in the configuration file by `data_root: data/`.
4
+
5
+ We train NAF using the ImageNet dataset, but it can be trained on any dataset of natural images. Then we evaluate NAF on several downstream tasks using the following datasets:
6
+ - Semantic Segmentation: Cityscapes, PASCAL VOC 2012, ADE20K, KITTI-360
7
+ - Depth Estimation: NYU Depth V2
8
+ - Video Object Segmentation: DAVIS 2017
9
+
10
+ To download the datasets used for training and evaluation, please refer to the [docs/data.sh](data.sh) file or see the official dataset documentation.
11
+
12
+ The data folder should have the following structure:
13
+
14
+ ```
15
+ data/
16
+ (training)
17
+ └── ImageNet/
18
+ (evaluation)
19
+ ├── cityscapes/
20
+ ├── VOCdevkit/
21
+ ├── ADEChallengeData2016/
22
+ ├── KITTI-360/
23
+ ├── DAVIS/
24
+ └── ...
25
+ ```
Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/EVALUATIONS.md ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # 🔄 Evaluation
2
+
3
+ You should put the loaded weight file `naf_release.pth` in the `output/` folder.
4
+
5
+ To evaluate the upsampler on semantic segmentation, simply run:
6
+ ``` python
7
+ python evaluation/eval_seg_probing.py dataset=voc eval.model_ckpt=output/naf_release.pth
8
+ ```
Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/INSTALL.md ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Installation Instructions
2
+ ## Install Natten
3
+ NAF is based on natten. Natten is build for specific torch versions. You simply need to install the compatible natten version for your torch version. For instance:
4
+
5
+ ```bash
6
+ # Version used for developping.
7
+ pip install natten==0.17.4+torch240cu118 -f https://shi-labs.com/natten/wheels/
8
+ ```
9
+ For newer versions of torch or natten check this [url](https://github.com/SHI-Labs/NATTEN/blob/main/docs/install.md).
10
+ For older versions of torch or natten check this [url](https://shi-labs.com/natten/wheels/).
11
+
12
+ We have verified the following compatibilities:
13
+ ```base
14
+ # For torch 2.7.0 + cu118
15
+ pip install torch==2.7.0 torchvision==0.22.0 --index-url https://download.pytorch.org/whl/cu128
16
+ pip install natten==0.20.1+torch270cu128 -f https://whl.natten.org
17
+
18
+ # For torch 2.4.0 + cu118
19
+ pip install torch==2.0.0 torchvision==0.15.1 --index-url https://download.pytorch.org/whl/cu118
20
+ pip install natten==0.17.3+torch200cu117 -f https://shi-labs.com/natten/wheels/
21
+ ```
22
+
23
+ Do not hesitate to open an issue if you face any problem installing natten.
24
+
25
+ ## Complete environment
26
+ We used the following environment for developping NAF. We recommend using [micromamba](https://mamba.readthedocs.io/en/latest/installation/micromamba-installation.html) to create the environment and using [uv](https://github.com/astral-sh/uv) to speed up pip installs.
27
+
28
+ ``` bash
29
+ micromamba create -n naf python==3.10.14 -y -c defaults
30
+ micromamba activate naf
31
+ pip install uv
32
+
33
+ # Install torch and natten
34
+ uv pip install torch==2.4.0 torchvision==0.19.0 --index-url https://download.pytorch.org/whl/cu118
35
+ pip install natten==0.17.4+torch240cu118 -f https://shi-labs.com/natten/wheels/
36
+
37
+ # Install other dependencies
38
+ uv pip install einops==0.8.0 numpy==1.24.4 timm==1.0.22 plotly==6.0.0 tensorboard==2.20.0 hydra-core==1.3.2 plotly==6.0.0 matplotlib==3.7.0 rich==14.2.0 torchmetrics==1.6.2 scipy==1.15.2 kornia==0.8.2 ipykernel ipympl pytest
39
+ ```
Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/README.md ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 📁 Repository Structure
2
+
3
+ The repository contains the important folders:
4
+ ```
5
+ |-- configs/ # Configuration files
6
+ |-- docs/ # Documentation
7
+ |-- evaluation/ # Scripts to reproduce results and datasets initialization
8
+ |-- notebooks/ # Jupyter notebooks for inference (of any VFM at any scale) and attention map visualizations
9
+ |-- src/ # Source code of the project
10
+ |-- test/ # Unit tests to compare model efficiency
11
+ ```
12
+
13
+
14
+ # 🔍 Tests
15
+
16
+ We provide unit tests to evaluate the efficiency of different model configurations, including forward/backward runtime, GPU memory usage, GFLOPs, and number of parameters.
17
+ All tests are available in the `test/` directory and we provide the test_results.json file with pre-computed results for reference computed on a 1 A100 40GB GPU.
18
+
Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/TRAINING.md ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Reproducibility
2
+
3
+ ⚠️ Before launching commands, please make sure to set the `dataroot` variable in [config/base.yaml](config/base.yaml) to the path where the datasets are stored.
4
+
5
+ General instructions:
6
+ - Change the VFM: add `model.backbone=<backbone_name>` to the command line (e.g. `model.backbone=franca_vitb16`).
7
+ - Change the dataset: add `dataset=<dataset_name>` to the command line (e.g. `dataset=cityscapes`).
8
+
9
+ For debugging, add `HYDRA_FULL_ERROR=1` before the command to get full stack traces.
10
+
11
+ # 🔄 Training
12
+ Training NAF take less than 2 hours consuming less than 8GB of GPU memory on a single NVIDIA A100.
13
+
14
+ To train the VFM-agnostic upsampler using NAF, simply run:
15
+ ``` python
16
+ python train.py hydra.run.dir=output/naf
17
+ ```
18
+
19
+ To train the denoising model using NAF, simply run:
20
+ ``` python
21
+ python denoising.py denoising.noise_type=gaussian
22
+ ```
23
+ You can select salt-and-pepper noise by setting `denoising.noise_type=salt_pepper`.
Pixal3D/torch_hub/hub/valeoai_NAF_main/docs/data.sh ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ---------- COCOStuff ----------
2
+ # Follow
3
+ # https://github.com/nightrome/cocostuff
4
+ # Download everything
5
+ mkdir COCOStuff
6
+ cd COCOStuff
7
+ wget --directory-prefix=downloads http://images.cocodataset.org/zips/train2017.zip
8
+ wget --directory-prefix=downloads http://images.cocodataset.org/zips/val2017.zip
9
+ wget --directory-prefix=downloads http://calvin.inf.ed.ac.uk/wp-content/uploads/data/cocostuffdataset/stuffthingmaps_trainval2017.zip
10
+
11
+ # Unpack everything
12
+ mkdir -p dataset/images
13
+ mkdir -p dataset/annotations
14
+ unzip downloads/train2017.zip -d dataset/images/
15
+ unzip downloads/val2017.zip -d dataset/images/
16
+ unzip downloads/stuffthingmaps_trainval2017.zip -d dataset/annotations/
17
+
18
+ # https://github.com/xu-ji/IIC/blob/master/datasets/README.txt
19
+ wget https://www.robots.ox.ac.uk/~xuji/datasets/COCOStuff164kCurated.tar.gz
20
+ tar -xzf COCOStuff164kCurated.tar.gz
21
+ mv COCO/COCOStuff164k ./currated
22
+ rmdir COCO
23
+ rm COCOStuff164kCurated.tar.gz
24
+
25
+ # ---------- Cityscapes ----------
26
+ # https://github.com/cemsaz/city-scapes-script
27
+ wget --keep-session-cookies --save-cookies=cookies.txt --post-data 'username=XXXX&password=XXX&submit=Login' https://www.cityscapes-dataset.com/login/
28
+ wget --load-cookies cookies.txt --content-disposition https://www.cityscapes-dataset.com/file-handling/?packageID=1
29
+ wget --load-cookies cookies.txt --content-disposition https://www.cityscapes-dataset.com/file-handling/?packageID=3
30
+ unzip gtFine_trainvaltest.zip
31
+ unzip leftImg8bit_trainvaltest.zip
32
+
33
+
34
+ # ---------- VOC ----------
35
+ wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar
36
+ # generate folder VOCdevkit/VOC2012
37
+ tar -xvf VOCtrainval_11-May-2012.tar
38
+
39
+ # ---------- ADE20K ----------
40
+ wget http://data.csail.mit.edu/places/ADEchallenge/ADEChallengeData2016.zip
41
+ unzip ADEChallengeData2016.zip
42
+
43
+
44
+ # ---------- DAVIS ----------
45
+ mkdir DAVIS
46
+ cd DAVIS
47
+ wget https://data.vision.ee.ethz.ch/csergi/share/davis/DAVIS-2017-trainval-480p.zip
48
+ unzip DAVIS-2017-trainval-480p.zip
49
+ rm DAVIS-2017-trainval-480p.zip
50
+ mv DAVIS/* ./
51
+ rmdir DAVIS
Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/ade20k.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import Optional
3
+
4
+ import torch
5
+ from PIL import Image
6
+ from torch.utils.data import Dataset
7
+
8
+
9
+ class ADE20KDataset(Dataset):
10
+ split_to_dir = {"train": "training", "val": "validation"}
11
+
12
+ def __init__(
13
+ self,
14
+ root,
15
+ transform,
16
+ target_transform,
17
+ split="train",
18
+ skip_other_class=False,
19
+ file_set=None,
20
+ num_classes=None,
21
+ tag=None,
22
+ ):
23
+ super().__init__()
24
+ self.transform = transform
25
+ self.target_transform = target_transform
26
+ self.split = split
27
+ self.root = root
28
+ self.skip_other_class = skip_other_class
29
+ self.file_set = file_set
30
+
31
+ # Collect the data
32
+ self.data = self.collect_data()
33
+
34
+ if split == "train":
35
+ assert self.__len__() == 20210
36
+ elif split == "val":
37
+ assert self.__len__() == 2000
38
+
39
+ def get_class_names(self):
40
+ return [
41
+ "background",
42
+ "wall",
43
+ "building",
44
+ "sky",
45
+ "floor",
46
+ "tree",
47
+ "ceiling",
48
+ "road",
49
+ "bed ",
50
+ "windowpane",
51
+ "grass",
52
+ "cabinet",
53
+ "sidewalk",
54
+ "person",
55
+ "earth",
56
+ "door",
57
+ "table",
58
+ "mountain",
59
+ "plant",
60
+ "curtain",
61
+ "chair",
62
+ "car",
63
+ "water",
64
+ "painting",
65
+ "sofa",
66
+ "shelf",
67
+ "house",
68
+ "sea",
69
+ "mirror",
70
+ "rug",
71
+ "field",
72
+ "armchair",
73
+ "seat",
74
+ "fence",
75
+ "desk",
76
+ "rock",
77
+ "wardrobe",
78
+ "lamp",
79
+ "bathtub",
80
+ "railing",
81
+ "cushion",
82
+ "base",
83
+ "box",
84
+ "column",
85
+ "signboard",
86
+ "chest of drawers",
87
+ "counter",
88
+ "sand",
89
+ "sink",
90
+ "skyscraper",
91
+ "fireplace",
92
+ "refrigerator",
93
+ "grandstand",
94
+ "path",
95
+ "stairs",
96
+ "runway",
97
+ "case",
98
+ "pool table",
99
+ "pillow",
100
+ "screen door",
101
+ "stairway",
102
+ "river",
103
+ "bridge",
104
+ "bookcase",
105
+ "blind",
106
+ "coffee table",
107
+ "toilet",
108
+ "flower",
109
+ "book",
110
+ "hill",
111
+ "bench",
112
+ "countertop",
113
+ "stove",
114
+ "palm",
115
+ "kitchen island",
116
+ "computer",
117
+ "swivel chair",
118
+ "boat",
119
+ "bar",
120
+ "arcade machine",
121
+ "hovel",
122
+ "bus",
123
+ "towel",
124
+ "light",
125
+ "truck",
126
+ "tower",
127
+ "chandelier",
128
+ "awning",
129
+ "streetlight",
130
+ "booth",
131
+ "television receiver",
132
+ "airplane",
133
+ "dirt track",
134
+ "apparel",
135
+ "pole",
136
+ "land",
137
+ "bannister",
138
+ "escalator",
139
+ "ottoman",
140
+ "bottle",
141
+ "buffet",
142
+ "poster",
143
+ "stage",
144
+ "van",
145
+ "ship",
146
+ "fountain",
147
+ "conveyer belt",
148
+ "canopy",
149
+ "washer",
150
+ "plaything",
151
+ "swimming pool",
152
+ "stool",
153
+ "barrel",
154
+ "basket",
155
+ "waterfall",
156
+ "tent",
157
+ "bag",
158
+ "minibike",
159
+ "cradle",
160
+ "oven",
161
+ "ball",
162
+ "food",
163
+ "step",
164
+ "tank",
165
+ "trade name",
166
+ "microwave",
167
+ "pot",
168
+ "animal",
169
+ "bicycle",
170
+ "lake",
171
+ "dishwasher",
172
+ "screen",
173
+ "blanket",
174
+ "sculpture",
175
+ "hood",
176
+ "sconce",
177
+ "vase",
178
+ "traffic light",
179
+ "tray",
180
+ "ashcan",
181
+ "fan",
182
+ "pier",
183
+ "crt screen",
184
+ "plate",
185
+ "monitor",
186
+ "bulletin board",
187
+ "shower",
188
+ "radiator",
189
+ "glass",
190
+ "clock",
191
+ "flag",
192
+ ]
193
+
194
+ def collect_data(self):
195
+ # Get the image and annotation dirs
196
+ image_dir = os.path.join(self.root, f"images/{self.split_to_dir[self.split]}")
197
+ annotation_dir = os.path.join(self.root, f"annotations/{self.split_to_dir[self.split]}")
198
+
199
+ # Collect the filepaths
200
+ if self.file_set is None:
201
+ image_paths = [os.path.join(image_dir, f) for f in sorted(os.listdir(image_dir))]
202
+ annotation_paths = [os.path.join(annotation_dir, f) for f in sorted(os.listdir(annotation_dir))]
203
+ else:
204
+ image_paths = [os.path.join(image_dir, f"{f}.jpg") for f in sorted(self.file_set)]
205
+ annotation_paths = [os.path.join(annotation_dir, f"{f}.png") for f in sorted(self.file_set)]
206
+
207
+ data = list(zip(image_paths, annotation_paths))
208
+ return data
209
+
210
+ def __len__(self):
211
+ return len(self.data)
212
+
213
+ def __getitem__(self, index):
214
+ # Get the paths
215
+ image_path, annotation_path = self.data[index]
216
+
217
+ # Load
218
+ image = Image.open(image_path).convert("RGB")
219
+ target = Image.open(annotation_path)
220
+
221
+ # Augment
222
+ image = self.transform(image).squeeze(0)
223
+ target = self.target_transform(target)
224
+ if self.skip_other_class == True:
225
+ target = target * 255.0
226
+ target[target.type(torch.int64) == 0] = 255.0
227
+ target /= 255.0
228
+ target = target.squeeze(0)
229
+
230
+ batch = {"image": image, "label": target}
231
+ return batch
Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/cityscapes.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import random
3
+
4
+ import numpy as np
5
+ import torch
6
+ from PIL import Image
7
+ from torch.utils.data import Dataset
8
+ from torchvision.datasets import Cityscapes
9
+
10
+
11
+ class CityscapesDataset(Dataset):
12
+ BASE_DIR = "cityscapes"
13
+ NUM_CLASS = 19
14
+
15
+ def __init__(
16
+ self,
17
+ root,
18
+ split="train",
19
+ transform=None,
20
+ target_transform=None,
21
+ include_labels=True,
22
+ num_classes=19,
23
+ tag=None,
24
+ **kwargs
25
+ ):
26
+ super().__init__()
27
+ self.root = root
28
+ self.split = split
29
+ self.transform = transform
30
+ self.target_transform = target_transform
31
+ self.include_labels = include_labels
32
+ self.num_classes = num_classes
33
+ self.tag = tag
34
+
35
+ # fmt: off
36
+ self._key = np.array([-1, -1, -1, -1, -1, -1,
37
+ -1, -1, 0, 1, -1, -1,
38
+ 2, 3, 4, -1, -1, -1,
39
+ 5, -1, 6, 7, 8, 9,
40
+ 10, 11, 12, 13, 14, 15,
41
+ -1, -1, 16, 17, 18])
42
+ self._mapping = np.array(range(-1, len(self._key) - 1)).astype("int32")
43
+ # fmt: on
44
+
45
+ # Get image/mask path pairs
46
+ self.cityscapes_dataset = Cityscapes(
47
+ root=root,
48
+ split=split,
49
+ mode="fine",
50
+ target_type=["instance", "semantic"],
51
+ transform=None,
52
+ target_transform=None,
53
+ )
54
+ # fmt: on
55
+
56
+ if split == "train":
57
+ assert self.__len__() == 2975
58
+ elif split == "val":
59
+ assert self.__len__() == 500
60
+
61
+ def get_class_names(
62
+ self,
63
+ ):
64
+ return [
65
+ "road",
66
+ "sidewalk",
67
+ "building",
68
+ "wall",
69
+ "fence",
70
+ "pole",
71
+ "traffic_light",
72
+ "traffic_sign",
73
+ "vegetation",
74
+ "terrain",
75
+ "sky",
76
+ "person",
77
+ "rider",
78
+ "car",
79
+ "truck",
80
+ "bus",
81
+ "train",
82
+ "motorcycle",
83
+ "bicycle",
84
+ ]
85
+
86
+ def __len__(self):
87
+ return len(self.cityscapes_dataset)
88
+
89
+ def __getitem__(self, index):
90
+ batch = {}
91
+
92
+ # Load image
93
+ data = self.cityscapes_dataset[index]
94
+ img = data[0]
95
+ mask = data[1][1]
96
+
97
+ if self.transform:
98
+ img = self.transform(img)
99
+ batch["image"] = img
100
+
101
+ if self.include_labels:
102
+ if self.target_transform:
103
+ mask = self.target_transform(mask)
104
+ # Convert to tensor and match COCO/ADE20K format
105
+ mask = torch.from_numpy(self._class_to_index(np.array(mask))).to(torch.uint8)
106
+ batch["label"] = mask.squeeze()
107
+
108
+ return batch
109
+
110
+ def _class_to_index(self, mask):
111
+ values = np.unique(mask)
112
+ for value in values:
113
+ assert value in self._mapping
114
+ index = np.digitize(mask.ravel(), self._mapping, right=True)
115
+ return self._key[index].reshape(mask.shape).astype(np.int64)
Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/coco.py ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ from os.path import join
3
+
4
+ import numpy as np
5
+ import torch
6
+ import torch.multiprocessing
7
+ from PIL import Image
8
+ from torch.utils.data import Dataset
9
+
10
+
11
+ def bit_get(val, idx):
12
+ """Gets the bit value.
13
+ Args:
14
+ val: Input value, int or numpy int array.
15
+ idx: Which bit of the input val.
16
+ Returns:
17
+ The "idx"-th bit of input val.
18
+ """
19
+ return (val >> idx) & 1
20
+
21
+
22
+ def create_pascal_label_colormap():
23
+ """Creates a label colormap used in PASCAL VOC segmentation benchmark.
24
+ Returns:
25
+ A colormap for visualizing segmentation results.
26
+ """
27
+ colormap = np.zeros((512, 3), dtype=int)
28
+ ind = np.arange(512, dtype=int)
29
+
30
+ for shift in reversed(list(range(8))):
31
+ for channel in range(3):
32
+ colormap[:, channel] |= bit_get(ind, channel) << shift
33
+ ind >>= 3
34
+
35
+ return colormap
36
+
37
+
38
+ class COCODataset(Dataset):
39
+
40
+ def __init__(
41
+ self,
42
+ root,
43
+ split,
44
+ transform,
45
+ target_transform,
46
+ include_labels=True,
47
+ coarse_labels=False,
48
+ exclude_things=False,
49
+ subset=None,
50
+ num_classes=None,
51
+ tag=None,
52
+ ):
53
+ super().__init__()
54
+ self.split = split
55
+ self.root = root
56
+ self.coarse_labels = coarse_labels
57
+ self.transform = transform
58
+ self.label_transform = target_transform
59
+ self.subset = subset
60
+ self.exclude_things = exclude_things
61
+ self.include_labels = include_labels
62
+
63
+ if self.subset is None:
64
+ self.image_list = "Coco164kFull_Stuff_Coarse.txt"
65
+ elif self.subset == 6: # IIC Coarse
66
+ self.image_list = "Coco164kFew_Stuff_6.txt"
67
+ elif self.subset == 7: # IIC Fine
68
+ self.image_list = "Coco164kFull_Stuff_Coarse_7.txt"
69
+
70
+ assert self.split in ["train", "val", "train+val"]
71
+ split_dirs = {
72
+ "train": ["train2017"],
73
+ "val": ["val2017"],
74
+ "train+val": ["train2017", "val2017"],
75
+ }
76
+
77
+ self.image_files = []
78
+ self.label_files = []
79
+ for split_dir in split_dirs[self.split]:
80
+ with open(join(self.root, "curated", split_dir, self.image_list), "r") as f:
81
+ img_ids = [fn.rstrip() for fn in f.readlines()]
82
+ for img_id in img_ids:
83
+ self.image_files.append(join(self.root, "images", split_dir, img_id + ".jpg"))
84
+ self.label_files.append(join(self.root, "annotations", split_dir, img_id + ".png"))
85
+
86
+ self.fine_to_coarse = {
87
+ 0: 9,
88
+ 1: 11,
89
+ 2: 11,
90
+ 3: 11,
91
+ 4: 11,
92
+ 5: 11,
93
+ 6: 11,
94
+ 7: 11,
95
+ 8: 11,
96
+ 9: 8,
97
+ 10: 8,
98
+ 11: 8,
99
+ 12: 8,
100
+ 13: 8,
101
+ 14: 8,
102
+ 15: 7,
103
+ 16: 7,
104
+ 17: 7,
105
+ 18: 7,
106
+ 19: 7,
107
+ 20: 7,
108
+ 21: 7,
109
+ 22: 7,
110
+ 23: 7,
111
+ 24: 7,
112
+ 25: 6,
113
+ 26: 6,
114
+ 27: 6,
115
+ 28: 6,
116
+ 29: 6,
117
+ 30: 6,
118
+ 31: 6,
119
+ 32: 6,
120
+ 33: 10,
121
+ 34: 10,
122
+ 35: 10,
123
+ 36: 10,
124
+ 37: 10,
125
+ 38: 10,
126
+ 39: 10,
127
+ 40: 10,
128
+ 41: 10,
129
+ 42: 10,
130
+ 43: 5,
131
+ 44: 5,
132
+ 45: 5,
133
+ 46: 5,
134
+ 47: 5,
135
+ 48: 5,
136
+ 49: 5,
137
+ 50: 5,
138
+ 51: 2,
139
+ 52: 2,
140
+ 53: 2,
141
+ 54: 2,
142
+ 55: 2,
143
+ 56: 2,
144
+ 57: 2,
145
+ 58: 2,
146
+ 59: 2,
147
+ 60: 2,
148
+ 61: 3,
149
+ 62: 3,
150
+ 63: 3,
151
+ 64: 3,
152
+ 65: 3,
153
+ 66: 3,
154
+ 67: 3,
155
+ 68: 3,
156
+ 69: 3,
157
+ 70: 3,
158
+ 71: 0,
159
+ 72: 0,
160
+ 73: 0,
161
+ 74: 0,
162
+ 75: 0,
163
+ 76: 0,
164
+ 77: 1,
165
+ 78: 1,
166
+ 79: 1,
167
+ 80: 1,
168
+ 81: 1,
169
+ 82: 1,
170
+ 83: 4,
171
+ 84: 4,
172
+ 85: 4,
173
+ 86: 4,
174
+ 87: 4,
175
+ 88: 4,
176
+ 89: 4,
177
+ 90: 4,
178
+ 91: 17,
179
+ 92: 17,
180
+ 93: 22,
181
+ 94: 20,
182
+ 95: 20,
183
+ 96: 22,
184
+ 97: 15,
185
+ 98: 25,
186
+ 99: 16,
187
+ 100: 13,
188
+ 101: 12,
189
+ 102: 12,
190
+ 103: 17,
191
+ 104: 17,
192
+ 105: 23,
193
+ 106: 15,
194
+ 107: 15,
195
+ 108: 17,
196
+ 109: 15,
197
+ 110: 21,
198
+ 111: 15,
199
+ 112: 25,
200
+ 113: 13,
201
+ 114: 13,
202
+ 115: 13,
203
+ 116: 13,
204
+ 117: 13,
205
+ 118: 22,
206
+ 119: 26,
207
+ 120: 14,
208
+ 121: 14,
209
+ 122: 15,
210
+ 123: 22,
211
+ 124: 21,
212
+ 125: 21,
213
+ 126: 24,
214
+ 127: 20,
215
+ 128: 22,
216
+ 129: 15,
217
+ 130: 17,
218
+ 131: 16,
219
+ 132: 15,
220
+ 133: 22,
221
+ 134: 24,
222
+ 135: 21,
223
+ 136: 17,
224
+ 137: 25,
225
+ 138: 16,
226
+ 139: 21,
227
+ 140: 17,
228
+ 141: 22,
229
+ 142: 16,
230
+ 143: 21,
231
+ 144: 21,
232
+ 145: 25,
233
+ 146: 21,
234
+ 147: 26,
235
+ 148: 21,
236
+ 149: 24,
237
+ 150: 20,
238
+ 151: 17,
239
+ 152: 14,
240
+ 153: 21,
241
+ 154: 26,
242
+ 155: 15,
243
+ 156: 23,
244
+ 157: 20,
245
+ 158: 21,
246
+ 159: 24,
247
+ 160: 15,
248
+ 161: 24,
249
+ 162: 22,
250
+ 163: 25,
251
+ 164: 15,
252
+ 165: 20,
253
+ 166: 17,
254
+ 167: 17,
255
+ 168: 22,
256
+ 169: 14,
257
+ 170: 18,
258
+ 171: 18,
259
+ 172: 18,
260
+ 173: 18,
261
+ 174: 18,
262
+ 175: 18,
263
+ 176: 18,
264
+ 177: 26,
265
+ 178: 26,
266
+ 179: 19,
267
+ 180: 19,
268
+ 181: 24,
269
+ }
270
+
271
+ self._label_names = [
272
+ "ground-stuff",
273
+ "plant-stuff",
274
+ "sky-stuff",
275
+ ]
276
+ self.cocostuff3_coarse_classes = [23, 22, 21]
277
+ self.first_stuff_index = 12
278
+ if split == "train":
279
+ assert self.__len__() == 97702
280
+ elif split == "val":
281
+ assert self.__len__() == 4172
282
+
283
+ def __len__(self):
284
+ return len(self.image_files)
285
+
286
+ def __getitem__(self, index):
287
+ image_path = self.image_files[index]
288
+ label_path = self.label_files[index]
289
+ batch = {}
290
+
291
+ img = self.transform(Image.open(image_path).convert("RGB"))
292
+ batch["image"] = img
293
+ batch["img_path"] = image_path
294
+
295
+ if self.include_labels:
296
+ label = self.label_transform(Image.open(label_path)).squeeze(0)
297
+ label[label == 255] = -1 # to be consistent with 10k
298
+ batch["label"] = label
299
+
300
+ coarse_label = torch.zeros_like(label)
301
+ for fine, coarse in self.fine_to_coarse.items():
302
+ coarse_label[label == fine] = coarse
303
+ coarse_label[label == -1] = -1
304
+
305
+ if self.coarse_labels:
306
+ coarser_labels = -torch.ones_like(label)
307
+ for i, c in enumerate(self.cocostuff3_coarse_classes):
308
+ coarser_labels[coarse_label == c] = i
309
+ batch["label"] = coarser_labels
310
+ else:
311
+ if self.exclude_things:
312
+ batch["label"] = coarse_label - self.first_stuff_index
313
+ else:
314
+ batch["label"] = coarse_label
315
+
316
+ batch["label"] = batch["label"].to(torch.uint8)
317
+ return batch
Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/davis.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glob
2
+ import os
3
+
4
+ import torch
5
+ from PIL import Image
6
+ from torch.utils.data import Dataset
7
+ from torchvision import transforms
8
+
9
+
10
+ class DAVIS(Dataset):
11
+ def __init__(self, root, split="train", transform=None, target_transform=None, tag=None):
12
+ """
13
+ Args:
14
+ root (string): Directory with all the videos.
15
+ transform (callable, optional): Optional transform to be applied on a sample.
16
+ """
17
+ videos = self.load_split(root, split)
18
+
19
+ frames = []
20
+ for video in videos:
21
+ video_path = os.path.join(root, f"JPEGImages/480p/{video}/*/*.jpg")
22
+ video_frames = sorted(glob.glob(video_path))
23
+ frames += video_frames
24
+ self.frames = frames
25
+ self.transform = transform
26
+ self.target_transform = target_transform
27
+ self.tag = tag
28
+
29
+ def load_split(self, root, split):
30
+ """
31
+ Load the split files for DAVIS dataset.
32
+ Args:
33
+ root (string): Directory with all the videos.
34
+ split (string): Split name (train, val, test).
35
+ Returns:
36
+ list: List of video paths.
37
+ """
38
+ split_path = os.path.join(root, "ImageSets/2017/", f"{split}.txt")
39
+ with open(split_path, "r") as f:
40
+ videos = [line.strip() for line in f.readlines()]
41
+ return videos
42
+
43
+ def __len__(self):
44
+ return len(self.frames)
45
+
46
+ def __getitem__(self, index):
47
+ image_path = self.frames[index]
48
+ annotation_path = image_path.replace("JPEGImages", "Annotations")
49
+ batch = {}
50
+
51
+ # Load
52
+ image = Image.open(image_path).convert("RGB")
53
+ target = Image.open(annotation_path)
54
+
55
+ # Augment
56
+ image = self.transform(image).squeeze(0)
57
+ target = self.target_transform(target)
58
+ target = target.squeeze(0)
59
+
60
+ batch = {"image": image, "label": target}
61
+ return batch
Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/image_dataset.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ import torch
5
+ import torchvision.transforms as T
6
+ from torchvision.datasets import folder
7
+ from torchvision.datasets.vision import VisionDataset
8
+
9
+
10
+ def remove_prefix(s, prefix):
11
+ if s.startswith(prefix):
12
+ s = s[len(prefix) :]
13
+ return s
14
+
15
+
16
+ class ImageDataset(VisionDataset):
17
+ """
18
+ modified from: https://pytorch.org/docs/stable/_modules/torchvision/datasets/folder.html#ImageFolder
19
+ uses cached directory listing if available rather than walking directory
20
+ Attributes:
21
+ classes (list): List of the class names.
22
+ class_to_idx (dict): Dict with items (class_name, class_index).
23
+ samples (list): List of (sample path, class_index) tuples
24
+ targets (list): The class_index value for each image in the dataset
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ root,
30
+ root_cache,
31
+ loader=folder.default_loader,
32
+ extensions=folder.IMG_EXTENSIONS,
33
+ transform=None,
34
+ is_valid_file=None,
35
+ include_labels=False,
36
+ **kwargs,
37
+ ):
38
+ super(ImageDataset, self).__init__(
39
+ root,
40
+ transform=transform,
41
+ )
42
+ self.root = root
43
+ classes, class_to_idx = self._find_classes(self.root)
44
+ if root_cache is None:
45
+ root_cache = root
46
+ cache = root_cache.rstrip("/") + ".txt"
47
+ print(cache)
48
+ # cache = './val_paths.txt'
49
+ if os.path.isfile(cache):
50
+ print("Using directory list at: %s" % cache)
51
+ with open(cache) as f:
52
+ samples = []
53
+ for line in f:
54
+ (path, idx) = line.strip().split(";")
55
+ samples.append((os.path.join(path), int(idx)))
56
+ else:
57
+ print("Walking directory: %s" % self.root)
58
+ samples = folder.make_dataset(self.root, class_to_idx, extensions, is_valid_file)
59
+ with open(cache, "w") as f:
60
+ for line in samples:
61
+ path, label = line
62
+ f.write("%s;%d\n" % (remove_prefix(path, self.root).lstrip("/"), label))
63
+
64
+ if len(samples) == 0:
65
+ raise (
66
+ RuntimeError(
67
+ "Found 0 files in subfolders of: " + self.root + "\n" "Supported extensions are: " + ",".join(extensions)
68
+ )
69
+ )
70
+
71
+ self.loader = loader
72
+ self.extensions = extensions
73
+ self.classes = classes
74
+ self.class_to_idx = class_to_idx
75
+ self.samples = samples
76
+ self.targets = [s[1] for s in samples]
77
+ self.transform = transform
78
+ self.include_labels = include_labels
79
+
80
+ def _find_classes(self, dir):
81
+ """
82
+ Finds the class folders in a dataset.
83
+ Ensures:
84
+ No class is a subdirectory of another.
85
+ """
86
+ if sys.version_info >= (3, 5):
87
+ # Faster and available in Python 3.5 and above
88
+ classes = [d.name for d in os.scandir(dir) if d.is_dir()]
89
+ else:
90
+ classes = [d for d in os.listdir(dir) if os.path.isdir(os.path.join(dir, d))]
91
+ classes.sort()
92
+ class_to_idx = {classes[i]: i for i in range(len(classes))}
93
+ return classes, class_to_idx
94
+
95
+ def __getitem__(self, index):
96
+ path, target = self.samples[index]
97
+ path = os.path.join(self.root, path)
98
+ sample = self.loader(path)
99
+ if self.transform is not None:
100
+ sample = self.transform(sample)
101
+
102
+ batch = {
103
+ "index": index,
104
+ "image": sample,
105
+ "target": target,
106
+ "path": path,
107
+ }
108
+
109
+ if self.include_labels:
110
+ target = self.targets[index]
111
+ if self.target_transform is not None:
112
+ target = self.target_transform(target)
113
+ batch["label"] = target
114
+
115
+ return batch
116
+
117
+ def __len__(self):
118
+ return len(self.samples)
Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/kitti360.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import random
4
+ from collections import namedtuple
5
+ from typing import Optional
6
+
7
+ import numpy as np
8
+ import torch
9
+ from PIL import Image
10
+ from torch.utils.data import Dataset
11
+
12
+ # https://github.com/autonomousvision/kitti360Scripts/blob/32f6d64eef27c32b52c542b4e95be8af9c9c6444/kitti360scripts/helpers/labels.py
13
+ # a label and all meta information
14
+ Label = namedtuple(
15
+ "Label",
16
+ ["name", "id", "kittiId", "trainId", "category", "categoryId", "hasInstances", "ignoreInEval", "ignoreInInst", "color"],
17
+ )
18
+
19
+
20
+ labels = [
21
+ # name id kittiId, trainId category catId hasInstances ignoreInEval ignoreInInst color
22
+ Label("unlabeled", 0, -1, 255, "void", 0, False, True, True, (0, 0, 0)),
23
+ Label("ego vehicle", 1, -1, 255, "void", 0, False, True, True, (0, 0, 0)),
24
+ Label("rectification border", 2, -1, 255, "void", 0, False, True, True, (0, 0, 0)),
25
+ Label("out of roi", 3, -1, 255, "void", 0, False, True, True, (0, 0, 0)),
26
+ Label("static", 4, -1, 255, "void", 0, False, True, True, (0, 0, 0)),
27
+ Label("dynamic", 5, -1, 255, "void", 0, False, True, True, (111, 74, 0)),
28
+ Label("ground", 6, -1, 255, "void", 0, False, True, True, (81, 0, 81)),
29
+ Label("road", 7, 1, 0, "flat", 1, False, False, False, (128, 64, 128)),
30
+ Label("sidewalk", 8, 3, 1, "flat", 1, False, False, False, (244, 35, 232)),
31
+ Label("parking", 9, 2, 255, "flat", 1, False, True, True, (250, 170, 160)),
32
+ Label("rail track", 10, 10, 255, "flat", 1, False, True, True, (230, 150, 140)),
33
+ Label("building", 11, 11, 2, "construction", 2, True, False, False, (70, 70, 70)),
34
+ Label("wall", 12, 7, 3, "construction", 2, False, False, False, (102, 102, 156)),
35
+ Label("fence", 13, 8, 4, "construction", 2, False, False, False, (190, 153, 153)),
36
+ Label("guard rail", 14, 30, 255, "construction", 2, False, True, True, (180, 165, 180)),
37
+ Label("bridge", 15, 31, 255, "construction", 2, False, True, True, (150, 100, 100)),
38
+ Label("tunnel", 16, 32, 255, "construction", 2, False, True, True, (150, 120, 90)),
39
+ Label("pole", 17, 21, 5, "object", 3, True, False, True, (153, 153, 153)),
40
+ Label("polegroup", 18, -1, 255, "object", 3, False, True, True, (153, 153, 153)),
41
+ Label("traffic light", 19, 23, 6, "object", 3, True, False, True, (250, 170, 30)),
42
+ Label("traffic sign", 20, 24, 7, "object", 3, True, False, True, (220, 220, 0)),
43
+ Label("vegetation", 21, 5, 8, "nature", 4, False, False, False, (107, 142, 35)),
44
+ Label("terrain", 22, 4, 9, "nature", 4, False, False, False, (152, 251, 152)),
45
+ Label("sky", 23, 9, 10, "sky", 5, False, False, False, (70, 130, 180)),
46
+ Label("person", 24, 19, 11, "human", 6, True, False, False, (220, 20, 60)),
47
+ Label("rider", 25, 20, 12, "human", 6, True, False, False, (255, 0, 0)),
48
+ Label("car", 26, 13, 13, "vehicle", 7, True, False, False, (0, 0, 142)),
49
+ Label("truck", 27, 14, 14, "vehicle", 7, True, False, False, (0, 0, 70)),
50
+ Label("bus", 28, 34, 15, "vehicle", 7, True, False, False, (0, 60, 100)),
51
+ Label("caravan", 29, 16, 255, "vehicle", 7, True, True, True, (0, 0, 90)),
52
+ Label("trailer", 30, 15, 255, "vehicle", 7, True, True, True, (0, 0, 110)),
53
+ Label("train", 31, 33, 16, "vehicle", 7, True, False, False, (0, 80, 100)),
54
+ Label("motorcycle", 32, 17, 17, "vehicle", 7, True, False, False, (0, 0, 230)),
55
+ Label("bicycle", 33, 18, 18, "vehicle", 7, True, False, False, (119, 11, 32)),
56
+ Label("garage", 34, 12, 2, "construction", 2, True, True, True, (64, 128, 128)),
57
+ Label("gate", 35, 6, 4, "construction", 2, False, True, True, (190, 153, 153)),
58
+ Label("stop", 36, 29, 255, "construction", 2, True, True, True, (150, 120, 90)),
59
+ Label("smallpole", 37, 22, 5, "object", 3, True, True, True, (153, 153, 153)),
60
+ Label("lamp", 38, 25, 255, "object", 3, True, True, True, (0, 64, 64)),
61
+ Label("trash bin", 39, 26, 255, "object", 3, True, True, True, (0, 128, 192)),
62
+ Label("vending machine", 40, 27, 255, "object", 3, True, True, True, (128, 64, 0)),
63
+ Label("box", 41, 28, 255, "object", 3, True, True, True, (64, 64, 128)),
64
+ Label("unknown construction", 42, 35, 255, "void", 0, False, True, True, (102, 0, 0)),
65
+ Label("unknown vehicle", 43, 36, 255, "void", 0, False, True, True, (51, 0, 51)),
66
+ Label("unknown object", 44, 37, 255, "void", 0, False, True, True, (32, 32, 32)),
67
+ Label("license plate", -1, -1, -1, "vehicle", 7, False, True, True, (0, 0, 142)),
68
+ ]
69
+
70
+
71
+ class KITTIDataset(Dataset):
72
+ def __init__(
73
+ self,
74
+ root: str,
75
+ split: str = "train",
76
+ transform: Optional[callable] = None,
77
+ target_transform: Optional[callable] = None,
78
+ include_labels: bool = True,
79
+ num_classes: int = 19,
80
+ tag: Optional[str] = None,
81
+ **kwargs,
82
+ ):
83
+ super().__init__()
84
+ self.root = root
85
+ self.split = split
86
+ self.transform = transform
87
+ self.target_transform = target_transform
88
+ self.include_labels = include_labels
89
+ self.num_classes = num_classes
90
+ self.tag = tag
91
+
92
+ # Create mapping from id to trainId
93
+ self.id_to_trainid = {label.id: label.trainId for label in labels}
94
+
95
+ # Initialize paths
96
+ self.image_paths = []
97
+ self.label_paths = []
98
+
99
+ # Set up split management
100
+ self.split_file = os.path.join(os.path.dirname(__file__), "kitti360", f"{split}_split.json")
101
+
102
+ # Create split if needed
103
+ if not os.path.exists(self.split_file):
104
+ self._create_split()
105
+
106
+ # Load paths from split file
107
+ self._load_split()
108
+
109
+ def _create_split(self):
110
+ """Create and save train/val split if it doesn't exist"""
111
+ # Collect all valid paths first
112
+ all_image_paths = []
113
+ all_label_paths = []
114
+
115
+ raw_base = os.path.join(self.root, "data_2d_raw")
116
+ sem_base = os.path.join(self.root, "data_2d_semantics", "train")
117
+
118
+ for drive in os.listdir(raw_base):
119
+ drive_path = os.path.join(raw_base, drive)
120
+
121
+ for camera in ["image_00"]:
122
+ img_dir = os.path.join(drive_path, camera, "data_rect")
123
+ lbl_dir = os.path.join(sem_base, drive, camera, "semantic")
124
+
125
+ if os.path.exists(img_dir) and os.path.exists(lbl_dir):
126
+ for fname in os.listdir(img_dir):
127
+ if fname.endswith(".png"):
128
+ base_name = os.path.splitext(fname)[0]
129
+ img_path = os.path.join(img_dir, fname)
130
+ lbl_path = os.path.join(lbl_dir, f"{base_name}.png")
131
+
132
+ if os.path.exists(lbl_path):
133
+ all_image_paths.append(img_path)
134
+ all_label_paths.append(lbl_path)
135
+
136
+ # Create 80-20 split
137
+ random.seed(42) # For reproducibility
138
+ combined = list(zip(all_image_paths, all_label_paths))
139
+ random.shuffle(combined)
140
+ split_idx = int(0.8 * len(combined))
141
+
142
+ train_split = combined[:split_idx]
143
+ val_split = combined[split_idx:]
144
+
145
+ # Create directory if needed
146
+ split_dir = os.path.dirname(self.split_file)
147
+ os.makedirs(split_dir, exist_ok=True)
148
+
149
+ # Save splits
150
+ with open(os.path.join(split_dir, "train_split.json"), "w") as f:
151
+ json.dump(train_split, f)
152
+
153
+ with open(os.path.join(split_dir, "val_split.json"), "w") as f:
154
+ json.dump(val_split, f)
155
+
156
+ def _load_split(self):
157
+ """Load paths from existing split file"""
158
+ with open(self.split_file, "r") as f:
159
+ split_data = json.load(f)
160
+
161
+ self.image_paths, self.label_paths = zip(*split_data)
162
+
163
+ def __len__(self):
164
+ return len(self.image_paths)
165
+
166
+ def __getitem__(self, index):
167
+ batch = {}
168
+ seed = random.randint(0, 2**32 - 1)
169
+
170
+ # Load image
171
+ random.seed(seed)
172
+ torch.manual_seed(seed)
173
+ img = Image.open(self.image_paths[index]).convert("RGB")
174
+ if self.transform:
175
+ img = self.transform(img)
176
+ batch["image"] = img
177
+ batch["img_path"] = self.image_paths[index]
178
+
179
+ if self.include_labels:
180
+ # Load and process label
181
+ random.seed(seed)
182
+ torch.manual_seed(seed)
183
+ label = Image.open(self.label_paths[index])
184
+
185
+ if self.target_transform:
186
+ label = self.target_transform(label)
187
+
188
+ # Convert from id to trainId
189
+ label_array = np.array(label)
190
+ for id_val, trainid_val in self.id_to_trainid.items():
191
+ label_array[label_array == id_val] = trainid_val
192
+
193
+ label = torch.from_numpy(label_array).to(torch.uint8)
194
+ batch["label"] = label.squeeze(0)
195
+
196
+ return batch
Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/kitti360/val_split.json ADDED
The diff for this file is too large to render. See raw diff
 
Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/dataset/voc.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ from typing import Optional
3
+
4
+ import numpy as np
5
+ import torch
6
+ from PIL import Image
7
+ from torch.utils.data import Dataset
8
+ from torchvision.datasets import VOCSegmentation
9
+
10
+
11
+ class VOCDataset(Dataset):
12
+ def __init__(
13
+ self,
14
+ root: str,
15
+ split: str = "train",
16
+ transform: Optional[callable] = None,
17
+ target_transform: Optional[callable] = None,
18
+ include_labels: bool = True,
19
+ num_classes: int = 21,
20
+ tag: Optional[str] = None,
21
+ year: str = "2012",
22
+ download: bool = False,
23
+ **kwargs
24
+ ):
25
+ super().__init__()
26
+ self.root = root
27
+ self.split = split
28
+ self.transform = transform
29
+ self.target_transform = target_transform
30
+ self.include_labels = include_labels
31
+ self.num_classes = num_classes
32
+ self.tag = tag
33
+
34
+ # Initialize torchvision's VOC dataset
35
+ self.voc_dataset = VOCSegmentation(
36
+ root=root,
37
+ year=year,
38
+ image_set="train" if split == "train" else "val",
39
+ download=download,
40
+ transform=None,
41
+ target_transform=None,
42
+ )
43
+
44
+ if split == "train":
45
+ assert self.__len__() == 1464
46
+ elif split == "val":
47
+ assert self.__len__() == 1449
48
+
49
+ def __len__(self):
50
+ return len(self.voc_dataset)
51
+
52
+ def __getitem__(self, index):
53
+ batch = {}
54
+ seed = random.randint(0, 2**32 - 1)
55
+
56
+ # Load image
57
+ img_path, mask_path = (
58
+ self.voc_dataset.images[index],
59
+ self.voc_dataset.masks[index],
60
+ )
61
+
62
+ random.seed(seed)
63
+ torch.manual_seed(seed)
64
+ img = Image.open(img_path).convert("RGB")
65
+ if self.transform:
66
+ img = self.transform(img)
67
+ batch["image"] = img
68
+ batch["img_path"] = img_path
69
+
70
+ if self.include_labels:
71
+ # Load and process mask
72
+ random.seed(seed)
73
+ torch.manual_seed(seed)
74
+ mask = Image.open(mask_path)
75
+
76
+ if self.target_transform:
77
+ mask = self.target_transform(mask)
78
+
79
+ # Convert to tensor and match COCO/ADE20K format
80
+ mask = torch.from_numpy(np.array(mask)).to(torch.uint8)
81
+ batch["label"] = mask.squeeze()
82
+
83
+ return batch
84
+
85
+ @property
86
+ def pred_offset(self):
87
+ return 0
88
+
89
+ def get_class_names(self):
90
+ """Return the class names for the VOC dataset."""
91
+ return [
92
+ "background",
93
+ "aeroplane",
94
+ "bicycle",
95
+ "bird",
96
+ "boat",
97
+ "bottle",
98
+ "bus",
99
+ "car",
100
+ "cat",
101
+ "chair",
102
+ "cow",
103
+ "diningtable",
104
+ "dog",
105
+ "horse",
106
+ "motorbike",
107
+ "person",
108
+ "pottedplant",
109
+ "sheep",
110
+ "sofa",
111
+ "train",
112
+ "tvmonitor",
113
+ ]
Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/eval_depth_probing.py ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import datetime
2
+ import os
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ import hydra
7
+ import numpy as np
8
+ import torch
9
+ import torch.nn as nn
10
+ import torch.nn.functional as F
11
+ import torchvision.transforms as T
12
+ from einops import rearrange
13
+ from hydra.core.global_hydra import GlobalHydra
14
+ from hydra.core.hydra_config import HydraConfig
15
+ from hydra.utils import instantiate
16
+ from omegaconf import OmegaConf
17
+ from rich.console import Console
18
+ from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn
19
+ from torch.utils.tensorboard import SummaryWriter
20
+ from tqdm import tqdm
21
+
22
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
23
+
24
+ import random
25
+
26
+ from src.loss import DepthLoss
27
+ from utils.training import get_batch, get_dataloaders, load_multiple_backbones
28
+
29
+ LOG_INTERVAL = 100
30
+
31
+
32
+ def eval_metrics(gt, pred, min_depth=1e-3, max_depth=10):
33
+ mask_1 = gt > min_depth
34
+ mask_2 = gt < max_depth
35
+ mask = np.logical_and(mask_1, mask_2)
36
+
37
+ gt = gt[mask]
38
+ pred = pred[mask]
39
+
40
+ thresh = np.maximum((gt / pred), (pred / gt))
41
+ d1 = (thresh < 1.25).mean()
42
+ d2 = (thresh < 1.25**2).mean()
43
+ d3 = (thresh < 1.25**3).mean()
44
+
45
+ abs_rel = np.mean(np.abs(gt - pred) / gt)
46
+ sq_rel = np.mean(((gt - pred) ** 2) / gt)
47
+
48
+ rmse = (gt - pred) ** 2
49
+ rmse = np.sqrt(rmse.mean())
50
+
51
+ rmse_log = (np.log(gt) - np.log(pred)) ** 2
52
+ rmse_log = np.sqrt(rmse_log.mean())
53
+
54
+ err = np.log(pred) - np.log(gt)
55
+ silog = np.sqrt(np.mean(err**2) - np.mean(err) ** 2) * 100
56
+
57
+ log_10 = (np.abs(np.log10(gt) - np.log10(pred))).mean()
58
+ return dict(
59
+ d1=d1,
60
+ d2=d2,
61
+ d3=d3,
62
+ abs_rel=abs_rel,
63
+ rmse=rmse,
64
+ log_10=log_10,
65
+ rmse_log=rmse_log,
66
+ silog=silog,
67
+ sq_rel=sq_rel,
68
+ )
69
+
70
+
71
+ class UpsamplerEvaluator(nn.Module):
72
+
73
+ def __init__(self, model, backbone, device, cfg, writer, console):
74
+ super().__init__()
75
+ self.model, self.backbone, self.device, self.cfg, self.writer, self.console = (
76
+ model,
77
+ backbone,
78
+ device,
79
+ cfg,
80
+ writer,
81
+ console,
82
+ )
83
+
84
+ # Use backbone-specific normalization (like train_jafar.py)
85
+ self.mean_bck = backbone.config["mean"]
86
+ self.std_bck = backbone.config["std"]
87
+
88
+ # Upsampler normalization (ImageNet standard)
89
+ self.mean_ups = (0.485, 0.456, 0.406)
90
+ self.std_ups = (0.229, 0.224, 0.225)
91
+
92
+ # Initialize depth estimation components
93
+ self.classifier = nn.Conv2d(backbone.embed_dim, 256, kernel_size=1, padding=0, stride=1).to(device)
94
+ self.depthloss = DepthLoss(max_depth=cfg.metrics.depth.max_depth)
95
+ self.register_buffer("bins", torch.linspace(cfg.metrics.depth.min_depth, cfg.metrics.depth.max_depth, 256))
96
+
97
+ def set_up_classifier(self, checkpoint_path):
98
+ """Load classifier weights from a checkpoint."""
99
+ if Path(checkpoint_path).exists():
100
+ checkpoint = torch.load(checkpoint_path)
101
+ self.classifier.load_state_dict(checkpoint)
102
+ self.console.print(f"Loaded classifier from checkpoint: {checkpoint_path}")
103
+ else:
104
+ raise FileNotFoundError(f"Checkpoint file not found: {checkpoint_path}")
105
+
106
+ def set_optimizer(self, cfg, loader):
107
+ params = []
108
+ params_classifier = self.classifier.parameters()
109
+ params_model = self.model.parameters()
110
+
111
+ params = list(params_classifier)
112
+ if self.cfg.eval.supervise_model == True:
113
+ params += params_model
114
+ optimizer = instantiate(cfg.optimizer, params=params)
115
+ scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=cfg.num_epochs * len(loader))
116
+ self.optimizer = optimizer
117
+ self.scheduler = scheduler
118
+
119
+ num_params = sum(p.numel() for p in params if p.requires_grad)
120
+ self.log_print(f"[bold cyan]Number of optimized parameters: {num_params:,}[/bold cyan]")
121
+
122
+ def log_print(self, *args, **kwargs):
123
+ Console(force_terminal=True).print(*args, **kwargs)
124
+ self.console.print(*args, **kwargs)
125
+ if hasattr(self.console, "file") and self.console.file:
126
+ self.console.file.flush()
127
+
128
+ def log_tensorboard(self, step, loss=None, metrics=None):
129
+ if loss is not None:
130
+ self.writer.add_scalar("Loss/Step", loss, step)
131
+ if metrics is not None:
132
+ for metric_name, metric_value in metrics.items():
133
+ self.writer.add_scalar(f"Metrics/{metric_name}", metric_value, step)
134
+
135
+ def process_batch(self, image_batch, target, is_training=True, batch_idx=0):
136
+ H, W = target.shape[-2:]
137
+
138
+ # Apply separate normalization like in train_jafar.py
139
+ image_batch_bck = T.functional.normalize(image_batch, mean=self.mean_bck, std=self.std_bck)
140
+ image_batch_ups = T.functional.normalize(image_batch, mean=self.mean_ups, std=self.std_ups)
141
+
142
+ with torch.no_grad():
143
+ pred = self.backbone(image_batch_bck)
144
+
145
+ if self.cfg.eval.supervise_model:
146
+ pred = self.model(image_batch_ups, pred, (H, W))
147
+ else:
148
+ with torch.no_grad():
149
+ pred = self.model(image_batch_ups, pred, (H, W)).detach()
150
+
151
+ if batch_idx == 0:
152
+ self.log_print(f"[red]Model output size {pred.shape[-2:]} for expected size {(H, W)}[/red]")
153
+ pred = self.classifier(pred)
154
+
155
+ if pred.shape[-2:] != (H, W):
156
+ pred = F.interpolate(pred, size=(H, W), mode="bilinear")
157
+
158
+ # https://github.com/mbanani/probe3d/blob/c52d00b069d949b2f00c544d4991716df68d5233/evals/models/probes.py#L108
159
+ pred = F.relu(pred)
160
+ eps = 0.1
161
+ pred = pred + eps
162
+ pred = pred / pred.sum(dim=1, keepdim=True)
163
+ pred = torch.einsum("ikmn,k->imn", [pred, self.bins]).unsqueeze(dim=1)
164
+ return pred, target
165
+
166
+ def train(
167
+ self,
168
+ train_dataloader,
169
+ progress,
170
+ epoch,
171
+ start_time,
172
+ ):
173
+ self.log_print(f"[yellow]Training model epoch {epoch+1}...[/yellow]")
174
+ self.backbone.eval()
175
+ if self.cfg.eval.supervise_model:
176
+ self.model.train()
177
+ else:
178
+ self.model.eval()
179
+ self.classifier.train()
180
+
181
+ epoch_task = progress.add_task(
182
+ f"Epoch {epoch+1}/{self.cfg.num_epochs}",
183
+ total=len(train_dataloader),
184
+ loss=0.0,
185
+ step=0,
186
+ )
187
+ total_loss = 0
188
+
189
+ for batch_idx, batch in enumerate(train_dataloader):
190
+ # Process batch using get_batch
191
+ batch = get_batch(batch, self.device)
192
+ image_batch = batch["image"]
193
+ target = batch["depth"].to(self.device)
194
+
195
+ if random.random() < 0.5:
196
+ image_batch = torch.flip(image_batch, dims=[3])
197
+ target = torch.flip(target, dims=[3])
198
+
199
+ self.optimizer.zero_grad()
200
+
201
+ pred, target = self.process_batch(image_batch, target, is_training=True, batch_idx=batch_idx)
202
+
203
+ loss = self.depthloss(pred, target)
204
+
205
+ loss.backward()
206
+ self.optimizer.step()
207
+ total_loss += loss.item()
208
+
209
+ avg_loss = total_loss / (batch_idx + 1)
210
+
211
+ if (batch_idx + 1) % LOG_INTERVAL == 0 or batch_idx == len(train_dataloader) - 1:
212
+ elapsed_time = datetime.datetime.now() - start_time
213
+ elapsed_str = str(elapsed_time).split(".")[0]
214
+ current_lr = self.optimizer.param_groups[0]["lr"]
215
+
216
+ progress.update(epoch_task, advance=LOG_INTERVAL, loss=avg_loss, step=batch_idx + 1)
217
+
218
+ self.log_print(
219
+ f"[cyan]Iteration {batch_idx + 1}[/cyan] - "
220
+ f"Loss: {avg_loss:.6f} - "
221
+ f"LR: {current_lr:.5e} - "
222
+ f"Elapsed Time: {elapsed_str}"
223
+ )
224
+ if self.console and hasattr(self.console, "file"):
225
+ self.console.file.flush()
226
+
227
+ progress.refresh()
228
+
229
+ self.log_tensorboard(epoch * len(train_dataloader) + batch_idx, loss=avg_loss)
230
+
231
+ if self.cfg.sanity and batch_idx == 0:
232
+ break
233
+
234
+ self.scheduler.step()
235
+
236
+ if self.cfg.sanity:
237
+ break
238
+
239
+ current_lr = self.optimizer.param_groups[0]["lr"]
240
+ self.log_print(
241
+ f"[bold cyan]Epoch {epoch+1} Summary:[/bold cyan] " f"Loss = {avg_loss:.6f} - " f"LR = {current_lr:.2e}"
242
+ )
243
+
244
+ return
245
+
246
+ def save_checkpoint(self, checkpoint_path):
247
+ console = self.console
248
+ # Save final model state after training
249
+ torch.save(self.classifier.state_dict(), checkpoint_path)
250
+ self.log_print(f"[bold green]Training completed. Model saved at: {checkpoint_path}[/bold green]")
251
+ console.file.flush()
252
+ return
253
+
254
+ @torch.inference_mode()
255
+ def evaluate(self, dataloader, epoch):
256
+ self.log_print("[yellow]Evaluating model...[/yellow]")
257
+ torch.cuda.empty_cache()
258
+
259
+ self.backbone.eval()
260
+ self.model.eval()
261
+ self.classifier.eval()
262
+
263
+ # Initialize depth metrics
264
+ results = {"d1": 0, "d2": 0, "d3": 0, "abs_rel": 0, "sq_rel": 0, "rmse": 0, "rmse_log": 0, "log_10": 0, "silog": 0}
265
+
266
+ nsamples = 0
267
+
268
+ for batch_idx, batch in enumerate(tqdm(dataloader, desc="Evaluating")):
269
+ batch = get_batch(batch, self.device)
270
+ image_batch = batch["image"]
271
+ target = batch["depth"].to(self.device)
272
+
273
+ # Process batch and get masked predictions and targets
274
+ pred, target = self.process_batch(image_batch, target, is_training=False, batch_idx=batch_idx)
275
+
276
+ cur_results = eval_metrics(
277
+ target.cpu().detach().numpy(),
278
+ pred.cpu().detach().numpy(),
279
+ self.cfg.metrics.depth.min_depth,
280
+ self.cfg.metrics.depth.max_depth,
281
+ )
282
+
283
+ for k in results.keys():
284
+ results[k] += cur_results[k]
285
+ nsamples += 1
286
+
287
+ if self.cfg.sanity and batch_idx == 0:
288
+ break
289
+
290
+ metrics = {}
291
+ for k in results.keys():
292
+ metrics[k] = results[k] / nsamples
293
+
294
+ # Log metrics to TensorBoard
295
+ self.log_tensorboard(step=epoch, metrics=metrics)
296
+
297
+ self.log_print(f"[bold green]Results: {metrics}[/bold green]")
298
+ return
299
+
300
+ @torch.inference_mode()
301
+ def simple_inference(self, image_batch):
302
+ self.backbone.eval()
303
+ self.model.eval()
304
+ self.classifier.eval()
305
+
306
+ H, W = image_batch.shape[-2:]
307
+ with torch.no_grad():
308
+ lr_feats = self.backbone(image_batch)
309
+ hr_feats = self.model(image_batch, lr_feats, (H, W))
310
+
311
+ labels = self.classifier(hr_feats) # Pass through the classifier
312
+
313
+ labels = F.relu(labels)
314
+ eps = 0.1
315
+ labels = labels + eps
316
+ labels = labels / labels.sum(dim=1, keepdim=True)
317
+ labels = torch.einsum("ikmn,k->imn", [labels, self.bins]).unsqueeze(dim=1)
318
+ return hr_feats, labels, lr_feats
319
+
320
+
321
+ @hydra.main(config_path="../config", config_name="eval_probing")
322
+ def main(cfg):
323
+ # Use Hydra's current working directory for all outputs
324
+ task = "depth"
325
+ model_name = cfg.model.name if hasattr(cfg.model, "name") else "base"
326
+
327
+ # Get Hydra's current working directory
328
+ hydra_cfg = HydraConfig.get()
329
+ current_run_dir = hydra_cfg.runtime.output_dir
330
+
331
+ # Setup Classifier checkpoint in current Hydra directory
332
+ checkpoint_filename = "linear_probe.pth"
333
+ checkpoint_path = os.path.join(current_run_dir, checkpoint_filename)
334
+
335
+ # Create persistent consoles instead of creating new ones each time
336
+ terminal_console = Console() # Terminal output
337
+ tag = ""
338
+ if cfg.eval.model_ckpt:
339
+ try:
340
+ tag = cfg.eval.model_ckpt.split("/")[-3]
341
+ except:
342
+ tag = ""
343
+
344
+ # Create log file names
345
+ if Path(checkpoint_path).exists():
346
+ file_name = f"eval_{model_name}_{tag}_{task}_prob_from_ups.log"
347
+ else:
348
+ file_name = f"train_{model_name}_{tag}_{task}_prob_from_ups.log"
349
+
350
+ # All outputs in Hydra's current directory
351
+ log_file_path = os.path.join(current_run_dir, file_name)
352
+ tb_dir = os.path.join(current_run_dir, "tb")
353
+ os.makedirs(tb_dir, exist_ok=True)
354
+
355
+ file_console = Console(file=open(log_file_path, "w"))
356
+
357
+ # Initialize TensorBoard writer in current Hydra directory
358
+ writer = SummaryWriter(log_dir=tb_dir)
359
+
360
+ def log_print(*args, **kwargs):
361
+ """Log to both terminal and file with immediate flushing"""
362
+ terminal_console.print(*args, **kwargs)
363
+ file_console.print(*args, **kwargs)
364
+ file_console.file.flush()
365
+
366
+ # Start logging
367
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
368
+ log_print(f"\n[bold blue]{'='*50}[/bold blue]")
369
+ log_print(f"[bold blue]Starting at {timestamp}[/bold blue]")
370
+ log_print(f"[bold green]Configuration:[/bold green]")
371
+ log_print(OmegaConf.to_yaml(cfg))
372
+
373
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
374
+ log_print(f"[bold yellow]Using device: {device}[/bold yellow]")
375
+
376
+ log_print(f"\n[bold cyan]Processing {task} task:[/bold cyan]")
377
+ log_print(f"\n[bold cyan]Image size: {cfg.img_size}[/bold cyan]")
378
+
379
+ # Setup Backbone
380
+ backbones, backbone_names, *_ = load_multiple_backbones(cfg, cfg.backbone, device)
381
+ backbone = backbones[0]
382
+ backbone.requires_grad_(False)
383
+ backbone.eval()
384
+
385
+ # Setup Model
386
+ model = instantiate(cfg.model).to(device)
387
+ if cfg.eval.model_ckpt:
388
+ checkpoint = torch.load(cfg.eval.model_ckpt, map_location=device, weights_only=False)
389
+ if cfg.model.name == "featup":
390
+ new_ckpts = {
391
+ k.replace("model.1.", "norm."): v
392
+ for k, v in checkpoint["state_dict"].items()
393
+ if "upsampler" in k or "model.1.norm" in k
394
+ }
395
+ model.load_state_dict(new_ckpts, strict=True)
396
+ else:
397
+ model.load_state_dict(checkpoint, strict=True)
398
+ log_print(f"[green]Loaded model from checkpoint: {cfg.eval.model_ckpt}[/green]")
399
+ else:
400
+ log_print(f"[yellow]No model checkpoint provided, using untrained model[/yellow]")
401
+
402
+ if cfg.eval.supervise_model == False:
403
+ model.requires_grad_(False)
404
+ model.eval()
405
+ else:
406
+ model.requires_grad_(True)
407
+ model.train()
408
+
409
+ # Setup Dataloaders - Modified to match train_jafar.py approach
410
+ train_loader, val_loader = get_dataloaders(cfg, shuffle=False)
411
+ log_print(f"[bold cyan]Train Dataset size: {len(train_loader.dataset)}[/bold cyan]")
412
+ log_print(f"[bold cyan]Val Dataset size: {len(val_loader.dataset)}[/bold cyan]")
413
+
414
+ # Setup Evaluator
415
+ evaluator = UpsamplerEvaluator(model, backbone, device, cfg, writer, file_console)
416
+ evaluator.to(device)
417
+
418
+ log_print("[yellow]Training classifier...[/yellow]\n")
419
+ evaluator.set_optimizer(cfg, train_loader)
420
+
421
+ progress = Progress(
422
+ SpinnerColumn(),
423
+ TextColumn("[progress.description]{task.description}"),
424
+ BarColumn(),
425
+ TaskProgressColumn(),
426
+ TextColumn("[yellow]Loss: {task.fields[loss]:.6f}"),
427
+ TextColumn("[green]Step: {task.fields[step]:.6e}"),
428
+ console=file_console,
429
+ )
430
+
431
+ start_time = datetime.datetime.now()
432
+
433
+ with progress:
434
+ # Standard training for upsampler
435
+ log_print(f"[yellow]Training for {cfg.num_epochs} epochs[/yellow]\n")
436
+
437
+ for epoch in range(cfg.num_epochs):
438
+ evaluator.train(train_loader, progress, epoch, start_time)
439
+ evaluator.evaluate(val_loader, epoch)
440
+
441
+ evaluator.save_checkpoint(checkpoint_path)
442
+
443
+ file_console.file.close()
444
+ writer.close() # Close TensorBoard writer
445
+
446
+
447
+ if __name__ == "__main__":
448
+ main()
Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/eval_seg_probing.py ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import datetime
2
+ import os
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ import hydra
7
+ import numpy as np
8
+ import torch
9
+ import torch.nn as nn
10
+ import torch.nn.functional as F
11
+ import torchvision.transforms as T
12
+ from einops import rearrange
13
+ from hydra.core.global_hydra import GlobalHydra
14
+ from hydra.core.hydra_config import HydraConfig
15
+ from hydra.utils import instantiate
16
+ from omegaconf import OmegaConf
17
+ from rich.console import Console
18
+ from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn
19
+ from torch.utils.tensorboard import SummaryWriter
20
+ from torchmetrics.classification import Accuracy, JaccardIndex
21
+ from tqdm import tqdm
22
+
23
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
24
+
25
+ import random
26
+
27
+ from utils.training import get_batch, get_dataloaders, load_multiple_backbones
28
+
29
+ LOG_INTERVAL = 100
30
+ IGNORE = 255
31
+
32
+
33
+ class UpsamplerEvaluator:
34
+
35
+ def __init__(self, model, backbone, device, cfg, writer, console):
36
+ self.model, self.backbone, self.device, self.cfg, self.writer, self.console = (
37
+ model,
38
+ backbone,
39
+ device,
40
+ cfg,
41
+ writer,
42
+ console,
43
+ )
44
+
45
+ # Use backbone-specific normalization (like train_jafar.py)
46
+ self.mean_bck = backbone.config["mean"]
47
+ self.std_bck = backbone.config["std"]
48
+
49
+ # Upsampler normalization (ImageNet standard)
50
+ self.mean_ups = (0.485, 0.456, 0.406)
51
+ self.std_ups = (0.229, 0.224, 0.225)
52
+
53
+ # Initialize task-specific components
54
+ self.accuracy_metric = Accuracy(num_classes=cfg.metrics.seg.num_classes, task="multiclass").to(device)
55
+ self.iou_metric = JaccardIndex(num_classes=cfg.metrics.seg.num_classes, task="multiclass").to(device)
56
+ self.classifier = nn.Conv2d(backbone.embed_dim, cfg.metrics.seg.num_classes, 1).to(device)
57
+
58
+ def set_up_classifier(self, checkpoint_path):
59
+ """Load classifier weights from a checkpoint."""
60
+ if Path(checkpoint_path).exists():
61
+ checkpoint = torch.load(checkpoint_path)
62
+ self.classifier.load_state_dict(checkpoint)
63
+ self.console.print(f"Loaded classifier from checkpoint: {checkpoint_path}")
64
+ else:
65
+ raise FileNotFoundError(f"Checkpoint file not found: {checkpoint_path}")
66
+
67
+ def set_optimizer(self, cfg, loader):
68
+ params = []
69
+ params_classifier = self.classifier.parameters()
70
+ params_model = self.model.parameters()
71
+
72
+ params = list(params_classifier)
73
+ optimizer = instantiate(cfg.optimizer, params=params)
74
+ scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=cfg.num_epochs * len(loader))
75
+ self.optimizer = optimizer
76
+ self.scheduler = scheduler
77
+
78
+ num_params = sum(p.numel() for p in params if p.requires_grad)
79
+ self.log_print(f"[bold cyan]Number of optimized parameters: {num_params:,}[/bold cyan]")
80
+
81
+ def log_print(self, *args, **kwargs):
82
+ Console(force_terminal=True).print(*args, **kwargs)
83
+ self.console.print(*args, **kwargs)
84
+ if hasattr(self.console, "file") and self.console.file:
85
+ self.console.file.flush()
86
+
87
+ def log_tensorboard(self, step, loss=None, metrics=None):
88
+ if loss is not None:
89
+ self.writer.add_scalar("Loss/Step", loss, step)
90
+ if metrics is not None:
91
+ for metric_name, metric_value in metrics.items():
92
+ self.writer.add_scalar(f"Metrics/{metric_name}", metric_value, step)
93
+
94
+ def process_batch(self, image_batch, target, batch_idx=0):
95
+ H, W = target.shape[-2:]
96
+
97
+ image_batch_bck = T.functional.normalize(image_batch, mean=self.mean_bck, std=self.std_bck)
98
+ image_batch_ups = T.functional.normalize(image_batch, mean=self.mean_ups, std=self.std_ups)
99
+
100
+ with torch.no_grad():
101
+ pred = self.backbone(image_batch_bck)
102
+
103
+ input_shape = pred.shape[-2:]
104
+ with torch.no_grad():
105
+ pred = self.model(image_batch_ups, pred, (H, W)).detach()
106
+
107
+ if batch_idx == 0:
108
+ self.log_print(
109
+ f"[red]Model input size {input_shape}, output size {pred.shape[-2:]} for expected size {(H, W)}[/red]"
110
+ )
111
+ pred = self.classifier(pred)
112
+
113
+ if pred.shape[-2:] != (H, W):
114
+ pred = F.interpolate(pred, size=(H, W), mode="bilinear")
115
+
116
+ if target.shape[-2:] != pred.shape[-2:]:
117
+ target = (
118
+ F.interpolate(
119
+ target.unsqueeze(1),
120
+ size=pred.shape[-2:],
121
+ mode="nearest-exact",
122
+ )
123
+ .squeeze(1)
124
+ .to(target.dtype)
125
+ )
126
+
127
+ valid_mask = target != IGNORE
128
+
129
+ pred = rearrange(pred, "b c h w -> (b h w) c")
130
+ target = rearrange(target, "b h w -> (b h w)")
131
+ valid_mask = rearrange(valid_mask, "b h w -> (b h w)")
132
+
133
+ pred = pred[valid_mask]
134
+ target = target[valid_mask]
135
+ return pred, target
136
+
137
+ def train(
138
+ self,
139
+ train_dataloader,
140
+ progress,
141
+ epoch,
142
+ start_time,
143
+ ):
144
+ self.log_print(f"[yellow]Training model epoch {epoch+1}...[/yellow]")
145
+ self.backbone.eval()
146
+ self.model.eval()
147
+ self.classifier.train()
148
+
149
+ epoch_task = progress.add_task(
150
+ f"Epoch {epoch+1}/{self.cfg.num_epochs}",
151
+ total=len(train_dataloader),
152
+ loss=0.0,
153
+ step=0,
154
+ )
155
+ total_loss = 0
156
+
157
+ for batch_idx, batch in enumerate(train_dataloader):
158
+ batch = get_batch(batch, self.device)
159
+ image_batch = batch["image"]
160
+ target = batch["label"].to(self.device)
161
+
162
+ if random.random() < 0.5:
163
+ assert len(image_batch.shape) == 4 and len(target.shape) == 3
164
+ image_batch = torch.flip(image_batch, dims=[3])
165
+ target = torch.flip(target, dims=[2])
166
+
167
+ self.optimizer.zero_grad()
168
+
169
+ pred, target = self.process_batch(image_batch, target, batch_idx=batch_idx)
170
+
171
+ loss = F.cross_entropy(pred, target)
172
+
173
+ loss.backward()
174
+ self.optimizer.step()
175
+ total_loss += loss.item()
176
+
177
+ avg_loss = total_loss / (batch_idx + 1)
178
+
179
+ if (batch_idx + 1) % LOG_INTERVAL == 0 or batch_idx == len(train_dataloader) - 1:
180
+ elapsed_time = datetime.datetime.now() - start_time
181
+ elapsed_str = str(elapsed_time).split(".")[0]
182
+ current_lr = self.optimizer.param_groups[0]["lr"]
183
+
184
+ progress.update(epoch_task, advance=LOG_INTERVAL, loss=avg_loss, step=batch_idx + 1)
185
+
186
+ self.log_print(
187
+ f"[cyan]Iteration {batch_idx + 1}[/cyan] - "
188
+ f"Loss: {avg_loss:.6f} - "
189
+ f"LR: {current_lr:.5e} - "
190
+ f"Elapsed Time: {elapsed_str}"
191
+ )
192
+ if self.console and hasattr(self.console, "file"):
193
+ self.console.file.flush()
194
+
195
+ progress.refresh()
196
+
197
+ self.log_tensorboard(epoch * len(train_dataloader) + batch_idx, loss=avg_loss)
198
+
199
+ if self.cfg.sanity and batch_idx == 0:
200
+ break
201
+
202
+ self.scheduler.step()
203
+
204
+ if self.cfg.sanity:
205
+ break
206
+
207
+ current_lr = self.optimizer.param_groups[0]["lr"]
208
+ self.log_print(
209
+ f"[bold cyan]Epoch {epoch+1} Summary:[/bold cyan] " f"Loss = {avg_loss:.6f} - " f"LR = {current_lr:.2e}"
210
+ )
211
+
212
+ return
213
+
214
+ def save_checkpoint(self, checkpoint_path):
215
+ console = self.console
216
+ torch.save(self.classifier.state_dict(), checkpoint_path)
217
+ self.log_print(f"[bold green]Training completed. Model saved at: {checkpoint_path}[/bold green]")
218
+ console.file.flush()
219
+ return
220
+
221
+ @torch.inference_mode()
222
+ def evaluate(self, dataloader, epoch):
223
+ self.log_print("[yellow]Evaluating model...[/yellow]")
224
+ torch.cuda.empty_cache()
225
+
226
+ self.backbone.eval()
227
+ self.model.eval()
228
+ self.classifier.eval()
229
+
230
+ self.accuracy_metric.reset()
231
+ self.iou_metric.reset()
232
+
233
+ for batch_idx, batch in enumerate(tqdm(dataloader, desc="Evaluating")):
234
+ batch = get_batch(batch, self.device)
235
+ image_batch = batch["image"]
236
+ target = batch["label"].to(self.device)
237
+
238
+ pred, target = self.process_batch(image_batch, target, batch_idx=batch_idx)
239
+
240
+ self.accuracy_metric(pred, target)
241
+ self.iou_metric(pred, target)
242
+
243
+ if self.cfg.sanity and batch_idx == 0:
244
+ break
245
+
246
+ metrics = {}
247
+ metrics.update(
248
+ {
249
+ "accuracy": self.accuracy_metric.compute().item(),
250
+ "iou": self.iou_metric.compute().item(),
251
+ }
252
+ )
253
+
254
+ self.log_tensorboard(step=epoch, metrics=metrics)
255
+
256
+ self.log_print(f"[bold green]Results: {metrics}[/bold green]")
257
+ return
258
+
259
+ @torch.inference_mode()
260
+ def simple_inference(self, image_batch):
261
+ self.backbone.eval()
262
+ self.model.eval()
263
+ self.classifier.eval()
264
+
265
+ H, W = image_batch.shape[-2:]
266
+ image_batch_bck = T.functional.normalize(image_batch, mean=self.mean_bck, std=self.std_bck)
267
+ image_batch_ups = T.functional.normalize(image_batch, mean=self.mean_ups, std=self.std_ups)
268
+
269
+ with torch.no_grad():
270
+ lr_feats = self.backbone(image_batch_bck)
271
+ hr_feats = self.model(image_batch_ups, lr_feats, (H, W))
272
+
273
+ labels = self.classifier(hr_feats).argmax(dim=1, keepdim=True)
274
+ return hr_feats, labels, lr_feats
275
+
276
+
277
+ @hydra.main(config_path="../config", config_name="eval_probing")
278
+ def main(cfg):
279
+ hydra_cfg = HydraConfig.get()
280
+ current_run_dir = hydra_cfg.runtime.output_dir
281
+
282
+ task = cfg.eval.task
283
+ model_name = cfg.model.name if hasattr(cfg.model, "name") else "base"
284
+
285
+ # Setup Classifier checkpoint in current Hydra directory
286
+ checkpoint_filename = "linear_probe.pth"
287
+ checkpoint_path = os.path.join(current_run_dir, checkpoint_filename)
288
+
289
+ # Create persistent consoles instead of creating new ones each time
290
+ terminal_console = Console()
291
+ tag = ""
292
+ if cfg.eval.model_ckpt:
293
+ try:
294
+ tag = cfg.eval.model_ckpt.split("/")[-3]
295
+ except:
296
+ tag = ""
297
+
298
+ # Create log file names
299
+ file_name = f"train_{model_name}_{tag}_{task}.log"
300
+
301
+ # All outputs in Hydra's current directory
302
+ log_file_path = os.path.join(current_run_dir, file_name)
303
+ tb_dir = os.path.join(current_run_dir, "tb")
304
+ os.makedirs(tb_dir, exist_ok=True)
305
+
306
+ file_console = Console(file=open(log_file_path, "w"))
307
+
308
+ # Initialize TensorBoard writer in current Hydra directory
309
+ writer = SummaryWriter(log_dir=tb_dir)
310
+
311
+ def log_print(*args, **kwargs):
312
+ """Log to both terminal and file with immediate flushing"""
313
+ terminal_console.print(*args, **kwargs)
314
+ file_console.print(*args, **kwargs)
315
+ file_console.file.flush()
316
+
317
+ # Start logging
318
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
319
+ log_print(f"\n[bold blue]{'='*50}[/bold blue]")
320
+ log_print(f"[bold blue]Starting at {timestamp}[/bold blue]")
321
+ log_print(f"[bold green]Configuration:[/bold green]")
322
+ log_print(OmegaConf.to_yaml(cfg))
323
+
324
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
325
+ log_print(f"[bold yellow]Using device: {device}[/bold yellow]")
326
+
327
+ log_print(f"\n[bold cyan]Processing {task} task:[/bold cyan]")
328
+ log_print(f"\n[bold cyan]Image size: {cfg.img_size}[/bold cyan]")
329
+
330
+ # Setup Backbone
331
+ backbones, *_ = load_multiple_backbones(cfg, cfg.backbone, device)
332
+ backbone = backbones[0]
333
+ backbone.requires_grad_(False)
334
+ backbone.eval()
335
+
336
+ # Setup Model
337
+ model = instantiate(cfg.model).to(device)
338
+ if cfg.eval.model_ckpt:
339
+ checkpoint = torch.load(cfg.eval.model_ckpt, map_location=device, weights_only=False)
340
+ if cfg.model.name == "featup":
341
+ new_ckpts = {
342
+ k.replace("model.1.", "norm."): v
343
+ for k, v in checkpoint["state_dict"].items()
344
+ if "upsampler" in k or "model.1.norm" in k
345
+ }
346
+ model.load_state_dict(new_ckpts, strict=True)
347
+ else:
348
+ model.load_state_dict(checkpoint, strict=True)
349
+ log_print(f"[green]Loaded model from checkpoint: {cfg.eval.model_ckpt}[/green]")
350
+ else:
351
+ log_print(f"[yellow]No model checkpoint provided, using untrained model[/yellow]")
352
+
353
+ model.requires_grad_(True)
354
+ model.train()
355
+
356
+ # Setup Dataloaders - Modified to match train_jafar.py approach
357
+ train_loader, val_loader = get_dataloaders(cfg, shuffle=False)
358
+ log_print(f"[bold cyan]Train Dataset size: {len(train_loader.dataset)}[/bold cyan]")
359
+ log_print(f"[bold cyan]Val Dataset size: {len(val_loader.dataset)}[/bold cyan]")
360
+
361
+ # Setup Evaluator
362
+ evaluator = UpsamplerEvaluator(model, backbone, device, cfg, writer, file_console)
363
+
364
+ log_print("[yellow]Training classifier...[/yellow]\n")
365
+ evaluator.set_optimizer(cfg, loader=train_loader)
366
+
367
+ progress = Progress(
368
+ SpinnerColumn(),
369
+ TextColumn("[progress.description]{task.description}"),
370
+ BarColumn(),
371
+ TaskProgressColumn(),
372
+ TextColumn("[yellow]Loss: {task.fields[loss]:.6f}"),
373
+ TextColumn("[green]Step: {task.fields[step]:.6e}"),
374
+ console=file_console,
375
+ )
376
+
377
+ start_time = datetime.datetime.now()
378
+
379
+ with progress:
380
+ log_print(f"[yellow]Training for {cfg.num_epochs} epochs[/yellow]\n")
381
+
382
+ for epoch in range(cfg.num_epochs):
383
+ evaluator.train(train_loader, progress, epoch, start_time)
384
+ evaluator.evaluate(val_loader, epoch)
385
+
386
+ evaluator.save_checkpoint(checkpoint_path)
387
+
388
+ file_console.file.close()
389
+ writer.close()
390
+
391
+
392
+ if __name__ == "__main__":
393
+ main()
Pixal3D/torch_hub/hub/valeoai_NAF_main/evaluation/eval_video_seg.py ADDED
@@ -0,0 +1,816 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Evaluate JAFAR performance on DAVIS video segmentation. Based on https://github.com/davisvideochallenge/davis2017-evaluation and https://github.com/facebookresearch/dino/blob/main/eval_video_segmentation.py
3
+
4
+ Code adapted by: Saksham Suri and Matthew Walmer
5
+
6
+ Sample Usage:
7
+ python eval_davis.py
8
+ python eval_davis.py backbone.name=vit_small_patch14_reg4_dinov2 model=jafar
9
+ """
10
+
11
+ import argparse
12
+ import copy
13
+ import datetime
14
+ import glob
15
+ import math
16
+ import os
17
+ import queue
18
+ import sys
19
+ import time
20
+ import warnings
21
+ from collections import defaultdict
22
+ from urllib.request import urlopen
23
+
24
+ import cv2
25
+ import hydra
26
+ import matplotlib.pyplot as plt
27
+ import numpy as np
28
+ import pandas as pd
29
+ import torch
30
+ import torch.multiprocessing
31
+ import torch.nn as nn
32
+ import torch.nn.functional as F
33
+ from einops import rearrange
34
+ from hydra.core.hydra_config import HydraConfig
35
+ from hydra.utils import instantiate
36
+ from omegaconf import DictConfig, OmegaConf
37
+ from PIL import Image
38
+ from rich.console import Console
39
+ from torch import nn
40
+ from torch.nn import functional as F
41
+ from torchvision import transforms
42
+ from tqdm import tqdm
43
+
44
+ warnings.filterwarnings("ignore", category=RuntimeWarning)
45
+
46
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
47
+
48
+ from utils.training import load_multiple_backbones
49
+
50
+ torch.multiprocessing.set_sharing_strategy("file_system")
51
+
52
+
53
+ # ============================================================================
54
+ # DAVIS Evaluation Classes and Functions (Self-contained)
55
+ # ============================================================================
56
+
57
+
58
+ class DAVISDataset(object):
59
+ """Simplified DAVIS dataset class for evaluation"""
60
+
61
+ SUBSET_OPTIONS = ["train", "val", "test-dev", "test-challenge"]
62
+ TASKS = ["semi-supervised", "unsupervised"]
63
+ VOID_LABEL = 255
64
+
65
+ def __init__(self, root, task="semi-supervised", subset="val", sequences="all", resolution="480p"):
66
+ self.task = task
67
+ self.subset = subset
68
+ self.root = root
69
+ self.img_path = os.path.join(self.root, "JPEGImages", resolution)
70
+ self.mask_path = os.path.join(self.root, "Annotations", resolution)
71
+ self.imagesets_path = os.path.join(self.root, "ImageSets", "2017")
72
+
73
+ if sequences == "all":
74
+ with open(os.path.join(self.imagesets_path, f"{self.subset}.txt"), "r") as f:
75
+ tmp = f.readlines()
76
+ sequences_names = [x.strip() for x in tmp]
77
+ else:
78
+ sequences_names = sequences if isinstance(sequences, list) else [sequences]
79
+
80
+ self.sequences = defaultdict(dict)
81
+ for seq in sequences_names:
82
+ images = np.sort(glob.glob(os.path.join(self.img_path, seq, "*.jpg"))).tolist()
83
+ self.sequences[seq]["images"] = images
84
+ masks = np.sort(glob.glob(os.path.join(self.mask_path, seq, "*.png"))).tolist()
85
+ self.sequences[seq]["masks"] = masks
86
+
87
+ def get_sequences(self):
88
+ return list(self.sequences.keys())
89
+
90
+ def get_all_masks(self, sequence, separate_objects_masks=False):
91
+ """Get all ground truth masks for a sequence"""
92
+ masks = []
93
+ mask_ids = []
94
+
95
+ for mask_path in self.sequences[sequence]["masks"]:
96
+ mask = np.array(Image.open(mask_path))
97
+ masks.append(mask)
98
+ mask_ids.append(os.path.splitext(os.path.basename(mask_path))[0])
99
+
100
+ masks = np.array(masks)
101
+
102
+ if separate_objects_masks:
103
+ # Convert to separate object masks
104
+ num_objects = int(np.max(masks))
105
+ if num_objects > 0:
106
+ object_masks = np.zeros((num_objects, *masks.shape))
107
+ for obj_id in range(1, num_objects + 1):
108
+ object_masks[obj_id - 1] = (masks == obj_id).astype(np.uint8)
109
+ void_masks = np.zeros_like(masks[0]) # No void pixels for simplicity
110
+ return object_masks, void_masks, mask_ids
111
+
112
+ return masks, mask_ids
113
+
114
+
115
+ class DAVISResults(object):
116
+ """Results reader for DAVIS evaluation"""
117
+
118
+ def __init__(self, root_dir):
119
+ self.root_dir = root_dir
120
+
121
+ def _read_mask(self, sequence, frame_id):
122
+ mask_path = os.path.join(self.root_dir, sequence, f"{frame_id}.png")
123
+ if not os.path.exists(mask_path):
124
+ raise FileNotFoundError(f"Mask not found: {mask_path}")
125
+ return np.array(Image.open(mask_path))
126
+
127
+ def read_masks(self, sequence, masks_id):
128
+ """Read all prediction masks for a sequence"""
129
+ mask_0 = self._read_mask(sequence, masks_id[0])
130
+ masks = np.zeros((len(masks_id), *mask_0.shape))
131
+ for ii, m in enumerate(masks_id):
132
+ masks[ii, ...] = self._read_mask(sequence, m)
133
+
134
+ # Convert to separate object masks
135
+ num_objects = int(np.max(masks))
136
+ if num_objects == 0:
137
+ return np.zeros((1, *masks.shape))
138
+
139
+ tmp = np.ones((num_objects, *masks.shape))
140
+ tmp = tmp * np.arange(1, num_objects + 1)[:, None, None, None]
141
+ masks = (tmp == masks[None, ...]) > 0
142
+ return masks
143
+
144
+
145
+ def davis_db_eval_iou(annotation, segmentation, void_pixels=None):
146
+ """Compute region similarity as the Jaccard Index"""
147
+ assert (
148
+ annotation.shape == segmentation.shape
149
+ ), f"Annotation({annotation.shape}) and segmentation:{segmentation.shape} dimensions do not match."
150
+
151
+ annotation = annotation.astype(bool)
152
+ segmentation = segmentation.astype(bool)
153
+
154
+ if void_pixels is not None:
155
+ void_pixels = void_pixels.astype(bool)
156
+ else:
157
+ void_pixels = np.zeros_like(segmentation)
158
+
159
+ # Intersection between all sets
160
+ inters = np.sum((segmentation & annotation) & np.logical_not(void_pixels), axis=(-2, -1))
161
+ union = np.sum((segmentation | annotation) & np.logical_not(void_pixels), axis=(-2, -1))
162
+
163
+ j = inters / union
164
+ if j.ndim == 0:
165
+ j = 1 if np.isclose(union, 0) else j
166
+ else:
167
+ j[np.isclose(union, 0)] = 1
168
+ return j
169
+
170
+
171
+ def davis_f_measure(foreground_mask, gt_mask, void_pixels=None, bound_th=0.008):
172
+ """Compute F-measure for boundary accuracy"""
173
+ assert foreground_mask.shape == gt_mask.shape
174
+
175
+ if void_pixels is not None:
176
+ assert foreground_mask.shape == void_pixels.shape
177
+ # Apply void mask
178
+ foreground_mask = foreground_mask.copy()
179
+ gt_mask = gt_mask.copy()
180
+ foreground_mask[void_pixels] = 0
181
+ gt_mask[void_pixels] = 0
182
+
183
+ bound_pix = bound_th if bound_th >= 1 else np.ceil(bound_th * np.linalg.norm(foreground_mask.shape))
184
+
185
+ # Get the pixel boundaries of both masks
186
+ fg_boundary = _seg2bmap(foreground_mask)
187
+ gt_boundary = _seg2bmap(gt_mask)
188
+
189
+ from scipy.ndimage import distance_transform_edt
190
+
191
+ # Distance transform
192
+ fg_dist = distance_transform_edt(1 - fg_boundary)
193
+ gt_dist = distance_transform_edt(1 - gt_boundary)
194
+
195
+ # Precision: how many predicted boundary pixels are close to gt
196
+ precision = np.sum(fg_boundary * (gt_dist <= bound_pix)) / (np.sum(fg_boundary) + 1e-10)
197
+
198
+ # Recall: how many gt boundary pixels are close to predicted
199
+ recall = np.sum(gt_boundary * (fg_dist <= bound_pix)) / (np.sum(gt_boundary) + 1e-10)
200
+
201
+ # F-measure
202
+ if precision + recall == 0:
203
+ f_score = 0
204
+ else:
205
+ f_score = 2 * precision * recall / (precision + recall)
206
+
207
+ return f_score
208
+
209
+
210
+ def _seg2bmap(seg, width=None, height=None):
211
+ """Convert segmentation to boundary map"""
212
+ seg = seg.astype(bool)
213
+ seg = seg.astype(np.uint8)
214
+
215
+ # Compute edges using Sobel operator
216
+ kernel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], dtype=np.float32)
217
+ kernel_y = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]], dtype=np.float32)
218
+
219
+ edges_x = cv2.filter2D(seg.astype(np.float32), -1, kernel_x)
220
+ edges_y = cv2.filter2D(seg.astype(np.float32), -1, kernel_y)
221
+ edges = np.sqrt(edges_x**2 + edges_y**2)
222
+
223
+ # Threshold to get binary boundary
224
+ bmap = edges > 0.1
225
+
226
+ return bmap.astype(bool)
227
+
228
+
229
+ def davis_db_eval_boundary(annotation, segmentation, void_pixels=None, bound_th=0.008):
230
+ """Compute boundary F-measure"""
231
+ assert annotation.shape == segmentation.shape
232
+ if void_pixels is not None:
233
+ assert annotation.shape == void_pixels.shape
234
+
235
+ if annotation.ndim == 3:
236
+ n_frames = annotation.shape[0]
237
+ f_res = np.zeros(n_frames)
238
+ for frame_id in range(n_frames):
239
+ void_pixels_frame = None if void_pixels is None else void_pixels[frame_id, :, :]
240
+ f_res[frame_id] = davis_f_measure(
241
+ segmentation[frame_id, :, :], annotation[frame_id, :, :], void_pixels_frame, bound_th=bound_th
242
+ )
243
+ elif annotation.ndim == 2:
244
+ f_res = davis_f_measure(segmentation, annotation, void_pixels, bound_th=bound_th)
245
+ else:
246
+ raise ValueError(f"Annotation and segmentation must be 2D or 3D arrays, got {annotation.ndim}D")
247
+
248
+ return f_res
249
+
250
+
251
+ def davis_db_statistics(per_frame_values):
252
+ """Compute mean, recall and decay from per-frame evaluation"""
253
+ # Strip off nan values
254
+ with warnings.catch_warnings():
255
+ warnings.simplefilter("ignore", category=RuntimeWarning)
256
+ M = np.nanmean(per_frame_values)
257
+ O = np.nanmean(per_frame_values > 0.5)
258
+
259
+ N_bins = 4
260
+ ids = np.round(np.linspace(1, len(per_frame_values), N_bins + 1) + 1e-10) - 1
261
+ ids = ids.astype(np.uint8)
262
+
263
+ D_bins = [per_frame_values[ids[i] : ids[i + 1] + 1] for i in range(0, 4)]
264
+
265
+ with warnings.catch_warnings():
266
+ warnings.simplefilter("ignore", category=RuntimeWarning)
267
+ D = np.nanmean(D_bins[0]) - np.nanmean(D_bins[3])
268
+
269
+ return M, O, D
270
+
271
+
272
+ class DAVISEvaluation(object):
273
+ """Complete DAVIS evaluation class"""
274
+
275
+ def __init__(self, davis_root, task="semi-supervised", gt_set="val", sequences="all"):
276
+ self.davis_root = davis_root
277
+ self.task = task
278
+ self.dataset = DAVISDataset(root=davis_root, task=task, subset=gt_set, sequences=sequences)
279
+
280
+ @staticmethod
281
+ def _evaluate_semisupervised(all_gt_masks, all_res_masks, all_void_masks, metric):
282
+ if all_res_masks.shape[0] > all_gt_masks.shape[0]:
283
+ print("\nIn your PNG files there is an index higher than the number of objects in the sequence!")
284
+ return None, None
285
+ elif all_res_masks.shape[0] < all_gt_masks.shape[0]:
286
+ zero_padding = np.zeros((all_gt_masks.shape[0] - all_res_masks.shape[0], *all_res_masks.shape[1:]))
287
+ all_res_masks = np.concatenate([all_res_masks, zero_padding], axis=0)
288
+
289
+ j_metrics_res, f_metrics_res = np.zeros(all_gt_masks.shape[:2]), np.zeros(all_gt_masks.shape[:2])
290
+ for ii in range(all_gt_masks.shape[0]):
291
+ if "J" in metric:
292
+ j_metrics_res[ii, :] = davis_db_eval_iou(all_gt_masks[ii, ...], all_res_masks[ii, ...], all_void_masks)
293
+ if "F" in metric:
294
+ f_metrics_res[ii, :] = davis_db_eval_boundary(all_gt_masks[ii, ...], all_res_masks[ii, ...], all_void_masks)
295
+ return j_metrics_res, f_metrics_res
296
+
297
+ def evaluate(self, res_path, metric=("J", "F"), debug=False):
298
+ """Run complete DAVIS evaluation"""
299
+ metric = metric if isinstance(metric, tuple) or isinstance(metric, list) else [metric]
300
+ if "T" in metric:
301
+ raise ValueError("Temporal metric not supported!")
302
+ if "J" not in metric and "F" not in metric:
303
+ raise ValueError("Metric possible values are J for IoU or F for Boundary")
304
+
305
+ # Containers
306
+ metrics_res = {}
307
+ if "J" in metric:
308
+ metrics_res["J"] = {"M": [], "R": [], "D": [], "M_per_object": {}}
309
+ if "F" in metric:
310
+ metrics_res["F"] = {"M": [], "R": [], "D": [], "M_per_object": {}}
311
+
312
+ # Sweep all sequences
313
+ results = DAVISResults(root_dir=res_path)
314
+ for seq in tqdm(list(self.dataset.get_sequences())):
315
+ try:
316
+ all_gt_masks, all_void_masks, all_masks_id = self.dataset.get_all_masks(seq, True)
317
+ if self.task == "semi-supervised":
318
+ all_gt_masks, all_masks_id = all_gt_masks[:, 1:-1, :, :], all_masks_id[1:-1]
319
+ all_res_masks = results.read_masks(seq, all_masks_id)
320
+
321
+ if self.task == "semi-supervised":
322
+ j_metrics_res, f_metrics_res = self._evaluate_semisupervised(all_gt_masks, all_res_masks, None, metric)
323
+ else:
324
+ # For unsupervised, use semi-supervised for now (simplified)
325
+ j_metrics_res, f_metrics_res = self._evaluate_semisupervised(all_gt_masks, all_res_masks, None, metric)
326
+
327
+ if j_metrics_res is None:
328
+ continue
329
+
330
+ for ii in range(all_gt_masks.shape[0]):
331
+ seq_name = f"{seq}_{ii+1}"
332
+ if "J" in metric:
333
+ JM, JR, JD = davis_db_statistics(j_metrics_res[ii])
334
+ metrics_res["J"]["M"].append(JM)
335
+ metrics_res["J"]["R"].append(JR)
336
+ metrics_res["J"]["D"].append(JD)
337
+ metrics_res["J"]["M_per_object"][seq_name] = JM
338
+ if "F" in metric:
339
+ FM, FR, FD = davis_db_statistics(f_metrics_res[ii])
340
+ metrics_res["F"]["M"].append(FM)
341
+ metrics_res["F"]["R"].append(FR)
342
+ metrics_res["F"]["D"].append(FD)
343
+ metrics_res["F"]["M_per_object"][seq_name] = FM
344
+
345
+ # Show progress
346
+ if debug:
347
+ print(f"Evaluated sequence: {seq}")
348
+
349
+ except Exception as e:
350
+ print(f"Error evaluating sequence {seq}: {e}")
351
+ continue
352
+
353
+ return metrics_res
354
+
355
+
356
+ @torch.no_grad()
357
+ def eval_video_tracking_davis(
358
+ cfg, backbone, upsampler, transform, frame_list, video_dir, first_seg, seg_ori, color_palette, log_print
359
+ ):
360
+ """
361
+ Evaluate tracking on a video given first frame & segmentation
362
+ """
363
+ output_dir = HydraConfig.get().runtime.output_dir
364
+ os.makedirs(output_dir, exist_ok=True)
365
+
366
+ # Create output directory based on model configuration
367
+ exp_name = f"davis_vidseg_{cfg.eval.ups_factor}_{cfg.model.name}_{cfg.backbone.name}"
368
+ output_subdir = os.path.join(output_dir, exp_name)
369
+ os.makedirs(output_subdir, exist_ok=True)
370
+
371
+ video_folder = os.path.join(output_subdir, video_dir.split("/")[-1])
372
+ os.makedirs(video_folder, exist_ok=True)
373
+
374
+ # Setup device and normalization
375
+ device = "cuda" if torch.cuda.is_available() else "cpu"
376
+
377
+ # Setup normalization transforms for backbone
378
+ mean_bck = backbone.config["mean"]
379
+ std_bck = backbone.config["std"]
380
+ mean_bck_tensor = torch.tensor(mean_bck, device=device).view(1, 3, 1, 1)
381
+ std_bck_tensor = torch.tensor(std_bck, device=device).view(1, 3, 1, 1)
382
+
383
+ # Upsampler normalization (ImageNet standard)
384
+ mean_ups = (0.485, 0.456, 0.406)
385
+ std_ups = (0.229, 0.224, 0.225)
386
+ mean_ups_tensor = torch.tensor(mean_ups, device=device).view(1, 3, 1, 1)
387
+ std_ups_tensor = torch.tensor(std_ups, device=device).view(1, 3, 1, 1)
388
+
389
+ # The queue stores the n preceding frames
390
+ que = queue.Queue(cfg.eval.n_last_frames)
391
+
392
+ # first frame
393
+ frame1, ori_h, ori_w = read_frame(frame_list[0], backbone.config["ps"])
394
+
395
+ # extract first frame feature using JAFAR pipeline
396
+ frame1_feat = extract_feature(
397
+ cfg,
398
+ backbone,
399
+ upsampler,
400
+ transform,
401
+ frame1,
402
+ mean_bck_tensor,
403
+ std_bck_tensor,
404
+ mean_ups_tensor,
405
+ std_ups_tensor,
406
+ )
407
+ frame1_feat = F.interpolate(frame1_feat, size=first_seg.shape[-2:], mode="bilinear", align_corners=False)
408
+ frame1_feat = rearrange(frame1_feat, "1 c h w -> c (h w)")
409
+
410
+ # saving first segmentation
411
+ out_path = os.path.join(video_folder, "00000.png")
412
+ imwrite_indexed(out_path, seg_ori, color_palette)
413
+ mask_neighborhood = None
414
+
415
+ for cnt in tqdm(range(1, len(frame_list))):
416
+ frame_tar = read_frame(frame_list[cnt], backbone.config["ps"])[0]
417
+
418
+ # we use the first segmentation and the n previous ones
419
+ used_frame_feats = [frame1_feat] + [pair[0] for pair in list(que.queue)]
420
+ used_segs = [first_seg] + [pair[1] for pair in list(que.queue)]
421
+ frame_tar_avg, feat_tar, mask_neighborhood = label_propagation(
422
+ cfg,
423
+ backbone,
424
+ upsampler,
425
+ transform,
426
+ frame_tar,
427
+ used_frame_feats,
428
+ used_segs,
429
+ mask_neighborhood,
430
+ mean_bck_tensor,
431
+ std_bck_tensor,
432
+ mean_ups_tensor,
433
+ std_ups_tensor,
434
+ )
435
+
436
+ # pop out oldest frame if necessary
437
+ if que.qsize() == cfg.eval.n_last_frames:
438
+ que.get()
439
+ # push current results into queue
440
+ seg = copy.deepcopy(frame_tar_avg)
441
+ que.put([feat_tar, seg])
442
+
443
+ # upsampling & argmax
444
+ frame_tar_avg = F.interpolate(
445
+ frame_tar_avg,
446
+ size=[v * backbone.config["ps"] // cfg.eval.ups_factor for v in frame_tar_avg.shape[-2:]],
447
+ mode="bilinear",
448
+ align_corners=False,
449
+ recompute_scale_factor=False,
450
+ )[0]
451
+ frame_tar_avg = frame_tar_avg.squeeze(0)
452
+ frame_tar_avg = norm_mask(frame_tar_avg)
453
+ _, frame_tar_seg = torch.max(frame_tar_avg, dim=0)
454
+
455
+ # saving to disk
456
+ frame_tar_seg = np.array(frame_tar_seg.squeeze().cpu(), dtype=np.uint8)
457
+ frame_tar_seg = np.array(Image.fromarray(frame_tar_seg).resize((ori_w, ori_h), 0))
458
+ frame_nm = frame_list[cnt].split("/")[-1].replace(".jpg", ".png")
459
+ imwrite_indexed(os.path.join(video_folder, frame_nm), frame_tar_seg, color_palette)
460
+
461
+
462
+ def restrict_neighborhood(cfg, h, w):
463
+ """
464
+ Ultra-fast approach using meshgrid and advanced indexing
465
+ """
466
+ size_mask = cfg.eval.size_mask_neighborhood
467
+
468
+ # Create coordinate meshgrids
469
+ query_i, query_j = torch.meshgrid(torch.arange(h), torch.arange(w), indexing="ij")
470
+ source_i, source_j = torch.meshgrid(torch.arange(h), torch.arange(w), indexing="ij")
471
+
472
+ # Flatten coordinates
473
+ query_i_flat = query_i.flatten().unsqueeze(1) # (h*w, 1)
474
+ query_j_flat = query_j.flatten().unsqueeze(1) # (h*w, 1)
475
+ source_i_flat = source_i.flatten().unsqueeze(0) # (1, h*w)
476
+ source_j_flat = source_j.flatten().unsqueeze(0) # (1, h*w)
477
+
478
+ # Calculate distances using broadcasting
479
+ i_dist = torch.abs(query_i_flat - source_i_flat) # (h*w, h*w)
480
+ j_dist = torch.abs(query_j_flat - source_j_flat) # (h*w, h*w)
481
+
482
+ # Create mask
483
+ mask = ((i_dist <= size_mask) & (j_dist <= size_mask)).float()
484
+
485
+ return mask.cuda(non_blocking=True)
486
+
487
+
488
+ def norm_mask(mask):
489
+ c, h, w = mask.size()
490
+ for cnt in range(c):
491
+ mask_cnt = mask[cnt, :, :]
492
+ if mask_cnt.max() > 0:
493
+ mask_cnt = mask_cnt - mask_cnt.min()
494
+ mask_cnt = mask_cnt / mask_cnt.max()
495
+ mask[cnt, :, :] = mask_cnt
496
+ return mask
497
+
498
+
499
+ def label_propagation(
500
+ cfg,
501
+ backbone,
502
+ upsampler,
503
+ transform,
504
+ frame_tar,
505
+ list_frame_feats,
506
+ list_segs,
507
+ mask_neighborhood,
508
+ mean_bck_tensor,
509
+ std_bck_tensor,
510
+ mean_ups_tensor,
511
+ std_ups_tensor,
512
+ ):
513
+ """
514
+ propagate segs of frames in list_frames to frame_tar
515
+ """
516
+ list_segs = [s.cuda() for s in list_segs]
517
+ segs = torch.cat(list_segs)
518
+ nmb_context, C, h, w = segs.shape
519
+
520
+ ## we only need to extract feature of the target frame
521
+ feat_tar = extract_feature(
522
+ cfg,
523
+ backbone,
524
+ upsampler,
525
+ transform,
526
+ frame_tar,
527
+ mean_bck_tensor,
528
+ std_bck_tensor,
529
+ mean_ups_tensor,
530
+ std_ups_tensor,
531
+ )
532
+ feat_tar = F.interpolate(feat_tar, size=(h, w), mode="bilinear", align_corners=False)
533
+ feat_tar = rearrange(feat_tar, "1 c h w -> (h w) c")
534
+ return_feat_tar = feat_tar.T # dim x h*w
535
+
536
+ ncontext = len(list_frame_feats)
537
+ feat_sources = torch.stack(list_frame_feats) # nmb_context x dim x h*w
538
+
539
+ feat_tar = F.normalize(feat_tar, dim=1, p=2)
540
+ feat_sources = F.normalize(feat_sources, dim=1, p=2)
541
+
542
+ feat_tar = feat_tar.unsqueeze(0).repeat(ncontext, 1, 1)
543
+ aff = torch.exp(torch.bmm(feat_tar, feat_sources) / 0.1) # nmb_context x h*w (tar: query) x h*w (source: keys)
544
+
545
+ if cfg.eval.size_mask_neighborhood > 0:
546
+ if mask_neighborhood is None:
547
+ mask_neighborhood = restrict_neighborhood(cfg, h, w)
548
+ mask_neighborhood = mask_neighborhood.unsqueeze(0).repeat(ncontext, 1, 1)
549
+ aff *= mask_neighborhood
550
+
551
+ aff = aff.transpose(2, 1).reshape(-1, h * w) # nmb_context*h*w (source: keys) x h*w (tar: queries)
552
+ tk_val, _ = torch.topk(aff, dim=0, k=cfg.eval.topk)
553
+ tk_val_min, _ = torch.min(tk_val, dim=0)
554
+ aff[aff < tk_val_min] = 0
555
+
556
+ aff = aff / torch.sum(aff, keepdim=True, axis=0)
557
+
558
+ segs = segs.reshape(nmb_context, C, -1).transpose(2, 1).reshape(-1, C).T # C x nmb_context*h*w
559
+ seg_tar = torch.mm(segs, aff)
560
+ seg_tar = seg_tar.reshape(1, C, h, w)
561
+ return seg_tar, return_feat_tar, mask_neighborhood
562
+
563
+
564
+ @torch.no_grad()
565
+ def extract_feature(
566
+ cfg,
567
+ backbone,
568
+ upsampler,
569
+ transform,
570
+ frame,
571
+ mean_bck_tensor,
572
+ std_bck_tensor,
573
+ mean_ups_tensor,
574
+ std_ups_tensor,
575
+ ):
576
+ """Extract one frame feature using JAFAR pipeline."""
577
+ # Transform frame and move to device
578
+ frame_tensor = transform(frame).unsqueeze(0).cuda()
579
+ frame_tensor = F.interpolate(
580
+ frame_tensor,
581
+ size=[v // backbone.config["ps"] * backbone.config["ps"] for v in frame_tensor.shape[-2:]],
582
+ mode="bilinear",
583
+ align_corners=False,
584
+ )
585
+
586
+ # Prepare image for backbone (normalize)
587
+ img_bck = (frame_tensor - mean_bck_tensor) / std_bck_tensor
588
+ lr_feats = backbone(img_bck)
589
+
590
+ # Upsample features
591
+ img_ups = (frame_tensor - mean_ups_tensor) / std_ups_tensor
592
+
593
+ # Resize input for upsampler to match target dimensions
594
+ hr_size = [v * cfg.eval.ups_factor for v in lr_feats.shape[-2:]]
595
+ img_ups = F.interpolate(img_ups, size=hr_size, mode="bicubic", align_corners=False)
596
+
597
+ hr_feats = upsampler(img_ups, lr_feats, hr_size)
598
+ return hr_feats
599
+
600
+
601
+ def imwrite_indexed(filename, array, color_palette):
602
+ """Save indexed png for DAVIS."""
603
+ if np.atleast_3d(array).shape[2] != 1:
604
+ raise Exception("Saving indexed PNGs requires 2D array.")
605
+
606
+ im = Image.fromarray(array)
607
+ im.putpalette(color_palette.ravel())
608
+ im.save(filename, format="PNG")
609
+
610
+
611
+ def to_one_hot(y_tensor, n_dims=None):
612
+ """
613
+ Take integer y (tensor or variable) with n dims &
614
+ convert it to 1-hot representation with n+1 dims.
615
+ """
616
+ if n_dims is None:
617
+ n_dims = int(y_tensor.max() + 1)
618
+ _, h, w = y_tensor.size()
619
+ y_tensor = y_tensor.type(torch.LongTensor).view(-1, 1)
620
+ n_dims = n_dims if n_dims is not None else int(torch.max(y_tensor)) + 1
621
+ y_one_hot = torch.zeros(y_tensor.size()[0], n_dims).scatter_(1, y_tensor, 1)
622
+ y_one_hot = y_one_hot.view(h, w, n_dims)
623
+ return y_one_hot.permute(2, 0, 1).unsqueeze(0)
624
+
625
+
626
+ def read_frame_list(video_dir):
627
+ frame_list = [img for img in glob.glob(os.path.join(video_dir, "*.jpg"))]
628
+ frame_list = sorted(frame_list)
629
+ return frame_list
630
+
631
+
632
+ def read_frame(frame_dir, ps=None):
633
+ img = Image.open(frame_dir)
634
+ ori_w, ori_h = img.size
635
+ return img, ori_h, ori_w
636
+
637
+
638
+ def read_seg(seg_dir, ps, factor):
639
+ seg = Image.open(seg_dir)
640
+ _w, _h = seg.size # note PIL.Image.Image's size is (w, h)
641
+ small_seg = np.array(seg.resize((_w // ps * factor, _h // ps * factor), 0))
642
+ small_seg = torch.from_numpy(small_seg.copy()).contiguous().float().unsqueeze(0)
643
+ return to_one_hot(small_seg), np.asarray(seg)
644
+
645
+
646
+ def run_video_segmentation(cfg, backbone, upsampler, log_print):
647
+ device = "cuda" if torch.cuda.is_available() else "cpu"
648
+
649
+ transform = transforms.Compose(
650
+ [
651
+ transforms.ToTensor(),
652
+ ]
653
+ )
654
+
655
+ color_palette = []
656
+ for line in urlopen("https://raw.githubusercontent.com/Liusifei/UVC/master/libs/data/palette.txt"):
657
+ color_palette.append([int(i) for i in line.decode("utf-8").split("\n")[0].split(" ")])
658
+ color_palette = np.asarray(color_palette, dtype=np.uint8).reshape(-1, 3)
659
+
660
+ video_list = open(os.path.join(cfg.dataset.dataroot, "ImageSets/2017/val.txt")).readlines()
661
+
662
+ start_time = time.time()
663
+
664
+ for i, video_name in enumerate(video_list):
665
+ video_name = video_name.strip()
666
+ log_print(f"[{i}/{len(video_list)}] Begin to segmentate video {video_name}.")
667
+
668
+ video_dir = os.path.join(cfg.dataset.dataroot, "JPEGImages/480p/", video_name)
669
+ frame_list = read_frame_list(video_dir)
670
+ seg_path = frame_list[0].replace("JPEGImages", "Annotations").replace("jpg", "png")
671
+
672
+ # Read segmentation with appropriate scaling
673
+ first_seg, seg_ori = read_seg(seg_path, backbone.config["ps"], cfg.eval.ups_factor)
674
+
675
+ eval_video_tracking_davis(
676
+ cfg, backbone, upsampler, transform, frame_list, video_dir, first_seg, seg_ori, color_palette, log_print
677
+ )
678
+
679
+ end_time = time.time()
680
+ log_print(f"[bold blue]Total time taken: {(end_time - start_time) / 60:.2f} minutes[/bold blue]")
681
+ log_print(f"[bold blue]Max GPU memory used: {torch.cuda.max_memory_allocated() / 1e9:.2f} GB[/bold blue]")
682
+
683
+ # Run comprehensive DAVIS evaluation
684
+ log_print("[bold cyan]Running comprehensive DAVIS eval...[/bold cyan]")
685
+ run_davis_evaluation(cfg, log_print)
686
+
687
+
688
+ def run_davis_evaluation(cfg, log_print):
689
+ """Run comprehensive DAVIS evaluation using the integrated evaluation method"""
690
+ output_dir = HydraConfig.get().runtime.output_dir
691
+ exp_name = f"davis_vidseg_{cfg.eval.ups_factor}_{cfg.model.name}_{cfg.backbone.name}"
692
+ results_path = os.path.join(output_dir, exp_name)
693
+
694
+ if not os.path.exists(results_path):
695
+ log_print(f"[bold red]Results path does not exist: {results_path}[/bold red]")
696
+ return
697
+
698
+ # Check if results already exist
699
+ csv_name_global = "global_results-val.csv"
700
+ csv_name_per_sequence = "per-sequence_results-val.csv"
701
+ csv_name_global_path = os.path.join(results_path, csv_name_global)
702
+ csv_name_per_sequence_path = os.path.join(results_path, csv_name_per_sequence)
703
+
704
+ if os.path.exists(csv_name_global_path) and os.path.exists(csv_name_per_sequence_path):
705
+ log_print("[bold yellow]Using precomputed DAVIS results...[/bold yellow]")
706
+ table_g = pd.read_csv(csv_name_global_path)
707
+ table_seq = pd.read_csv(csv_name_per_sequence_path)
708
+ else:
709
+ log_print("[bold cyan]Computing DAVIS evaluation metrics...[/bold cyan]")
710
+
711
+ # Create dataset and evaluate
712
+ dataset_eval = DAVISEvaluation(davis_root=cfg.dataset.dataroot, task="semi-supervised", gt_set="val")
713
+ metrics_res = dataset_eval.evaluate(results_path)
714
+
715
+ if not metrics_res or "J" not in metrics_res or "F" not in metrics_res:
716
+ log_print("[bold red]DAVIS evaluation failed![/bold red]")
717
+ return
718
+
719
+ J, F = metrics_res["J"], metrics_res["F"]
720
+
721
+ # Generate dataframe for the general results
722
+ g_measures = ["J&F-Mean", "J-Mean", "J-Recall", "J-Decay", "F-Mean", "F-Recall", "F-Decay"]
723
+ final_mean = (np.mean(J["M"]) + np.mean(F["M"])) / 2.0
724
+ g_res = np.array(
725
+ [final_mean, np.mean(J["M"]), np.mean(J["R"]), np.mean(J["D"]), np.mean(F["M"]), np.mean(F["R"]), np.mean(F["D"])]
726
+ )
727
+ g_res = np.reshape(g_res, [1, len(g_res)])
728
+ table_g = pd.DataFrame(data=g_res, columns=g_measures)
729
+
730
+ with open(csv_name_global_path, "w") as f:
731
+ table_g.to_csv(f, index=False, float_format="%.3f")
732
+ log_print(f"[bold green]Global results saved in {csv_name_global_path}[/bold green]")
733
+
734
+ # Generate a dataframe for the per sequence results
735
+ seq_names = list(J["M_per_object"].keys())
736
+ seq_measures = ["Sequence", "J-Mean", "F-Mean"]
737
+ J_per_object = [J["M_per_object"][x] for x in seq_names]
738
+ F_per_object = [F["M_per_object"][x] for x in seq_names]
739
+ table_seq = pd.DataFrame(data=list(zip(seq_names, J_per_object, F_per_object)), columns=seq_measures)
740
+
741
+ with open(csv_name_per_sequence_path, "w") as f:
742
+ table_seq.to_csv(f, index=False, float_format="%.3f")
743
+ log_print(f"[bold green]Per-sequence results saved in {csv_name_per_sequence_path}[/bold green]")
744
+
745
+ # Print the results
746
+ log_print("[bold blue]--------------------------- Global DAVIS Results ---------------------------[/bold blue]")
747
+ log_print(table_g.to_string(index=False))
748
+ log_print("[bold blue]---------- Per-sequence DAVIS Results ----------[/bold blue]")
749
+ log_print(table_seq.to_string(index=False))
750
+
751
+ # Also save as JSON for compatibility
752
+ metrics_dict = table_g.iloc[0].to_dict()
753
+ import json
754
+
755
+ with open(os.path.join(output_dir, "davis_metrics.json"), "w") as f:
756
+ json.dump(metrics_dict, f, indent=2)
757
+ log_print(f"[bold cyan]DAVIS metrics saved to: {os.path.join(output_dir, 'davis_metrics.json')}[/bold cyan]")
758
+
759
+
760
+ @hydra.main(config_path="../config", config_name="eval_video_seg", version_base=None)
761
+ def main(cfg: DictConfig):
762
+ # Setup dual console logging (terminal and file)
763
+ terminal_console = Console()
764
+ current_run_dir = HydraConfig.get().runtime.output_dir
765
+ log_file_path = os.path.join(current_run_dir, "eval_davis.log")
766
+ file_console = Console(file=open(log_file_path, "w"))
767
+
768
+ def log_print(*args, **kwargs):
769
+ """Log to both terminal and file with immediate flushing"""
770
+ terminal_console.print(*args, **kwargs)
771
+ file_console.print(*args, **kwargs)
772
+ file_console.file.flush()
773
+
774
+ # Start logging
775
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
776
+ log_print(f"\n[bold blue]{'='*50}[/bold blue]")
777
+ log_print(f"[bold blue]Starting DAVIS Video Segmentation Evaluation at {timestamp}[/bold blue]")
778
+ log_print(f"[bold green]Configuration:[/bold green]")
779
+ log_print(OmegaConf.to_yaml(cfg))
780
+
781
+ device = "cuda"
782
+ log_print(f"[bold yellow]Using device: {device}[/bold yellow]")
783
+
784
+ # Load backbone and upsampler using Hydra configuration
785
+ backbone_configs = [{"name": cfg.backbone.name}]
786
+ backbones = load_multiple_backbones(cfg, backbone_configs, device="cuda")[0]
787
+ backbone = backbones[0].eval().cuda()
788
+ upsampler = instantiate(cfg.model).eval().cuda()
789
+
790
+ # Load model checkpoint if provided
791
+ if cfg.eval.model_ckpt:
792
+ checkpoint = torch.load(cfg.eval.model_ckpt, map_location=device, weights_only=False)
793
+ if cfg.model.name == "featup":
794
+ new_ckpts = {
795
+ k.replace("model.1.", "norm."): v
796
+ for k, v in checkpoint["state_dict"].items()
797
+ if "upsampler" in k or "model.1.norm" in k
798
+ }
799
+ upsampler.load_state_dict(new_ckpts, strict=True)
800
+ else:
801
+ upsampler.load_state_dict(checkpoint, strict=True)
802
+ log_print(f"[green]Loaded model from checkpoint: {cfg.eval.model_ckpt}[/green]")
803
+ else:
804
+ log_print(f"[yellow]No model checkpoint provided, using untrained model[/yellow]")
805
+
806
+ run_video_segmentation(cfg, backbone, upsampler, log_print)
807
+
808
+ # Log completion before closing file
809
+ log_print(f"[bold blue]Evaluation completed. Log saved to: {log_file_path}[/bold blue]")
810
+
811
+ # Close file console
812
+ file_console.file.close()
813
+
814
+
815
+ if __name__ == "__main__":
816
+ main()
Pixal3D/torch_hub/hub/valeoai_NAF_main/hydra_plugins/resolvers.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ from omegaconf import OmegaConf
4
+
5
+
6
+ def get_feature(target: str) -> int:
7
+ """Resolve feature dimensions from backbone name patterns"""
8
+ model_name = target.lower()
9
+
10
+ if "vits" in model_name or "small" in model_name:
11
+ return 384
12
+ elif "vitb" in model_name or "base" in model_name or model_name == "radio_v2.5-b":
13
+ return 768
14
+ elif "vitl" in model_name or "large" in model_name or model_name == "radio_v2.5-l":
15
+ return 1024
16
+ elif "tiny" in model_name:
17
+ return 192
18
+ else:
19
+ print(f"Warning: get_feature() - Unsupported backbone: {model_name}. Returning default 0.")
20
+ return 0
21
+
22
+
23
+ def get_patch_size(target: str) -> int:
24
+ """Resolve patch size from backbone name patterns using regex"""
25
+ model_name = target.lower()
26
+
27
+ # Return a default value for consistency
28
+ if "franca" in model_name:
29
+ return 14
30
+
31
+ # Use regex to find patchXX pattern
32
+ match = re.search(r"patch(\d+)", model_name)
33
+ if match:
34
+ return int(match.group(1))
35
+
36
+ # Default to 16 if no patch size found
37
+ return 16
38
+
39
+
40
+ OmegaConf.register_new_resolver("get_feature", lambda name: get_feature(name))
41
+ OmegaConf.register_new_resolver("get_patch_size", lambda name: get_patch_size(name))
Pixal3D/torch_hub/hub/valeoai_NAF_main/notebooks/attention_maps.ipynb ADDED
@@ -0,0 +1,389 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "fa359c75",
6
+ "metadata": {},
7
+ "source": [
8
+ "# NAF: Neighborhood Attention Map\n",
9
+ "\n",
10
+ "This notebook provides tools to inspect and visualize attention weights from the NAF model.\n",
11
+ "\n",
12
+ "**Features:**\n",
13
+ "- Load pre-trained NAF model and test images\n",
14
+ "- Select query points on the image\n",
15
+ "- Extract and visualize attention weights for the selected position\n",
16
+ "- Compare attention patterns across different image regions"
17
+ ]
18
+ },
19
+ {
20
+ "cell_type": "markdown",
21
+ "id": "13d519e9",
22
+ "metadata": {},
23
+ "source": [
24
+ "## Setup and Imports"
25
+ ]
26
+ },
27
+ {
28
+ "cell_type": "code",
29
+ "execution_count": null,
30
+ "id": "fdfa00f8",
31
+ "metadata": {},
32
+ "outputs": [],
33
+ "source": [
34
+ "# Use inline matplotlib for better compatibility\n",
35
+ "%matplotlib widget"
36
+ ]
37
+ },
38
+ {
39
+ "cell_type": "code",
40
+ "execution_count": null,
41
+ "id": "c5aaceb7",
42
+ "metadata": {},
43
+ "outputs": [],
44
+ "source": [
45
+ "import sys\n",
46
+ "sys.path.append('..')\n",
47
+ "\n",
48
+ "import torch\n",
49
+ "import numpy as np\n",
50
+ "import matplotlib.pyplot as plt\n",
51
+ "import matplotlib.patches as patches\n",
52
+ "from PIL import Image\n",
53
+ "import torchvision.transforms as T\n",
54
+ "\n",
55
+ "from hydra import compose, initialize\n",
56
+ "from hydra.core.global_hydra import GlobalHydra\n",
57
+ "from hydra.utils import instantiate\n",
58
+ "from IPython.display import clear_output\n",
59
+ "\n",
60
+ "from src.model.naf import NAF\n",
61
+ "from utils.training import load_multiple_backbones\n",
62
+ "\n",
63
+ "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
64
+ "print(f\"Using device: {device}\")"
65
+ ]
66
+ },
67
+ {
68
+ "cell_type": "markdown",
69
+ "id": "2417fe5b",
70
+ "metadata": {},
71
+ "source": [
72
+ "## 2. Load Model and Configuration"
73
+ ]
74
+ },
75
+ {
76
+ "cell_type": "code",
77
+ "execution_count": null,
78
+ "id": "5139fa03",
79
+ "metadata": {},
80
+ "outputs": [],
81
+ "source": [
82
+ "# Initialize Hydra config\n",
83
+ "if not GlobalHydra.instance().is_initialized():\n",
84
+ " initialize(config_path=\"../config\", version_base=None)\n",
85
+ "\n",
86
+ "cfg = compose(config_name=\"base\", overrides=[\"model=naf\", \"img_size=1024\"])\n",
87
+ "\n",
88
+ "# Initialize NAF model\n",
89
+ "model = torch.hub.load(\"valeoai/NAF\", \"naf\", pretrained=True, device=device)\n",
90
+ "model.eval()\n",
91
+ "\n",
92
+ "# Load backbone\n",
93
+ "backbone_configs = [{\"name\": \"vit_base_patch16_dinov3.lvd1689m\"}]\n",
94
+ "backbones, names, _ = load_multiple_backbones(cfg, backbone_configs, device)\n",
95
+ "backbone = backbones[0]\n",
96
+ "backbone.eval()\n",
97
+ "\n",
98
+ "print(f\"Model loaded: {type(model).__name__}\")\n",
99
+ "print(f\"Backbone loaded: {names[0]}\")\n",
100
+ "print(f\"Kernel size: {model.upsampler.kernel_size}\")"
101
+ ]
102
+ },
103
+ {
104
+ "cell_type": "markdown",
105
+ "id": "b7ae72e4",
106
+ "metadata": {},
107
+ "source": [
108
+ "## 3. Load and Preprocess Image"
109
+ ]
110
+ },
111
+ {
112
+ "cell_type": "code",
113
+ "execution_count": null,
114
+ "id": "ae219175",
115
+ "metadata": {},
116
+ "outputs": [],
117
+ "source": [
118
+ "# Load image\n",
119
+ "image_path = \"../asset/dog0.jpg\"\n",
120
+ "pil_image = Image.open(image_path).convert(\"RGB\")\n",
121
+ "\n",
122
+ "# Resize to model input size\n",
123
+ "H, W = cfg.img_size, cfg.img_size\n",
124
+ "transform = T.Compose([T.Resize((H, W)), T.ToTensor()])\n",
125
+ "image = transform(pil_image).unsqueeze(0).to(device)\n",
126
+ "\n",
127
+ "# Normalize for backbone\n",
128
+ "mean_bck = backbone.config[\"mean\"]\n",
129
+ "std_bck = backbone.config[\"std\"]\n",
130
+ "normalize_bck = T.Normalize(mean=mean_bck, std=std_bck)\n",
131
+ "image_bck = normalize_bck(image)\n",
132
+ "\n",
133
+ "# Normalize for upsampler (ImageNet normalization)\n",
134
+ "normalize_ups = T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n",
135
+ "image_ups = normalize_ups(image)\n",
136
+ "\n",
137
+ "# Convert to display format\n",
138
+ "image_vis = (image.clone().cpu().squeeze(0).permute(1, 2, 0).numpy() * 255).astype(np.uint8)\n",
139
+ "\n",
140
+ "print(f\"Loaded image from: {image_path}\")\n",
141
+ "print(f\"Image size: {image.shape}\")"
142
+ ]
143
+ },
144
+ {
145
+ "cell_type": "markdown",
146
+ "id": "0457092d",
147
+ "metadata": {},
148
+ "source": [
149
+ "## 4. Extract Backbone Features"
150
+ ]
151
+ },
152
+ {
153
+ "cell_type": "code",
154
+ "execution_count": null,
155
+ "id": "784c862e",
156
+ "metadata": {},
157
+ "outputs": [],
158
+ "source": [
159
+ "# Extract features from backbone\n",
160
+ "with torch.no_grad():\n",
161
+ " hr_feats = backbone(image_bck)\n",
162
+ "\n",
163
+ "# Define output size for encoder (match backbone output)\n",
164
+ "output_size = (hr_feats.shape[2], hr_feats.shape[3])\n",
165
+ "\n",
166
+ "# Compute scaling factors between image and feature space\n",
167
+ "scale_h = output_size[0] / H\n",
168
+ "scale_w = output_size[1] / W\n",
169
+ "\n",
170
+ "print(f\"Backbone features shape: {hr_feats.shape}\")\n",
171
+ "print(f\"Feature map size: {output_size}\")\n",
172
+ "print(f\"Scaling factors - h: {scale_h:.3f}, w: {scale_w:.3f}\")"
173
+ ]
174
+ },
175
+ {
176
+ "cell_type": "markdown",
177
+ "id": "d4a80c95",
178
+ "metadata": {},
179
+ "source": [
180
+ "## 5. Helper Functions for Query Position and Attention Extraction"
181
+ ]
182
+ },
183
+ {
184
+ "cell_type": "code",
185
+ "execution_count": null,
186
+ "id": "a62a3b4a",
187
+ "metadata": {},
188
+ "outputs": [],
189
+ "source": [
190
+ "def extract_attention_at_position(query_img_h, query_img_w):\n",
191
+ " \"\"\"\n",
192
+ " Extract attention weights for a query position.\n",
193
+ " \n",
194
+ " Args:\n",
195
+ " query_img_h: Query position height in image coordinates (0-447)\n",
196
+ " query_img_w: Query position width in image coordinates (0-447)\n",
197
+ " \n",
198
+ " Returns:\n",
199
+ " attn_map: 2D attention map of shape (kernel_h, kernel_w)\n",
200
+ " pos_feat: Query position in feature space\n",
201
+ " \"\"\"\n",
202
+ " # Convert image coordinates to feature space\n",
203
+ " pos_feat = (int(query_img_h * scale_h), int(query_img_w * scale_w))\n",
204
+ " \n",
205
+ " # Extract attention weights\n",
206
+ " with torch.no_grad():\n",
207
+ " # Get RoPE-transformed features (queries)\n",
208
+ " _, attn_weights = model(image_ups, hr_feats, output_size=output_size, return_weights=True)\n",
209
+ " \n",
210
+ " # Extract attention for the query position\n",
211
+ " # attn_weights: [B, num_heads, H, W, kernel_h * kernel_w]\n",
212
+ " query_h, query_w = pos_feat\n",
213
+ " attn_at_query = attn_weights[0, :, query_h, query_w, :] # [num_heads, kernel_h*kernel_w]\n",
214
+ " \n",
215
+ " # Average across heads\n",
216
+ " attn_mean = attn_at_query.mean(dim=0) # [kernel_h*kernel_w]\n",
217
+ " \n",
218
+ " # Reshape to 2D attention map\n",
219
+ " kernel_h, kernel_w = model.upsampler.kernel_size\n",
220
+ " attn_map = attn_mean.view(kernel_h, kernel_w).cpu().numpy()\n",
221
+ " \n",
222
+ " return attn_map, pos_feat\n",
223
+ "\n",
224
+ "\n",
225
+ "print(\"Helper functions loaded\")"
226
+ ]
227
+ },
228
+ {
229
+ "cell_type": "markdown",
230
+ "id": "f289e60c",
231
+ "metadata": {},
232
+ "source": [
233
+ "## 6. Visualize Attention for a Single Query Point"
234
+ ]
235
+ },
236
+ {
237
+ "cell_type": "code",
238
+ "execution_count": null,
239
+ "id": "ccd18126",
240
+ "metadata": {},
241
+ "outputs": [],
242
+ "source": [
243
+ "# Interactive attention visualization\n",
244
+ "fig, axes = plt.subplots(1, 3, figsize=(12*2/3, 4*2/3))\n",
245
+ "\n",
246
+ "# Initialize with default position\n",
247
+ "query_img_h, query_img_w = 650, 270\n",
248
+ "\n",
249
+ "# Plot elements that will be updated\n",
250
+ "img_display = axes[0].imshow(image_vis)\n",
251
+ "query_point, = axes[0].plot([], [], 'r*', markersize=15, markeredgecolor='white', markeredgewidth=1)\n",
252
+ "rect_patch = patches.Rectangle((0, 0), 0, 0, linewidth=2, edgecolor='red', facecolor='none', linestyle='--')\n",
253
+ "axes[0].add_patch(rect_patch)\n",
254
+ "\n",
255
+ "axes[0].set_xlabel('Image Width')\n",
256
+ "axes[0].set_ylabel('Image Height')\n",
257
+ "axes[0].set_title('Click on image to select query point')\n",
258
+ "axes[0].set_aspect('equal')\n",
259
+ "\n",
260
+ "# Attention heatmap (will be updated)\n",
261
+ "attn_display = axes[1].imshow(np.zeros((5, 5)), cmap='RdBu_r', aspect='equal')\n",
262
+ "axes[1].set_xlabel('Kernel Width')\n",
263
+ "axes[1].set_ylabel('Kernel Height')\n",
264
+ "axes[1].set_title('Attention Map')\n",
265
+ "\n",
266
+ "cbar1 = plt.colorbar(attn_display, ax=axes[1], fraction=0.046, pad=0.04)\n",
267
+ "cbar1.set_label('Attention Weight')\n",
268
+ "\n",
269
+ "# Overlay visualization\n",
270
+ "overlay_display = axes[2].imshow(np.zeros((10, 10, 3), dtype=np.uint8))\n",
271
+ "axes[2].set_xlabel('Width')\n",
272
+ "axes[2].set_ylabel('Height')\n",
273
+ "axes[2].set_title('Attention Overlay on Neighborhood')\n",
274
+ "axes[2].set_aspect('equal')\n",
275
+ "\n",
276
+ "# Statistics text\n",
277
+ "stats_text = axes[1].text(0.02, 0.98, '', transform=axes[1].transAxes, \n",
278
+ " verticalalignment='top', fontsize=8, \n",
279
+ " bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))\n",
280
+ "\n",
281
+ "def update_attention(query_h, query_w):\n",
282
+ " \"\"\"Update the attention visualization for a new query position.\"\"\"\n",
283
+ " # Extract attention map\n",
284
+ " attn_map, pos_feat = extract_attention_at_position(query_h, query_w)\n",
285
+ " kernel_h, kernel_w = model.upsampler.kernel_size\n",
286
+ " \n",
287
+ " # Update query point\n",
288
+ " query_point.set_data([query_w], [query_h])\n",
289
+ " \n",
290
+ " # Update neighborhood rectangle\n",
291
+ " half_h, half_w = kernel_h // 2, kernel_w // 2\n",
292
+ " box_x0 = max(0, query_w - int(half_w / scale_w))\n",
293
+ " box_y0 = max(0, query_h - int(half_h / scale_h))\n",
294
+ " box_x1 = min(W, query_w + int(half_w / scale_w))\n",
295
+ " box_y1 = min(H, query_h + int(half_h / scale_h))\n",
296
+ " \n",
297
+ " rect_patch.set_xy((box_x0, box_y0))\n",
298
+ " rect_patch.set_width(box_x1 - box_x0)\n",
299
+ " rect_patch.set_height(box_y1 - box_y0)\n",
300
+ " \n",
301
+ " # Update attention heatmap\n",
302
+ " attn_display.set_data(attn_map)\n",
303
+ " attn_display.set_clim(vmin=attn_map.min(), vmax=attn_map.max())\n",
304
+ " axes[1].set_title(f'Attention Map ({kernel_h}×{kernel_w})')\n",
305
+ " \n",
306
+ " # Create overlay visualization\n",
307
+ " # Extract neighborhood from image\n",
308
+ " neighborhood = image_vis[box_y0:box_y1, box_x0:box_x1, :]\n",
309
+ " \n",
310
+ " # Resize attention map to match neighborhood size\n",
311
+ " from scipy.ndimage import zoom\n",
312
+ " attn_resized = zoom(attn_map, (neighborhood.shape[0] / attn_map.shape[0], \n",
313
+ " neighborhood.shape[1] / attn_map.shape[1]), order=1)\n",
314
+ " \n",
315
+ " # Normalize attention to 0-1 for overlay\n",
316
+ " attn_norm = (attn_resized - attn_resized.min()) / (attn_resized.max() - attn_resized.min() + 1e-8)\n",
317
+ " \n",
318
+ " # Create overlay: blend image with attention heatmap\n",
319
+ " overlay = neighborhood.copy().astype(float)\n",
320
+ " \n",
321
+ " # Apply colormap to attention (red-blue)\n",
322
+ " import matplotlib.cm as cm\n",
323
+ " attn_colored = cm.RdBu_r(attn_norm)[:, :, :3] * 255 # RGB only\n",
324
+ " \n",
325
+ " # Blend: 50% image, 50% attention heatmap\n",
326
+ " overlay_blended = (0.5 * overlay + 0.5 * attn_colored).astype(np.uint8)\n",
327
+ " \n",
328
+ " # Update overlay display\n",
329
+ " overlay_display.set_data(overlay_blended)\n",
330
+ " overlay_display.set_extent([box_x0, box_x1, box_y1, box_y0])\n",
331
+ " axes[2].set_xlim(box_x0, box_x1)\n",
332
+ " axes[2].set_ylim(box_y1, box_y0)\n",
333
+ " axes[2].set_title('Attention Overlay on Neighborhood')\n",
334
+ " \n",
335
+ " # Update statistics text\n",
336
+ " fig.canvas.draw_idle()\n",
337
+ " \n",
338
+ " # Print to console\n",
339
+ " print(f\"Query: ({query_h}, {query_w}) | Feature: {pos_feat} | Center attn: {attn_map[kernel_h//2, kernel_w//2]:.4f}\")\n",
340
+ "\n",
341
+ "def onclick(event):\n",
342
+ " \"\"\"Handle click events on the image.\"\"\"\n",
343
+ " if event.inaxes == axes[0] and event.xdata is not None and event.ydata is not None:\n",
344
+ " new_w = int(np.clip(event.xdata, 0, W-1))\n",
345
+ " new_h = int(np.clip(event.ydata, 0, H-1))\n",
346
+ " update_attention(new_h, new_w)\n",
347
+ "\n",
348
+ "fig.canvas.mpl_connect('button_press_event', onclick)\n",
349
+ "\n",
350
+ "plt.tight_layout()\n",
351
+ "\n",
352
+ "# Initial visualization\n",
353
+ "update_attention(query_img_h, query_img_w)\n",
354
+ "plt.show()\n",
355
+ "\n",
356
+ "print(\"Click on the left image to explore attention patterns!\")"
357
+ ]
358
+ },
359
+ {
360
+ "cell_type": "code",
361
+ "execution_count": null,
362
+ "id": "04e96127",
363
+ "metadata": {},
364
+ "outputs": [],
365
+ "source": []
366
+ }
367
+ ],
368
+ "metadata": {
369
+ "kernelspec": {
370
+ "display_name": "naf",
371
+ "language": "python",
372
+ "name": "python3"
373
+ },
374
+ "language_info": {
375
+ "codemirror_mode": {
376
+ "name": "ipython",
377
+ "version": 3
378
+ },
379
+ "file_extension": ".py",
380
+ "mimetype": "text/x-python",
381
+ "name": "python",
382
+ "nbconvert_exporter": "python",
383
+ "pygments_lexer": "ipython3",
384
+ "version": "3.10.14"
385
+ }
386
+ },
387
+ "nbformat": 4,
388
+ "nbformat_minor": 5
389
+ }
Pixal3D/torch_hub/hub/valeoai_NAF_main/notebooks/inference.ipynb ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# NAF: Zero-Shot Feature Upsampling via Neighborhood Attention Filtering.\n",
8
+ "\n",
9
+ "In this notebook we present how to upsample **Any Feature from Any VFM at Any Resolution**"
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "markdown",
14
+ "metadata": {},
15
+ "source": [
16
+ "## Setup"
17
+ ]
18
+ },
19
+ {
20
+ "cell_type": "markdown",
21
+ "metadata": {},
22
+ "source": [
23
+ "Imports"
24
+ ]
25
+ },
26
+ {
27
+ "cell_type": "code",
28
+ "execution_count": null,
29
+ "metadata": {},
30
+ "outputs": [],
31
+ "source": [
32
+ "import random\n",
33
+ "import sys\n",
34
+ "from pathlib import Path\n",
35
+ "\n",
36
+ "import matplotlib.pyplot as plt\n",
37
+ "import torch\n",
38
+ "import PIL\n",
39
+ "import torch.nn.functional as F\n",
40
+ "import torchvision.transforms as T\n",
41
+ "from hydra import compose, initialize\n",
42
+ "from hydra.core.global_hydra import GlobalHydra\n",
43
+ "from hydra.utils import instantiate\n",
44
+ "from IPython.display import clear_output\n",
45
+ "\n",
46
+ "project_root = str(Path().absolute().parent)\n",
47
+ "sys.path.append(project_root)\n",
48
+ "\n",
49
+ "from utils.training import get_batch, get_dataloaders, load_multiple_backbones\n",
50
+ "from utils.visualization import plot_feats"
51
+ ]
52
+ },
53
+ {
54
+ "cell_type": "markdown",
55
+ "metadata": {},
56
+ "source": [
57
+ "Load config"
58
+ ]
59
+ },
60
+ {
61
+ "cell_type": "code",
62
+ "execution_count": null,
63
+ "metadata": {},
64
+ "outputs": [],
65
+ "source": [
66
+ "if not GlobalHydra.instance().is_initialized():\n",
67
+ " initialize(config_path=\"../config\", version_base=None)\n",
68
+ "\n",
69
+ "overrides = [\"val_dataloader.batch_size=1\", \n",
70
+ " \"train_dataloader.batch_size=1\", \n",
71
+ " \"model=naf\", \n",
72
+ " \"img_size=448\"]\n",
73
+ "\n",
74
+ "cfg = compose(config_name=\"base\", overrides=overrides)\n",
75
+ "clear_output()"
76
+ ]
77
+ },
78
+ {
79
+ "cell_type": "markdown",
80
+ "metadata": {},
81
+ "source": [
82
+ "Dataloader"
83
+ ]
84
+ },
85
+ {
86
+ "cell_type": "code",
87
+ "execution_count": null,
88
+ "metadata": {},
89
+ "outputs": [],
90
+ "source": [
91
+ "train_dataloader, val_dataloader = get_dataloaders(cfg, shuffle=True)\n",
92
+ "clear_output()"
93
+ ]
94
+ },
95
+ {
96
+ "cell_type": "markdown",
97
+ "metadata": {},
98
+ "source": [
99
+ "Model"
100
+ ]
101
+ },
102
+ {
103
+ "cell_type": "code",
104
+ "execution_count": null,
105
+ "metadata": {},
106
+ "outputs": [],
107
+ "source": [
108
+ "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
109
+ "model = torch.hub.load(\"valeoai/NAF\", \"naf\", pretrained=True, device=device)\n",
110
+ "model.cuda()\n",
111
+ "\n",
112
+ "clear_output()"
113
+ ]
114
+ },
115
+ {
116
+ "cell_type": "markdown",
117
+ "metadata": {},
118
+ "source": [
119
+ "Data"
120
+ ]
121
+ },
122
+ {
123
+ "cell_type": "code",
124
+ "execution_count": null,
125
+ "metadata": {},
126
+ "outputs": [],
127
+ "source": [
128
+ "# Either iterate over the dataloader\n",
129
+ "batch = next(iter(val_dataloader))\n",
130
+ "batch = get_batch(batch, device)\n",
131
+ "img_batch = batch[\"image\"]\n",
132
+ "bs = img_batch.shape[0]\n",
133
+ "IMG_PATH = None\n",
134
+ "\n",
135
+ "# Or load a custom image\n",
136
+ "IMG_PATH = \"../asset/dinov3.png\"\n",
137
+ "img_batch = PIL.Image.open(IMG_PATH).convert(\"RGB\")\n",
138
+ "img_batch = val_dataloader.dataset.transform(img_batch).unsqueeze(0).to(device)"
139
+ ]
140
+ },
141
+ {
142
+ "cell_type": "markdown",
143
+ "metadata": {},
144
+ "source": [
145
+ "Backbone"
146
+ ]
147
+ },
148
+ {
149
+ "cell_type": "code",
150
+ "execution_count": null,
151
+ "metadata": {},
152
+ "outputs": [],
153
+ "source": [
154
+ "@torch.no_grad()\n",
155
+ "def upsample_backbone(backbone, img, model, mean_std_bck, mean_std_ups, sizes=[512]):\n",
156
+ " torch.cuda.empty_cache()\n",
157
+ " model.eval()\n",
158
+ " mean_bck, std_bck = mean_std_bck\n",
159
+ " mean_ups, std_ups = mean_std_ups\n",
160
+ "\n",
161
+ " img_bck = T.functional.normalize(img, mean=mean_bck, std=std_bck)\n",
162
+ " img_ups = T.functional.normalize(img, mean=mean_ups, std=std_ups)\n",
163
+ "\n",
164
+ " hr_feats = backbone(img_bck)\n",
165
+ " hr_size = hr_feats.shape[-1]\n",
166
+ " preds = []\n",
167
+ " for size in sizes:\n",
168
+ " pred = model(img_ups, hr_feats, (size, size))\n",
169
+ " preds.append(pred)\n",
170
+ "\n",
171
+ " hr_feats_ = torch.nn.functional.interpolate(hr_feats, size, mode=\"nearest-exact\")\n",
172
+ " plot_feats(\n",
173
+ " img_batch[0],\n",
174
+ " hr_feats_[0],\n",
175
+ " [p[0] for p in preds],\n",
176
+ " legend=[f\"Input\", f\"{hr_size}x{hr_size}\"] + [f\"{size}x{size}\" for size in sizes],\n",
177
+ " font_size=20,\n",
178
+ " )\n",
179
+ " plt.show()\n",
180
+ " torch.cuda.empty_cache()"
181
+ ]
182
+ },
183
+ {
184
+ "cell_type": "markdown",
185
+ "metadata": {},
186
+ "source": [
187
+ "## Any Backbone"
188
+ ]
189
+ },
190
+ {
191
+ "cell_type": "code",
192
+ "execution_count": null,
193
+ "metadata": {},
194
+ "outputs": [],
195
+ "source": [
196
+ "backbone_configs = [\n",
197
+ " {\"name\": \"vit_base_patch16_dinov3.lvd1689m\"},\n",
198
+ " {\"name\": \"radio_v2.5-b\"},\n",
199
+ " {\"name\": \"franca_vitb14\"},\n",
200
+ " {\"name\": \"vit_base_patch14_reg4_dinov2\"},\n",
201
+ " {\"name\": \"vit_base_patch14_dinov2.lvd142m\"},\n",
202
+ "]\n",
203
+ "mapping = {\n",
204
+ " \"vit_base_patch16_dinov3.lvd1689m\": \"Dinov3-B\",\n",
205
+ " \"radio_v2.5-b\": \"Radio-v2.5-B\",\n",
206
+ " \"franca_vitb14\": \"Franca-B14\",\n",
207
+ " \"vit_base_patch14_reg4_dinov2\": \"Dinov2-R-B\",\n",
208
+ " \"vit_base_patch14_dinov2.lvd142m\": \"Dinov2-B\",\n",
209
+ "}\n",
210
+ "backbones, *_ = load_multiple_backbones(cfg, backbone_configs, device)\n",
211
+ "clear_output()"
212
+ ]
213
+ },
214
+ {
215
+ "cell_type": "code",
216
+ "execution_count": null,
217
+ "metadata": {},
218
+ "outputs": [],
219
+ "source": [
220
+ "mean_ups, std_ups = (0.485, 0.456, 0.406), (0.229, 0.224, 0.225)\n",
221
+ "for backbone in backbones:\n",
222
+ " backbone.cuda().eval()\n",
223
+ "\n",
224
+ " mean_bck, std_bck = backbone.config[\"mean\"], backbone.config[\"std\"]\n",
225
+ "\n",
226
+ " print(f\"BACKBONE: {mapping[backbone_configs[backbones.index(backbone)]['name']].upper()}\")\n",
227
+ " upsample_backbone(backbone, img_batch, model, (mean_bck, std_bck), (mean_ups, std_ups), sizes=[448])"
228
+ ]
229
+ },
230
+ {
231
+ "cell_type": "markdown",
232
+ "metadata": {},
233
+ "source": [
234
+ "## Any Resolution"
235
+ ]
236
+ },
237
+ {
238
+ "cell_type": "code",
239
+ "execution_count": null,
240
+ "metadata": {},
241
+ "outputs": [],
242
+ "source": [
243
+ "backbone_configs = [\n",
244
+ " {\"name\": \"vit_base_patch16_dinov3.lvd1689m\"},\n",
245
+ "]\n",
246
+ "backbones, *_ = load_multiple_backbones(cfg, backbone_configs, device)\n",
247
+ "clear_output()"
248
+ ]
249
+ },
250
+ {
251
+ "cell_type": "code",
252
+ "execution_count": null,
253
+ "metadata": {},
254
+ "outputs": [],
255
+ "source": [
256
+ "backbone = backbones[0]\n",
257
+ "backbone.cuda().eval()\n",
258
+ "\n",
259
+ "mean_bck, std_bck = backbone.config[\"mean\"], backbone.config[\"std\"]\n",
260
+ "\n",
261
+ "print(f\"BACKBONE: {backbone_configs[backbones.index(backbone)]['name']}\")\n",
262
+ "upsample_backbone(backbone, img_batch, model, (mean_bck, std_bck), (mean_ups, std_ups), sizes=[64, 128, 256, 512, 1024])"
263
+ ]
264
+ },
265
+ {
266
+ "cell_type": "code",
267
+ "execution_count": null,
268
+ "metadata": {},
269
+ "outputs": [],
270
+ "source": []
271
+ }
272
+ ],
273
+ "metadata": {
274
+ "kernelspec": {
275
+ "display_name": "naf",
276
+ "language": "python",
277
+ "name": "python3"
278
+ },
279
+ "language_info": {
280
+ "codemirror_mode": {
281
+ "name": "ipython",
282
+ "version": 3
283
+ },
284
+ "file_extension": ".py",
285
+ "mimetype": "text/x-python",
286
+ "name": "python",
287
+ "nbconvert_exporter": "python",
288
+ "pygments_lexer": "ipython3",
289
+ "version": "3.10.14"
290
+ }
291
+ },
292
+ "nbformat": 4,
293
+ "nbformat_minor": 4
294
+ }
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/backbone/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .vit_wrapper import PretrainedViTWrapper
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/backbone/vit_wrapper.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import types
3
+ from typing import List, Tuple, Union
4
+
5
+ import timm
6
+ import timm.data
7
+ import torch
8
+ import torch.nn.functional as F
9
+ from einops import rearrange
10
+ from timm.models.vision_transformer import VisionTransformer
11
+ from torch import nn
12
+ from torchvision import transforms
13
+
14
+ # We provide a list of timm model names, more are available on their official repo.
15
+ MODEL_LIST = [
16
+ # DINO
17
+ "vit_base_patch16_224.dino",
18
+ # DINOv2
19
+ "vit_base_patch14_dinov2.lvd142m",
20
+ # DINOv2-R
21
+ "vit_base_patch14_reg4_dinov2",
22
+ # Franca
23
+ "franca_vitb14",
24
+ # DINOv3-ViT
25
+ "vit_base_patch16_dinov3.lvd1689m",
26
+ "vit_large_patch16_dinov3.lvd1689m",
27
+ "vit_7b_patch16_dinov3.lvd1689m",
28
+ # SigLIP2
29
+ "vit_base_patch16_siglip_512.v2_webli",
30
+ # PE Core
31
+ "vit_pe_core_small_patch16_384.fb",
32
+ # PE Spatial
33
+ "vit_pe_spatial_tiny_patch16_512.fb",
34
+ # RADIO
35
+ "radio_v2.5-b",
36
+ # CAPI
37
+ "capi_vitl14_lvd",
38
+ # MAE
39
+ "vit_large_patch16_224.mae",
40
+ ]
41
+
42
+ IMAGENET_DEFAULT_MEAN = (0.485, 0.456, 0.406)
43
+ IMAGENET_DEFAULT_STD = (0.229, 0.224, 0.225)
44
+
45
+
46
+ class PretrainedViTWrapper(nn.Module):
47
+
48
+ def __init__(
49
+ self,
50
+ name,
51
+ norm: bool = True,
52
+ dynamic_img_size: bool = True,
53
+ dynamic_img_pad: bool = False,
54
+ **kwargs,
55
+ ):
56
+ super().__init__()
57
+ # comment out the following line to test the models not in the list
58
+ self.name = name
59
+
60
+ load_weights = False
61
+ if "dvt_" == name[:4]:
62
+ load_weights = True
63
+ load_tag = "dvt"
64
+ name = name.replace("dvt_", "")
65
+ if "fit3d_" == name[:6]:
66
+ load_weights = True
67
+ load_tag = "fit3d"
68
+ name = name.replace("fit3d_", "")
69
+
70
+ # Set patch size
71
+ try:
72
+ self.patch_size = int(re.search(r"patch(\d+)", name).group(1))
73
+ except:
74
+ self.patch_size = 16
75
+ if "franca" in name or "capi" in name:
76
+ self.patch_size = 14
77
+ if "convnext" in name:
78
+ self.patch_size = 32
79
+
80
+ name, self.patch_size
81
+
82
+ self.dynamic_img_size = dynamic_img_size
83
+ self.dynamic_img_pad = dynamic_img_pad
84
+ self.model, self.config = self.create_model(name, **kwargs)
85
+ self.config["ps"] = self.patch_size
86
+ self.embed_dim = self.model.embed_dim
87
+ self.norm = norm
88
+
89
+ if load_weights:
90
+ ckpt = torch.load(f"/home/lchambon/workspace/JAFAR/ckpts/{load_tag}_{name}.pth", map_location="cpu")
91
+ if load_tag == "dvt":
92
+ self.load_state_dict(ckpt["model"], strict=True)
93
+ elif load_tag == "fit3d":
94
+ self.model.load_state_dict(ckpt, strict=True)
95
+
96
+ def create_model(self, name: str, **kwargs) -> Tuple[VisionTransformer, transforms.Compose]:
97
+ if "radio" in self.name:
98
+ model = torch.hub.load(
99
+ "NVlabs/RADIO",
100
+ "radio_model",
101
+ version=name,
102
+ progress=True,
103
+ skip_validation=True,
104
+ )
105
+ data_config = {
106
+ "mean": torch.tensor([0.0, 0.0, 0.0]),
107
+ "std": torch.tensor([1.0, 1.0, 1.0]),
108
+ "input_size": (3, 512, 512),
109
+ }
110
+
111
+ elif "franca" in self.name:
112
+ model = torch.hub.load("valeoai/Franca", name, use_rasa_head=True)
113
+ data_config = {"mean": IMAGENET_DEFAULT_MEAN, "std": IMAGENET_DEFAULT_STD, "input_size": (3, 448, 448)}
114
+
115
+ elif "capi" in self.name:
116
+ model = torch.hub.load("facebookresearch/capi:main", name, force_reload=False)
117
+
118
+ data_config = {"mean": IMAGENET_DEFAULT_MEAN, "std": IMAGENET_DEFAULT_STD, "input_size": (3, 448, 448)}
119
+
120
+ else:
121
+ timm_kwargs = dict(
122
+ pretrained=True,
123
+ num_classes=0,
124
+ patch_size=self.patch_size,
125
+ )
126
+
127
+ if "sam" not in self.name and "convnext" not in self.name:
128
+ timm_kwargs["dynamic_img_size"] = self.dynamic_img_size
129
+ timm_kwargs["dynamic_img_pad"] = self.dynamic_img_pad
130
+
131
+ timm_kwargs.update(kwargs)
132
+ model = timm.create_model(name, **timm_kwargs)
133
+ data_config = timm.data.resolve_model_data_config(model=model)
134
+
135
+ model = model.eval()
136
+
137
+ return model, data_config
138
+
139
+ def forward(
140
+ self,
141
+ x: torch.Tensor,
142
+ n: Union[int, List[int], Tuple[int]] = 1,
143
+ return_prefix_tokens: bool = False,
144
+ ) -> Union[List[torch.Tensor], Tuple[torch.Tensor, List[torch.Tensor]]]:
145
+ """Intermediate layer accessor inspired by DINO / DINOv2 interface.
146
+ Args:
147
+ x: Input tensor.
148
+ n: Take last n blocks if int, all if None, select matching indices if sequence
149
+ reshape: Whether to reshape the output.
150
+ """
151
+
152
+ common_kwargs = dict(
153
+ norm=self.norm,
154
+ output_fmt="NCHW",
155
+ intermediates_only=True,
156
+ )
157
+
158
+ if "sam" not in self.name and return_prefix_tokens:
159
+ common_kwargs["return_prefix_tokens"] = return_prefix_tokens
160
+
161
+ elif "franca" in self.name:
162
+ B, C, H, W = x.shape
163
+ feats = self.model.forward_features(x, use_rasa_head=True)
164
+ out = feats["patch_token_rasa"]
165
+ out = rearrange(out, "b (h w) c -> b c h w", h=H // self.patch_size, w=W // self.patch_size)
166
+
167
+ elif "capi" in self.name:
168
+ *_, out = self.model(x)
169
+ out = out.permute(0, 3, 1, 2)
170
+
171
+ else:
172
+ out = self.model.forward_intermediates(x, n, **common_kwargs)
173
+
174
+ # "sam" models return feats only, others may return (feats, prefix)
175
+ if not isinstance(out, list) and not isinstance(out, tuple):
176
+ out = [out]
177
+ return out[0]
178
+ else:
179
+ assert len(out) == 1, f"Out contains {len(out)} elements, expected 1."
180
+ return out[0]
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .attentions import CrossAttention
2
+ from .convolutions import encoder
3
+ from .rope import RoPE
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (354 Bytes). View file
 
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/__pycache__/attentions.cpython-311.pyc ADDED
Binary file (4.08 kB). View file
 
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/__pycache__/convolutions.cpython-311.pyc ADDED
Binary file (3.49 kB). View file
 
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/__pycache__/rope.cpython-311.pyc ADDED
Binary file (8.44 kB). View file
 
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/attentions.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ from einops import rearrange
5
+
6
+ try:
7
+ NATTEN_RECENT = False
8
+ from natten.functional import na2d_av, na2d_qk
9
+ except:
10
+ NATTEN_RECENT = True
11
+ from natten import na2d
12
+
13
+ from src.layers.rope import RoPE
14
+
15
+
16
+ def legacy_attention(q, k, v, kernel_size, dilation, scale=1, return_weights=False):
17
+ q = rearrange(q, "b h w n d -> b n h w d")
18
+ k = rearrange(k, "b h w n d -> b n h w d")
19
+ v = rearrange(v, "b h w n d -> b n h w d")
20
+ attn_scores = na2d_qk(q, k, kernel_size=kernel_size, dilation=dilation)
21
+ attn_scores = attn_scores * scale
22
+
23
+ attn_weights = attn_scores.softmax(dim=-1)
24
+ features = na2d_av(attn_weights, v, kernel_size=kernel_size, dilation=dilation)
25
+ features = rearrange(features, "b n h w d -> b h w n d")
26
+
27
+ if return_weights:
28
+ return features, attn_scores
29
+ return features
30
+
31
+
32
+ class CrossAttention(nn.Module):
33
+ def __init__(
34
+ self,
35
+ dim,
36
+ num_heads,
37
+ kernel_size=(9, 9),
38
+ **kwargs,
39
+ ):
40
+ super().__init__()
41
+ assert dim % num_heads == 0, "dim must be divisible by num_heads"
42
+
43
+ self.num_heads = num_heads
44
+ self.kernel_size = kernel_size
45
+
46
+ self.scale = (dim // num_heads) ** -0.5
47
+
48
+ def _resize(self, x, size, dtype):
49
+ x = F.interpolate(x, size=size, mode="nearest-exact")
50
+ x = rearrange(x, "b (n d) h w -> b h w n d", n=self.num_heads)
51
+ return x.to(dtype)
52
+
53
+ def forward(self, q, k, v, image=None, return_weights=False, **kwargs):
54
+ hq, wq = q.shape[-2:]
55
+ hk, wk = k.shape[-2:]
56
+ dilation = (hq // hk, wq // wk)
57
+ self.dilation = dilation
58
+
59
+ q = rearrange(q, "b (n d) h w -> b h w n d", n=self.num_heads)
60
+ k = self._resize(k, size=(hq, wq), dtype=q.dtype)
61
+ v = self._resize(v, size=(hq, wq), dtype=q.dtype)
62
+
63
+ # Use legacy attention pattern
64
+ if return_weights:
65
+ assert not NATTEN_RECENT, "Return weights not supported with recent natten versions"
66
+ out, attn_weights = legacy_attention(q, k, v, self.kernel_size, dilation, scale=self.scale, return_weights=True)
67
+ return rearrange(out, "b h w n d -> b (n d) h w"), attn_weights
68
+ else:
69
+ if NATTEN_RECENT:
70
+ # Use modern na2d attention
71
+ # Note: Modern na2d doesn't support position bias directly
72
+ out = na2d(q, k, v, kernel_size=self.kernel_size, dilation=dilation, stride=1, backend="cutlass-fna")
73
+ else:
74
+ out = legacy_attention(q, k, v, self.kernel_size, dilation, scale=self.scale)
75
+ return rearrange(out, "b h w n d -> b (n d) h w")
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/convolutions.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch.nn as nn
2
+ import torch.nn.functional as F
3
+
4
+
5
+ # Convolutions
6
+ class EncBlock(nn.Module):
7
+ def __init__(
8
+ self,
9
+ in_channels,
10
+ out_channels,
11
+ kernel_size=3,
12
+ norm_kwargs={},
13
+ pad_mode="zeros",
14
+ norm_fn=None,
15
+ activation_fn=nn.SiLU,
16
+ use_conv_shortcut=False,
17
+ bias=True,
18
+ residual=False,
19
+ ):
20
+ super().__init__()
21
+ self.use_conv_shortcut = use_conv_shortcut
22
+ self.norm1 = norm_fn(**norm_kwargs)
23
+ self.conv1 = nn.Conv2d(
24
+ in_channels,
25
+ out_channels,
26
+ kernel_size=kernel_size,
27
+ padding=kernel_size // 2,
28
+ padding_mode=pad_mode,
29
+ bias=bias,
30
+ )
31
+ self.norm2 = norm_fn(**norm_kwargs)
32
+ self.conv2 = nn.Conv2d(
33
+ out_channels,
34
+ out_channels,
35
+ kernel_size=kernel_size,
36
+ padding=kernel_size // 2,
37
+ padding_mode=pad_mode,
38
+ bias=bias,
39
+ )
40
+ self.activation_fn = activation_fn()
41
+ if in_channels != out_channels:
42
+ self.shortcut = nn.Conv2d(
43
+ in_channels,
44
+ out_channels,
45
+ kernel_size=1,
46
+ padding=0,
47
+ padding_mode=pad_mode,
48
+ bias=bias,
49
+ )
50
+ self.residual = residual
51
+
52
+ def forward(self, x):
53
+ residual = x
54
+ x = self.norm1(x)
55
+ x = self.activation_fn(x)
56
+ x = self.conv1(x)
57
+ x = self.norm2(x)
58
+ x = self.activation_fn(x)
59
+ x = self.conv2(x)
60
+ if self.use_conv_shortcut or residual.shape != x.shape:
61
+ residual = self.shortcut(residual)
62
+ if self.residual:
63
+ return x + residual
64
+ return x
65
+
66
+
67
+ def encoder(in_dim, hidden_dim, kernel_size=1, ks_res=1, num_layers=2, bias=True, num_groups=8, residual=False):
68
+ return nn.Sequential(
69
+ nn.Conv2d(
70
+ in_dim,
71
+ hidden_dim,
72
+ kernel_size=kernel_size,
73
+ padding=kernel_size // 2,
74
+ padding_mode="reflect",
75
+ bias=bias,
76
+ ),
77
+ *[
78
+ EncBlock(
79
+ hidden_dim,
80
+ hidden_dim,
81
+ kernel_size=ks_res,
82
+ pad_mode="reflect",
83
+ norm_fn=nn.GroupNorm,
84
+ norm_kwargs={"num_groups": num_groups, "num_channels": hidden_dim},
85
+ activation_fn=nn.SiLU,
86
+ use_conv_shortcut=False,
87
+ bias=bias,
88
+ residual=residual,
89
+ )
90
+ for _ in range(num_layers)
91
+ ],
92
+ )
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/layers/rope.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # This software may be used and distributed in accordance with
4
+ # the terms of the DINOv3 License Agreement.
5
+
6
+ import math
7
+ from typing import Literal, Optional
8
+
9
+ import numpy as np
10
+ import torch
11
+ from einops import rearrange
12
+ from torch import Tensor, nn
13
+
14
+
15
+ def rope_rotate_half(x: Tensor) -> Tensor:
16
+ # x: [ x0 x1 x2 x3 x4 x5]
17
+ # out: [-x3 -x4 -x5 x0 x1 x2]
18
+ x1, x2 = x.chunk(2, dim=-1)
19
+ return torch.cat([-x2, x1], dim=-1)
20
+
21
+
22
+ def rope_apply(x: Tensor, sin: Tensor, cos: Tensor) -> Tensor:
23
+ """
24
+ - x: feature vector of shape [..., D] (e.g., [x0, x1, ..., x_{D-1}])
25
+ - sin, cos: shape [..., D], e.g., [sin0, sin1, ..., sin_{D/2-1}, sin0, ..., sin_{D/2-1}]
26
+ - rope_apply rotates the embedding by pairing the first half with the second half:
27
+ - out = [ x0*cos0 - x_{D/2}*sin0,
28
+ x1*cos1 - x_{D/2+1}*sin1,
29
+ ...,
30
+ x_{D/2}*cos0 + x0*sin0,
31
+ x_{D/2+1}*cos1 + x1*sin1,
32
+ ... ]
33
+ """
34
+ return (x * cos) + (rope_rotate_half(x) * sin)
35
+
36
+
37
+ # RoPE positional embedding with no mixing of coordinates (axial) and no learnable weights
38
+ # Supports two parametrizations of the rope parameters: either using `base` or `min_period` and `max_period`.
39
+ class RoPE(nn.Module):
40
+ def __init__(
41
+ self,
42
+ embed_dim: int,
43
+ *,
44
+ num_heads: int,
45
+ base: float = 100.0,
46
+ min_period: float = None,
47
+ max_period: float = None,
48
+ normalize_coords: Literal["min", "max", "separate"] = "separate",
49
+ shift_coords: float = None,
50
+ jitter_coords: float = None,
51
+ rescale_coords: float = None,
52
+ dtype: torch.dtype = None,
53
+ device: torch.device = None,
54
+ ):
55
+ super().__init__()
56
+ assert embed_dim % (4 * num_heads) == 0
57
+ both_periods = min_period is not None and max_period is not None
58
+ if (base is None and not both_periods) or (base is not None and both_periods):
59
+ raise ValueError("Either `base` or `min_period`+`max_period` must be provided.")
60
+
61
+ D_head = embed_dim // num_heads
62
+ self.num_heads = num_heads
63
+ self.base = base
64
+ self.min_period = min_period
65
+ self.max_period = max_period
66
+ self.D_head = D_head
67
+ self.normalize_coords = normalize_coords
68
+ self.shift_coords = shift_coords
69
+ self.jitter_coords = jitter_coords
70
+ self.rescale_coords = rescale_coords
71
+
72
+ self._cached_coords = None
73
+ self._cached_hw = None
74
+
75
+ # Needs persistent=True because we do teacher.load_state_dict(student.state_dict()) to initialize the teacher
76
+ self.dtype = dtype # Don't rely on self.periods.dtype
77
+ self.register_buffer(
78
+ "periods",
79
+ torch.empty(D_head // 4, device=device, dtype=dtype),
80
+ persistent=True,
81
+ )
82
+ self._init_weights()
83
+
84
+ def create_coordinate(self, *, H: int, W: int) -> tuple[Tensor, Tensor]:
85
+ device = self.periods.device
86
+ dtype = self.dtype
87
+ dd = {"device": device, "dtype": dtype}
88
+
89
+ # Prepare coords in range [-1, +1]
90
+ if self.normalize_coords == "max":
91
+ max_HW = max(H, W)
92
+ coords_h = torch.arange(0.5, H, **dd) / max_HW # [H]
93
+ coords_w = torch.arange(0.5, W, **dd) / max_HW # [W]
94
+ elif self.normalize_coords == "min":
95
+ min_HW = min(H, W)
96
+ coords_h = torch.arange(0.5, H, **dd) / min_HW # [H]
97
+ coords_w = torch.arange(0.5, W, **dd) / min_HW # [W]
98
+ elif self.normalize_coords == "separate":
99
+ coords_h = torch.arange(0.5, H, **dd) / H # [H]
100
+ coords_w = torch.arange(0.5, W, **dd) / W # [W]
101
+ else:
102
+ raise ValueError(f"Unknown normalize_coords: {self.normalize_coords}")
103
+ coords = torch.stack(torch.meshgrid(coords_h, coords_w, indexing="ij"), dim=-1) # [H, W, 2]
104
+ coords = coords.flatten(0, 1) # [HW, 2]
105
+ coords = 2.0 * coords - 1.0 # Shift range [0, 1] to [-1, +1]
106
+
107
+ # Shift coords by adding a uniform value in [-shift, shift]
108
+ if self.training and self.shift_coords is not None:
109
+ shift_hw = torch.empty(2, **dd).uniform_(-self.shift_coords, self.shift_coords)
110
+ coords += shift_hw[None, :]
111
+
112
+ # Jitter coords by multiplying the range [-1, 1] by a log-uniform value in [1/jitter, jitter]
113
+ if self.training and self.jitter_coords is not None:
114
+ jitter_max = np.log(self.jitter_coords)
115
+ jitter_min = -jitter_max
116
+ jitter_hw = torch.empty(2, **dd).uniform_(jitter_min, jitter_max).exp()
117
+ coords *= jitter_hw[None, :]
118
+
119
+ # Rescale coords by multiplying the range [-1, 1] by a log-uniform value in [1/rescale, rescale]
120
+ if self.training and self.rescale_coords is not None:
121
+ rescale_max = np.log(self.rescale_coords)
122
+ rescale_min = -rescale_max
123
+ rescale_hw = torch.empty(1, **dd).uniform_(rescale_min, rescale_max).exp()
124
+ coords *= rescale_hw
125
+
126
+ return coords
127
+
128
+ def _init_weights(self):
129
+ device = self.periods.device
130
+ dtype = self.dtype
131
+ if self.base is not None:
132
+ periods = self.base ** (2 * torch.arange(self.D_head // 4, device=device, dtype=dtype) / (self.D_head // 2))
133
+ else:
134
+ periods = torch.logspace(math.log10(self.min_period), math.log10(self.max_period), steps=self.D_head // 4)
135
+ self.periods.data = periods
136
+
137
+ def rotate(self, x, coords):
138
+ # Prepare angles and sin/cos
139
+ angles = 2 * math.pi * coords[:, :, None] / self.periods[None, None, :] # [HW, 2, D//4]
140
+ # Flatten u and v frequencies into a single vector per spatial location: [u1, ..., u_{D//4}, v1, ..., v_{D//4}]
141
+ angles = angles.flatten(1, 2) # [HW, D//2]
142
+ # Repeat the vector to match embedding dimension D: [u1,...,u_{D//4}, v1,...,v_{D//4}, u1,...,u_{D//4}, v1,...,v_{D//4}]
143
+ angles = angles.tile(2) # [HW, D]
144
+
145
+ cos = torch.cos(angles) # [HW, D]
146
+ sin = torch.sin(angles) # [HW, D]
147
+
148
+ # x_u0 ... x_u{D//2-1} x_v0 ... x_v{D//2-1}
149
+
150
+ # Apply RoPE: rotates each (u_i, v_i) pair
151
+ # first half: x[i]*cos[i] - x[i+D/2]*sin[i]
152
+ # second half: x[i+D/2]*cos[i] + x[i]*sin[i]
153
+ return rope_apply(x, sin, cos)
154
+
155
+ def forward(self, x, layout: str = "spatial"):
156
+ h, w = x.shape[-2:]
157
+ x = rearrange(x, "b (n d) h w -> b n (h w) d", n=self.num_heads)
158
+
159
+ if (h, w) != self._cached_hw:
160
+ self._cached_coords = self.create_coordinate(H=h, W=w)
161
+ self._cached_hw = (h, w)
162
+
163
+ coords = self._cached_coords
164
+
165
+ # Rotate
166
+ x = self.rotate(x, coords)
167
+
168
+ # Reshape
169
+ if layout == "spatial":
170
+ x = rearrange(x, f"b n (h w) d -> b (n d) h w", h=h, w=w)
171
+ elif layout == "flatten":
172
+ x = rearrange(x, f"b n (h w) d -> b h w n d", h=h, w=w)
173
+
174
+ return x
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/loss.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from torch import nn
4
+ from torch.nn import functional as F
5
+
6
+
7
+ class MSELoss(nn.Module):
8
+ def __init__(self):
9
+ super().__init__()
10
+ self.mse_loss = torch.nn.MSELoss()
11
+
12
+ def forward(self, pred, target, normalize=False):
13
+ if normalize:
14
+ # If you must normalize (example: min-max scaling)
15
+ min_val = torch.min(target, dim=1, keepdim=True).values
16
+ max_val = torch.max(target, dim=1, keepdim=True).values
17
+ pred_normalized = (pred - min_val) / (max_val - min_val + 1e-6)
18
+ target_normalized = (target - min_val) / (max_val - min_val + 1e-6)
19
+ else:
20
+ pred_normalized = pred
21
+ target_normalized = target
22
+
23
+ return self.mse_loss(pred_normalized, target_normalized)
24
+
25
+
26
+ class Loss(nn.Module):
27
+
28
+ def __init__(
29
+ self,
30
+ loss_type,
31
+ dim=384,
32
+ ):
33
+ super().__init__()
34
+ self.dim = dim
35
+
36
+ if loss_type == "mse":
37
+ loss = MSELoss()
38
+ else:
39
+ raise NotImplementedError(f"Loss type {loss_type} not implemented")
40
+
41
+ self.loss_func = loss
42
+
43
+ def __call__(self, pred, target, *args, **kwargs):
44
+ loss = self.loss_func(pred, target, *args, **kwargs)
45
+ return {"total": loss}
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .anyup import AnyUpsampler
2
+ from .base import BaseUpsampler
3
+ from .bilinear import Bilinear
4
+ from .featup import FeatUp
5
+ from .ircnn import IRCNN
6
+ from .jafar import JAFAR
7
+ from .jbf import JBF
8
+ from .jbu import JBU
9
+ from .naf import NAF
10
+ from .nearest import Nearest
11
+ from .rednet import REDNet
12
+ from .restormer import Restormer
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (815 Bytes). View file
 
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__pycache__/anyup.cpython-311.pyc ADDED
Binary file (1.53 kB). View file
 
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__pycache__/base.cpython-311.pyc ADDED
Binary file (1.09 kB). View file
 
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__pycache__/bilinear.cpython-311.pyc ADDED
Binary file (1.18 kB). View file
 
Pixal3D/torch_hub/hub/valeoai_NAF_main/src/model/__pycache__/featup.cpython-311.pyc ADDED
Binary file (11.3 kB). View file