release code
Browse files- .gitignore +2 -0
- README.md +46 -2
- configs/final.yaml +40 -0
- data/casia_datalist.json +0 -0
- data/columbia_datalist.json +1997 -0
- data/coverage_datalist.json +1048 -0
- datasets/__init__.py +28 -0
- datasets/dataset.py +230 -0
- engine.py +454 -0
- losses/__init__.py +61 -0
- losses/bundled_loss.py +84 -0
- losses/consisitency_loss.py +73 -0
- losses/entropy_loss.py +20 -0
- losses/loss.py +93 -0
- losses/map_label_loss.py +34 -0
- losses/map_mask_loss.py +26 -0
- losses/multi_view_consistency_loss.py +152 -0
- losses/volume_label_loss.py +16 -0
- losses/volume_mask_loss.py +40 -0
- main.py +204 -0
- models/__init__.py +65 -0
- models/bayar_conv.py +67 -0
- models/early_fusion_pre_filter.py +25 -0
- models/ensemble_model.py +32 -0
- models/hrnet.py +537 -0
- models/main_model.py +290 -0
- models/mobilenet.py +166 -0
- models/models.py +687 -0
- models/resnet.py +229 -0
- models/resnext.py +178 -0
- models/srm_conv.py +68 -0
- models/utils.py +20 -0
- opt.py +483 -0
- requirements.txt +29 -0
- utils/__init__.py +0 -0
- utils/convcrf/__init__.py +0 -0
- utils/convcrf/convcrf.py +669 -0
- utils/crf.py +41 -0
- utils/misc.py +370 -0
.gitignore
CHANGED
@@ -178,3 +178,5 @@ pyrightconfig.json
|
|
178 |
|
179 |
*.DS_Store
|
180 |
|
|
|
|
|
|
178 |
|
179 |
*.DS_Store
|
180 |
|
181 |
+
tmp/
|
182 |
+
pretrained/
|
README.md
CHANGED
@@ -1,6 +1,5 @@
|
|
1 |
# Towards Generic Image Manipulation Detection with Weakly-Supervised Self-Consistency Learning
|
2 |
|
3 |
-
This repo contains the original PyTorch implementation of our paper:
|
4 |
|
5 |
> [**Towards Generic Image Manipulation Detection with Weakly-Supervised Self-Consistency Learning**](https://arxiv.org/abs/2309.01246)
|
6 |
>
|
@@ -10,4 +9,49 @@ This repo contains the original PyTorch implementation of our paper:
|
|
10 |
>
|
11 |
> ICCV 2023
|
12 |
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# Towards Generic Image Manipulation Detection with Weakly-Supervised Self-Consistency Learning
|
2 |
|
|
|
3 |
|
4 |
> [**Towards Generic Image Manipulation Detection with Weakly-Supervised Self-Consistency Learning**](https://arxiv.org/abs/2309.01246)
|
5 |
>
|
|
|
9 |
>
|
10 |
> ICCV 2023
|
11 |
|
12 |
+
This repo contains the MIL-FCN version of our WSCL implementation.
|
13 |
+
|
14 |
+
## 1. Setup
|
15 |
+
Clone this repo
|
16 |
+
|
17 |
+
```bash
|
18 |
+
git clone [email protected]:yhZhai/WSCL.git
|
19 |
+
```
|
20 |
+
|
21 |
+
Install packages
|
22 |
+
```bash
|
23 |
+
pip install -r requirements.txt
|
24 |
+
```
|
25 |
+
|
26 |
+
## 2. Data preparation
|
27 |
+
|
28 |
+
We provide preprocessed CASIA (v1 and v2), Columbia, and Coverage datasets [here](https://buffalo.box.com/s/2t3eqvwp7ua2ircpdx12sfq04sne4x50).
|
29 |
+
Place them under the `data` folder.
|
30 |
+
|
31 |
+
|
32 |
+
## 3. Training and evaluation
|
33 |
+
|
34 |
+
Runing the following script to train on CASIAv2, and evalute on CASIAv1, Columbia and Coverage.
|
35 |
+
|
36 |
+
```shell
|
37 |
+
python main.py --load configs/final.yaml
|
38 |
+
```
|
39 |
+
|
40 |
+
|
41 |
+
## Citation
|
42 |
+
If you feel this project is helpful, please consider citing our paper
|
43 |
+
```bibtex
|
44 |
+
@inproceedings{zhai2023towards,
|
45 |
+
title={Towards Generic Image Manipulation Detection with Weakly-Supervised Self-Consistency Learning},
|
46 |
+
author={Zhai, Yuanhao and Luan, Tianyu and Doermann, David and Yuan, Junsong},
|
47 |
+
booktitle={Proceedings of the IEEE/CVF International Conference on Computer Vision},
|
48 |
+
pages={22390--22400},
|
49 |
+
year={2023}
|
50 |
+
}
|
51 |
+
```
|
52 |
+
|
53 |
+
|
54 |
+
## Acknowledgement
|
55 |
+
We would like to thank the following repos for their great work:
|
56 |
+
- [awesome-semantic-segmentation-pytorch](https://github.com/Tramac/awesome-semantic-segmentation-pytorch)
|
57 |
+
- [DETR](https://github.com/facebookresearch/detr)
|
configs/final.yaml
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
modality:
|
2 |
+
- rgb
|
3 |
+
- srm
|
4 |
+
- bayar
|
5 |
+
train_datalist:
|
6 |
+
casia: data/casia_datalist.json
|
7 |
+
val_datalist:
|
8 |
+
casia: data/casia_datalist.json
|
9 |
+
columbia: data/columbia_datalist.json
|
10 |
+
coverage: data/coverage_datalist.json
|
11 |
+
no_gaussian_blur: True
|
12 |
+
no_color_jitter: True
|
13 |
+
|
14 |
+
# model
|
15 |
+
loss_on_mid_map: True
|
16 |
+
otsu_sel: True
|
17 |
+
otsu_portion: 1
|
18 |
+
|
19 |
+
# losses
|
20 |
+
map_label_weight: 1.
|
21 |
+
map_mask_weight: 0.
|
22 |
+
volume_mask_weight: 0.
|
23 |
+
volume_label_weight: 0.
|
24 |
+
consistency_weight: 0.1
|
25 |
+
consistency_source: ensemble
|
26 |
+
mvc_weight: 0.1
|
27 |
+
mvc_single_weight:
|
28 |
+
- 1
|
29 |
+
- 2
|
30 |
+
- 2
|
31 |
+
mvc_time_dependent: True
|
32 |
+
|
33 |
+
# arch
|
34 |
+
fcn_up: 16
|
35 |
+
|
36 |
+
# misc
|
37 |
+
batch_size: 36
|
38 |
+
|
39 |
+
# eval
|
40 |
+
tile_size: 1024
|
data/casia_datalist.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
data/columbia_datalist.json
ADDED
@@ -0,0 +1,1997 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"nikond70_05_sub_09.tif": {
|
3 |
+
"subset": "val",
|
4 |
+
"path": "data/columbia/val/au/nikond70_05_sub_09.tif",
|
5 |
+
"label": 0
|
6 |
+
},
|
7 |
+
"canonxt_11_sub_07.tif": {
|
8 |
+
"subset": "val",
|
9 |
+
"path": "data/columbia/val/au/canonxt_11_sub_07.tif",
|
10 |
+
"label": 0
|
11 |
+
},
|
12 |
+
"canong3_02_sub_06.tif": {
|
13 |
+
"subset": "val",
|
14 |
+
"path": "data/columbia/val/au/canong3_02_sub_06.tif",
|
15 |
+
"label": 0
|
16 |
+
},
|
17 |
+
"nikond70_08_sub_06.tif": {
|
18 |
+
"subset": "val",
|
19 |
+
"path": "data/columbia/val/au/nikond70_08_sub_06.tif",
|
20 |
+
"label": 0
|
21 |
+
},
|
22 |
+
"kodakdcs330_03_sub_01.tif": {
|
23 |
+
"subset": "val",
|
24 |
+
"path": "data/columbia/val/au/kodakdcs330_03_sub_01.tif",
|
25 |
+
"label": 0
|
26 |
+
},
|
27 |
+
"canonxt_11_sub_03.tif": {
|
28 |
+
"subset": "val",
|
29 |
+
"path": "data/columbia/val/au/canonxt_11_sub_03.tif",
|
30 |
+
"label": 0
|
31 |
+
},
|
32 |
+
"canonxt_11_sub_08.tif": {
|
33 |
+
"subset": "val",
|
34 |
+
"path": "data/columbia/val/au/canonxt_11_sub_08.tif",
|
35 |
+
"label": 0
|
36 |
+
},
|
37 |
+
"nikond70_05_sub_03.tif": {
|
38 |
+
"subset": "val",
|
39 |
+
"path": "data/columbia/val/au/nikond70_05_sub_03.tif",
|
40 |
+
"label": 0
|
41 |
+
},
|
42 |
+
"canong3_02_sub_09.tif": {
|
43 |
+
"subset": "val",
|
44 |
+
"path": "data/columbia/val/au/canong3_02_sub_09.tif",
|
45 |
+
"label": 0
|
46 |
+
},
|
47 |
+
"canonxt_38_sub_08.tif": {
|
48 |
+
"subset": "val",
|
49 |
+
"path": "data/columbia/val/au/canonxt_38_sub_08.tif",
|
50 |
+
"label": 0
|
51 |
+
},
|
52 |
+
"canonxt_02_sub_02.tif": {
|
53 |
+
"subset": "val",
|
54 |
+
"path": "data/columbia/val/au/canonxt_02_sub_02.tif",
|
55 |
+
"label": 0
|
56 |
+
},
|
57 |
+
"canonxt_08_sub_03.tif": {
|
58 |
+
"subset": "val",
|
59 |
+
"path": "data/columbia/val/au/canonxt_08_sub_03.tif",
|
60 |
+
"label": 0
|
61 |
+
},
|
62 |
+
"nikond70_08_sub_08.tif": {
|
63 |
+
"subset": "val",
|
64 |
+
"path": "data/columbia/val/au/nikond70_08_sub_08.tif",
|
65 |
+
"label": 0
|
66 |
+
},
|
67 |
+
"canonxt_26_sub_02.tif": {
|
68 |
+
"subset": "val",
|
69 |
+
"path": "data/columbia/val/au/canonxt_26_sub_02.tif",
|
70 |
+
"label": 0
|
71 |
+
},
|
72 |
+
"canonxt_05_sub_07.tif": {
|
73 |
+
"subset": "val",
|
74 |
+
"path": "data/columbia/val/au/canonxt_05_sub_07.tif",
|
75 |
+
"label": 0
|
76 |
+
},
|
77 |
+
"nikond70_02_sub_01.tif": {
|
78 |
+
"subset": "val",
|
79 |
+
"path": "data/columbia/val/au/nikond70_02_sub_01.tif",
|
80 |
+
"label": 0
|
81 |
+
},
|
82 |
+
"canonxt_17_sub_09.tif": {
|
83 |
+
"subset": "val",
|
84 |
+
"path": "data/columbia/val/au/canonxt_17_sub_09.tif",
|
85 |
+
"label": 0
|
86 |
+
},
|
87 |
+
"canonxt_20_sub_09.tif": {
|
88 |
+
"subset": "val",
|
89 |
+
"path": "data/columbia/val/au/canonxt_20_sub_09.tif",
|
90 |
+
"label": 0
|
91 |
+
},
|
92 |
+
"nikond70_08_sub_09.tif": {
|
93 |
+
"subset": "val",
|
94 |
+
"path": "data/columbia/val/au/nikond70_08_sub_09.tif",
|
95 |
+
"label": 0
|
96 |
+
},
|
97 |
+
"canonxt_23_sub_09.tif": {
|
98 |
+
"subset": "val",
|
99 |
+
"path": "data/columbia/val/au/canonxt_23_sub_09.tif",
|
100 |
+
"label": 0
|
101 |
+
},
|
102 |
+
"canong3_08_sub_02.tif": {
|
103 |
+
"subset": "val",
|
104 |
+
"path": "data/columbia/val/au/canong3_08_sub_02.tif",
|
105 |
+
"label": 0
|
106 |
+
},
|
107 |
+
"canong3_02_sub_08.tif": {
|
108 |
+
"subset": "val",
|
109 |
+
"path": "data/columbia/val/au/canong3_02_sub_08.tif",
|
110 |
+
"label": 0
|
111 |
+
},
|
112 |
+
"canonxt_02_sub_09.tif": {
|
113 |
+
"subset": "val",
|
114 |
+
"path": "data/columbia/val/au/canonxt_02_sub_09.tif",
|
115 |
+
"label": 0
|
116 |
+
},
|
117 |
+
"canonxt_08_sub_07.tif": {
|
118 |
+
"subset": "val",
|
119 |
+
"path": "data/columbia/val/au/canonxt_08_sub_07.tif",
|
120 |
+
"label": 0
|
121 |
+
},
|
122 |
+
"canonxt_23_sub_03.tif": {
|
123 |
+
"subset": "val",
|
124 |
+
"path": "data/columbia/val/au/canonxt_23_sub_03.tif",
|
125 |
+
"label": 0
|
126 |
+
},
|
127 |
+
"canong3_05_sub_01.tif": {
|
128 |
+
"subset": "val",
|
129 |
+
"path": "data/columbia/val/au/canong3_05_sub_01.tif",
|
130 |
+
"label": 0
|
131 |
+
},
|
132 |
+
"canonxt_05_sub_09.tif": {
|
133 |
+
"subset": "val",
|
134 |
+
"path": "data/columbia/val/au/canonxt_05_sub_09.tif",
|
135 |
+
"label": 0
|
136 |
+
},
|
137 |
+
"canonxt_20_sub_04.tif": {
|
138 |
+
"subset": "val",
|
139 |
+
"path": "data/columbia/val/au/canonxt_20_sub_04.tif",
|
140 |
+
"label": 0
|
141 |
+
},
|
142 |
+
"canonxt_08_sub_01.tif": {
|
143 |
+
"subset": "val",
|
144 |
+
"path": "data/columbia/val/au/canonxt_08_sub_01.tif",
|
145 |
+
"label": 0
|
146 |
+
},
|
147 |
+
"nikond70_11_sub_02.tif": {
|
148 |
+
"subset": "val",
|
149 |
+
"path": "data/columbia/val/au/nikond70_11_sub_02.tif",
|
150 |
+
"label": 0
|
151 |
+
},
|
152 |
+
"canonxt_29_sub_09.tif": {
|
153 |
+
"subset": "val",
|
154 |
+
"path": "data/columbia/val/au/canonxt_29_sub_09.tif",
|
155 |
+
"label": 0
|
156 |
+
},
|
157 |
+
"canonxt_32_sub_01.tif": {
|
158 |
+
"subset": "val",
|
159 |
+
"path": "data/columbia/val/au/canonxt_32_sub_01.tif",
|
160 |
+
"label": 0
|
161 |
+
},
|
162 |
+
"canonxt_08_sub_06.tif": {
|
163 |
+
"subset": "val",
|
164 |
+
"path": "data/columbia/val/au/canonxt_08_sub_06.tif",
|
165 |
+
"label": 0
|
166 |
+
},
|
167 |
+
"canong3_08_sub_09.tif": {
|
168 |
+
"subset": "val",
|
169 |
+
"path": "data/columbia/val/au/canong3_08_sub_09.tif",
|
170 |
+
"label": 0
|
171 |
+
},
|
172 |
+
"canonxt_29_sub_06.tif": {
|
173 |
+
"subset": "val",
|
174 |
+
"path": "data/columbia/val/au/canonxt_29_sub_06.tif",
|
175 |
+
"label": 0
|
176 |
+
},
|
177 |
+
"canonxt_35_sub_07.tif": {
|
178 |
+
"subset": "val",
|
179 |
+
"path": "data/columbia/val/au/canonxt_35_sub_07.tif",
|
180 |
+
"label": 0
|
181 |
+
},
|
182 |
+
"canonxt_20_sub_03.tif": {
|
183 |
+
"subset": "val",
|
184 |
+
"path": "data/columbia/val/au/canonxt_20_sub_03.tif",
|
185 |
+
"label": 0
|
186 |
+
},
|
187 |
+
"nikond70_11_sub_05.tif": {
|
188 |
+
"subset": "val",
|
189 |
+
"path": "data/columbia/val/au/nikond70_11_sub_05.tif",
|
190 |
+
"label": 0
|
191 |
+
},
|
192 |
+
"canonxt_29_sub_04.tif": {
|
193 |
+
"subset": "val",
|
194 |
+
"path": "data/columbia/val/au/canonxt_29_sub_04.tif",
|
195 |
+
"label": 0
|
196 |
+
},
|
197 |
+
"canong3_05_sub_07.tif": {
|
198 |
+
"subset": "val",
|
199 |
+
"path": "data/columbia/val/au/canong3_05_sub_07.tif",
|
200 |
+
"label": 0
|
201 |
+
},
|
202 |
+
"canonxt_05_sub_02.tif": {
|
203 |
+
"subset": "val",
|
204 |
+
"path": "data/columbia/val/au/canonxt_05_sub_02.tif",
|
205 |
+
"label": 0
|
206 |
+
},
|
207 |
+
"nikond70_11_sub_06.tif": {
|
208 |
+
"subset": "val",
|
209 |
+
"path": "data/columbia/val/au/nikond70_11_sub_06.tif",
|
210 |
+
"label": 0
|
211 |
+
},
|
212 |
+
"canonxt_02_sub_06.tif": {
|
213 |
+
"subset": "val",
|
214 |
+
"path": "data/columbia/val/au/canonxt_02_sub_06.tif",
|
215 |
+
"label": 0
|
216 |
+
},
|
217 |
+
"canonxt_05_sub_05.tif": {
|
218 |
+
"subset": "val",
|
219 |
+
"path": "data/columbia/val/au/canonxt_05_sub_05.tif",
|
220 |
+
"label": 0
|
221 |
+
},
|
222 |
+
"canonxt_38_sub_09.tif": {
|
223 |
+
"subset": "val",
|
224 |
+
"path": "data/columbia/val/au/canonxt_38_sub_09.tif",
|
225 |
+
"label": 0
|
226 |
+
},
|
227 |
+
"canonxt_38_sub_01.tif": {
|
228 |
+
"subset": "val",
|
229 |
+
"path": "data/columbia/val/au/canonxt_38_sub_01.tif",
|
230 |
+
"label": 0
|
231 |
+
},
|
232 |
+
"canonxt_14_sub_05.tif": {
|
233 |
+
"subset": "val",
|
234 |
+
"path": "data/columbia/val/au/canonxt_14_sub_05.tif",
|
235 |
+
"label": 0
|
236 |
+
},
|
237 |
+
"canong3_02_sub_07.tif": {
|
238 |
+
"subset": "val",
|
239 |
+
"path": "data/columbia/val/au/canong3_02_sub_07.tif",
|
240 |
+
"label": 0
|
241 |
+
},
|
242 |
+
"canonxt_35_sub_04.tif": {
|
243 |
+
"subset": "val",
|
244 |
+
"path": "data/columbia/val/au/canonxt_35_sub_04.tif",
|
245 |
+
"label": 0
|
246 |
+
},
|
247 |
+
"canonxt_11_sub_01.tif": {
|
248 |
+
"subset": "val",
|
249 |
+
"path": "data/columbia/val/au/canonxt_11_sub_01.tif",
|
250 |
+
"label": 0
|
251 |
+
},
|
252 |
+
"nikond70_05_sub_08.tif": {
|
253 |
+
"subset": "val",
|
254 |
+
"path": "data/columbia/val/au/nikond70_05_sub_08.tif",
|
255 |
+
"label": 0
|
256 |
+
},
|
257 |
+
"nikond70_02_sub_06.tif": {
|
258 |
+
"subset": "val",
|
259 |
+
"path": "data/columbia/val/au/nikond70_02_sub_06.tif",
|
260 |
+
"label": 0
|
261 |
+
},
|
262 |
+
"nikond70_11_sub_08.tif": {
|
263 |
+
"subset": "val",
|
264 |
+
"path": "data/columbia/val/au/nikond70_11_sub_08.tif",
|
265 |
+
"label": 0
|
266 |
+
},
|
267 |
+
"canong3_05_sub_06.tif": {
|
268 |
+
"subset": "val",
|
269 |
+
"path": "data/columbia/val/au/canong3_05_sub_06.tif",
|
270 |
+
"label": 0
|
271 |
+
},
|
272 |
+
"canonxt_32_sub_05.tif": {
|
273 |
+
"subset": "val",
|
274 |
+
"path": "data/columbia/val/au/canonxt_32_sub_05.tif",
|
275 |
+
"label": 0
|
276 |
+
},
|
277 |
+
"canonxt_14_sub_09.tif": {
|
278 |
+
"subset": "val",
|
279 |
+
"path": "data/columbia/val/au/canonxt_14_sub_09.tif",
|
280 |
+
"label": 0
|
281 |
+
},
|
282 |
+
"canong3_05_sub_03.tif": {
|
283 |
+
"subset": "val",
|
284 |
+
"path": "data/columbia/val/au/canong3_05_sub_03.tif",
|
285 |
+
"label": 0
|
286 |
+
},
|
287 |
+
"canonxt_20_sub_01.tif": {
|
288 |
+
"subset": "val",
|
289 |
+
"path": "data/columbia/val/au/canonxt_20_sub_01.tif",
|
290 |
+
"label": 0
|
291 |
+
},
|
292 |
+
"canonxt_29_sub_01.tif": {
|
293 |
+
"subset": "val",
|
294 |
+
"path": "data/columbia/val/au/canonxt_29_sub_01.tif",
|
295 |
+
"label": 0
|
296 |
+
},
|
297 |
+
"canonxt_26_sub_03.tif": {
|
298 |
+
"subset": "val",
|
299 |
+
"path": "data/columbia/val/au/canonxt_26_sub_03.tif",
|
300 |
+
"label": 0
|
301 |
+
},
|
302 |
+
"canonxt_38_sub_02.tif": {
|
303 |
+
"subset": "val",
|
304 |
+
"path": "data/columbia/val/au/canonxt_38_sub_02.tif",
|
305 |
+
"label": 0
|
306 |
+
},
|
307 |
+
"canonxt_38_sub_03.tif": {
|
308 |
+
"subset": "val",
|
309 |
+
"path": "data/columbia/val/au/canonxt_38_sub_03.tif",
|
310 |
+
"label": 0
|
311 |
+
},
|
312 |
+
"canonxt_26_sub_06.tif": {
|
313 |
+
"subset": "val",
|
314 |
+
"path": "data/columbia/val/au/canonxt_26_sub_06.tif",
|
315 |
+
"label": 0
|
316 |
+
},
|
317 |
+
"canonxt_20_sub_06.tif": {
|
318 |
+
"subset": "val",
|
319 |
+
"path": "data/columbia/val/au/canonxt_20_sub_06.tif",
|
320 |
+
"label": 0
|
321 |
+
},
|
322 |
+
"canonxt_08_sub_02.tif": {
|
323 |
+
"subset": "val",
|
324 |
+
"path": "data/columbia/val/au/canonxt_08_sub_02.tif",
|
325 |
+
"label": 0
|
326 |
+
},
|
327 |
+
"canonxt_14_sub_01.tif": {
|
328 |
+
"subset": "val",
|
329 |
+
"path": "data/columbia/val/au/canonxt_14_sub_01.tif",
|
330 |
+
"label": 0
|
331 |
+
},
|
332 |
+
"nikond70_11_sub_01.tif": {
|
333 |
+
"subset": "val",
|
334 |
+
"path": "data/columbia/val/au/nikond70_11_sub_01.tif",
|
335 |
+
"label": 0
|
336 |
+
},
|
337 |
+
"canonxt_05_sub_01.tif": {
|
338 |
+
"subset": "val",
|
339 |
+
"path": "data/columbia/val/au/canonxt_05_sub_01.tif",
|
340 |
+
"label": 0
|
341 |
+
},
|
342 |
+
"canonxt_17_sub_01.tif": {
|
343 |
+
"subset": "val",
|
344 |
+
"path": "data/columbia/val/au/canonxt_17_sub_01.tif",
|
345 |
+
"label": 0
|
346 |
+
},
|
347 |
+
"canonxt_38_sub_04.tif": {
|
348 |
+
"subset": "val",
|
349 |
+
"path": "data/columbia/val/au/canonxt_38_sub_04.tif",
|
350 |
+
"label": 0
|
351 |
+
},
|
352 |
+
"canonxt_17_sub_04.tif": {
|
353 |
+
"subset": "val",
|
354 |
+
"path": "data/columbia/val/au/canonxt_17_sub_04.tif",
|
355 |
+
"label": 0
|
356 |
+
},
|
357 |
+
"canonxt_35_sub_03.tif": {
|
358 |
+
"subset": "val",
|
359 |
+
"path": "data/columbia/val/au/canonxt_35_sub_03.tif",
|
360 |
+
"label": 0
|
361 |
+
},
|
362 |
+
"nikond70_02_sub_07.tif": {
|
363 |
+
"subset": "val",
|
364 |
+
"path": "data/columbia/val/au/nikond70_02_sub_07.tif",
|
365 |
+
"label": 0
|
366 |
+
},
|
367 |
+
"canong3_08_sub_08.tif": {
|
368 |
+
"subset": "val",
|
369 |
+
"path": "data/columbia/val/au/canong3_08_sub_08.tif",
|
370 |
+
"label": 0
|
371 |
+
},
|
372 |
+
"canonxt_17_sub_08.tif": {
|
373 |
+
"subset": "val",
|
374 |
+
"path": "data/columbia/val/au/canonxt_17_sub_08.tif",
|
375 |
+
"label": 0
|
376 |
+
},
|
377 |
+
"canonxt_35_sub_09.tif": {
|
378 |
+
"subset": "val",
|
379 |
+
"path": "data/columbia/val/au/canonxt_35_sub_09.tif",
|
380 |
+
"label": 0
|
381 |
+
},
|
382 |
+
"canonxt_02_sub_08.tif": {
|
383 |
+
"subset": "val",
|
384 |
+
"path": "data/columbia/val/au/canonxt_02_sub_08.tif",
|
385 |
+
"label": 0
|
386 |
+
},
|
387 |
+
"canonxt_08_sub_05.tif": {
|
388 |
+
"subset": "val",
|
389 |
+
"path": "data/columbia/val/au/canonxt_08_sub_05.tif",
|
390 |
+
"label": 0
|
391 |
+
},
|
392 |
+
"canonxt_05_sub_06.tif": {
|
393 |
+
"subset": "val",
|
394 |
+
"path": "data/columbia/val/au/canonxt_05_sub_06.tif",
|
395 |
+
"label": 0
|
396 |
+
},
|
397 |
+
"nikond70_05_sub_07.tif": {
|
398 |
+
"subset": "val",
|
399 |
+
"path": "data/columbia/val/au/nikond70_05_sub_07.tif",
|
400 |
+
"label": 0
|
401 |
+
},
|
402 |
+
"canonxt_17_sub_02.tif": {
|
403 |
+
"subset": "val",
|
404 |
+
"path": "data/columbia/val/au/canonxt_17_sub_02.tif",
|
405 |
+
"label": 0
|
406 |
+
},
|
407 |
+
"canonxt_26_sub_08.tif": {
|
408 |
+
"subset": "val",
|
409 |
+
"path": "data/columbia/val/au/canonxt_26_sub_08.tif",
|
410 |
+
"label": 0
|
411 |
+
},
|
412 |
+
"canonxt_38_sub_06.tif": {
|
413 |
+
"subset": "val",
|
414 |
+
"path": "data/columbia/val/au/canonxt_38_sub_06.tif",
|
415 |
+
"label": 0
|
416 |
+
},
|
417 |
+
"canonxt_26_sub_04.tif": {
|
418 |
+
"subset": "val",
|
419 |
+
"path": "data/columbia/val/au/canonxt_26_sub_04.tif",
|
420 |
+
"label": 0
|
421 |
+
},
|
422 |
+
"canonxt_23_sub_01.tif": {
|
423 |
+
"subset": "val",
|
424 |
+
"path": "data/columbia/val/au/canonxt_23_sub_01.tif",
|
425 |
+
"label": 0
|
426 |
+
},
|
427 |
+
"canong3_05_sub_09.tif": {
|
428 |
+
"subset": "val",
|
429 |
+
"path": "data/columbia/val/au/canong3_05_sub_09.tif",
|
430 |
+
"label": 0
|
431 |
+
},
|
432 |
+
"canonxt_02_sub_01.tif": {
|
433 |
+
"subset": "val",
|
434 |
+
"path": "data/columbia/val/au/canonxt_02_sub_01.tif",
|
435 |
+
"label": 0
|
436 |
+
},
|
437 |
+
"nikond70_05_sub_05.tif": {
|
438 |
+
"subset": "val",
|
439 |
+
"path": "data/columbia/val/au/nikond70_05_sub_05.tif",
|
440 |
+
"label": 0
|
441 |
+
},
|
442 |
+
"canong3_05_sub_04.tif": {
|
443 |
+
"subset": "val",
|
444 |
+
"path": "data/columbia/val/au/canong3_05_sub_04.tif",
|
445 |
+
"label": 0
|
446 |
+
},
|
447 |
+
"canonxt_11_sub_06.tif": {
|
448 |
+
"subset": "val",
|
449 |
+
"path": "data/columbia/val/au/canonxt_11_sub_06.tif",
|
450 |
+
"label": 0
|
451 |
+
},
|
452 |
+
"canonxt_02_sub_07.tif": {
|
453 |
+
"subset": "val",
|
454 |
+
"path": "data/columbia/val/au/canonxt_02_sub_07.tif",
|
455 |
+
"label": 0
|
456 |
+
},
|
457 |
+
"canonxt_29_sub_02.tif": {
|
458 |
+
"subset": "val",
|
459 |
+
"path": "data/columbia/val/au/canonxt_29_sub_02.tif",
|
460 |
+
"label": 0
|
461 |
+
},
|
462 |
+
"canonxt_26_sub_07.tif": {
|
463 |
+
"subset": "val",
|
464 |
+
"path": "data/columbia/val/au/canonxt_26_sub_07.tif",
|
465 |
+
"label": 0
|
466 |
+
},
|
467 |
+
"canonxt_14_sub_08.tif": {
|
468 |
+
"subset": "val",
|
469 |
+
"path": "data/columbia/val/au/canonxt_14_sub_08.tif",
|
470 |
+
"label": 0
|
471 |
+
},
|
472 |
+
"canonxt_14_sub_02.tif": {
|
473 |
+
"subset": "val",
|
474 |
+
"path": "data/columbia/val/au/canonxt_14_sub_02.tif",
|
475 |
+
"label": 0
|
476 |
+
},
|
477 |
+
"canonxt_11_sub_09.tif": {
|
478 |
+
"subset": "val",
|
479 |
+
"path": "data/columbia/val/au/canonxt_11_sub_09.tif",
|
480 |
+
"label": 0
|
481 |
+
},
|
482 |
+
"canong3_02_sub_02.tif": {
|
483 |
+
"subset": "val",
|
484 |
+
"path": "data/columbia/val/au/canong3_02_sub_02.tif",
|
485 |
+
"label": 0
|
486 |
+
},
|
487 |
+
"canonxt_35_sub_02.tif": {
|
488 |
+
"subset": "val",
|
489 |
+
"path": "data/columbia/val/au/canonxt_35_sub_02.tif",
|
490 |
+
"label": 0
|
491 |
+
},
|
492 |
+
"canonxt_23_sub_05.tif": {
|
493 |
+
"subset": "val",
|
494 |
+
"path": "data/columbia/val/au/canonxt_23_sub_05.tif",
|
495 |
+
"label": 0
|
496 |
+
},
|
497 |
+
"canonxt_17_sub_06.tif": {
|
498 |
+
"subset": "val",
|
499 |
+
"path": "data/columbia/val/au/canonxt_17_sub_06.tif",
|
500 |
+
"label": 0
|
501 |
+
},
|
502 |
+
"canonxt_26_sub_05.tif": {
|
503 |
+
"subset": "val",
|
504 |
+
"path": "data/columbia/val/au/canonxt_26_sub_05.tif",
|
505 |
+
"label": 0
|
506 |
+
},
|
507 |
+
"canonxt_38_sub_05.tif": {
|
508 |
+
"subset": "val",
|
509 |
+
"path": "data/columbia/val/au/canonxt_38_sub_05.tif",
|
510 |
+
"label": 0
|
511 |
+
},
|
512 |
+
"canonxt_32_sub_08.tif": {
|
513 |
+
"subset": "val",
|
514 |
+
"path": "data/columbia/val/au/canonxt_32_sub_08.tif",
|
515 |
+
"label": 0
|
516 |
+
},
|
517 |
+
"nikond70_05_sub_02.tif": {
|
518 |
+
"subset": "val",
|
519 |
+
"path": "data/columbia/val/au/nikond70_05_sub_02.tif",
|
520 |
+
"label": 0
|
521 |
+
},
|
522 |
+
"canonxt_02_sub_05.tif": {
|
523 |
+
"subset": "val",
|
524 |
+
"path": "data/columbia/val/au/canonxt_02_sub_05.tif",
|
525 |
+
"label": 0
|
526 |
+
},
|
527 |
+
"canonxt_17_sub_03.tif": {
|
528 |
+
"subset": "val",
|
529 |
+
"path": "data/columbia/val/au/canonxt_17_sub_03.tif",
|
530 |
+
"label": 0
|
531 |
+
},
|
532 |
+
"nikond70_08_sub_02.tif": {
|
533 |
+
"subset": "val",
|
534 |
+
"path": "data/columbia/val/au/nikond70_08_sub_02.tif",
|
535 |
+
"label": 0
|
536 |
+
},
|
537 |
+
"nikond70_08_sub_05.tif": {
|
538 |
+
"subset": "val",
|
539 |
+
"path": "data/columbia/val/au/nikond70_08_sub_05.tif",
|
540 |
+
"label": 0
|
541 |
+
},
|
542 |
+
"canonxt_14_sub_03.tif": {
|
543 |
+
"subset": "val",
|
544 |
+
"path": "data/columbia/val/au/canonxt_14_sub_03.tif",
|
545 |
+
"label": 0
|
546 |
+
},
|
547 |
+
"canonxt_20_sub_07.tif": {
|
548 |
+
"subset": "val",
|
549 |
+
"path": "data/columbia/val/au/canonxt_20_sub_07.tif",
|
550 |
+
"label": 0
|
551 |
+
},
|
552 |
+
"nikond70_02_sub_04.tif": {
|
553 |
+
"subset": "val",
|
554 |
+
"path": "data/columbia/val/au/nikond70_02_sub_04.tif",
|
555 |
+
"label": 0
|
556 |
+
},
|
557 |
+
"canonxt_23_sub_06.tif": {
|
558 |
+
"subset": "val",
|
559 |
+
"path": "data/columbia/val/au/canonxt_23_sub_06.tif",
|
560 |
+
"label": 0
|
561 |
+
},
|
562 |
+
"canonxt_20_sub_02.tif": {
|
563 |
+
"subset": "val",
|
564 |
+
"path": "data/columbia/val/au/canonxt_20_sub_02.tif",
|
565 |
+
"label": 0
|
566 |
+
},
|
567 |
+
"nikond70_11_sub_09.tif": {
|
568 |
+
"subset": "val",
|
569 |
+
"path": "data/columbia/val/au/nikond70_11_sub_09.tif",
|
570 |
+
"label": 0
|
571 |
+
},
|
572 |
+
"canong3_08_sub_07.tif": {
|
573 |
+
"subset": "val",
|
574 |
+
"path": "data/columbia/val/au/canong3_08_sub_07.tif",
|
575 |
+
"label": 0
|
576 |
+
},
|
577 |
+
"canonxt_05_sub_08.tif": {
|
578 |
+
"subset": "val",
|
579 |
+
"path": "data/columbia/val/au/canonxt_05_sub_08.tif",
|
580 |
+
"label": 0
|
581 |
+
},
|
582 |
+
"canong3_05_sub_05.tif": {
|
583 |
+
"subset": "val",
|
584 |
+
"path": "data/columbia/val/au/canong3_05_sub_05.tif",
|
585 |
+
"label": 0
|
586 |
+
},
|
587 |
+
"canonxt_17_sub_05.tif": {
|
588 |
+
"subset": "val",
|
589 |
+
"path": "data/columbia/val/au/canonxt_17_sub_05.tif",
|
590 |
+
"label": 0
|
591 |
+
},
|
592 |
+
"canong3_08_sub_03.tif": {
|
593 |
+
"subset": "val",
|
594 |
+
"path": "data/columbia/val/au/canong3_08_sub_03.tif",
|
595 |
+
"label": 0
|
596 |
+
},
|
597 |
+
"canonxt_20_sub_08.tif": {
|
598 |
+
"subset": "val",
|
599 |
+
"path": "data/columbia/val/au/canonxt_20_sub_08.tif",
|
600 |
+
"label": 0
|
601 |
+
},
|
602 |
+
"canonxt_11_sub_05.tif": {
|
603 |
+
"subset": "val",
|
604 |
+
"path": "data/columbia/val/au/canonxt_11_sub_05.tif",
|
605 |
+
"label": 0
|
606 |
+
},
|
607 |
+
"nikond70_11_sub_03.tif": {
|
608 |
+
"subset": "val",
|
609 |
+
"path": "data/columbia/val/au/nikond70_11_sub_03.tif",
|
610 |
+
"label": 0
|
611 |
+
},
|
612 |
+
"canong3_05_sub_02.tif": {
|
613 |
+
"subset": "val",
|
614 |
+
"path": "data/columbia/val/au/canong3_05_sub_02.tif",
|
615 |
+
"label": 0
|
616 |
+
},
|
617 |
+
"canonxt_11_sub_02.tif": {
|
618 |
+
"subset": "val",
|
619 |
+
"path": "data/columbia/val/au/canonxt_11_sub_02.tif",
|
620 |
+
"label": 0
|
621 |
+
},
|
622 |
+
"nikond70_11_sub_04.tif": {
|
623 |
+
"subset": "val",
|
624 |
+
"path": "data/columbia/val/au/nikond70_11_sub_04.tif",
|
625 |
+
"label": 0
|
626 |
+
},
|
627 |
+
"nikond70_11_sub_07.tif": {
|
628 |
+
"subset": "val",
|
629 |
+
"path": "data/columbia/val/au/nikond70_11_sub_07.tif",
|
630 |
+
"label": 0
|
631 |
+
},
|
632 |
+
"canong3_08_sub_05.tif": {
|
633 |
+
"subset": "val",
|
634 |
+
"path": "data/columbia/val/au/canong3_08_sub_05.tif",
|
635 |
+
"label": 0
|
636 |
+
},
|
637 |
+
"kodakdcs330_01_sub_01.tif": {
|
638 |
+
"subset": "val",
|
639 |
+
"path": "data/columbia/val/au/kodakdcs330_01_sub_01.tif",
|
640 |
+
"label": 0
|
641 |
+
},
|
642 |
+
"nikond70_08_sub_03.tif": {
|
643 |
+
"subset": "val",
|
644 |
+
"path": "data/columbia/val/au/nikond70_08_sub_03.tif",
|
645 |
+
"label": 0
|
646 |
+
},
|
647 |
+
"canonxt_32_sub_04.tif": {
|
648 |
+
"subset": "val",
|
649 |
+
"path": "data/columbia/val/au/canonxt_32_sub_04.tif",
|
650 |
+
"label": 0
|
651 |
+
},
|
652 |
+
"canonxt_29_sub_05.tif": {
|
653 |
+
"subset": "val",
|
654 |
+
"path": "data/columbia/val/au/canonxt_29_sub_05.tif",
|
655 |
+
"label": 0
|
656 |
+
},
|
657 |
+
"canong3_02_sub_03.tif": {
|
658 |
+
"subset": "val",
|
659 |
+
"path": "data/columbia/val/au/canong3_02_sub_03.tif",
|
660 |
+
"label": 0
|
661 |
+
},
|
662 |
+
"canonxt_14_sub_04.tif": {
|
663 |
+
"subset": "val",
|
664 |
+
"path": "data/columbia/val/au/canonxt_14_sub_04.tif",
|
665 |
+
"label": 0
|
666 |
+
},
|
667 |
+
"nikond70_08_sub_07.tif": {
|
668 |
+
"subset": "val",
|
669 |
+
"path": "data/columbia/val/au/nikond70_08_sub_07.tif",
|
670 |
+
"label": 0
|
671 |
+
},
|
672 |
+
"canong3_02_sub_04.tif": {
|
673 |
+
"subset": "val",
|
674 |
+
"path": "data/columbia/val/au/canong3_02_sub_04.tif",
|
675 |
+
"label": 0
|
676 |
+
},
|
677 |
+
"canonxt_38_sub_07.tif": {
|
678 |
+
"subset": "val",
|
679 |
+
"path": "data/columbia/val/au/canonxt_38_sub_07.tif",
|
680 |
+
"label": 0
|
681 |
+
},
|
682 |
+
"canonxt_29_sub_08.tif": {
|
683 |
+
"subset": "val",
|
684 |
+
"path": "data/columbia/val/au/canonxt_29_sub_08.tif",
|
685 |
+
"label": 0
|
686 |
+
},
|
687 |
+
"canong3_08_sub_06.tif": {
|
688 |
+
"subset": "val",
|
689 |
+
"path": "data/columbia/val/au/canong3_08_sub_06.tif",
|
690 |
+
"label": 0
|
691 |
+
},
|
692 |
+
"canonxt_35_sub_06.tif": {
|
693 |
+
"subset": "val",
|
694 |
+
"path": "data/columbia/val/au/canonxt_35_sub_06.tif",
|
695 |
+
"label": 0
|
696 |
+
},
|
697 |
+
"canonxt_35_sub_01.tif": {
|
698 |
+
"subset": "val",
|
699 |
+
"path": "data/columbia/val/au/canonxt_35_sub_01.tif",
|
700 |
+
"label": 0
|
701 |
+
},
|
702 |
+
"canonxt_20_sub_05.tif": {
|
703 |
+
"subset": "val",
|
704 |
+
"path": "data/columbia/val/au/canonxt_20_sub_05.tif",
|
705 |
+
"label": 0
|
706 |
+
},
|
707 |
+
"canonxt_23_sub_02.tif": {
|
708 |
+
"subset": "val",
|
709 |
+
"path": "data/columbia/val/au/canonxt_23_sub_02.tif",
|
710 |
+
"label": 0
|
711 |
+
},
|
712 |
+
"canonxt_35_sub_08.tif": {
|
713 |
+
"subset": "val",
|
714 |
+
"path": "data/columbia/val/au/canonxt_35_sub_08.tif",
|
715 |
+
"label": 0
|
716 |
+
},
|
717 |
+
"nikond70_05_sub_06.tif": {
|
718 |
+
"subset": "val",
|
719 |
+
"path": "data/columbia/val/au/nikond70_05_sub_06.tif",
|
720 |
+
"label": 0
|
721 |
+
},
|
722 |
+
"canong3_02_sub_01.tif": {
|
723 |
+
"subset": "val",
|
724 |
+
"path": "data/columbia/val/au/canong3_02_sub_01.tif",
|
725 |
+
"label": 0
|
726 |
+
},
|
727 |
+
"nikond70_02_sub_03.tif": {
|
728 |
+
"subset": "val",
|
729 |
+
"path": "data/columbia/val/au/nikond70_02_sub_03.tif",
|
730 |
+
"label": 0
|
731 |
+
},
|
732 |
+
"canonxt_29_sub_03.tif": {
|
733 |
+
"subset": "val",
|
734 |
+
"path": "data/columbia/val/au/canonxt_29_sub_03.tif",
|
735 |
+
"label": 0
|
736 |
+
},
|
737 |
+
"kodakdcs330_02_sub_01.tif": {
|
738 |
+
"subset": "val",
|
739 |
+
"path": "data/columbia/val/au/kodakdcs330_02_sub_01.tif",
|
740 |
+
"label": 0
|
741 |
+
},
|
742 |
+
"canonxt_32_sub_06.tif": {
|
743 |
+
"subset": "val",
|
744 |
+
"path": "data/columbia/val/au/canonxt_32_sub_06.tif",
|
745 |
+
"label": 0
|
746 |
+
},
|
747 |
+
"canonxt_23_sub_07.tif": {
|
748 |
+
"subset": "val",
|
749 |
+
"path": "data/columbia/val/au/canonxt_23_sub_07.tif",
|
750 |
+
"label": 0
|
751 |
+
},
|
752 |
+
"canonxt_32_sub_07.tif": {
|
753 |
+
"subset": "val",
|
754 |
+
"path": "data/columbia/val/au/canonxt_32_sub_07.tif",
|
755 |
+
"label": 0
|
756 |
+
},
|
757 |
+
"nikond70_02_sub_08.tif": {
|
758 |
+
"subset": "val",
|
759 |
+
"path": "data/columbia/val/au/nikond70_02_sub_08.tif",
|
760 |
+
"label": 0
|
761 |
+
},
|
762 |
+
"canonxt_26_sub_01.tif": {
|
763 |
+
"subset": "val",
|
764 |
+
"path": "data/columbia/val/au/canonxt_26_sub_01.tif",
|
765 |
+
"label": 0
|
766 |
+
},
|
767 |
+
"canonxt_02_sub_04.tif": {
|
768 |
+
"subset": "val",
|
769 |
+
"path": "data/columbia/val/au/canonxt_02_sub_04.tif",
|
770 |
+
"label": 0
|
771 |
+
},
|
772 |
+
"canonxt_26_sub_09.tif": {
|
773 |
+
"subset": "val",
|
774 |
+
"path": "data/columbia/val/au/canonxt_26_sub_09.tif",
|
775 |
+
"label": 0
|
776 |
+
},
|
777 |
+
"canonxt_23_sub_04.tif": {
|
778 |
+
"subset": "val",
|
779 |
+
"path": "data/columbia/val/au/canonxt_23_sub_04.tif",
|
780 |
+
"label": 0
|
781 |
+
},
|
782 |
+
"canong3_08_sub_01.tif": {
|
783 |
+
"subset": "val",
|
784 |
+
"path": "data/columbia/val/au/canong3_08_sub_01.tif",
|
785 |
+
"label": 0
|
786 |
+
},
|
787 |
+
"canonxt_32_sub_03.tif": {
|
788 |
+
"subset": "val",
|
789 |
+
"path": "data/columbia/val/au/canonxt_32_sub_03.tif",
|
790 |
+
"label": 0
|
791 |
+
},
|
792 |
+
"canong3_08_sub_04.tif": {
|
793 |
+
"subset": "val",
|
794 |
+
"path": "data/columbia/val/au/canong3_08_sub_04.tif",
|
795 |
+
"label": 0
|
796 |
+
},
|
797 |
+
"canonxt_17_sub_07.tif": {
|
798 |
+
"subset": "val",
|
799 |
+
"path": "data/columbia/val/au/canonxt_17_sub_07.tif",
|
800 |
+
"label": 0
|
801 |
+
},
|
802 |
+
"canonxt_14_sub_07.tif": {
|
803 |
+
"subset": "val",
|
804 |
+
"path": "data/columbia/val/au/canonxt_14_sub_07.tif",
|
805 |
+
"label": 0
|
806 |
+
},
|
807 |
+
"canonxt_29_sub_07.tif": {
|
808 |
+
"subset": "val",
|
809 |
+
"path": "data/columbia/val/au/canonxt_29_sub_07.tif",
|
810 |
+
"label": 0
|
811 |
+
},
|
812 |
+
"canong3_02_sub_05.tif": {
|
813 |
+
"subset": "val",
|
814 |
+
"path": "data/columbia/val/au/canong3_02_sub_05.tif",
|
815 |
+
"label": 0
|
816 |
+
},
|
817 |
+
"canonxt_08_sub_04.tif": {
|
818 |
+
"subset": "val",
|
819 |
+
"path": "data/columbia/val/au/canonxt_08_sub_04.tif",
|
820 |
+
"label": 0
|
821 |
+
},
|
822 |
+
"nikond70_08_sub_01.tif": {
|
823 |
+
"subset": "val",
|
824 |
+
"path": "data/columbia/val/au/nikond70_08_sub_01.tif",
|
825 |
+
"label": 0
|
826 |
+
},
|
827 |
+
"canonxt_05_sub_03.tif": {
|
828 |
+
"subset": "val",
|
829 |
+
"path": "data/columbia/val/au/canonxt_05_sub_03.tif",
|
830 |
+
"label": 0
|
831 |
+
},
|
832 |
+
"canonxt_35_sub_05.tif": {
|
833 |
+
"subset": "val",
|
834 |
+
"path": "data/columbia/val/au/canonxt_35_sub_05.tif",
|
835 |
+
"label": 0
|
836 |
+
},
|
837 |
+
"canonxt_32_sub_09.tif": {
|
838 |
+
"subset": "val",
|
839 |
+
"path": "data/columbia/val/au/canonxt_32_sub_09.tif",
|
840 |
+
"label": 0
|
841 |
+
},
|
842 |
+
"nikond70_05_sub_04.tif": {
|
843 |
+
"subset": "val",
|
844 |
+
"path": "data/columbia/val/au/nikond70_05_sub_04.tif",
|
845 |
+
"label": 0
|
846 |
+
},
|
847 |
+
"nikond70_02_sub_09.tif": {
|
848 |
+
"subset": "val",
|
849 |
+
"path": "data/columbia/val/au/nikond70_02_sub_09.tif",
|
850 |
+
"label": 0
|
851 |
+
},
|
852 |
+
"canonxt_14_sub_06.tif": {
|
853 |
+
"subset": "val",
|
854 |
+
"path": "data/columbia/val/au/canonxt_14_sub_06.tif",
|
855 |
+
"label": 0
|
856 |
+
},
|
857 |
+
"canonxt_32_sub_02.tif": {
|
858 |
+
"subset": "val",
|
859 |
+
"path": "data/columbia/val/au/canonxt_32_sub_02.tif",
|
860 |
+
"label": 0
|
861 |
+
},
|
862 |
+
"canonxt_05_sub_04.tif": {
|
863 |
+
"subset": "val",
|
864 |
+
"path": "data/columbia/val/au/canonxt_05_sub_04.tif",
|
865 |
+
"label": 0
|
866 |
+
},
|
867 |
+
"nikond70_08_sub_04.tif": {
|
868 |
+
"subset": "val",
|
869 |
+
"path": "data/columbia/val/au/nikond70_08_sub_04.tif",
|
870 |
+
"label": 0
|
871 |
+
},
|
872 |
+
"canong3_05_sub_08.tif": {
|
873 |
+
"subset": "val",
|
874 |
+
"path": "data/columbia/val/au/canong3_05_sub_08.tif",
|
875 |
+
"label": 0
|
876 |
+
},
|
877 |
+
"canonxt_08_sub_08.tif": {
|
878 |
+
"subset": "val",
|
879 |
+
"path": "data/columbia/val/au/canonxt_08_sub_08.tif",
|
880 |
+
"label": 0
|
881 |
+
},
|
882 |
+
"nikond70_02_sub_05.tif": {
|
883 |
+
"subset": "val",
|
884 |
+
"path": "data/columbia/val/au/nikond70_02_sub_05.tif",
|
885 |
+
"label": 0
|
886 |
+
},
|
887 |
+
"nikond70_05_sub_01.tif": {
|
888 |
+
"subset": "val",
|
889 |
+
"path": "data/columbia/val/au/nikond70_05_sub_01.tif",
|
890 |
+
"label": 0
|
891 |
+
},
|
892 |
+
"canonxt_11_sub_04.tif": {
|
893 |
+
"subset": "val",
|
894 |
+
"path": "data/columbia/val/au/canonxt_11_sub_04.tif",
|
895 |
+
"label": 0
|
896 |
+
},
|
897 |
+
"canonxt_23_sub_08.tif": {
|
898 |
+
"subset": "val",
|
899 |
+
"path": "data/columbia/val/au/canonxt_23_sub_08.tif",
|
900 |
+
"label": 0
|
901 |
+
},
|
902 |
+
"canonxt_02_sub_03.tif": {
|
903 |
+
"subset": "val",
|
904 |
+
"path": "data/columbia/val/au/canonxt_02_sub_03.tif",
|
905 |
+
"label": 0
|
906 |
+
},
|
907 |
+
"nikond70_02_sub_02.tif": {
|
908 |
+
"subset": "val",
|
909 |
+
"path": "data/columbia/val/au/nikond70_02_sub_02.tif",
|
910 |
+
"label": 0
|
911 |
+
},
|
912 |
+
"canonxt_08_sub_09.tif": {
|
913 |
+
"subset": "val",
|
914 |
+
"path": "data/columbia/val/au/canonxt_08_sub_09.tif",
|
915 |
+
"label": 0
|
916 |
+
},
|
917 |
+
"canong3_kodakdcs330_sub_07.tif": {
|
918 |
+
"subset": "val",
|
919 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_07.tif",
|
920 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_07_gt.png",
|
921 |
+
"label": 1
|
922 |
+
},
|
923 |
+
"canong3_canonxt_sub_03.tif": {
|
924 |
+
"subset": "val",
|
925 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_03.tif",
|
926 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_03_gt.png",
|
927 |
+
"label": 1
|
928 |
+
},
|
929 |
+
"nikond70_kodakdcs330_sub_10.tif": {
|
930 |
+
"subset": "val",
|
931 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_10.tif",
|
932 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_10_gt.png",
|
933 |
+
"label": 1
|
934 |
+
},
|
935 |
+
"nikond70_canonxt_sub_26.tif": {
|
936 |
+
"subset": "val",
|
937 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_26.tif",
|
938 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_26_gt.png",
|
939 |
+
"label": 1
|
940 |
+
},
|
941 |
+
"canong3_canonxt_sub_29.tif": {
|
942 |
+
"subset": "val",
|
943 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_29.tif",
|
944 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_29_gt.png",
|
945 |
+
"label": 1
|
946 |
+
},
|
947 |
+
"canong3_kodakdcs330_sub_21.tif": {
|
948 |
+
"subset": "val",
|
949 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_21.tif",
|
950 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_21_gt.png",
|
951 |
+
"label": 1
|
952 |
+
},
|
953 |
+
"canong3_nikond70_sub_15.tif": {
|
954 |
+
"subset": "val",
|
955 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_15.tif",
|
956 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_15_gt.png",
|
957 |
+
"label": 1
|
958 |
+
},
|
959 |
+
"canonxt_kodakdcs330_sub_01.tif": {
|
960 |
+
"subset": "val",
|
961 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_01.tif",
|
962 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_01_gt.png",
|
963 |
+
"label": 1
|
964 |
+
},
|
965 |
+
"canong3_kodakdcs330_sub_30.tif": {
|
966 |
+
"subset": "val",
|
967 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_30.tif",
|
968 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_30_gt.png",
|
969 |
+
"label": 1
|
970 |
+
},
|
971 |
+
"nikond70_kodakdcs330_sub_01.tif": {
|
972 |
+
"subset": "val",
|
973 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_01.tif",
|
974 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_01_gt.png",
|
975 |
+
"label": 1
|
976 |
+
},
|
977 |
+
"canong3_kodakdcs330_sub_20.tif": {
|
978 |
+
"subset": "val",
|
979 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_20.tif",
|
980 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_20_gt.png",
|
981 |
+
"label": 1
|
982 |
+
},
|
983 |
+
"nikond70_kodakdcs330_sub_15.tif": {
|
984 |
+
"subset": "val",
|
985 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_15.tif",
|
986 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_15_gt.png",
|
987 |
+
"label": 1
|
988 |
+
},
|
989 |
+
"nikond70_canonxt_sub_16.tif": {
|
990 |
+
"subset": "val",
|
991 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_16.tif",
|
992 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_16_gt.png",
|
993 |
+
"label": 1
|
994 |
+
},
|
995 |
+
"canong3_nikond70_sub_13.tif": {
|
996 |
+
"subset": "val",
|
997 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_13.tif",
|
998 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_13_gt.png",
|
999 |
+
"label": 1
|
1000 |
+
},
|
1001 |
+
"canong3_canonxt_sub_05.tif": {
|
1002 |
+
"subset": "val",
|
1003 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_05.tif",
|
1004 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_05_gt.png",
|
1005 |
+
"label": 1
|
1006 |
+
},
|
1007 |
+
"canonxt_kodakdcs330_sub_24.tif": {
|
1008 |
+
"subset": "val",
|
1009 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_24.tif",
|
1010 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_24_gt.png",
|
1011 |
+
"label": 1
|
1012 |
+
},
|
1013 |
+
"nikond70_kodakdcs330_sub_20.tif": {
|
1014 |
+
"subset": "val",
|
1015 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_20.tif",
|
1016 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_20_gt.png",
|
1017 |
+
"label": 1
|
1018 |
+
},
|
1019 |
+
"nikond70_kodakdcs330_sub_30.tif": {
|
1020 |
+
"subset": "val",
|
1021 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_30.tif",
|
1022 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_30_gt.png",
|
1023 |
+
"label": 1
|
1024 |
+
},
|
1025 |
+
"nikond70_canonxt_sub_28.tif": {
|
1026 |
+
"subset": "val",
|
1027 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_28.tif",
|
1028 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_28_gt.png",
|
1029 |
+
"label": 1
|
1030 |
+
},
|
1031 |
+
"canonxt_kodakdcs330_sub_04.tif": {
|
1032 |
+
"subset": "val",
|
1033 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_04.tif",
|
1034 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_04_gt.png",
|
1035 |
+
"label": 1
|
1036 |
+
},
|
1037 |
+
"canong3_kodakdcs330_sub_02.tif": {
|
1038 |
+
"subset": "val",
|
1039 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_02.tif",
|
1040 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_02_gt.png",
|
1041 |
+
"label": 1
|
1042 |
+
},
|
1043 |
+
"nikond70_canonxt_sub_04.tif": {
|
1044 |
+
"subset": "val",
|
1045 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_04.tif",
|
1046 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_04_gt.png",
|
1047 |
+
"label": 1
|
1048 |
+
},
|
1049 |
+
"canong3_canonxt_sub_22.tif": {
|
1050 |
+
"subset": "val",
|
1051 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_22.tif",
|
1052 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_22_gt.png",
|
1053 |
+
"label": 1
|
1054 |
+
},
|
1055 |
+
"nikond70_canonxt_sub_09.tif": {
|
1056 |
+
"subset": "val",
|
1057 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_09.tif",
|
1058 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_09_gt.png",
|
1059 |
+
"label": 1
|
1060 |
+
},
|
1061 |
+
"nikond70_canonxt_sub_24.tif": {
|
1062 |
+
"subset": "val",
|
1063 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_24.tif",
|
1064 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_24_gt.png",
|
1065 |
+
"label": 1
|
1066 |
+
},
|
1067 |
+
"canonxt_kodakdcs330_sub_28.tif": {
|
1068 |
+
"subset": "val",
|
1069 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_28.tif",
|
1070 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_28_gt.png",
|
1071 |
+
"label": 1
|
1072 |
+
},
|
1073 |
+
"canonxt_kodakdcs330_sub_02.tif": {
|
1074 |
+
"subset": "val",
|
1075 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_02.tif",
|
1076 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_02_gt.png",
|
1077 |
+
"label": 1
|
1078 |
+
},
|
1079 |
+
"canong3_canonxt_sub_15.tif": {
|
1080 |
+
"subset": "val",
|
1081 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_15.tif",
|
1082 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_15_gt.png",
|
1083 |
+
"label": 1
|
1084 |
+
},
|
1085 |
+
"canong3_canonxt_sub_11.tif": {
|
1086 |
+
"subset": "val",
|
1087 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_11.tif",
|
1088 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_11_gt.png",
|
1089 |
+
"label": 1
|
1090 |
+
},
|
1091 |
+
"nikond70_kodakdcs330_sub_25.tif": {
|
1092 |
+
"subset": "val",
|
1093 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_25.tif",
|
1094 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_25_gt.png",
|
1095 |
+
"label": 1
|
1096 |
+
},
|
1097 |
+
"nikond70_kodakdcs330_sub_04.tif": {
|
1098 |
+
"subset": "val",
|
1099 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_04.tif",
|
1100 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_04_gt.png",
|
1101 |
+
"label": 1
|
1102 |
+
},
|
1103 |
+
"nikond70_kodakdcs330_sub_21.tif": {
|
1104 |
+
"subset": "val",
|
1105 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_21.tif",
|
1106 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_21_gt.png",
|
1107 |
+
"label": 1
|
1108 |
+
},
|
1109 |
+
"canonxt_kodakdcs330_sub_30.tif": {
|
1110 |
+
"subset": "val",
|
1111 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_30.tif",
|
1112 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_30_gt.png",
|
1113 |
+
"label": 1
|
1114 |
+
},
|
1115 |
+
"canong3_nikond70_sub_25.tif": {
|
1116 |
+
"subset": "val",
|
1117 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_25.tif",
|
1118 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_25_gt.png",
|
1119 |
+
"label": 1
|
1120 |
+
},
|
1121 |
+
"canong3_kodakdcs330_sub_22.tif": {
|
1122 |
+
"subset": "val",
|
1123 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_22.tif",
|
1124 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_22_gt.png",
|
1125 |
+
"label": 1
|
1126 |
+
},
|
1127 |
+
"nikond70_canonxt_sub_25.tif": {
|
1128 |
+
"subset": "val",
|
1129 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_25.tif",
|
1130 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_25_gt.png",
|
1131 |
+
"label": 1
|
1132 |
+
},
|
1133 |
+
"canong3_canonxt_sub_18.tif": {
|
1134 |
+
"subset": "val",
|
1135 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_18.tif",
|
1136 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_18_gt.png",
|
1137 |
+
"label": 1
|
1138 |
+
},
|
1139 |
+
"canong3_nikond70_sub_11.tif": {
|
1140 |
+
"subset": "val",
|
1141 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_11.tif",
|
1142 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_11_gt.png",
|
1143 |
+
"label": 1
|
1144 |
+
},
|
1145 |
+
"canong3_canonxt_sub_17.tif": {
|
1146 |
+
"subset": "val",
|
1147 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_17.tif",
|
1148 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_17_gt.png",
|
1149 |
+
"label": 1
|
1150 |
+
},
|
1151 |
+
"nikond70_canonxt_sub_17.tif": {
|
1152 |
+
"subset": "val",
|
1153 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_17.tif",
|
1154 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_17_gt.png",
|
1155 |
+
"label": 1
|
1156 |
+
},
|
1157 |
+
"canong3_kodakdcs330_sub_03.tif": {
|
1158 |
+
"subset": "val",
|
1159 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_03.tif",
|
1160 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_03_gt.png",
|
1161 |
+
"label": 1
|
1162 |
+
},
|
1163 |
+
"canong3_kodakdcs330_sub_13.tif": {
|
1164 |
+
"subset": "val",
|
1165 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_13.tif",
|
1166 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_13_gt.png",
|
1167 |
+
"label": 1
|
1168 |
+
},
|
1169 |
+
"nikond70_canonxt_sub_21.tif": {
|
1170 |
+
"subset": "val",
|
1171 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_21.tif",
|
1172 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_21_gt.png",
|
1173 |
+
"label": 1
|
1174 |
+
},
|
1175 |
+
"nikond70_kodakdcs330_sub_07.tif": {
|
1176 |
+
"subset": "val",
|
1177 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_07.tif",
|
1178 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_07_gt.png",
|
1179 |
+
"label": 1
|
1180 |
+
},
|
1181 |
+
"canonxt_kodakdcs330_sub_08.tif": {
|
1182 |
+
"subset": "val",
|
1183 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_08.tif",
|
1184 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_08_gt.png",
|
1185 |
+
"label": 1
|
1186 |
+
},
|
1187 |
+
"canong3_nikond70_sub_01.tif": {
|
1188 |
+
"subset": "val",
|
1189 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_01.tif",
|
1190 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_01_gt.png",
|
1191 |
+
"label": 1
|
1192 |
+
},
|
1193 |
+
"nikond70_canonxt_sub_19.tif": {
|
1194 |
+
"subset": "val",
|
1195 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_19.tif",
|
1196 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_19_gt.png",
|
1197 |
+
"label": 1
|
1198 |
+
},
|
1199 |
+
"nikond70_canonxt_sub_08.tif": {
|
1200 |
+
"subset": "val",
|
1201 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_08.tif",
|
1202 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_08_gt.png",
|
1203 |
+
"label": 1
|
1204 |
+
},
|
1205 |
+
"nikond70_kodakdcs330_sub_14.tif": {
|
1206 |
+
"subset": "val",
|
1207 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_14.tif",
|
1208 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_14_gt.png",
|
1209 |
+
"label": 1
|
1210 |
+
},
|
1211 |
+
"nikond70_kodakdcs330_sub_29.tif": {
|
1212 |
+
"subset": "val",
|
1213 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_29.tif",
|
1214 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_29_gt.png",
|
1215 |
+
"label": 1
|
1216 |
+
},
|
1217 |
+
"canonxt_kodakdcs330_sub_09.tif": {
|
1218 |
+
"subset": "val",
|
1219 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_09.tif",
|
1220 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_09_gt.png",
|
1221 |
+
"label": 1
|
1222 |
+
},
|
1223 |
+
"canong3_kodakdcs330_sub_04.tif": {
|
1224 |
+
"subset": "val",
|
1225 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_04.tif",
|
1226 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_04_gt.png",
|
1227 |
+
"label": 1
|
1228 |
+
},
|
1229 |
+
"canong3_canonxt_sub_06.tif": {
|
1230 |
+
"subset": "val",
|
1231 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_06.tif",
|
1232 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_06_gt.png",
|
1233 |
+
"label": 1
|
1234 |
+
},
|
1235 |
+
"canong3_nikond70_sub_22.tif": {
|
1236 |
+
"subset": "val",
|
1237 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_22.tif",
|
1238 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_22_gt.png",
|
1239 |
+
"label": 1
|
1240 |
+
},
|
1241 |
+
"canong3_kodakdcs330_sub_09.tif": {
|
1242 |
+
"subset": "val",
|
1243 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_09.tif",
|
1244 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_09_gt.png",
|
1245 |
+
"label": 1
|
1246 |
+
},
|
1247 |
+
"nikond70_canonxt_sub_20.tif": {
|
1248 |
+
"subset": "val",
|
1249 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_20.tif",
|
1250 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_20_gt.png",
|
1251 |
+
"label": 1
|
1252 |
+
},
|
1253 |
+
"canong3_canonxt_sub_10.tif": {
|
1254 |
+
"subset": "val",
|
1255 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_10.tif",
|
1256 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_10_gt.png",
|
1257 |
+
"label": 1
|
1258 |
+
},
|
1259 |
+
"canong3_nikond70_sub_18.tif": {
|
1260 |
+
"subset": "val",
|
1261 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_18.tif",
|
1262 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_18_gt.png",
|
1263 |
+
"label": 1
|
1264 |
+
},
|
1265 |
+
"canong3_kodakdcs330_sub_28.tif": {
|
1266 |
+
"subset": "val",
|
1267 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_28.tif",
|
1268 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_28_gt.png",
|
1269 |
+
"label": 1
|
1270 |
+
},
|
1271 |
+
"canonxt_kodakdcs330_sub_25.tif": {
|
1272 |
+
"subset": "val",
|
1273 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_25.tif",
|
1274 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_25_gt.png",
|
1275 |
+
"label": 1
|
1276 |
+
},
|
1277 |
+
"canong3_nikond70_sub_03.tif": {
|
1278 |
+
"subset": "val",
|
1279 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_03.tif",
|
1280 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_03_gt.png",
|
1281 |
+
"label": 1
|
1282 |
+
},
|
1283 |
+
"canong3_nikond70_sub_12.tif": {
|
1284 |
+
"subset": "val",
|
1285 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_12.tif",
|
1286 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_12_gt.png",
|
1287 |
+
"label": 1
|
1288 |
+
},
|
1289 |
+
"canong3_canonxt_sub_09.tif": {
|
1290 |
+
"subset": "val",
|
1291 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_09.tif",
|
1292 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_09_gt.png",
|
1293 |
+
"label": 1
|
1294 |
+
},
|
1295 |
+
"nikond70_canonxt_sub_22.tif": {
|
1296 |
+
"subset": "val",
|
1297 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_22.tif",
|
1298 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_22_gt.png",
|
1299 |
+
"label": 1
|
1300 |
+
},
|
1301 |
+
"canong3_canonxt_sub_16.tif": {
|
1302 |
+
"subset": "val",
|
1303 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_16.tif",
|
1304 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_16_gt.png",
|
1305 |
+
"label": 1
|
1306 |
+
},
|
1307 |
+
"canong3_canonxt_sub_19.tif": {
|
1308 |
+
"subset": "val",
|
1309 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_19.tif",
|
1310 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_19_gt.png",
|
1311 |
+
"label": 1
|
1312 |
+
},
|
1313 |
+
"canong3_kodakdcs330_sub_06.tif": {
|
1314 |
+
"subset": "val",
|
1315 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_06.tif",
|
1316 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_06_gt.png",
|
1317 |
+
"label": 1
|
1318 |
+
},
|
1319 |
+
"canonxt_kodakdcs330_sub_26.tif": {
|
1320 |
+
"subset": "val",
|
1321 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_26.tif",
|
1322 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_26_gt.png",
|
1323 |
+
"label": 1
|
1324 |
+
},
|
1325 |
+
"canong3_nikond70_sub_09.tif": {
|
1326 |
+
"subset": "val",
|
1327 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_09.tif",
|
1328 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_09_gt.png",
|
1329 |
+
"label": 1
|
1330 |
+
},
|
1331 |
+
"nikond70_kodakdcs330_sub_28.tif": {
|
1332 |
+
"subset": "val",
|
1333 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_28.tif",
|
1334 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_28_gt.png",
|
1335 |
+
"label": 1
|
1336 |
+
},
|
1337 |
+
"nikond70_kodakdcs330_sub_26.tif": {
|
1338 |
+
"subset": "val",
|
1339 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_26.tif",
|
1340 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_26_gt.png",
|
1341 |
+
"label": 1
|
1342 |
+
},
|
1343 |
+
"nikond70_canonxt_sub_11.tif": {
|
1344 |
+
"subset": "val",
|
1345 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_11.tif",
|
1346 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_11_gt.png",
|
1347 |
+
"label": 1
|
1348 |
+
},
|
1349 |
+
"canong3_kodakdcs330_sub_15.tif": {
|
1350 |
+
"subset": "val",
|
1351 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_15.tif",
|
1352 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_15_gt.png",
|
1353 |
+
"label": 1
|
1354 |
+
},
|
1355 |
+
"canonxt_kodakdcs330_sub_22.tif": {
|
1356 |
+
"subset": "val",
|
1357 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_22.tif",
|
1358 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_22_gt.png",
|
1359 |
+
"label": 1
|
1360 |
+
},
|
1361 |
+
"nikond70_canonxt_sub_23.tif": {
|
1362 |
+
"subset": "val",
|
1363 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_23.tif",
|
1364 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_23_gt.png",
|
1365 |
+
"label": 1
|
1366 |
+
},
|
1367 |
+
"canonxt_kodakdcs330_sub_27.tif": {
|
1368 |
+
"subset": "val",
|
1369 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_27.tif",
|
1370 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_27_gt.png",
|
1371 |
+
"label": 1
|
1372 |
+
},
|
1373 |
+
"nikond70_canonxt_sub_05.tif": {
|
1374 |
+
"subset": "val",
|
1375 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_05.tif",
|
1376 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_05_gt.png",
|
1377 |
+
"label": 1
|
1378 |
+
},
|
1379 |
+
"nikond70_kodakdcs330_sub_22.tif": {
|
1380 |
+
"subset": "val",
|
1381 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_22.tif",
|
1382 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_22_gt.png",
|
1383 |
+
"label": 1
|
1384 |
+
},
|
1385 |
+
"nikond70_kodakdcs330_sub_23.tif": {
|
1386 |
+
"subset": "val",
|
1387 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_23.tif",
|
1388 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_23_gt.png",
|
1389 |
+
"label": 1
|
1390 |
+
},
|
1391 |
+
"canong3_canonxt_sub_23.tif": {
|
1392 |
+
"subset": "val",
|
1393 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_23.tif",
|
1394 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_23_gt.png",
|
1395 |
+
"label": 1
|
1396 |
+
},
|
1397 |
+
"canong3_kodakdcs330_sub_27.tif": {
|
1398 |
+
"subset": "val",
|
1399 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_27.tif",
|
1400 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_27_gt.png",
|
1401 |
+
"label": 1
|
1402 |
+
},
|
1403 |
+
"canong3_canonxt_sub_04.tif": {
|
1404 |
+
"subset": "val",
|
1405 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_04.tif",
|
1406 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_04_gt.png",
|
1407 |
+
"label": 1
|
1408 |
+
},
|
1409 |
+
"canong3_nikond70_sub_07.tif": {
|
1410 |
+
"subset": "val",
|
1411 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_07.tif",
|
1412 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_07_gt.png",
|
1413 |
+
"label": 1
|
1414 |
+
},
|
1415 |
+
"canong3_kodakdcs330_sub_10.tif": {
|
1416 |
+
"subset": "val",
|
1417 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_10.tif",
|
1418 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_10_gt.png",
|
1419 |
+
"label": 1
|
1420 |
+
},
|
1421 |
+
"canong3_kodakdcs330_sub_18.tif": {
|
1422 |
+
"subset": "val",
|
1423 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_18.tif",
|
1424 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_18_gt.png",
|
1425 |
+
"label": 1
|
1426 |
+
},
|
1427 |
+
"nikond70_kodakdcs330_sub_03.tif": {
|
1428 |
+
"subset": "val",
|
1429 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_03.tif",
|
1430 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_03_gt.png",
|
1431 |
+
"label": 1
|
1432 |
+
},
|
1433 |
+
"canong3_kodakdcs330_sub_24.tif": {
|
1434 |
+
"subset": "val",
|
1435 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_24.tif",
|
1436 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_24_gt.png",
|
1437 |
+
"label": 1
|
1438 |
+
},
|
1439 |
+
"canonxt_kodakdcs330_sub_07.tif": {
|
1440 |
+
"subset": "val",
|
1441 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_07.tif",
|
1442 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_07_gt.png",
|
1443 |
+
"label": 1
|
1444 |
+
},
|
1445 |
+
"canonxt_kodakdcs330_sub_14.tif": {
|
1446 |
+
"subset": "val",
|
1447 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_14.tif",
|
1448 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_14_gt.png",
|
1449 |
+
"label": 1
|
1450 |
+
},
|
1451 |
+
"canong3_nikond70_sub_21.tif": {
|
1452 |
+
"subset": "val",
|
1453 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_21.tif",
|
1454 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_21_gt.png",
|
1455 |
+
"label": 1
|
1456 |
+
},
|
1457 |
+
"nikond70_canonxt_sub_30.tif": {
|
1458 |
+
"subset": "val",
|
1459 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_30.tif",
|
1460 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_30_gt.png",
|
1461 |
+
"label": 1
|
1462 |
+
},
|
1463 |
+
"nikond70_kodakdcs330_sub_05.tif": {
|
1464 |
+
"subset": "val",
|
1465 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_05.tif",
|
1466 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_05_gt.png",
|
1467 |
+
"label": 1
|
1468 |
+
},
|
1469 |
+
"canonxt_kodakdcs330_sub_23.tif": {
|
1470 |
+
"subset": "val",
|
1471 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_23.tif",
|
1472 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_23_gt.png",
|
1473 |
+
"label": 1
|
1474 |
+
},
|
1475 |
+
"nikond70_canonxt_sub_27.tif": {
|
1476 |
+
"subset": "val",
|
1477 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_27.tif",
|
1478 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_27_gt.png",
|
1479 |
+
"label": 1
|
1480 |
+
},
|
1481 |
+
"canong3_canonxt_sub_27.tif": {
|
1482 |
+
"subset": "val",
|
1483 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_27.tif",
|
1484 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_27_gt.png",
|
1485 |
+
"label": 1
|
1486 |
+
},
|
1487 |
+
"nikond70_kodakdcs330_sub_24.tif": {
|
1488 |
+
"subset": "val",
|
1489 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_24.tif",
|
1490 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_24_gt.png",
|
1491 |
+
"label": 1
|
1492 |
+
},
|
1493 |
+
"canong3_canonxt_sub_24.tif": {
|
1494 |
+
"subset": "val",
|
1495 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_24.tif",
|
1496 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_24_gt.png",
|
1497 |
+
"label": 1
|
1498 |
+
},
|
1499 |
+
"canong3_canonxt_sub_08.tif": {
|
1500 |
+
"subset": "val",
|
1501 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_08.tif",
|
1502 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_08_gt.png",
|
1503 |
+
"label": 1
|
1504 |
+
},
|
1505 |
+
"nikond70_canonxt_sub_06.tif": {
|
1506 |
+
"subset": "val",
|
1507 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_06.tif",
|
1508 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_06_gt.png",
|
1509 |
+
"label": 1
|
1510 |
+
},
|
1511 |
+
"nikond70_kodakdcs330_sub_27.tif": {
|
1512 |
+
"subset": "val",
|
1513 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_27.tif",
|
1514 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_27_gt.png",
|
1515 |
+
"label": 1
|
1516 |
+
},
|
1517 |
+
"canong3_kodakdcs330_sub_29.tif": {
|
1518 |
+
"subset": "val",
|
1519 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_29.tif",
|
1520 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_29_gt.png",
|
1521 |
+
"label": 1
|
1522 |
+
},
|
1523 |
+
"nikond70_canonxt_sub_12.tif": {
|
1524 |
+
"subset": "val",
|
1525 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_12.tif",
|
1526 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_12_gt.png",
|
1527 |
+
"label": 1
|
1528 |
+
},
|
1529 |
+
"nikond70_canonxt_sub_01.tif": {
|
1530 |
+
"subset": "val",
|
1531 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_01.tif",
|
1532 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_01_gt.png",
|
1533 |
+
"label": 1
|
1534 |
+
},
|
1535 |
+
"canonxt_kodakdcs330_sub_10.tif": {
|
1536 |
+
"subset": "val",
|
1537 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_10.tif",
|
1538 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_10_gt.png",
|
1539 |
+
"label": 1
|
1540 |
+
},
|
1541 |
+
"canong3_kodakdcs330_sub_14.tif": {
|
1542 |
+
"subset": "val",
|
1543 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_14.tif",
|
1544 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_14_gt.png",
|
1545 |
+
"label": 1
|
1546 |
+
},
|
1547 |
+
"canonxt_kodakdcs330_sub_16.tif": {
|
1548 |
+
"subset": "val",
|
1549 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_16.tif",
|
1550 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_16_gt.png",
|
1551 |
+
"label": 1
|
1552 |
+
},
|
1553 |
+
"nikond70_kodakdcs330_sub_12.tif": {
|
1554 |
+
"subset": "val",
|
1555 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_12.tif",
|
1556 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_12_gt.png",
|
1557 |
+
"label": 1
|
1558 |
+
},
|
1559 |
+
"nikond70_kodakdcs330_sub_08.tif": {
|
1560 |
+
"subset": "val",
|
1561 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_08.tif",
|
1562 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_08_gt.png",
|
1563 |
+
"label": 1
|
1564 |
+
},
|
1565 |
+
"nikond70_kodakdcs330_sub_19.tif": {
|
1566 |
+
"subset": "val",
|
1567 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_19.tif",
|
1568 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_19_gt.png",
|
1569 |
+
"label": 1
|
1570 |
+
},
|
1571 |
+
"nikond70_canonxt_sub_03.tif": {
|
1572 |
+
"subset": "val",
|
1573 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_03.tif",
|
1574 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_03_gt.png",
|
1575 |
+
"label": 1
|
1576 |
+
},
|
1577 |
+
"nikond70_kodakdcs330_sub_09.tif": {
|
1578 |
+
"subset": "val",
|
1579 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_09.tif",
|
1580 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_09_gt.png",
|
1581 |
+
"label": 1
|
1582 |
+
},
|
1583 |
+
"canonxt_kodakdcs330_sub_17.tif": {
|
1584 |
+
"subset": "val",
|
1585 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_17.tif",
|
1586 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_17_gt.png",
|
1587 |
+
"label": 1
|
1588 |
+
},
|
1589 |
+
"nikond70_kodakdcs330_sub_02.tif": {
|
1590 |
+
"subset": "val",
|
1591 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_02.tif",
|
1592 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_02_gt.png",
|
1593 |
+
"label": 1
|
1594 |
+
},
|
1595 |
+
"nikond70_kodakdcs330_sub_16.tif": {
|
1596 |
+
"subset": "val",
|
1597 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_16.tif",
|
1598 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_16_gt.png",
|
1599 |
+
"label": 1
|
1600 |
+
},
|
1601 |
+
"canong3_canonxt_sub_20.tif": {
|
1602 |
+
"subset": "val",
|
1603 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_20.tif",
|
1604 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_20_gt.png",
|
1605 |
+
"label": 1
|
1606 |
+
},
|
1607 |
+
"canong3_kodakdcs330_sub_17.tif": {
|
1608 |
+
"subset": "val",
|
1609 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_17.tif",
|
1610 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_17_gt.png",
|
1611 |
+
"label": 1
|
1612 |
+
},
|
1613 |
+
"canong3_kodakdcs330_sub_16.tif": {
|
1614 |
+
"subset": "val",
|
1615 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_16.tif",
|
1616 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_16_gt.png",
|
1617 |
+
"label": 1
|
1618 |
+
},
|
1619 |
+
"canong3_kodakdcs330_sub_23.tif": {
|
1620 |
+
"subset": "val",
|
1621 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_23.tif",
|
1622 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_23_gt.png",
|
1623 |
+
"label": 1
|
1624 |
+
},
|
1625 |
+
"canonxt_kodakdcs330_sub_05.tif": {
|
1626 |
+
"subset": "val",
|
1627 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_05.tif",
|
1628 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_05_gt.png",
|
1629 |
+
"label": 1
|
1630 |
+
},
|
1631 |
+
"canong3_nikond70_sub_20.tif": {
|
1632 |
+
"subset": "val",
|
1633 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_20.tif",
|
1634 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_20_gt.png",
|
1635 |
+
"label": 1
|
1636 |
+
},
|
1637 |
+
"nikond70_kodakdcs330_sub_18.tif": {
|
1638 |
+
"subset": "val",
|
1639 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_18.tif",
|
1640 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_18_gt.png",
|
1641 |
+
"label": 1
|
1642 |
+
},
|
1643 |
+
"canong3_nikond70_sub_30.tif": {
|
1644 |
+
"subset": "val",
|
1645 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_30.tif",
|
1646 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_30_gt.png",
|
1647 |
+
"label": 1
|
1648 |
+
},
|
1649 |
+
"canong3_canonxt_sub_21.tif": {
|
1650 |
+
"subset": "val",
|
1651 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_21.tif",
|
1652 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_21_gt.png",
|
1653 |
+
"label": 1
|
1654 |
+
},
|
1655 |
+
"canong3_canonxt_sub_12.tif": {
|
1656 |
+
"subset": "val",
|
1657 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_12.tif",
|
1658 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_12_gt.png",
|
1659 |
+
"label": 1
|
1660 |
+
},
|
1661 |
+
"canonxt_kodakdcs330_sub_13.tif": {
|
1662 |
+
"subset": "val",
|
1663 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_13.tif",
|
1664 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_13_gt.png",
|
1665 |
+
"label": 1
|
1666 |
+
},
|
1667 |
+
"nikond70_canonxt_sub_18.tif": {
|
1668 |
+
"subset": "val",
|
1669 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_18.tif",
|
1670 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_18_gt.png",
|
1671 |
+
"label": 1
|
1672 |
+
},
|
1673 |
+
"canong3_canonxt_sub_07.tif": {
|
1674 |
+
"subset": "val",
|
1675 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_07.tif",
|
1676 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_07_gt.png",
|
1677 |
+
"label": 1
|
1678 |
+
},
|
1679 |
+
"nikond70_canonxt_sub_07.tif": {
|
1680 |
+
"subset": "val",
|
1681 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_07.tif",
|
1682 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_07_gt.png",
|
1683 |
+
"label": 1
|
1684 |
+
},
|
1685 |
+
"canong3_nikond70_sub_17.tif": {
|
1686 |
+
"subset": "val",
|
1687 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_17.tif",
|
1688 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_17_gt.png",
|
1689 |
+
"label": 1
|
1690 |
+
},
|
1691 |
+
"canonxt_kodakdcs330_sub_11.tif": {
|
1692 |
+
"subset": "val",
|
1693 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_11.tif",
|
1694 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_11_gt.png",
|
1695 |
+
"label": 1
|
1696 |
+
},
|
1697 |
+
"canong3_nikond70_sub_06.tif": {
|
1698 |
+
"subset": "val",
|
1699 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_06.tif",
|
1700 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_06_gt.png",
|
1701 |
+
"label": 1
|
1702 |
+
},
|
1703 |
+
"nikond70_canonxt_sub_29.tif": {
|
1704 |
+
"subset": "val",
|
1705 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_29.tif",
|
1706 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_29_gt.png",
|
1707 |
+
"label": 1
|
1708 |
+
},
|
1709 |
+
"canong3_nikond70_sub_14.tif": {
|
1710 |
+
"subset": "val",
|
1711 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_14.tif",
|
1712 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_14_gt.png",
|
1713 |
+
"label": 1
|
1714 |
+
},
|
1715 |
+
"nikond70_canonxt_sub_14.tif": {
|
1716 |
+
"subset": "val",
|
1717 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_14.tif",
|
1718 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_14_gt.png",
|
1719 |
+
"label": 1
|
1720 |
+
},
|
1721 |
+
"canong3_kodakdcs330_sub_01.tif": {
|
1722 |
+
"subset": "val",
|
1723 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_01.tif",
|
1724 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_01_gt.png",
|
1725 |
+
"label": 1
|
1726 |
+
},
|
1727 |
+
"canong3_nikond70_sub_26.tif": {
|
1728 |
+
"subset": "val",
|
1729 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_26.tif",
|
1730 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_26_gt.png",
|
1731 |
+
"label": 1
|
1732 |
+
},
|
1733 |
+
"canonxt_kodakdcs330_sub_29.tif": {
|
1734 |
+
"subset": "val",
|
1735 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_29.tif",
|
1736 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_29_gt.png",
|
1737 |
+
"label": 1
|
1738 |
+
},
|
1739 |
+
"canong3_nikond70_sub_24.tif": {
|
1740 |
+
"subset": "val",
|
1741 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_24.tif",
|
1742 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_24_gt.png",
|
1743 |
+
"label": 1
|
1744 |
+
},
|
1745 |
+
"nikond70_kodakdcs330_sub_13.tif": {
|
1746 |
+
"subset": "val",
|
1747 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_13.tif",
|
1748 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_13_gt.png",
|
1749 |
+
"label": 1
|
1750 |
+
},
|
1751 |
+
"canong3_canonxt_sub_30.tif": {
|
1752 |
+
"subset": "val",
|
1753 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_30.tif",
|
1754 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_30_gt.png",
|
1755 |
+
"label": 1
|
1756 |
+
},
|
1757 |
+
"nikond70_kodakdcs330_sub_17.tif": {
|
1758 |
+
"subset": "val",
|
1759 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_17.tif",
|
1760 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_17_gt.png",
|
1761 |
+
"label": 1
|
1762 |
+
},
|
1763 |
+
"nikond70_canonxt_sub_13.tif": {
|
1764 |
+
"subset": "val",
|
1765 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_13.tif",
|
1766 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_13_gt.png",
|
1767 |
+
"label": 1
|
1768 |
+
},
|
1769 |
+
"canong3_nikond70_sub_19.tif": {
|
1770 |
+
"subset": "val",
|
1771 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_19.tif",
|
1772 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_19_gt.png",
|
1773 |
+
"label": 1
|
1774 |
+
},
|
1775 |
+
"canong3_nikond70_sub_04.tif": {
|
1776 |
+
"subset": "val",
|
1777 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_04.tif",
|
1778 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_04_gt.png",
|
1779 |
+
"label": 1
|
1780 |
+
},
|
1781 |
+
"nikond70_kodakdcs330_sub_11.tif": {
|
1782 |
+
"subset": "val",
|
1783 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_11.tif",
|
1784 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_11_gt.png",
|
1785 |
+
"label": 1
|
1786 |
+
},
|
1787 |
+
"canong3_canonxt_sub_01.tif": {
|
1788 |
+
"subset": "val",
|
1789 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_01.tif",
|
1790 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_01_gt.png",
|
1791 |
+
"label": 1
|
1792 |
+
},
|
1793 |
+
"canonxt_kodakdcs330_sub_12.tif": {
|
1794 |
+
"subset": "val",
|
1795 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_12.tif",
|
1796 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_12_gt.png",
|
1797 |
+
"label": 1
|
1798 |
+
},
|
1799 |
+
"canong3_canonxt_sub_13.tif": {
|
1800 |
+
"subset": "val",
|
1801 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_13.tif",
|
1802 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_13_gt.png",
|
1803 |
+
"label": 1
|
1804 |
+
},
|
1805 |
+
"canong3_canonxt_sub_14.tif": {
|
1806 |
+
"subset": "val",
|
1807 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_14.tif",
|
1808 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_14_gt.png",
|
1809 |
+
"label": 1
|
1810 |
+
},
|
1811 |
+
"canong3_kodakdcs330_sub_26.tif": {
|
1812 |
+
"subset": "val",
|
1813 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_26.tif",
|
1814 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_26_gt.png",
|
1815 |
+
"label": 1
|
1816 |
+
},
|
1817 |
+
"canong3_canonxt_sub_26.tif": {
|
1818 |
+
"subset": "val",
|
1819 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_26.tif",
|
1820 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_26_gt.png",
|
1821 |
+
"label": 1
|
1822 |
+
},
|
1823 |
+
"canong3_kodakdcs330_sub_11.tif": {
|
1824 |
+
"subset": "val",
|
1825 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_11.tif",
|
1826 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_11_gt.png",
|
1827 |
+
"label": 1
|
1828 |
+
},
|
1829 |
+
"nikond70_canonxt_sub_02.tif": {
|
1830 |
+
"subset": "val",
|
1831 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_02.tif",
|
1832 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_02_gt.png",
|
1833 |
+
"label": 1
|
1834 |
+
},
|
1835 |
+
"canong3_nikond70_sub_23.tif": {
|
1836 |
+
"subset": "val",
|
1837 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_23.tif",
|
1838 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_23_gt.png",
|
1839 |
+
"label": 1
|
1840 |
+
},
|
1841 |
+
"canong3_kodakdcs330_sub_08.tif": {
|
1842 |
+
"subset": "val",
|
1843 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_08.tif",
|
1844 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_08_gt.png",
|
1845 |
+
"label": 1
|
1846 |
+
},
|
1847 |
+
"canong3_nikond70_sub_28.tif": {
|
1848 |
+
"subset": "val",
|
1849 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_28.tif",
|
1850 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_28_gt.png",
|
1851 |
+
"label": 1
|
1852 |
+
},
|
1853 |
+
"canong3_nikond70_sub_05.tif": {
|
1854 |
+
"subset": "val",
|
1855 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_05.tif",
|
1856 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_05_gt.png",
|
1857 |
+
"label": 1
|
1858 |
+
},
|
1859 |
+
"nikond70_canonxt_sub_15.tif": {
|
1860 |
+
"subset": "val",
|
1861 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_15.tif",
|
1862 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_15_gt.png",
|
1863 |
+
"label": 1
|
1864 |
+
},
|
1865 |
+
"canonxt_kodakdcs330_sub_20.tif": {
|
1866 |
+
"subset": "val",
|
1867 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_20.tif",
|
1868 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_20_gt.png",
|
1869 |
+
"label": 1
|
1870 |
+
},
|
1871 |
+
"nikond70_kodakdcs330_sub_06.tif": {
|
1872 |
+
"subset": "val",
|
1873 |
+
"path": "data/columbia/val/tp/nikond70_kodakdcs330_sub_06.tif",
|
1874 |
+
"mask": "data/columbia/val/mask/nikond70_kodakdcs330_sub_06_gt.png",
|
1875 |
+
"label": 1
|
1876 |
+
},
|
1877 |
+
"canong3_nikond70_sub_10.tif": {
|
1878 |
+
"subset": "val",
|
1879 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_10.tif",
|
1880 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_10_gt.png",
|
1881 |
+
"label": 1
|
1882 |
+
},
|
1883 |
+
"canonxt_kodakdcs330_sub_15.tif": {
|
1884 |
+
"subset": "val",
|
1885 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_15.tif",
|
1886 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_15_gt.png",
|
1887 |
+
"label": 1
|
1888 |
+
},
|
1889 |
+
"canong3_nikond70_sub_02.tif": {
|
1890 |
+
"subset": "val",
|
1891 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_02.tif",
|
1892 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_02_gt.png",
|
1893 |
+
"label": 1
|
1894 |
+
},
|
1895 |
+
"canonxt_kodakdcs330_sub_21.tif": {
|
1896 |
+
"subset": "val",
|
1897 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_21.tif",
|
1898 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_21_gt.png",
|
1899 |
+
"label": 1
|
1900 |
+
},
|
1901 |
+
"canong3_nikond70_sub_16.tif": {
|
1902 |
+
"subset": "val",
|
1903 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_16.tif",
|
1904 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_16_gt.png",
|
1905 |
+
"label": 1
|
1906 |
+
},
|
1907 |
+
"canong3_kodakdcs330_sub_25.tif": {
|
1908 |
+
"subset": "val",
|
1909 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_25.tif",
|
1910 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_25_gt.png",
|
1911 |
+
"label": 1
|
1912 |
+
},
|
1913 |
+
"canong3_kodakdcs330_sub_19.tif": {
|
1914 |
+
"subset": "val",
|
1915 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_19.tif",
|
1916 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_19_gt.png",
|
1917 |
+
"label": 1
|
1918 |
+
},
|
1919 |
+
"canonxt_kodakdcs330_sub_06.tif": {
|
1920 |
+
"subset": "val",
|
1921 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_06.tif",
|
1922 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_06_gt.png",
|
1923 |
+
"label": 1
|
1924 |
+
},
|
1925 |
+
"canonxt_kodakdcs330_sub_19.tif": {
|
1926 |
+
"subset": "val",
|
1927 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_19.tif",
|
1928 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_19_gt.png",
|
1929 |
+
"label": 1
|
1930 |
+
},
|
1931 |
+
"canonxt_kodakdcs330_sub_03.tif": {
|
1932 |
+
"subset": "val",
|
1933 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_03.tif",
|
1934 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_03_gt.png",
|
1935 |
+
"label": 1
|
1936 |
+
},
|
1937 |
+
"canong3_canonxt_sub_28.tif": {
|
1938 |
+
"subset": "val",
|
1939 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_28.tif",
|
1940 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_28_gt.png",
|
1941 |
+
"label": 1
|
1942 |
+
},
|
1943 |
+
"canong3_kodakdcs330_sub_05.tif": {
|
1944 |
+
"subset": "val",
|
1945 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_05.tif",
|
1946 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_05_gt.png",
|
1947 |
+
"label": 1
|
1948 |
+
},
|
1949 |
+
"canong3_nikond70_sub_29.tif": {
|
1950 |
+
"subset": "val",
|
1951 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_29.tif",
|
1952 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_29_gt.png",
|
1953 |
+
"label": 1
|
1954 |
+
},
|
1955 |
+
"canong3_kodakdcs330_sub_12.tif": {
|
1956 |
+
"subset": "val",
|
1957 |
+
"path": "data/columbia/val/tp/canong3_kodakdcs330_sub_12.tif",
|
1958 |
+
"mask": "data/columbia/val/mask/canong3_kodakdcs330_sub_12_gt.png",
|
1959 |
+
"label": 1
|
1960 |
+
},
|
1961 |
+
"canonxt_kodakdcs330_sub_18.tif": {
|
1962 |
+
"subset": "val",
|
1963 |
+
"path": "data/columbia/val/tp/canonxt_kodakdcs330_sub_18.tif",
|
1964 |
+
"mask": "data/columbia/val/mask/canonxt_kodakdcs330_sub_18_gt.png",
|
1965 |
+
"label": 1
|
1966 |
+
},
|
1967 |
+
"canong3_nikond70_sub_27.tif": {
|
1968 |
+
"subset": "val",
|
1969 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_27.tif",
|
1970 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_27_gt.png",
|
1971 |
+
"label": 1
|
1972 |
+
},
|
1973 |
+
"canong3_canonxt_sub_02.tif": {
|
1974 |
+
"subset": "val",
|
1975 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_02.tif",
|
1976 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_02_gt.png",
|
1977 |
+
"label": 1
|
1978 |
+
},
|
1979 |
+
"nikond70_canonxt_sub_10.tif": {
|
1980 |
+
"subset": "val",
|
1981 |
+
"path": "data/columbia/val/tp/nikond70_canonxt_sub_10.tif",
|
1982 |
+
"mask": "data/columbia/val/mask/nikond70_canonxt_sub_10_gt.png",
|
1983 |
+
"label": 1
|
1984 |
+
},
|
1985 |
+
"canong3_canonxt_sub_25.tif": {
|
1986 |
+
"subset": "val",
|
1987 |
+
"path": "data/columbia/val/tp/canong3_canonxt_sub_25.tif",
|
1988 |
+
"mask": "data/columbia/val/mask/canong3_canonxt_sub_25_gt.png",
|
1989 |
+
"label": 1
|
1990 |
+
},
|
1991 |
+
"canong3_nikond70_sub_08.tif": {
|
1992 |
+
"subset": "val",
|
1993 |
+
"path": "data/columbia/val/tp/canong3_nikond70_sub_08.tif",
|
1994 |
+
"mask": "data/columbia/val/mask/canong3_nikond70_sub_08_gt.png",
|
1995 |
+
"label": 1
|
1996 |
+
}
|
1997 |
+
}
|
data/coverage_datalist.json
ADDED
@@ -0,0 +1,1048 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"16t.tif": {
|
3 |
+
"subset": "val",
|
4 |
+
"path": "data/coverage/val/image/16t.tif",
|
5 |
+
"mask": "data/coverage/val/mask/16forged.tif",
|
6 |
+
"label": 1
|
7 |
+
},
|
8 |
+
"25t.tif": {
|
9 |
+
"subset": "val",
|
10 |
+
"path": "data/coverage/val/image/25t.tif",
|
11 |
+
"mask": "data/coverage/val/mask/25forged.tif",
|
12 |
+
"label": 1
|
13 |
+
},
|
14 |
+
"81.tif": {
|
15 |
+
"subset": "val",
|
16 |
+
"path": "data/coverage/val/image/81.tif",
|
17 |
+
"label": 0
|
18 |
+
},
|
19 |
+
"26.tif": {
|
20 |
+
"subset": "val",
|
21 |
+
"path": "data/coverage/val/image/26.tif",
|
22 |
+
"label": 0
|
23 |
+
},
|
24 |
+
"67.tif": {
|
25 |
+
"subset": "val",
|
26 |
+
"path": "data/coverage/val/image/67.tif",
|
27 |
+
"label": 0
|
28 |
+
},
|
29 |
+
"35t.tif": {
|
30 |
+
"subset": "val",
|
31 |
+
"path": "data/coverage/val/image/35t.tif",
|
32 |
+
"mask": "data/coverage/val/mask/35forged.tif",
|
33 |
+
"label": 1
|
34 |
+
},
|
35 |
+
"30t.tif": {
|
36 |
+
"subset": "val",
|
37 |
+
"path": "data/coverage/val/image/30t.tif",
|
38 |
+
"mask": "data/coverage/val/mask/30forged.tif",
|
39 |
+
"label": 1
|
40 |
+
},
|
41 |
+
"94t.tif": {
|
42 |
+
"subset": "val",
|
43 |
+
"path": "data/coverage/val/image/94t.tif",
|
44 |
+
"mask": "data/coverage/val/mask/94forged.tif",
|
45 |
+
"label": 1
|
46 |
+
},
|
47 |
+
"9.tif": {
|
48 |
+
"subset": "val",
|
49 |
+
"path": "data/coverage/val/image/9.tif",
|
50 |
+
"label": 0
|
51 |
+
},
|
52 |
+
"52.tif": {
|
53 |
+
"subset": "val",
|
54 |
+
"path": "data/coverage/val/image/52.tif",
|
55 |
+
"label": 0
|
56 |
+
},
|
57 |
+
"46t.tif": {
|
58 |
+
"subset": "val",
|
59 |
+
"path": "data/coverage/val/image/46t.tif",
|
60 |
+
"mask": "data/coverage/val/mask/46forged.tif",
|
61 |
+
"label": 1
|
62 |
+
},
|
63 |
+
"86t.tif": {
|
64 |
+
"subset": "val",
|
65 |
+
"path": "data/coverage/val/image/86t.tif",
|
66 |
+
"mask": "data/coverage/val/mask/86forged.tif",
|
67 |
+
"label": 1
|
68 |
+
},
|
69 |
+
"95.tif": {
|
70 |
+
"subset": "val",
|
71 |
+
"path": "data/coverage/val/image/95.tif",
|
72 |
+
"label": 0
|
73 |
+
},
|
74 |
+
"94.tif": {
|
75 |
+
"subset": "val",
|
76 |
+
"path": "data/coverage/val/image/94.tif",
|
77 |
+
"label": 0
|
78 |
+
},
|
79 |
+
"73t.tif": {
|
80 |
+
"subset": "val",
|
81 |
+
"path": "data/coverage/val/image/73t.tif",
|
82 |
+
"mask": "data/coverage/val/mask/73forged.tif",
|
83 |
+
"label": 1
|
84 |
+
},
|
85 |
+
"23t.tif": {
|
86 |
+
"subset": "val",
|
87 |
+
"path": "data/coverage/val/image/23t.tif",
|
88 |
+
"mask": "data/coverage/val/mask/23forged.tif",
|
89 |
+
"label": 1
|
90 |
+
},
|
91 |
+
"68t.tif": {
|
92 |
+
"subset": "val",
|
93 |
+
"path": "data/coverage/val/image/68t.tif",
|
94 |
+
"mask": "data/coverage/val/mask/68forged.tif",
|
95 |
+
"label": 1
|
96 |
+
},
|
97 |
+
"88t.tif": {
|
98 |
+
"subset": "val",
|
99 |
+
"path": "data/coverage/val/image/88t.tif",
|
100 |
+
"mask": "data/coverage/val/mask/88forged.tif",
|
101 |
+
"label": 1
|
102 |
+
},
|
103 |
+
"45.tif": {
|
104 |
+
"subset": "val",
|
105 |
+
"path": "data/coverage/val/image/45.tif",
|
106 |
+
"label": 0
|
107 |
+
},
|
108 |
+
"93t.tif": {
|
109 |
+
"subset": "val",
|
110 |
+
"path": "data/coverage/val/image/93t.tif",
|
111 |
+
"mask": "data/coverage/val/mask/93forged.tif",
|
112 |
+
"label": 1
|
113 |
+
},
|
114 |
+
"27.tif": {
|
115 |
+
"subset": "val",
|
116 |
+
"path": "data/coverage/val/image/27.tif",
|
117 |
+
"label": 0
|
118 |
+
},
|
119 |
+
"44.tif": {
|
120 |
+
"subset": "val",
|
121 |
+
"path": "data/coverage/val/image/44.tif",
|
122 |
+
"label": 0
|
123 |
+
},
|
124 |
+
"48.tif": {
|
125 |
+
"subset": "val",
|
126 |
+
"path": "data/coverage/val/image/48.tif",
|
127 |
+
"label": 0
|
128 |
+
},
|
129 |
+
"10.tif": {
|
130 |
+
"subset": "val",
|
131 |
+
"path": "data/coverage/val/image/10.tif",
|
132 |
+
"label": 0
|
133 |
+
},
|
134 |
+
"50t.tif": {
|
135 |
+
"subset": "val",
|
136 |
+
"path": "data/coverage/val/image/50t.tif",
|
137 |
+
"mask": "data/coverage/val/mask/50forged.tif",
|
138 |
+
"label": 1
|
139 |
+
},
|
140 |
+
"90.tif": {
|
141 |
+
"subset": "val",
|
142 |
+
"path": "data/coverage/val/image/90.tif",
|
143 |
+
"label": 0
|
144 |
+
},
|
145 |
+
"6.tif": {
|
146 |
+
"subset": "val",
|
147 |
+
"path": "data/coverage/val/image/6.tif",
|
148 |
+
"label": 0
|
149 |
+
},
|
150 |
+
"24t.tif": {
|
151 |
+
"subset": "val",
|
152 |
+
"path": "data/coverage/val/image/24t.tif",
|
153 |
+
"mask": "data/coverage/val/mask/24forged.tif",
|
154 |
+
"label": 1
|
155 |
+
},
|
156 |
+
"87.tif": {
|
157 |
+
"subset": "val",
|
158 |
+
"path": "data/coverage/val/image/87.tif",
|
159 |
+
"label": 0
|
160 |
+
},
|
161 |
+
"20.tif": {
|
162 |
+
"subset": "val",
|
163 |
+
"path": "data/coverage/val/image/20.tif",
|
164 |
+
"label": 0
|
165 |
+
},
|
166 |
+
"54.tif": {
|
167 |
+
"subset": "val",
|
168 |
+
"path": "data/coverage/val/image/54.tif",
|
169 |
+
"label": 0
|
170 |
+
},
|
171 |
+
"72t.tif": {
|
172 |
+
"subset": "val",
|
173 |
+
"path": "data/coverage/val/image/72t.tif",
|
174 |
+
"mask": "data/coverage/val/mask/72forged.tif",
|
175 |
+
"label": 1
|
176 |
+
},
|
177 |
+
"13.tif": {
|
178 |
+
"subset": "val",
|
179 |
+
"path": "data/coverage/val/image/13.tif",
|
180 |
+
"label": 0
|
181 |
+
},
|
182 |
+
"67t.tif": {
|
183 |
+
"subset": "val",
|
184 |
+
"path": "data/coverage/val/image/67t.tif",
|
185 |
+
"mask": "data/coverage/val/mask/67forged.tif",
|
186 |
+
"label": 1
|
187 |
+
},
|
188 |
+
"32.tif": {
|
189 |
+
"subset": "val",
|
190 |
+
"path": "data/coverage/val/image/32.tif",
|
191 |
+
"label": 0
|
192 |
+
},
|
193 |
+
"8t.tif": {
|
194 |
+
"subset": "val",
|
195 |
+
"path": "data/coverage/val/image/8t.tif",
|
196 |
+
"mask": "data/coverage/val/mask/8forged.tif",
|
197 |
+
"label": 1
|
198 |
+
},
|
199 |
+
"22.tif": {
|
200 |
+
"subset": "val",
|
201 |
+
"path": "data/coverage/val/image/22.tif",
|
202 |
+
"label": 0
|
203 |
+
},
|
204 |
+
"35.tif": {
|
205 |
+
"subset": "val",
|
206 |
+
"path": "data/coverage/val/image/35.tif",
|
207 |
+
"label": 0
|
208 |
+
},
|
209 |
+
"18t.tif": {
|
210 |
+
"subset": "val",
|
211 |
+
"path": "data/coverage/val/image/18t.tif",
|
212 |
+
"mask": "data/coverage/val/mask/18forged.tif",
|
213 |
+
"label": 1
|
214 |
+
},
|
215 |
+
"20t.tif": {
|
216 |
+
"subset": "val",
|
217 |
+
"path": "data/coverage/val/image/20t.tif",
|
218 |
+
"mask": "data/coverage/val/mask/20forged.tif",
|
219 |
+
"label": 1
|
220 |
+
},
|
221 |
+
"63t.tif": {
|
222 |
+
"subset": "val",
|
223 |
+
"path": "data/coverage/val/image/63t.tif",
|
224 |
+
"mask": "data/coverage/val/mask/63forged.tif",
|
225 |
+
"label": 1
|
226 |
+
},
|
227 |
+
"75t.tif": {
|
228 |
+
"subset": "val",
|
229 |
+
"path": "data/coverage/val/image/75t.tif",
|
230 |
+
"mask": "data/coverage/val/mask/75forged.tif",
|
231 |
+
"label": 1
|
232 |
+
},
|
233 |
+
"63.tif": {
|
234 |
+
"subset": "val",
|
235 |
+
"path": "data/coverage/val/image/63.tif",
|
236 |
+
"label": 0
|
237 |
+
},
|
238 |
+
"56.tif": {
|
239 |
+
"subset": "val",
|
240 |
+
"path": "data/coverage/val/image/56.tif",
|
241 |
+
"label": 0
|
242 |
+
},
|
243 |
+
"3t.tif": {
|
244 |
+
"subset": "val",
|
245 |
+
"path": "data/coverage/val/image/3t.tif",
|
246 |
+
"mask": "data/coverage/val/mask/3forged.tif",
|
247 |
+
"label": 1
|
248 |
+
},
|
249 |
+
"97.tif": {
|
250 |
+
"subset": "val",
|
251 |
+
"path": "data/coverage/val/image/97.tif",
|
252 |
+
"label": 0
|
253 |
+
},
|
254 |
+
"42t.tif": {
|
255 |
+
"subset": "val",
|
256 |
+
"path": "data/coverage/val/image/42t.tif",
|
257 |
+
"mask": "data/coverage/val/mask/42forged.tif",
|
258 |
+
"label": 1
|
259 |
+
},
|
260 |
+
"86.tif": {
|
261 |
+
"subset": "val",
|
262 |
+
"path": "data/coverage/val/image/86.tif",
|
263 |
+
"label": 0
|
264 |
+
},
|
265 |
+
"66t.tif": {
|
266 |
+
"subset": "val",
|
267 |
+
"path": "data/coverage/val/image/66t.tif",
|
268 |
+
"mask": "data/coverage/val/mask/66forged.tif",
|
269 |
+
"label": 1
|
270 |
+
},
|
271 |
+
"61.tif": {
|
272 |
+
"subset": "val",
|
273 |
+
"path": "data/coverage/val/image/61.tif",
|
274 |
+
"label": 0
|
275 |
+
},
|
276 |
+
"49.tif": {
|
277 |
+
"subset": "val",
|
278 |
+
"path": "data/coverage/val/image/49.tif",
|
279 |
+
"label": 0
|
280 |
+
},
|
281 |
+
"4.tif": {
|
282 |
+
"subset": "val",
|
283 |
+
"path": "data/coverage/val/image/4.tif",
|
284 |
+
"label": 0
|
285 |
+
},
|
286 |
+
"96t.tif": {
|
287 |
+
"subset": "val",
|
288 |
+
"path": "data/coverage/val/image/96t.tif",
|
289 |
+
"mask": "data/coverage/val/mask/96forged.tif",
|
290 |
+
"label": 1
|
291 |
+
},
|
292 |
+
"81t.tif": {
|
293 |
+
"subset": "val",
|
294 |
+
"path": "data/coverage/val/image/81t.tif",
|
295 |
+
"mask": "data/coverage/val/mask/81forged.tif",
|
296 |
+
"label": 1
|
297 |
+
},
|
298 |
+
"2t.tif": {
|
299 |
+
"subset": "val",
|
300 |
+
"path": "data/coverage/val/image/2t.tif",
|
301 |
+
"mask": "data/coverage/val/mask/2forged.tif",
|
302 |
+
"label": 1
|
303 |
+
},
|
304 |
+
"62.tif": {
|
305 |
+
"subset": "val",
|
306 |
+
"path": "data/coverage/val/image/62.tif",
|
307 |
+
"label": 0
|
308 |
+
},
|
309 |
+
"78t.tif": {
|
310 |
+
"subset": "val",
|
311 |
+
"path": "data/coverage/val/image/78t.tif",
|
312 |
+
"mask": "data/coverage/val/mask/78forged.tif",
|
313 |
+
"label": 1
|
314 |
+
},
|
315 |
+
"92t.tif": {
|
316 |
+
"subset": "val",
|
317 |
+
"path": "data/coverage/val/image/92t.tif",
|
318 |
+
"mask": "data/coverage/val/mask/92forged.tif",
|
319 |
+
"label": 1
|
320 |
+
},
|
321 |
+
"77.tif": {
|
322 |
+
"subset": "val",
|
323 |
+
"path": "data/coverage/val/image/77.tif",
|
324 |
+
"label": 0
|
325 |
+
},
|
326 |
+
"14.tif": {
|
327 |
+
"subset": "val",
|
328 |
+
"path": "data/coverage/val/image/14.tif",
|
329 |
+
"label": 0
|
330 |
+
},
|
331 |
+
"12t.tif": {
|
332 |
+
"subset": "val",
|
333 |
+
"path": "data/coverage/val/image/12t.tif",
|
334 |
+
"mask": "data/coverage/val/mask/12forged.tif",
|
335 |
+
"label": 1
|
336 |
+
},
|
337 |
+
"96.tif": {
|
338 |
+
"subset": "val",
|
339 |
+
"path": "data/coverage/val/image/96.tif",
|
340 |
+
"label": 0
|
341 |
+
},
|
342 |
+
"85t.tif": {
|
343 |
+
"subset": "val",
|
344 |
+
"path": "data/coverage/val/image/85t.tif",
|
345 |
+
"mask": "data/coverage/val/mask/85forged.tif",
|
346 |
+
"label": 1
|
347 |
+
},
|
348 |
+
"50.tif": {
|
349 |
+
"subset": "val",
|
350 |
+
"path": "data/coverage/val/image/50.tif",
|
351 |
+
"label": 0
|
352 |
+
},
|
353 |
+
"100.tif": {
|
354 |
+
"subset": "val",
|
355 |
+
"path": "data/coverage/val/image/100.tif",
|
356 |
+
"label": 0
|
357 |
+
},
|
358 |
+
"76t.tif": {
|
359 |
+
"subset": "val",
|
360 |
+
"path": "data/coverage/val/image/76t.tif",
|
361 |
+
"mask": "data/coverage/val/mask/76forged.tif",
|
362 |
+
"label": 1
|
363 |
+
},
|
364 |
+
"71.tif": {
|
365 |
+
"subset": "val",
|
366 |
+
"path": "data/coverage/val/image/71.tif",
|
367 |
+
"label": 0
|
368 |
+
},
|
369 |
+
"42.tif": {
|
370 |
+
"subset": "val",
|
371 |
+
"path": "data/coverage/val/image/42.tif",
|
372 |
+
"label": 0
|
373 |
+
},
|
374 |
+
"5t.tif": {
|
375 |
+
"subset": "val",
|
376 |
+
"path": "data/coverage/val/image/5t.tif",
|
377 |
+
"mask": "data/coverage/val/mask/5forged.tif",
|
378 |
+
"label": 1
|
379 |
+
},
|
380 |
+
"41.tif": {
|
381 |
+
"subset": "val",
|
382 |
+
"path": "data/coverage/val/image/41.tif",
|
383 |
+
"label": 0
|
384 |
+
},
|
385 |
+
"71t.tif": {
|
386 |
+
"subset": "val",
|
387 |
+
"path": "data/coverage/val/image/71t.tif",
|
388 |
+
"mask": "data/coverage/val/mask/71forged.tif",
|
389 |
+
"label": 1
|
390 |
+
},
|
391 |
+
"90t.tif": {
|
392 |
+
"subset": "val",
|
393 |
+
"path": "data/coverage/val/image/90t.tif",
|
394 |
+
"mask": "data/coverage/val/mask/90forged.tif",
|
395 |
+
"label": 1
|
396 |
+
},
|
397 |
+
"32t.tif": {
|
398 |
+
"subset": "val",
|
399 |
+
"path": "data/coverage/val/image/32t.tif",
|
400 |
+
"mask": "data/coverage/val/mask/32forged.tif",
|
401 |
+
"label": 1
|
402 |
+
},
|
403 |
+
"33.tif": {
|
404 |
+
"subset": "val",
|
405 |
+
"path": "data/coverage/val/image/33.tif",
|
406 |
+
"label": 0
|
407 |
+
},
|
408 |
+
"87t.tif": {
|
409 |
+
"subset": "val",
|
410 |
+
"path": "data/coverage/val/image/87t.tif",
|
411 |
+
"mask": "data/coverage/val/mask/87forged.tif",
|
412 |
+
"label": 1
|
413 |
+
},
|
414 |
+
"70.tif": {
|
415 |
+
"subset": "val",
|
416 |
+
"path": "data/coverage/val/image/70.tif",
|
417 |
+
"label": 0
|
418 |
+
},
|
419 |
+
"2.tif": {
|
420 |
+
"subset": "val",
|
421 |
+
"path": "data/coverage/val/image/2.tif",
|
422 |
+
"label": 0
|
423 |
+
},
|
424 |
+
"43.tif": {
|
425 |
+
"subset": "val",
|
426 |
+
"path": "data/coverage/val/image/43.tif",
|
427 |
+
"label": 0
|
428 |
+
},
|
429 |
+
"43t.tif": {
|
430 |
+
"subset": "val",
|
431 |
+
"path": "data/coverage/val/image/43t.tif",
|
432 |
+
"mask": "data/coverage/val/mask/43forged.tif",
|
433 |
+
"label": 1
|
434 |
+
},
|
435 |
+
"75.tif": {
|
436 |
+
"subset": "val",
|
437 |
+
"path": "data/coverage/val/image/75.tif",
|
438 |
+
"label": 0
|
439 |
+
},
|
440 |
+
"40t.tif": {
|
441 |
+
"subset": "val",
|
442 |
+
"path": "data/coverage/val/image/40t.tif",
|
443 |
+
"mask": "data/coverage/val/mask/40forged.tif",
|
444 |
+
"label": 1
|
445 |
+
},
|
446 |
+
"17t.tif": {
|
447 |
+
"subset": "val",
|
448 |
+
"path": "data/coverage/val/image/17t.tif",
|
449 |
+
"mask": "data/coverage/val/mask/17forged.tif",
|
450 |
+
"label": 1
|
451 |
+
},
|
452 |
+
"28t.tif": {
|
453 |
+
"subset": "val",
|
454 |
+
"path": "data/coverage/val/image/28t.tif",
|
455 |
+
"mask": "data/coverage/val/mask/28forged.tif",
|
456 |
+
"label": 1
|
457 |
+
},
|
458 |
+
"82.tif": {
|
459 |
+
"subset": "val",
|
460 |
+
"path": "data/coverage/val/image/82.tif",
|
461 |
+
"label": 0
|
462 |
+
},
|
463 |
+
"73.tif": {
|
464 |
+
"subset": "val",
|
465 |
+
"path": "data/coverage/val/image/73.tif",
|
466 |
+
"label": 0
|
467 |
+
},
|
468 |
+
"78.tif": {
|
469 |
+
"subset": "val",
|
470 |
+
"path": "data/coverage/val/image/78.tif",
|
471 |
+
"label": 0
|
472 |
+
},
|
473 |
+
"64.tif": {
|
474 |
+
"subset": "val",
|
475 |
+
"path": "data/coverage/val/image/64.tif",
|
476 |
+
"label": 0
|
477 |
+
},
|
478 |
+
"69t.tif": {
|
479 |
+
"subset": "val",
|
480 |
+
"path": "data/coverage/val/image/69t.tif",
|
481 |
+
"mask": "data/coverage/val/mask/69forged.tif",
|
482 |
+
"label": 1
|
483 |
+
},
|
484 |
+
"15t.tif": {
|
485 |
+
"subset": "val",
|
486 |
+
"path": "data/coverage/val/image/15t.tif",
|
487 |
+
"mask": "data/coverage/val/mask/15forged.tif",
|
488 |
+
"label": 1
|
489 |
+
},
|
490 |
+
"47t.tif": {
|
491 |
+
"subset": "val",
|
492 |
+
"path": "data/coverage/val/image/47t.tif",
|
493 |
+
"mask": "data/coverage/val/mask/47forged.tif",
|
494 |
+
"label": 1
|
495 |
+
},
|
496 |
+
"13t.tif": {
|
497 |
+
"subset": "val",
|
498 |
+
"path": "data/coverage/val/image/13t.tif",
|
499 |
+
"mask": "data/coverage/val/mask/13forged.tif",
|
500 |
+
"label": 1
|
501 |
+
},
|
502 |
+
"15.tif": {
|
503 |
+
"subset": "val",
|
504 |
+
"path": "data/coverage/val/image/15.tif",
|
505 |
+
"label": 0
|
506 |
+
},
|
507 |
+
"23.tif": {
|
508 |
+
"subset": "val",
|
509 |
+
"path": "data/coverage/val/image/23.tif",
|
510 |
+
"label": 0
|
511 |
+
},
|
512 |
+
"64t.tif": {
|
513 |
+
"subset": "val",
|
514 |
+
"path": "data/coverage/val/image/64t.tif",
|
515 |
+
"mask": "data/coverage/val/mask/64forged.tif",
|
516 |
+
"label": 1
|
517 |
+
},
|
518 |
+
"77t.tif": {
|
519 |
+
"subset": "val",
|
520 |
+
"path": "data/coverage/val/image/77t.tif",
|
521 |
+
"mask": "data/coverage/val/mask/77forged.tif",
|
522 |
+
"label": 1
|
523 |
+
},
|
524 |
+
"98.tif": {
|
525 |
+
"subset": "val",
|
526 |
+
"path": "data/coverage/val/image/98.tif",
|
527 |
+
"label": 0
|
528 |
+
},
|
529 |
+
"5.tif": {
|
530 |
+
"subset": "val",
|
531 |
+
"path": "data/coverage/val/image/5.tif",
|
532 |
+
"label": 0
|
533 |
+
},
|
534 |
+
"79t.tif": {
|
535 |
+
"subset": "val",
|
536 |
+
"path": "data/coverage/val/image/79t.tif",
|
537 |
+
"mask": "data/coverage/val/mask/79forged.tif",
|
538 |
+
"label": 1
|
539 |
+
},
|
540 |
+
"9t.tif": {
|
541 |
+
"subset": "val",
|
542 |
+
"path": "data/coverage/val/image/9t.tif",
|
543 |
+
"mask": "data/coverage/val/mask/9forged.tif",
|
544 |
+
"label": 1
|
545 |
+
},
|
546 |
+
"91.tif": {
|
547 |
+
"subset": "val",
|
548 |
+
"path": "data/coverage/val/image/91.tif",
|
549 |
+
"label": 0
|
550 |
+
},
|
551 |
+
"85.tif": {
|
552 |
+
"subset": "val",
|
553 |
+
"path": "data/coverage/val/image/85.tif",
|
554 |
+
"label": 0
|
555 |
+
},
|
556 |
+
"91t.tif": {
|
557 |
+
"subset": "val",
|
558 |
+
"path": "data/coverage/val/image/91t.tif",
|
559 |
+
"mask": "data/coverage/val/mask/91forged.tif",
|
560 |
+
"label": 1
|
561 |
+
},
|
562 |
+
"97t.tif": {
|
563 |
+
"subset": "val",
|
564 |
+
"path": "data/coverage/val/image/97t.tif",
|
565 |
+
"mask": "data/coverage/val/mask/97forged.tif",
|
566 |
+
"label": 1
|
567 |
+
},
|
568 |
+
"98t.tif": {
|
569 |
+
"subset": "val",
|
570 |
+
"path": "data/coverage/val/image/98t.tif",
|
571 |
+
"mask": "data/coverage/val/mask/98forged.tif",
|
572 |
+
"label": 1
|
573 |
+
},
|
574 |
+
"60t.tif": {
|
575 |
+
"subset": "val",
|
576 |
+
"path": "data/coverage/val/image/60t.tif",
|
577 |
+
"mask": "data/coverage/val/mask/60forged.tif",
|
578 |
+
"label": 1
|
579 |
+
},
|
580 |
+
"11t.tif": {
|
581 |
+
"subset": "val",
|
582 |
+
"path": "data/coverage/val/image/11t.tif",
|
583 |
+
"mask": "data/coverage/val/mask/11forged.tif",
|
584 |
+
"label": 1
|
585 |
+
},
|
586 |
+
"68.tif": {
|
587 |
+
"subset": "val",
|
588 |
+
"path": "data/coverage/val/image/68.tif",
|
589 |
+
"label": 0
|
590 |
+
},
|
591 |
+
"84.tif": {
|
592 |
+
"subset": "val",
|
593 |
+
"path": "data/coverage/val/image/84.tif",
|
594 |
+
"label": 0
|
595 |
+
},
|
596 |
+
"84t.tif": {
|
597 |
+
"subset": "val",
|
598 |
+
"path": "data/coverage/val/image/84t.tif",
|
599 |
+
"mask": "data/coverage/val/mask/84forged.tif",
|
600 |
+
"label": 1
|
601 |
+
},
|
602 |
+
"4t.tif": {
|
603 |
+
"subset": "val",
|
604 |
+
"path": "data/coverage/val/image/4t.tif",
|
605 |
+
"mask": "data/coverage/val/mask/4forged.tif",
|
606 |
+
"label": 1
|
607 |
+
},
|
608 |
+
"79.tif": {
|
609 |
+
"subset": "val",
|
610 |
+
"path": "data/coverage/val/image/79.tif",
|
611 |
+
"label": 0
|
612 |
+
},
|
613 |
+
"36t.tif": {
|
614 |
+
"subset": "val",
|
615 |
+
"path": "data/coverage/val/image/36t.tif",
|
616 |
+
"mask": "data/coverage/val/mask/36forged.tif",
|
617 |
+
"label": 1
|
618 |
+
},
|
619 |
+
"1.tif": {
|
620 |
+
"subset": "val",
|
621 |
+
"path": "data/coverage/val/image/1.tif",
|
622 |
+
"label": 0
|
623 |
+
},
|
624 |
+
"10t.tif": {
|
625 |
+
"subset": "val",
|
626 |
+
"path": "data/coverage/val/image/10t.tif",
|
627 |
+
"mask": "data/coverage/val/mask/10forged.tif",
|
628 |
+
"label": 1
|
629 |
+
},
|
630 |
+
"38.tif": {
|
631 |
+
"subset": "val",
|
632 |
+
"path": "data/coverage/val/image/38.tif",
|
633 |
+
"label": 0
|
634 |
+
},
|
635 |
+
"39.tif": {
|
636 |
+
"subset": "val",
|
637 |
+
"path": "data/coverage/val/image/39.tif",
|
638 |
+
"label": 0
|
639 |
+
},
|
640 |
+
"40.tif": {
|
641 |
+
"subset": "val",
|
642 |
+
"path": "data/coverage/val/image/40.tif",
|
643 |
+
"label": 0
|
644 |
+
},
|
645 |
+
"17.tif": {
|
646 |
+
"subset": "val",
|
647 |
+
"path": "data/coverage/val/image/17.tif",
|
648 |
+
"label": 0
|
649 |
+
},
|
650 |
+
"59.tif": {
|
651 |
+
"subset": "val",
|
652 |
+
"path": "data/coverage/val/image/59.tif",
|
653 |
+
"label": 0
|
654 |
+
},
|
655 |
+
"3.tif": {
|
656 |
+
"subset": "val",
|
657 |
+
"path": "data/coverage/val/image/3.tif",
|
658 |
+
"label": 0
|
659 |
+
},
|
660 |
+
"53t.tif": {
|
661 |
+
"subset": "val",
|
662 |
+
"path": "data/coverage/val/image/53t.tif",
|
663 |
+
"mask": "data/coverage/val/mask/53forged.tif",
|
664 |
+
"label": 1
|
665 |
+
},
|
666 |
+
"92.tif": {
|
667 |
+
"subset": "val",
|
668 |
+
"path": "data/coverage/val/image/92.tif",
|
669 |
+
"label": 0
|
670 |
+
},
|
671 |
+
"62t.tif": {
|
672 |
+
"subset": "val",
|
673 |
+
"path": "data/coverage/val/image/62t.tif",
|
674 |
+
"mask": "data/coverage/val/mask/62forged.tif",
|
675 |
+
"label": 1
|
676 |
+
},
|
677 |
+
"66.tif": {
|
678 |
+
"subset": "val",
|
679 |
+
"path": "data/coverage/val/image/66.tif",
|
680 |
+
"label": 0
|
681 |
+
},
|
682 |
+
"14t.tif": {
|
683 |
+
"subset": "val",
|
684 |
+
"path": "data/coverage/val/image/14t.tif",
|
685 |
+
"mask": "data/coverage/val/mask/14forged.tif",
|
686 |
+
"label": 1
|
687 |
+
},
|
688 |
+
"58.tif": {
|
689 |
+
"subset": "val",
|
690 |
+
"path": "data/coverage/val/image/58.tif",
|
691 |
+
"label": 0
|
692 |
+
},
|
693 |
+
"82t.tif": {
|
694 |
+
"subset": "val",
|
695 |
+
"path": "data/coverage/val/image/82t.tif",
|
696 |
+
"mask": "data/coverage/val/mask/82forged.tif",
|
697 |
+
"label": 1
|
698 |
+
},
|
699 |
+
"31t.tif": {
|
700 |
+
"subset": "val",
|
701 |
+
"path": "data/coverage/val/image/31t.tif",
|
702 |
+
"mask": "data/coverage/val/mask/31forged.tif",
|
703 |
+
"label": 1
|
704 |
+
},
|
705 |
+
"55.tif": {
|
706 |
+
"subset": "val",
|
707 |
+
"path": "data/coverage/val/image/55.tif",
|
708 |
+
"label": 0
|
709 |
+
},
|
710 |
+
"31.tif": {
|
711 |
+
"subset": "val",
|
712 |
+
"path": "data/coverage/val/image/31.tif",
|
713 |
+
"label": 0
|
714 |
+
},
|
715 |
+
"80t.tif": {
|
716 |
+
"subset": "val",
|
717 |
+
"path": "data/coverage/val/image/80t.tif",
|
718 |
+
"mask": "data/coverage/val/mask/80forged.tif",
|
719 |
+
"label": 1
|
720 |
+
},
|
721 |
+
"18.tif": {
|
722 |
+
"subset": "val",
|
723 |
+
"path": "data/coverage/val/image/18.tif",
|
724 |
+
"label": 0
|
725 |
+
},
|
726 |
+
"53.tif": {
|
727 |
+
"subset": "val",
|
728 |
+
"path": "data/coverage/val/image/53.tif",
|
729 |
+
"label": 0
|
730 |
+
},
|
731 |
+
"46.tif": {
|
732 |
+
"subset": "val",
|
733 |
+
"path": "data/coverage/val/image/46.tif",
|
734 |
+
"label": 0
|
735 |
+
},
|
736 |
+
"26t.tif": {
|
737 |
+
"subset": "val",
|
738 |
+
"path": "data/coverage/val/image/26t.tif",
|
739 |
+
"mask": "data/coverage/val/mask/26forged.tif",
|
740 |
+
"label": 1
|
741 |
+
},
|
742 |
+
"99.tif": {
|
743 |
+
"subset": "val",
|
744 |
+
"path": "data/coverage/val/image/99.tif",
|
745 |
+
"label": 0
|
746 |
+
},
|
747 |
+
"28.tif": {
|
748 |
+
"subset": "val",
|
749 |
+
"path": "data/coverage/val/image/28.tif",
|
750 |
+
"label": 0
|
751 |
+
},
|
752 |
+
"38t.tif": {
|
753 |
+
"subset": "val",
|
754 |
+
"path": "data/coverage/val/image/38t.tif",
|
755 |
+
"mask": "data/coverage/val/mask/38forged.tif",
|
756 |
+
"label": 1
|
757 |
+
},
|
758 |
+
"70t.tif": {
|
759 |
+
"subset": "val",
|
760 |
+
"path": "data/coverage/val/image/70t.tif",
|
761 |
+
"mask": "data/coverage/val/mask/70forged.tif",
|
762 |
+
"label": 1
|
763 |
+
},
|
764 |
+
"47.tif": {
|
765 |
+
"subset": "val",
|
766 |
+
"path": "data/coverage/val/image/47.tif",
|
767 |
+
"label": 0
|
768 |
+
},
|
769 |
+
"34.tif": {
|
770 |
+
"subset": "val",
|
771 |
+
"path": "data/coverage/val/image/34.tif",
|
772 |
+
"label": 0
|
773 |
+
},
|
774 |
+
"49t.tif": {
|
775 |
+
"subset": "val",
|
776 |
+
"path": "data/coverage/val/image/49t.tif",
|
777 |
+
"mask": "data/coverage/val/mask/49forged.tif",
|
778 |
+
"label": 1
|
779 |
+
},
|
780 |
+
"22t.tif": {
|
781 |
+
"subset": "val",
|
782 |
+
"path": "data/coverage/val/image/22t.tif",
|
783 |
+
"mask": "data/coverage/val/mask/22forged.tif",
|
784 |
+
"label": 1
|
785 |
+
},
|
786 |
+
"74t.tif": {
|
787 |
+
"subset": "val",
|
788 |
+
"path": "data/coverage/val/image/74t.tif",
|
789 |
+
"mask": "data/coverage/val/mask/74forged.tif",
|
790 |
+
"label": 1
|
791 |
+
},
|
792 |
+
"65t.tif": {
|
793 |
+
"subset": "val",
|
794 |
+
"path": "data/coverage/val/image/65t.tif",
|
795 |
+
"mask": "data/coverage/val/mask/65forged.tif",
|
796 |
+
"label": 1
|
797 |
+
},
|
798 |
+
"8.tif": {
|
799 |
+
"subset": "val",
|
800 |
+
"path": "data/coverage/val/image/8.tif",
|
801 |
+
"label": 0
|
802 |
+
},
|
803 |
+
"1t.tif": {
|
804 |
+
"subset": "val",
|
805 |
+
"path": "data/coverage/val/image/1t.tif",
|
806 |
+
"mask": "data/coverage/val/mask/1forged.tif",
|
807 |
+
"label": 1
|
808 |
+
},
|
809 |
+
"80.tif": {
|
810 |
+
"subset": "val",
|
811 |
+
"path": "data/coverage/val/image/80.tif",
|
812 |
+
"label": 0
|
813 |
+
},
|
814 |
+
"60.tif": {
|
815 |
+
"subset": "val",
|
816 |
+
"path": "data/coverage/val/image/60.tif",
|
817 |
+
"label": 0
|
818 |
+
},
|
819 |
+
"21.tif": {
|
820 |
+
"subset": "val",
|
821 |
+
"path": "data/coverage/val/image/21.tif",
|
822 |
+
"label": 0
|
823 |
+
},
|
824 |
+
"57.tif": {
|
825 |
+
"subset": "val",
|
826 |
+
"path": "data/coverage/val/image/57.tif",
|
827 |
+
"label": 0
|
828 |
+
},
|
829 |
+
"51.tif": {
|
830 |
+
"subset": "val",
|
831 |
+
"path": "data/coverage/val/image/51.tif",
|
832 |
+
"label": 0
|
833 |
+
},
|
834 |
+
"7t.tif": {
|
835 |
+
"subset": "val",
|
836 |
+
"path": "data/coverage/val/image/7t.tif",
|
837 |
+
"mask": "data/coverage/val/mask/7forged.tif",
|
838 |
+
"label": 1
|
839 |
+
},
|
840 |
+
"93.tif": {
|
841 |
+
"subset": "val",
|
842 |
+
"path": "data/coverage/val/image/93.tif",
|
843 |
+
"label": 0
|
844 |
+
},
|
845 |
+
"83.tif": {
|
846 |
+
"subset": "val",
|
847 |
+
"path": "data/coverage/val/image/83.tif",
|
848 |
+
"label": 0
|
849 |
+
},
|
850 |
+
"27t.tif": {
|
851 |
+
"subset": "val",
|
852 |
+
"path": "data/coverage/val/image/27t.tif",
|
853 |
+
"mask": "data/coverage/val/mask/27forged.tif",
|
854 |
+
"label": 1
|
855 |
+
},
|
856 |
+
"19.tif": {
|
857 |
+
"subset": "val",
|
858 |
+
"path": "data/coverage/val/image/19.tif",
|
859 |
+
"label": 0
|
860 |
+
},
|
861 |
+
"34t.tif": {
|
862 |
+
"subset": "val",
|
863 |
+
"path": "data/coverage/val/image/34t.tif",
|
864 |
+
"mask": "data/coverage/val/mask/34forged.tif",
|
865 |
+
"label": 1
|
866 |
+
},
|
867 |
+
"52t.tif": {
|
868 |
+
"subset": "val",
|
869 |
+
"path": "data/coverage/val/image/52t.tif",
|
870 |
+
"mask": "data/coverage/val/mask/52forged.tif",
|
871 |
+
"label": 1
|
872 |
+
},
|
873 |
+
"45t.tif": {
|
874 |
+
"subset": "val",
|
875 |
+
"path": "data/coverage/val/image/45t.tif",
|
876 |
+
"mask": "data/coverage/val/mask/45forged.tif",
|
877 |
+
"label": 1
|
878 |
+
},
|
879 |
+
"12.tif": {
|
880 |
+
"subset": "val",
|
881 |
+
"path": "data/coverage/val/image/12.tif",
|
882 |
+
"label": 0
|
883 |
+
},
|
884 |
+
"16.tif": {
|
885 |
+
"subset": "val",
|
886 |
+
"path": "data/coverage/val/image/16.tif",
|
887 |
+
"label": 0
|
888 |
+
},
|
889 |
+
"29.tif": {
|
890 |
+
"subset": "val",
|
891 |
+
"path": "data/coverage/val/image/29.tif",
|
892 |
+
"label": 0
|
893 |
+
},
|
894 |
+
"89.tif": {
|
895 |
+
"subset": "val",
|
896 |
+
"path": "data/coverage/val/image/89.tif",
|
897 |
+
"label": 0
|
898 |
+
},
|
899 |
+
"29t.tif": {
|
900 |
+
"subset": "val",
|
901 |
+
"path": "data/coverage/val/image/29t.tif",
|
902 |
+
"mask": "data/coverage/val/mask/29forged.tif",
|
903 |
+
"label": 1
|
904 |
+
},
|
905 |
+
"36.tif": {
|
906 |
+
"subset": "val",
|
907 |
+
"path": "data/coverage/val/image/36.tif",
|
908 |
+
"label": 0
|
909 |
+
},
|
910 |
+
"39t.tif": {
|
911 |
+
"subset": "val",
|
912 |
+
"path": "data/coverage/val/image/39t.tif",
|
913 |
+
"mask": "data/coverage/val/mask/39forged.tif",
|
914 |
+
"label": 1
|
915 |
+
},
|
916 |
+
"100t.tif": {
|
917 |
+
"subset": "val",
|
918 |
+
"path": "data/coverage/val/image/100t.tif",
|
919 |
+
"mask": "data/coverage/val/mask/100forged.tif",
|
920 |
+
"label": 1
|
921 |
+
},
|
922 |
+
"21t.tif": {
|
923 |
+
"subset": "val",
|
924 |
+
"path": "data/coverage/val/image/21t.tif",
|
925 |
+
"mask": "data/coverage/val/mask/21forged.tif",
|
926 |
+
"label": 1
|
927 |
+
},
|
928 |
+
"88.tif": {
|
929 |
+
"subset": "val",
|
930 |
+
"path": "data/coverage/val/image/88.tif",
|
931 |
+
"label": 0
|
932 |
+
},
|
933 |
+
"74.tif": {
|
934 |
+
"subset": "val",
|
935 |
+
"path": "data/coverage/val/image/74.tif",
|
936 |
+
"label": 0
|
937 |
+
},
|
938 |
+
"7.tif": {
|
939 |
+
"subset": "val",
|
940 |
+
"path": "data/coverage/val/image/7.tif",
|
941 |
+
"label": 0
|
942 |
+
},
|
943 |
+
"33t.tif": {
|
944 |
+
"subset": "val",
|
945 |
+
"path": "data/coverage/val/image/33t.tif",
|
946 |
+
"mask": "data/coverage/val/mask/33forged.tif",
|
947 |
+
"label": 1
|
948 |
+
},
|
949 |
+
"89t.tif": {
|
950 |
+
"subset": "val",
|
951 |
+
"path": "data/coverage/val/image/89t.tif",
|
952 |
+
"mask": "data/coverage/val/mask/89forged.tif",
|
953 |
+
"label": 1
|
954 |
+
},
|
955 |
+
"24.tif": {
|
956 |
+
"subset": "val",
|
957 |
+
"path": "data/coverage/val/image/24.tif",
|
958 |
+
"label": 0
|
959 |
+
},
|
960 |
+
"37t.tif": {
|
961 |
+
"subset": "val",
|
962 |
+
"path": "data/coverage/val/image/37t.tif",
|
963 |
+
"mask": "data/coverage/val/mask/37forged.tif",
|
964 |
+
"label": 1
|
965 |
+
},
|
966 |
+
"83t.tif": {
|
967 |
+
"subset": "val",
|
968 |
+
"path": "data/coverage/val/image/83t.tif",
|
969 |
+
"mask": "data/coverage/val/mask/83forged.tif",
|
970 |
+
"label": 1
|
971 |
+
},
|
972 |
+
"19t.tif": {
|
973 |
+
"subset": "val",
|
974 |
+
"path": "data/coverage/val/image/19t.tif",
|
975 |
+
"mask": "data/coverage/val/mask/19forged.tif",
|
976 |
+
"label": 1
|
977 |
+
},
|
978 |
+
"76.tif": {
|
979 |
+
"subset": "val",
|
980 |
+
"path": "data/coverage/val/image/76.tif",
|
981 |
+
"label": 0
|
982 |
+
},
|
983 |
+
"65.tif": {
|
984 |
+
"subset": "val",
|
985 |
+
"path": "data/coverage/val/image/65.tif",
|
986 |
+
"label": 0
|
987 |
+
},
|
988 |
+
"51t.tif": {
|
989 |
+
"subset": "val",
|
990 |
+
"path": "data/coverage/val/image/51t.tif",
|
991 |
+
"mask": "data/coverage/val/mask/51forged.tif",
|
992 |
+
"label": 1
|
993 |
+
},
|
994 |
+
"69.tif": {
|
995 |
+
"subset": "val",
|
996 |
+
"path": "data/coverage/val/image/69.tif",
|
997 |
+
"label": 0
|
998 |
+
},
|
999 |
+
"37.tif": {
|
1000 |
+
"subset": "val",
|
1001 |
+
"path": "data/coverage/val/image/37.tif",
|
1002 |
+
"label": 0
|
1003 |
+
},
|
1004 |
+
"25.tif": {
|
1005 |
+
"subset": "val",
|
1006 |
+
"path": "data/coverage/val/image/25.tif",
|
1007 |
+
"label": 0
|
1008 |
+
},
|
1009 |
+
"72.tif": {
|
1010 |
+
"subset": "val",
|
1011 |
+
"path": "data/coverage/val/image/72.tif",
|
1012 |
+
"label": 0
|
1013 |
+
},
|
1014 |
+
"44t.tif": {
|
1015 |
+
"subset": "val",
|
1016 |
+
"path": "data/coverage/val/image/44t.tif",
|
1017 |
+
"mask": "data/coverage/val/mask/44forged.tif",
|
1018 |
+
"label": 1
|
1019 |
+
},
|
1020 |
+
"54t.tif": {
|
1021 |
+
"subset": "val",
|
1022 |
+
"path": "data/coverage/val/image/54t.tif",
|
1023 |
+
"mask": "data/coverage/val/mask/54forged.tif",
|
1024 |
+
"label": 1
|
1025 |
+
},
|
1026 |
+
"99t.tif": {
|
1027 |
+
"subset": "val",
|
1028 |
+
"path": "data/coverage/val/image/99t.tif",
|
1029 |
+
"mask": "data/coverage/val/mask/99forged.tif",
|
1030 |
+
"label": 1
|
1031 |
+
},
|
1032 |
+
"30.tif": {
|
1033 |
+
"subset": "val",
|
1034 |
+
"path": "data/coverage/val/image/30.tif",
|
1035 |
+
"label": 0
|
1036 |
+
},
|
1037 |
+
"11.tif": {
|
1038 |
+
"subset": "val",
|
1039 |
+
"path": "data/coverage/val/image/11.tif",
|
1040 |
+
"label": 0
|
1041 |
+
},
|
1042 |
+
"6t.tif": {
|
1043 |
+
"subset": "val",
|
1044 |
+
"path": "data/coverage/val/image/6t.tif",
|
1045 |
+
"mask": "data/coverage/val/mask/6forged.tif",
|
1046 |
+
"label": 1
|
1047 |
+
}
|
1048 |
+
}
|
datasets/__init__.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Dict
|
2 |
+
|
3 |
+
import albumentations as A
|
4 |
+
|
5 |
+
from .dataset import ImageDataset, crop_to_smallest_collate_fn
|
6 |
+
|
7 |
+
|
8 |
+
def get_dataset(datalist: Dict, subset, transform, opt):
|
9 |
+
datasets = {}
|
10 |
+
for k, v in datalist.items():
|
11 |
+
# val_transform = transform
|
12 |
+
if k in ["imd2020", "nist16"]:
|
13 |
+
val_transform = A.Compose([A.SmallestMaxSize(opt.tile_size)])
|
14 |
+
else:
|
15 |
+
val_transform = transform
|
16 |
+
datasets[k] = ImageDataset(
|
17 |
+
k,
|
18 |
+
v,
|
19 |
+
subset,
|
20 |
+
val_transform,
|
21 |
+
opt.uncorrect_label,
|
22 |
+
opt.mvc_spixel
|
23 |
+
if subset == "train"
|
24 |
+
else opt.crf_postproc or opt.convcrf_postproc or opt.spixel_postproc,
|
25 |
+
opt.mvc_num_spixel,
|
26 |
+
)
|
27 |
+
|
28 |
+
return datasets
|
datasets/dataset.py
ADDED
@@ -0,0 +1,230 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import os
|
3 |
+
import random
|
4 |
+
import signal
|
5 |
+
|
6 |
+
import albumentations as A
|
7 |
+
import cv2
|
8 |
+
import h5py
|
9 |
+
import numpy as np
|
10 |
+
import torch
|
11 |
+
import torchvision.transforms as T
|
12 |
+
from albumentations.pytorch.functional import img_to_tensor, mask_to_tensor
|
13 |
+
from skimage import segmentation
|
14 |
+
from termcolor import cprint
|
15 |
+
from timm.data.constants import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD
|
16 |
+
|
17 |
+
|
18 |
+
class ImageDataset(torch.utils.data.Dataset):
|
19 |
+
def __init__(
|
20 |
+
self,
|
21 |
+
dataset_name: str,
|
22 |
+
datalist: str,
|
23 |
+
mode: str,
|
24 |
+
transform=None,
|
25 |
+
uncorrect_label=False,
|
26 |
+
spixel: bool = False,
|
27 |
+
num_spixel: int = 100,
|
28 |
+
):
|
29 |
+
super().__init__()
|
30 |
+
|
31 |
+
assert os.path.exists(datalist), f"{datalist} does not exist"
|
32 |
+
assert mode in ["train", "val"], f"{mode} unsupported mode"
|
33 |
+
|
34 |
+
with open(datalist, "r") as f:
|
35 |
+
self.datalist = json.load(f)
|
36 |
+
|
37 |
+
self.datalist = dict(
|
38 |
+
filter(lambda x: x[1]["subset"] == mode, self.datalist.items())
|
39 |
+
)
|
40 |
+
if len(self.datalist) == 0:
|
41 |
+
raise NotImplementedError(f"no item in {datalist} {mode} dataset")
|
42 |
+
self.video_id_list = list(self.datalist.keys())
|
43 |
+
self.transform = transform
|
44 |
+
self.uncorrect_label = uncorrect_label
|
45 |
+
|
46 |
+
self.dataset_name = dataset_name
|
47 |
+
h5_path = os.path.join("data", dataset_name + "_dataset.hdf5")
|
48 |
+
self.use_h5 = os.path.exists(h5_path)
|
49 |
+
if self.use_h5:
|
50 |
+
cprint(
|
51 |
+
f"{dataset_name} {mode} HDF5 database found, loading into memory...",
|
52 |
+
"blue",
|
53 |
+
)
|
54 |
+
try:
|
55 |
+
with timeout(seconds=60):
|
56 |
+
self.database = h5py.File(h5_path, "r", driver="core")
|
57 |
+
except Exception as e:
|
58 |
+
self.database = h5py.File(h5_path, "r")
|
59 |
+
cprint(
|
60 |
+
"Failed to load {} HDF5 database to memory due to {}".format(
|
61 |
+
dataset_name, str(e)
|
62 |
+
),
|
63 |
+
"red",
|
64 |
+
)
|
65 |
+
else:
|
66 |
+
cprint(
|
67 |
+
f"{dataset_name} {mode} HDF5 database not found, using raw images.",
|
68 |
+
"blue",
|
69 |
+
)
|
70 |
+
|
71 |
+
self.spixel = False
|
72 |
+
self.num_spixel = num_spixel
|
73 |
+
if spixel:
|
74 |
+
self.spixel = True
|
75 |
+
self.spixel_dict = {}
|
76 |
+
|
77 |
+
def __getitem__(self, index):
|
78 |
+
image_id = self.video_id_list[index]
|
79 |
+
info = self.datalist[image_id]
|
80 |
+
label = float(info["label"])
|
81 |
+
if self.use_h5:
|
82 |
+
try:
|
83 |
+
image = self.database[info["path"].replace("/", "-")][()]
|
84 |
+
except Exception as e:
|
85 |
+
cprint(
|
86 |
+
"Failed to load {} from {} due to {}".format(
|
87 |
+
image_id, self.dataset_name, str(e)
|
88 |
+
),
|
89 |
+
"red",
|
90 |
+
)
|
91 |
+
image = cv2.imread(info["path"])
|
92 |
+
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
93 |
+
else:
|
94 |
+
assert os.path.exists(info["path"]), f"{info['path']} does not exist!"
|
95 |
+
image = cv2.imread(info["path"])
|
96 |
+
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
97 |
+
|
98 |
+
if self.spixel and image_id not in self.spixel_dict.keys():
|
99 |
+
spixel = segmentation.slic(
|
100 |
+
image, n_segments=self.num_spixel, channel_axis=2, start_label=0
|
101 |
+
)
|
102 |
+
self.spixel_dict[image_id] = spixel
|
103 |
+
|
104 |
+
image_size = image.shape[:2]
|
105 |
+
|
106 |
+
# 1 means modified area, 0 means pristine
|
107 |
+
if "mask" in info.keys():
|
108 |
+
if self.use_h5:
|
109 |
+
try:
|
110 |
+
mask = self.database[info["mask"].replace("/", "-")][()]
|
111 |
+
except Exception as e:
|
112 |
+
cprint(
|
113 |
+
"Failed to load {} mask from {} due to {}".format(
|
114 |
+
image_id, self.dataset_name, str(e)
|
115 |
+
),
|
116 |
+
"red",
|
117 |
+
)
|
118 |
+
mask = cv2.imread(info["mask"], cv2.IMREAD_GRAYSCALE)
|
119 |
+
else:
|
120 |
+
mask = cv2.imread(info["mask"], cv2.IMREAD_GRAYSCALE)
|
121 |
+
else:
|
122 |
+
if label == 0:
|
123 |
+
mask = np.zeros(image_size)
|
124 |
+
else:
|
125 |
+
mask = np.ones(image_size)
|
126 |
+
|
127 |
+
if self.transform is not None:
|
128 |
+
if self.spixel:
|
129 |
+
transformed = self.transform(
|
130 |
+
image=image, masks=[mask, self.spixel_dict[image_id]]
|
131 |
+
) # TODO I am not sure if this is correct for scaling
|
132 |
+
mask = transformed["masks"][0]
|
133 |
+
spixel = transformed["masks"][1]
|
134 |
+
else:
|
135 |
+
transformed = self.transform(image=image, mask=mask)
|
136 |
+
mask = transformed["mask"]
|
137 |
+
|
138 |
+
image = transformed["image"]
|
139 |
+
if not self.uncorrect_label:
|
140 |
+
label = float(mask.max() != 0.0)
|
141 |
+
|
142 |
+
if label == 1.0 and image.shape[:-1] != mask.shape:
|
143 |
+
mask = cv2.resize(mask, dsize=(image.shape[1], image.shape[0]))
|
144 |
+
|
145 |
+
unnormalized_image = img_to_tensor(image)
|
146 |
+
image = img_to_tensor(
|
147 |
+
image,
|
148 |
+
normalize={"mean": IMAGENET_DEFAULT_MEAN, "std": IMAGENET_DEFAULT_STD},
|
149 |
+
)
|
150 |
+
mask = mask_to_tensor(mask, num_classes=1, sigmoid=True)
|
151 |
+
|
152 |
+
output = {
|
153 |
+
"image": image, # tensor of 3, H, W
|
154 |
+
"label": label, # float
|
155 |
+
"mask": mask, # tensor of 1, H, W
|
156 |
+
"id": image_id, # string
|
157 |
+
"unnormalized_image": unnormalized_image,
|
158 |
+
} # tensor of 3, H, W
|
159 |
+
if self.spixel:
|
160 |
+
spixel = torch.from_numpy(spixel).unsqueeze(0)
|
161 |
+
output["spixel"] = spixel
|
162 |
+
return output
|
163 |
+
|
164 |
+
def __len__(self):
|
165 |
+
return len(self.video_id_list)
|
166 |
+
|
167 |
+
|
168 |
+
def crop_to_smallest_collate_fn(batch, max_size=128, uncorrect_label=False):
|
169 |
+
# get the smallest image size in a batch
|
170 |
+
smallest_size = [max_size, max_size]
|
171 |
+
for item in batch:
|
172 |
+
if item["mask"].shape[-2:] != item["image"].shape[-2:]:
|
173 |
+
cprint(
|
174 |
+
f"{item['id']} has inconsistent image-mask sizes,"
|
175 |
+
"with image size {item['image'].shape[-2:]} and mask size"
|
176 |
+
"{item['mask'].shape[-2:]}!",
|
177 |
+
"red",
|
178 |
+
)
|
179 |
+
image_size = item["image"].shape[-2:]
|
180 |
+
if image_size[0] < smallest_size[0]:
|
181 |
+
smallest_size[0] = image_size[0]
|
182 |
+
if image_size[1] < smallest_size[1]:
|
183 |
+
smallest_size[1] = image_size[1]
|
184 |
+
|
185 |
+
# crop all images and masks in each item to the smallest size
|
186 |
+
result = {}
|
187 |
+
for item in batch:
|
188 |
+
image_size = item["image"].shape[-2:]
|
189 |
+
x1 = random.randint(0, image_size[1] - smallest_size[1])
|
190 |
+
y1 = random.randint(0, image_size[0] - smallest_size[0])
|
191 |
+
x2 = x1 + smallest_size[1]
|
192 |
+
y2 = y1 + smallest_size[0]
|
193 |
+
for k in ["image", "mask", "unnormalized_image", "spixel"]:
|
194 |
+
if k not in item.keys():
|
195 |
+
continue
|
196 |
+
item[k] = item[k][:, y1:y2, x1:x2]
|
197 |
+
if not uncorrect_label:
|
198 |
+
item["label"] = float(item["mask"].max() != 0.0)
|
199 |
+
for k, v in item.items():
|
200 |
+
if k in result.keys():
|
201 |
+
result[k].append(v)
|
202 |
+
else:
|
203 |
+
result[k] = [v]
|
204 |
+
|
205 |
+
# stack all outputs
|
206 |
+
for k, v in result.items():
|
207 |
+
if k in ["image", "mask", "unnormalized_image", "spixel"]:
|
208 |
+
if k not in result.keys():
|
209 |
+
continue
|
210 |
+
result[k] = torch.stack(v, dim=0)
|
211 |
+
elif k in ["label"]:
|
212 |
+
result[k] = torch.tensor(v).float()
|
213 |
+
|
214 |
+
return result
|
215 |
+
|
216 |
+
|
217 |
+
class timeout:
|
218 |
+
def __init__(self, seconds=1, error_message="Timeout"):
|
219 |
+
self.seconds = seconds
|
220 |
+
self.error_message = error_message
|
221 |
+
|
222 |
+
def handle_timeout(self, signum, frame):
|
223 |
+
raise TimeoutError(self.error_message)
|
224 |
+
|
225 |
+
def __enter__(self):
|
226 |
+
signal.signal(signal.SIGALRM, self.handle_timeout)
|
227 |
+
signal.alarm(self.seconds)
|
228 |
+
|
229 |
+
def __exit__(self, type, value, traceback):
|
230 |
+
signal.alarm(0)
|
engine.py
ADDED
@@ -0,0 +1,454 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import itertools
|
2 |
+
import os
|
3 |
+
import random
|
4 |
+
import shutil
|
5 |
+
from math import ceil
|
6 |
+
from typing import Dict, List
|
7 |
+
|
8 |
+
import numpy as np
|
9 |
+
import prettytable as pt
|
10 |
+
import torch
|
11 |
+
import torch.nn as nn
|
12 |
+
from fast_pytorch_kmeans import KMeans
|
13 |
+
from pathlib2 import Path
|
14 |
+
from scipy.stats import hmean
|
15 |
+
from sklearn import metrics
|
16 |
+
from termcolor import cprint
|
17 |
+
from torchvision.utils import draw_segmentation_masks, make_grid, save_image
|
18 |
+
|
19 |
+
import utils.misc as misc
|
20 |
+
from losses import get_spixel_tgt_map, get_volume_seg_map
|
21 |
+
from utils.convcrf import convcrf
|
22 |
+
from utils.crf import DenseCRF
|
23 |
+
|
24 |
+
|
25 |
+
def train(
|
26 |
+
model: nn.Module,
|
27 |
+
dataloader,
|
28 |
+
dataset_title: str,
|
29 |
+
optimizer_dict: Dict,
|
30 |
+
criterion,
|
31 |
+
epoch: int,
|
32 |
+
writer,
|
33 |
+
suffix: str,
|
34 |
+
opt,
|
35 |
+
):
|
36 |
+
|
37 |
+
metric_logger = misc.MetricLogger(writer=writer, suffix=suffix)
|
38 |
+
cprint("{}-th epoch training on {}".format(epoch, dataset_title), "blue")
|
39 |
+
model.train()
|
40 |
+
roc_auc_elements = {
|
41 |
+
modality: {"map_scores": [], "vol_scores": []}
|
42 |
+
for modality in itertools.chain(opt.modality, ["ensemble"])
|
43 |
+
}
|
44 |
+
roc_auc_elements["labels"] = []
|
45 |
+
|
46 |
+
for i, data in metric_logger.log_every(
|
47 |
+
dataloader, print_freq=opt.print_freq, header=f"[{suffix} {epoch}]"
|
48 |
+
):
|
49 |
+
if (opt.debug or opt.wholetest) and i > 50:
|
50 |
+
break
|
51 |
+
|
52 |
+
for modality, optimizer in optimizer_dict.items():
|
53 |
+
optimizer.zero_grad()
|
54 |
+
|
55 |
+
image = data["image"].to(opt.device)
|
56 |
+
unnormalized_image = data["unnormalized_image"].to(opt.device)
|
57 |
+
label = data["label"].to(opt.device)
|
58 |
+
mask = data["mask"].to(opt.device)
|
59 |
+
spixel = data["spixel"].to(opt.device) if opt.mvc_spixel else None
|
60 |
+
|
61 |
+
outputs = model(
|
62 |
+
image,
|
63 |
+
seg_size=None
|
64 |
+
if opt.loss_on_mid_map
|
65 |
+
else [image.shape[-2], image.shape[-1]],
|
66 |
+
)
|
67 |
+
|
68 |
+
losses = criterion(
|
69 |
+
outputs,
|
70 |
+
label,
|
71 |
+
mask,
|
72 |
+
epoch=epoch,
|
73 |
+
max_epoch=opt.epochs,
|
74 |
+
spixel=spixel,
|
75 |
+
raw_image=unnormalized_image,
|
76 |
+
)
|
77 |
+
total_loss = losses["total_loss"]
|
78 |
+
total_loss.backward()
|
79 |
+
|
80 |
+
for modality in opt.modality:
|
81 |
+
if opt.grad_clip > 0.0:
|
82 |
+
grad_norm = nn.utils.clip_grad_norm_(
|
83 |
+
model.sub_models[modality].parameters(), opt.grad_clip
|
84 |
+
)
|
85 |
+
metric_logger.update(**{f"grad_norm/{modality}": grad_norm})
|
86 |
+
|
87 |
+
optimizer_dict[modality].step()
|
88 |
+
|
89 |
+
# image-level metrices logger
|
90 |
+
roc_auc_elements["labels"].extend(label.tolist())
|
91 |
+
for modality in itertools.chain(opt.modality, ["ensemble"]):
|
92 |
+
roc_auc_elements[modality]["map_scores"].extend(
|
93 |
+
outputs[modality]["map_pred"].tolist()
|
94 |
+
)
|
95 |
+
roc_auc_elements[modality]["vol_scores"].extend(
|
96 |
+
(outputs[modality]["vol_pred"]).tolist()
|
97 |
+
)
|
98 |
+
|
99 |
+
metric_logger.update(**losses)
|
100 |
+
|
101 |
+
image_metrics = update_image_roc_auc_metric(
|
102 |
+
opt.modality + ["ensemble"], roc_auc_elements, None
|
103 |
+
)
|
104 |
+
metric_logger.update(**image_metrics)
|
105 |
+
|
106 |
+
metric_logger.write_tensorboard(epoch)
|
107 |
+
print("Average status:")
|
108 |
+
print(metric_logger.stat_table())
|
109 |
+
|
110 |
+
|
111 |
+
def bundled_evaluate(
|
112 |
+
model: nn.Module, dataloaders: Dict, criterion, epoch, writer, suffix, opt
|
113 |
+
):
|
114 |
+
|
115 |
+
metric_logger = misc.MetricLogger(writer=writer, suffix=suffix + "_avg")
|
116 |
+
for dataset, dataloader in dataloaders.items():
|
117 |
+
outputs = evaluate(
|
118 |
+
model,
|
119 |
+
dataloader,
|
120 |
+
criterion,
|
121 |
+
dataset,
|
122 |
+
epoch,
|
123 |
+
writer,
|
124 |
+
suffix + f"_{dataset}",
|
125 |
+
opt,
|
126 |
+
)
|
127 |
+
old_keys = list(outputs.keys())
|
128 |
+
for k in old_keys:
|
129 |
+
outputs[k.replace(dataset.upper(), "AVG")] = outputs[k]
|
130 |
+
for k in old_keys:
|
131 |
+
del outputs[k]
|
132 |
+
|
133 |
+
metric_logger.update(**outputs)
|
134 |
+
|
135 |
+
metric_logger.write_tensorboard(epoch)
|
136 |
+
print("Average status:")
|
137 |
+
print(metric_logger.stat_table())
|
138 |
+
return metric_logger.get_meters()
|
139 |
+
|
140 |
+
|
141 |
+
def evaluate(
|
142 |
+
model: nn.Module,
|
143 |
+
dataloader,
|
144 |
+
criterion,
|
145 |
+
dataset_title: str,
|
146 |
+
epoch: int,
|
147 |
+
writer,
|
148 |
+
suffix: str,
|
149 |
+
opt,
|
150 |
+
):
|
151 |
+
|
152 |
+
metric_logger = misc.MetricLogger(writer=writer, suffix=suffix)
|
153 |
+
cprint("{}-th epoch evaluation on {}".format(epoch, dataset_title.upper()), "blue")
|
154 |
+
|
155 |
+
model.eval()
|
156 |
+
|
157 |
+
if opt.crf_postproc:
|
158 |
+
postprocess = DenseCRF(
|
159 |
+
iter_max=opt.crf_iter_max,
|
160 |
+
pos_w=opt.crf_pos_w,
|
161 |
+
pos_xy_std=opt.crf_pos_xy_std,
|
162 |
+
bi_w=opt.crf_bi_w,
|
163 |
+
bi_xy_std=opt.crf_bi_xy_std,
|
164 |
+
bi_rgb_std=opt.crf_bi_rgb_std,
|
165 |
+
)
|
166 |
+
elif opt.convcrf_postproc:
|
167 |
+
convcrf_config = convcrf.default_conf
|
168 |
+
convcrf_config["skip_init_softmax"] = True
|
169 |
+
convcrf_config["final_softmax"] = True
|
170 |
+
shape = [opt.convcrf_shape, opt.convcrf_shape]
|
171 |
+
postprocess = convcrf.GaussCRF(
|
172 |
+
conf=convcrf_config, shape=shape, nclasses=2, use_gpu=True
|
173 |
+
).to(opt.device)
|
174 |
+
|
175 |
+
figure_path = opt.figure_path + f"_{dataset_title.upper()}"
|
176 |
+
if opt.save_figure:
|
177 |
+
if os.path.exists(figure_path):
|
178 |
+
shutil.rmtree(figure_path)
|
179 |
+
os.mkdir(figure_path)
|
180 |
+
cprint("Saving figures to {}".format(figure_path), "blue")
|
181 |
+
|
182 |
+
if opt.max_pool_postproc > 1:
|
183 |
+
max_pool = nn.MaxPool2d(
|
184 |
+
kernel_size=opt.max_pool_postproc,
|
185 |
+
stride=1,
|
186 |
+
padding=(opt.max_pool_postproc - 1) // 2,
|
187 |
+
).to(opt.device)
|
188 |
+
else:
|
189 |
+
max_pool = nn.Identity().to(opt.device)
|
190 |
+
# used_sliding_prediction = False
|
191 |
+
roc_auc_elements = {
|
192 |
+
modality: {"map_scores": [], "vol_scores": []}
|
193 |
+
for modality in itertools.chain(opt.modality, ["ensemble"])
|
194 |
+
}
|
195 |
+
roc_auc_elements["labels"] = []
|
196 |
+
with torch.no_grad():
|
197 |
+
for i, data in metric_logger.log_every(
|
198 |
+
dataloader, print_freq=opt.print_freq, header=f"[{suffix} {epoch}]"
|
199 |
+
):
|
200 |
+
if (opt.debug or opt.wholetest) and i > 50:
|
201 |
+
break
|
202 |
+
|
203 |
+
image_size = data["image"].shape[-2:]
|
204 |
+
label = data["label"]
|
205 |
+
mask = data["mask"]
|
206 |
+
if opt.crf_postproc or opt.spixel_postproc or opt.convcrf_postproc:
|
207 |
+
spixel = data["spixel"].to(opt.device)
|
208 |
+
if max(image_size) > opt.tile_size and opt.large_image_strategy == "slide":
|
209 |
+
outputs = sliding_predict(
|
210 |
+
model, data, opt.tile_size, opt.tile_overlap, opt
|
211 |
+
)
|
212 |
+
else:
|
213 |
+
image = data["image"].to(opt.device)
|
214 |
+
outputs = model(image, seg_size=image.shape[-2:])
|
215 |
+
|
216 |
+
if opt.max_pool_postproc > 1:
|
217 |
+
for modality in itertools.chain(opt.modality, ["ensemble"]):
|
218 |
+
outputs[modality]["out_map"] = max_pool(
|
219 |
+
outputs[modality]["out_map"]
|
220 |
+
)
|
221 |
+
# CRF
|
222 |
+
if opt.crf_postproc:
|
223 |
+
raw_prob = outputs["ensemble"]["out_map"]
|
224 |
+
image = data["unnormalized_image"] * 255.0
|
225 |
+
if opt.crf_downsample > 1:
|
226 |
+
image = (
|
227 |
+
torch.nn.functional.interpolate(
|
228 |
+
image,
|
229 |
+
size=(
|
230 |
+
image_size[0] // opt.crf_downsample,
|
231 |
+
image_size[1] // opt.crf_downsample,
|
232 |
+
),
|
233 |
+
mode="bilinear",
|
234 |
+
align_corners=False,
|
235 |
+
)
|
236 |
+
.clamp(0, 255)
|
237 |
+
.int()
|
238 |
+
)
|
239 |
+
image = image.squeeze(0).numpy().astype(np.uint8).transpose(1, 2, 0)
|
240 |
+
for modality in itertools.chain(opt.modality, ["ensemble"]):
|
241 |
+
prob = outputs[modality]["out_map"].squeeze(1)
|
242 |
+
if opt.crf_downsample > 1:
|
243 |
+
prob = (
|
244 |
+
torch.nn.functional.interpolate(
|
245 |
+
prob,
|
246 |
+
size=(
|
247 |
+
image_size[0] // opt.crf_downsample,
|
248 |
+
image_size[1] // opt.crf_downsample,
|
249 |
+
),
|
250 |
+
mode="bilinear",
|
251 |
+
align_corners=False,
|
252 |
+
)
|
253 |
+
.clamp(0, 1)
|
254 |
+
.squeeze(0)
|
255 |
+
)
|
256 |
+
prob = torch.cat([prob, 1 - prob], dim=0).detach().cpu().numpy()
|
257 |
+
prob = postprocess(image, prob)
|
258 |
+
prob = prob[None, 0, ...]
|
259 |
+
prob = torch.tensor(prob, device=opt.device).unsqueeze(0)
|
260 |
+
if opt.crf_downsample > 1:
|
261 |
+
prob = torch.nn.functional.interpolate(
|
262 |
+
prob, size=image_size, mode="bilinear", align_corners=False
|
263 |
+
).clamp(0, 1)
|
264 |
+
outputs[modality]["out_map"] = prob
|
265 |
+
outputs[modality]["map_pred"] = (
|
266 |
+
outputs[modality]["out_map"].max().unsqueeze(0)
|
267 |
+
)
|
268 |
+
elif opt.convcrf_postproc:
|
269 |
+
raw_prob = outputs["ensemble"]["out_map"]
|
270 |
+
image = data["unnormalized_image"].to(opt.device) * 255.0
|
271 |
+
image = (
|
272 |
+
torch.nn.functional.interpolate(
|
273 |
+
image,
|
274 |
+
size=(opt.convcrf_shape, opt.convcrf_shape),
|
275 |
+
mode="bilinear",
|
276 |
+
align_corners=False,
|
277 |
+
)
|
278 |
+
.clamp(0, 255)
|
279 |
+
.int()
|
280 |
+
)
|
281 |
+
for modality in itertools.chain(opt.modality, ["ensemble"]):
|
282 |
+
prob = outputs[modality]["out_map"]
|
283 |
+
prob = torch.cat([prob, 1 - prob], dim=1)
|
284 |
+
prob = torch.nn.functional.interpolate(
|
285 |
+
prob,
|
286 |
+
size=(opt.convcrf_shape, opt.convcrf_shape),
|
287 |
+
mode="bilinear",
|
288 |
+
align_corners=False,
|
289 |
+
).clamp(0, 1)
|
290 |
+
prob = postprocess(unary=prob, img=image)
|
291 |
+
prob = torch.nn.functional.interpolate(
|
292 |
+
prob, size=image_size, mode="bilinear", align_corners=False
|
293 |
+
).clamp(0, 1)
|
294 |
+
outputs[modality]["out_map"] = prob[:, 0, None, ...]
|
295 |
+
outputs[modality]["map_pred"] = (
|
296 |
+
outputs[modality]["out_map"].max().unsqueeze(0)
|
297 |
+
)
|
298 |
+
elif opt.spixel_postproc:
|
299 |
+
raw_prob = outputs["ensemble"]["out_map"]
|
300 |
+
for modality in itertools.chain(opt.modality, ["ensemble"]):
|
301 |
+
outputs[modality]["out_map"] = get_spixel_tgt_map(
|
302 |
+
outputs[modality]["out_map"], spixel
|
303 |
+
)
|
304 |
+
|
305 |
+
# image-level metrices logger
|
306 |
+
roc_auc_elements["labels"].extend(label.detach().cpu().tolist())
|
307 |
+
for modality in itertools.chain(opt.modality, ["ensemble"]):
|
308 |
+
roc_auc_elements[modality]["map_scores"].extend(
|
309 |
+
outputs[modality]["map_pred"].detach().cpu().tolist()
|
310 |
+
)
|
311 |
+
roc_auc_elements[modality]["vol_scores"].extend(
|
312 |
+
(outputs[modality]["vol_pred"]).detach().cpu().tolist()
|
313 |
+
)
|
314 |
+
|
315 |
+
# generate binary prediction mask
|
316 |
+
out_map = {
|
317 |
+
modality: outputs[modality]["out_map"] > opt.mask_threshold
|
318 |
+
for modality in itertools.chain(opt.modality, ["ensemble"])
|
319 |
+
}
|
320 |
+
|
321 |
+
# only compute pixel-level metrics for manipulated images
|
322 |
+
if label.item() == 1.0:
|
323 |
+
for modality in itertools.chain(opt.modality, ["ensemble"]):
|
324 |
+
pixel_metrics = misc.calculate_pixel_f1(
|
325 |
+
out_map[modality].float().detach().cpu().numpy().flatten(),
|
326 |
+
mask.detach().cpu().numpy().flatten(),
|
327 |
+
suffix=f"/{modality}",
|
328 |
+
)
|
329 |
+
metric_logger.update(**pixel_metrics)
|
330 |
+
|
331 |
+
# save images, mask, and prediction map
|
332 |
+
if opt.save_figure:
|
333 |
+
unnormalized_image = data["unnormalized_image"]
|
334 |
+
# image_id = data['id'][0].split('.')[0]
|
335 |
+
image_id = Path(data["id"][0]).stem
|
336 |
+
save_image(
|
337 |
+
(
|
338 |
+
outputs["ensemble"]["out_map"][0, ...] > opt.mask_threshold
|
339 |
+
).float()
|
340 |
+
* 255,
|
341 |
+
os.path.join(figure_path, f"{image_id}_ensemble_map.png"),
|
342 |
+
)
|
343 |
+
|
344 |
+
image_metrics = update_image_roc_auc_metric(
|
345 |
+
opt.modality + ["ensemble"],
|
346 |
+
roc_auc_elements,
|
347 |
+
{
|
348 |
+
modality: metric_logger.meters[f"pixel_f1/{modality}"].avg
|
349 |
+
for modality in itertools.chain(opt.modality, ["ensemble"])
|
350 |
+
},
|
351 |
+
)
|
352 |
+
metric_logger.update(**image_metrics)
|
353 |
+
|
354 |
+
metric_logger.prepend_subprefix(f"{dataset_title.upper()}_")
|
355 |
+
metric_logger.write_tensorboard(epoch)
|
356 |
+
print("Average status:")
|
357 |
+
print(metric_logger.stat_table())
|
358 |
+
|
359 |
+
return metric_logger.get_meters()
|
360 |
+
|
361 |
+
|
362 |
+
def update_image_roc_auc_metric(modalities: List, roc_auc_elements, pixel_f1=None):
|
363 |
+
|
364 |
+
result = {}
|
365 |
+
for modality in modalities:
|
366 |
+
image_metrics = misc.calculate_img_score(
|
367 |
+
np.array(roc_auc_elements[modality]["map_scores"]) > 0.5,
|
368 |
+
(np.array(roc_auc_elements["labels"]) > 0).astype(np.int),
|
369 |
+
suffix=f"/{modality}",
|
370 |
+
)
|
371 |
+
if pixel_f1 is not None:
|
372 |
+
image_f1 = image_metrics[f"image_f1/{modality}"]
|
373 |
+
combined_f1 = hmean([image_f1, pixel_f1[modality]])
|
374 |
+
image_metrics[f"comb_f1/{modality}"] = float(combined_f1)
|
375 |
+
if 0.0 in roc_auc_elements["labels"] and 1.0 in roc_auc_elements["labels"]:
|
376 |
+
image_auc = metrics.roc_auc_score(
|
377 |
+
roc_auc_elements["labels"], roc_auc_elements[modality]["map_scores"]
|
378 |
+
)
|
379 |
+
image_metrics[f"image_auc/{modality}"] = image_auc
|
380 |
+
result.update(image_metrics)
|
381 |
+
|
382 |
+
return result
|
383 |
+
|
384 |
+
|
385 |
+
def pad_image(image, target_size):
|
386 |
+
image_size = image.shape[-2:]
|
387 |
+
if image_size != target_size:
|
388 |
+
row_missing = target_size[0] - image_size[0]
|
389 |
+
col_missing = target_size[1] - image_size[1]
|
390 |
+
image = nn.functional.pad(
|
391 |
+
image, (0, row_missing, 0, col_missing), "constant", 0
|
392 |
+
)
|
393 |
+
return image
|
394 |
+
|
395 |
+
|
396 |
+
def sliding_predict(model: nn.Module, data, tile_size, tile_overlap, opt):
|
397 |
+
image = data["image"]
|
398 |
+
mask = data["mask"]
|
399 |
+
image = image.to(opt.device)
|
400 |
+
image_size = image.shape[-2:]
|
401 |
+
stride = ceil(tile_size * (1 - tile_overlap))
|
402 |
+
tile_rows = int(ceil((image_size[0] - tile_size) / stride) + 1)
|
403 |
+
tile_cols = int(ceil((image_size[1] - tile_size) / stride) + 1)
|
404 |
+
result = {}
|
405 |
+
for modality in itertools.chain(opt.modality, ["ensemble"]):
|
406 |
+
result[modality] = {
|
407 |
+
"out_map": torch.zeros_like(
|
408 |
+
mask, requires_grad=False, dtype=torch.float32, device=opt.device
|
409 |
+
),
|
410 |
+
"out_vol_map": torch.zeros_like(
|
411 |
+
mask, requires_grad=False, dtype=torch.float32, device=opt.device
|
412 |
+
),
|
413 |
+
}
|
414 |
+
map_counter = torch.zeros_like(
|
415 |
+
mask, requires_grad=False, dtype=torch.float32, device=opt.device
|
416 |
+
)
|
417 |
+
|
418 |
+
with torch.no_grad():
|
419 |
+
for row in range(tile_rows):
|
420 |
+
for col in range(tile_cols):
|
421 |
+
x1 = int(col * stride)
|
422 |
+
y1 = int(row * stride)
|
423 |
+
x2 = min(x1 + tile_size, image_size[1])
|
424 |
+
y2 = min(y1 + tile_size, image_size[0])
|
425 |
+
x1 = max(int(x2 - tile_size), 0)
|
426 |
+
y1 = max(int(y2 - tile_size), 0)
|
427 |
+
|
428 |
+
image_tile = image[:, :, y1:y2, x1:x2]
|
429 |
+
image_tile = pad_image(image_tile, [opt.tile_size, opt.tile_size])
|
430 |
+
tile_outputs = model(image_tile, seg_size=(image_tile.shape[-2:]))
|
431 |
+
for modality in itertools.chain(opt.modality, ["ensemble"]):
|
432 |
+
result[modality]["out_map"][:, :, y1:y2, x1:x2] += tile_outputs[
|
433 |
+
modality
|
434 |
+
]["out_map"][:, :, : y2 - y1, : x2 - x1]
|
435 |
+
out_vol_map = get_volume_seg_map(
|
436 |
+
tile_outputs[modality]["out_vol"],
|
437 |
+
size=image_tile.shape[-2:],
|
438 |
+
label=data["label"],
|
439 |
+
kmeans=KMeans(2) if opt.consistency_kmeans else None,
|
440 |
+
)[:, :, : y2 - y1, : x2 - x1]
|
441 |
+
result[modality]["out_vol_map"][:, :, y1:y2, x1:x2] += out_vol_map
|
442 |
+
map_counter[:, :, y1:y2, x1:x2] += 1
|
443 |
+
|
444 |
+
for modality in itertools.chain(opt.modality, ["ensemble"]):
|
445 |
+
result[modality]["out_map"] /= map_counter
|
446 |
+
result[modality]["out_vol_map"] /= map_counter
|
447 |
+
result[modality]["map_pred"] = (
|
448 |
+
result[modality]["out_map"].max().unsqueeze(0)
|
449 |
+
)
|
450 |
+
result[modality]["vol_pred"] = (
|
451 |
+
result[modality]["out_vol_map"].max().unsqueeze(0)
|
452 |
+
)
|
453 |
+
|
454 |
+
return result
|
losses/__init__.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .bundled_loss import BundledLoss
|
2 |
+
from .consisitency_loss import get_consistency_loss, get_volume_seg_map
|
3 |
+
from .entropy_loss import get_entropy_loss
|
4 |
+
from .loss import Loss
|
5 |
+
from .map_label_loss import get_map_label_loss
|
6 |
+
from .map_mask_loss import get_map_mask_loss
|
7 |
+
from .multi_view_consistency_loss import (
|
8 |
+
get_multi_view_consistency_loss,
|
9 |
+
get_spixel_tgt_map,
|
10 |
+
)
|
11 |
+
from .volume_label_loss import get_volume_label_loss
|
12 |
+
from .volume_mask_loss import get_volume_mask_loss
|
13 |
+
|
14 |
+
|
15 |
+
def get_bundled_loss(opt):
|
16 |
+
"""Loss function for the overeall training, including the multi-view
|
17 |
+
consistency loss."""
|
18 |
+
single_modality_loss = get_loss(opt)
|
19 |
+
multi_view_consistency_loss = get_multi_view_consistency_loss(opt)
|
20 |
+
volume_mask_loss = get_volume_mask_loss(opt)
|
21 |
+
bundled_loss = BundledLoss(
|
22 |
+
single_modality_loss,
|
23 |
+
multi_view_consistency_loss,
|
24 |
+
volume_mask_loss,
|
25 |
+
opt.mvc_weight,
|
26 |
+
opt.mvc_time_dependent,
|
27 |
+
opt.mvc_steepness,
|
28 |
+
opt.modality,
|
29 |
+
opt.consistency_weight,
|
30 |
+
opt.consistency_source,
|
31 |
+
)
|
32 |
+
|
33 |
+
return bundled_loss
|
34 |
+
|
35 |
+
|
36 |
+
def get_loss(opt):
|
37 |
+
"""Loss function for a single model, excluding the multi-view consistency
|
38 |
+
loss."""
|
39 |
+
map_label_loss = get_map_label_loss(opt)
|
40 |
+
volume_label_loss = get_volume_label_loss(opt)
|
41 |
+
map_mask_loss = get_map_mask_loss(opt)
|
42 |
+
volume_mask_loss = get_volume_mask_loss(opt)
|
43 |
+
consisitency_loss = get_consistency_loss(opt)
|
44 |
+
entropy_loss = get_entropy_loss(opt)
|
45 |
+
loss = Loss(
|
46 |
+
map_label_loss,
|
47 |
+
volume_label_loss,
|
48 |
+
map_mask_loss,
|
49 |
+
volume_mask_loss,
|
50 |
+
consisitency_loss,
|
51 |
+
entropy_loss,
|
52 |
+
opt.map_label_weight,
|
53 |
+
opt.volume_label_weight,
|
54 |
+
opt.map_mask_weight,
|
55 |
+
opt.volume_mask_weight,
|
56 |
+
opt.consistency_weight,
|
57 |
+
opt.map_entropy_weight,
|
58 |
+
opt.volume_entropy_weight,
|
59 |
+
opt.consistency_source,
|
60 |
+
)
|
61 |
+
return loss
|
losses/bundled_loss.py
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import math
|
2 |
+
from typing import Dict, List, Optional
|
3 |
+
|
4 |
+
import torch
|
5 |
+
import torch.nn as nn
|
6 |
+
|
7 |
+
|
8 |
+
class BundledLoss(nn.Module):
|
9 |
+
def __init__(
|
10 |
+
self,
|
11 |
+
single_modality_loss,
|
12 |
+
multi_view_consistency_loss,
|
13 |
+
volume_mask_loss,
|
14 |
+
multi_view_consistency_weight: float,
|
15 |
+
mvc_time_dependent: bool,
|
16 |
+
mvc_steepness: float,
|
17 |
+
modality: List,
|
18 |
+
consistency_weight: float,
|
19 |
+
consistency_source: str,
|
20 |
+
):
|
21 |
+
super().__init__()
|
22 |
+
|
23 |
+
self.single_modality_loss = single_modality_loss
|
24 |
+
self.multi_view_consistency_loss = multi_view_consistency_loss
|
25 |
+
self.volume_mask_loss = volume_mask_loss
|
26 |
+
|
27 |
+
self.mvc_weight = multi_view_consistency_weight
|
28 |
+
self.mvc_time_dependent = mvc_time_dependent
|
29 |
+
self.mvc_steepness = mvc_steepness
|
30 |
+
self.modality = modality
|
31 |
+
self.consistency_weight = consistency_weight
|
32 |
+
self.consistency_source = consistency_source
|
33 |
+
|
34 |
+
def forward(
|
35 |
+
self,
|
36 |
+
output: Dict,
|
37 |
+
label,
|
38 |
+
mask,
|
39 |
+
epoch: int = 1,
|
40 |
+
max_epoch: int = 70,
|
41 |
+
spixel=None,
|
42 |
+
raw_image=None,
|
43 |
+
):
|
44 |
+
|
45 |
+
total_loss = 0.0
|
46 |
+
loss_dict = {}
|
47 |
+
for modality in self.modality:
|
48 |
+
single_loss = self.single_modality_loss(output[modality], label, mask)
|
49 |
+
|
50 |
+
for k, v in single_loss.items():
|
51 |
+
loss_dict[f"{k}/{modality}"] = v
|
52 |
+
total_loss = total_loss + single_loss["total_loss"]
|
53 |
+
|
54 |
+
if self.mvc_time_dependent:
|
55 |
+
mvc_weight = self.mvc_weight * math.exp(
|
56 |
+
-self.mvc_steepness * (1 - epoch / max_epoch) ** 2
|
57 |
+
)
|
58 |
+
else:
|
59 |
+
mvc_weight = self.mvc_weight
|
60 |
+
|
61 |
+
multi_view_consistency_loss = self.multi_view_consistency_loss(
|
62 |
+
output, label, spixel, raw_image, mask
|
63 |
+
)
|
64 |
+
for k, v in multi_view_consistency_loss.items():
|
65 |
+
if k not in ["total_loss", "tgt_map"]:
|
66 |
+
loss_dict.update({k: v})
|
67 |
+
|
68 |
+
if self.consistency_weight != 0.0 and self.consistency_source == "ensemble":
|
69 |
+
for modality in self.modality:
|
70 |
+
consisitency_loss = self.volume_mask_loss(
|
71 |
+
output[modality]["out_vol"], multi_view_consistency_loss["tgt_map"]
|
72 |
+
)
|
73 |
+
consisitency_loss = consisitency_loss["loss"]
|
74 |
+
loss_dict[f"consistency_loss/{modality}"] = consisitency_loss
|
75 |
+
total_loss = (
|
76 |
+
total_loss
|
77 |
+
+ self.consistency_weight
|
78 |
+
* consisitency_loss
|
79 |
+
* math.exp(-self.mvc_steepness * (1 - epoch / max_epoch) ** 2)
|
80 |
+
)
|
81 |
+
|
82 |
+
total_loss = total_loss + mvc_weight * multi_view_consistency_loss["total_loss"]
|
83 |
+
|
84 |
+
return {"total_loss": total_loss, **loss_dict}
|
losses/consisitency_loss.py
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
from einops import rearrange
|
4 |
+
from fast_pytorch_kmeans import KMeans
|
5 |
+
|
6 |
+
|
7 |
+
def get_consistency_loss(opt):
|
8 |
+
loss = ConsistencyLoss(
|
9 |
+
opt.consistency_type, opt.consistency_kmeans, opt.consistency_stop_map_grad
|
10 |
+
)
|
11 |
+
return loss
|
12 |
+
|
13 |
+
|
14 |
+
class ConsistencyLoss(nn.Module):
|
15 |
+
def __init__(
|
16 |
+
self, loss: str, do_kmeans: bool = True, consistency_stop_map_grad: bool = False
|
17 |
+
):
|
18 |
+
super().__init__()
|
19 |
+
assert loss in ["l1", "l2"]
|
20 |
+
|
21 |
+
if loss == "l1":
|
22 |
+
self.consistency_loss = nn.L1Loss(reduction="mean")
|
23 |
+
else: # l2
|
24 |
+
self.consistency_loss = nn.MSELoss(reduction="mean")
|
25 |
+
|
26 |
+
self.do_kmeans = do_kmeans
|
27 |
+
if do_kmeans:
|
28 |
+
self.kmeans = KMeans(2)
|
29 |
+
else:
|
30 |
+
self.kmeans = None
|
31 |
+
|
32 |
+
self.consistency_stop_map_grad = consistency_stop_map_grad
|
33 |
+
|
34 |
+
def forward(self, out_volume, out_map, label):
|
35 |
+
map_shape = out_map.shape[-2:]
|
36 |
+
out_volume = get_volume_seg_map(out_volume, map_shape, label, self.kmeans)
|
37 |
+
if self.consistency_stop_map_grad:
|
38 |
+
loss = self.consistency_loss(out_volume, out_map.detach())
|
39 |
+
else:
|
40 |
+
loss = self.consistency_loss(out_volume, out_map)
|
41 |
+
return {"loss": loss, "out_vol": out_volume.squeeze(1)}
|
42 |
+
|
43 |
+
|
44 |
+
def get_volume_seg_map(volume, size, label, kmeans=None):
|
45 |
+
"""volume is of shape [b, h, w, h, w], and size is [h', w']"""
|
46 |
+
batch_size = volume.shape[0]
|
47 |
+
volume_shape = volume.shape[-2:]
|
48 |
+
volume = rearrange(volume, "b h1 w1 h2 w2 -> b (h1 w1) (h2 w2)")
|
49 |
+
if kmeans is not None: # do k-means on out_volume
|
50 |
+
for i in range(batch_size):
|
51 |
+
# NOTE K-means only applies for manipulated images!
|
52 |
+
if label[i] == 0:
|
53 |
+
continue
|
54 |
+
batch_volume = volume[i, ...]
|
55 |
+
out = kmeans.fit_predict(batch_volume)
|
56 |
+
ones = torch.where(out == 1)
|
57 |
+
zeros = torch.where(out == 0)
|
58 |
+
if (
|
59 |
+
ones[0].numel() >= zeros[0].numel()
|
60 |
+
): # intuitively, the cluster with fewer elements is the modified cluster
|
61 |
+
pristine, modified = ones, zeros
|
62 |
+
else:
|
63 |
+
pristine, modified = zeros, ones
|
64 |
+
volume[i, :, modified[0]] = 1 - volume[i, :, modified[0]]
|
65 |
+
|
66 |
+
volume = volume.mean(dim=-1)
|
67 |
+
volume = rearrange(volume, "b (h w) -> b h w", h=volume_shape[0])
|
68 |
+
volume = volume.unsqueeze(1)
|
69 |
+
if volume_shape != size:
|
70 |
+
volume = nn.functional.interpolate(
|
71 |
+
volume, size=size, mode="bilinear", align_corners=False
|
72 |
+
)
|
73 |
+
return volume # size [b, 1, h, w]
|
losses/entropy_loss.py
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
|
4 |
+
|
5 |
+
def get_entropy_loss(opt):
|
6 |
+
return EntropyLoss()
|
7 |
+
|
8 |
+
|
9 |
+
class EntropyLoss(nn.Module):
|
10 |
+
def __init__(self):
|
11 |
+
super().__init__()
|
12 |
+
self.exp = 1e-7
|
13 |
+
assert self.exp < 0.5
|
14 |
+
|
15 |
+
def forward(self, item):
|
16 |
+
item = item.clamp(min=self.exp, max=1 - self.exp)
|
17 |
+
entropy = -item * torch.log(item) - (1 - item) * torch.log(1 - item)
|
18 |
+
entropy = entropy.mean()
|
19 |
+
|
20 |
+
return {"loss": entropy}
|
losses/loss.py
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
|
4 |
+
|
5 |
+
class Loss(nn.Module):
|
6 |
+
def __init__(
|
7 |
+
self,
|
8 |
+
map_label_loss,
|
9 |
+
volume_label_loss,
|
10 |
+
map_mask_loss,
|
11 |
+
volume_mask_loss,
|
12 |
+
consistency_loss,
|
13 |
+
entropy_loss,
|
14 |
+
map_label_weight,
|
15 |
+
volume_label_weight,
|
16 |
+
map_mask_weight,
|
17 |
+
volume_mask_weight,
|
18 |
+
consistency_weight,
|
19 |
+
map_entropy_weight,
|
20 |
+
volume_entropy_weight,
|
21 |
+
consistency_source,
|
22 |
+
):
|
23 |
+
super().__init__()
|
24 |
+
|
25 |
+
self.map_label_loss = map_label_loss
|
26 |
+
self.volume_label_loss = volume_label_loss
|
27 |
+
self.map_mask_loss = map_mask_loss
|
28 |
+
self.volume_mask_loss = volume_mask_loss
|
29 |
+
self.consistency_loss = consistency_loss
|
30 |
+
self.entropy_loss = entropy_loss
|
31 |
+
|
32 |
+
self.map_label_weight = map_label_weight
|
33 |
+
self.volume_label_weight = volume_label_weight
|
34 |
+
self.map_mask_weight = map_mask_weight
|
35 |
+
self.volume_mask_weight = volume_mask_weight
|
36 |
+
self.consistency_weight = consistency_weight
|
37 |
+
self.map_entropy_weight = map_entropy_weight
|
38 |
+
self.volume_entropy_weight = volume_entropy_weight
|
39 |
+
self.consistency_source = consistency_source
|
40 |
+
|
41 |
+
def forward(self, output, label, mask):
|
42 |
+
total_loss = 0.0
|
43 |
+
loss_dict = {}
|
44 |
+
|
45 |
+
# --- label loss ---
|
46 |
+
label = label.float()
|
47 |
+
# compute map label loss anyway
|
48 |
+
map_label_loss = self.map_label_loss(
|
49 |
+
output["map_pred"], output["out_map"], label
|
50 |
+
)["loss"]
|
51 |
+
total_loss = total_loss + self.map_label_weight * map_label_loss
|
52 |
+
loss_dict.update({"map_label_loss": map_label_loss})
|
53 |
+
|
54 |
+
if self.volume_label_weight != 0.0:
|
55 |
+
volume_label_loss = self.volume_label_loss(
|
56 |
+
output["vol_pred"], output["out_vol"], label
|
57 |
+
)["loss"]
|
58 |
+
total_loss = total_loss + self.volume_label_weight * volume_label_loss
|
59 |
+
loss_dict.update({"vol_label_loss": volume_label_loss})
|
60 |
+
|
61 |
+
# --- mask loss ---
|
62 |
+
# compute map mask loss anyway
|
63 |
+
map_mask_loss = self.map_mask_loss(output["out_map"], mask)["loss"]
|
64 |
+
total_loss = total_loss + self.map_mask_weight * map_mask_loss
|
65 |
+
loss_dict.update({"map_mask_loss": map_mask_loss})
|
66 |
+
|
67 |
+
if self.volume_mask_weight != 0.0:
|
68 |
+
volume_mask_loss = self.volume_mask_loss(output["out_vol"], mask)["loss"]
|
69 |
+
total_loss = total_loss + self.volume_mask_weight * volume_mask_loss
|
70 |
+
loss_dict.update({"vol_mask_loss": volume_mask_loss})
|
71 |
+
|
72 |
+
# --- self-consistency loss ---
|
73 |
+
if self.consistency_weight != 0.0 and self.consistency_source == "self":
|
74 |
+
consistency_loss = self.consistency_loss(
|
75 |
+
output["out_vol"], output["out_map"], label
|
76 |
+
)
|
77 |
+
consistency_loss = consistency_loss["loss"]
|
78 |
+
total_loss = total_loss + self.consistency_weight * consistency_loss
|
79 |
+
loss_dict.update({"consistency_loss": consistency_loss})
|
80 |
+
|
81 |
+
# --- entropy loss ---
|
82 |
+
if self.map_entropy_weight != 0.0:
|
83 |
+
map_entropy_loss = self.entropy_loss(output["out_map"])["loss"]
|
84 |
+
total_loss = total_loss + self.map_entropy_weight * map_entropy_loss
|
85 |
+
loss_dict.update({"map_entropy_loss": map_entropy_loss})
|
86 |
+
|
87 |
+
if self.volume_entropy_weight != 0:
|
88 |
+
volume_entropy_loss = self.entropy_loss(output["out_vol"])["loss"]
|
89 |
+
total_loss = total_loss + self.volume_entropy_weight * volume_entropy_loss
|
90 |
+
loss_dict.update({"vol_entropy_loss": volume_entropy_loss})
|
91 |
+
|
92 |
+
loss_dict.update({"total_loss": total_loss})
|
93 |
+
return loss_dict
|
losses/map_label_loss.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
|
4 |
+
|
5 |
+
def get_map_label_loss(opt):
|
6 |
+
return MapLabelLoss(opt.label_loss_on_whole_map)
|
7 |
+
|
8 |
+
|
9 |
+
class MapLabelLoss(nn.Module):
|
10 |
+
def __init__(self, label_loss_on_whole_map=False):
|
11 |
+
super().__init__()
|
12 |
+
|
13 |
+
self.bce_loss = nn.BCELoss(reduction="none")
|
14 |
+
self.label_loss_on_whole_map = label_loss_on_whole_map
|
15 |
+
|
16 |
+
def forward(self, pred, out_map, label):
|
17 |
+
batch_size = label.shape[0]
|
18 |
+
if (
|
19 |
+
self.label_loss_on_whole_map
|
20 |
+
): # apply the loss on the whole map for pristine images
|
21 |
+
total_loss = 0
|
22 |
+
for i in range(batch_size):
|
23 |
+
if label[i] == 0: # pristine
|
24 |
+
total_loss = (
|
25 |
+
total_loss
|
26 |
+
+ self.bce_loss(out_map[i, ...].mean(), label[i]).mean()
|
27 |
+
)
|
28 |
+
else: # modified
|
29 |
+
total_loss = total_loss + self.bce_loss(pred[i], label[i]).mean()
|
30 |
+
loss = total_loss / batch_size
|
31 |
+
else:
|
32 |
+
loss = self.bce_loss(pred, label)
|
33 |
+
loss = loss.mean()
|
34 |
+
return {"loss": loss}
|
losses/map_mask_loss.py
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
|
4 |
+
|
5 |
+
def get_map_mask_loss(opt):
|
6 |
+
return MapMaskLoss()
|
7 |
+
|
8 |
+
|
9 |
+
class MapMaskLoss(nn.Module):
|
10 |
+
def __init__(self):
|
11 |
+
super().__init__()
|
12 |
+
|
13 |
+
self.bce_loss = nn.BCELoss(reduction="mean")
|
14 |
+
|
15 |
+
def forward(self, out_map, mask):
|
16 |
+
mask_size = mask.shape[-2:]
|
17 |
+
if out_map.shape[-2:] != mask_size:
|
18 |
+
out_map = nn.functional.interpolate(
|
19 |
+
out_map, size=mask_size, mode="bilinear", align_corners=False
|
20 |
+
)
|
21 |
+
loss = self.bce_loss(out_map, mask)
|
22 |
+
return {"loss": loss}
|
23 |
+
|
24 |
+
|
25 |
+
if __name__ == "__main__":
|
26 |
+
map_mask_loss = MapMaskLoss()
|
losses/multi_view_consistency_loss.py
ADDED
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Dict, List
|
2 |
+
|
3 |
+
import matplotlib.pyplot as plt
|
4 |
+
import numpy as np
|
5 |
+
import torch
|
6 |
+
import torch.nn as nn
|
7 |
+
from skimage import segmentation
|
8 |
+
|
9 |
+
|
10 |
+
def get_multi_view_consistency_loss(opt):
|
11 |
+
loss = MultiViewConsistencyLoss(
|
12 |
+
opt.mvc_soft,
|
13 |
+
opt.mvc_zeros_on_au,
|
14 |
+
opt.mvc_single_weight,
|
15 |
+
opt.modality,
|
16 |
+
opt.mvc_spixel,
|
17 |
+
opt.mvc_num_spixel,
|
18 |
+
)
|
19 |
+
return loss
|
20 |
+
|
21 |
+
|
22 |
+
class MultiViewConsistencyLoss(nn.Module):
|
23 |
+
def __init__(
|
24 |
+
self,
|
25 |
+
soft: bool,
|
26 |
+
zeros_on_au: bool,
|
27 |
+
single_weight: Dict,
|
28 |
+
modality: List,
|
29 |
+
spixel: bool = False,
|
30 |
+
num_spixel: int = 100,
|
31 |
+
eps: float = 1e-4,
|
32 |
+
):
|
33 |
+
super().__init__()
|
34 |
+
self.soft = soft
|
35 |
+
self.zeros_on_au = zeros_on_au
|
36 |
+
self.single_weight = single_weight
|
37 |
+
self.modality = modality
|
38 |
+
self.spixel = spixel
|
39 |
+
self.num_spixel = num_spixel
|
40 |
+
self.eps = eps
|
41 |
+
|
42 |
+
self.mse_loss = nn.MSELoss(reduction="mean")
|
43 |
+
|
44 |
+
def forward(self, output: Dict, label, spixel=None, image=None, mask=None):
|
45 |
+
|
46 |
+
tgt_map = torch.zeros_like(
|
47 |
+
output[self.modality[0]]["out_map"], requires_grad=False
|
48 |
+
)
|
49 |
+
with torch.no_grad():
|
50 |
+
for modality in self.modality:
|
51 |
+
weight = self.single_weight[modality.lower()]
|
52 |
+
tgt_map = tgt_map + weight * output[modality]["out_map"]
|
53 |
+
|
54 |
+
if self.spixel:
|
55 |
+
# raw_tgt_map = tgt_map.clone()
|
56 |
+
tgt_map = get_spixel_tgt_map(tgt_map, spixel)
|
57 |
+
|
58 |
+
if not self.soft:
|
59 |
+
for b in range(tgt_map.shape[0]):
|
60 |
+
if tgt_map[b, ...].max() <= 0.5 and label[b] == 1.0:
|
61 |
+
tgt_map[b, ...][
|
62 |
+
torch.where(tgt_map[b, ...] == torch.max(tgt_map[b, ...]))
|
63 |
+
] = 1.0
|
64 |
+
tgt_map[torch.where(tgt_map > 0.5)] = 1
|
65 |
+
tgt_map[torch.where(tgt_map <= 0.5)] = 0
|
66 |
+
tgt_map[torch.where(label == 0.0)[0], ...] = 0.0
|
67 |
+
|
68 |
+
if self.zeros_on_au:
|
69 |
+
tgt_map[torch.where(label == 0.0)[0], ...] = 0.0
|
70 |
+
|
71 |
+
total_loss = 0.0
|
72 |
+
loss_dict = {}
|
73 |
+
for modality in self.modality:
|
74 |
+
loss = self.mse_loss(output[modality]["out_map"], tgt_map)
|
75 |
+
loss_dict[f"multi_view_consistency_loss_{modality}"] = loss
|
76 |
+
total_loss = total_loss + loss
|
77 |
+
|
78 |
+
return {**loss_dict, "tgt_map": tgt_map, "total_loss": total_loss}
|
79 |
+
|
80 |
+
def _save(
|
81 |
+
self,
|
82 |
+
spixel: torch.Tensor,
|
83 |
+
image: torch.Tensor,
|
84 |
+
mask: torch.Tensor,
|
85 |
+
tgt_map: torch.Tensor,
|
86 |
+
raw_tgt_map: torch.Tensor,
|
87 |
+
out_path: str = "tmp/spixel_tgt_map.png",
|
88 |
+
):
|
89 |
+
spixel = spixel.permute(0, 2, 3, 1).detach().cpu().numpy()
|
90 |
+
image = image.permute(0, 2, 3, 1).detach().cpu().numpy()
|
91 |
+
mask = mask.permute(0, 2, 3, 1).detach().cpu().numpy() * 255.0
|
92 |
+
tgt_map = tgt_map.permute(0, 2, 3, 1).squeeze(3).detach().cpu().numpy() * 255.0
|
93 |
+
raw_tgt_map = (
|
94 |
+
raw_tgt_map.permute(0, 2, 3, 1).squeeze(3).detach().cpu().numpy() * 255.0
|
95 |
+
)
|
96 |
+
bn = spixel.shape[0]
|
97 |
+
i = 1
|
98 |
+
for b in range(bn):
|
99 |
+
plt.subplot(bn, 5, i)
|
100 |
+
i += 1
|
101 |
+
plt.imshow(image[b])
|
102 |
+
plt.axis("off")
|
103 |
+
plt.title("image")
|
104 |
+
plt.subplot(bn, 5, i)
|
105 |
+
i += 1
|
106 |
+
plt.imshow(mask[b])
|
107 |
+
plt.axis("off")
|
108 |
+
plt.title("mask")
|
109 |
+
plt.subplot(bn, 5, i)
|
110 |
+
i += 1
|
111 |
+
plt.imshow(spixel[b])
|
112 |
+
plt.axis("off")
|
113 |
+
plt.title("superpixel")
|
114 |
+
plt.subplot(bn, 5, i)
|
115 |
+
i += 1
|
116 |
+
plt.imshow(raw_tgt_map[b])
|
117 |
+
plt.axis("off")
|
118 |
+
plt.title("raw target map")
|
119 |
+
plt.subplot(bn, 5, i)
|
120 |
+
i += 1
|
121 |
+
plt.imshow(tgt_map[b])
|
122 |
+
plt.axis("off")
|
123 |
+
plt.title("target map")
|
124 |
+
plt.tight_layout()
|
125 |
+
plt.savefig(out_path, dpi=300)
|
126 |
+
plt.close()
|
127 |
+
|
128 |
+
|
129 |
+
def get_spixel_tgt_map(weighted_sum, spixel):
|
130 |
+
b, _, h, w = weighted_sum.shape
|
131 |
+
spixel_tgt_map = torch.zeros_like(weighted_sum, requires_grad=False)
|
132 |
+
|
133 |
+
for bidx in range(b):
|
134 |
+
spixel_indices = spixel[bidx, ...].unique()
|
135 |
+
# num_spixel = spixel_idx.shape[0]
|
136 |
+
for spixel_idx in spixel_indices.tolist():
|
137 |
+
area = (spixel[bidx, ...] == spixel_idx).sum()
|
138 |
+
weighted_sum_in_area = weighted_sum[bidx, ...][
|
139 |
+
torch.where(spixel[bidx, ...] == spixel_idx)
|
140 |
+
].sum()
|
141 |
+
avg_area = weighted_sum_in_area / area
|
142 |
+
# this is soft map, and the threshold process will be conducted in the forward function
|
143 |
+
spixel_tgt_map[bidx][
|
144 |
+
torch.where(spixel[bidx, ...] == spixel_idx)
|
145 |
+
] = avg_area
|
146 |
+
|
147 |
+
return spixel_tgt_map
|
148 |
+
|
149 |
+
|
150 |
+
if __name__ == "__main__":
|
151 |
+
mvc_loss = MultiViewConsistencyLoss(True, True, [1, 1, 2])
|
152 |
+
print("a")
|
losses/volume_label_loss.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch.nn as nn
|
2 |
+
|
3 |
+
|
4 |
+
def get_volume_label_loss(opt):
|
5 |
+
return VolumeLabelLoss()
|
6 |
+
|
7 |
+
|
8 |
+
class VolumeLabelLoss(nn.Module):
|
9 |
+
def __init__(self):
|
10 |
+
super().__init__()
|
11 |
+
|
12 |
+
self.BCE_loss = nn.BCELoss(reduction="mean")
|
13 |
+
|
14 |
+
def forward(self, pred, volume, label):
|
15 |
+
loss = self.BCE_loss(pred, label)
|
16 |
+
return {"loss": loss}
|
losses/volume_mask_loss.py
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
from einops import rearrange
|
4 |
+
|
5 |
+
|
6 |
+
def get_volume_mask_loss(opt):
|
7 |
+
return VolumeMaskLoss()
|
8 |
+
|
9 |
+
|
10 |
+
class VolumeMaskLoss(nn.Module):
|
11 |
+
def __init__(self):
|
12 |
+
super().__init__()
|
13 |
+
|
14 |
+
self.bce_loss = nn.BCELoss(reduction="mean")
|
15 |
+
|
16 |
+
def _get_volume_mask(self, mask):
|
17 |
+
with torch.no_grad():
|
18 |
+
h, w = mask.shape[-2:]
|
19 |
+
# use orthogonal vector [0, 1] and [1, 0] to generate the ground truth
|
20 |
+
mask[torch.where(mask > 0.5)] = 1.0
|
21 |
+
mask[torch.where(mask <= 0.5)] = 0.0
|
22 |
+
|
23 |
+
mask = rearrange(mask, "b c h w -> b c (h w)")
|
24 |
+
mask_append = 1 - mask.clone()
|
25 |
+
mask = torch.cat([mask, mask_append], dim=1)
|
26 |
+
mask = torch.bmm(mask.transpose(-1, -2), mask)
|
27 |
+
mask = rearrange(mask, "b (h1 w1) (h2 w2) -> b h1 w1 h2 w2", h1=h, h2=h)
|
28 |
+
mask = 1 - mask # 0 indicates consistency, and 1 indicates inconsistency
|
29 |
+
return mask
|
30 |
+
|
31 |
+
def forward(self, out_volume, mask):
|
32 |
+
volume_size = out_volume.shape[-2:]
|
33 |
+
if volume_size != mask.shape[-2:]:
|
34 |
+
mask = nn.functional.interpolate(
|
35 |
+
mask, size=volume_size, mode="bilinear", align_corners=False
|
36 |
+
)
|
37 |
+
volume_mask = self._get_volume_mask(mask)
|
38 |
+
loss = self.bce_loss(out_volume, volume_mask)
|
39 |
+
|
40 |
+
return {"loss": loss}
|
main.py
ADDED
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import datetime
|
2 |
+
import math
|
3 |
+
import os
|
4 |
+
from functools import partial
|
5 |
+
|
6 |
+
import albumentations as A
|
7 |
+
import torch.optim as optim
|
8 |
+
from termcolor import cprint
|
9 |
+
from timm.scheduler import create_scheduler
|
10 |
+
from torch.utils.data import DataLoader
|
11 |
+
|
12 |
+
import utils.misc as misc
|
13 |
+
from datasets import crop_to_smallest_collate_fn, get_dataset
|
14 |
+
from engine import bundled_evaluate, train
|
15 |
+
from losses import get_bundled_loss, get_loss
|
16 |
+
from models import get_ensemble_model, get_single_modal_model
|
17 |
+
from opt import get_opt
|
18 |
+
|
19 |
+
|
20 |
+
def main(opt):
|
21 |
+
# get tensorboard writer
|
22 |
+
writer = misc.setup_env(opt)
|
23 |
+
|
24 |
+
# dataset
|
25 |
+
# training sets
|
26 |
+
train_loaders = {}
|
27 |
+
if not opt.eval:
|
28 |
+
train_transform = A.Compose(
|
29 |
+
[
|
30 |
+
A.HorizontalFlip(0.5),
|
31 |
+
A.SmallestMaxSize(int(opt.input_size * 1.5))
|
32 |
+
if opt.resize_aug
|
33 |
+
else A.NoOp(),
|
34 |
+
A.RandomSizedCrop(
|
35 |
+
(opt.input_size, int(opt.input_size * 1.5)),
|
36 |
+
opt.input_size,
|
37 |
+
opt.input_size,
|
38 |
+
)
|
39 |
+
if opt.resize_aug
|
40 |
+
else A.NoOp(),
|
41 |
+
A.NoOp() if opt.no_gaussian_blur else A.GaussianBlur(p=0.5),
|
42 |
+
A.NoOp() if opt.no_color_jitter else A.ColorJitter(p=0.5),
|
43 |
+
A.NoOp() if opt.no_jpeg_compression else A.ImageCompression(p=0.5),
|
44 |
+
]
|
45 |
+
)
|
46 |
+
train_sets = get_dataset(opt.train_datalist, "train", train_transform, opt)
|
47 |
+
for k, dataset in train_sets.items():
|
48 |
+
train_loaders[k] = DataLoader(
|
49 |
+
dataset,
|
50 |
+
batch_size=opt.batch_size,
|
51 |
+
shuffle=True,
|
52 |
+
pin_memory=True,
|
53 |
+
num_workers=0 if opt.debug else opt.num_workers,
|
54 |
+
collate_fn=partial(
|
55 |
+
crop_to_smallest_collate_fn,
|
56 |
+
max_size=opt.input_size,
|
57 |
+
uncorrect_label=opt.uncorrect_label,
|
58 |
+
),
|
59 |
+
)
|
60 |
+
# validation sets
|
61 |
+
if opt.large_image_strategy == "rescale":
|
62 |
+
val_transform = A.Compose([A.SmallestMaxSize(opt.tile_size)])
|
63 |
+
else:
|
64 |
+
val_transform = None
|
65 |
+
val_sets = get_dataset(opt.val_datalist, opt.val_set, val_transform, opt)
|
66 |
+
val_loaders = {}
|
67 |
+
for k, dataset in val_sets.items():
|
68 |
+
val_loaders[k] = DataLoader(
|
69 |
+
dataset,
|
70 |
+
batch_size=1,
|
71 |
+
shuffle=opt.val_shuffle,
|
72 |
+
pin_memory=True,
|
73 |
+
num_workers=0 if opt.debug else opt.num_workers,
|
74 |
+
)
|
75 |
+
|
76 |
+
# multi-view models and optimizers
|
77 |
+
optimizer_dict = {}
|
78 |
+
scheduler_dict = {}
|
79 |
+
model = get_ensemble_model(opt).to(opt.device)
|
80 |
+
n_param = sum(p.numel() for p in model.parameters() if p.requires_grad)
|
81 |
+
print(
|
82 |
+
f"Number of total params: {n_param}, num params per model: {int(n_param / len(opt.modality))}"
|
83 |
+
)
|
84 |
+
|
85 |
+
# optimizer and scheduler
|
86 |
+
for modality in opt.modality:
|
87 |
+
if opt.optimizer.lower() == "adamw":
|
88 |
+
optimizer = optim.AdamW(
|
89 |
+
model.sub_models[modality].parameters(),
|
90 |
+
opt.lr,
|
91 |
+
weight_decay=opt.weight_decay,
|
92 |
+
)
|
93 |
+
elif opt.optimizer.lower() == "sgd":
|
94 |
+
optimizer = optim.SGD(
|
95 |
+
model.sub_models[modality].parameters(),
|
96 |
+
opt.lr,
|
97 |
+
opt.momentum,
|
98 |
+
weight_decay=opt.weight_decay,
|
99 |
+
)
|
100 |
+
else:
|
101 |
+
raise RuntimeError(f"Unsupported optimizer {opt.optimizer}.")
|
102 |
+
|
103 |
+
scheduler, num_epoch = create_scheduler(opt, optimizer)
|
104 |
+
|
105 |
+
optimizer_dict[modality] = optimizer
|
106 |
+
scheduler_dict[modality] = scheduler
|
107 |
+
opt.epochs = num_epoch
|
108 |
+
|
109 |
+
# loss functions
|
110 |
+
# loss function including the multi-view consistency loss, for training
|
111 |
+
bundled_criterion = get_bundled_loss(opt).to(opt.device)
|
112 |
+
# loss function excluding the multi-view consistency loss, for evaluation
|
113 |
+
single_criterion = get_loss(opt).to(opt.device)
|
114 |
+
|
115 |
+
if opt.resume:
|
116 |
+
misc.resume_from(model, opt.resume)
|
117 |
+
|
118 |
+
if opt.eval:
|
119 |
+
bundled_evaluate(
|
120 |
+
model, val_loaders, single_criterion, 0, writer, suffix="val", opt=opt
|
121 |
+
)
|
122 |
+
return
|
123 |
+
|
124 |
+
cprint("The training will last for {} epochs.".format(opt.epochs), "blue")
|
125 |
+
best_ensemble_image_f1 = -math.inf
|
126 |
+
for epoch in range(opt.epochs):
|
127 |
+
for title, dataloader in train_loaders.items():
|
128 |
+
train(
|
129 |
+
model,
|
130 |
+
dataloader,
|
131 |
+
title,
|
132 |
+
optimizer_dict,
|
133 |
+
bundled_criterion,
|
134 |
+
epoch,
|
135 |
+
writer,
|
136 |
+
suffix="train",
|
137 |
+
opt=opt,
|
138 |
+
)
|
139 |
+
for sched_idx, scheduler in enumerate(scheduler_dict.values()):
|
140 |
+
if sched_idx == 0 and writer is not None:
|
141 |
+
writer.add_scalar("lr", scheduler._get_lr(epoch)[0], epoch)
|
142 |
+
scheduler.step(epoch)
|
143 |
+
|
144 |
+
if (epoch + 1) % opt.eval_freq == 0 or epoch in [opt.epochs - 1]:
|
145 |
+
result = bundled_evaluate(
|
146 |
+
model,
|
147 |
+
val_loaders,
|
148 |
+
single_criterion,
|
149 |
+
epoch,
|
150 |
+
writer,
|
151 |
+
suffix="val",
|
152 |
+
opt=opt,
|
153 |
+
)
|
154 |
+
misc.save_model(
|
155 |
+
os.path.join(
|
156 |
+
opt.save_root_path, opt.dir_name, "checkpoint", f"{epoch}.pt"
|
157 |
+
),
|
158 |
+
model,
|
159 |
+
epoch,
|
160 |
+
opt,
|
161 |
+
performance=result,
|
162 |
+
)
|
163 |
+
if result["image_f1/AVG_ensemble"] > best_ensemble_image_f1:
|
164 |
+
best_ensemble_image_f1 = result["image_f1/AVG_ensemble"]
|
165 |
+
misc.save_model(
|
166 |
+
os.path.join(
|
167 |
+
opt.save_root_path, opt.dir_name, "checkpoint", "best.pt"
|
168 |
+
),
|
169 |
+
model,
|
170 |
+
epoch,
|
171 |
+
opt,
|
172 |
+
performance=result,
|
173 |
+
)
|
174 |
+
misc.update_record(result, epoch, opt, "best_record")
|
175 |
+
misc.update_record(result, epoch, opt, "latest_record")
|
176 |
+
|
177 |
+
print("best performance:", best_ensemble_image_f1)
|
178 |
+
|
179 |
+
|
180 |
+
if __name__ == "__main__":
|
181 |
+
opt = get_opt()
|
182 |
+
|
183 |
+
# import cProfile
|
184 |
+
# import pstats
|
185 |
+
# profiler = cProfile.Profile()
|
186 |
+
# profiler.enable()
|
187 |
+
|
188 |
+
st = datetime.datetime.now()
|
189 |
+
main(opt)
|
190 |
+
total_time = datetime.datetime.now() - st
|
191 |
+
total_time = str(datetime.timedelta(seconds=total_time.seconds))
|
192 |
+
print(f"Total time: {total_time}")
|
193 |
+
|
194 |
+
print("finished")
|
195 |
+
|
196 |
+
# profiler.disable()
|
197 |
+
# stats = pstats.Stats(profiler).sort_stats('cumtime')
|
198 |
+
# stats.strip_dirs()
|
199 |
+
# stats_name = f'cprofile-data{opt.suffix}'
|
200 |
+
# if not opt.debug and not opt.eval:
|
201 |
+
# stats_name = os.path.join(opt.save_root_path, opt.dir_name, stats_name)
|
202 |
+
# else:
|
203 |
+
# stats_name = os.path.join('tmp', stats_name)
|
204 |
+
# stats.dump_stats(stats_name)
|
models/__init__.py
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch.nn as nn
|
2 |
+
|
3 |
+
from .bayar_conv import BayarConv2d
|
4 |
+
from .early_fusion_pre_filter import EarlyFusionPreFilter
|
5 |
+
from .ensemble_model import EnsembleModel
|
6 |
+
from .main_model import MainModel
|
7 |
+
from .models import ModelBuilder, SegmentationModule
|
8 |
+
from .srm_conv import SRMConv2d
|
9 |
+
|
10 |
+
|
11 |
+
def get_ensemble_model(opt):
|
12 |
+
models = {}
|
13 |
+
for modality in opt.modality:
|
14 |
+
models[modality] = get_single_modal_model(opt, modality)
|
15 |
+
|
16 |
+
ensemble_model = EnsembleModel(
|
17 |
+
models=models, mvc_single_weight=opt.mvc_single_weight
|
18 |
+
)
|
19 |
+
return ensemble_model
|
20 |
+
|
21 |
+
|
22 |
+
def get_single_modal_model(opt, modality):
|
23 |
+
encoder = ModelBuilder.build_encoder( # TODO check the implementation of FCN
|
24 |
+
arch=opt.encoder.lower(), fc_dim=opt.fc_dim, weights=opt.encoder_weight
|
25 |
+
)
|
26 |
+
decoder = ModelBuilder.build_decoder(
|
27 |
+
arch=opt.decoder.lower(),
|
28 |
+
fc_dim=opt.fc_dim,
|
29 |
+
weights=opt.decoder_weight,
|
30 |
+
num_class=opt.num_class,
|
31 |
+
dropout=opt.dropout,
|
32 |
+
fcn_up=opt.fcn_up,
|
33 |
+
)
|
34 |
+
|
35 |
+
if modality.lower() == "bayar":
|
36 |
+
pre_filter = BayarConv2d(
|
37 |
+
3, 3, 5, stride=1, padding=2, magnitude=opt.bayar_magnitude
|
38 |
+
)
|
39 |
+
elif modality.lower() == "srm":
|
40 |
+
pre_filter = SRMConv2d(
|
41 |
+
stride=1, padding=2, clip=opt.srm_clip
|
42 |
+
) # TODO check the implementation of SRM filter
|
43 |
+
elif modality.lower() == "rgb":
|
44 |
+
pre_filter = nn.Identity()
|
45 |
+
else: # early
|
46 |
+
pre_filter = EarlyFusionPreFilter(
|
47 |
+
bayar_magnitude=opt.bayar_magnitude, srm_clip=opt.srm_clip
|
48 |
+
)
|
49 |
+
|
50 |
+
model = MainModel(
|
51 |
+
encoder,
|
52 |
+
decoder,
|
53 |
+
opt.fc_dim,
|
54 |
+
opt.volume_block_idx,
|
55 |
+
opt.share_embed_head,
|
56 |
+
pre_filter,
|
57 |
+
opt.gem,
|
58 |
+
opt.gem_coef,
|
59 |
+
opt.gsm,
|
60 |
+
opt.map_portion,
|
61 |
+
opt.otsu_sel,
|
62 |
+
opt.otsu_portion,
|
63 |
+
)
|
64 |
+
|
65 |
+
return model
|
models/bayar_conv.py
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
from einops import rearrange
|
4 |
+
|
5 |
+
|
6 |
+
class BayarConv2d(nn.Module):
|
7 |
+
def __init__(
|
8 |
+
self,
|
9 |
+
in_channles: int,
|
10 |
+
out_channels: int,
|
11 |
+
kernel_size: int = 5,
|
12 |
+
stride: int = 1,
|
13 |
+
padding: int = 0,
|
14 |
+
magnitude: float = 1.0,
|
15 |
+
):
|
16 |
+
super().__init__()
|
17 |
+
assert kernel_size > 1, "Bayar conv kernel size must be greater than 1"
|
18 |
+
|
19 |
+
self.in_channels = in_channles
|
20 |
+
self.out_channels = out_channels
|
21 |
+
self.kernel_size = kernel_size
|
22 |
+
self.stride = stride
|
23 |
+
self.padding = padding
|
24 |
+
self.magnitude = magnitude
|
25 |
+
|
26 |
+
self.center_weight = nn.Parameter(
|
27 |
+
torch.ones(self.in_channels, self.out_channels, 1) * -1.0 * magnitude,
|
28 |
+
requires_grad=False,
|
29 |
+
)
|
30 |
+
self.kernel_weight = nn.Parameter(
|
31 |
+
torch.rand((self.in_channels, self.out_channels, kernel_size**2 - 1)),
|
32 |
+
requires_grad=True,
|
33 |
+
)
|
34 |
+
|
35 |
+
def _constraint_weight(self):
|
36 |
+
self.kernel_weight.data = self.kernel_weight.permute(2, 0, 1)
|
37 |
+
self.kernel_weight.data = torch.div(
|
38 |
+
self.kernel_weight.data, self.kernel_weight.data.sum(0)
|
39 |
+
)
|
40 |
+
self.kernel_weight.data = self.kernel_weight.permute(1, 2, 0) * self.magnitude
|
41 |
+
center_idx = self.kernel_size**2 // 2
|
42 |
+
full_kernel = torch.cat(
|
43 |
+
[
|
44 |
+
self.kernel_weight[:, :, :center_idx],
|
45 |
+
self.center_weight,
|
46 |
+
self.kernel_weight[:, :, center_idx:],
|
47 |
+
],
|
48 |
+
dim=2,
|
49 |
+
)
|
50 |
+
full_kernel = rearrange(
|
51 |
+
full_kernel, "ci co (kw kh) -> ci co kw kh", kw=self.kernel_size
|
52 |
+
)
|
53 |
+
return full_kernel
|
54 |
+
|
55 |
+
def forward(self, x):
|
56 |
+
x = nn.functional.conv2d(
|
57 |
+
x, self._constraint_weight(), stride=self.stride, padding=self.padding
|
58 |
+
)
|
59 |
+
return x
|
60 |
+
|
61 |
+
|
62 |
+
if __name__ == "__main__":
|
63 |
+
device = "cuda"
|
64 |
+
bayer_conv2d = BayarConv2d(3, 3, 3, magnitude=1).to(device)
|
65 |
+
bayer_conv2d._constraint_weight()
|
66 |
+
i = torch.rand(16, 3, 16, 16).to(device)
|
67 |
+
o = bayer_conv2d(i)
|
models/early_fusion_pre_filter.py
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
|
4 |
+
from .bayar_conv import BayarConv2d
|
5 |
+
from .srm_conv import SRMConv2d
|
6 |
+
|
7 |
+
|
8 |
+
class EarlyFusionPreFilter(nn.Module):
|
9 |
+
def __init__(self, bayar_magnitude: float, srm_clip: float):
|
10 |
+
super().__init__()
|
11 |
+
self.bayar_filter = BayarConv2d(
|
12 |
+
3, 3, 5, stride=1, padding=2, magnitude=bayar_magnitude
|
13 |
+
)
|
14 |
+
self.srm_filter = SRMConv2d(stride=1, padding=2, clip=srm_clip)
|
15 |
+
self.rgb_filter = nn.Identity()
|
16 |
+
self.map = nn.Conv2d(9, 3, 1, stride=1, padding=0)
|
17 |
+
|
18 |
+
def forward(self, x):
|
19 |
+
x_bayar = self.bayar_filter(x)
|
20 |
+
x_srm = self.srm_filter(x)
|
21 |
+
x_rgb = self.rgb_filter(x)
|
22 |
+
|
23 |
+
x_concat = torch.cat([x_bayar, x_srm, x_rgb], dim=1)
|
24 |
+
x_concat = self.map(x_concat)
|
25 |
+
return x_concat
|
models/ensemble_model.py
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Dict, List
|
2 |
+
|
3 |
+
import torch
|
4 |
+
import torch.nn as nn
|
5 |
+
|
6 |
+
|
7 |
+
class EnsembleModel(nn.Module):
|
8 |
+
def __init__(self, models: Dict, mvc_single_weight: Dict):
|
9 |
+
super().__init__()
|
10 |
+
|
11 |
+
self.sub_models = nn.ModuleDict(models)
|
12 |
+
self.modality = list(self.sub_models.keys())
|
13 |
+
self.mvc_single_weight = mvc_single_weight
|
14 |
+
for k, v in self.mvc_single_weight.items():
|
15 |
+
assert 0 <= v <= 1, "The weight of {} for {} is out of range".format(v, k)
|
16 |
+
|
17 |
+
def forward(self, image, seg_size=None):
|
18 |
+
result = {}
|
19 |
+
for modality in self.modality:
|
20 |
+
result[modality] = self.sub_models[modality](image, seg_size)
|
21 |
+
|
22 |
+
avg_result = {}
|
23 |
+
for k in result[self.modality[0]].keys():
|
24 |
+
avg_result[k] = torch.zeros_like(result[self.modality[0]][k])
|
25 |
+
for modality in self.modality:
|
26 |
+
avg_result[k] = (
|
27 |
+
avg_result[k]
|
28 |
+
+ self.mvc_single_weight[modality] * result[modality][k]
|
29 |
+
)
|
30 |
+
result["ensemble"] = avg_result
|
31 |
+
|
32 |
+
return result
|
models/hrnet.py
ADDED
@@ -0,0 +1,537 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
This HRNet implementation is modified from the following repository:
|
3 |
+
https://github.com/HRNet/HRNet-Semantic-Segmentation
|
4 |
+
"""
|
5 |
+
|
6 |
+
import logging
|
7 |
+
|
8 |
+
import torch
|
9 |
+
import torch.nn as nn
|
10 |
+
import torch.nn.functional as F
|
11 |
+
|
12 |
+
from .lib.nn import SynchronizedBatchNorm2d
|
13 |
+
from .utils import load_url
|
14 |
+
|
15 |
+
BatchNorm2d = SynchronizedBatchNorm2d
|
16 |
+
BN_MOMENTUM = 0.1
|
17 |
+
logger = logging.getLogger(__name__)
|
18 |
+
|
19 |
+
|
20 |
+
__all__ = ["hrnetv2"]
|
21 |
+
|
22 |
+
|
23 |
+
model_urls = {
|
24 |
+
"hrnetv2": "http://sceneparsing.csail.mit.edu/model/pretrained_resnet/hrnetv2_w48-imagenet.pth",
|
25 |
+
}
|
26 |
+
|
27 |
+
|
28 |
+
def conv3x3(in_planes, out_planes, stride=1):
|
29 |
+
"""3x3 convolution with padding"""
|
30 |
+
return nn.Conv2d(
|
31 |
+
in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False
|
32 |
+
)
|
33 |
+
|
34 |
+
|
35 |
+
class BasicBlock(nn.Module):
|
36 |
+
expansion = 1
|
37 |
+
|
38 |
+
def __init__(self, inplanes, planes, stride=1, downsample=None):
|
39 |
+
super(BasicBlock, self).__init__()
|
40 |
+
self.conv1 = conv3x3(inplanes, planes, stride)
|
41 |
+
self.bn1 = BatchNorm2d(planes, momentum=BN_MOMENTUM)
|
42 |
+
self.relu = nn.ReLU(inplace=True)
|
43 |
+
self.conv2 = conv3x3(planes, planes)
|
44 |
+
self.bn2 = BatchNorm2d(planes, momentum=BN_MOMENTUM)
|
45 |
+
self.downsample = downsample
|
46 |
+
self.stride = stride
|
47 |
+
|
48 |
+
def forward(self, x):
|
49 |
+
residual = x
|
50 |
+
|
51 |
+
out = self.conv1(x)
|
52 |
+
out = self.bn1(out)
|
53 |
+
out = self.relu(out)
|
54 |
+
|
55 |
+
out = self.conv2(out)
|
56 |
+
out = self.bn2(out)
|
57 |
+
|
58 |
+
if self.downsample is not None:
|
59 |
+
residual = self.downsample(x)
|
60 |
+
|
61 |
+
out += residual
|
62 |
+
out = self.relu(out)
|
63 |
+
|
64 |
+
return out
|
65 |
+
|
66 |
+
|
67 |
+
class Bottleneck(nn.Module):
|
68 |
+
expansion = 4
|
69 |
+
|
70 |
+
def __init__(self, inplanes, planes, stride=1, downsample=None):
|
71 |
+
super(Bottleneck, self).__init__()
|
72 |
+
self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
|
73 |
+
self.bn1 = BatchNorm2d(planes, momentum=BN_MOMENTUM)
|
74 |
+
self.conv2 = nn.Conv2d(
|
75 |
+
planes, planes, kernel_size=3, stride=stride, padding=1, bias=False
|
76 |
+
)
|
77 |
+
self.bn2 = BatchNorm2d(planes, momentum=BN_MOMENTUM)
|
78 |
+
self.conv3 = nn.Conv2d(
|
79 |
+
planes, planes * self.expansion, kernel_size=1, bias=False
|
80 |
+
)
|
81 |
+
self.bn3 = BatchNorm2d(planes * self.expansion, momentum=BN_MOMENTUM)
|
82 |
+
self.relu = nn.ReLU(inplace=True)
|
83 |
+
self.downsample = downsample
|
84 |
+
self.stride = stride
|
85 |
+
|
86 |
+
def forward(self, x):
|
87 |
+
residual = x
|
88 |
+
|
89 |
+
out = self.conv1(x)
|
90 |
+
out = self.bn1(out)
|
91 |
+
out = self.relu(out)
|
92 |
+
|
93 |
+
out = self.conv2(out)
|
94 |
+
out = self.bn2(out)
|
95 |
+
out = self.relu(out)
|
96 |
+
|
97 |
+
out = self.conv3(out)
|
98 |
+
out = self.bn3(out)
|
99 |
+
|
100 |
+
if self.downsample is not None:
|
101 |
+
residual = self.downsample(x)
|
102 |
+
|
103 |
+
out += residual
|
104 |
+
out = self.relu(out)
|
105 |
+
|
106 |
+
return out
|
107 |
+
|
108 |
+
|
109 |
+
class HighResolutionModule(nn.Module):
|
110 |
+
def __init__(
|
111 |
+
self,
|
112 |
+
num_branches,
|
113 |
+
blocks,
|
114 |
+
num_blocks,
|
115 |
+
num_inchannels,
|
116 |
+
num_channels,
|
117 |
+
fuse_method,
|
118 |
+
multi_scale_output=True,
|
119 |
+
):
|
120 |
+
super(HighResolutionModule, self).__init__()
|
121 |
+
self._check_branches(
|
122 |
+
num_branches, blocks, num_blocks, num_inchannels, num_channels
|
123 |
+
)
|
124 |
+
|
125 |
+
self.num_inchannels = num_inchannels
|
126 |
+
self.fuse_method = fuse_method
|
127 |
+
self.num_branches = num_branches
|
128 |
+
|
129 |
+
self.multi_scale_output = multi_scale_output
|
130 |
+
|
131 |
+
self.branches = self._make_branches(
|
132 |
+
num_branches, blocks, num_blocks, num_channels
|
133 |
+
)
|
134 |
+
self.fuse_layers = self._make_fuse_layers()
|
135 |
+
self.relu = nn.ReLU(inplace=True)
|
136 |
+
|
137 |
+
def _check_branches(
|
138 |
+
self, num_branches, blocks, num_blocks, num_inchannels, num_channels
|
139 |
+
):
|
140 |
+
if num_branches != len(num_blocks):
|
141 |
+
error_msg = "NUM_BRANCHES({}) <> NUM_BLOCKS({})".format(
|
142 |
+
num_branches, len(num_blocks)
|
143 |
+
)
|
144 |
+
logger.error(error_msg)
|
145 |
+
raise ValueError(error_msg)
|
146 |
+
|
147 |
+
if num_branches != len(num_channels):
|
148 |
+
error_msg = "NUM_BRANCHES({}) <> NUM_CHANNELS({})".format(
|
149 |
+
num_branches, len(num_channels)
|
150 |
+
)
|
151 |
+
logger.error(error_msg)
|
152 |
+
raise ValueError(error_msg)
|
153 |
+
|
154 |
+
if num_branches != len(num_inchannels):
|
155 |
+
error_msg = "NUM_BRANCHES({}) <> NUM_INCHANNELS({})".format(
|
156 |
+
num_branches, len(num_inchannels)
|
157 |
+
)
|
158 |
+
logger.error(error_msg)
|
159 |
+
raise ValueError(error_msg)
|
160 |
+
|
161 |
+
def _make_one_branch(self, branch_index, block, num_blocks, num_channels, stride=1):
|
162 |
+
downsample = None
|
163 |
+
if (
|
164 |
+
stride != 1
|
165 |
+
or self.num_inchannels[branch_index]
|
166 |
+
!= num_channels[branch_index] * block.expansion
|
167 |
+
):
|
168 |
+
downsample = nn.Sequential(
|
169 |
+
nn.Conv2d(
|
170 |
+
self.num_inchannels[branch_index],
|
171 |
+
num_channels[branch_index] * block.expansion,
|
172 |
+
kernel_size=1,
|
173 |
+
stride=stride,
|
174 |
+
bias=False,
|
175 |
+
),
|
176 |
+
BatchNorm2d(
|
177 |
+
num_channels[branch_index] * block.expansion, momentum=BN_MOMENTUM
|
178 |
+
),
|
179 |
+
)
|
180 |
+
|
181 |
+
layers = []
|
182 |
+
layers.append(
|
183 |
+
block(
|
184 |
+
self.num_inchannels[branch_index],
|
185 |
+
num_channels[branch_index],
|
186 |
+
stride,
|
187 |
+
downsample,
|
188 |
+
)
|
189 |
+
)
|
190 |
+
self.num_inchannels[branch_index] = num_channels[branch_index] * block.expansion
|
191 |
+
for i in range(1, num_blocks[branch_index]):
|
192 |
+
layers.append(
|
193 |
+
block(self.num_inchannels[branch_index], num_channels[branch_index])
|
194 |
+
)
|
195 |
+
|
196 |
+
return nn.Sequential(*layers)
|
197 |
+
|
198 |
+
def _make_branches(self, num_branches, block, num_blocks, num_channels):
|
199 |
+
branches = []
|
200 |
+
|
201 |
+
for i in range(num_branches):
|
202 |
+
branches.append(self._make_one_branch(i, block, num_blocks, num_channels))
|
203 |
+
|
204 |
+
return nn.ModuleList(branches)
|
205 |
+
|
206 |
+
def _make_fuse_layers(self):
|
207 |
+
if self.num_branches == 1:
|
208 |
+
return None
|
209 |
+
|
210 |
+
num_branches = self.num_branches
|
211 |
+
num_inchannels = self.num_inchannels
|
212 |
+
fuse_layers = []
|
213 |
+
for i in range(num_branches if self.multi_scale_output else 1):
|
214 |
+
fuse_layer = []
|
215 |
+
for j in range(num_branches):
|
216 |
+
if j > i:
|
217 |
+
fuse_layer.append(
|
218 |
+
nn.Sequential(
|
219 |
+
nn.Conv2d(
|
220 |
+
num_inchannels[j],
|
221 |
+
num_inchannels[i],
|
222 |
+
1,
|
223 |
+
1,
|
224 |
+
0,
|
225 |
+
bias=False,
|
226 |
+
),
|
227 |
+
BatchNorm2d(num_inchannels[i], momentum=BN_MOMENTUM),
|
228 |
+
)
|
229 |
+
)
|
230 |
+
elif j == i:
|
231 |
+
fuse_layer.append(None)
|
232 |
+
else:
|
233 |
+
conv3x3s = []
|
234 |
+
for k in range(i - j):
|
235 |
+
if k == i - j - 1:
|
236 |
+
num_outchannels_conv3x3 = num_inchannels[i]
|
237 |
+
conv3x3s.append(
|
238 |
+
nn.Sequential(
|
239 |
+
nn.Conv2d(
|
240 |
+
num_inchannels[j],
|
241 |
+
num_outchannels_conv3x3,
|
242 |
+
3,
|
243 |
+
2,
|
244 |
+
1,
|
245 |
+
bias=False,
|
246 |
+
),
|
247 |
+
BatchNorm2d(
|
248 |
+
num_outchannels_conv3x3, momentum=BN_MOMENTUM
|
249 |
+
),
|
250 |
+
)
|
251 |
+
)
|
252 |
+
else:
|
253 |
+
num_outchannels_conv3x3 = num_inchannels[j]
|
254 |
+
conv3x3s.append(
|
255 |
+
nn.Sequential(
|
256 |
+
nn.Conv2d(
|
257 |
+
num_inchannels[j],
|
258 |
+
num_outchannels_conv3x3,
|
259 |
+
3,
|
260 |
+
2,
|
261 |
+
1,
|
262 |
+
bias=False,
|
263 |
+
),
|
264 |
+
BatchNorm2d(
|
265 |
+
num_outchannels_conv3x3, momentum=BN_MOMENTUM
|
266 |
+
),
|
267 |
+
nn.ReLU(inplace=True),
|
268 |
+
)
|
269 |
+
)
|
270 |
+
fuse_layer.append(nn.Sequential(*conv3x3s))
|
271 |
+
fuse_layers.append(nn.ModuleList(fuse_layer))
|
272 |
+
|
273 |
+
return nn.ModuleList(fuse_layers)
|
274 |
+
|
275 |
+
def get_num_inchannels(self):
|
276 |
+
return self.num_inchannels
|
277 |
+
|
278 |
+
def forward(self, x):
|
279 |
+
if self.num_branches == 1:
|
280 |
+
return [self.branches[0](x[0])]
|
281 |
+
|
282 |
+
for i in range(self.num_branches):
|
283 |
+
x[i] = self.branches[i](x[i])
|
284 |
+
|
285 |
+
x_fuse = []
|
286 |
+
for i in range(len(self.fuse_layers)):
|
287 |
+
y = x[0] if i == 0 else self.fuse_layers[i][0](x[0])
|
288 |
+
for j in range(1, self.num_branches):
|
289 |
+
if i == j:
|
290 |
+
y = y + x[j]
|
291 |
+
elif j > i:
|
292 |
+
width_output = x[i].shape[-1]
|
293 |
+
height_output = x[i].shape[-2]
|
294 |
+
y = y + F.interpolate(
|
295 |
+
self.fuse_layers[i][j](x[j]),
|
296 |
+
size=(height_output, width_output),
|
297 |
+
mode="bilinear",
|
298 |
+
align_corners=False,
|
299 |
+
)
|
300 |
+
else:
|
301 |
+
y = y + self.fuse_layers[i][j](x[j])
|
302 |
+
x_fuse.append(self.relu(y))
|
303 |
+
|
304 |
+
return x_fuse
|
305 |
+
|
306 |
+
|
307 |
+
blocks_dict = {"BASIC": BasicBlock, "BOTTLENECK": Bottleneck}
|
308 |
+
|
309 |
+
|
310 |
+
class HRNetV2(nn.Module):
|
311 |
+
def __init__(self, n_class, **kwargs):
|
312 |
+
super(HRNetV2, self).__init__()
|
313 |
+
extra = {
|
314 |
+
"STAGE2": {
|
315 |
+
"NUM_MODULES": 1,
|
316 |
+
"NUM_BRANCHES": 2,
|
317 |
+
"BLOCK": "BASIC",
|
318 |
+
"NUM_BLOCKS": (4, 4),
|
319 |
+
"NUM_CHANNELS": (48, 96),
|
320 |
+
"FUSE_METHOD": "SUM",
|
321 |
+
},
|
322 |
+
"STAGE3": {
|
323 |
+
"NUM_MODULES": 4,
|
324 |
+
"NUM_BRANCHES": 3,
|
325 |
+
"BLOCK": "BASIC",
|
326 |
+
"NUM_BLOCKS": (4, 4, 4),
|
327 |
+
"NUM_CHANNELS": (48, 96, 192),
|
328 |
+
"FUSE_METHOD": "SUM",
|
329 |
+
},
|
330 |
+
"STAGE4": {
|
331 |
+
"NUM_MODULES": 3,
|
332 |
+
"NUM_BRANCHES": 4,
|
333 |
+
"BLOCK": "BASIC",
|
334 |
+
"NUM_BLOCKS": (4, 4, 4, 4),
|
335 |
+
"NUM_CHANNELS": (48, 96, 192, 384),
|
336 |
+
"FUSE_METHOD": "SUM",
|
337 |
+
},
|
338 |
+
"FINAL_CONV_KERNEL": 1,
|
339 |
+
}
|
340 |
+
|
341 |
+
# stem net
|
342 |
+
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=2, padding=1, bias=False)
|
343 |
+
self.bn1 = BatchNorm2d(64, momentum=BN_MOMENTUM)
|
344 |
+
self.conv2 = nn.Conv2d(64, 64, kernel_size=3, stride=2, padding=1, bias=False)
|
345 |
+
self.bn2 = BatchNorm2d(64, momentum=BN_MOMENTUM)
|
346 |
+
self.relu = nn.ReLU(inplace=True)
|
347 |
+
|
348 |
+
self.layer1 = self._make_layer(Bottleneck, 64, 64, 4)
|
349 |
+
|
350 |
+
self.stage2_cfg = extra["STAGE2"]
|
351 |
+
num_channels = self.stage2_cfg["NUM_CHANNELS"]
|
352 |
+
block = blocks_dict[self.stage2_cfg["BLOCK"]]
|
353 |
+
num_channels = [
|
354 |
+
num_channels[i] * block.expansion for i in range(len(num_channels))
|
355 |
+
]
|
356 |
+
self.transition1 = self._make_transition_layer([256], num_channels)
|
357 |
+
self.stage2, pre_stage_channels = self._make_stage(
|
358 |
+
self.stage2_cfg, num_channels
|
359 |
+
)
|
360 |
+
|
361 |
+
self.stage3_cfg = extra["STAGE3"]
|
362 |
+
num_channels = self.stage3_cfg["NUM_CHANNELS"]
|
363 |
+
block = blocks_dict[self.stage3_cfg["BLOCK"]]
|
364 |
+
num_channels = [
|
365 |
+
num_channels[i] * block.expansion for i in range(len(num_channels))
|
366 |
+
]
|
367 |
+
self.transition2 = self._make_transition_layer(pre_stage_channels, num_channels)
|
368 |
+
self.stage3, pre_stage_channels = self._make_stage(
|
369 |
+
self.stage3_cfg, num_channels
|
370 |
+
)
|
371 |
+
|
372 |
+
self.stage4_cfg = extra["STAGE4"]
|
373 |
+
num_channels = self.stage4_cfg["NUM_CHANNELS"]
|
374 |
+
block = blocks_dict[self.stage4_cfg["BLOCK"]]
|
375 |
+
num_channels = [
|
376 |
+
num_channels[i] * block.expansion for i in range(len(num_channels))
|
377 |
+
]
|
378 |
+
self.transition3 = self._make_transition_layer(pre_stage_channels, num_channels)
|
379 |
+
self.stage4, pre_stage_channels = self._make_stage(
|
380 |
+
self.stage4_cfg, num_channels, multi_scale_output=True
|
381 |
+
)
|
382 |
+
|
383 |
+
def _make_transition_layer(self, num_channels_pre_layer, num_channels_cur_layer):
|
384 |
+
num_branches_cur = len(num_channels_cur_layer)
|
385 |
+
num_branches_pre = len(num_channels_pre_layer)
|
386 |
+
|
387 |
+
transition_layers = []
|
388 |
+
for i in range(num_branches_cur):
|
389 |
+
if i < num_branches_pre:
|
390 |
+
if num_channels_cur_layer[i] != num_channels_pre_layer[i]:
|
391 |
+
transition_layers.append(
|
392 |
+
nn.Sequential(
|
393 |
+
nn.Conv2d(
|
394 |
+
num_channels_pre_layer[i],
|
395 |
+
num_channels_cur_layer[i],
|
396 |
+
3,
|
397 |
+
1,
|
398 |
+
1,
|
399 |
+
bias=False,
|
400 |
+
),
|
401 |
+
BatchNorm2d(
|
402 |
+
num_channels_cur_layer[i], momentum=BN_MOMENTUM
|
403 |
+
),
|
404 |
+
nn.ReLU(inplace=True),
|
405 |
+
)
|
406 |
+
)
|
407 |
+
else:
|
408 |
+
transition_layers.append(None)
|
409 |
+
else:
|
410 |
+
conv3x3s = []
|
411 |
+
for j in range(i + 1 - num_branches_pre):
|
412 |
+
inchannels = num_channels_pre_layer[-1]
|
413 |
+
outchannels = (
|
414 |
+
num_channels_cur_layer[i]
|
415 |
+
if j == i - num_branches_pre
|
416 |
+
else inchannels
|
417 |
+
)
|
418 |
+
conv3x3s.append(
|
419 |
+
nn.Sequential(
|
420 |
+
nn.Conv2d(inchannels, outchannels, 3, 2, 1, bias=False),
|
421 |
+
BatchNorm2d(outchannels, momentum=BN_MOMENTUM),
|
422 |
+
nn.ReLU(inplace=True),
|
423 |
+
)
|
424 |
+
)
|
425 |
+
transition_layers.append(nn.Sequential(*conv3x3s))
|
426 |
+
|
427 |
+
return nn.ModuleList(transition_layers)
|
428 |
+
|
429 |
+
def _make_layer(self, block, inplanes, planes, blocks, stride=1):
|
430 |
+
downsample = None
|
431 |
+
if stride != 1 or inplanes != planes * block.expansion:
|
432 |
+
downsample = nn.Sequential(
|
433 |
+
nn.Conv2d(
|
434 |
+
inplanes,
|
435 |
+
planes * block.expansion,
|
436 |
+
kernel_size=1,
|
437 |
+
stride=stride,
|
438 |
+
bias=False,
|
439 |
+
),
|
440 |
+
BatchNorm2d(planes * block.expansion, momentum=BN_MOMENTUM),
|
441 |
+
)
|
442 |
+
|
443 |
+
layers = []
|
444 |
+
layers.append(block(inplanes, planes, stride, downsample))
|
445 |
+
inplanes = planes * block.expansion
|
446 |
+
for i in range(1, blocks):
|
447 |
+
layers.append(block(inplanes, planes))
|
448 |
+
|
449 |
+
return nn.Sequential(*layers)
|
450 |
+
|
451 |
+
def _make_stage(self, layer_config, num_inchannels, multi_scale_output=True):
|
452 |
+
num_modules = layer_config["NUM_MODULES"]
|
453 |
+
num_branches = layer_config["NUM_BRANCHES"]
|
454 |
+
num_blocks = layer_config["NUM_BLOCKS"]
|
455 |
+
num_channels = layer_config["NUM_CHANNELS"]
|
456 |
+
block = blocks_dict[layer_config["BLOCK"]]
|
457 |
+
fuse_method = layer_config["FUSE_METHOD"]
|
458 |
+
|
459 |
+
modules = []
|
460 |
+
for i in range(num_modules):
|
461 |
+
# multi_scale_output is only used last module
|
462 |
+
if not multi_scale_output and i == num_modules - 1:
|
463 |
+
reset_multi_scale_output = False
|
464 |
+
else:
|
465 |
+
reset_multi_scale_output = True
|
466 |
+
modules.append(
|
467 |
+
HighResolutionModule(
|
468 |
+
num_branches,
|
469 |
+
block,
|
470 |
+
num_blocks,
|
471 |
+
num_inchannels,
|
472 |
+
num_channels,
|
473 |
+
fuse_method,
|
474 |
+
reset_multi_scale_output,
|
475 |
+
)
|
476 |
+
)
|
477 |
+
num_inchannels = modules[-1].get_num_inchannels()
|
478 |
+
|
479 |
+
return nn.Sequential(*modules), num_inchannels
|
480 |
+
|
481 |
+
def forward(self, x, return_feature_maps=False):
|
482 |
+
x = self.conv1(x)
|
483 |
+
x = self.bn1(x)
|
484 |
+
x = self.relu(x)
|
485 |
+
x = self.conv2(x)
|
486 |
+
x = self.bn2(x)
|
487 |
+
x = self.relu(x)
|
488 |
+
x = self.layer1(x)
|
489 |
+
|
490 |
+
x_list = []
|
491 |
+
for i in range(self.stage2_cfg["NUM_BRANCHES"]):
|
492 |
+
if self.transition1[i] is not None:
|
493 |
+
x_list.append(self.transition1[i](x))
|
494 |
+
else:
|
495 |
+
x_list.append(x)
|
496 |
+
y_list = self.stage2(x_list)
|
497 |
+
|
498 |
+
x_list = []
|
499 |
+
for i in range(self.stage3_cfg["NUM_BRANCHES"]):
|
500 |
+
if self.transition2[i] is not None:
|
501 |
+
x_list.append(self.transition2[i](y_list[-1]))
|
502 |
+
else:
|
503 |
+
x_list.append(y_list[i])
|
504 |
+
y_list = self.stage3(x_list)
|
505 |
+
|
506 |
+
x_list = []
|
507 |
+
for i in range(self.stage4_cfg["NUM_BRANCHES"]):
|
508 |
+
if self.transition3[i] is not None:
|
509 |
+
x_list.append(self.transition3[i](y_list[-1]))
|
510 |
+
else:
|
511 |
+
x_list.append(y_list[i])
|
512 |
+
x = self.stage4(x_list)
|
513 |
+
|
514 |
+
# Upsampling
|
515 |
+
x0_h, x0_w = x[0].size(2), x[0].size(3)
|
516 |
+
x1 = F.interpolate(
|
517 |
+
x[1], size=(x0_h, x0_w), mode="bilinear", align_corners=False
|
518 |
+
)
|
519 |
+
x2 = F.interpolate(
|
520 |
+
x[2], size=(x0_h, x0_w), mode="bilinear", align_corners=False
|
521 |
+
)
|
522 |
+
x3 = F.interpolate(
|
523 |
+
x[3], size=(x0_h, x0_w), mode="bilinear", align_corners=False
|
524 |
+
)
|
525 |
+
|
526 |
+
x = torch.cat([x[0], x1, x2, x3], 1)
|
527 |
+
|
528 |
+
# x = self.last_layer(x)
|
529 |
+
return [x]
|
530 |
+
|
531 |
+
|
532 |
+
def hrnetv2(pretrained=False, **kwargs):
|
533 |
+
model = HRNetV2(n_class=1000, **kwargs)
|
534 |
+
if pretrained:
|
535 |
+
model.load_state_dict(load_url(model_urls["hrnetv2"]), strict=False)
|
536 |
+
|
537 |
+
return model
|
models/main_model.py
ADDED
@@ -0,0 +1,290 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Optional
|
2 |
+
|
3 |
+
import torch
|
4 |
+
import torch.nn as nn
|
5 |
+
from einops import rearrange
|
6 |
+
|
7 |
+
|
8 |
+
class MainModel(nn.Module):
|
9 |
+
def __init__(
|
10 |
+
self,
|
11 |
+
encoder,
|
12 |
+
decoder,
|
13 |
+
fc_dim: int,
|
14 |
+
volume_block_idx: int,
|
15 |
+
share_embed_head: bool,
|
16 |
+
pre_filter=None,
|
17 |
+
use_gem: bool = False,
|
18 |
+
gem_coef: Optional[float] = None,
|
19 |
+
use_gsm: bool = False,
|
20 |
+
map_portion: float = 0,
|
21 |
+
otsu_sel: bool = False,
|
22 |
+
otsu_portion: float = 1,
|
23 |
+
):
|
24 |
+
super().__init__()
|
25 |
+
self.encoder = encoder
|
26 |
+
self.decoder = decoder
|
27 |
+
self.use_gem = use_gem
|
28 |
+
self.gem_coef = gem_coef
|
29 |
+
self.use_gsm = use_gsm
|
30 |
+
self.map_portion = map_portion
|
31 |
+
assert self.map_portion <= 0.5, "Map_portion must be less than 0.5"
|
32 |
+
self.otsu_sel = otsu_sel
|
33 |
+
self.otsu_portion = otsu_portion
|
34 |
+
|
35 |
+
self.volume_block_idx = volume_block_idx
|
36 |
+
volume_in_channel = int(fc_dim * (2 ** (self.volume_block_idx - 3)))
|
37 |
+
volume_out_channel = volume_in_channel // 2
|
38 |
+
|
39 |
+
self.scale = volume_out_channel**0.5
|
40 |
+
self.share_embed_head = share_embed_head
|
41 |
+
self.proj_head1 = nn.Sequential(
|
42 |
+
nn.Conv2d(
|
43 |
+
volume_in_channel, volume_in_channel, kernel_size=1, stride=1, padding=0
|
44 |
+
),
|
45 |
+
nn.LeakyReLU(),
|
46 |
+
nn.Conv2d(
|
47 |
+
volume_in_channel,
|
48 |
+
volume_out_channel,
|
49 |
+
kernel_size=1,
|
50 |
+
stride=1,
|
51 |
+
padding=0,
|
52 |
+
),
|
53 |
+
)
|
54 |
+
if not share_embed_head:
|
55 |
+
self.proj_head2 = nn.Sequential(
|
56 |
+
nn.Conv2d(
|
57 |
+
volume_in_channel,
|
58 |
+
volume_in_channel,
|
59 |
+
kernel_size=1,
|
60 |
+
stride=1,
|
61 |
+
padding=0,
|
62 |
+
),
|
63 |
+
nn.LeakyReLU(),
|
64 |
+
nn.Conv2d(
|
65 |
+
volume_in_channel,
|
66 |
+
volume_out_channel,
|
67 |
+
kernel_size=1,
|
68 |
+
stride=1,
|
69 |
+
padding=0,
|
70 |
+
),
|
71 |
+
)
|
72 |
+
|
73 |
+
self.pre_filter = pre_filter
|
74 |
+
|
75 |
+
def forward(self, image, seg_size=None):
|
76 |
+
"""
|
77 |
+
for output maps, the return value is the raw logits
|
78 |
+
for consistency volume, the return value is the value after sigmoid
|
79 |
+
"""
|
80 |
+
bs = image.shape[0]
|
81 |
+
if self.pre_filter is not None:
|
82 |
+
image = self.pre_filter(image)
|
83 |
+
|
84 |
+
# get output map
|
85 |
+
encoder_feature = self.encoder(image, return_feature_maps=True)
|
86 |
+
output_map = self.decoder(encoder_feature, segSize=seg_size)
|
87 |
+
output_map = output_map.sigmoid()
|
88 |
+
# b, _, h, w = output_map.shape
|
89 |
+
|
90 |
+
# get image-level prediction
|
91 |
+
if self.use_gem:
|
92 |
+
mh, mw = output_map.shape[-2:]
|
93 |
+
image_pred = output_map.flatten(1)
|
94 |
+
image_pred = torch.linalg.norm(image_pred, ord=self.gem_coef, dim=1)
|
95 |
+
image_pred = image_pred / (mh * mw)
|
96 |
+
elif self.use_gsm:
|
97 |
+
image_pred = output_map.flatten(1)
|
98 |
+
weight = project_onto_l1_ball(image_pred, 1.0)
|
99 |
+
image_pred = (image_pred * weight).sum(1)
|
100 |
+
else:
|
101 |
+
if self.otsu_sel:
|
102 |
+
n_pixel = output_map.shape[-1] * output_map.shape[-2]
|
103 |
+
image_pred = output_map.flatten(1)
|
104 |
+
image_pred, _ = torch.sort(image_pred, dim=1)
|
105 |
+
tmp = []
|
106 |
+
for b in range(bs):
|
107 |
+
num_otsu_sel = get_otsu_k(image_pred[b, ...], sorted=True)
|
108 |
+
num_otsu_sel = max(num_otsu_sel, n_pixel // 2 + 1)
|
109 |
+
tpk = int(max(1, (n_pixel - num_otsu_sel) * self.otsu_portion))
|
110 |
+
topk_output = torch.topk(image_pred[b, ...], k=tpk, dim=0)[0]
|
111 |
+
tmp.append(topk_output.mean())
|
112 |
+
image_pred = torch.stack(tmp)
|
113 |
+
else:
|
114 |
+
if self.map_portion == 0:
|
115 |
+
image_pred = nn.functional.max_pool2d(
|
116 |
+
output_map, kernel_size=output_map.shape[-2:]
|
117 |
+
)
|
118 |
+
image_pred = image_pred.squeeze(1).squeeze(1).squeeze(1)
|
119 |
+
else:
|
120 |
+
n_pixel = output_map.shape[-1] * output_map.shape[-2]
|
121 |
+
k = int(max(1, int(self.map_portion * n_pixel)))
|
122 |
+
topk_output = torch.topk(output_map.flatten(1), k, dim=1)[0]
|
123 |
+
image_pred = topk_output.mean(1)
|
124 |
+
|
125 |
+
if seg_size is not None:
|
126 |
+
output_map = nn.functional.interpolate(
|
127 |
+
output_map, size=seg_size, mode="bilinear", align_corners=False
|
128 |
+
)
|
129 |
+
output_map = output_map.clamp(0, 1)
|
130 |
+
|
131 |
+
# compute consistency volume, 0 for consistency, and 1 for inconsistency
|
132 |
+
feature_map1 = self.proj_head1(encoder_feature[self.volume_block_idx])
|
133 |
+
if not self.share_embed_head:
|
134 |
+
feature_map2 = self.proj_head2(encoder_feature[self.volume_block_idx])
|
135 |
+
else:
|
136 |
+
feature_map2 = feature_map1.clone()
|
137 |
+
b, c, h, w = feature_map1.shape
|
138 |
+
feature_map1 = rearrange(feature_map1, "b c h w -> b c (h w)")
|
139 |
+
feature_map2 = rearrange(feature_map2, "b c h w -> b c (h w)")
|
140 |
+
consistency_volume = torch.bmm(feature_map1.transpose(-1, -2), feature_map2)
|
141 |
+
consistency_volume = rearrange(
|
142 |
+
consistency_volume, "b (h1 w1) (h2 w2) -> b h1 w1 h2 w2", h1=h, h2=h
|
143 |
+
)
|
144 |
+
consistency_volume = consistency_volume / self.scale
|
145 |
+
consistency_volume = 1 - consistency_volume.sigmoid()
|
146 |
+
|
147 |
+
vh, vw = consistency_volume.shape[-2:]
|
148 |
+
if self.use_gem:
|
149 |
+
volume_image_pred = consistency_volume.flatten(1)
|
150 |
+
volume_image_pred = torch.linalg.norm(
|
151 |
+
volume_image_pred, ord=self.gem_coef, dim=1
|
152 |
+
)
|
153 |
+
volume_image_pred = volume_image_pred / (vh * vw * vh * vw)
|
154 |
+
elif self.use_gsm:
|
155 |
+
volume_image_pred = consistency_volume.flatten(1)
|
156 |
+
weight = project_onto_l1_ball(volume_image_pred, 1.0)
|
157 |
+
volume_image_pred = (volume_image_pred * weight).sum(1)
|
158 |
+
else:
|
159 |
+
# FIXME skip Otsu's selection on volume due to its slowness
|
160 |
+
# if self.otsu_sel:
|
161 |
+
# n_ele = vh * vw * vh * vw
|
162 |
+
# volume_image_pred = consistency_volume.flatten(1)
|
163 |
+
# volume_image_pred, _ = torch.sort(volume_image_pred, dim=1)
|
164 |
+
# tmp = []
|
165 |
+
# for b in range(bs):
|
166 |
+
# num_otsu_sel = get_otsu_k(volume_image_pred[b, ...], sorted=True)
|
167 |
+
# num_otsu_sel = max(num_otsu_sel, n_ele // 2 + 1)
|
168 |
+
# tpk = int(max(1, (n_ele - num_otsu_sel) * self.otsu_portion))
|
169 |
+
# topk_output = torch.topk(volume_image_pred[b, ...], k=tpk, dim=0)[0]
|
170 |
+
# tmp.append(topk_output.mean())
|
171 |
+
# volume_image_pred = torch.stack(tmp)
|
172 |
+
# else:
|
173 |
+
if self.map_portion == 0:
|
174 |
+
volume_image_pred = torch.max(consistency_volume.flatten(1), dim=1)[0]
|
175 |
+
else:
|
176 |
+
n_ele = vh * vw * vh * vw
|
177 |
+
k = int(max(1, int(self.map_portion * n_ele)))
|
178 |
+
topk_output = torch.topk(consistency_volume.flatten(1), k, dim=1)[0]
|
179 |
+
volume_image_pred = topk_output.mean(1)
|
180 |
+
|
181 |
+
return {
|
182 |
+
"out_map": output_map,
|
183 |
+
"map_pred": image_pred,
|
184 |
+
"out_vol": consistency_volume,
|
185 |
+
"vol_pred": volume_image_pred,
|
186 |
+
}
|
187 |
+
|
188 |
+
|
189 |
+
def project_onto_l1_ball(x, eps):
|
190 |
+
"""
|
191 |
+
Compute Euclidean projection onto the L1 ball for a batch.
|
192 |
+
|
193 |
+
min ||x - u||_2 s.t. ||u||_1 <= eps
|
194 |
+
|
195 |
+
Inspired by the corresponding numpy version by Adrien Gaidon.
|
196 |
+
|
197 |
+
Parameters
|
198 |
+
----------
|
199 |
+
x: (batch_size, *) torch array
|
200 |
+
batch of arbitrary-size tensors to project, possibly on GPU
|
201 |
+
|
202 |
+
eps: float
|
203 |
+
radius of l-1 ball to project onto
|
204 |
+
|
205 |
+
Returns
|
206 |
+
-------
|
207 |
+
u: (batch_size, *) torch array
|
208 |
+
batch of projected tensors, reshaped to match the original
|
209 |
+
|
210 |
+
Notes
|
211 |
+
-----
|
212 |
+
The complexity of this algorithm is in O(dlogd) as it involves sorting x.
|
213 |
+
|
214 |
+
References
|
215 |
+
----------
|
216 |
+
[1] Efficient Projections onto the l1-Ball for Learning in High Dimensions
|
217 |
+
John Duchi, Shai Shalev-Shwartz, Yoram Singer, and Tushar Chandra.
|
218 |
+
International Conference on Machine Learning (ICML 2008)
|
219 |
+
"""
|
220 |
+
with torch.no_grad():
|
221 |
+
original_shape = x.shape
|
222 |
+
x = x.view(x.shape[0], -1)
|
223 |
+
mask = (torch.norm(x, p=1, dim=1) < eps).float().unsqueeze(1)
|
224 |
+
mu, _ = torch.sort(torch.abs(x), dim=1, descending=True)
|
225 |
+
cumsum = torch.cumsum(mu, dim=1)
|
226 |
+
arange = torch.arange(1, x.shape[1] + 1, device=x.device)
|
227 |
+
rho, _ = torch.max((mu * arange > (cumsum - eps)) * arange, dim=1)
|
228 |
+
theta = (cumsum[torch.arange(x.shape[0]), rho.cpu() - 1] - eps) / rho
|
229 |
+
proj = (torch.abs(x) - theta.unsqueeze(1)).clamp(min=0)
|
230 |
+
x = mask * x + (1 - mask) * proj * torch.sign(x)
|
231 |
+
x = x.view(original_shape)
|
232 |
+
return x
|
233 |
+
|
234 |
+
|
235 |
+
def get_otsu_k(attention, return_value=False, sorted=False):
|
236 |
+
def _get_weighted_var(seq, pivot: int):
|
237 |
+
# seq is of shape [t], in ascending order
|
238 |
+
length = seq.shape[0]
|
239 |
+
wb = pivot / length
|
240 |
+
vb = seq[:pivot].var()
|
241 |
+
wf = 1 - pivot / length
|
242 |
+
vf = seq[pivot:].var()
|
243 |
+
return wb * vb + wf * vf
|
244 |
+
|
245 |
+
# attention shape: t
|
246 |
+
# TODO use half
|
247 |
+
length = attention.shape[0]
|
248 |
+
if length == 1:
|
249 |
+
return 0
|
250 |
+
elif length == 2:
|
251 |
+
return 1
|
252 |
+
if not sorted:
|
253 |
+
attention, _ = torch.sort(attention)
|
254 |
+
optimal_i = length // 2
|
255 |
+
min_intra_class_var = _get_weighted_var(attention, optimal_i)
|
256 |
+
|
257 |
+
# for i in range(1, length):
|
258 |
+
# intra_class_var = _get_weighted_var(attention, i)
|
259 |
+
# if intra_class_var < min_intra_class_var:
|
260 |
+
# min_intra_class_var = intra_class_var
|
261 |
+
# optimal_i = i
|
262 |
+
|
263 |
+
got_it = False
|
264 |
+
# look left
|
265 |
+
for i in range(optimal_i - 1, 0, -1):
|
266 |
+
intra_class_var = _get_weighted_var(attention, i)
|
267 |
+
if intra_class_var > min_intra_class_var:
|
268 |
+
break
|
269 |
+
else:
|
270 |
+
min_intra_class_var = intra_class_var
|
271 |
+
optimal_i = i
|
272 |
+
got_it = True
|
273 |
+
# look right
|
274 |
+
if not got_it:
|
275 |
+
for i in range(optimal_i + 1, length):
|
276 |
+
intra_class_var = _get_weighted_var(attention, i)
|
277 |
+
if intra_class_var > min_intra_class_var:
|
278 |
+
break
|
279 |
+
else:
|
280 |
+
min_intra_class_var = intra_class_var
|
281 |
+
optimal_i = i
|
282 |
+
|
283 |
+
if return_value:
|
284 |
+
return attention[optimal_i]
|
285 |
+
else:
|
286 |
+
return optimal_i
|
287 |
+
|
288 |
+
|
289 |
+
if __name__ == "__main__":
|
290 |
+
model = MainModel(None, None, 1024, 2, True, "srm")
|
models/mobilenet.py
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
This MobileNetV2 implementation is modified from the following repository:
|
3 |
+
https://github.com/tonylins/pytorch-mobilenet-v2
|
4 |
+
"""
|
5 |
+
|
6 |
+
import math
|
7 |
+
|
8 |
+
import torch.nn as nn
|
9 |
+
|
10 |
+
from .lib.nn import SynchronizedBatchNorm2d
|
11 |
+
from .utils import load_url
|
12 |
+
|
13 |
+
BatchNorm2d = SynchronizedBatchNorm2d
|
14 |
+
|
15 |
+
|
16 |
+
__all__ = ["mobilenetv2"]
|
17 |
+
|
18 |
+
|
19 |
+
model_urls = {
|
20 |
+
"mobilenetv2": "http://sceneparsing.csail.mit.edu/model/pretrained_resnet/mobilenet_v2.pth.tar",
|
21 |
+
}
|
22 |
+
|
23 |
+
|
24 |
+
def conv_bn(inp, oup, stride):
|
25 |
+
return nn.Sequential(
|
26 |
+
nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
|
27 |
+
BatchNorm2d(oup),
|
28 |
+
nn.ReLU6(inplace=True),
|
29 |
+
)
|
30 |
+
|
31 |
+
|
32 |
+
def conv_1x1_bn(inp, oup):
|
33 |
+
return nn.Sequential(
|
34 |
+
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
|
35 |
+
BatchNorm2d(oup),
|
36 |
+
nn.ReLU6(inplace=True),
|
37 |
+
)
|
38 |
+
|
39 |
+
|
40 |
+
class InvertedResidual(nn.Module):
|
41 |
+
def __init__(self, inp, oup, stride, expand_ratio):
|
42 |
+
super(InvertedResidual, self).__init__()
|
43 |
+
self.stride = stride
|
44 |
+
assert stride in [1, 2]
|
45 |
+
|
46 |
+
hidden_dim = round(inp * expand_ratio)
|
47 |
+
self.use_res_connect = self.stride == 1 and inp == oup
|
48 |
+
|
49 |
+
if expand_ratio == 1:
|
50 |
+
self.conv = nn.Sequential(
|
51 |
+
# dw
|
52 |
+
nn.Conv2d(
|
53 |
+
hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False
|
54 |
+
),
|
55 |
+
BatchNorm2d(hidden_dim),
|
56 |
+
nn.ReLU6(inplace=True),
|
57 |
+
# pw-linear
|
58 |
+
nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
|
59 |
+
BatchNorm2d(oup),
|
60 |
+
)
|
61 |
+
else:
|
62 |
+
self.conv = nn.Sequential(
|
63 |
+
# pw
|
64 |
+
nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False),
|
65 |
+
BatchNorm2d(hidden_dim),
|
66 |
+
nn.ReLU6(inplace=True),
|
67 |
+
# dw
|
68 |
+
nn.Conv2d(
|
69 |
+
hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False
|
70 |
+
),
|
71 |
+
BatchNorm2d(hidden_dim),
|
72 |
+
nn.ReLU6(inplace=True),
|
73 |
+
# pw-linear
|
74 |
+
nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
|
75 |
+
BatchNorm2d(oup),
|
76 |
+
)
|
77 |
+
|
78 |
+
def forward(self, x):
|
79 |
+
if self.use_res_connect:
|
80 |
+
return x + self.conv(x)
|
81 |
+
else:
|
82 |
+
return self.conv(x)
|
83 |
+
|
84 |
+
|
85 |
+
class MobileNetV2(nn.Module):
|
86 |
+
def __init__(self, n_class=1000, input_size=224, width_mult=1.0):
|
87 |
+
super(MobileNetV2, self).__init__()
|
88 |
+
block = InvertedResidual
|
89 |
+
input_channel = 32
|
90 |
+
last_channel = 1280
|
91 |
+
interverted_residual_setting = [
|
92 |
+
# t, c, n, s
|
93 |
+
[1, 16, 1, 1],
|
94 |
+
[6, 24, 2, 2],
|
95 |
+
[6, 32, 3, 2],
|
96 |
+
[6, 64, 4, 2],
|
97 |
+
[6, 96, 3, 1],
|
98 |
+
[6, 160, 3, 2],
|
99 |
+
[6, 320, 1, 1],
|
100 |
+
]
|
101 |
+
|
102 |
+
# building first layer
|
103 |
+
assert input_size % 32 == 0
|
104 |
+
input_channel = int(input_channel * width_mult)
|
105 |
+
self.last_channel = (
|
106 |
+
int(last_channel * width_mult) if width_mult > 1.0 else last_channel
|
107 |
+
)
|
108 |
+
self.features = [conv_bn(3, input_channel, 2)]
|
109 |
+
# building inverted residual blocks
|
110 |
+
for t, c, n, s in interverted_residual_setting:
|
111 |
+
output_channel = int(c * width_mult)
|
112 |
+
for i in range(n):
|
113 |
+
if i == 0:
|
114 |
+
self.features.append(
|
115 |
+
block(input_channel, output_channel, s, expand_ratio=t)
|
116 |
+
)
|
117 |
+
else:
|
118 |
+
self.features.append(
|
119 |
+
block(input_channel, output_channel, 1, expand_ratio=t)
|
120 |
+
)
|
121 |
+
input_channel = output_channel
|
122 |
+
# building last several layers
|
123 |
+
self.features.append(conv_1x1_bn(input_channel, self.last_channel))
|
124 |
+
# make it nn.Sequential
|
125 |
+
self.features = nn.Sequential(*self.features)
|
126 |
+
|
127 |
+
# building classifier
|
128 |
+
self.classifier = nn.Sequential(
|
129 |
+
nn.Dropout(0.2),
|
130 |
+
nn.Linear(self.last_channel, n_class),
|
131 |
+
)
|
132 |
+
|
133 |
+
self._initialize_weights()
|
134 |
+
|
135 |
+
def forward(self, x):
|
136 |
+
x = self.features(x)
|
137 |
+
x = x.mean(3).mean(2)
|
138 |
+
x = self.classifier(x)
|
139 |
+
return x
|
140 |
+
|
141 |
+
def _initialize_weights(self):
|
142 |
+
for m in self.modules():
|
143 |
+
if isinstance(m, nn.Conv2d):
|
144 |
+
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
|
145 |
+
m.weight.data.normal_(0, math.sqrt(2.0 / n))
|
146 |
+
if m.bias is not None:
|
147 |
+
m.bias.data.zero_()
|
148 |
+
elif isinstance(m, BatchNorm2d):
|
149 |
+
m.weight.data.fill_(1)
|
150 |
+
m.bias.data.zero_()
|
151 |
+
elif isinstance(m, nn.Linear):
|
152 |
+
n = m.weight.size(1)
|
153 |
+
m.weight.data.normal_(0, 0.01)
|
154 |
+
m.bias.data.zero_()
|
155 |
+
|
156 |
+
|
157 |
+
def mobilenetv2(pretrained=False, **kwargs):
|
158 |
+
"""Constructs a MobileNet_V2 model.
|
159 |
+
|
160 |
+
Args:
|
161 |
+
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
162 |
+
"""
|
163 |
+
model = MobileNetV2(n_class=1000, **kwargs)
|
164 |
+
if pretrained:
|
165 |
+
model.load_state_dict(load_url(model_urls["mobilenetv2"]), strict=False)
|
166 |
+
return model
|
models/models.py
ADDED
@@ -0,0 +1,687 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import List
|
2 |
+
|
3 |
+
import torch
|
4 |
+
import torch.nn as nn
|
5 |
+
|
6 |
+
from . import hrnet, mobilenet, resnet, resnext
|
7 |
+
from .lib.nn import SynchronizedBatchNorm2d
|
8 |
+
|
9 |
+
BatchNorm2d = SynchronizedBatchNorm2d
|
10 |
+
|
11 |
+
|
12 |
+
class SegmentationModuleBase(nn.Module):
|
13 |
+
def __init__(self):
|
14 |
+
super(SegmentationModuleBase, self).__init__()
|
15 |
+
|
16 |
+
def pixel_acc(self, pred, label):
|
17 |
+
_, preds = torch.max(pred, dim=1)
|
18 |
+
valid = (label >= 0).long()
|
19 |
+
acc_sum = torch.sum(valid * (preds == label).long())
|
20 |
+
pixel_sum = torch.sum(valid)
|
21 |
+
acc = acc_sum.float() / (pixel_sum.float() + 1e-10)
|
22 |
+
return acc
|
23 |
+
|
24 |
+
|
25 |
+
class SegmentationModule(SegmentationModuleBase):
|
26 |
+
def __init__(self, net_enc, net_dec, crit, deep_sup_scale=None):
|
27 |
+
super(SegmentationModule, self).__init__()
|
28 |
+
self.encoder = net_enc
|
29 |
+
self.decoder = net_dec
|
30 |
+
self.crit = crit
|
31 |
+
self.deep_sup_scale = deep_sup_scale
|
32 |
+
|
33 |
+
def forward(self, feed_dict, *, segSize=None):
|
34 |
+
# training
|
35 |
+
if segSize is None:
|
36 |
+
if self.deep_sup_scale is not None: # use deep supervision technique
|
37 |
+
(pred, pred_deepsup) = self.decoder(
|
38 |
+
self.encoder(feed_dict["img_data"], return_feature_maps=True)
|
39 |
+
)
|
40 |
+
else:
|
41 |
+
pred = self.decoder(
|
42 |
+
self.encoder(feed_dict["img_data"], return_feature_maps=True)
|
43 |
+
)
|
44 |
+
|
45 |
+
loss = self.crit(pred, feed_dict["seg_label"])
|
46 |
+
if self.deep_sup_scale is not None:
|
47 |
+
loss_deepsup = self.crit(pred_deepsup, feed_dict["seg_label"])
|
48 |
+
loss = loss + loss_deepsup * self.deep_sup_scale
|
49 |
+
|
50 |
+
acc = self.pixel_acc(pred, feed_dict["seg_label"])
|
51 |
+
return loss, acc
|
52 |
+
# inference
|
53 |
+
else:
|
54 |
+
pred = self.decoder(
|
55 |
+
self.encoder(feed_dict["img_data"], return_feature_maps=True),
|
56 |
+
segSize=segSize,
|
57 |
+
)
|
58 |
+
return pred
|
59 |
+
|
60 |
+
|
61 |
+
class ModelBuilder:
|
62 |
+
# custom weights initialization
|
63 |
+
@staticmethod
|
64 |
+
def weights_init(m):
|
65 |
+
classname = m.__class__.__name__
|
66 |
+
if classname.find("Conv") != -1:
|
67 |
+
nn.init.kaiming_normal_(m.weight.data)
|
68 |
+
elif classname.find("BatchNorm") != -1:
|
69 |
+
m.weight.data.fill_(1.0)
|
70 |
+
m.bias.data.fill_(1e-4)
|
71 |
+
# elif classname.find('Linear') != -1:
|
72 |
+
# m.weight.data.normal_(0.0, 0.0001)
|
73 |
+
|
74 |
+
@staticmethod
|
75 |
+
def build_encoder(arch="resnet50dilated", fc_dim=512, weights=""):
|
76 |
+
pretrained = True if len(weights) == 0 else False
|
77 |
+
arch = arch.lower()
|
78 |
+
if arch == "mobilenetv2dilated":
|
79 |
+
orig_mobilenet = mobilenet.__dict__["mobilenetv2"](pretrained=pretrained)
|
80 |
+
net_encoder = MobileNetV2Dilated(orig_mobilenet, dilate_scale=8)
|
81 |
+
elif arch == "resnet18":
|
82 |
+
orig_resnet = resnet.__dict__["resnet18"](pretrained=pretrained)
|
83 |
+
net_encoder = Resnet(orig_resnet)
|
84 |
+
elif arch == "resnet18dilated":
|
85 |
+
orig_resnet = resnet.__dict__["resnet18"](pretrained=pretrained)
|
86 |
+
net_encoder = ResnetDilated(orig_resnet, dilate_scale=8)
|
87 |
+
elif arch == "resnet34":
|
88 |
+
raise NotImplementedError
|
89 |
+
orig_resnet = resnet.__dict__["resnet34"](pretrained=pretrained)
|
90 |
+
net_encoder = Resnet(orig_resnet)
|
91 |
+
elif arch == "resnet34dilated":
|
92 |
+
raise NotImplementedError
|
93 |
+
orig_resnet = resnet.__dict__["resnet34"](pretrained=pretrained)
|
94 |
+
net_encoder = ResnetDilated(orig_resnet, dilate_scale=8)
|
95 |
+
elif arch == "resnet50":
|
96 |
+
orig_resnet = resnet.__dict__["resnet50"](pretrained=pretrained)
|
97 |
+
net_encoder = Resnet(orig_resnet)
|
98 |
+
elif arch == "resnet50dilated":
|
99 |
+
orig_resnet = resnet.__dict__["resnet50"](pretrained=pretrained)
|
100 |
+
net_encoder = ResnetDilated(orig_resnet, dilate_scale=8)
|
101 |
+
elif arch == "resnet101":
|
102 |
+
orig_resnet = resnet.__dict__["resnet101"](pretrained=pretrained)
|
103 |
+
net_encoder = Resnet(orig_resnet)
|
104 |
+
elif arch == "resnet101dilated":
|
105 |
+
orig_resnet = resnet.__dict__["resnet101"](pretrained=pretrained)
|
106 |
+
net_encoder = ResnetDilated(orig_resnet, dilate_scale=8)
|
107 |
+
elif arch == "resnext101":
|
108 |
+
orig_resnext = resnext.__dict__["resnext101"](pretrained=pretrained)
|
109 |
+
net_encoder = Resnet(orig_resnext) # we can still use class Resnet
|
110 |
+
elif arch == "hrnetv2":
|
111 |
+
net_encoder = hrnet.__dict__["hrnetv2"](pretrained=pretrained)
|
112 |
+
else:
|
113 |
+
raise Exception("Architecture undefined!")
|
114 |
+
|
115 |
+
# encoders are usually pretrained
|
116 |
+
# net_encoder.apply(ModelBuilder.weights_init)
|
117 |
+
if len(weights) > 0:
|
118 |
+
print("Loading weights for net_encoder")
|
119 |
+
net_encoder.load_state_dict(
|
120 |
+
torch.load(weights, map_location=lambda storage, loc: storage),
|
121 |
+
strict=False,
|
122 |
+
)
|
123 |
+
return net_encoder
|
124 |
+
|
125 |
+
@staticmethod
|
126 |
+
def build_decoder(
|
127 |
+
arch="ppm_deepsup",
|
128 |
+
fc_dim=512,
|
129 |
+
num_class=150,
|
130 |
+
weights="",
|
131 |
+
use_softmax=False,
|
132 |
+
dropout=0.0,
|
133 |
+
fcn_up: int = 32,
|
134 |
+
):
|
135 |
+
arch = arch.lower()
|
136 |
+
if arch == "c1_deepsup":
|
137 |
+
net_decoder = C1DeepSup(
|
138 |
+
num_class=num_class, fc_dim=fc_dim, use_softmax=use_softmax
|
139 |
+
)
|
140 |
+
elif arch == "c1": # currently only support C1
|
141 |
+
net_decoder = C1(
|
142 |
+
num_class=num_class,
|
143 |
+
fc_dim=fc_dim,
|
144 |
+
use_softmax=use_softmax,
|
145 |
+
dropout=dropout,
|
146 |
+
fcn_up=fcn_up,
|
147 |
+
)
|
148 |
+
elif arch == "ppm":
|
149 |
+
net_decoder = PPM(
|
150 |
+
num_class=num_class, fc_dim=fc_dim, use_softmax=use_softmax
|
151 |
+
)
|
152 |
+
elif arch == "ppm_deepsup":
|
153 |
+
net_decoder = PPMDeepsup(
|
154 |
+
num_class=num_class, fc_dim=fc_dim, use_softmax=use_softmax
|
155 |
+
)
|
156 |
+
elif arch == "upernet_lite":
|
157 |
+
net_decoder = UPerNet(
|
158 |
+
num_class=num_class, fc_dim=fc_dim, use_softmax=use_softmax, fpn_dim=256
|
159 |
+
)
|
160 |
+
elif arch == "upernet":
|
161 |
+
net_decoder = UPerNet(
|
162 |
+
num_class=num_class, fc_dim=fc_dim, use_softmax=use_softmax, fpn_dim=512
|
163 |
+
)
|
164 |
+
else:
|
165 |
+
raise Exception("Architecture undefined!")
|
166 |
+
|
167 |
+
net_decoder.apply(ModelBuilder.weights_init)
|
168 |
+
if len(weights) > 0:
|
169 |
+
print("Loading weights for net_decoder")
|
170 |
+
net_decoder.load_state_dict(
|
171 |
+
torch.load(weights, map_location=lambda storage, loc: storage),
|
172 |
+
strict=False,
|
173 |
+
)
|
174 |
+
return net_decoder
|
175 |
+
|
176 |
+
|
177 |
+
def conv3x3_bn_relu(in_planes, out_planes, stride=1):
|
178 |
+
"3x3 convolution + BN + relu"
|
179 |
+
return nn.Sequential(
|
180 |
+
nn.Conv2d(
|
181 |
+
in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False
|
182 |
+
),
|
183 |
+
BatchNorm2d(out_planes),
|
184 |
+
nn.ReLU(inplace=True),
|
185 |
+
)
|
186 |
+
|
187 |
+
|
188 |
+
class Resnet(nn.Module):
|
189 |
+
def __init__(self, orig_resnet):
|
190 |
+
super(Resnet, self).__init__()
|
191 |
+
|
192 |
+
# take pretrained resnet, except AvgPool and FC
|
193 |
+
self.conv1 = orig_resnet.conv1
|
194 |
+
self.bn1 = orig_resnet.bn1
|
195 |
+
self.relu1 = orig_resnet.relu1
|
196 |
+
self.conv2 = orig_resnet.conv2
|
197 |
+
self.bn2 = orig_resnet.bn2
|
198 |
+
self.relu2 = orig_resnet.relu2
|
199 |
+
self.conv3 = orig_resnet.conv3
|
200 |
+
self.bn3 = orig_resnet.bn3
|
201 |
+
self.relu3 = orig_resnet.relu3
|
202 |
+
self.maxpool = orig_resnet.maxpool
|
203 |
+
self.layer1 = orig_resnet.layer1
|
204 |
+
self.layer2 = orig_resnet.layer2
|
205 |
+
self.layer3 = orig_resnet.layer3
|
206 |
+
self.layer4 = orig_resnet.layer4
|
207 |
+
|
208 |
+
def forward(self, x, return_feature_maps=False):
|
209 |
+
conv_out = []
|
210 |
+
|
211 |
+
x = self.relu1(self.bn1(self.conv1(x)))
|
212 |
+
x = self.relu2(self.bn2(self.conv2(x)))
|
213 |
+
x = self.relu3(self.bn3(self.conv3(x)))
|
214 |
+
x = self.maxpool(x) # b, 128, h / 2, w / 2
|
215 |
+
|
216 |
+
x = self.layer1(x)
|
217 |
+
conv_out.append(x)
|
218 |
+
# b, 128, h / 4, w / 4
|
219 |
+
x = self.layer2(x)
|
220 |
+
conv_out.append(x)
|
221 |
+
# b, 128, h / 8, w / 8
|
222 |
+
x = self.layer3(x)
|
223 |
+
conv_out.append(x)
|
224 |
+
# b, 128, h / 16, w / 16
|
225 |
+
x = self.layer4(x)
|
226 |
+
conv_out.append(x)
|
227 |
+
# b, 128, h / 32, w / 32
|
228 |
+
|
229 |
+
if return_feature_maps:
|
230 |
+
return conv_out
|
231 |
+
return [x]
|
232 |
+
|
233 |
+
|
234 |
+
class ResnetDilated(nn.Module):
|
235 |
+
def __init__(self, orig_resnet, dilate_scale=8):
|
236 |
+
super(ResnetDilated, self).__init__()
|
237 |
+
from functools import partial
|
238 |
+
|
239 |
+
if dilate_scale == 8:
|
240 |
+
orig_resnet.layer3.apply(partial(self._nostride_dilate, dilate=2))
|
241 |
+
orig_resnet.layer4.apply(partial(self._nostride_dilate, dilate=4))
|
242 |
+
elif dilate_scale == 16:
|
243 |
+
orig_resnet.layer4.apply(partial(self._nostride_dilate, dilate=2))
|
244 |
+
|
245 |
+
# take pretrained resnet, except AvgPool and FC
|
246 |
+
self.conv1 = orig_resnet.conv1
|
247 |
+
self.bn1 = orig_resnet.bn1
|
248 |
+
self.relu1 = orig_resnet.relu1
|
249 |
+
self.conv2 = orig_resnet.conv2
|
250 |
+
self.bn2 = orig_resnet.bn2
|
251 |
+
self.relu2 = orig_resnet.relu2
|
252 |
+
self.conv3 = orig_resnet.conv3
|
253 |
+
self.bn3 = orig_resnet.bn3
|
254 |
+
self.relu3 = orig_resnet.relu3
|
255 |
+
self.maxpool = orig_resnet.maxpool
|
256 |
+
self.layer1 = orig_resnet.layer1
|
257 |
+
self.layer2 = orig_resnet.layer2
|
258 |
+
self.layer3 = orig_resnet.layer3
|
259 |
+
self.layer4 = orig_resnet.layer4
|
260 |
+
|
261 |
+
def _nostride_dilate(self, m, dilate):
|
262 |
+
classname = m.__class__.__name__
|
263 |
+
if classname.find("Conv") != -1:
|
264 |
+
# the convolution with stride
|
265 |
+
if m.stride == (2, 2):
|
266 |
+
m.stride = (1, 1)
|
267 |
+
if m.kernel_size == (3, 3):
|
268 |
+
m.dilation = (dilate // 2, dilate // 2)
|
269 |
+
m.padding = (dilate // 2, dilate // 2)
|
270 |
+
# other convoluions
|
271 |
+
else:
|
272 |
+
if m.kernel_size == (3, 3):
|
273 |
+
m.dilation = (dilate, dilate)
|
274 |
+
m.padding = (dilate, dilate)
|
275 |
+
|
276 |
+
def forward(self, x, return_feature_maps=False):
|
277 |
+
conv_out = []
|
278 |
+
|
279 |
+
x = self.relu1(self.bn1(self.conv1(x)))
|
280 |
+
x = self.relu2(self.bn2(self.conv2(x)))
|
281 |
+
x = self.relu3(self.bn3(self.conv3(x)))
|
282 |
+
x = self.maxpool(x)
|
283 |
+
|
284 |
+
x = self.layer1(x)
|
285 |
+
conv_out.append(x)
|
286 |
+
x = self.layer2(x)
|
287 |
+
conv_out.append(x)
|
288 |
+
x = self.layer3(x)
|
289 |
+
conv_out.append(x)
|
290 |
+
x = self.layer4(x)
|
291 |
+
conv_out.append(x)
|
292 |
+
|
293 |
+
if return_feature_maps:
|
294 |
+
return conv_out
|
295 |
+
return [x]
|
296 |
+
|
297 |
+
|
298 |
+
class MobileNetV2Dilated(nn.Module):
|
299 |
+
def __init__(self, orig_net, dilate_scale=8):
|
300 |
+
super(MobileNetV2Dilated, self).__init__()
|
301 |
+
from functools import partial
|
302 |
+
|
303 |
+
# take pretrained mobilenet features
|
304 |
+
self.features = orig_net.features[:-1]
|
305 |
+
|
306 |
+
self.total_idx = len(self.features)
|
307 |
+
self.down_idx = [2, 4, 7, 14]
|
308 |
+
|
309 |
+
if dilate_scale == 8:
|
310 |
+
for i in range(self.down_idx[-2], self.down_idx[-1]):
|
311 |
+
self.features[i].apply(partial(self._nostride_dilate, dilate=2))
|
312 |
+
for i in range(self.down_idx[-1], self.total_idx):
|
313 |
+
self.features[i].apply(partial(self._nostride_dilate, dilate=4))
|
314 |
+
elif dilate_scale == 16:
|
315 |
+
for i in range(self.down_idx[-1], self.total_idx):
|
316 |
+
self.features[i].apply(partial(self._nostride_dilate, dilate=2))
|
317 |
+
|
318 |
+
def _nostride_dilate(self, m, dilate):
|
319 |
+
classname = m.__class__.__name__
|
320 |
+
if classname.find("Conv") != -1:
|
321 |
+
# the convolution with stride
|
322 |
+
if m.stride == (2, 2):
|
323 |
+
m.stride = (1, 1)
|
324 |
+
if m.kernel_size == (3, 3):
|
325 |
+
m.dilation = (dilate // 2, dilate // 2)
|
326 |
+
m.padding = (dilate // 2, dilate // 2)
|
327 |
+
# other convoluions
|
328 |
+
else:
|
329 |
+
if m.kernel_size == (3, 3):
|
330 |
+
m.dilation = (dilate, dilate)
|
331 |
+
m.padding = (dilate, dilate)
|
332 |
+
|
333 |
+
def forward(self, x, return_feature_maps=False):
|
334 |
+
if return_feature_maps:
|
335 |
+
conv_out = []
|
336 |
+
for i in range(self.total_idx):
|
337 |
+
x = self.features[i](x)
|
338 |
+
if i in self.down_idx:
|
339 |
+
conv_out.append(x)
|
340 |
+
conv_out.append(x)
|
341 |
+
return conv_out
|
342 |
+
|
343 |
+
else:
|
344 |
+
return [self.features(x)]
|
345 |
+
|
346 |
+
|
347 |
+
# last conv, deep supervision
|
348 |
+
class C1DeepSup(nn.Module):
|
349 |
+
def __init__(self, num_class=150, fc_dim=2048, use_softmax=False):
|
350 |
+
super(C1DeepSup, self).__init__()
|
351 |
+
self.use_softmax = use_softmax
|
352 |
+
|
353 |
+
self.cbr = conv3x3_bn_relu(fc_dim, fc_dim // 4, 1)
|
354 |
+
self.cbr_deepsup = conv3x3_bn_relu(fc_dim // 2, fc_dim // 4, 1)
|
355 |
+
|
356 |
+
# last conv
|
357 |
+
self.conv_last = nn.Conv2d(fc_dim // 4, num_class, 1, 1, 0)
|
358 |
+
self.conv_last_deepsup = nn.Conv2d(fc_dim // 4, num_class, 1, 1, 0)
|
359 |
+
|
360 |
+
def forward(self, conv_out, segSize=None):
|
361 |
+
conv5 = conv_out[-1]
|
362 |
+
|
363 |
+
x = self.cbr(conv5)
|
364 |
+
x = self.conv_last(x)
|
365 |
+
|
366 |
+
if self.use_softmax: # is True during inference
|
367 |
+
x = nn.functional.interpolate(
|
368 |
+
x, size=segSize, mode="bilinear", align_corners=False
|
369 |
+
)
|
370 |
+
x = nn.functional.softmax(x, dim=1)
|
371 |
+
return x
|
372 |
+
|
373 |
+
# deep sup
|
374 |
+
conv4 = conv_out[-2]
|
375 |
+
_ = self.cbr_deepsup(conv4)
|
376 |
+
_ = self.conv_last_deepsup(_)
|
377 |
+
|
378 |
+
x = nn.functional.log_softmax(x, dim=1)
|
379 |
+
_ = nn.functional.log_softmax(_, dim=1)
|
380 |
+
|
381 |
+
return (x, _)
|
382 |
+
|
383 |
+
|
384 |
+
# last conv
|
385 |
+
class C1(nn.Module):
|
386 |
+
def __init__(
|
387 |
+
self,
|
388 |
+
num_class=150,
|
389 |
+
fc_dim: int = 2048,
|
390 |
+
use_softmax=False,
|
391 |
+
dropout=0.0,
|
392 |
+
fcn_up: int = 32,
|
393 |
+
):
|
394 |
+
super(C1, self).__init__()
|
395 |
+
self.use_softmax = use_softmax
|
396 |
+
self.fcn_up = fcn_up
|
397 |
+
|
398 |
+
if fcn_up == 32:
|
399 |
+
in_dim = fc_dim
|
400 |
+
elif fcn_up == 16:
|
401 |
+
in_dim = int(fc_dim / 2 * 3)
|
402 |
+
else: # 8
|
403 |
+
in_dim = int(fc_dim / 2 * 3 + fc_dim / 4)
|
404 |
+
self.cbr = conv3x3_bn_relu(in_dim, fc_dim // 4, 1)
|
405 |
+
|
406 |
+
# last conv
|
407 |
+
self.dropout = nn.Dropout2d(dropout)
|
408 |
+
self.conv_last = nn.Conv2d(fc_dim // 4, num_class, 1, 1, 0)
|
409 |
+
|
410 |
+
def forward(self, conv_out: List, segSize=None):
|
411 |
+
if self.fcn_up == 32:
|
412 |
+
conv5 = conv_out[-1]
|
413 |
+
elif self.fcn_up == 16:
|
414 |
+
conv4 = conv_out[-2]
|
415 |
+
tgt_shape = conv4.shape[-2:]
|
416 |
+
conv5 = conv_out[-1]
|
417 |
+
conv5 = nn.functional.interpolate(
|
418 |
+
conv5, size=tgt_shape, mode="bilinear", align_corners=False
|
419 |
+
)
|
420 |
+
conv5 = torch.cat([conv4, conv5], dim=1)
|
421 |
+
else: # 8
|
422 |
+
conv3 = conv_out[-3]
|
423 |
+
tgt_shape = conv3.shape[-2:]
|
424 |
+
conv4 = conv_out[-2]
|
425 |
+
conv5 = conv_out[-1]
|
426 |
+
conv4 = nn.functional.interpolate(
|
427 |
+
conv4, size=tgt_shape, mode="bilinear", align_corners=False
|
428 |
+
)
|
429 |
+
conv5 = nn.functional.interpolate(
|
430 |
+
conv5, size=tgt_shape, mode="bilinear", align_corners=False
|
431 |
+
)
|
432 |
+
conv5 = torch.cat([conv3, conv4, conv5], dim=1)
|
433 |
+
x = self.cbr(conv5)
|
434 |
+
x = self.dropout(x)
|
435 |
+
x = self.conv_last(x)
|
436 |
+
|
437 |
+
return x
|
438 |
+
|
439 |
+
|
440 |
+
# pyramid pooling
|
441 |
+
class PPM(nn.Module):
|
442 |
+
def __init__(
|
443 |
+
self, num_class=150, fc_dim=4096, use_softmax=False, pool_scales=(1, 2, 3, 6)
|
444 |
+
):
|
445 |
+
super(PPM, self).__init__()
|
446 |
+
self.use_softmax = use_softmax
|
447 |
+
|
448 |
+
self.ppm = []
|
449 |
+
for scale in pool_scales:
|
450 |
+
self.ppm.append(
|
451 |
+
nn.Sequential(
|
452 |
+
nn.AdaptiveAvgPool2d(scale),
|
453 |
+
nn.Conv2d(fc_dim, 512, kernel_size=1, bias=False),
|
454 |
+
BatchNorm2d(512),
|
455 |
+
nn.ReLU(inplace=True),
|
456 |
+
)
|
457 |
+
)
|
458 |
+
self.ppm = nn.ModuleList(self.ppm)
|
459 |
+
|
460 |
+
self.conv_last = nn.Sequential(
|
461 |
+
nn.Conv2d(
|
462 |
+
fc_dim + len(pool_scales) * 512,
|
463 |
+
512,
|
464 |
+
kernel_size=3,
|
465 |
+
padding=1,
|
466 |
+
bias=False,
|
467 |
+
),
|
468 |
+
BatchNorm2d(512),
|
469 |
+
nn.ReLU(inplace=True),
|
470 |
+
nn.Dropout2d(0.1),
|
471 |
+
nn.Conv2d(512, num_class, kernel_size=1),
|
472 |
+
)
|
473 |
+
|
474 |
+
def forward(self, conv_out, segSize=None):
|
475 |
+
conv5 = conv_out[-1]
|
476 |
+
|
477 |
+
input_size = conv5.size()
|
478 |
+
ppm_out = [conv5]
|
479 |
+
for pool_scale in self.ppm:
|
480 |
+
ppm_out.append(
|
481 |
+
nn.functional.interpolate(
|
482 |
+
pool_scale(conv5),
|
483 |
+
(input_size[2], input_size[3]),
|
484 |
+
mode="bilinear",
|
485 |
+
align_corners=False,
|
486 |
+
)
|
487 |
+
)
|
488 |
+
ppm_out = torch.cat(ppm_out, 1)
|
489 |
+
|
490 |
+
x = self.conv_last(ppm_out)
|
491 |
+
|
492 |
+
if segSize is not None: # for inference
|
493 |
+
x = nn.functional.interpolate(
|
494 |
+
x, size=segSize, mode="bilinear", align_corners=False
|
495 |
+
)
|
496 |
+
return x
|
497 |
+
|
498 |
+
|
499 |
+
# pyramid pooling, deep supervision
|
500 |
+
class PPMDeepsup(nn.Module):
|
501 |
+
def __init__(
|
502 |
+
self, num_class=150, fc_dim=4096, use_softmax=False, pool_scales=(1, 2, 3, 6)
|
503 |
+
):
|
504 |
+
super(PPMDeepsup, self).__init__()
|
505 |
+
self.use_softmax = use_softmax
|
506 |
+
|
507 |
+
self.ppm = []
|
508 |
+
for scale in pool_scales:
|
509 |
+
self.ppm.append(
|
510 |
+
nn.Sequential(
|
511 |
+
nn.AdaptiveAvgPool2d(scale),
|
512 |
+
nn.Conv2d(fc_dim, 512, kernel_size=1, bias=False),
|
513 |
+
BatchNorm2d(512),
|
514 |
+
nn.ReLU(inplace=True),
|
515 |
+
)
|
516 |
+
)
|
517 |
+
self.ppm = nn.ModuleList(self.ppm)
|
518 |
+
self.cbr_deepsup = conv3x3_bn_relu(fc_dim // 2, fc_dim // 4, 1)
|
519 |
+
|
520 |
+
self.conv_last = nn.Sequential(
|
521 |
+
nn.Conv2d(
|
522 |
+
fc_dim + len(pool_scales) * 512,
|
523 |
+
512,
|
524 |
+
kernel_size=3,
|
525 |
+
padding=1,
|
526 |
+
bias=False,
|
527 |
+
),
|
528 |
+
BatchNorm2d(512),
|
529 |
+
nn.ReLU(inplace=True),
|
530 |
+
nn.Dropout2d(0.1),
|
531 |
+
nn.Conv2d(512, num_class, kernel_size=1),
|
532 |
+
)
|
533 |
+
self.conv_last_deepsup = nn.Conv2d(fc_dim // 4, num_class, 1, 1, 0)
|
534 |
+
self.dropout_deepsup = nn.Dropout2d(0.1)
|
535 |
+
|
536 |
+
def forward(self, conv_out, segSize=None):
|
537 |
+
conv5 = conv_out[-1]
|
538 |
+
|
539 |
+
input_size = conv5.size()
|
540 |
+
ppm_out = [conv5]
|
541 |
+
for pool_scale in self.ppm:
|
542 |
+
ppm_out.append(
|
543 |
+
nn.functional.interpolate(
|
544 |
+
pool_scale(conv5),
|
545 |
+
(input_size[2], input_size[3]),
|
546 |
+
mode="bilinear",
|
547 |
+
align_corners=False,
|
548 |
+
)
|
549 |
+
)
|
550 |
+
ppm_out = torch.cat(ppm_out, 1)
|
551 |
+
|
552 |
+
x = self.conv_last(ppm_out)
|
553 |
+
|
554 |
+
if self.use_softmax: # is True during inference
|
555 |
+
x = nn.functional.interpolate(
|
556 |
+
x, size=segSize, mode="bilinear", align_corners=False
|
557 |
+
)
|
558 |
+
x = nn.functional.softmax(x, dim=1)
|
559 |
+
return x
|
560 |
+
|
561 |
+
# deep sup
|
562 |
+
conv4 = conv_out[-2]
|
563 |
+
_ = self.cbr_deepsup(conv4)
|
564 |
+
_ = self.dropout_deepsup(_)
|
565 |
+
_ = self.conv_last_deepsup(_)
|
566 |
+
|
567 |
+
x = nn.functional.log_softmax(x, dim=1)
|
568 |
+
_ = nn.functional.log_softmax(_, dim=1)
|
569 |
+
|
570 |
+
return (x, _)
|
571 |
+
|
572 |
+
|
573 |
+
# upernet
|
574 |
+
class UPerNet(nn.Module):
|
575 |
+
def __init__(
|
576 |
+
self,
|
577 |
+
num_class=150,
|
578 |
+
fc_dim=4096,
|
579 |
+
use_softmax=False,
|
580 |
+
pool_scales=(1, 2, 3, 6),
|
581 |
+
fpn_inplanes=(256, 512, 1024, 2048),
|
582 |
+
fpn_dim=256,
|
583 |
+
):
|
584 |
+
super(UPerNet, self).__init__()
|
585 |
+
self.use_softmax = use_softmax
|
586 |
+
|
587 |
+
# PPM Module
|
588 |
+
self.ppm_pooling = []
|
589 |
+
self.ppm_conv = []
|
590 |
+
|
591 |
+
for scale in pool_scales:
|
592 |
+
self.ppm_pooling.append(nn.AdaptiveAvgPool2d(scale))
|
593 |
+
self.ppm_conv.append(
|
594 |
+
nn.Sequential(
|
595 |
+
nn.Conv2d(fc_dim, 512, kernel_size=1, bias=False),
|
596 |
+
BatchNorm2d(512),
|
597 |
+
nn.ReLU(inplace=True),
|
598 |
+
)
|
599 |
+
)
|
600 |
+
self.ppm_pooling = nn.ModuleList(self.ppm_pooling)
|
601 |
+
self.ppm_conv = nn.ModuleList(self.ppm_conv)
|
602 |
+
self.ppm_last_conv = conv3x3_bn_relu(
|
603 |
+
fc_dim + len(pool_scales) * 512, fpn_dim, 1
|
604 |
+
)
|
605 |
+
|
606 |
+
# FPN Module
|
607 |
+
self.fpn_in = []
|
608 |
+
for fpn_inplane in fpn_inplanes[:-1]: # skip the top layer
|
609 |
+
self.fpn_in.append(
|
610 |
+
nn.Sequential(
|
611 |
+
nn.Conv2d(fpn_inplane, fpn_dim, kernel_size=1, bias=False),
|
612 |
+
BatchNorm2d(fpn_dim),
|
613 |
+
nn.ReLU(inplace=True),
|
614 |
+
)
|
615 |
+
)
|
616 |
+
self.fpn_in = nn.ModuleList(self.fpn_in)
|
617 |
+
|
618 |
+
self.fpn_out = []
|
619 |
+
for i in range(len(fpn_inplanes) - 1): # skip the top layer
|
620 |
+
self.fpn_out.append(
|
621 |
+
nn.Sequential(
|
622 |
+
conv3x3_bn_relu(fpn_dim, fpn_dim, 1),
|
623 |
+
)
|
624 |
+
)
|
625 |
+
self.fpn_out = nn.ModuleList(self.fpn_out)
|
626 |
+
|
627 |
+
self.conv_last = nn.Sequential(
|
628 |
+
conv3x3_bn_relu(len(fpn_inplanes) * fpn_dim, fpn_dim, 1),
|
629 |
+
nn.Conv2d(fpn_dim, num_class, kernel_size=1),
|
630 |
+
)
|
631 |
+
|
632 |
+
def forward(self, conv_out, segSize=None):
|
633 |
+
conv5 = conv_out[-1]
|
634 |
+
|
635 |
+
input_size = conv5.size()
|
636 |
+
ppm_out = [conv5]
|
637 |
+
for pool_scale, pool_conv in zip(self.ppm_pooling, self.ppm_conv):
|
638 |
+
ppm_out.append(
|
639 |
+
pool_conv(
|
640 |
+
nn.functional.interpolate(
|
641 |
+
pool_scale(conv5),
|
642 |
+
(input_size[2], input_size[3]),
|
643 |
+
mode="bilinear",
|
644 |
+
align_corners=False,
|
645 |
+
)
|
646 |
+
)
|
647 |
+
)
|
648 |
+
ppm_out = torch.cat(ppm_out, 1)
|
649 |
+
f = self.ppm_last_conv(ppm_out)
|
650 |
+
|
651 |
+
fpn_feature_list = [f]
|
652 |
+
for i in reversed(range(len(conv_out) - 1)):
|
653 |
+
conv_x = conv_out[i]
|
654 |
+
conv_x = self.fpn_in[i](conv_x) # lateral branch
|
655 |
+
|
656 |
+
f = nn.functional.interpolate(
|
657 |
+
f, size=conv_x.size()[2:], mode="bilinear", align_corners=False
|
658 |
+
) # top-down branch
|
659 |
+
f = conv_x + f
|
660 |
+
|
661 |
+
fpn_feature_list.append(self.fpn_out[i](f))
|
662 |
+
|
663 |
+
fpn_feature_list.reverse() # [P2 - P5]
|
664 |
+
output_size = fpn_feature_list[0].size()[2:]
|
665 |
+
fusion_list = [fpn_feature_list[0]]
|
666 |
+
for i in range(1, len(fpn_feature_list)):
|
667 |
+
fusion_list.append(
|
668 |
+
nn.functional.interpolate(
|
669 |
+
fpn_feature_list[i],
|
670 |
+
output_size,
|
671 |
+
mode="bilinear",
|
672 |
+
align_corners=False,
|
673 |
+
)
|
674 |
+
)
|
675 |
+
fusion_out = torch.cat(fusion_list, 1)
|
676 |
+
x = self.conv_last(fusion_out)
|
677 |
+
|
678 |
+
if self.use_softmax: # is True during inference
|
679 |
+
x = nn.functional.interpolate(
|
680 |
+
x, size=segSize, mode="bilinear", align_corners=False
|
681 |
+
)
|
682 |
+
x = nn.functional.softmax(x, dim=1)
|
683 |
+
return x
|
684 |
+
|
685 |
+
x = nn.functional.log_softmax(x, dim=1)
|
686 |
+
|
687 |
+
return x
|
models/resnet.py
ADDED
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import math
|
2 |
+
|
3 |
+
import torch.nn as nn
|
4 |
+
|
5 |
+
from .lib.nn import SynchronizedBatchNorm2d
|
6 |
+
from .utils import load_url
|
7 |
+
|
8 |
+
BatchNorm2d = SynchronizedBatchNorm2d
|
9 |
+
|
10 |
+
|
11 |
+
__all__ = ["ResNet", "resnet18", "resnet50", "resnet101"] # resnet101 is coming soon!
|
12 |
+
|
13 |
+
|
14 |
+
model_urls = {
|
15 |
+
"resnet18": "http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet18-imagenet.pth",
|
16 |
+
"resnet50": "http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet50-imagenet.pth",
|
17 |
+
"resnet101": "http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet101-imagenet.pth",
|
18 |
+
}
|
19 |
+
|
20 |
+
|
21 |
+
def conv3x3(in_planes, out_planes, stride=1):
|
22 |
+
"3x3 convolution with padding"
|
23 |
+
return nn.Conv2d(
|
24 |
+
in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False
|
25 |
+
)
|
26 |
+
|
27 |
+
|
28 |
+
class BasicBlock(nn.Module):
|
29 |
+
expansion = 1
|
30 |
+
|
31 |
+
def __init__(self, inplanes, planes, stride=1, downsample=None):
|
32 |
+
super(BasicBlock, self).__init__()
|
33 |
+
self.conv1 = conv3x3(inplanes, planes, stride)
|
34 |
+
self.bn1 = BatchNorm2d(planes)
|
35 |
+
self.relu = nn.ReLU(inplace=True)
|
36 |
+
self.conv2 = conv3x3(planes, planes)
|
37 |
+
self.bn2 = BatchNorm2d(planes)
|
38 |
+
self.downsample = downsample
|
39 |
+
self.stride = stride
|
40 |
+
|
41 |
+
def forward(self, x):
|
42 |
+
residual = x
|
43 |
+
|
44 |
+
out = self.conv1(x)
|
45 |
+
out = self.bn1(out)
|
46 |
+
out = self.relu(out)
|
47 |
+
|
48 |
+
out = self.conv2(out)
|
49 |
+
out = self.bn2(out)
|
50 |
+
|
51 |
+
if self.downsample is not None:
|
52 |
+
residual = self.downsample(x)
|
53 |
+
|
54 |
+
out += residual
|
55 |
+
out = self.relu(out)
|
56 |
+
|
57 |
+
return out
|
58 |
+
|
59 |
+
|
60 |
+
class Bottleneck(nn.Module):
|
61 |
+
expansion = 4
|
62 |
+
|
63 |
+
def __init__(self, inplanes, planes, stride=1, downsample=None):
|
64 |
+
super(Bottleneck, self).__init__()
|
65 |
+
self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
|
66 |
+
self.bn1 = BatchNorm2d(planes)
|
67 |
+
self.conv2 = nn.Conv2d(
|
68 |
+
planes, planes, kernel_size=3, stride=stride, padding=1, bias=False
|
69 |
+
)
|
70 |
+
self.bn2 = BatchNorm2d(planes)
|
71 |
+
self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
|
72 |
+
self.bn3 = BatchNorm2d(planes * 4)
|
73 |
+
self.relu = nn.ReLU(inplace=True)
|
74 |
+
self.downsample = downsample
|
75 |
+
self.stride = stride
|
76 |
+
|
77 |
+
def forward(self, x):
|
78 |
+
residual = x
|
79 |
+
|
80 |
+
out = self.conv1(x)
|
81 |
+
out = self.bn1(out)
|
82 |
+
out = self.relu(out)
|
83 |
+
|
84 |
+
out = self.conv2(out)
|
85 |
+
out = self.bn2(out)
|
86 |
+
out = self.relu(out)
|
87 |
+
|
88 |
+
out = self.conv3(out)
|
89 |
+
out = self.bn3(out)
|
90 |
+
|
91 |
+
if self.downsample is not None:
|
92 |
+
residual = self.downsample(x)
|
93 |
+
|
94 |
+
out += residual
|
95 |
+
out = self.relu(out)
|
96 |
+
|
97 |
+
return out
|
98 |
+
|
99 |
+
|
100 |
+
class ResNet(nn.Module):
|
101 |
+
def __init__(self, block, layers, num_classes=1000):
|
102 |
+
self.inplanes = 128
|
103 |
+
super(ResNet, self).__init__()
|
104 |
+
self.conv1 = conv3x3(3, 64, stride=2)
|
105 |
+
self.bn1 = BatchNorm2d(64)
|
106 |
+
self.relu1 = nn.ReLU(inplace=True)
|
107 |
+
self.conv2 = conv3x3(64, 64)
|
108 |
+
self.bn2 = BatchNorm2d(64)
|
109 |
+
self.relu2 = nn.ReLU(inplace=True)
|
110 |
+
self.conv3 = conv3x3(64, 128)
|
111 |
+
self.bn3 = BatchNorm2d(128)
|
112 |
+
self.relu3 = nn.ReLU(inplace=True)
|
113 |
+
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
114 |
+
|
115 |
+
self.layer1 = self._make_layer(block, 64, layers[0])
|
116 |
+
self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
|
117 |
+
self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
|
118 |
+
self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
|
119 |
+
self.avgpool = nn.AvgPool2d(7, stride=1)
|
120 |
+
self.fc = nn.Linear(512 * block.expansion, num_classes)
|
121 |
+
|
122 |
+
for m in self.modules():
|
123 |
+
if isinstance(m, nn.Conv2d):
|
124 |
+
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
|
125 |
+
m.weight.data.normal_(0, math.sqrt(2.0 / n))
|
126 |
+
elif isinstance(m, BatchNorm2d):
|
127 |
+
m.weight.data.fill_(1)
|
128 |
+
m.bias.data.zero_()
|
129 |
+
|
130 |
+
def _make_layer(self, block, planes, blocks, stride=1):
|
131 |
+
downsample = None
|
132 |
+
if stride != 1 or self.inplanes != planes * block.expansion:
|
133 |
+
downsample = nn.Sequential(
|
134 |
+
nn.Conv2d(
|
135 |
+
self.inplanes,
|
136 |
+
planes * block.expansion,
|
137 |
+
kernel_size=1,
|
138 |
+
stride=stride,
|
139 |
+
bias=False,
|
140 |
+
),
|
141 |
+
BatchNorm2d(planes * block.expansion),
|
142 |
+
)
|
143 |
+
|
144 |
+
layers = []
|
145 |
+
layers.append(block(self.inplanes, planes, stride, downsample))
|
146 |
+
self.inplanes = planes * block.expansion
|
147 |
+
for i in range(1, blocks):
|
148 |
+
layers.append(block(self.inplanes, planes))
|
149 |
+
|
150 |
+
return nn.Sequential(*layers)
|
151 |
+
|
152 |
+
def forward(self, x):
|
153 |
+
x = self.relu1(self.bn1(self.conv1(x)))
|
154 |
+
x = self.relu2(self.bn2(self.conv2(x)))
|
155 |
+
x = self.relu3(self.bn3(self.conv3(x)))
|
156 |
+
x = self.maxpool(x)
|
157 |
+
|
158 |
+
x = self.layer1(x)
|
159 |
+
x = self.layer2(x)
|
160 |
+
x = self.layer3(x)
|
161 |
+
x = self.layer4(x)
|
162 |
+
|
163 |
+
x = self.avgpool(x)
|
164 |
+
x = x.view(x.size(0), -1)
|
165 |
+
x = self.fc(x)
|
166 |
+
|
167 |
+
return x
|
168 |
+
|
169 |
+
|
170 |
+
def resnet18(pretrained=False, **kwargs):
|
171 |
+
"""Constructs a ResNet-18 model.
|
172 |
+
|
173 |
+
Args:
|
174 |
+
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
175 |
+
"""
|
176 |
+
model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)
|
177 |
+
if pretrained:
|
178 |
+
model.load_state_dict(load_url(model_urls["resnet18"]))
|
179 |
+
return model
|
180 |
+
|
181 |
+
|
182 |
+
'''
|
183 |
+
def resnet34(pretrained=False, **kwargs):
|
184 |
+
"""Constructs a ResNet-34 model.
|
185 |
+
|
186 |
+
Args:
|
187 |
+
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
188 |
+
"""
|
189 |
+
model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs)
|
190 |
+
if pretrained:
|
191 |
+
model.load_state_dict(load_url(model_urls['resnet34']))
|
192 |
+
return model
|
193 |
+
'''
|
194 |
+
|
195 |
+
|
196 |
+
def resnet50(pretrained=False, **kwargs):
|
197 |
+
"""Constructs a ResNet-50 model.
|
198 |
+
|
199 |
+
Args:
|
200 |
+
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
201 |
+
"""
|
202 |
+
model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs)
|
203 |
+
if pretrained:
|
204 |
+
model.load_state_dict(load_url(model_urls["resnet50"]), strict=False)
|
205 |
+
return model
|
206 |
+
|
207 |
+
|
208 |
+
def resnet101(pretrained=False, **kwargs):
|
209 |
+
"""Constructs a ResNet-101 model.
|
210 |
+
|
211 |
+
Args:
|
212 |
+
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
213 |
+
"""
|
214 |
+
model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs)
|
215 |
+
if pretrained:
|
216 |
+
model.load_state_dict(load_url(model_urls["resnet101"]), strict=False)
|
217 |
+
return model
|
218 |
+
|
219 |
+
|
220 |
+
# def resnet152(pretrained=False, **kwargs):
|
221 |
+
# """Constructs a ResNet-152 model.
|
222 |
+
#
|
223 |
+
# Args:
|
224 |
+
# pretrained (bool): If True, returns a model pre-trained on ImageNet
|
225 |
+
# """
|
226 |
+
# model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs)
|
227 |
+
# if pretrained:
|
228 |
+
# model.load_state_dict(load_url(model_urls['resnet152']))
|
229 |
+
# return model
|
models/resnext.py
ADDED
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import math
|
2 |
+
|
3 |
+
import torch.nn as nn
|
4 |
+
|
5 |
+
from .lib.nn import SynchronizedBatchNorm2d
|
6 |
+
from .utils import load_url
|
7 |
+
|
8 |
+
BatchNorm2d = SynchronizedBatchNorm2d
|
9 |
+
|
10 |
+
|
11 |
+
__all__ = ["ResNeXt", "resnext101"] # support resnext 101
|
12 |
+
|
13 |
+
|
14 |
+
model_urls = {
|
15 |
+
#'resnext50': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnext50-imagenet.pth',
|
16 |
+
"resnext101": "http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnext101-imagenet.pth"
|
17 |
+
}
|
18 |
+
|
19 |
+
|
20 |
+
def conv3x3(in_planes, out_planes, stride=1):
|
21 |
+
"3x3 convolution with padding"
|
22 |
+
return nn.Conv2d(
|
23 |
+
in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False
|
24 |
+
)
|
25 |
+
|
26 |
+
|
27 |
+
class GroupBottleneck(nn.Module):
|
28 |
+
expansion = 2
|
29 |
+
|
30 |
+
def __init__(self, inplanes, planes, stride=1, groups=1, downsample=None):
|
31 |
+
super(GroupBottleneck, self).__init__()
|
32 |
+
self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
|
33 |
+
self.bn1 = BatchNorm2d(planes)
|
34 |
+
self.conv2 = nn.Conv2d(
|
35 |
+
planes,
|
36 |
+
planes,
|
37 |
+
kernel_size=3,
|
38 |
+
stride=stride,
|
39 |
+
padding=1,
|
40 |
+
groups=groups,
|
41 |
+
bias=False,
|
42 |
+
)
|
43 |
+
self.bn2 = BatchNorm2d(planes)
|
44 |
+
self.conv3 = nn.Conv2d(planes, planes * 2, kernel_size=1, bias=False)
|
45 |
+
self.bn3 = BatchNorm2d(planes * 2)
|
46 |
+
self.relu = nn.ReLU(inplace=True)
|
47 |
+
self.downsample = downsample
|
48 |
+
self.stride = stride
|
49 |
+
|
50 |
+
def forward(self, x):
|
51 |
+
residual = x
|
52 |
+
|
53 |
+
out = self.conv1(x)
|
54 |
+
out = self.bn1(out)
|
55 |
+
out = self.relu(out)
|
56 |
+
|
57 |
+
out = self.conv2(out)
|
58 |
+
out = self.bn2(out)
|
59 |
+
out = self.relu(out)
|
60 |
+
|
61 |
+
out = self.conv3(out)
|
62 |
+
out = self.bn3(out)
|
63 |
+
|
64 |
+
if self.downsample is not None:
|
65 |
+
residual = self.downsample(x)
|
66 |
+
|
67 |
+
out += residual
|
68 |
+
out = self.relu(out)
|
69 |
+
|
70 |
+
return out
|
71 |
+
|
72 |
+
|
73 |
+
class ResNeXt(nn.Module):
|
74 |
+
def __init__(self, block, layers, groups=32, num_classes=1000):
|
75 |
+
self.inplanes = 128
|
76 |
+
super(ResNeXt, self).__init__()
|
77 |
+
self.conv1 = conv3x3(3, 64, stride=2)
|
78 |
+
self.bn1 = BatchNorm2d(64)
|
79 |
+
self.relu1 = nn.ReLU(inplace=True)
|
80 |
+
self.conv2 = conv3x3(64, 64)
|
81 |
+
self.bn2 = BatchNorm2d(64)
|
82 |
+
self.relu2 = nn.ReLU(inplace=True)
|
83 |
+
self.conv3 = conv3x3(64, 128)
|
84 |
+
self.bn3 = BatchNorm2d(128)
|
85 |
+
self.relu3 = nn.ReLU(inplace=True)
|
86 |
+
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
87 |
+
|
88 |
+
self.layer1 = self._make_layer(block, 128, layers[0], groups=groups)
|
89 |
+
self.layer2 = self._make_layer(block, 256, layers[1], stride=2, groups=groups)
|
90 |
+
self.layer3 = self._make_layer(block, 512, layers[2], stride=2, groups=groups)
|
91 |
+
self.layer4 = self._make_layer(block, 1024, layers[3], stride=2, groups=groups)
|
92 |
+
self.avgpool = nn.AvgPool2d(7, stride=1)
|
93 |
+
self.fc = nn.Linear(1024 * block.expansion, num_classes)
|
94 |
+
|
95 |
+
for m in self.modules():
|
96 |
+
if isinstance(m, nn.Conv2d):
|
97 |
+
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels // m.groups
|
98 |
+
m.weight.data.normal_(0, math.sqrt(2.0 / n))
|
99 |
+
elif isinstance(m, BatchNorm2d):
|
100 |
+
m.weight.data.fill_(1)
|
101 |
+
m.bias.data.zero_()
|
102 |
+
|
103 |
+
def _make_layer(self, block, planes, blocks, stride=1, groups=1):
|
104 |
+
downsample = None
|
105 |
+
if stride != 1 or self.inplanes != planes * block.expansion:
|
106 |
+
downsample = nn.Sequential(
|
107 |
+
nn.Conv2d(
|
108 |
+
self.inplanes,
|
109 |
+
planes * block.expansion,
|
110 |
+
kernel_size=1,
|
111 |
+
stride=stride,
|
112 |
+
bias=False,
|
113 |
+
),
|
114 |
+
BatchNorm2d(planes * block.expansion),
|
115 |
+
)
|
116 |
+
|
117 |
+
layers = []
|
118 |
+
layers.append(block(self.inplanes, planes, stride, groups, downsample))
|
119 |
+
self.inplanes = planes * block.expansion
|
120 |
+
for i in range(1, blocks):
|
121 |
+
layers.append(block(self.inplanes, planes, groups=groups))
|
122 |
+
|
123 |
+
return nn.Sequential(*layers)
|
124 |
+
|
125 |
+
def forward(self, x):
|
126 |
+
x = self.relu1(self.bn1(self.conv1(x)))
|
127 |
+
x = self.relu2(self.bn2(self.conv2(x)))
|
128 |
+
x = self.relu3(self.bn3(self.conv3(x)))
|
129 |
+
x = self.maxpool(x)
|
130 |
+
|
131 |
+
x = self.layer1(x)
|
132 |
+
x = self.layer2(x)
|
133 |
+
x = self.layer3(x)
|
134 |
+
x = self.layer4(x)
|
135 |
+
|
136 |
+
x = self.avgpool(x)
|
137 |
+
x = x.view(x.size(0), -1)
|
138 |
+
x = self.fc(x)
|
139 |
+
|
140 |
+
return x
|
141 |
+
|
142 |
+
|
143 |
+
'''
|
144 |
+
def resnext50(pretrained=False, **kwargs):
|
145 |
+
"""Constructs a ResNet-50 model.
|
146 |
+
|
147 |
+
Args:
|
148 |
+
pretrained (bool): If True, returns a model pre-trained on Places
|
149 |
+
"""
|
150 |
+
model = ResNeXt(GroupBottleneck, [3, 4, 6, 3], **kwargs)
|
151 |
+
if pretrained:
|
152 |
+
model.load_state_dict(load_url(model_urls['resnext50']), strict=False)
|
153 |
+
return model
|
154 |
+
'''
|
155 |
+
|
156 |
+
|
157 |
+
def resnext101(pretrained=False, **kwargs):
|
158 |
+
"""Constructs a ResNet-101 model.
|
159 |
+
|
160 |
+
Args:
|
161 |
+
pretrained (bool): If True, returns a model pre-trained on Places
|
162 |
+
"""
|
163 |
+
model = ResNeXt(GroupBottleneck, [3, 4, 23, 3], **kwargs)
|
164 |
+
if pretrained:
|
165 |
+
model.load_state_dict(load_url(model_urls["resnext101"]), strict=False)
|
166 |
+
return model
|
167 |
+
|
168 |
+
|
169 |
+
# def resnext152(pretrained=False, **kwargs):
|
170 |
+
# """Constructs a ResNeXt-152 model.
|
171 |
+
#
|
172 |
+
# Args:
|
173 |
+
# pretrained (bool): If True, returns a model pre-trained on Places
|
174 |
+
# """
|
175 |
+
# model = ResNeXt(GroupBottleneck, [3, 8, 36, 3], **kwargs)
|
176 |
+
# if pretrained:
|
177 |
+
# model.load_state_dict(load_url(model_urls['resnext152']))
|
178 |
+
# return model
|
models/srm_conv.py
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
import torch
|
3 |
+
import torch.nn as nn
|
4 |
+
|
5 |
+
|
6 |
+
class SRMConv2d(nn.Module):
|
7 |
+
def __init__(self, stride: int = 1, padding: int = 2, clip: float = 2):
|
8 |
+
super().__init__()
|
9 |
+
self.stride = stride
|
10 |
+
self.padding = padding
|
11 |
+
self.clip = clip
|
12 |
+
self.conv = self._get_srm_filter()
|
13 |
+
|
14 |
+
def _get_srm_filter(self):
|
15 |
+
filter1 = [
|
16 |
+
[0, 0, 0, 0, 0],
|
17 |
+
[0, -1, 2, -1, 0],
|
18 |
+
[0, 2, -4, 2, 0],
|
19 |
+
[0, -1, 2, -1, 0],
|
20 |
+
[0, 0, 0, 0, 0],
|
21 |
+
]
|
22 |
+
filter2 = [
|
23 |
+
[-1, 2, -2, 2, -1],
|
24 |
+
[2, -6, 8, -6, 2],
|
25 |
+
[-2, 8, -12, 8, -2],
|
26 |
+
[2, -6, 8, -6, 2],
|
27 |
+
[-1, 2, -2, 2, -1],
|
28 |
+
]
|
29 |
+
filter3 = [
|
30 |
+
[0, 0, 0, 0, 0],
|
31 |
+
[0, 0, 0, 0, 0],
|
32 |
+
[0, 1, -2, 1, 0],
|
33 |
+
[0, 0, 0, 0, 0],
|
34 |
+
[0, 0, 0, 0, 0],
|
35 |
+
]
|
36 |
+
q = [4.0, 12.0, 2.0]
|
37 |
+
filter1 = np.asarray(filter1, dtype=float) / q[0]
|
38 |
+
filter2 = np.asarray(filter2, dtype=float) / q[1]
|
39 |
+
filter3 = np.asarray(filter3, dtype=float) / q[2]
|
40 |
+
filters = [
|
41 |
+
[filter1, filter1, filter1],
|
42 |
+
[filter2, filter2, filter2],
|
43 |
+
[filter3, filter3, filter3],
|
44 |
+
]
|
45 |
+
filters = torch.tensor(filters).float()
|
46 |
+
conv2d = nn.Conv2d(
|
47 |
+
3,
|
48 |
+
3,
|
49 |
+
kernel_size=5,
|
50 |
+
stride=self.stride,
|
51 |
+
padding=self.padding,
|
52 |
+
padding_mode="zeros",
|
53 |
+
)
|
54 |
+
conv2d.weight = nn.Parameter(filters, requires_grad=False)
|
55 |
+
conv2d.bias = nn.Parameter(torch.zeros_like(conv2d.bias), requires_grad=False)
|
56 |
+
return conv2d
|
57 |
+
|
58 |
+
def forward(self, x):
|
59 |
+
x = self.conv(x)
|
60 |
+
if self.clip != 0.0:
|
61 |
+
x = x.clamp(-self.clip, self.clip)
|
62 |
+
return x
|
63 |
+
|
64 |
+
|
65 |
+
if __name__ == "__main__":
|
66 |
+
srm = SRMConv2d()
|
67 |
+
x = torch.rand((63, 3, 64, 64))
|
68 |
+
x = srm(x)
|
models/utils.py
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import sys
|
3 |
+
|
4 |
+
try:
|
5 |
+
from urllib import urlretrieve
|
6 |
+
except ImportError:
|
7 |
+
from urllib.request import urlretrieve
|
8 |
+
|
9 |
+
import torch
|
10 |
+
|
11 |
+
|
12 |
+
def load_url(url, model_dir="./pretrained", map_location=torch.device("cpu")):
|
13 |
+
if not os.path.exists(model_dir):
|
14 |
+
os.makedirs(model_dir)
|
15 |
+
filename = url.split("/")[-1]
|
16 |
+
cached_file = os.path.join(model_dir, filename)
|
17 |
+
if not os.path.exists(cached_file):
|
18 |
+
sys.stderr.write('Downloading: "{}" to {}\n'.format(url, cached_file))
|
19 |
+
urlretrieve(url, cached_file)
|
20 |
+
return torch.load(cached_file, map_location=map_location)
|
opt.py
ADDED
@@ -0,0 +1,483 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import argparse
|
2 |
+
import os
|
3 |
+
import sys
|
4 |
+
import time
|
5 |
+
from typing import List, Optional
|
6 |
+
|
7 |
+
import prettytable as pt
|
8 |
+
import torch
|
9 |
+
import yaml
|
10 |
+
from termcolor import cprint
|
11 |
+
|
12 |
+
|
13 |
+
def load_dataset_arguments(opt):
|
14 |
+
if opt.load is None:
|
15 |
+
return
|
16 |
+
|
17 |
+
# exclude parameters assigned in the command
|
18 |
+
if len(sys.argv) > 1:
|
19 |
+
arguments = sys.argv[1:]
|
20 |
+
arguments = list(
|
21 |
+
map(lambda x: x.replace("--", ""), filter(lambda x: "--" in x, arguments))
|
22 |
+
)
|
23 |
+
else:
|
24 |
+
arguments = []
|
25 |
+
|
26 |
+
# load parameters in the yaml file
|
27 |
+
assert os.path.exists(opt.load)
|
28 |
+
with open(opt.load, "r") as f:
|
29 |
+
yaml_arguments = yaml.safe_load(f)
|
30 |
+
# TODO this should be verified
|
31 |
+
for k, v in yaml_arguments.items():
|
32 |
+
if not k in arguments:
|
33 |
+
setattr(opt, k, v)
|
34 |
+
|
35 |
+
|
36 |
+
def get_opt(additional_parsers: Optional[List] = None):
|
37 |
+
parents = [get_arguments_parser()]
|
38 |
+
if additional_parsers:
|
39 |
+
parents.extend(additional_parsers)
|
40 |
+
parser = argparse.ArgumentParser(
|
41 |
+
"Options for training and evaluation", parents=parents, allow_abbrev=False
|
42 |
+
)
|
43 |
+
opt = parser.parse_known_args()[0]
|
44 |
+
|
45 |
+
# load dataset argument file
|
46 |
+
load_dataset_arguments(opt)
|
47 |
+
|
48 |
+
# user-defined warnings and assertions
|
49 |
+
if opt.decoder.lower() not in ["c1"]:
|
50 |
+
cprint("Not supported yet! Check if the output use log_softmax!", "red")
|
51 |
+
time.sleep(3)
|
52 |
+
|
53 |
+
if opt.map_mask_weight > 0.0 or opt.volume_mask_weight > 0.0:
|
54 |
+
cprint("Mask loss is not 0!", "red")
|
55 |
+
time.sleep(3)
|
56 |
+
|
57 |
+
if opt.val_set != "val":
|
58 |
+
cprint(f"Evaluating on {opt.val_set} set!", "red")
|
59 |
+
time.sleep(3)
|
60 |
+
|
61 |
+
if opt.mvc_spixel:
|
62 |
+
assert (
|
63 |
+
not opt.loss_on_mid_map
|
64 |
+
), "Middle map supervision is not supported with spixel!"
|
65 |
+
|
66 |
+
if "early" in opt.modality:
|
67 |
+
assert (
|
68 |
+
len(opt.modality) == 1
|
69 |
+
), "Early fusion is not supported for multi-modality!"
|
70 |
+
for modal in opt.modality:
|
71 |
+
assert modal in [
|
72 |
+
"rgb",
|
73 |
+
"srm",
|
74 |
+
"bayar",
|
75 |
+
"early",
|
76 |
+
], f"Unsupported modality {modal}!"
|
77 |
+
|
78 |
+
if opt.resume:
|
79 |
+
assert os.path.exists(opt.resume)
|
80 |
+
|
81 |
+
# if opt.mvc_weight <= 0. and opt.consistency_weight > 0.:
|
82 |
+
# assert opt.consistency_source == 'self', 'Ensemble consistency is not supported when mvc_weight is 0!'
|
83 |
+
|
84 |
+
# automatically set parameters
|
85 |
+
if len(sys.argv) > 1:
|
86 |
+
arguments = sys.argv[1:]
|
87 |
+
arguments = list(
|
88 |
+
map(lambda x: x.replace("--", ""), filter(lambda x: "--" in x, arguments))
|
89 |
+
)
|
90 |
+
params = []
|
91 |
+
for argument in arguments:
|
92 |
+
if not argument in [
|
93 |
+
"suffix",
|
94 |
+
"save_root_path",
|
95 |
+
"dataset",
|
96 |
+
"source",
|
97 |
+
"resume",
|
98 |
+
"num_workers",
|
99 |
+
"eval_freq",
|
100 |
+
"print_freq",
|
101 |
+
"lr_steps",
|
102 |
+
"rgb_resume",
|
103 |
+
"srm_resume",
|
104 |
+
"bayar_resume",
|
105 |
+
"teacher_resume",
|
106 |
+
"occ",
|
107 |
+
"load",
|
108 |
+
"amp_opt_level",
|
109 |
+
"val_shuffle",
|
110 |
+
"tile_size",
|
111 |
+
"modality",
|
112 |
+
]:
|
113 |
+
try:
|
114 |
+
value = (
|
115 |
+
str(eval("opt.{}".format(argument.split("=")[0])))
|
116 |
+
.replace("[", "")
|
117 |
+
.replace("]", "")
|
118 |
+
.replace(" ", "-")
|
119 |
+
.replace(",", "")
|
120 |
+
)
|
121 |
+
params.append(
|
122 |
+
argument.split("=")[0].replace("_", "").replace(" ", "")
|
123 |
+
+ "="
|
124 |
+
+ value
|
125 |
+
)
|
126 |
+
except:
|
127 |
+
cprint("Unknown argument: {}".format(argument), "red")
|
128 |
+
if "early" in opt.modality:
|
129 |
+
params.append("modality=early")
|
130 |
+
test_name = "_".join(params)
|
131 |
+
|
132 |
+
else:
|
133 |
+
test_name = ""
|
134 |
+
|
135 |
+
time_stamp = time.strftime("%b-%d-%H-%M-%S", time.localtime())
|
136 |
+
dir_name = "{}_{}{}_{}".format(
|
137 |
+
"-".join(list(opt.train_datalist.keys())).upper(),
|
138 |
+
test_name,
|
139 |
+
opt.suffix,
|
140 |
+
time_stamp,
|
141 |
+
).replace("__", "_")
|
142 |
+
|
143 |
+
opt.time_stamp = time_stamp
|
144 |
+
opt.dir_name = dir_name
|
145 |
+
opt.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
146 |
+
|
147 |
+
if opt.debug or opt.wholetest:
|
148 |
+
opt.val_shuffle = True
|
149 |
+
cprint("Setting val_shuffle to True in debug and wholetest mode!", "red")
|
150 |
+
time.sleep(3)
|
151 |
+
|
152 |
+
if len(opt.modality) < 2 and opt.mvc_weight != 0.0:
|
153 |
+
opt.mvc_weight = 0.0
|
154 |
+
cprint(
|
155 |
+
"Setting multi-view consistency weight to 0. for single modality training",
|
156 |
+
"red",
|
157 |
+
)
|
158 |
+
time.sleep(3)
|
159 |
+
|
160 |
+
if "early" in opt.modality:
|
161 |
+
opt.mvc_single_weight = {"early": 1.0}
|
162 |
+
else:
|
163 |
+
if "rgb" not in opt.modality:
|
164 |
+
opt.mvc_single_weight[0] = 0.0
|
165 |
+
if "srm" not in opt.modality:
|
166 |
+
opt.mvc_single_weight[1] = 0.0
|
167 |
+
if "bayar" not in opt.modality:
|
168 |
+
opt.mvc_single_weight[2] = 0.0
|
169 |
+
weight_sum = sum(opt.mvc_single_weight)
|
170 |
+
single_weight = list(map(lambda x: x / weight_sum, opt.mvc_single_weight))
|
171 |
+
opt.mvc_single_weight = {
|
172 |
+
"rgb": single_weight[0],
|
173 |
+
"srm": single_weight[1],
|
174 |
+
"bayar": single_weight[2],
|
175 |
+
}
|
176 |
+
cprint(
|
177 |
+
"Change mvc single modality weight to {}".format(opt.mvc_single_weight), "blue"
|
178 |
+
)
|
179 |
+
time.sleep(3)
|
180 |
+
|
181 |
+
# print parameters
|
182 |
+
tb = pt.PrettyTable(field_names=["Arguments", "Values"])
|
183 |
+
for k, v in vars(opt).items():
|
184 |
+
# some parameters might be too long to display
|
185 |
+
if k not in ["dir_name", "resume", "rgb_resume", "srm_resume", "bayar_resume"]:
|
186 |
+
tb.add_row([k, v])
|
187 |
+
print(tb)
|
188 |
+
|
189 |
+
return opt
|
190 |
+
|
191 |
+
|
192 |
+
def get_arguments_parser():
|
193 |
+
parser = argparse.ArgumentParser(
|
194 |
+
"CVPR2022 image manipulation detection model", add_help=False
|
195 |
+
)
|
196 |
+
parser.add_argument("--debug", action="store_true", default=False)
|
197 |
+
parser.add_argument("--wholetest", action="store_true", default=False)
|
198 |
+
|
199 |
+
parser.add_argument(
|
200 |
+
"--load", default="configs/final.yaml", help="Load configuration YAML file."
|
201 |
+
)
|
202 |
+
parser.add_argument("--num_class", type=int, default=1, help="Use sigmoid.")
|
203 |
+
|
204 |
+
# loss-related
|
205 |
+
parser.add_argument("--map_label_weight", type=float, default=1.0)
|
206 |
+
parser.add_argument("--volume_label_weight", type=float, default=1.0)
|
207 |
+
parser.add_argument(
|
208 |
+
"--map_mask_weight",
|
209 |
+
type=float,
|
210 |
+
default=0.0,
|
211 |
+
help="Only use this for debug purpose.",
|
212 |
+
)
|
213 |
+
parser.add_argument(
|
214 |
+
"--volume_mask_weight",
|
215 |
+
type=float,
|
216 |
+
default=0.0,
|
217 |
+
help="Only use this for debug purpose.",
|
218 |
+
)
|
219 |
+
parser.add_argument(
|
220 |
+
"--consistency_weight",
|
221 |
+
type=float,
|
222 |
+
default=0.0,
|
223 |
+
help="Consitency between output map and volume within a single view.",
|
224 |
+
)
|
225 |
+
parser.add_argument(
|
226 |
+
"--consistency_type", type=str, default="l2", choices=["l1", "l2"]
|
227 |
+
)
|
228 |
+
parser.add_argument(
|
229 |
+
"--consistency_kmeans",
|
230 |
+
action="store_true",
|
231 |
+
default=False,
|
232 |
+
help="Perform k-means on the volume to determine pristine and modified areas.",
|
233 |
+
)
|
234 |
+
parser.add_argument(
|
235 |
+
"--consistency_stop_map_grad",
|
236 |
+
action="store_true",
|
237 |
+
default=False,
|
238 |
+
help="Stop gradient for the map.",
|
239 |
+
)
|
240 |
+
parser.add_argument(
|
241 |
+
"--consistency_source", type=str, default="self", choices=["self", "ensemble"]
|
242 |
+
)
|
243 |
+
parser.add_argument("--map_entropy_weight", type=float, default=0.0)
|
244 |
+
parser.add_argument("--volume_entropy_weight", type=float, default=0.0)
|
245 |
+
parser.add_argument("--mvc_weight", type=float, default=0.0)
|
246 |
+
parser.add_argument(
|
247 |
+
"--mvc_time_dependent",
|
248 |
+
action="store_true",
|
249 |
+
default=False,
|
250 |
+
help="Use Gaussian smooth on the MVCW weight.",
|
251 |
+
)
|
252 |
+
parser.add_argument("--mvc_soft", action="store_true", default=False)
|
253 |
+
parser.add_argument("--mvc_zeros_on_au", action="store_true", default=False)
|
254 |
+
parser.add_argument(
|
255 |
+
"--mvc_single_weight",
|
256 |
+
type=float,
|
257 |
+
nargs="+",
|
258 |
+
default=[1.0, 1.0, 1.0],
|
259 |
+
help="Weight for the RGB, SRM and Bayar modality for MVC training.",
|
260 |
+
)
|
261 |
+
parser.add_argument(
|
262 |
+
"--mvc_steepness", type=float, default=5.0, help="The large the slower."
|
263 |
+
)
|
264 |
+
parser.add_argument("--mvc_spixel", action="store_true", default=False)
|
265 |
+
parser.add_argument("--mvc_num_spixel", type=int, default=100)
|
266 |
+
parser.add_argument(
|
267 |
+
"--loss_on_mid_map",
|
268 |
+
action="store_true",
|
269 |
+
default=False,
|
270 |
+
help="This only applies for the output map, but not for the consistency volume.",
|
271 |
+
)
|
272 |
+
parser.add_argument(
|
273 |
+
"--label_loss_on_whole_map",
|
274 |
+
action="store_true",
|
275 |
+
default=False,
|
276 |
+
help="Apply cls loss on the avg(map) for pristine images, instead of max(map).",
|
277 |
+
)
|
278 |
+
|
279 |
+
# network architecture
|
280 |
+
parser.add_argument("--modality", type=str, default=["rgb"], nargs="+")
|
281 |
+
parser.add_argument("--srm_clip", type=float, default=5.0)
|
282 |
+
parser.add_argument("--bayar_magnitude", type=float, default=1.0)
|
283 |
+
parser.add_argument("--encoder", type=str, default="ResNet50")
|
284 |
+
parser.add_argument("--encoder_weight", type=str, default="")
|
285 |
+
parser.add_argument("--decoder", type=str, default="C1")
|
286 |
+
parser.add_argument("--decoder_weight", type=str, default="")
|
287 |
+
parser.add_argument(
|
288 |
+
"--fc_dim",
|
289 |
+
type=int,
|
290 |
+
default=2048,
|
291 |
+
help="Changing this might leads to error in the conjunction between encoder and decoder.",
|
292 |
+
)
|
293 |
+
parser.add_argument(
|
294 |
+
"--volume_block_idx",
|
295 |
+
type=int,
|
296 |
+
default=1,
|
297 |
+
choices=[0, 1, 2, 3],
|
298 |
+
help="Compute the consistency volume at certain block.",
|
299 |
+
)
|
300 |
+
parser.add_argument("--share_embed_head", action="store_true", default=False)
|
301 |
+
parser.add_argument(
|
302 |
+
"--fcn_up",
|
303 |
+
type=int,
|
304 |
+
default=32,
|
305 |
+
choices=[8, 16, 32],
|
306 |
+
help="FCN architecture, 32s, 16s, or 8s.",
|
307 |
+
)
|
308 |
+
parser.add_argument("--gem", action="store_true", default=False)
|
309 |
+
parser.add_argument("--gem_coef", type=float, default=100)
|
310 |
+
parser.add_argument("--gsm", action="store_true", default=False)
|
311 |
+
parser.add_argument(
|
312 |
+
"--map_portion",
|
313 |
+
type=float,
|
314 |
+
default=0,
|
315 |
+
help="Select topk portion of the output map for the image-level classification. 0 for use max.",
|
316 |
+
)
|
317 |
+
parser.add_argument("--otsu_sel", action="store_true", default=False)
|
318 |
+
parser.add_argument("--otsu_portion", type=float, default=1.0)
|
319 |
+
|
320 |
+
# training parameters
|
321 |
+
parser.add_argument("--no_gaussian_blur", action="store_true", default=False)
|
322 |
+
parser.add_argument("--no_color_jitter", action="store_true", default=False)
|
323 |
+
parser.add_argument("--no_jpeg_compression", action="store_true", default=False)
|
324 |
+
parser.add_argument("--resize_aug", action="store_true", default=False)
|
325 |
+
parser.add_argument(
|
326 |
+
"--uncorrect_label",
|
327 |
+
action="store_true",
|
328 |
+
default=False,
|
329 |
+
help="This will not correct image-level labels caused by image cropping.",
|
330 |
+
)
|
331 |
+
parser.add_argument("--input_size", type=int, default=224)
|
332 |
+
parser.add_argument("--dropout", type=float, default=0.0)
|
333 |
+
parser.add_argument(
|
334 |
+
"--optimizer", type=str, default="adamw", choices=["sgd", "adamw"]
|
335 |
+
)
|
336 |
+
parser.add_argument("--resume", type=str, default="")
|
337 |
+
parser.add_argument("--eval", action="store_true", default=False)
|
338 |
+
parser.add_argument(
|
339 |
+
"--val_set",
|
340 |
+
type=str,
|
341 |
+
default="val",
|
342 |
+
choices=["train", "val"],
|
343 |
+
help="Change to train for debug purpose.",
|
344 |
+
)
|
345 |
+
parser.add_argument(
|
346 |
+
"--val_shuffle", action="store_true", default=False, help="Shuffle val set."
|
347 |
+
)
|
348 |
+
parser.add_argument("--save_figure", action="store_true", default=False)
|
349 |
+
parser.add_argument("--figure_path", type=str, default="figures")
|
350 |
+
parser.add_argument("--batch_size", type=int, default=36)
|
351 |
+
parser.add_argument("--epochs", type=int, default=60)
|
352 |
+
parser.add_argument("--eval_freq", type=int, default=3)
|
353 |
+
parser.add_argument("--weight_decay", type=float, default=5e-4)
|
354 |
+
parser.add_argument("--num_workers", type=int, default=36)
|
355 |
+
parser.add_argument("--grad_clip", type=float, default=0.0)
|
356 |
+
# lr
|
357 |
+
parser.add_argument(
|
358 |
+
"--sched",
|
359 |
+
default="cosine",
|
360 |
+
type=str,
|
361 |
+
metavar="SCHEDULER",
|
362 |
+
help='LR scheduler (default: "cosine"',
|
363 |
+
)
|
364 |
+
parser.add_argument(
|
365 |
+
"--lr",
|
366 |
+
type=float,
|
367 |
+
default=1e-4,
|
368 |
+
metavar="LR",
|
369 |
+
help="learning rate (default: 5e-4)",
|
370 |
+
)
|
371 |
+
parser.add_argument(
|
372 |
+
"--lr-noise",
|
373 |
+
type=float,
|
374 |
+
nargs="+",
|
375 |
+
default=None,
|
376 |
+
metavar="pct, pct",
|
377 |
+
help="learning rate noise on/off epoch percentages",
|
378 |
+
)
|
379 |
+
parser.add_argument(
|
380 |
+
"--lr-noise-pct",
|
381 |
+
type=float,
|
382 |
+
default=0.67,
|
383 |
+
metavar="PERCENT",
|
384 |
+
help="learning rate noise limit percent (default: 0.67)",
|
385 |
+
)
|
386 |
+
parser.add_argument(
|
387 |
+
"--lr-noise-std",
|
388 |
+
type=float,
|
389 |
+
default=1.0,
|
390 |
+
metavar="STDDEV",
|
391 |
+
help="learning rate noise std-dev (default: 1.0)",
|
392 |
+
)
|
393 |
+
parser.add_argument(
|
394 |
+
"--warmup-lr",
|
395 |
+
type=float,
|
396 |
+
default=2e-7,
|
397 |
+
metavar="LR",
|
398 |
+
help="warmup learning rate (default: 1e-6)",
|
399 |
+
)
|
400 |
+
parser.add_argument(
|
401 |
+
"--min-lr",
|
402 |
+
type=float,
|
403 |
+
default=2e-6,
|
404 |
+
metavar="LR",
|
405 |
+
help="lower lr bound for cyclic schedulers that hit 0 (1e-5)",
|
406 |
+
)
|
407 |
+
parser.add_argument(
|
408 |
+
"--decay-epochs",
|
409 |
+
type=float,
|
410 |
+
default=20,
|
411 |
+
metavar="N",
|
412 |
+
help="epoch interval to decay LR",
|
413 |
+
)
|
414 |
+
parser.add_argument(
|
415 |
+
"--warmup-epochs",
|
416 |
+
type=int,
|
417 |
+
default=5,
|
418 |
+
metavar="N",
|
419 |
+
help="epochs to warmup LR, if scheduler supports",
|
420 |
+
)
|
421 |
+
parser.add_argument(
|
422 |
+
"--cooldown-epochs",
|
423 |
+
type=int,
|
424 |
+
default=5,
|
425 |
+
metavar="N",
|
426 |
+
help="epochs to cooldown LR at min_lr, after cyclic schedule ends",
|
427 |
+
)
|
428 |
+
parser.add_argument(
|
429 |
+
"--patience-epochs",
|
430 |
+
type=int,
|
431 |
+
default=5,
|
432 |
+
metavar="N",
|
433 |
+
help="patience epochs for Plateau LR scheduler (default: 10",
|
434 |
+
)
|
435 |
+
parser.add_argument(
|
436 |
+
"--decay-rate",
|
437 |
+
"-dr",
|
438 |
+
type=float,
|
439 |
+
default=0.5,
|
440 |
+
metavar="RATE",
|
441 |
+
help="LR decay rate (default: 0.1)",
|
442 |
+
)
|
443 |
+
parser.add_argument("--lr_cycle_limit", "-lcl", type=int, default=1)
|
444 |
+
parser.add_argument("--lr_cycle_mul", "-lcm", type=float, default=1)
|
445 |
+
|
446 |
+
# inference hyperparameters
|
447 |
+
parser.add_argument("--mask_threshold", type=float, default=0.5)
|
448 |
+
parser.add_argument(
|
449 |
+
"-lis",
|
450 |
+
"--large_image_strategy",
|
451 |
+
choices=["rescale", "slide", "none"],
|
452 |
+
default="slide",
|
453 |
+
help="Slide will get better performance than rescale.",
|
454 |
+
)
|
455 |
+
parser.add_argument(
|
456 |
+
"--tile_size",
|
457 |
+
type=int,
|
458 |
+
default=768,
|
459 |
+
help="If the testing image is larger than tile_size, I will use sliding window to do the inference.",
|
460 |
+
)
|
461 |
+
parser.add_argument("--tile_overlap", type=float, default=0.1)
|
462 |
+
parser.add_argument("--spixel_postproc", action="store_true", default=False)
|
463 |
+
parser.add_argument("--convcrf_postproc", action="store_true", default=False)
|
464 |
+
parser.add_argument("--convcrf_shape", type=int, default=512)
|
465 |
+
parser.add_argument("--crf_postproc", action="store_true", default=False)
|
466 |
+
parser.add_argument("--max_pool_postproc", type=int, default=1)
|
467 |
+
parser.add_argument("--crf_downsample", type=int, default=1)
|
468 |
+
parser.add_argument("--crf_iter_max", type=int, default=5)
|
469 |
+
parser.add_argument("--crf_pos_w", type=int, default=3)
|
470 |
+
parser.add_argument("--crf_pos_xy_std", type=int, default=1)
|
471 |
+
parser.add_argument("--crf_bi_w", type=int, default=4)
|
472 |
+
parser.add_argument("--crf_bi_xy_std", type=int, default=67)
|
473 |
+
parser.add_argument("--crf_bi_rgb_std", type=int, default=3)
|
474 |
+
|
475 |
+
# save
|
476 |
+
parser.add_argument("--save_root_path", type=str, default="tmp")
|
477 |
+
parser.add_argument("--suffix", type=str, default="")
|
478 |
+
parser.add_argument("--print_freq", type=int, default=100)
|
479 |
+
|
480 |
+
# misc
|
481 |
+
parser.add_argument("--seed", type=int, default=1)
|
482 |
+
|
483 |
+
return parser
|
requirements.txt
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
albumentations==1.0.0
|
2 |
+
einops==0.4.1
|
3 |
+
fast_pytorch_kmeans==0.1.6
|
4 |
+
glob2==0.7
|
5 |
+
gpustat==0.6.0
|
6 |
+
h5py==3.6.0
|
7 |
+
matplotlib==3.3.4
|
8 |
+
numpy==1.22.4
|
9 |
+
opencv_contrib_python==4.5.3.56
|
10 |
+
opencv_python==4.4.0.46
|
11 |
+
opencv_python_headless==4.5.3.56
|
12 |
+
pandas==1.3.5
|
13 |
+
pathlib2==2.3.5
|
14 |
+
Pillow==9.4.0
|
15 |
+
prettytable==2.2.1
|
16 |
+
pydensecrf==1.0rc2
|
17 |
+
PyYAML==5.4.1
|
18 |
+
scikit_image==0.18.3
|
19 |
+
scikit_learn==0.24.1
|
20 |
+
scipy==1.7.3
|
21 |
+
spatial_correlation_sampler==0.4.0
|
22 |
+
SQLAlchemy==1.4.15
|
23 |
+
sync_batchnorm==0.0.1
|
24 |
+
tensorboard==2.12.2
|
25 |
+
termcolor==2.4.0
|
26 |
+
timm==0.9.12
|
27 |
+
torch==1.12.1+cu116
|
28 |
+
torchvision==0.13.1+cu116
|
29 |
+
tqdm==4.64.1
|
utils/__init__.py
ADDED
File without changes
|
utils/convcrf/__init__.py
ADDED
File without changes
|
utils/convcrf/convcrf.py
ADDED
@@ -0,0 +1,669 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
The MIT License (MIT)
|
3 |
+
|
4 |
+
Copyright (c) 2017 Marvin Teichmann
|
5 |
+
"""
|
6 |
+
|
7 |
+
from __future__ import absolute_import, division, print_function
|
8 |
+
|
9 |
+
import logging
|
10 |
+
import math
|
11 |
+
import os
|
12 |
+
import sys
|
13 |
+
import warnings
|
14 |
+
|
15 |
+
import numpy as np
|
16 |
+
import scipy as scp
|
17 |
+
|
18 |
+
logging.basicConfig(
|
19 |
+
format="%(asctime)s %(levelname)s %(message)s",
|
20 |
+
level=logging.INFO,
|
21 |
+
stream=sys.stdout,
|
22 |
+
)
|
23 |
+
|
24 |
+
try:
|
25 |
+
import pyinn as P
|
26 |
+
|
27 |
+
has_pyinn = True
|
28 |
+
except ImportError:
|
29 |
+
# PyInn is required to use our cuda based message-passing implementation
|
30 |
+
# Torch 0.4 provides a im2col operation, which will be used instead.
|
31 |
+
# It is ~15% slower.
|
32 |
+
has_pyinn = False
|
33 |
+
pass
|
34 |
+
|
35 |
+
import gc
|
36 |
+
|
37 |
+
import torch
|
38 |
+
import torch.nn as nn
|
39 |
+
import torch.nn.functional as F
|
40 |
+
from torch.autograd import Variable
|
41 |
+
from torch.nn import functional as nnfun
|
42 |
+
from torch.nn.parameter import Parameter
|
43 |
+
|
44 |
+
# Default config as proposed by Philipp Kraehenbuehl and Vladlen Koltun,
|
45 |
+
default_conf = {
|
46 |
+
"filter_size": 11,
|
47 |
+
"blur": 4,
|
48 |
+
"merge": True,
|
49 |
+
"norm": "none",
|
50 |
+
"weight": "vector",
|
51 |
+
"unary_weight": 1,
|
52 |
+
"weight_init": 0.2,
|
53 |
+
"trainable": False,
|
54 |
+
"convcomp": False,
|
55 |
+
"logsoftmax": True, # use logsoftmax for numerical stability
|
56 |
+
"softmax": True,
|
57 |
+
"skip_init_softmax": False,
|
58 |
+
"final_softmax": False,
|
59 |
+
"pos_feats": {
|
60 |
+
"sdims": 3,
|
61 |
+
"compat": 3,
|
62 |
+
},
|
63 |
+
"col_feats": {
|
64 |
+
"sdims": 80,
|
65 |
+
"schan": 13, # schan depend on the input scale.
|
66 |
+
# use schan = 13 for images in [0, 255]
|
67 |
+
# for normalized images in [-0.5, 0.5] try schan = 0.1
|
68 |
+
"compat": 10,
|
69 |
+
"use_bias": False,
|
70 |
+
},
|
71 |
+
"trainable_bias": False,
|
72 |
+
"pyinn": False,
|
73 |
+
}
|
74 |
+
|
75 |
+
# Config used for test cases on 10 x 10 pixel greyscale inpu
|
76 |
+
test_config = {
|
77 |
+
"filter_size": 5,
|
78 |
+
"blur": 1,
|
79 |
+
"merge": False,
|
80 |
+
"norm": "sym",
|
81 |
+
"trainable": False,
|
82 |
+
"weight": "scalar",
|
83 |
+
"unary_weight": 1,
|
84 |
+
"weight_init": 0.5,
|
85 |
+
"convcomp": False,
|
86 |
+
"trainable": False,
|
87 |
+
"convcomp": False,
|
88 |
+
"logsoftmax": True, # use logsoftmax for numerical stability
|
89 |
+
"softmax": True,
|
90 |
+
"pos_feats": {
|
91 |
+
"sdims": 1.5,
|
92 |
+
"compat": 3,
|
93 |
+
},
|
94 |
+
"col_feats": {"sdims": 2, "schan": 2, "compat": 3, "use_bias": True},
|
95 |
+
"trainable_bias": False,
|
96 |
+
}
|
97 |
+
|
98 |
+
|
99 |
+
class GaussCRF(nn.Module):
|
100 |
+
"""Implements ConvCRF with hand-crafted features.
|
101 |
+
|
102 |
+
It uses the more generic ConvCRF class as basis and utilizes a config
|
103 |
+
dict to easily set hyperparameters and follows the design choices of:
|
104 |
+
Philipp Kraehenbuehl and Vladlen Koltun, "Efficient Inference in Fully
|
105 |
+
"Connected CRFs with Gaussian Edge Pots" (arxiv.org/abs/1210.5644)
|
106 |
+
"""
|
107 |
+
|
108 |
+
def __init__(self, conf, shape, nclasses=None, use_gpu=True):
|
109 |
+
super(GaussCRF, self).__init__()
|
110 |
+
|
111 |
+
self.conf = conf
|
112 |
+
self.shape = shape
|
113 |
+
self.nclasses = nclasses
|
114 |
+
|
115 |
+
self.trainable = conf["trainable"]
|
116 |
+
|
117 |
+
if not conf["trainable_bias"]:
|
118 |
+
self.register_buffer("mesh", self._create_mesh())
|
119 |
+
else:
|
120 |
+
self.register_parameter("mesh", Parameter(self._create_mesh()))
|
121 |
+
|
122 |
+
if self.trainable:
|
123 |
+
|
124 |
+
def register(name, tensor):
|
125 |
+
self.register_parameter(name, Parameter(tensor))
|
126 |
+
|
127 |
+
else:
|
128 |
+
|
129 |
+
def register(name, tensor):
|
130 |
+
self.register_buffer(name, Variable(tensor))
|
131 |
+
|
132 |
+
register("pos_sdims", torch.Tensor([1 / conf["pos_feats"]["sdims"]]))
|
133 |
+
|
134 |
+
if conf["col_feats"]["use_bias"]:
|
135 |
+
register("col_sdims", torch.Tensor([1 / conf["col_feats"]["sdims"]]))
|
136 |
+
else:
|
137 |
+
self.col_sdims = None
|
138 |
+
|
139 |
+
register("col_schan", torch.Tensor([1 / conf["col_feats"]["schan"]]))
|
140 |
+
register("col_compat", torch.Tensor([conf["col_feats"]["compat"]]))
|
141 |
+
register("pos_compat", torch.Tensor([conf["pos_feats"]["compat"]]))
|
142 |
+
|
143 |
+
if conf["weight"] is None:
|
144 |
+
weight = None
|
145 |
+
elif conf["weight"] == "scalar":
|
146 |
+
val = conf["weight_init"]
|
147 |
+
weight = torch.Tensor([val])
|
148 |
+
elif conf["weight"] == "vector":
|
149 |
+
val = conf["weight_init"]
|
150 |
+
weight = val * torch.ones(1, nclasses, 1, 1)
|
151 |
+
|
152 |
+
self.CRF = ConvCRF(
|
153 |
+
shape,
|
154 |
+
nclasses,
|
155 |
+
mode="col",
|
156 |
+
conf=conf,
|
157 |
+
use_gpu=use_gpu,
|
158 |
+
filter_size=conf["filter_size"],
|
159 |
+
norm=conf["norm"],
|
160 |
+
blur=conf["blur"],
|
161 |
+
trainable=conf["trainable"],
|
162 |
+
convcomp=conf["convcomp"],
|
163 |
+
weight=weight,
|
164 |
+
final_softmax=conf["final_softmax"],
|
165 |
+
unary_weight=conf["unary_weight"],
|
166 |
+
pyinn=conf["pyinn"],
|
167 |
+
)
|
168 |
+
|
169 |
+
return
|
170 |
+
|
171 |
+
def forward(self, unary, img, num_iter=5):
|
172 |
+
"""Run a forward pass through ConvCRF.
|
173 |
+
|
174 |
+
Arguments:
|
175 |
+
unary: torch.Tensor with shape [bs, num_classes, height, width].
|
176 |
+
The unary predictions. Logsoftmax is applied to the unaries
|
177 |
+
during inference. When using CNNs don't apply softmax,
|
178 |
+
use unnormalized output (logits) instead.
|
179 |
+
|
180 |
+
img: torch.Tensor with shape [bs, 3, height, width]
|
181 |
+
The input image. Default config assumes image
|
182 |
+
data in [0, 255]. For normalized images adapt
|
183 |
+
`schan`. Try schan = 0.1 for images in [-0.5, 0.5]
|
184 |
+
"""
|
185 |
+
|
186 |
+
conf = self.conf
|
187 |
+
|
188 |
+
bs, c, x, y = img.shape
|
189 |
+
|
190 |
+
pos_feats = self.create_position_feats(sdims=self.pos_sdims, bs=bs)
|
191 |
+
col_feats = self.create_colour_feats(
|
192 |
+
img,
|
193 |
+
sdims=self.col_sdims,
|
194 |
+
schan=self.col_schan,
|
195 |
+
bias=conf["col_feats"]["use_bias"],
|
196 |
+
bs=bs,
|
197 |
+
)
|
198 |
+
|
199 |
+
compats = [self.pos_compat, self.col_compat]
|
200 |
+
|
201 |
+
self.CRF.add_pairwise_energies([pos_feats, col_feats], compats, conf["merge"])
|
202 |
+
|
203 |
+
prediction = self.CRF.inference(unary, num_iter=num_iter)
|
204 |
+
|
205 |
+
self.CRF.clean_filters()
|
206 |
+
return prediction
|
207 |
+
|
208 |
+
def _create_mesh(self, requires_grad=False):
|
209 |
+
hcord_range = [range(s) for s in self.shape]
|
210 |
+
mesh = np.array(np.meshgrid(*hcord_range, indexing="ij"), dtype=np.float32)
|
211 |
+
|
212 |
+
return torch.from_numpy(mesh)
|
213 |
+
|
214 |
+
def create_colour_feats(self, img, schan, sdims=0.0, bias=True, bs=1):
|
215 |
+
norm_img = img * schan
|
216 |
+
|
217 |
+
if bias:
|
218 |
+
norm_mesh = self.create_position_feats(sdims=sdims, bs=bs)
|
219 |
+
feats = torch.cat([norm_mesh, norm_img], dim=1)
|
220 |
+
else:
|
221 |
+
feats = norm_img
|
222 |
+
return feats
|
223 |
+
|
224 |
+
def create_position_feats(self, sdims, bs=1):
|
225 |
+
if type(self.mesh) is Parameter:
|
226 |
+
return torch.stack(bs * [self.mesh * sdims])
|
227 |
+
else:
|
228 |
+
return torch.stack(bs * [Variable(self.mesh) * sdims])
|
229 |
+
|
230 |
+
|
231 |
+
def show_memusage(device=0, name=""):
|
232 |
+
import gpustat
|
233 |
+
|
234 |
+
gc.collect()
|
235 |
+
gpu_stats = gpustat.GPUStatCollection.new_query()
|
236 |
+
item = gpu_stats.jsonify()["gpus"][device]
|
237 |
+
|
238 |
+
logging.info(
|
239 |
+
"{:>5}/{:>5} MB Usage at {}".format(
|
240 |
+
item["memory.used"], item["memory.total"], name
|
241 |
+
)
|
242 |
+
)
|
243 |
+
|
244 |
+
|
245 |
+
def exp_and_normalize(features, dim=0):
|
246 |
+
"""
|
247 |
+
Aka "softmax" in deep learning literature
|
248 |
+
"""
|
249 |
+
normalized = torch.nn.functional.softmax(features, dim=dim)
|
250 |
+
return normalized
|
251 |
+
|
252 |
+
|
253 |
+
def _get_ind(dz):
|
254 |
+
if dz == 0:
|
255 |
+
return 0, 0
|
256 |
+
if dz < 0:
|
257 |
+
return 0, -dz
|
258 |
+
if dz > 0:
|
259 |
+
return dz, 0
|
260 |
+
|
261 |
+
|
262 |
+
def _negative(dz):
|
263 |
+
"""
|
264 |
+
Computes -dz for numpy indexing. Goal is to use as in array[i:-dz].
|
265 |
+
|
266 |
+
However, if dz=0 this indexing does not work.
|
267 |
+
None needs to be used instead.
|
268 |
+
"""
|
269 |
+
if dz == 0:
|
270 |
+
return None
|
271 |
+
else:
|
272 |
+
return -dz
|
273 |
+
|
274 |
+
|
275 |
+
class MessagePassingCol:
|
276 |
+
"""Perform the Message passing of ConvCRFs.
|
277 |
+
|
278 |
+
The main magic happens here.
|
279 |
+
"""
|
280 |
+
|
281 |
+
def __init__(
|
282 |
+
self,
|
283 |
+
feat_list,
|
284 |
+
compat_list,
|
285 |
+
merge,
|
286 |
+
npixels,
|
287 |
+
nclasses,
|
288 |
+
norm="sym",
|
289 |
+
filter_size=5,
|
290 |
+
clip_edges=0,
|
291 |
+
use_gpu=False,
|
292 |
+
blur=1,
|
293 |
+
matmul=False,
|
294 |
+
verbose=False,
|
295 |
+
pyinn=False,
|
296 |
+
):
|
297 |
+
|
298 |
+
if not norm == "sym" and not norm == "none":
|
299 |
+
raise NotImplementedError
|
300 |
+
|
301 |
+
span = filter_size // 2
|
302 |
+
assert filter_size % 2 == 1
|
303 |
+
self.span = span
|
304 |
+
self.filter_size = filter_size
|
305 |
+
self.use_gpu = use_gpu
|
306 |
+
self.verbose = verbose
|
307 |
+
self.blur = blur
|
308 |
+
self.pyinn = pyinn
|
309 |
+
|
310 |
+
self.merge = merge
|
311 |
+
|
312 |
+
self.npixels = npixels
|
313 |
+
|
314 |
+
if not self.blur == 1 and self.blur % 2:
|
315 |
+
raise NotImplementedError
|
316 |
+
|
317 |
+
self.matmul = matmul
|
318 |
+
|
319 |
+
self._gaus_list = []
|
320 |
+
self._norm_list = []
|
321 |
+
|
322 |
+
for feats, compat in zip(feat_list, compat_list):
|
323 |
+
gaussian = self._create_convolutional_filters(feats)
|
324 |
+
if not norm == "none":
|
325 |
+
mynorm = self._get_norm(gaussian)
|
326 |
+
self._norm_list.append(mynorm)
|
327 |
+
else:
|
328 |
+
self._norm_list.append(None)
|
329 |
+
|
330 |
+
gaussian = compat * gaussian
|
331 |
+
self._gaus_list.append(gaussian)
|
332 |
+
|
333 |
+
if merge:
|
334 |
+
self.gaussian = sum(self._gaus_list)
|
335 |
+
if not norm == "none":
|
336 |
+
raise NotImplementedError
|
337 |
+
|
338 |
+
def _get_norm(self, gaus):
|
339 |
+
norm_tensor = torch.ones([1, 1, self.npixels[0], self.npixels[1]])
|
340 |
+
normalization_feats = torch.autograd.Variable(norm_tensor)
|
341 |
+
if self.use_gpu:
|
342 |
+
normalization_feats = normalization_feats.cuda()
|
343 |
+
|
344 |
+
norm_out = self._compute_gaussian(normalization_feats, gaussian=gaus)
|
345 |
+
return 1 / torch.sqrt(norm_out + 1e-20)
|
346 |
+
|
347 |
+
def _create_convolutional_filters(self, features):
|
348 |
+
|
349 |
+
span = self.span
|
350 |
+
|
351 |
+
bs = features.shape[0]
|
352 |
+
|
353 |
+
if self.blur > 1:
|
354 |
+
off_0 = (self.blur - self.npixels[0] % self.blur) % self.blur
|
355 |
+
off_1 = (self.blur - self.npixels[1] % self.blur) % self.blur
|
356 |
+
pad_0 = math.ceil(off_0 / 2)
|
357 |
+
pad_1 = math.ceil(off_1 / 2)
|
358 |
+
if self.blur == 2:
|
359 |
+
assert pad_0 == self.npixels[0] % 2
|
360 |
+
assert pad_1 == self.npixels[1] % 2
|
361 |
+
|
362 |
+
features = torch.nn.functional.avg_pool2d(
|
363 |
+
features,
|
364 |
+
kernel_size=self.blur,
|
365 |
+
padding=(pad_0, pad_1),
|
366 |
+
count_include_pad=False,
|
367 |
+
)
|
368 |
+
|
369 |
+
npixels = [
|
370 |
+
math.ceil(self.npixels[0] / self.blur),
|
371 |
+
math.ceil(self.npixels[1] / self.blur),
|
372 |
+
]
|
373 |
+
assert npixels[0] == features.shape[2]
|
374 |
+
assert npixels[1] == features.shape[3]
|
375 |
+
else:
|
376 |
+
npixels = self.npixels
|
377 |
+
|
378 |
+
gaussian_tensor = features.data.new(
|
379 |
+
bs, self.filter_size, self.filter_size, npixels[0], npixels[1]
|
380 |
+
).fill_(0)
|
381 |
+
|
382 |
+
gaussian = Variable(gaussian_tensor)
|
383 |
+
|
384 |
+
for dx in range(-span, span + 1):
|
385 |
+
for dy in range(-span, span + 1):
|
386 |
+
|
387 |
+
dx1, dx2 = _get_ind(dx)
|
388 |
+
dy1, dy2 = _get_ind(dy)
|
389 |
+
|
390 |
+
feat_t = features[:, :, dx1 : _negative(dx2), dy1 : _negative(dy2)]
|
391 |
+
feat_t2 = features[
|
392 |
+
:, :, dx2 : _negative(dx1), dy2 : _negative(dy1)
|
393 |
+
] # NOQA
|
394 |
+
|
395 |
+
diff = feat_t - feat_t2
|
396 |
+
diff_sq = diff * diff
|
397 |
+
exp_diff = torch.exp(torch.sum(-0.5 * diff_sq, dim=1))
|
398 |
+
|
399 |
+
gaussian[
|
400 |
+
:, dx + span, dy + span, dx2 : _negative(dx1), dy2 : _negative(dy1)
|
401 |
+
] = exp_diff
|
402 |
+
|
403 |
+
return gaussian.view(
|
404 |
+
bs, 1, self.filter_size, self.filter_size, npixels[0], npixels[1]
|
405 |
+
)
|
406 |
+
|
407 |
+
def compute(self, input):
|
408 |
+
if self.merge:
|
409 |
+
pred = self._compute_gaussian(input, self.gaussian)
|
410 |
+
else:
|
411 |
+
assert len(self._gaus_list) == len(self._norm_list)
|
412 |
+
pred = 0
|
413 |
+
for gaus, norm in zip(self._gaus_list, self._norm_list):
|
414 |
+
pred += self._compute_gaussian(input, gaus, norm)
|
415 |
+
|
416 |
+
return pred
|
417 |
+
|
418 |
+
def _compute_gaussian(self, input, gaussian, norm=None):
|
419 |
+
|
420 |
+
if norm is not None:
|
421 |
+
input = input * norm
|
422 |
+
|
423 |
+
shape = input.shape
|
424 |
+
num_channels = shape[1]
|
425 |
+
bs = shape[0]
|
426 |
+
|
427 |
+
if self.blur > 1:
|
428 |
+
off_0 = (self.blur - self.npixels[0] % self.blur) % self.blur
|
429 |
+
off_1 = (self.blur - self.npixels[1] % self.blur) % self.blur
|
430 |
+
pad_0 = int(math.ceil(off_0 / 2))
|
431 |
+
pad_1 = int(math.ceil(off_1 / 2))
|
432 |
+
input = torch.nn.functional.avg_pool2d(
|
433 |
+
input,
|
434 |
+
kernel_size=self.blur,
|
435 |
+
padding=(pad_0, pad_1),
|
436 |
+
count_include_pad=False,
|
437 |
+
)
|
438 |
+
npixels = [
|
439 |
+
math.ceil(self.npixels[0] / self.blur),
|
440 |
+
math.ceil(self.npixels[1] / self.blur),
|
441 |
+
]
|
442 |
+
assert npixels[0] == input.shape[2]
|
443 |
+
assert npixels[1] == input.shape[3]
|
444 |
+
else:
|
445 |
+
npixels = self.npixels
|
446 |
+
|
447 |
+
if self.verbose:
|
448 |
+
show_memusage(name="Init")
|
449 |
+
|
450 |
+
if self.pyinn:
|
451 |
+
input_col = P.im2col(input, self.filter_size, 1, self.span)
|
452 |
+
else:
|
453 |
+
# An alternative implementation of num2col.
|
454 |
+
#
|
455 |
+
# This has implementation uses the torch 0.4 im2col operation.
|
456 |
+
# This implementation was not avaible when we did the experiments
|
457 |
+
# published in our paper. So less "testing" has been done.
|
458 |
+
#
|
459 |
+
# It is around ~20% slower then the pyinn implementation but
|
460 |
+
# easier to use as it removes a dependency.
|
461 |
+
input_unfold = F.unfold(input, self.filter_size, 1, self.span)
|
462 |
+
input_unfold = input_unfold.view(
|
463 |
+
bs,
|
464 |
+
num_channels,
|
465 |
+
self.filter_size,
|
466 |
+
self.filter_size,
|
467 |
+
npixels[0],
|
468 |
+
npixels[1],
|
469 |
+
)
|
470 |
+
input_col = input_unfold
|
471 |
+
|
472 |
+
k_sqr = self.filter_size * self.filter_size
|
473 |
+
|
474 |
+
if self.verbose:
|
475 |
+
show_memusage(name="Im2Col")
|
476 |
+
|
477 |
+
product = gaussian * input_col
|
478 |
+
if self.verbose:
|
479 |
+
show_memusage(name="Product")
|
480 |
+
|
481 |
+
product = product.view([bs, num_channels, k_sqr, npixels[0], npixels[1]])
|
482 |
+
|
483 |
+
message = product.sum(2)
|
484 |
+
|
485 |
+
if self.verbose:
|
486 |
+
show_memusage(name="FinalNorm")
|
487 |
+
|
488 |
+
if self.blur > 1:
|
489 |
+
in_0 = self.npixels[0]
|
490 |
+
in_1 = self.npixels[1]
|
491 |
+
message = message.view(bs, num_channels, npixels[0], npixels[1])
|
492 |
+
with warnings.catch_warnings():
|
493 |
+
warnings.simplefilter("ignore")
|
494 |
+
# Suppress warning regarding corner alignment
|
495 |
+
message = torch.nn.functional.upsample(
|
496 |
+
message, scale_factor=self.blur, mode="bilinear"
|
497 |
+
)
|
498 |
+
|
499 |
+
message = message[:, :, pad_0 : pad_0 + in_0, pad_1 : in_1 + pad_1]
|
500 |
+
message = message.contiguous()
|
501 |
+
|
502 |
+
message = message.view(shape)
|
503 |
+
assert message.shape == shape
|
504 |
+
|
505 |
+
if norm is not None:
|
506 |
+
message = norm * message
|
507 |
+
|
508 |
+
return message
|
509 |
+
|
510 |
+
|
511 |
+
class ConvCRF(nn.Module):
|
512 |
+
"""
|
513 |
+
Implements a generic CRF class.
|
514 |
+
|
515 |
+
This class provides tools to build
|
516 |
+
your own ConvCRF based model.
|
517 |
+
"""
|
518 |
+
|
519 |
+
def __init__(
|
520 |
+
self,
|
521 |
+
npixels,
|
522 |
+
nclasses,
|
523 |
+
conf,
|
524 |
+
mode="conv",
|
525 |
+
filter_size=5,
|
526 |
+
clip_edges=0,
|
527 |
+
blur=1,
|
528 |
+
use_gpu=False,
|
529 |
+
norm="sym",
|
530 |
+
merge=False,
|
531 |
+
verbose=False,
|
532 |
+
trainable=False,
|
533 |
+
convcomp=False,
|
534 |
+
weight=None,
|
535 |
+
final_softmax=True,
|
536 |
+
unary_weight=10,
|
537 |
+
pyinn=False,
|
538 |
+
skip_init_softmax=False,
|
539 |
+
eps=1e-8,
|
540 |
+
):
|
541 |
+
|
542 |
+
super(ConvCRF, self).__init__()
|
543 |
+
self.nclasses = nclasses
|
544 |
+
|
545 |
+
self.filter_size = filter_size
|
546 |
+
self.clip_edges = clip_edges
|
547 |
+
self.use_gpu = use_gpu
|
548 |
+
self.mode = mode
|
549 |
+
self.norm = norm
|
550 |
+
self.merge = merge
|
551 |
+
self.kernel = None
|
552 |
+
self.verbose = verbose
|
553 |
+
self.blur = blur
|
554 |
+
self.final_softmax = final_softmax
|
555 |
+
self.pyinn = pyinn
|
556 |
+
self.skip_init_softmax = skip_init_softmax
|
557 |
+
self.eps = eps
|
558 |
+
|
559 |
+
self.conf = conf
|
560 |
+
|
561 |
+
self.unary_weight = unary_weight
|
562 |
+
|
563 |
+
if self.use_gpu:
|
564 |
+
if not torch.cuda.is_available():
|
565 |
+
logging.error("GPU mode requested but not avaible.")
|
566 |
+
logging.error("Please run using use_gpu=False.")
|
567 |
+
raise ValueError
|
568 |
+
|
569 |
+
self.npixels = npixels
|
570 |
+
|
571 |
+
if type(npixels) is tuple or type(npixels) is list:
|
572 |
+
self.height = npixels[0]
|
573 |
+
self.width = npixels[1]
|
574 |
+
else:
|
575 |
+
self.npixels = npixels
|
576 |
+
|
577 |
+
if trainable:
|
578 |
+
|
579 |
+
def register(name, tensor):
|
580 |
+
self.register_parameter(name, Parameter(tensor))
|
581 |
+
|
582 |
+
else:
|
583 |
+
|
584 |
+
def register(name, tensor):
|
585 |
+
self.register_buffer(name, Variable(tensor))
|
586 |
+
|
587 |
+
if weight is None:
|
588 |
+
self.weight = None
|
589 |
+
else:
|
590 |
+
register("weight", weight)
|
591 |
+
|
592 |
+
if convcomp:
|
593 |
+
self.comp = nn.Conv2d(
|
594 |
+
nclasses, nclasses, kernel_size=1, stride=1, padding=0, bias=False
|
595 |
+
)
|
596 |
+
|
597 |
+
self.comp.weight.data.fill_(0.1 * math.sqrt(2.0 / nclasses))
|
598 |
+
else:
|
599 |
+
self.comp = None
|
600 |
+
|
601 |
+
def clean_filters(self):
|
602 |
+
self.kernel = None
|
603 |
+
|
604 |
+
def add_pairwise_energies(self, feat_list, compat_list, merge):
|
605 |
+
assert len(feat_list) == len(compat_list)
|
606 |
+
|
607 |
+
self.kernel = MessagePassingCol(
|
608 |
+
feat_list=feat_list,
|
609 |
+
compat_list=compat_list,
|
610 |
+
merge=merge,
|
611 |
+
npixels=self.npixels,
|
612 |
+
filter_size=self.filter_size,
|
613 |
+
nclasses=self.nclasses,
|
614 |
+
use_gpu=self.use_gpu,
|
615 |
+
norm=self.norm,
|
616 |
+
verbose=self.verbose,
|
617 |
+
blur=self.blur,
|
618 |
+
pyinn=self.pyinn,
|
619 |
+
)
|
620 |
+
|
621 |
+
def inference(self, unary, num_iter=5):
|
622 |
+
|
623 |
+
if not self.skip_init_softmax:
|
624 |
+
if not self.conf["logsoftmax"]:
|
625 |
+
lg_unary = torch.log(unary)
|
626 |
+
prediction = exp_and_normalize(lg_unary, dim=1)
|
627 |
+
else:
|
628 |
+
lg_unary = nnfun.log_softmax(unary, dim=1, _stacklevel=5)
|
629 |
+
prediction = lg_unary
|
630 |
+
else:
|
631 |
+
unary = unary + self.eps
|
632 |
+
unary = unary.clamp(0, 1)
|
633 |
+
lg_unary = torch.log(unary)
|
634 |
+
prediction = lg_unary
|
635 |
+
|
636 |
+
for i in range(num_iter):
|
637 |
+
message = self.kernel.compute(prediction)
|
638 |
+
|
639 |
+
if self.comp is not None:
|
640 |
+
# message_r = message.view(tuple([1]) + message.shape)
|
641 |
+
comp = self.comp(message)
|
642 |
+
message = message + comp
|
643 |
+
|
644 |
+
if self.weight is None:
|
645 |
+
prediction = lg_unary + message
|
646 |
+
else:
|
647 |
+
prediction = (
|
648 |
+
self.unary_weight - self.weight
|
649 |
+
) * lg_unary + self.weight * message
|
650 |
+
|
651 |
+
if not i == num_iter - 1 or self.final_softmax:
|
652 |
+
if self.conf["softmax"]:
|
653 |
+
prediction = exp_and_normalize(prediction, dim=1)
|
654 |
+
|
655 |
+
return prediction
|
656 |
+
|
657 |
+
def start_inference(self):
|
658 |
+
pass
|
659 |
+
|
660 |
+
def step_inference(self):
|
661 |
+
pass
|
662 |
+
|
663 |
+
|
664 |
+
def get_test_conf():
|
665 |
+
return test_config.copy()
|
666 |
+
|
667 |
+
|
668 |
+
def get_default_conf():
|
669 |
+
return default_conf.copy()
|
utils/crf.py
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python
|
2 |
+
# coding: utf-8
|
3 |
+
#
|
4 |
+
# Author: Kazuto Nakashima
|
5 |
+
# URL: https://kazuto1011.github.io
|
6 |
+
# Date: 09 January 2019
|
7 |
+
|
8 |
+
|
9 |
+
import numpy as np
|
10 |
+
import pydensecrf.densecrf as dcrf
|
11 |
+
import pydensecrf.utils as utils
|
12 |
+
|
13 |
+
|
14 |
+
class DenseCRF(object):
|
15 |
+
def __init__(self, iter_max, pos_w, pos_xy_std, bi_w, bi_xy_std, bi_rgb_std):
|
16 |
+
self.iter_max = iter_max
|
17 |
+
self.pos_w = pos_w
|
18 |
+
self.pos_xy_std = pos_xy_std
|
19 |
+
self.bi_w = bi_w
|
20 |
+
self.bi_xy_std = bi_xy_std
|
21 |
+
self.bi_rgb_std = bi_rgb_std
|
22 |
+
|
23 |
+
def __call__(self, image, probmap):
|
24 |
+
C, H, W = probmap.shape
|
25 |
+
|
26 |
+
U = utils.unary_from_softmax(probmap)
|
27 |
+
U = np.ascontiguousarray(U)
|
28 |
+
|
29 |
+
image = np.ascontiguousarray(image)
|
30 |
+
|
31 |
+
d = dcrf.DenseCRF2D(W, H, C)
|
32 |
+
d.setUnaryEnergy(U)
|
33 |
+
d.addPairwiseGaussian(sxy=self.pos_xy_std, compat=self.pos_w)
|
34 |
+
d.addPairwiseBilateral(
|
35 |
+
sxy=self.bi_xy_std, srgb=self.bi_rgb_std, rgbim=image, compat=self.bi_w
|
36 |
+
)
|
37 |
+
|
38 |
+
Q = d.inference(self.iter_max)
|
39 |
+
Q = np.array(Q).reshape((C, H, W))
|
40 |
+
|
41 |
+
return Q
|
utils/misc.py
ADDED
@@ -0,0 +1,370 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import copy
|
2 |
+
import datetime
|
3 |
+
import json
|
4 |
+
import math
|
5 |
+
import os
|
6 |
+
import random
|
7 |
+
import signal
|
8 |
+
import subprocess
|
9 |
+
import sys
|
10 |
+
import time
|
11 |
+
import warnings
|
12 |
+
from collections import defaultdict
|
13 |
+
from shutil import copy2
|
14 |
+
from typing import Dict
|
15 |
+
|
16 |
+
import numpy as np
|
17 |
+
import prettytable as pt
|
18 |
+
import torch
|
19 |
+
import torch.nn as nn
|
20 |
+
from termcolor import cprint
|
21 |
+
from torch.utils.tensorboard import SummaryWriter
|
22 |
+
|
23 |
+
|
24 |
+
class Logger(object):
|
25 |
+
def __init__(self, filename, stream=sys.stdout):
|
26 |
+
self.terminal = stream
|
27 |
+
self.log = open(filename, "a")
|
28 |
+
|
29 |
+
def write(self, message):
|
30 |
+
self.terminal.write(message)
|
31 |
+
self.log.write(message)
|
32 |
+
|
33 |
+
def flush(self):
|
34 |
+
pass
|
35 |
+
|
36 |
+
|
37 |
+
class AverageMeter(object):
|
38 |
+
"""Computes and stores the average and current value"""
|
39 |
+
|
40 |
+
def __init__(self):
|
41 |
+
self.sum = 0
|
42 |
+
self.avg = 0
|
43 |
+
self.val = 0
|
44 |
+
self.count = 0
|
45 |
+
|
46 |
+
def reset(self):
|
47 |
+
self.sum = 0
|
48 |
+
self.avg = 0
|
49 |
+
self.val = 0
|
50 |
+
self.count = 0
|
51 |
+
|
52 |
+
def update(self, val, n=1):
|
53 |
+
self.val = val
|
54 |
+
self.sum = self.sum + val * n
|
55 |
+
self.count = self.count + n
|
56 |
+
self.avg = self.sum / self.count
|
57 |
+
|
58 |
+
def __str__(self):
|
59 |
+
return f"{self.avg: .5f}"
|
60 |
+
|
61 |
+
|
62 |
+
def get_sha():
|
63 |
+
"""Get git current status"""
|
64 |
+
cwd = os.path.dirname(os.path.abspath(__file__))
|
65 |
+
|
66 |
+
def _run(command):
|
67 |
+
return subprocess.check_output(command, cwd=cwd).decode("ascii").strip()
|
68 |
+
|
69 |
+
sha = "N/A"
|
70 |
+
diff = "clean"
|
71 |
+
branch = "N/A"
|
72 |
+
message = "N/A"
|
73 |
+
try:
|
74 |
+
sha = _run(["git", "rev-parse", "HEAD"])
|
75 |
+
sha = sha[:8]
|
76 |
+
subprocess.check_output(["git", "diff"], cwd=cwd)
|
77 |
+
diff = _run(["git", "diff-index", "HEAD"])
|
78 |
+
diff = "has uncommited changes" if diff else "clean"
|
79 |
+
branch = _run(["git", "rev-parse", "--abbrev-ref", "HEAD"])
|
80 |
+
message = _run(["git", "log", "--pretty=format:'%s'", sha, "-1"]).replace(
|
81 |
+
"'", ""
|
82 |
+
)
|
83 |
+
except Exception:
|
84 |
+
pass
|
85 |
+
|
86 |
+
return {"sha": sha, "status": diff, "branch": branch, "prev_commit": message}
|
87 |
+
|
88 |
+
|
89 |
+
def setup_env(opt):
|
90 |
+
if opt.eval or opt.debug:
|
91 |
+
opt.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
92 |
+
torch.autograd.set_detect_anomaly(True)
|
93 |
+
return None
|
94 |
+
|
95 |
+
dir_name = opt.dir_name
|
96 |
+
save_root_path = opt.save_root_path
|
97 |
+
if not os.path.exists(save_root_path):
|
98 |
+
os.mkdir(save_root_path)
|
99 |
+
|
100 |
+
# deterministic
|
101 |
+
torch.manual_seed(opt.seed)
|
102 |
+
np.random.seed(opt.seed)
|
103 |
+
random.seed(opt.seed)
|
104 |
+
torch.backends.cudnn.deterministic = True
|
105 |
+
torch.backends.cudnn.benchmark = True
|
106 |
+
|
107 |
+
# mkdir subdirectories
|
108 |
+
checkpoint = "checkpoint"
|
109 |
+
if not os.path.exists(os.path.join(save_root_path, dir_name)):
|
110 |
+
os.mkdir(os.path.join(save_root_path, dir_name))
|
111 |
+
os.mkdir(os.path.join(save_root_path, dir_name, checkpoint))
|
112 |
+
|
113 |
+
# save log
|
114 |
+
sys.stdout = Logger(os.path.join(save_root_path, dir_name, "log.log"), sys.stdout)
|
115 |
+
sys.stderr = Logger(os.path.join(save_root_path, dir_name, "error.log"), sys.stderr)
|
116 |
+
|
117 |
+
# save parameters
|
118 |
+
params = copy.deepcopy(vars(opt))
|
119 |
+
params.pop("device")
|
120 |
+
with open(os.path.join(save_root_path, dir_name, "params.json"), "w") as f:
|
121 |
+
json.dump(params, f)
|
122 |
+
|
123 |
+
# print info
|
124 |
+
print(
|
125 |
+
"Running on {}, PyTorch version {}, files will be saved at {}".format(
|
126 |
+
opt.device, torch.__version__, os.path.join(save_root_path, dir_name)
|
127 |
+
)
|
128 |
+
)
|
129 |
+
print("Devices:")
|
130 |
+
for i in range(torch.cuda.device_count()):
|
131 |
+
print(" {}:".format(i), torch.cuda.get_device_name(i))
|
132 |
+
print(f"Git: {get_sha()}.")
|
133 |
+
|
134 |
+
# return tensorboard summarywriter
|
135 |
+
return SummaryWriter("{}/{}/".format(opt.save_root_path, opt.dir_name))
|
136 |
+
|
137 |
+
|
138 |
+
class MetricLogger(object):
|
139 |
+
def __init__(self, delimiter=" ", writer=None, suffix=None):
|
140 |
+
self.meters = defaultdict(AverageMeter)
|
141 |
+
self.delimiter = delimiter
|
142 |
+
self.writer = writer
|
143 |
+
self.suffix = suffix
|
144 |
+
|
145 |
+
def update(self, **kwargs):
|
146 |
+
for k, v in kwargs.items():
|
147 |
+
if isinstance(v, torch.Tensor):
|
148 |
+
v = v.item()
|
149 |
+
assert isinstance(v, (float, int)), f"Unsupport type {type(v)}."
|
150 |
+
self.meters[k].update(v)
|
151 |
+
|
152 |
+
def add_meter(self, name, meter):
|
153 |
+
self.meters[name] = meter
|
154 |
+
|
155 |
+
def get_meters(self):
|
156 |
+
result = {}
|
157 |
+
for k, v in self.meters.items():
|
158 |
+
result[k] = v.avg
|
159 |
+
return result
|
160 |
+
|
161 |
+
def prepend_subprefix(self, subprefix: str):
|
162 |
+
old_keys = list(self.meters.keys())
|
163 |
+
for k in old_keys:
|
164 |
+
self.meters[k.replace("/", f"/{subprefix}")] = self.meters[k]
|
165 |
+
for k in old_keys:
|
166 |
+
del self.meters[k]
|
167 |
+
|
168 |
+
def log_every(self, iterable, print_freq=10, header=""):
|
169 |
+
i = 0
|
170 |
+
start_time = time.time()
|
171 |
+
end = time.time()
|
172 |
+
iter_time = AverageMeter()
|
173 |
+
space_fmt = ":" + str(len(str(len(iterable)))) + "d"
|
174 |
+
log_msg = self.delimiter.join(
|
175 |
+
[
|
176 |
+
header,
|
177 |
+
"[{0" + space_fmt + "}/{1}]",
|
178 |
+
"eta: {eta}",
|
179 |
+
"{meters}",
|
180 |
+
"iter time: {time}s",
|
181 |
+
]
|
182 |
+
)
|
183 |
+
for obj in iterable:
|
184 |
+
yield i, obj
|
185 |
+
iter_time.update(time.time() - end)
|
186 |
+
if (i + 1) % print_freq == 0 or i == len(iterable) - 1:
|
187 |
+
eta_seconds = iter_time.avg * (len(iterable) - i)
|
188 |
+
eta_string = str(datetime.timedelta(seconds=int(eta_seconds)))
|
189 |
+
print(
|
190 |
+
log_msg.format(
|
191 |
+
i + 1,
|
192 |
+
len(iterable),
|
193 |
+
eta=eta_string,
|
194 |
+
meters=str(self),
|
195 |
+
time=str(iter_time),
|
196 |
+
).replace(" ", " ")
|
197 |
+
)
|
198 |
+
i += 1
|
199 |
+
end = time.time()
|
200 |
+
total_time = time.time() - start_time
|
201 |
+
total_time_str = str(datetime.timedelta(seconds=int(total_time)))
|
202 |
+
print(
|
203 |
+
"{} Total time: {} ({:.4f}s / it)".format(
|
204 |
+
header, total_time_str, total_time / len(iterable)
|
205 |
+
)
|
206 |
+
)
|
207 |
+
|
208 |
+
def write_tensorboard(self, step):
|
209 |
+
if self.writer is not None:
|
210 |
+
for k, v in self.meters.items():
|
211 |
+
# if self.suffix:
|
212 |
+
# self.writer.add_scalar(
|
213 |
+
# '{}/{}'.format(k, self.suffix), v.avg, step)
|
214 |
+
# else:
|
215 |
+
self.writer.add_scalar(k, v.avg, step)
|
216 |
+
|
217 |
+
def stat_table(self):
|
218 |
+
tb = pt.PrettyTable(field_names=["Metrics", "Values"])
|
219 |
+
for name, meter in self.meters.items():
|
220 |
+
tb.add_row([name, str(meter)])
|
221 |
+
return tb.get_string()
|
222 |
+
|
223 |
+
def __getattr__(self, attr):
|
224 |
+
if attr in self.meters:
|
225 |
+
return self.meters[attr]
|
226 |
+
if attr in self.__dict__:
|
227 |
+
return self.__dict__[attr]
|
228 |
+
raise AttributeError(
|
229 |
+
"'{}' object has no attribute '{}'".format(type(self).__name__, attr)
|
230 |
+
)
|
231 |
+
|
232 |
+
def __str__(self):
|
233 |
+
loss_str = []
|
234 |
+
for name, meter in self.meters.items():
|
235 |
+
loss_str.append("{}: {}".format(name, str(meter)))
|
236 |
+
return self.delimiter.join(loss_str).replace(" ", " ")
|
237 |
+
|
238 |
+
|
239 |
+
def save_model(path, model: nn.Module, epoch, opt, performance=None):
|
240 |
+
if not opt.debug:
|
241 |
+
try:
|
242 |
+
torch.save(
|
243 |
+
{
|
244 |
+
"model": model.state_dict(),
|
245 |
+
"epoch": epoch,
|
246 |
+
"opt": opt,
|
247 |
+
"performance": performance,
|
248 |
+
},
|
249 |
+
path,
|
250 |
+
)
|
251 |
+
except Exception as e:
|
252 |
+
cprint("Failed to save {} because {}".format(path, str(e)))
|
253 |
+
|
254 |
+
|
255 |
+
def resume_from(model: nn.Module, resume_path: str):
|
256 |
+
checkpoint = torch.load(resume_path, map_location="cpu")
|
257 |
+
state_dict = checkpoint["model"]
|
258 |
+
performance = checkpoint["performance"]
|
259 |
+
try:
|
260 |
+
model.load_state_dict(state_dict)
|
261 |
+
except Exception as e:
|
262 |
+
model.load_state_dict(state_dict, strict=False)
|
263 |
+
cprint("Failed to load full model because {}".format(str(e)), "red")
|
264 |
+
time.sleep(3)
|
265 |
+
print(f"{resume_path} model loaded. It performance is")
|
266 |
+
if performance is not None:
|
267 |
+
for k, v in performance.items():
|
268 |
+
print(f"{k}: {v}")
|
269 |
+
|
270 |
+
|
271 |
+
def update_record(result: Dict, epoch: int, opt, file_name: str = "latest_record"):
|
272 |
+
if not opt.debug:
|
273 |
+
# save txt file
|
274 |
+
tb = pt.PrettyTable(field_names=["Metrics", "Values"])
|
275 |
+
with open(
|
276 |
+
os.path.join(opt.save_root_path, opt.dir_name, f"{file_name}.txt"), "w"
|
277 |
+
) as f:
|
278 |
+
f.write(f"Performance at {epoch}-th epoch:\n\n")
|
279 |
+
for k, v in result.items():
|
280 |
+
tb.add_row([k, "{:.7f}".format(v)])
|
281 |
+
f.write(tb.get_string())
|
282 |
+
|
283 |
+
# save json file
|
284 |
+
result["epoch"] = epoch
|
285 |
+
with open(
|
286 |
+
os.path.join(opt.save_root_path, opt.dir_name, f"{file_name}.json"), "w"
|
287 |
+
) as f:
|
288 |
+
json.dump(result, f)
|
289 |
+
|
290 |
+
|
291 |
+
def pixel_acc(pred, label):
|
292 |
+
"""Compute pixel-level prediction accuracy."""
|
293 |
+
warnings.warn("I am not sure if this implementation is correct.")
|
294 |
+
|
295 |
+
label_size = label.shape[-2:]
|
296 |
+
if pred.shape[-2] != label_size:
|
297 |
+
pred = torch.nn.functional.interpolate(
|
298 |
+
pred, size=label_size, mode="bilinear", align_corners=False
|
299 |
+
)
|
300 |
+
|
301 |
+
pred[torch.where(pred > 0.5)] = 1
|
302 |
+
pred[torch.where(pred <= 0.5)] = 0
|
303 |
+
correct = torch.sum((pred + label) == 1.0)
|
304 |
+
total = torch.numel(pred)
|
305 |
+
return correct / (total + 1e-8)
|
306 |
+
|
307 |
+
|
308 |
+
def calculate_pixel_f1(pd, gt, prefix="", suffix=""):
|
309 |
+
if np.max(pd) == np.max(gt) and np.max(pd) == 0:
|
310 |
+
f1, iou = 1.0, 1.0
|
311 |
+
return f1, 0.0, 0.0
|
312 |
+
seg_inv, gt_inv = np.logical_not(pd), np.logical_not(gt)
|
313 |
+
true_pos = float(np.logical_and(pd, gt).sum())
|
314 |
+
false_pos = np.logical_and(pd, gt_inv).sum()
|
315 |
+
false_neg = np.logical_and(seg_inv, gt).sum()
|
316 |
+
f1 = 2 * true_pos / (2 * true_pos + false_pos + false_neg + 1e-6)
|
317 |
+
precision = true_pos / (true_pos + false_pos + 1e-6)
|
318 |
+
recall = true_pos / (true_pos + false_neg + 1e-6)
|
319 |
+
|
320 |
+
return {
|
321 |
+
f"{prefix}pixel_f1{suffix}": f1,
|
322 |
+
f"{prefix}pixel_prec{suffix}": precision,
|
323 |
+
f"{prefix}pixel_recall{suffix}": recall,
|
324 |
+
}
|
325 |
+
|
326 |
+
|
327 |
+
def calculate_img_score(pd, gt, prefix="", suffix="", eta=1e-6):
|
328 |
+
seg_inv, gt_inv = np.logical_not(pd), np.logical_not(gt)
|
329 |
+
true_pos = float(np.logical_and(pd, gt).sum())
|
330 |
+
false_pos = float(np.logical_and(pd, gt_inv).sum())
|
331 |
+
false_neg = float(np.logical_and(seg_inv, gt).sum())
|
332 |
+
true_neg = float(np.logical_and(seg_inv, gt_inv).sum())
|
333 |
+
acc = (true_pos + true_neg) / (true_pos + true_neg + false_neg + false_pos + eta)
|
334 |
+
sen = true_pos / (true_pos + false_neg + eta)
|
335 |
+
spe = true_neg / (true_neg + false_pos + eta)
|
336 |
+
precision = true_pos / (true_pos + false_pos + eta)
|
337 |
+
recall = true_pos / (true_pos + false_neg + eta)
|
338 |
+
try:
|
339 |
+
f1 = 2 * sen * spe / (sen + spe)
|
340 |
+
except:
|
341 |
+
f1 = -math.inf
|
342 |
+
|
343 |
+
return {
|
344 |
+
f"{prefix}image_acc{suffix}": acc,
|
345 |
+
f"{prefix}image_sen{suffix}": sen,
|
346 |
+
f"{prefix}image_spe{suffix}": spe,
|
347 |
+
f"{prefix}image_f1{suffix}": f1,
|
348 |
+
f"{prefix}image_true_pos{suffix}": true_pos,
|
349 |
+
f"{prefix}image_true_neg{suffix}": true_neg,
|
350 |
+
f"{prefix}image_false_pos{suffix}": false_pos,
|
351 |
+
f"{prefix}image_false_neg{suffix}": false_neg,
|
352 |
+
f"{prefix}image_prec{suffix}": precision,
|
353 |
+
f"{prefix}image_recall{suffix}": recall,
|
354 |
+
}
|
355 |
+
|
356 |
+
|
357 |
+
class timeout:
|
358 |
+
def __init__(self, seconds=1, error_message="Timeout"):
|
359 |
+
self.seconds = seconds
|
360 |
+
self.error_message = error_message
|
361 |
+
|
362 |
+
def handle_timeout(self, signum, frame):
|
363 |
+
raise TimeoutError(self.error_message)
|
364 |
+
|
365 |
+
def __enter__(self):
|
366 |
+
signal.signal(signal.SIGALRM, self.handle_timeout)
|
367 |
+
signal.alarm(self.seconds)
|
368 |
+
|
369 |
+
def __exit__(self, type, value, traceback):
|
370 |
+
signal.alarm(0)
|