Spaces:
Sleeping
Sleeping
Commit ·
1df4279
0
Parent(s):
Initial commit with Git LFS for binaries
Browse files- .gitattributes +3 -0
- .gitignore +8 -0
- README.md +28 -0
- app.py +442 -0
- configs/_base_/datasets/coco_detection.py +49 -0
- configs/_base_/datasets/coco_instance.py +49 -0
- configs/_base_/datasets/coco_instance_semantic.py +54 -0
- configs/_base_/default_runtime.py +16 -0
- configs/_base_/models/mask_rcnn_r50_fpn.py +120 -0
- configs/_base_/schedules/schedule_1x.py +11 -0
- configs/_base_/schedules/schedule_20e.py +11 -0
- configs/_base_/schedules/schedule_2x.py +11 -0
- configs/dsb/mask_rcnn_geovig_m_fpn_1x_dsb.py +102 -0
- configs/dsb/mask_rcnn_mobilevig_m_fpn_1x_dsb.py +74 -0
- configs/dsb/mask_rcnn_resnet50_fpn_1x_dsb.py +74 -0
- configs/dsb/mask_rcnn_swin_t_fpn_1x_dsb.py +93 -0
- configs/kvasir/mask_rcnn_fastvit_sa12_fpn_1x_kvasir.py +82 -0
- configs/kvasir/mask_rcnn_geovig_m_fpn_1x_kvasir.py +85 -0
- configs/kvasir/mask_rcnn_mobilevig_m_fpn_1x_kvasir.py +66 -0
- configs/kvasir/mask_rcnn_resnet50_fpn_1x_kvasir.py +72 -0
- configs/kvasir/mask_rcnn_swin_t_fpn_1x_kvasir.py +92 -0
- configs/mask_rcnn_geovig_b_fpn_1x_coco.py +32 -0
- configs/mask_rcnn_geovig_m_fpn_1x_coco.py +28 -0
- configs/mask_rcnn_mobilevig_b_fpn_1x_coco.py +24 -0
- configs/mask_rcnn_mobilevig_m_fpn_1x_coco.py +25 -0
- configs/mask_rcnn_mobilevigv2_b_fpn_1x_coco.py +24 -0
- configs/mask_rcnn_mobilevigv2_m_fpn_1x_coco.py +25 -0
- configs/s +324 -0
- examples/000000032081.jpg +3 -0
- examples/000000045472.jpg +3 -0
- examples/0c2550a23b8a0f29a7575de8c61690d3c31bc897dd5ba66caec201d201a278c2.png +3 -0
- examples/cat.png +3 -0
- examples/cju5y84q3mdv50817eyp82xf3.jpg +3 -0
- examples/n03394916_14162.JPEG +3 -0
- examples/n03417042_2960.JPEG +3 -0
- geovig.py +560 -0
- geovig_backbone.py +667 -0
- geovig_det_backbone.py +676 -0
- imagenet_labels.json +1000 -0
- pre_run.sh +6 -0
- requirements.txt +15 -0
.gitattributes
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.jpg filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.JPEG filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/
|
| 2 |
+
*.py[cod]
|
| 3 |
+
*.class
|
| 4 |
+
.DS_Store
|
| 5 |
+
checkpoints/
|
| 6 |
+
=4.0.0
|
| 7 |
+
backbone_keys.txt
|
| 8 |
+
keys.txt
|
README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: GeoViG — Geometry-Aware Graph Vision
|
| 3 |
+
emoji: 🔷
|
| 4 |
+
colorFrom: indigo
|
| 5 |
+
colorTo: blue
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: "4.44.0"
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pre_load_timeout: 300
|
| 10 |
+
pinned: true
|
| 11 |
+
license: apache-2.0
|
| 12 |
+
short_description: ImageNet + detection + medical segmentation with GeoViG
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
# GeoViG Gradio Demo
|
| 16 |
+
|
| 17 |
+
Interactive demo for **GeoViG** — a lightweight hybrid CNN-GNN architecture featuring **SpreadEdgePool** and **GraphMRConv**.
|
| 18 |
+
|
| 19 |
+
### 🌟 Features
|
| 20 |
+
- **🖼️ Image Classification**: ImageNet-1K predictions with Ti/S/M/B variants.
|
| 21 |
+
- **📦 Object Detection**: Mask R-CNN outputs for COCO objects.
|
| 22 |
+
- **🏥 Medical Segmentation**: Polyp (Kvasir-SEG) and Nuclei (DSB 2018) segmentation.
|
| 23 |
+
|
| 24 |
+
### 🛠️ Local Setup
|
| 25 |
+
To run this locally:
|
| 26 |
+
1. `pip install -r requirements.txt`
|
| 27 |
+
2. `sh pre_run.sh` (to install MMCV/MMDet)
|
| 28 |
+
3. `python app.py`
|
app.py
ADDED
|
@@ -0,0 +1,442 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
GeoViG Multi-Task Gradio Demo
|
| 3 |
+
Tabs:
|
| 4 |
+
1. Image Classification (ImageNet-1K) — Ti / S / M / B
|
| 5 |
+
2. Object Detection & Segmentation — GeoViG-M on COCO 2017
|
| 6 |
+
3. Medical Segmentation — GeoViG-M on Kvasir-SEG / DSB 2018
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import sys, os, json, urllib.request
|
| 10 |
+
import torch
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from torchvision import transforms
|
| 13 |
+
from PIL import Image, ImageDraw
|
| 14 |
+
from huggingface_hub import hf_hub_download
|
| 15 |
+
import numpy as np
|
| 16 |
+
import gradio as gr
|
| 17 |
+
|
| 18 |
+
# Detect Gradio API version — theme/css moved to launch() in v6
|
| 19 |
+
_GR_MAJOR = int(gr.__version__.split(".")[0])
|
| 20 |
+
_BLOCKS_KWARGS = {} if _GR_MAJOR >= 6 else {
|
| 21 |
+
"theme": gr.themes.Soft(primary_hue="indigo"),
|
| 22 |
+
"css": "footer { display: none !important; }"
|
| 23 |
+
}
|
| 24 |
+
_LAUNCH_KWARGS = {
|
| 25 |
+
"theme": gr.themes.Soft(primary_hue="indigo"),
|
| 26 |
+
"css": "footer { display: none !important; }"
|
| 27 |
+
} if _GR_MAJOR >= 6 else {}
|
| 28 |
+
|
| 29 |
+
from geovig import geovig_ti, geovig_s, geovig_m, geovig_b
|
| 30 |
+
|
| 31 |
+
# ── Paths ──────────────────────────────────────────────────────────────────────
|
| 32 |
+
BASE = "."
|
| 33 |
+
REPO_ID = "OmarAlasqa/GeoViG"
|
| 34 |
+
CKPT_FILES = {
|
| 35 |
+
"GeoViG-Ti": "pth/geovig_ti_5e4_8G_300_75_22/checkpoint.pth",
|
| 36 |
+
"GeoViG-S": "pth/geovig_s_5e4_8G_300_77_48/checkpoint.pth",
|
| 37 |
+
"GeoViG-M": "pth/geovig_m_5e4_8G_300_80_70/checkpoint.pth",
|
| 38 |
+
"GeoViG-B": "pth/geovig_b_5e4_8G_300_82_38/checkpoint.pth",
|
| 39 |
+
"det_m": "coco_det_seg_pth/geovig_m_det_seg/epoch_12.pth",
|
| 40 |
+
"det_b": "coco_det_seg_pth/geovig_b_det_seg/epoch_12.pth",
|
| 41 |
+
"kvasir_m": "medical/kvasir_geovig_m/checkpoint.pth",
|
| 42 |
+
"dsb_m": "medical/dsb_geovig_m/checkpoint.pth"
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
def get_ckpt_path(key):
|
| 46 |
+
return hf_hub_download(repo_id=REPO_ID, filename=CKPT_FILES[key])
|
| 47 |
+
|
| 48 |
+
MMDET_DIR = "."
|
| 49 |
+
|
| 50 |
+
# ── ImageNet labels ────────────────────────────────────────────────────────────
|
| 51 |
+
LABELS_FILE = "imagenet_labels.json"
|
| 52 |
+
if not os.path.exists(LABELS_FILE):
|
| 53 |
+
urllib.request.urlretrieve(
|
| 54 |
+
"https://raw.githubusercontent.com/anishathalye/imagenet-simple-labels/master/imagenet-simple-labels.json",
|
| 55 |
+
LABELS_FILE)
|
| 56 |
+
with open(LABELS_FILE) as f:
|
| 57 |
+
IMAGENET_LABELS = json.load(f)
|
| 58 |
+
|
| 59 |
+
# ── MMDetection availability ───────────────────────────────────────────────────
|
| 60 |
+
try:
|
| 61 |
+
sys.path.insert(0, MMDET_DIR)
|
| 62 |
+
import mmcv
|
| 63 |
+
from mmdet.apis import init_detector, inference_detector
|
| 64 |
+
import mmdet.models # noqa – registers all standard detectors
|
| 65 |
+
import geovig_det_backbone # noqa – local copy: registers geovig_m_feat / geovig_b_feat
|
| 66 |
+
MMDET_OK = True
|
| 67 |
+
MMDET_ERROR = ""
|
| 68 |
+
except Exception as e:
|
| 69 |
+
MMDET_OK = False
|
| 70 |
+
MMDET_ERROR = str(e)
|
| 71 |
+
|
| 72 |
+
# ── Pre-processing ─────────────────────────────────────────────────────────────
|
| 73 |
+
IMAGENET_TF = transforms.Compose([
|
| 74 |
+
transforms.Resize(256, interpolation=transforms.InterpolationMode.BICUBIC),
|
| 75 |
+
transforms.CenterCrop(224),
|
| 76 |
+
transforms.ToTensor(),
|
| 77 |
+
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
| 78 |
+
])
|
| 79 |
+
|
| 80 |
+
MEDICAL_TF = transforms.Compose([
|
| 81 |
+
transforms.Resize((224, 224)),
|
| 82 |
+
transforms.ToTensor(),
|
| 83 |
+
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
| 84 |
+
])
|
| 85 |
+
|
| 86 |
+
# ── Model cache ────────────────────────────────────────────────────────────────
|
| 87 |
+
MODEL_CACHE = {}
|
| 88 |
+
|
| 89 |
+
BUILDERS = {
|
| 90 |
+
"GeoViG-Ti": geovig_ti,
|
| 91 |
+
"GeoViG-S": geovig_s,
|
| 92 |
+
"GeoViG-M": geovig_m,
|
| 93 |
+
"GeoViG-B": geovig_b,
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
def _load_cls_model(variant: str):
|
| 97 |
+
if variant in MODEL_CACHE:
|
| 98 |
+
return MODEL_CACHE[variant]
|
| 99 |
+
model = BUILDERS[variant]()
|
| 100 |
+
ckpt_path = get_ckpt_path(variant)
|
| 101 |
+
try:
|
| 102 |
+
sd = torch.load(ckpt_path, map_location="cpu", weights_only=False)
|
| 103 |
+
except TypeError:
|
| 104 |
+
sd = torch.load(ckpt_path, map_location="cpu") # PyTorch < 1.13
|
| 105 |
+
sd = sd.get("model", sd.get("state_dict", sd))
|
| 106 |
+
model.load_state_dict(sd, strict=False)
|
| 107 |
+
model.eval()
|
| 108 |
+
MODEL_CACHE[variant] = model
|
| 109 |
+
return model
|
| 110 |
+
|
| 111 |
+
# ── TAB 1 — Classification ─────────────────────────────────────────────────────
|
| 112 |
+
def classify(image: Image.Image, variant: str, top_k: int):
|
| 113 |
+
if image is None:
|
| 114 |
+
return {}
|
| 115 |
+
model = _load_cls_model(variant)
|
| 116 |
+
x = IMAGENET_TF(image.convert("RGB")).unsqueeze(0)
|
| 117 |
+
with torch.no_grad():
|
| 118 |
+
probs = F.softmax(model(x), dim=-1)[0]
|
| 119 |
+
top_probs, top_idxs = probs.topk(int(top_k))
|
| 120 |
+
return {IMAGENET_LABELS[i.item()]: float(p) for p, i in zip(top_probs, top_idxs)}
|
| 121 |
+
|
| 122 |
+
# ── TAB 2 — Detection (COCO) ──────────────────────────────────────────────────
|
| 123 |
+
COCO_NAMES = [
|
| 124 |
+
"person","bicycle","car","motorcycle","airplane","bus","train","truck","boat",
|
| 125 |
+
"traffic light","fire hydrant","stop sign","parking meter","bench","bird","cat",
|
| 126 |
+
"dog","horse","sheep","cow","elephant","bear","zebra","giraffe","backpack",
|
| 127 |
+
"umbrella","handbag","tie","suitcase","frisbee","skis","snowboard","sports ball",
|
| 128 |
+
"kite","baseball bat","baseball glove","skateboard","surfboard","tennis racket",
|
| 129 |
+
"bottle","wine glass","cup","fork","knife","spoon","bowl","banana","apple",
|
| 130 |
+
"sandwich","orange","broccoli","carrot","hot dog","pizza","donut","cake","chair",
|
| 131 |
+
"couch","potted plant","bed","dining table","toilet","tv","laptop","mouse",
|
| 132 |
+
"remote","keyboard","cell phone","microwave","oven","toaster","sink","refrigerator",
|
| 133 |
+
"book","clock","vase","scissors","teddy bear","hair drier","toothbrush"
|
| 134 |
+
]
|
| 135 |
+
|
| 136 |
+
def _draw_detections(image: Image.Image, result, score_thr=0.3):
|
| 137 |
+
"""Draw bboxes and masks from MMDet result onto PIL image."""
|
| 138 |
+
img_draw = image.convert("RGBA")
|
| 139 |
+
draw = ImageDraw.Draw(img_draw)
|
| 140 |
+
|
| 141 |
+
# For the separate segmentation image
|
| 142 |
+
seg_img_draw = image.convert("RGBA")
|
| 143 |
+
seg_draw = ImageDraw.Draw(seg_img_draw)
|
| 144 |
+
seg_np_img = np.array(image.convert("RGB"))
|
| 145 |
+
|
| 146 |
+
if isinstance(result, tuple):
|
| 147 |
+
bbox_result, segm_result = result
|
| 148 |
+
else:
|
| 149 |
+
bbox_result, segm_result = result, None
|
| 150 |
+
|
| 151 |
+
for cls_id, bboxes in enumerate(bbox_result):
|
| 152 |
+
color = tuple(np.random.randint(100, 255, 3).tolist())
|
| 153 |
+
for i, bbox in enumerate(bboxes):
|
| 154 |
+
if bbox[4] < score_thr:
|
| 155 |
+
continue
|
| 156 |
+
x1, y1, x2, y2, score = bbox
|
| 157 |
+
draw.rectangle([x1, y1, x2, y2], outline=color, width=4)
|
| 158 |
+
label = f"{COCO_NAMES[cls_id]} {score:.2f}"
|
| 159 |
+
# Draw text with a small black outline for readability
|
| 160 |
+
for offset in [(-1,-1), (1,-1), (-1,1), (1,1)]:
|
| 161 |
+
draw.text((x1 + 2 + offset[0], y1 + 2 + offset[1]), label, fill=(0, 0, 0))
|
| 162 |
+
draw.text((x1 + 2, y1 + 2), label, fill=(255, 255, 0))
|
| 163 |
+
|
| 164 |
+
# Draw mask if available
|
| 165 |
+
if segm_result is not None and len(segm_result) > cls_id and segm_result[cls_id] is not None:
|
| 166 |
+
try:
|
| 167 |
+
mask = segm_result[cls_id][i]
|
| 168 |
+
if isinstance(mask, dict):
|
| 169 |
+
import pycocotools.mask as mask_util
|
| 170 |
+
mask = mask_util.decode(mask)
|
| 171 |
+
|
| 172 |
+
# Ensure mask is boolean and same shape
|
| 173 |
+
mask_bool = mask.astype(bool)
|
| 174 |
+
# Alpha blending ONLY on the masked pixels
|
| 175 |
+
# Background (1 - alpha) * image + alpha * color
|
| 176 |
+
alpha = 0.5
|
| 177 |
+
img_pixels = seg_np_img[mask_bool].astype(float)
|
| 178 |
+
color_pixels = np.array(color, dtype=float)
|
| 179 |
+
blended = (img_pixels * (1 - alpha) + color_pixels * alpha).astype(np.uint8)
|
| 180 |
+
seg_np_img[mask_bool] = blended
|
| 181 |
+
except Exception:
|
| 182 |
+
pass
|
| 183 |
+
|
| 184 |
+
det_result = img_draw.convert("RGB")
|
| 185 |
+
seg_result = Image.fromarray(seg_np_img)
|
| 186 |
+
return det_result, seg_result
|
| 187 |
+
|
| 188 |
+
DET_MODEL_CACHE = {}
|
| 189 |
+
|
| 190 |
+
def detect(image: Image.Image, model_size: str, score_thr: float):
|
| 191 |
+
if image is None:
|
| 192 |
+
return None, None, "⚠️ Please upload an image."
|
| 193 |
+
if not MMDET_OK:
|
| 194 |
+
return None, None, f"⚠️ MMDetection not available.\nInstall: `pip install mmcv-full==1.7.2 mmdet==2.28.0`\nError: {MMDET_ERROR}"
|
| 195 |
+
|
| 196 |
+
key = f"det_{model_size.lower()}"
|
| 197 |
+
if key not in DET_MODEL_CACHE:
|
| 198 |
+
cfg_map = {
|
| 199 |
+
"M": f"{MMDET_DIR}/configs/mask_rcnn_geovig_m_fpn_1x_coco.py",
|
| 200 |
+
"B": f"{MMDET_DIR}/configs/mask_rcnn_geovig_b_fpn_1x_coco.py",
|
| 201 |
+
}
|
| 202 |
+
ckpt_path = get_ckpt_path(f"det_{model_size.lower()}")
|
| 203 |
+
try:
|
| 204 |
+
det_model = init_detector(cfg_map[model_size], ckpt_path, device="cpu")
|
| 205 |
+
DET_MODEL_CACHE[key] = det_model
|
| 206 |
+
except Exception as e:
|
| 207 |
+
return None, None, f"⚠️ Failed to load detection model:\n{e}"
|
| 208 |
+
|
| 209 |
+
det_model = DET_MODEL_CACHE[key]
|
| 210 |
+
import cv2
|
| 211 |
+
cv_img = cv2.cvtColor(np.array(image.convert("RGB")), cv2.COLOR_RGB2BGR)
|
| 212 |
+
try:
|
| 213 |
+
result = inference_detector(det_model, cv_img)
|
| 214 |
+
bbox_result = result[0] if isinstance(result, tuple) else result
|
| 215 |
+
max_score = max([b[:, 4].max() if b.size > 0 else 0 for b in bbox_result])
|
| 216 |
+
print(f"DEBUG [{model_size} COCO]: Max Score = {max_score:.4f}")
|
| 217 |
+
|
| 218 |
+
det_img, seg_img = _draw_detections(image, result, score_thr=score_thr)
|
| 219 |
+
|
| 220 |
+
# Count detections
|
| 221 |
+
count = sum(len([b for b in cls_bboxes if b[4] >= score_thr]) for cls_bboxes in result[0])
|
| 222 |
+
status = f"✅ GeoViG-{model_size} detection complete. Found {count} objects."
|
| 223 |
+
if count == 0:
|
| 224 |
+
status += " (Try lowering score threshold)"
|
| 225 |
+
|
| 226 |
+
return det_img, seg_img, status
|
| 227 |
+
except Exception as e:
|
| 228 |
+
return None, None, f"⚠️ Inference error:\n{e}"
|
| 229 |
+
|
| 230 |
+
# ── TAB 3 — Medical Segmentation ──────────────────────────────────────────────
|
| 231 |
+
MED_MODEL_CACHE = {}
|
| 232 |
+
|
| 233 |
+
def _overlay_mask(image: Image.Image, mask: np.ndarray, color=(0, 200, 100), alpha=0.7):
|
| 234 |
+
"""Overlay a binary mask on the PIL image."""
|
| 235 |
+
img_arr = np.array(image.convert("RGB")).astype(float)
|
| 236 |
+
overlay = img_arr.copy()
|
| 237 |
+
overlay[mask > 0.5] = np.array(color, dtype=float)
|
| 238 |
+
blended = (img_arr * (1 - alpha) + overlay * alpha).astype(np.uint8)
|
| 239 |
+
return Image.fromarray(blended)
|
| 240 |
+
|
| 241 |
+
def segment_medical(image: Image.Image, dataset: str, score_thr: float):
|
| 242 |
+
if image is None:
|
| 243 |
+
return None, "⚠️ Please upload an image."
|
| 244 |
+
if not MMDET_OK:
|
| 245 |
+
return None, f"⚠️ MMDetection not available.\nInstall: `pip install mmcv-full==1.7.2 mmdet==2.28.0`\nError: {MMDET_ERROR}"
|
| 246 |
+
|
| 247 |
+
key = f"med_{dataset.lower().replace(' ', '_')}"
|
| 248 |
+
if key not in MED_MODEL_CACHE:
|
| 249 |
+
cfg_map = {
|
| 250 |
+
"Kvasir-SEG": f"{MMDET_DIR}/configs/kvasir/mask_rcnn_geovig_m_fpn_1x_kvasir.py",
|
| 251 |
+
"DSB 2018": f"{MMDET_DIR}/configs/dsb/mask_rcnn_geovig_m_fpn_1x_dsb.py",
|
| 252 |
+
}
|
| 253 |
+
v_key = "kvasir_m" if dataset == "Kvasir-SEG" else "dsb_m"
|
| 254 |
+
ckpt_path = get_ckpt_path(v_key)
|
| 255 |
+
try:
|
| 256 |
+
med_model = init_detector(cfg_map[dataset], ckpt_path, device="cpu")
|
| 257 |
+
MED_MODEL_CACHE[key] = med_model
|
| 258 |
+
except Exception as e:
|
| 259 |
+
return None, f"⚠️ Failed to load model for {dataset}:\n{e}"
|
| 260 |
+
|
| 261 |
+
med_model = MED_MODEL_CACHE[key]
|
| 262 |
+
import cv2
|
| 263 |
+
cv_img = cv2.cvtColor(np.array(image.convert("RGB")), cv2.COLOR_RGB2BGR)
|
| 264 |
+
try:
|
| 265 |
+
result = inference_detector(med_model, cv_img)
|
| 266 |
+
bbox_result = result[0] if isinstance(result, tuple) else result
|
| 267 |
+
segm_result = result[1] if isinstance(result, tuple) else None
|
| 268 |
+
|
| 269 |
+
max_score = max([b[:, 4].max() if b.size > 0 else 0 for b in bbox_result])
|
| 270 |
+
print(f"DEBUG [{dataset}]: Max Score = {max_score:.4f}")
|
| 271 |
+
|
| 272 |
+
# Merge all masks above threshold
|
| 273 |
+
combined_mask = np.zeros(cv_img.shape[:2], dtype=np.float32)
|
| 274 |
+
count = 0
|
| 275 |
+
if segm_result:
|
| 276 |
+
for cls_id, cls_masks in enumerate(segm_result):
|
| 277 |
+
bboxes = bbox_result[cls_id]
|
| 278 |
+
for i, mask in enumerate(cls_masks):
|
| 279 |
+
if bboxes[i, 4] < score_thr:
|
| 280 |
+
continue
|
| 281 |
+
if isinstance(mask, dict):
|
| 282 |
+
import pycocotools.mask as mask_util
|
| 283 |
+
mask = mask_util.decode(mask)
|
| 284 |
+
combined_mask = np.maximum(combined_mask, mask.astype(np.float32))
|
| 285 |
+
count += 1
|
| 286 |
+
|
| 287 |
+
if count == 0:
|
| 288 |
+
return image, f"⚠️ No objects detected above score {score_thr}."
|
| 289 |
+
|
| 290 |
+
color = (0, 200, 100) if dataset == "Kvasir-SEG" else (100, 150, 255)
|
| 291 |
+
out_img = _overlay_mask(image, combined_mask, color=color)
|
| 292 |
+
|
| 293 |
+
# Now draw boxes on top of the masked image
|
| 294 |
+
draw = ImageDraw.Draw(out_img)
|
| 295 |
+
if bbox_result:
|
| 296 |
+
for cls_bboxes in bbox_result:
|
| 297 |
+
for bbox in cls_bboxes:
|
| 298 |
+
if bbox[4] >= score_thr:
|
| 299 |
+
x1, y1, x2, y2, score = bbox
|
| 300 |
+
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
|
| 301 |
+
|
| 302 |
+
return out_img, f"✅ GeoViG-M medical segmentation complete. Found {count} instances."
|
| 303 |
+
except Exception as e:
|
| 304 |
+
return None, f"⚠️ Medical inference error:\n{e}"
|
| 305 |
+
|
| 306 |
+
# ── Gradio Layout ──────────────────────────────────────────────────────────────
|
| 307 |
+
with gr.Blocks(title="GeoViG Demo", **_BLOCKS_KWARGS) as demo:
|
| 308 |
+
|
| 309 |
+
gr.HTML("""
|
| 310 |
+
<div style="text-align:center; padding: 1rem 0 0.5rem">
|
| 311 |
+
<h1 style="font-size:2rem; font-weight:700; margin:0">🔷 GeoViG</h1>
|
| 312 |
+
<p style="color:#6b7280; margin:0.3rem 0 0">
|
| 313 |
+
Geometry-Aware Graph Reasoning for Mobile Vision |
|
| 314 |
+
<a href="https://huggingface.co/OmarAlasqa/GeoViG" target="_blank">Model Card</a>
|
| 315 |
+
</p>
|
| 316 |
+
</div>
|
| 317 |
+
""")
|
| 318 |
+
|
| 319 |
+
with gr.Tabs():
|
| 320 |
+
|
| 321 |
+
# ── Tab 1: Classification ──────────────────────────────────────────────
|
| 322 |
+
with gr.TabItem("🖼️ Image Classification"):
|
| 323 |
+
gr.Markdown("**ImageNet-1K Classification** — Upload any image and get top-K predictions.")
|
| 324 |
+
with gr.Row():
|
| 325 |
+
with gr.Column(scale=1):
|
| 326 |
+
cls_image = gr.Image(type="pil", label="Input Image", height=280)
|
| 327 |
+
cls_variant = gr.Radio(
|
| 328 |
+
choices=["GeoViG-Ti", "GeoViG-S", "GeoViG-M", "GeoViG-B"],
|
| 329 |
+
value="GeoViG-Ti",
|
| 330 |
+
label="Model",
|
| 331 |
+
info="Ti 75.2% · S 77.5% · M 80.7% · B 82.4% (ImageNet Top-1)"
|
| 332 |
+
)
|
| 333 |
+
cls_topk = gr.Slider(1, 10, value=5, step=1, label="Top-K")
|
| 334 |
+
cls_btn = gr.Button("🚀 Classify", variant="primary")
|
| 335 |
+
with gr.Column(scale=1):
|
| 336 |
+
cls_output = gr.Label(label="Predictions", num_top_classes=10)
|
| 337 |
+
|
| 338 |
+
cls_btn.click(fn=classify, inputs=[cls_image, cls_variant, cls_topk], outputs=cls_output)
|
| 339 |
+
cls_image.change(fn=classify, inputs=[cls_image, cls_variant, cls_topk], outputs=cls_output)
|
| 340 |
+
|
| 341 |
+
gr.Examples(
|
| 342 |
+
examples=[
|
| 343 |
+
["examples/n03394916_14162.JPEG", "GeoViG-Ti", 5],
|
| 344 |
+
["examples/n03417042_2960.JPEG", "GeoViG-M", 5],
|
| 345 |
+
],
|
| 346 |
+
inputs=[cls_image, cls_variant, cls_topk],
|
| 347 |
+
outputs=cls_output,
|
| 348 |
+
fn=classify,
|
| 349 |
+
cache_examples=False,
|
| 350 |
+
)
|
| 351 |
+
|
| 352 |
+
gr.Markdown("""
|
| 353 |
+
| Model | Params | MACs | Top-1 | GPU (iPhone 13) |
|
| 354 |
+
|:---|:---:|:---:|:---:|:---:|
|
| 355 |
+
| GeoViG-Ti | 3.5M | 0.9G | 75.2% | 10.1ms |
|
| 356 |
+
| GeoViG-S | 5.0M | 1.2G | 77.5% | 12.2ms |
|
| 357 |
+
| GeoViG-M | 10.3M | 2.2G | 80.7% | 14.4ms |
|
| 358 |
+
| GeoViG-B | 19.7M | 4.5G | 82.4% | 29.0ms |
|
| 359 |
+
""")
|
| 360 |
+
|
| 361 |
+
# ── Tab 2: Object Detection ────────────────────────────────────────────
|
| 362 |
+
with gr.TabItem("📦 Object Detection (COCO)"):
|
| 363 |
+
mmdet_status = "✅ MMDetection available" if MMDET_OK else "⚠️ MMDetection not installed — install `mmcv-full==1.7.2` and `mmdet==2.28.0` to enable this tab"
|
| 364 |
+
gr.Markdown(f"**MS COCO Object Detection & Instance Segmentation** (Mask R-CNN backbone)\n\n_{mmdet_status}_")
|
| 365 |
+
with gr.Row():
|
| 366 |
+
with gr.Column(scale=1):
|
| 367 |
+
det_image = gr.Image(type="pil", label="Input Image", height=280)
|
| 368 |
+
det_size = gr.Radio(choices=["M", "B"], value="M",
|
| 369 |
+
label="Backbone",
|
| 370 |
+
info="M: 40.7 Box AP · B: 42.5 Box AP")
|
| 371 |
+
det_thr = gr.Slider(0.1, 0.9, value=0.7, step=0.05, label="Score threshold")
|
| 372 |
+
det_btn = gr.Button("🔍 Detect & Segment", variant="primary", interactive=MMDET_OK)
|
| 373 |
+
det_output_txt = gr.Textbox(label="Status", lines=2)
|
| 374 |
+
with gr.Column(scale=2):
|
| 375 |
+
with gr.Row():
|
| 376 |
+
det_output_img = gr.Image(label="Detection (Boxes)", type="pil")
|
| 377 |
+
seg_output_img = gr.Image(label="Instance Segmentation (Masks)", type="pil")
|
| 378 |
+
|
| 379 |
+
det_btn.click(fn=detect, inputs=[det_image, det_size, det_thr],
|
| 380 |
+
outputs=[det_output_img, seg_output_img, det_output_txt])
|
| 381 |
+
|
| 382 |
+
gr.Examples(
|
| 383 |
+
examples=[
|
| 384 |
+
["examples/000000032081.jpg", "M", 0.7],
|
| 385 |
+
["examples/000000045472.jpg", "M", 0.7],
|
| 386 |
+
["examples/cat.png", "B", 0.7],
|
| 387 |
+
],
|
| 388 |
+
inputs=[det_image, det_size, det_thr],
|
| 389 |
+
outputs=[det_output_img, seg_output_img, det_output_txt],
|
| 390 |
+
fn=detect,
|
| 391 |
+
cache_examples=False,
|
| 392 |
+
)
|
| 393 |
+
|
| 394 |
+
gr.Markdown("""
|
| 395 |
+
| Backbone | Params | Box AP | Mask AP |
|
| 396 |
+
|:---|:---:|:---:|:---:|
|
| 397 |
+
| GeoViG-M | 10.3M | 40.7 | 37.7 |
|
| 398 |
+
| GeoViG-B | 19.7M | 42.5 | 38.9 |
|
| 399 |
+
""")
|
| 400 |
+
|
| 401 |
+
# ── Tab 3: Medical Segmentation ────────────────────────────────────────
|
| 402 |
+
with gr.TabItem("🏥 Medical Segmentation"):
|
| 403 |
+
gr.Markdown(f"**Medical Image Segmentation** — GeoViG-M encoder · Mask R-CNN decoder\n\n_{mmdet_status}_")
|
| 404 |
+
with gr.Row():
|
| 405 |
+
with gr.Column(scale=1):
|
| 406 |
+
med_image = gr.Image(type="pil", label="Medical Image", height=280)
|
| 407 |
+
med_dataset = gr.Radio(
|
| 408 |
+
choices=["Kvasir-SEG", "DSB 2018"],
|
| 409 |
+
value="Kvasir-SEG",
|
| 410 |
+
label="Dataset / Task",
|
| 411 |
+
info="Kvasir: Polyp segmentation (Dice 0.945, HD 12.94) · DSB: Nuclei (Dice 0.908)"
|
| 412 |
+
)
|
| 413 |
+
med_thr = gr.Slider(0.1, 0.9, value=0.7, step=0.05, label="Score threshold")
|
| 414 |
+
med_btn = gr.Button("🔬 Segment", variant="primary", interactive=MMDET_OK)
|
| 415 |
+
with gr.Column(scale=1):
|
| 416 |
+
med_output_img = gr.Image(label="Segmentation Result", type="pil")
|
| 417 |
+
med_output_txt = gr.Textbox(label="Status", lines=3)
|
| 418 |
+
|
| 419 |
+
med_btn.click(fn=segment_medical, inputs=[med_image, med_dataset, med_thr],
|
| 420 |
+
outputs=[med_output_img, med_output_txt])
|
| 421 |
+
|
| 422 |
+
gr.Examples(
|
| 423 |
+
examples=[
|
| 424 |
+
["examples/cju5y84q3mdv50817eyp82xf3.jpg", "Kvasir-SEG", 0.7],
|
| 425 |
+
["examples/0c2550a23b8a0f29a7575de8c61690d3c31bc897dd5ba66caec201d201a278c2.png", "DSB 2018", 0.7],
|
| 426 |
+
],
|
| 427 |
+
inputs=[med_image, med_dataset, med_thr],
|
| 428 |
+
outputs=[med_output_img, med_output_txt],
|
| 429 |
+
fn=segment_medical,
|
| 430 |
+
cache_examples=False,
|
| 431 |
+
)
|
| 432 |
+
|
| 433 |
+
gr.Markdown("""
|
| 434 |
+
| Task | Dataset | Dice ↑ | IoU ↑ | Hausdorff ↓ |
|
| 435 |
+
|:---|:---|:---:|:---:|:---:|
|
| 436 |
+
| Polyp Seg | Kvasir-SEG | **0.945** | **0.909** | **12.94** |
|
| 437 |
+
| Nuclei Seg | DSB 2018 | **0.908** | **0.839** | **5.19** |
|
| 438 |
+
|
| 439 |
+
> GeoViG reduces Hausdorff Distance **5.4× vs ResNet-50** (70.37 → 12.94) on Kvasir-SEG.
|
| 440 |
+
""")
|
| 441 |
+
|
| 442 |
+
demo.launch(**_LAUNCH_KWARGS)
|
configs/_base_/datasets/coco_detection.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# dataset settings
|
| 2 |
+
dataset_type = 'CocoDataset'
|
| 3 |
+
data_root = '../../mmdetection/data/coco/'
|
| 4 |
+
img_norm_cfg = dict(
|
| 5 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 6 |
+
train_pipeline = [
|
| 7 |
+
dict(type='LoadImageFromFile'),
|
| 8 |
+
dict(type='LoadAnnotations', with_bbox=True),
|
| 9 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 10 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 11 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 12 |
+
dict(type='Pad', size_divisor=32),
|
| 13 |
+
dict(type='DefaultFormatBundle'),
|
| 14 |
+
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
|
| 15 |
+
]
|
| 16 |
+
test_pipeline = [
|
| 17 |
+
dict(type='LoadImageFromFile'),
|
| 18 |
+
dict(
|
| 19 |
+
type='MultiScaleFlipAug',
|
| 20 |
+
img_scale=(1333, 800),
|
| 21 |
+
flip=False,
|
| 22 |
+
transforms=[
|
| 23 |
+
dict(type='Resize', keep_ratio=True),
|
| 24 |
+
dict(type='RandomFlip'),
|
| 25 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 26 |
+
dict(type='Pad', size_divisor=32),
|
| 27 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 28 |
+
dict(type='Collect', keys=['img']),
|
| 29 |
+
])
|
| 30 |
+
]
|
| 31 |
+
data = dict(
|
| 32 |
+
samples_per_gpu=2,
|
| 33 |
+
workers_per_gpu=2,
|
| 34 |
+
train=dict(
|
| 35 |
+
type=dataset_type,
|
| 36 |
+
ann_file=data_root + 'annotations/instances_train2017.json',
|
| 37 |
+
img_prefix=data_root + 'train2017/',
|
| 38 |
+
pipeline=train_pipeline),
|
| 39 |
+
val=dict(
|
| 40 |
+
type=dataset_type,
|
| 41 |
+
ann_file=data_root + 'annotations/instances_val2017.json',
|
| 42 |
+
img_prefix=data_root + 'val2017/',
|
| 43 |
+
pipeline=test_pipeline),
|
| 44 |
+
test=dict(
|
| 45 |
+
type=dataset_type,
|
| 46 |
+
ann_file=data_root + 'annotations/instances_val2017.json',
|
| 47 |
+
img_prefix=data_root + 'val2017/',
|
| 48 |
+
pipeline=test_pipeline))
|
| 49 |
+
evaluation = dict(interval=1, metric='bbox')
|
configs/_base_/datasets/coco_instance.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# dataset settings
|
| 2 |
+
dataset_type = 'CocoDataset'
|
| 3 |
+
data_root = '../../../hvig/detection/data/coco/'
|
| 4 |
+
img_norm_cfg = dict(
|
| 5 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 6 |
+
train_pipeline = [
|
| 7 |
+
dict(type='LoadImageFromFile'),
|
| 8 |
+
dict(type='LoadAnnotations', with_bbox=True, with_mask=True),
|
| 9 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 10 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 11 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 12 |
+
dict(type='Pad', size_divisor=32),
|
| 13 |
+
dict(type='DefaultFormatBundle'),
|
| 14 |
+
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
| 15 |
+
]
|
| 16 |
+
test_pipeline = [
|
| 17 |
+
dict(type='LoadImageFromFile'),
|
| 18 |
+
dict(
|
| 19 |
+
type='MultiScaleFlipAug',
|
| 20 |
+
img_scale=(1333, 800),
|
| 21 |
+
flip=False,
|
| 22 |
+
transforms=[
|
| 23 |
+
dict(type='Resize', keep_ratio=True),
|
| 24 |
+
dict(type='RandomFlip'),
|
| 25 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 26 |
+
dict(type='Pad', size_divisor=32),
|
| 27 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 28 |
+
dict(type='Collect', keys=['img']),
|
| 29 |
+
])
|
| 30 |
+
]
|
| 31 |
+
data = dict(
|
| 32 |
+
samples_per_gpu=1, # was 2, made 1 to avoid out of memory
|
| 33 |
+
workers_per_gpu=2,
|
| 34 |
+
train=dict(
|
| 35 |
+
type=dataset_type,
|
| 36 |
+
ann_file=data_root + 'annotations/instances_train2017.json',
|
| 37 |
+
img_prefix=data_root + 'train2017/',
|
| 38 |
+
pipeline=train_pipeline),
|
| 39 |
+
val=dict(
|
| 40 |
+
type=dataset_type,
|
| 41 |
+
ann_file=data_root + 'annotations/instances_val2017.json',
|
| 42 |
+
img_prefix=data_root + 'val2017/',
|
| 43 |
+
pipeline=test_pipeline),
|
| 44 |
+
test=dict(
|
| 45 |
+
type=dataset_type,
|
| 46 |
+
ann_file=data_root + 'annotations/instances_val2017.json',
|
| 47 |
+
img_prefix=data_root + 'val2017/',
|
| 48 |
+
pipeline=test_pipeline))
|
| 49 |
+
evaluation = dict(metric=['bbox', 'segm'])
|
configs/_base_/datasets/coco_instance_semantic.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# dataset settings
|
| 2 |
+
dataset_type = 'CocoDataset'
|
| 3 |
+
data_root = 'data/coco/'
|
| 4 |
+
img_norm_cfg = dict(
|
| 5 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 6 |
+
train_pipeline = [
|
| 7 |
+
dict(type='LoadImageFromFile'),
|
| 8 |
+
dict(
|
| 9 |
+
type='LoadAnnotations', with_bbox=True, with_mask=True, with_seg=True),
|
| 10 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 11 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 12 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 13 |
+
dict(type='Pad', size_divisor=32),
|
| 14 |
+
dict(type='SegRescale', scale_factor=1 / 8),
|
| 15 |
+
dict(type='DefaultFormatBundle'),
|
| 16 |
+
dict(
|
| 17 |
+
type='Collect',
|
| 18 |
+
keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks', 'gt_semantic_seg']),
|
| 19 |
+
]
|
| 20 |
+
test_pipeline = [
|
| 21 |
+
dict(type='LoadImageFromFile'),
|
| 22 |
+
dict(
|
| 23 |
+
type='MultiScaleFlipAug',
|
| 24 |
+
img_scale=(1333, 800),
|
| 25 |
+
flip=False,
|
| 26 |
+
transforms=[
|
| 27 |
+
dict(type='Resize', keep_ratio=True),
|
| 28 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 29 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 30 |
+
dict(type='Pad', size_divisor=32),
|
| 31 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 32 |
+
dict(type='Collect', keys=['img']),
|
| 33 |
+
])
|
| 34 |
+
]
|
| 35 |
+
data = dict(
|
| 36 |
+
samples_per_gpu=2,
|
| 37 |
+
workers_per_gpu=2,
|
| 38 |
+
train=dict(
|
| 39 |
+
type=dataset_type,
|
| 40 |
+
ann_file=data_root + 'annotations/instances_train2017.json',
|
| 41 |
+
img_prefix=data_root + 'train2017/',
|
| 42 |
+
seg_prefix=data_root + 'stuffthingmaps/train2017/',
|
| 43 |
+
pipeline=train_pipeline),
|
| 44 |
+
val=dict(
|
| 45 |
+
type=dataset_type,
|
| 46 |
+
ann_file=data_root + 'annotations/instances_val2017.json',
|
| 47 |
+
img_prefix=data_root + 'val2017/',
|
| 48 |
+
pipeline=test_pipeline),
|
| 49 |
+
test=dict(
|
| 50 |
+
type=dataset_type,
|
| 51 |
+
ann_file=data_root + 'annotations/instances_val2017.json',
|
| 52 |
+
img_prefix=data_root + 'val2017/',
|
| 53 |
+
pipeline=test_pipeline))
|
| 54 |
+
evaluation = dict(metric=['bbox', 'segm'])
|
configs/_base_/default_runtime.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
checkpoint_config = dict(interval=1)
|
| 2 |
+
# yapf:disable
|
| 3 |
+
log_config = dict(
|
| 4 |
+
interval=50,
|
| 5 |
+
hooks=[
|
| 6 |
+
dict(type='TextLoggerHook'),
|
| 7 |
+
# dict(type='TensorboardLoggerHook')
|
| 8 |
+
])
|
| 9 |
+
# yapf:enable
|
| 10 |
+
custom_hooks = [dict(type='NumClassCheckHook')]
|
| 11 |
+
|
| 12 |
+
dist_params = dict(backend='nccl')
|
| 13 |
+
log_level = 'INFO'
|
| 14 |
+
load_from = None
|
| 15 |
+
resume_from = None
|
| 16 |
+
workflow = [('train', 1)]
|
configs/_base_/models/mask_rcnn_r50_fpn.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# model settings
|
| 2 |
+
model = dict(
|
| 3 |
+
type='MaskRCNN',
|
| 4 |
+
pretrained='torchvision://resnet50',
|
| 5 |
+
backbone=dict(
|
| 6 |
+
type='ResNet',
|
| 7 |
+
depth=50,
|
| 8 |
+
num_stages=4,
|
| 9 |
+
out_indices=(0, 1, 2, 3),
|
| 10 |
+
frozen_stages=1,
|
| 11 |
+
norm_cfg=dict(type='BN', requires_grad=True),
|
| 12 |
+
norm_eval=True,
|
| 13 |
+
style='pytorch'),
|
| 14 |
+
neck=dict(
|
| 15 |
+
type='FPN',
|
| 16 |
+
in_channels=[256, 512, 1024, 2048],
|
| 17 |
+
out_channels=256,
|
| 18 |
+
num_outs=5),
|
| 19 |
+
rpn_head=dict(
|
| 20 |
+
type='RPNHead',
|
| 21 |
+
in_channels=256,
|
| 22 |
+
feat_channels=256,
|
| 23 |
+
anchor_generator=dict(
|
| 24 |
+
type='AnchorGenerator',
|
| 25 |
+
scales=[8],
|
| 26 |
+
ratios=[0.5, 1.0, 2.0],
|
| 27 |
+
strides=[4, 8, 16, 32, 64]),
|
| 28 |
+
bbox_coder=dict(
|
| 29 |
+
type='DeltaXYWHBBoxCoder',
|
| 30 |
+
target_means=[.0, .0, .0, .0],
|
| 31 |
+
target_stds=[1.0, 1.0, 1.0, 1.0]),
|
| 32 |
+
loss_cls=dict(
|
| 33 |
+
type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
|
| 34 |
+
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
| 35 |
+
roi_head=dict(
|
| 36 |
+
type='StandardRoIHead',
|
| 37 |
+
bbox_roi_extractor=dict(
|
| 38 |
+
type='SingleRoIExtractor',
|
| 39 |
+
roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
|
| 40 |
+
out_channels=256,
|
| 41 |
+
featmap_strides=[4, 8, 16, 32]),
|
| 42 |
+
bbox_head=dict(
|
| 43 |
+
type='Shared2FCBBoxHead',
|
| 44 |
+
in_channels=256,
|
| 45 |
+
fc_out_channels=1024,
|
| 46 |
+
roi_feat_size=7,
|
| 47 |
+
num_classes=80,
|
| 48 |
+
bbox_coder=dict(
|
| 49 |
+
type='DeltaXYWHBBoxCoder',
|
| 50 |
+
target_means=[0., 0., 0., 0.],
|
| 51 |
+
target_stds=[0.1, 0.1, 0.2, 0.2]),
|
| 52 |
+
reg_class_agnostic=False,
|
| 53 |
+
loss_cls=dict(
|
| 54 |
+
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
|
| 55 |
+
loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
|
| 56 |
+
mask_roi_extractor=dict(
|
| 57 |
+
type='SingleRoIExtractor',
|
| 58 |
+
roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),
|
| 59 |
+
out_channels=256,
|
| 60 |
+
featmap_strides=[4, 8, 16, 32]),
|
| 61 |
+
mask_head=dict(
|
| 62 |
+
type='FCNMaskHead',
|
| 63 |
+
num_convs=4,
|
| 64 |
+
in_channels=256,
|
| 65 |
+
conv_out_channels=256,
|
| 66 |
+
num_classes=80,
|
| 67 |
+
loss_mask=dict(
|
| 68 |
+
type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),
|
| 69 |
+
# model training and testing settings
|
| 70 |
+
train_cfg=dict(
|
| 71 |
+
rpn=dict(
|
| 72 |
+
assigner=dict(
|
| 73 |
+
type='MaxIoUAssigner',
|
| 74 |
+
pos_iou_thr=0.7,
|
| 75 |
+
neg_iou_thr=0.3,
|
| 76 |
+
min_pos_iou=0.3,
|
| 77 |
+
match_low_quality=True,
|
| 78 |
+
ignore_iof_thr=-1),
|
| 79 |
+
sampler=dict(
|
| 80 |
+
type='RandomSampler',
|
| 81 |
+
num=256,
|
| 82 |
+
pos_fraction=0.5,
|
| 83 |
+
neg_pos_ub=-1,
|
| 84 |
+
add_gt_as_proposals=False),
|
| 85 |
+
allowed_border=-1,
|
| 86 |
+
pos_weight=-1,
|
| 87 |
+
debug=False),
|
| 88 |
+
rpn_proposal=dict(
|
| 89 |
+
nms_pre=2000,
|
| 90 |
+
max_per_img=1000,
|
| 91 |
+
nms=dict(type='nms', iou_threshold=0.7),
|
| 92 |
+
min_bbox_size=0),
|
| 93 |
+
rcnn=dict(
|
| 94 |
+
assigner=dict(
|
| 95 |
+
type='MaxIoUAssigner',
|
| 96 |
+
pos_iou_thr=0.5,
|
| 97 |
+
neg_iou_thr=0.5,
|
| 98 |
+
min_pos_iou=0.5,
|
| 99 |
+
match_low_quality=True,
|
| 100 |
+
ignore_iof_thr=-1),
|
| 101 |
+
sampler=dict(
|
| 102 |
+
type='RandomSampler',
|
| 103 |
+
num=512,
|
| 104 |
+
pos_fraction=0.25,
|
| 105 |
+
neg_pos_ub=-1,
|
| 106 |
+
add_gt_as_proposals=True),
|
| 107 |
+
mask_size=28,
|
| 108 |
+
pos_weight=-1,
|
| 109 |
+
debug=False)),
|
| 110 |
+
test_cfg=dict(
|
| 111 |
+
rpn=dict(
|
| 112 |
+
nms_pre=1000,
|
| 113 |
+
max_per_img=1000,
|
| 114 |
+
nms=dict(type='nms', iou_threshold=0.7),
|
| 115 |
+
min_bbox_size=0),
|
| 116 |
+
rcnn=dict(
|
| 117 |
+
score_thr=0.05,
|
| 118 |
+
nms=dict(type='nms', iou_threshold=0.5),
|
| 119 |
+
max_per_img=100,
|
| 120 |
+
mask_thr_binary=0.5)))
|
configs/_base_/schedules/schedule_1x.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# optimizer
|
| 2 |
+
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
|
| 3 |
+
optimizer_config = dict(grad_clip=None)
|
| 4 |
+
# learning policy
|
| 5 |
+
lr_config = dict(
|
| 6 |
+
policy='step',
|
| 7 |
+
warmup='linear',
|
| 8 |
+
warmup_iters=500,
|
| 9 |
+
warmup_ratio=1e-6, # 0.001
|
| 10 |
+
step=[8, 11])
|
| 11 |
+
runner = dict(type='EpochBasedRunner', max_epochs=12)
|
configs/_base_/schedules/schedule_20e.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# optimizer
|
| 2 |
+
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
|
| 3 |
+
optimizer_config = dict(grad_clip=None)
|
| 4 |
+
# learning policy
|
| 5 |
+
lr_config = dict(
|
| 6 |
+
policy='step',
|
| 7 |
+
warmup='linear',
|
| 8 |
+
warmup_iters=500,
|
| 9 |
+
warmup_ratio=0.001,
|
| 10 |
+
step=[16, 19])
|
| 11 |
+
runner = dict(type='EpochBasedRunner', max_epochs=20)
|
configs/_base_/schedules/schedule_2x.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# optimizer
|
| 2 |
+
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
|
| 3 |
+
optimizer_config = dict(grad_clip=None)
|
| 4 |
+
# learning policy
|
| 5 |
+
lr_config = dict(
|
| 6 |
+
policy='step',
|
| 7 |
+
warmup='linear',
|
| 8 |
+
warmup_iters=500,
|
| 9 |
+
warmup_ratio=0.001,
|
| 10 |
+
step=[16, 22])
|
| 11 |
+
runner = dict(type='EpochBasedRunner', max_epochs=24)
|
configs/dsb/mask_rcnn_geovig_m_fpn_1x_dsb.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'../_base_/models/mask_rcnn_r50_fpn.py',
|
| 3 |
+
'../_base_/schedules/schedule_1x.py',
|
| 4 |
+
'../_base_/default_runtime.py'
|
| 5 |
+
]
|
| 6 |
+
|
| 7 |
+
model = dict(
|
| 8 |
+
backbone=dict(
|
| 9 |
+
_delete_=True,
|
| 10 |
+
type='geovig_m_feat',
|
| 11 |
+
style='pytorch',
|
| 12 |
+
pretrained=False,
|
| 13 |
+
use_detect_adapter=True,
|
| 14 |
+
init_cfg=None
|
| 15 |
+
),
|
| 16 |
+
neck=dict(
|
| 17 |
+
type='FPN',
|
| 18 |
+
in_channels=[64, 128, 256, 384],
|
| 19 |
+
out_channels=256,
|
| 20 |
+
num_outs=5,
|
| 21 |
+
norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)
|
| 22 |
+
),
|
| 23 |
+
roi_head=dict(
|
| 24 |
+
bbox_head=dict(num_classes=1),
|
| 25 |
+
mask_head=dict(num_classes=1)
|
| 26 |
+
),
|
| 27 |
+
test_cfg=dict(
|
| 28 |
+
rpn=dict(nms_pre=1000, max_per_img=1000, nms=dict(type='nms', iou_threshold=0.7), min_bbox_size=0),
|
| 29 |
+
rcnn=dict(
|
| 30 |
+
score_thr=0.05,
|
| 31 |
+
nms=dict(type='nms', iou_threshold=0.3),
|
| 32 |
+
max_per_img=200,
|
| 33 |
+
mask_thr_binary=0.5
|
| 34 |
+
)
|
| 35 |
+
)
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
# Dataset settings
|
| 39 |
+
dataset_type = 'DSBDataset'
|
| 40 |
+
data_root = '../../data/dsb2018/stage1_train'
|
| 41 |
+
img_norm_cfg = dict(
|
| 42 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 43 |
+
train_pipeline = [
|
| 44 |
+
dict(type='LoadImageFromFile'),
|
| 45 |
+
dict(type='LoadAnnotations', with_bbox=True, with_mask=False),
|
| 46 |
+
dict(type='LoadMasksFromAnn'),
|
| 47 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 48 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 49 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 50 |
+
dict(type='Pad', size_divisor=32),
|
| 51 |
+
dict(type='DefaultFormatBundle'),
|
| 52 |
+
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
| 53 |
+
]
|
| 54 |
+
test_pipeline = [
|
| 55 |
+
dict(type='LoadImageFromFile'),
|
| 56 |
+
dict(
|
| 57 |
+
type='MultiScaleFlipAug',
|
| 58 |
+
img_scale=(1333, 800),
|
| 59 |
+
flip=False,
|
| 60 |
+
transforms=[
|
| 61 |
+
dict(type='Resize', keep_ratio=True),
|
| 62 |
+
dict(type='RandomFlip'),
|
| 63 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 64 |
+
dict(type='Pad', size_divisor=32),
|
| 65 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 66 |
+
dict(type='Collect', keys=['img']),
|
| 67 |
+
])
|
| 68 |
+
]
|
| 69 |
+
data = dict(
|
| 70 |
+
samples_per_gpu=1,
|
| 71 |
+
workers_per_gpu=4,
|
| 72 |
+
train=dict(
|
| 73 |
+
type=dataset_type,
|
| 74 |
+
img_prefix=data_root,
|
| 75 |
+
ann_file='',
|
| 76 |
+
pipeline=train_pipeline),
|
| 77 |
+
val=dict(
|
| 78 |
+
type=dataset_type,
|
| 79 |
+
img_prefix=data_root,
|
| 80 |
+
ann_file='',
|
| 81 |
+
pipeline=test_pipeline),
|
| 82 |
+
test=dict(
|
| 83 |
+
type=dataset_type,
|
| 84 |
+
img_prefix=data_root,
|
| 85 |
+
ann_file='',
|
| 86 |
+
pipeline=test_pipeline))
|
| 87 |
+
|
| 88 |
+
# Fine-tuning config
|
| 89 |
+
load_from = 'geovig_m_seg_4G/epoch_12.pth'
|
| 90 |
+
|
| 91 |
+
evaluation = dict(metric='mAP')
|
| 92 |
+
|
| 93 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05)
|
| 94 |
+
optimizer_config = dict(_delete_=True, grad_clip=dict(max_norm=1.0, norm_type=2))
|
| 95 |
+
|
| 96 |
+
runner = dict(type='EpochBasedRunner', max_epochs=20)
|
| 97 |
+
lr_config = dict(
|
| 98 |
+
policy='step',
|
| 99 |
+
warmup='linear',
|
| 100 |
+
warmup_iters=500,
|
| 101 |
+
warmup_ratio=0.001,
|
| 102 |
+
step=[16, 19])
|
configs/dsb/mask_rcnn_mobilevig_m_fpn_1x_dsb.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'../mask_rcnn_mobilevig_m_fpn_1x_coco.py'
|
| 3 |
+
]
|
| 4 |
+
|
| 5 |
+
model = dict(
|
| 6 |
+
roi_head=dict(
|
| 7 |
+
bbox_head=dict(num_classes=1),
|
| 8 |
+
mask_head=dict(num_classes=1)
|
| 9 |
+
),
|
| 10 |
+
test_cfg=dict(
|
| 11 |
+
rpn=dict(nms_pre=1000, max_per_img=1000, nms=dict(type='nms', iou_threshold=0.7), min_bbox_size=0),
|
| 12 |
+
rcnn=dict(
|
| 13 |
+
score_thr=0.05,
|
| 14 |
+
nms=dict(type='nms', iou_threshold=0.3),
|
| 15 |
+
max_per_img=200,
|
| 16 |
+
mask_thr_binary=0.5
|
| 17 |
+
)
|
| 18 |
+
)
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
# Dataset settings
|
| 22 |
+
dataset_type = 'DSBDataset'
|
| 23 |
+
data_root = '../../data/dsb2018/stage1_train'
|
| 24 |
+
img_norm_cfg = dict(
|
| 25 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 26 |
+
|
| 27 |
+
train_pipeline = [
|
| 28 |
+
dict(type='LoadImageFromFile'),
|
| 29 |
+
dict(type='LoadAnnotations', with_bbox=True, with_mask=False),
|
| 30 |
+
dict(type='LoadMasksFromAnn'),
|
| 31 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 32 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 33 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 34 |
+
dict(type='Pad', size_divisor=32),
|
| 35 |
+
dict(type='DefaultFormatBundle'),
|
| 36 |
+
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
| 37 |
+
]
|
| 38 |
+
test_pipeline = [
|
| 39 |
+
dict(type='LoadImageFromFile'),
|
| 40 |
+
dict(
|
| 41 |
+
type='MultiScaleFlipAug',
|
| 42 |
+
img_scale=(1333, 800),
|
| 43 |
+
flip=False,
|
| 44 |
+
transforms=[
|
| 45 |
+
dict(type='Resize', keep_ratio=True),
|
| 46 |
+
dict(type='RandomFlip'),
|
| 47 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 48 |
+
dict(type='Pad', size_divisor=32),
|
| 49 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 50 |
+
dict(type='Collect', keys=['img']),
|
| 51 |
+
])
|
| 52 |
+
]
|
| 53 |
+
data = dict(
|
| 54 |
+
samples_per_gpu=1,
|
| 55 |
+
workers_per_gpu=4,
|
| 56 |
+
train=dict(
|
| 57 |
+
type=dataset_type,
|
| 58 |
+
img_prefix=data_root,
|
| 59 |
+
ann_file='',
|
| 60 |
+
pipeline=train_pipeline),
|
| 61 |
+
val=dict(
|
| 62 |
+
type=dataset_type,
|
| 63 |
+
img_prefix=data_root,
|
| 64 |
+
ann_file='',
|
| 65 |
+
pipeline=test_pipeline),
|
| 66 |
+
test=dict(
|
| 67 |
+
type=dataset_type,
|
| 68 |
+
img_prefix=data_root,
|
| 69 |
+
ann_file='',
|
| 70 |
+
pipeline=test_pipeline))
|
| 71 |
+
|
| 72 |
+
# MobileViG Pretrained Weights
|
| 73 |
+
load_from = 'geovig_m_seg_4G/det_mobilevig_m_62_8.pth'
|
| 74 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05)
|
configs/dsb/mask_rcnn_resnet50_fpn_1x_dsb.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'../_base_/models/mask_rcnn_r50_fpn.py',
|
| 3 |
+
'../_base_/schedules/schedule_1x.py',
|
| 4 |
+
'../_base_/default_runtime.py'
|
| 5 |
+
]
|
| 6 |
+
|
| 7 |
+
model = dict(
|
| 8 |
+
roi_head=dict(
|
| 9 |
+
bbox_head=dict(num_classes=1),
|
| 10 |
+
mask_head=dict(num_classes=1)
|
| 11 |
+
),
|
| 12 |
+
test_cfg=dict(
|
| 13 |
+
rpn=dict(nms_pre=1000, max_per_img=1000, nms=dict(type='nms', iou_threshold=0.7), min_bbox_size=0),
|
| 14 |
+
rcnn=dict(
|
| 15 |
+
score_thr=0.05,
|
| 16 |
+
nms=dict(type='nms', iou_threshold=0.3),
|
| 17 |
+
max_per_img=200,
|
| 18 |
+
mask_thr_binary=0.5
|
| 19 |
+
)
|
| 20 |
+
)
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
# Dataset settings
|
| 24 |
+
dataset_type = 'DSBDataset'
|
| 25 |
+
data_root = '../../data/dsb2018/stage1_train'
|
| 26 |
+
img_norm_cfg = dict(
|
| 27 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 28 |
+
train_pipeline = [
|
| 29 |
+
dict(type='LoadImageFromFile'),
|
| 30 |
+
dict(type='LoadAnnotations', with_bbox=True, with_mask=False),
|
| 31 |
+
dict(type='LoadMasksFromAnn'),
|
| 32 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 33 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 34 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 35 |
+
dict(type='Pad', size_divisor=32),
|
| 36 |
+
dict(type='DefaultFormatBundle'),
|
| 37 |
+
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
| 38 |
+
]
|
| 39 |
+
test_pipeline = [
|
| 40 |
+
dict(type='LoadImageFromFile'),
|
| 41 |
+
dict(
|
| 42 |
+
type='MultiScaleFlipAug',
|
| 43 |
+
img_scale=(1333, 800),
|
| 44 |
+
flip=False,
|
| 45 |
+
transforms=[
|
| 46 |
+
dict(type='Resize', keep_ratio=True),
|
| 47 |
+
dict(type='RandomFlip'),
|
| 48 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 49 |
+
dict(type='Pad', size_divisor=32),
|
| 50 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 51 |
+
dict(type='Collect', keys=['img']),
|
| 52 |
+
])
|
| 53 |
+
]
|
| 54 |
+
data = dict(
|
| 55 |
+
samples_per_gpu=1,
|
| 56 |
+
workers_per_gpu=4,
|
| 57 |
+
train=dict(
|
| 58 |
+
type=dataset_type,
|
| 59 |
+
img_prefix=data_root,
|
| 60 |
+
ann_file='',
|
| 61 |
+
pipeline=train_pipeline),
|
| 62 |
+
val=dict(
|
| 63 |
+
type=dataset_type,
|
| 64 |
+
img_prefix=data_root,
|
| 65 |
+
ann_file='',
|
| 66 |
+
pipeline=test_pipeline),
|
| 67 |
+
test=dict(
|
| 68 |
+
type=dataset_type,
|
| 69 |
+
img_prefix=data_root,
|
| 70 |
+
ann_file='',
|
| 71 |
+
pipeline=test_pipeline))
|
| 72 |
+
|
| 73 |
+
load_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth'
|
| 74 |
+
optimizer = dict(type='SGD', lr=0.002, momentum=0.9, weight_decay=0.0001)
|
configs/dsb/mask_rcnn_swin_t_fpn_1x_dsb.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'../_base_/models/mask_rcnn_r50_fpn.py',
|
| 3 |
+
'../_base_/schedules/schedule_1x.py',
|
| 4 |
+
'../_base_/default_runtime.py'
|
| 5 |
+
]
|
| 6 |
+
|
| 7 |
+
model = dict(
|
| 8 |
+
backbone=dict(
|
| 9 |
+
_delete_=True,
|
| 10 |
+
type='SwinTransformer',
|
| 11 |
+
embed_dims=96,
|
| 12 |
+
depths=[2, 2, 6, 2],
|
| 13 |
+
num_heads=[3, 6, 12, 24],
|
| 14 |
+
window_size=7,
|
| 15 |
+
mlp_ratio=4,
|
| 16 |
+
qkv_bias=True,
|
| 17 |
+
qk_scale=None,
|
| 18 |
+
drop_rate=0.,
|
| 19 |
+
attn_drop_rate=0.,
|
| 20 |
+
drop_path_rate=0.2,
|
| 21 |
+
patch_norm=True,
|
| 22 |
+
out_indices=(0, 1, 2, 3),
|
| 23 |
+
with_cp=False,
|
| 24 |
+
convert_weights=True,
|
| 25 |
+
),
|
| 26 |
+
neck=dict(in_channels=[96, 192, 384, 768]),
|
| 27 |
+
roi_head=dict(
|
| 28 |
+
bbox_head=dict(num_classes=1),
|
| 29 |
+
mask_head=dict(num_classes=1)
|
| 30 |
+
),
|
| 31 |
+
test_cfg=dict(
|
| 32 |
+
rpn=dict(nms_pre=1000, max_per_img=1000, nms=dict(type='nms', iou_threshold=0.7), min_bbox_size=0),
|
| 33 |
+
rcnn=dict(
|
| 34 |
+
score_thr=0.05,
|
| 35 |
+
nms=dict(type='nms', iou_threshold=0.3),
|
| 36 |
+
max_per_img=200,
|
| 37 |
+
mask_thr_binary=0.5
|
| 38 |
+
)
|
| 39 |
+
)
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
# Dataset settings
|
| 43 |
+
dataset_type = 'DSBDataset'
|
| 44 |
+
data_root = '../../data/dsb2018/stage1_train'
|
| 45 |
+
img_norm_cfg = dict(
|
| 46 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 47 |
+
train_pipeline = [
|
| 48 |
+
dict(type='LoadImageFromFile'),
|
| 49 |
+
dict(type='LoadAnnotations', with_bbox=True, with_mask=False),
|
| 50 |
+
dict(type='LoadMasksFromAnn'),
|
| 51 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 52 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 53 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 54 |
+
dict(type='Pad', size_divisor=32),
|
| 55 |
+
dict(type='DefaultFormatBundle'),
|
| 56 |
+
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
| 57 |
+
]
|
| 58 |
+
test_pipeline = [
|
| 59 |
+
dict(type='LoadImageFromFile'),
|
| 60 |
+
dict(
|
| 61 |
+
type='MultiScaleFlipAug',
|
| 62 |
+
img_scale=(1333, 800),
|
| 63 |
+
flip=False,
|
| 64 |
+
transforms=[
|
| 65 |
+
dict(type='Resize', keep_ratio=True),
|
| 66 |
+
dict(type='RandomFlip'),
|
| 67 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 68 |
+
dict(type='Pad', size_divisor=32),
|
| 69 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 70 |
+
dict(type='Collect', keys=['img']),
|
| 71 |
+
])
|
| 72 |
+
]
|
| 73 |
+
data = dict(
|
| 74 |
+
samples_per_gpu=1,
|
| 75 |
+
workers_per_gpu=4,
|
| 76 |
+
train=dict(
|
| 77 |
+
type=dataset_type,
|
| 78 |
+
img_prefix=data_root,
|
| 79 |
+
ann_file='',
|
| 80 |
+
pipeline=train_pipeline),
|
| 81 |
+
val=dict(
|
| 82 |
+
type=dataset_type,
|
| 83 |
+
img_prefix=data_root,
|
| 84 |
+
ann_file='',
|
| 85 |
+
pipeline=test_pipeline),
|
| 86 |
+
test=dict(
|
| 87 |
+
type=dataset_type,
|
| 88 |
+
img_prefix=data_root,
|
| 89 |
+
ann_file='',
|
| 90 |
+
pipeline=test_pipeline))
|
| 91 |
+
|
| 92 |
+
load_from = 'https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_1x_coco/mask_rcnn_swin-t-p4-w7_fpn_1x_coco_20210902_120937-9d6b7cfa.pth'
|
| 93 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05)
|
configs/kvasir/mask_rcnn_fastvit_sa12_fpn_1x_kvasir.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'../_base_/models/mask_rcnn_r50_fpn.py',
|
| 3 |
+
'../_base_/datasets/coco_instance.py',
|
| 4 |
+
'../_base_/schedules/schedule_1x.py',
|
| 5 |
+
'../_base_/default_runtime.py'
|
| 6 |
+
]
|
| 7 |
+
|
| 8 |
+
# Requires importing fastvit_backbone in main/test scripts
|
| 9 |
+
custom_imports = dict(imports=['fastvit_backbone'], allow_failed_imports=False)
|
| 10 |
+
|
| 11 |
+
model = dict(
|
| 12 |
+
backbone=dict(
|
| 13 |
+
_delete_=True,
|
| 14 |
+
type='fastvit_sa12',
|
| 15 |
+
init_cfg=dict(type='Pretrained', checkpoint='geovig_m_5e4_8G_300_80_70/fastvit_sa12.pth.tar')
|
| 16 |
+
),
|
| 17 |
+
neck=dict(
|
| 18 |
+
type='FPN',
|
| 19 |
+
in_channels=[64, 128, 256, 512],
|
| 20 |
+
out_channels=256,
|
| 21 |
+
num_outs=5,
|
| 22 |
+
norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)
|
| 23 |
+
),
|
| 24 |
+
roi_head=dict(
|
| 25 |
+
bbox_head=dict(num_classes=1),
|
| 26 |
+
mask_head=dict(num_classes=1)
|
| 27 |
+
)
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
dataset_type = 'KvasirDataset'
|
| 31 |
+
data_root = '../../data/Kvasir-SEG'
|
| 32 |
+
img_norm_cfg = dict(
|
| 33 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 34 |
+
|
| 35 |
+
train_pipeline = [
|
| 36 |
+
dict(type='LoadImageFromFile'),
|
| 37 |
+
dict(type='LoadAnnotations', with_bbox=True, with_mask=False),
|
| 38 |
+
dict(type='LoadMasksFromAnn'),
|
| 39 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 40 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 41 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 42 |
+
dict(type='Pad', size_divisor=32),
|
| 43 |
+
dict(type='DefaultFormatBundle'),
|
| 44 |
+
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
| 45 |
+
]
|
| 46 |
+
test_pipeline = [
|
| 47 |
+
dict(type='LoadImageFromFile'),
|
| 48 |
+
dict(
|
| 49 |
+
type='MultiScaleFlipAug',
|
| 50 |
+
img_scale=(1333, 800),
|
| 51 |
+
flip=False,
|
| 52 |
+
transforms=[
|
| 53 |
+
dict(type='Resize', keep_ratio=True),
|
| 54 |
+
dict(type='RandomFlip'),
|
| 55 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 56 |
+
dict(type='Pad', size_divisor=32),
|
| 57 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 58 |
+
dict(type='Collect', keys=['img']),
|
| 59 |
+
])
|
| 60 |
+
]
|
| 61 |
+
|
| 62 |
+
data = dict(
|
| 63 |
+
samples_per_gpu=1,
|
| 64 |
+
workers_per_gpu=4,
|
| 65 |
+
train=dict(
|
| 66 |
+
type=dataset_type,
|
| 67 |
+
img_prefix=data_root,
|
| 68 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 69 |
+
pipeline=train_pipeline),
|
| 70 |
+
val=dict(
|
| 71 |
+
type=dataset_type,
|
| 72 |
+
img_prefix=data_root,
|
| 73 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 74 |
+
pipeline=test_pipeline),
|
| 75 |
+
test=dict(
|
| 76 |
+
type=dataset_type,
|
| 77 |
+
img_prefix=data_root,
|
| 78 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 79 |
+
pipeline=test_pipeline))
|
| 80 |
+
|
| 81 |
+
evaluation = dict(metric='mAP', save_best='mAP')
|
| 82 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05)
|
configs/kvasir/mask_rcnn_geovig_m_fpn_1x_kvasir.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'../_base_/models/mask_rcnn_r50_fpn.py',
|
| 3 |
+
'../_base_/schedules/schedule_1x.py',
|
| 4 |
+
'../_base_/default_runtime.py'
|
| 5 |
+
]
|
| 6 |
+
|
| 7 |
+
model = dict(
|
| 8 |
+
backbone=dict(
|
| 9 |
+
_delete_=True,
|
| 10 |
+
type='geovig_m_feat',
|
| 11 |
+
style='pytorch',
|
| 12 |
+
pretrained=False,
|
| 13 |
+
use_detect_adapter=True,
|
| 14 |
+
init_cfg=None
|
| 15 |
+
),
|
| 16 |
+
neck=dict(
|
| 17 |
+
type='FPN',
|
| 18 |
+
in_channels=[64, 128, 256, 384],
|
| 19 |
+
out_channels=256,
|
| 20 |
+
num_outs=5,
|
| 21 |
+
norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)
|
| 22 |
+
),
|
| 23 |
+
roi_head=dict(
|
| 24 |
+
bbox_head=dict(num_classes=1),
|
| 25 |
+
mask_head=dict(num_classes=1)
|
| 26 |
+
)
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
# Dataset settings
|
| 30 |
+
dataset_type = 'KvasirDataset'
|
| 31 |
+
data_root = '../../data/Kvasir-SEG'
|
| 32 |
+
img_norm_cfg = dict(
|
| 33 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 34 |
+
train_pipeline = [
|
| 35 |
+
dict(type='LoadImageFromFile'),
|
| 36 |
+
dict(type='LoadAnnotations', with_bbox=True, with_mask=False),
|
| 37 |
+
dict(type='LoadMasksFromAnn'),
|
| 38 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 39 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 40 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 41 |
+
dict(type='Pad', size_divisor=32),
|
| 42 |
+
dict(type='DefaultFormatBundle'),
|
| 43 |
+
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
| 44 |
+
]
|
| 45 |
+
test_pipeline = [
|
| 46 |
+
dict(type='LoadImageFromFile'),
|
| 47 |
+
dict(
|
| 48 |
+
type='MultiScaleFlipAug',
|
| 49 |
+
img_scale=(1333, 800),
|
| 50 |
+
flip=False,
|
| 51 |
+
transforms=[
|
| 52 |
+
dict(type='Resize', keep_ratio=True),
|
| 53 |
+
dict(type='RandomFlip'),
|
| 54 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 55 |
+
dict(type='Pad', size_divisor=32),
|
| 56 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 57 |
+
dict(type='Collect', keys=['img']),
|
| 58 |
+
])
|
| 59 |
+
]
|
| 60 |
+
data = dict(
|
| 61 |
+
samples_per_gpu=1,
|
| 62 |
+
workers_per_gpu=4,
|
| 63 |
+
train=dict(
|
| 64 |
+
type=dataset_type,
|
| 65 |
+
img_prefix=data_root,
|
| 66 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 67 |
+
pipeline=train_pipeline),
|
| 68 |
+
val=dict(
|
| 69 |
+
type=dataset_type,
|
| 70 |
+
img_prefix=data_root,
|
| 71 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 72 |
+
pipeline=test_pipeline),
|
| 73 |
+
test=dict(
|
| 74 |
+
type=dataset_type,
|
| 75 |
+
img_prefix=data_root,
|
| 76 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 77 |
+
pipeline=test_pipeline))
|
| 78 |
+
|
| 79 |
+
# Fine-tuning config
|
| 80 |
+
load_from = 'geovig_m_seg_4G/epoch_12.pth'
|
| 81 |
+
|
| 82 |
+
evaluation = dict(metric='mAP')
|
| 83 |
+
|
| 84 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05)
|
| 85 |
+
optimizer_config = dict(_delete_=True, grad_clip=dict(max_norm=1.0, norm_type=2))
|
configs/kvasir/mask_rcnn_mobilevig_m_fpn_1x_kvasir.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'../mask_rcnn_mobilevig_m_fpn_1x_coco.py'
|
| 3 |
+
]
|
| 4 |
+
|
| 5 |
+
model = dict(
|
| 6 |
+
roi_head=dict(
|
| 7 |
+
bbox_head=dict(num_classes=1),
|
| 8 |
+
mask_head=dict(num_classes=1)
|
| 9 |
+
)
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
dataset_type = 'KvasirDataset'
|
| 13 |
+
data_root = '../../data/Kvasir-SEG'
|
| 14 |
+
img_norm_cfg = dict(
|
| 15 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 16 |
+
|
| 17 |
+
train_pipeline = [
|
| 18 |
+
dict(type='LoadImageFromFile'),
|
| 19 |
+
dict(type='LoadAnnotations', with_bbox=True, with_mask=False),
|
| 20 |
+
dict(type='LoadMasksFromAnn'),
|
| 21 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 22 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 23 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 24 |
+
dict(type='Pad', size_divisor=32),
|
| 25 |
+
dict(type='DefaultFormatBundle'),
|
| 26 |
+
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
| 27 |
+
]
|
| 28 |
+
test_pipeline = [
|
| 29 |
+
dict(type='LoadImageFromFile'),
|
| 30 |
+
dict(
|
| 31 |
+
type='MultiScaleFlipAug',
|
| 32 |
+
img_scale=(1333, 800),
|
| 33 |
+
flip=False,
|
| 34 |
+
transforms=[
|
| 35 |
+
dict(type='Resize', keep_ratio=True),
|
| 36 |
+
dict(type='RandomFlip'),
|
| 37 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 38 |
+
dict(type='Pad', size_divisor=32),
|
| 39 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 40 |
+
dict(type='Collect', keys=['img']),
|
| 41 |
+
])
|
| 42 |
+
]
|
| 43 |
+
|
| 44 |
+
data = dict(
|
| 45 |
+
samples_per_gpu=1,
|
| 46 |
+
workers_per_gpu=4,
|
| 47 |
+
train=dict(
|
| 48 |
+
type=dataset_type,
|
| 49 |
+
img_prefix=data_root,
|
| 50 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 51 |
+
pipeline=train_pipeline),
|
| 52 |
+
val=dict(
|
| 53 |
+
type=dataset_type,
|
| 54 |
+
img_prefix=data_root,
|
| 55 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 56 |
+
pipeline=test_pipeline),
|
| 57 |
+
test=dict(
|
| 58 |
+
type=dataset_type,
|
| 59 |
+
img_prefix=data_root,
|
| 60 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 61 |
+
pipeline=test_pipeline))
|
| 62 |
+
|
| 63 |
+
evaluation = dict(metric='mAP', save_best='mAP')
|
| 64 |
+
|
| 65 |
+
# MobileViG Pretrained Weights (COCO)
|
| 66 |
+
load_from = 'geovig_m_seg_4G/det_mobilevig_m_62_8.pth'
|
configs/kvasir/mask_rcnn_resnet50_fpn_1x_kvasir.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'../_base_/models/mask_rcnn_r50_fpn.py',
|
| 3 |
+
'../_base_/datasets/coco_instance.py',
|
| 4 |
+
'../_base_/schedules/schedule_1x.py',
|
| 5 |
+
'../_base_/default_runtime.py'
|
| 6 |
+
]
|
| 7 |
+
|
| 8 |
+
model = dict(
|
| 9 |
+
roi_head=dict(
|
| 10 |
+
bbox_head=dict(num_classes=1),
|
| 11 |
+
mask_head=dict(num_classes=1)
|
| 12 |
+
)
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
dataset_type = 'KvasirDataset'
|
| 16 |
+
data_root = '../../data/Kvasir-SEG'
|
| 17 |
+
img_norm_cfg = dict(
|
| 18 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 19 |
+
|
| 20 |
+
train_pipeline = [
|
| 21 |
+
dict(type='LoadImageFromFile'),
|
| 22 |
+
dict(type='LoadAnnotations', with_bbox=True, with_mask=False),
|
| 23 |
+
dict(type='LoadMasksFromAnn'),
|
| 24 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 25 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 26 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 27 |
+
dict(type='Pad', size_divisor=32),
|
| 28 |
+
dict(type='DefaultFormatBundle'),
|
| 29 |
+
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
| 30 |
+
]
|
| 31 |
+
test_pipeline = [
|
| 32 |
+
dict(type='LoadImageFromFile'),
|
| 33 |
+
dict(
|
| 34 |
+
type='MultiScaleFlipAug',
|
| 35 |
+
img_scale=(1333, 800),
|
| 36 |
+
flip=False,
|
| 37 |
+
transforms=[
|
| 38 |
+
dict(type='Resize', keep_ratio=True),
|
| 39 |
+
dict(type='RandomFlip'),
|
| 40 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 41 |
+
dict(type='Pad', size_divisor=32),
|
| 42 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 43 |
+
dict(type='Collect', keys=['img']),
|
| 44 |
+
])
|
| 45 |
+
]
|
| 46 |
+
|
| 47 |
+
data = dict(
|
| 48 |
+
samples_per_gpu=1,
|
| 49 |
+
workers_per_gpu=4,
|
| 50 |
+
train=dict(
|
| 51 |
+
type=dataset_type,
|
| 52 |
+
img_prefix=data_root,
|
| 53 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 54 |
+
pipeline=train_pipeline),
|
| 55 |
+
val=dict(
|
| 56 |
+
type=dataset_type,
|
| 57 |
+
img_prefix=data_root,
|
| 58 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 59 |
+
pipeline=test_pipeline),
|
| 60 |
+
test=dict(
|
| 61 |
+
type=dataset_type,
|
| 62 |
+
img_prefix=data_root,
|
| 63 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 64 |
+
pipeline=test_pipeline))
|
| 65 |
+
|
| 66 |
+
# COCO Pretrained Weights
|
| 67 |
+
load_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_fpn_1x_coco/mask_rcnn_r50_fpn_1x_coco_20200205-d4b0c5d6.pth'
|
| 68 |
+
|
| 69 |
+
evaluation = dict(metric='mAP', save_best='mAP')
|
| 70 |
+
|
| 71 |
+
optimizer = dict(type='SGD', lr=0.002, momentum=0.9, weight_decay=0.0001)
|
| 72 |
+
|
configs/kvasir/mask_rcnn_swin_t_fpn_1x_kvasir.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'../_base_/models/mask_rcnn_r50_fpn.py',
|
| 3 |
+
'../_base_/datasets/coco_instance.py',
|
| 4 |
+
'../_base_/schedules/schedule_1x.py',
|
| 5 |
+
'../_base_/default_runtime.py'
|
| 6 |
+
]
|
| 7 |
+
|
| 8 |
+
model = dict(
|
| 9 |
+
backbone=dict(
|
| 10 |
+
_delete_=True,
|
| 11 |
+
type='SwinTransformer',
|
| 12 |
+
embed_dims=96,
|
| 13 |
+
depths=[2, 2, 6, 2],
|
| 14 |
+
num_heads=[3, 6, 12, 24],
|
| 15 |
+
window_size=7,
|
| 16 |
+
mlp_ratio=4,
|
| 17 |
+
qkv_bias=True,
|
| 18 |
+
qk_scale=None,
|
| 19 |
+
drop_rate=0.,
|
| 20 |
+
attn_drop_rate=0.,
|
| 21 |
+
drop_path_rate=0.2,
|
| 22 |
+
patch_norm=True,
|
| 23 |
+
out_indices=(0, 1, 2, 3),
|
| 24 |
+
with_cp=False,
|
| 25 |
+
convert_weights=True,
|
| 26 |
+
),
|
| 27 |
+
neck=dict(in_channels=[96, 192, 384, 768]),
|
| 28 |
+
roi_head=dict(
|
| 29 |
+
bbox_head=dict(num_classes=1),
|
| 30 |
+
mask_head=dict(num_classes=1)
|
| 31 |
+
)
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
dataset_type = 'KvasirDataset'
|
| 35 |
+
data_root = '../../data/Kvasir-SEG'
|
| 36 |
+
img_norm_cfg = dict(
|
| 37 |
+
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
| 38 |
+
|
| 39 |
+
train_pipeline = [
|
| 40 |
+
dict(type='LoadImageFromFile'),
|
| 41 |
+
dict(type='LoadAnnotations', with_bbox=True, with_mask=False),
|
| 42 |
+
dict(type='LoadMasksFromAnn'),
|
| 43 |
+
dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
|
| 44 |
+
dict(type='RandomFlip', flip_ratio=0.5),
|
| 45 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 46 |
+
dict(type='Pad', size_divisor=32),
|
| 47 |
+
dict(type='DefaultFormatBundle'),
|
| 48 |
+
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']),
|
| 49 |
+
]
|
| 50 |
+
test_pipeline = [
|
| 51 |
+
dict(type='LoadImageFromFile'),
|
| 52 |
+
dict(
|
| 53 |
+
type='MultiScaleFlipAug',
|
| 54 |
+
img_scale=(1333, 800),
|
| 55 |
+
flip=False,
|
| 56 |
+
transforms=[
|
| 57 |
+
dict(type='Resize', keep_ratio=True),
|
| 58 |
+
dict(type='RandomFlip'),
|
| 59 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 60 |
+
dict(type='Pad', size_divisor=32),
|
| 61 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 62 |
+
dict(type='Collect', keys=['img']),
|
| 63 |
+
])
|
| 64 |
+
]
|
| 65 |
+
|
| 66 |
+
data = dict(
|
| 67 |
+
samples_per_gpu=1,
|
| 68 |
+
workers_per_gpu=4,
|
| 69 |
+
train=dict(
|
| 70 |
+
type=dataset_type,
|
| 71 |
+
img_prefix=data_root,
|
| 72 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 73 |
+
pipeline=train_pipeline),
|
| 74 |
+
val=dict(
|
| 75 |
+
type=dataset_type,
|
| 76 |
+
img_prefix=data_root,
|
| 77 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 78 |
+
pipeline=test_pipeline),
|
| 79 |
+
test=dict(
|
| 80 |
+
type=dataset_type,
|
| 81 |
+
img_prefix=data_root,
|
| 82 |
+
ann_file=data_root + '/kavsir_bboxes.json',
|
| 83 |
+
pipeline=test_pipeline))
|
| 84 |
+
|
| 85 |
+
evaluation = dict(metric='mAP', save_best='mAP')
|
| 86 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0001, betas=(0.9, 0.999), weight_decay=0.05,
|
| 87 |
+
paramwise_cfg=dict(custom_keys={'absolute_pos_embed': dict(decay_mult=0.),
|
| 88 |
+
'relative_position_bias_table': dict(decay_mult=0.),
|
| 89 |
+
'norm': dict(decay_mult=0.)}))
|
| 90 |
+
|
| 91 |
+
# We use init_cfg for backbone loading, but can also set load_from for the whole detector
|
| 92 |
+
load_from = 'https://download.openmmlab.com/mmdetection/v2.0/swin/mask_rcnn_swin-t-p4-w7_fpn_1x_coco/mask_rcnn_swin-t-p4-w7_fpn_1x_coco_20210902_120937-9d6b7cfa.pth'
|
configs/mask_rcnn_geovig_b_fpn_1x_coco.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
_base_ = [
|
| 3 |
+
'_base_/models/mask_rcnn_r50_fpn.py',
|
| 4 |
+
'_base_/datasets/coco_instance.py',
|
| 5 |
+
'_base_/schedules/schedule_1x.py',
|
| 6 |
+
'_base_/default_runtime.py'
|
| 7 |
+
]
|
| 8 |
+
|
| 9 |
+
model = dict(
|
| 10 |
+
backbone=dict(
|
| 11 |
+
_delete_=True,
|
| 12 |
+
type='geovig_b_feat',
|
| 13 |
+
style='pytorch',
|
| 14 |
+
pretrained='../../geovig_b_5e4_8G_300_82_38/checkpoint.pth',
|
| 15 |
+
use_detect_adapter=True, # Enable new adapter
|
| 16 |
+
init_cfg=None
|
| 17 |
+
),
|
| 18 |
+
neck=dict(
|
| 19 |
+
type='FPN',
|
| 20 |
+
in_channels=[80, 160, 320, 512],
|
| 21 |
+
out_channels=256,
|
| 22 |
+
num_outs=5,
|
| 23 |
+
norm_cfg=dict(type='GN', num_groups=32, requires_grad=True) # GroupNorm Stability
|
| 24 |
+
))
|
| 25 |
+
|
| 26 |
+
# optimizer
|
| 27 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05) # Lower LR + AdamW
|
| 28 |
+
optimizer_config = dict(_delete_=True, grad_clip=dict(max_norm=1.0, norm_type=2)) # Gradient Clipping
|
| 29 |
+
|
| 30 |
+
# Evaluation: Disable BN buffer broadcast (redundant for frozen BN and causes sync hangs)
|
| 31 |
+
# Evaluation: DISABLED during training to prevent DDP sync timeouts. Validate offline.
|
| 32 |
+
evaluation = dict(interval=100, metric=['bbox', 'segm'])
|
configs/mask_rcnn_geovig_m_fpn_1x_coco.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
_base_ = [
|
| 3 |
+
'_base_/models/mask_rcnn_r50_fpn.py',
|
| 4 |
+
'_base_/datasets/coco_instance.py',
|
| 5 |
+
'_base_/schedules/schedule_1x.py',
|
| 6 |
+
'_base_/default_runtime.py'
|
| 7 |
+
]
|
| 8 |
+
|
| 9 |
+
model = dict(
|
| 10 |
+
backbone=dict(
|
| 11 |
+
_delete_=True,
|
| 12 |
+
type='geovig_m_feat',
|
| 13 |
+
style='pytorch',
|
| 14 |
+
pretrained='../../geovig_m_5e4_8G_300_80_70/checkpoint.pth',
|
| 15 |
+
use_detect_adapter=True, # Enable new adapter
|
| 16 |
+
init_cfg=None
|
| 17 |
+
),
|
| 18 |
+
neck=dict(
|
| 19 |
+
type='FPN',
|
| 20 |
+
in_channels=[64, 128, 256, 384],
|
| 21 |
+
out_channels=256,
|
| 22 |
+
num_outs=5,
|
| 23 |
+
norm_cfg=dict(type='GN', num_groups=32, requires_grad=True) # GroupNorm Stability
|
| 24 |
+
))
|
| 25 |
+
|
| 26 |
+
# optimizer
|
| 27 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0001, weight_decay=0.05) # Lower LR + AdamW
|
| 28 |
+
optimizer_config = dict(_delete_=True, grad_clip=dict(max_norm=1.0, norm_type=2)) # Gradient Clipping
|
configs/mask_rcnn_mobilevig_b_fpn_1x_coco.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'_base_/models/mask_rcnn_r50_fpn.py',
|
| 3 |
+
'_base_/datasets/coco_instance.py',
|
| 4 |
+
'_base_/schedules/schedule_1x.py',
|
| 5 |
+
'_base_/default_runtime.py'
|
| 6 |
+
]
|
| 7 |
+
model = dict(
|
| 8 |
+
backbone=dict(
|
| 9 |
+
type='mobilevig_b_feat',
|
| 10 |
+
style='pytorch',
|
| 11 |
+
init_cfg=dict(
|
| 12 |
+
type='Pretrained',
|
| 13 |
+
checkpoint='../Pretrained_Models_MobileViG/MobileViG_B_82_6.pth.tar',
|
| 14 |
+
),
|
| 15 |
+
),
|
| 16 |
+
neck=dict(
|
| 17 |
+
type='FPN',
|
| 18 |
+
in_channels=[42, 84, 240, 464],
|
| 19 |
+
out_channels=256,
|
| 20 |
+
num_outs=5))
|
| 21 |
+
# optimizer
|
| 22 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0002, weight_decay=0.0001)
|
| 23 |
+
optimizer_config = dict(grad_clip=None)
|
| 24 |
+
|
configs/mask_rcnn_mobilevig_m_fpn_1x_coco.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'_base_/models/mask_rcnn_r50_fpn.py',
|
| 3 |
+
'_base_/datasets/coco_instance.py',
|
| 4 |
+
'_base_/schedules/schedule_1x.py',
|
| 5 |
+
'_base_/default_runtime.py'
|
| 6 |
+
]
|
| 7 |
+
# optimizer
|
| 8 |
+
model = dict(
|
| 9 |
+
backbone=dict(
|
| 10 |
+
type='mobilevig_m_feat',
|
| 11 |
+
style='pytorch',
|
| 12 |
+
init_cfg=dict(
|
| 13 |
+
type='Pretrained',
|
| 14 |
+
checkpoint='../../geovig_m_5e4_8G_300_80_70/MobileViG_M_80_7.pth.tar',
|
| 15 |
+
),
|
| 16 |
+
),
|
| 17 |
+
neck=dict(
|
| 18 |
+
type='FPN',
|
| 19 |
+
in_channels=[42, 84, 224, 400],
|
| 20 |
+
out_channels=256,
|
| 21 |
+
num_outs=5))
|
| 22 |
+
# optimizer
|
| 23 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0002, weight_decay=0.05)
|
| 24 |
+
optimizer_config = dict(grad_clip=None)
|
| 25 |
+
|
configs/mask_rcnn_mobilevigv2_b_fpn_1x_coco.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'_base_/models/mask_rcnn_r50_fpn.py',
|
| 3 |
+
'_base_/datasets/coco_instance.py',
|
| 4 |
+
'_base_/schedules/schedule_1x.py',
|
| 5 |
+
'_base_/default_runtime.py'
|
| 6 |
+
]
|
| 7 |
+
model = dict(
|
| 8 |
+
backbone=dict(
|
| 9 |
+
type='mobilevigv2_b_feat',
|
| 10 |
+
style='pytorch',
|
| 11 |
+
init_cfg=dict(
|
| 12 |
+
type='Pretrained',
|
| 13 |
+
checkpoint='../Results/MobileViG_V2_B_Detection.pth',
|
| 14 |
+
),
|
| 15 |
+
),
|
| 16 |
+
neck=dict(
|
| 17 |
+
type='FPN',
|
| 18 |
+
in_channels=[64, 128, 256, 512],
|
| 19 |
+
out_channels=256,
|
| 20 |
+
num_outs=5))
|
| 21 |
+
# optimizer
|
| 22 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0002, weight_decay=0.0001)
|
| 23 |
+
optimizer_config = dict(grad_clip=None)
|
| 24 |
+
|
configs/mask_rcnn_mobilevigv2_m_fpn_1x_coco.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
_base_ = [
|
| 2 |
+
'_base_/models/mask_rcnn_r50_fpn.py',
|
| 3 |
+
'_base_/datasets/coco_instance.py',
|
| 4 |
+
'_base_/schedules/schedule_1x.py',
|
| 5 |
+
'_base_/default_runtime.py'
|
| 6 |
+
]
|
| 7 |
+
# optimizer
|
| 8 |
+
model = dict(
|
| 9 |
+
backbone=dict(
|
| 10 |
+
type='mobilevigv2_m_feat',
|
| 11 |
+
style='pytorch',
|
| 12 |
+
init_cfg=dict(
|
| 13 |
+
type='Pretrained',
|
| 14 |
+
checkpoint='../Results/MobileViG_V2_M_Detection.pth',
|
| 15 |
+
),
|
| 16 |
+
),
|
| 17 |
+
neck=dict(
|
| 18 |
+
type='FPN',
|
| 19 |
+
in_channels=[32, 64, 192, 384],
|
| 20 |
+
out_channels=256,
|
| 21 |
+
num_outs=5))
|
| 22 |
+
# optimizer
|
| 23 |
+
optimizer = dict(_delete_=True, type='AdamW', lr=0.0002, weight_decay=0.05)
|
| 24 |
+
optimizer_config = dict(grad_clip=None)
|
| 25 |
+
|
configs/s
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" ImageNet Training Script
|
| 2 |
+
#train.py from vig_pytorch
|
| 3 |
+
|
| 4 |
+
This is intended to be a lean and easily modifiable ImageNet training script that reproduces ImageNet
|
| 5 |
+
training results with some of the latest networks and training techniques. It favours canonical PyTorch
|
| 6 |
+
and standard Python style over trying to be able to 'do it all.' That said, it offers quite a few speed
|
| 7 |
+
and training result improvements over the usual PyTorch example scripts. Repurpose as you see fit.
|
| 8 |
+
|
| 9 |
+
This script was started from an early version of the PyTorch ImageNet example
|
| 10 |
+
(https://github.com/pytorch/examples/tree/master/imagenet)
|
| 11 |
+
|
| 12 |
+
Hacked together by / Copyright 2020 Ross Wightman (https://github.com/rwightman)
|
| 13 |
+
|
| 14 |
+
Modified from the training script used for the Vision GNN model found in:
|
| 15 |
+
@misc{
|
| 16 |
+
https://doi.org/10.48550/arxiv.2206.00272,
|
| 17 |
+
doi = {10.48550/ARXIV.2206.00272},
|
| 18 |
+
url = {https://arxiv.org/abs/2206.00272},
|
| 19 |
+
author = {Han, Kai and Wang, Yunhe and Guo, Jianyuan and Tang, Yehui and Wu, Enhua},
|
| 20 |
+
keywords = {Computer Vision and Pattern Recognition (cs.CV), FOS: Computer and information sciences, FOS: Computer and information sciences},
|
| 21 |
+
title = {Vision GNN: An Image is Worth Graph of Nodes},
|
| 22 |
+
publisher = {arXiv},
|
| 23 |
+
year = {2022},
|
| 24 |
+
copyright = {arXiv.org perpetual, non-exclusive license}
|
| 25 |
+
}
|
| 26 |
+
"""
|
| 27 |
+
# Copyright (c) OpenMMLab. All rights reserved.
|
| 28 |
+
import warnings
|
| 29 |
+
warnings.filterwarnings('ignore')
|
| 30 |
+
import argparse
|
| 31 |
+
import time
|
| 32 |
+
import yaml
|
| 33 |
+
import os
|
| 34 |
+
import logging
|
| 35 |
+
from collections import OrderedDict
|
| 36 |
+
from contextlib import suppress
|
| 37 |
+
from datetime import datetime
|
| 38 |
+
|
| 39 |
+
import copy
|
| 40 |
+
import os.path as osp
|
| 41 |
+
|
| 42 |
+
import torch
|
| 43 |
+
import torch.nn as nn
|
| 44 |
+
import torchvision.utils
|
| 45 |
+
from torch.nn.parallel import DistributedDataParallel
|
| 46 |
+
import timm
|
| 47 |
+
|
| 48 |
+
from timm.data import ImageDataset, resolve_data_config, Mixup, FastCollateMixup, AugMixDataset #create_loader
|
| 49 |
+
# from timm.models import create_model, resume_checkpoint, convert_splitbn_model
|
| 50 |
+
from timm.utils import *
|
| 51 |
+
from timm.loss import LabelSmoothingCrossEntropy, SoftTargetCrossEntropy, JsdCrossEntropy
|
| 52 |
+
from timm.optim import create_optimizer
|
| 53 |
+
from timm.scheduler import create_scheduler
|
| 54 |
+
from timm.utils import NativeScaler
|
| 55 |
+
|
| 56 |
+
import mmcv
|
| 57 |
+
from mmcv import Config, DictAction
|
| 58 |
+
from mmcv.runner import get_dist_info, init_dist
|
| 59 |
+
from mmcv.utils import get_git_hash
|
| 60 |
+
|
| 61 |
+
from mmdet import __version__
|
| 62 |
+
from mmdet.apis import init_random_seed, set_random_seed # , train_detector
|
| 63 |
+
from mmdet.datasets import build_dataset
|
| 64 |
+
from mmdet.models import build_detector
|
| 65 |
+
from mmdet.utils import (collect_env, get_device, get_root_logger,
|
| 66 |
+
setup_multi_processes, update_data_root)
|
| 67 |
+
|
| 68 |
+
from mmdet_custom.apis.train import train_detector
|
| 69 |
+
import mmcv_custom.runner.epoch_based_runner
|
| 70 |
+
import mmcv_custom.runner.optimizer
|
| 71 |
+
|
| 72 |
+
import mobilevigv2_backbone
|
| 73 |
+
import geovig_backbone
|
| 74 |
+
|
| 75 |
+
# # 1. Disable TF32 (H100 uses this by default, but it crashes older cuBLAS)
|
| 76 |
+
# torch.backends.cuda.matmul.allow_tf32 = False
|
| 77 |
+
# torch.backends.cudnn.allow_tf32 = False
|
| 78 |
+
|
| 79 |
+
# # 2. Force cuBLAS to use a specific, stable workspace configuration
|
| 80 |
+
# # This must match the environment variable you set earlier
|
| 81 |
+
# import os
|
| 82 |
+
# os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8'
|
| 83 |
+
|
| 84 |
+
# # 3. Disable the specific cuBLASLt heuristics that cause Hopper crashes
|
| 85 |
+
# os.environ['DISABLE_ADDMM_CUDA_LT'] = '1'
|
| 86 |
+
|
| 87 |
+
# # 4. Optional: Force deterministic math (prevents cuBLAS from choosing risky 'fast' paths)
|
| 88 |
+
# torch.use_deterministic_algorithms(True, warn_only=True)
|
| 89 |
+
|
| 90 |
+
parser = argparse.ArgumentParser(description='Train a detector')
|
| 91 |
+
parser.add_argument('config', help='train config file path')
|
| 92 |
+
parser.add_argument('--work-dir', help='the dir to save logs and models')
|
| 93 |
+
parser.add_argument('--mobilevigv2_model', default='mobilevigv2_m', type=str, metavar='MODEL',
|
| 94 |
+
help='Name of model to train (default: "mobilevigv2_m"')
|
| 95 |
+
parser.add_argument(
|
| 96 |
+
'--resume-from', help='the checkpoint file to resume from')
|
| 97 |
+
parser.add_argument(
|
| 98 |
+
'--auto-resume',
|
| 99 |
+
action='store_true',
|
| 100 |
+
help='resume from the latest checkpoint automatically')
|
| 101 |
+
parser.add_argument(
|
| 102 |
+
'--no-validate',
|
| 103 |
+
action='store_true',
|
| 104 |
+
help='whether not to evaluate the checkpoint during training')
|
| 105 |
+
group_gpus = parser.add_mutually_exclusive_group()
|
| 106 |
+
group_gpus.add_argument(
|
| 107 |
+
'--gpus',
|
| 108 |
+
type=int,
|
| 109 |
+
help='(Deprecated, please use --gpu-id) number of gpus to use '
|
| 110 |
+
'(only applicable to non-distributed training)')
|
| 111 |
+
group_gpus.add_argument(
|
| 112 |
+
'--gpu-ids',
|
| 113 |
+
type=int,
|
| 114 |
+
nargs='+',
|
| 115 |
+
help='(Deprecated, please use --gpu-id) ids of gpus to use '
|
| 116 |
+
'(only applicable to non-distributed training)')
|
| 117 |
+
group_gpus.add_argument(
|
| 118 |
+
'--gpu-id',
|
| 119 |
+
type=int,
|
| 120 |
+
default=0,
|
| 121 |
+
help='id of gpu to use '
|
| 122 |
+
'(only applicable to non-distributed training)')
|
| 123 |
+
parser.add_argument('--seed', type=int, default=None, help='random seed')
|
| 124 |
+
parser.add_argument(
|
| 125 |
+
'--diff-seed',
|
| 126 |
+
action='store_true',
|
| 127 |
+
help='Whether or not set different seeds for different ranks')
|
| 128 |
+
parser.add_argument(
|
| 129 |
+
'--deterministic',
|
| 130 |
+
action='store_true',
|
| 131 |
+
help='whether to set deterministic options for CUDNN backend.')
|
| 132 |
+
parser.add_argument(
|
| 133 |
+
'--options',
|
| 134 |
+
nargs='+',
|
| 135 |
+
action=DictAction,
|
| 136 |
+
help='override some settings in the used config, the key-value pair '
|
| 137 |
+
'in xxx=yyy format will be merged into config file (deprecate), '
|
| 138 |
+
'change to --cfg-options instead.')
|
| 139 |
+
parser.add_argument(
|
| 140 |
+
'--cfg-options',
|
| 141 |
+
nargs='+',
|
| 142 |
+
action=DictAction,
|
| 143 |
+
help='override some settings in the used config, the key-value pair '
|
| 144 |
+
'in xxx=yyy format will be merged into config file. If the value to '
|
| 145 |
+
'be overwritten is a list, it should be like key="[a,b]" or key=a,b '
|
| 146 |
+
'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" '
|
| 147 |
+
'Note that the quotation marks are necessary and that no white space '
|
| 148 |
+
'is allowed.')
|
| 149 |
+
parser.add_argument(
|
| 150 |
+
'--launcher',
|
| 151 |
+
choices=['none', 'pytorch', 'slurm', 'mpi'],
|
| 152 |
+
default='none',
|
| 153 |
+
help='job launcher')
|
| 154 |
+
parser.add_argument('--local_rank', type=int, default=0)
|
| 155 |
+
parser.add_argument(
|
| 156 |
+
'--auto-scale-lr',
|
| 157 |
+
action='store_true',
|
| 158 |
+
help='enable automatically scaling LR.')
|
| 159 |
+
|
| 160 |
+
args = parser.parse_args()
|
| 161 |
+
print(args)
|
| 162 |
+
print("\n mobilevigv2_model = " + str(args.mobilevigv2_model))
|
| 163 |
+
|
| 164 |
+
def parse_args():
|
| 165 |
+
args = parser.parse_args()
|
| 166 |
+
if 'LOCAL_RANK' not in os.environ:
|
| 167 |
+
os.environ['LOCAL_RANK'] = str(args.local_rank)
|
| 168 |
+
|
| 169 |
+
if args.options and args.cfg_options:
|
| 170 |
+
raise ValueError(
|
| 171 |
+
'--options and --cfg-options cannot be both '
|
| 172 |
+
'specified, --options is deprecated in favor of --cfg-options')
|
| 173 |
+
if args.options:
|
| 174 |
+
warnings.warn('--options is deprecated in favor of --cfg-options')
|
| 175 |
+
args.cfg_options = args.options
|
| 176 |
+
|
| 177 |
+
return args
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def main():
|
| 181 |
+
args = parse_args()
|
| 182 |
+
|
| 183 |
+
cfg = Config.fromfile(args.config)
|
| 184 |
+
|
| 185 |
+
# update data root according to MMDET_DATASETS
|
| 186 |
+
update_data_root(cfg)
|
| 187 |
+
|
| 188 |
+
if args.cfg_options is not None:
|
| 189 |
+
cfg.merge_from_dict(args.cfg_options)
|
| 190 |
+
|
| 191 |
+
if args.auto_scale_lr:
|
| 192 |
+
if 'auto_scale_lr' in cfg and \
|
| 193 |
+
'enable' in cfg.auto_scale_lr and \
|
| 194 |
+
'base_batch_size' in cfg.auto_scale_lr:
|
| 195 |
+
cfg.auto_scale_lr.enable = True
|
| 196 |
+
else:
|
| 197 |
+
warnings.warn('Can not find "auto_scale_lr" or '
|
| 198 |
+
'"auto_scale_lr.enable" or '
|
| 199 |
+
'"auto_scale_lr.base_batch_size" in your'
|
| 200 |
+
' configuration file. Please update all the '
|
| 201 |
+
'configuration files to mmdet >= 2.24.1.')
|
| 202 |
+
|
| 203 |
+
# set multi-process settings
|
| 204 |
+
setup_multi_processes(cfg)
|
| 205 |
+
|
| 206 |
+
# set cudnn_benchmark
|
| 207 |
+
if cfg.get('cudnn_benchmark', False):
|
| 208 |
+
torch.backends.cudnn.benchmark = True
|
| 209 |
+
|
| 210 |
+
# work_dir is determined in this priority: CLI > segment in file > filename
|
| 211 |
+
if args.work_dir is not None:
|
| 212 |
+
# update configs according to CLI args if args.work_dir is not None
|
| 213 |
+
cfg.work_dir = args.work_dir
|
| 214 |
+
elif cfg.get('work_dir', None) is None:
|
| 215 |
+
# use config filename as default work_dir if cfg.work_dir is None
|
| 216 |
+
cfg.work_dir = osp.join('./work_dirs',
|
| 217 |
+
osp.splitext(osp.basename(args.config))[0])
|
| 218 |
+
if args.resume_from is not None:
|
| 219 |
+
cfg.resume_from = args.resume_from
|
| 220 |
+
cfg.auto_resume = args.auto_resume
|
| 221 |
+
if args.gpus is not None:
|
| 222 |
+
cfg.gpu_ids = range(1)
|
| 223 |
+
warnings.warn('`--gpus` is deprecated because we only support '
|
| 224 |
+
'single GPU mode in non-distributed training. '
|
| 225 |
+
'Use `gpus=1` now.')
|
| 226 |
+
if args.gpu_ids is not None:
|
| 227 |
+
cfg.gpu_ids = args.gpu_ids[0:1]
|
| 228 |
+
warnings.warn('`--gpu-ids` is deprecated, please use `--gpu-id`. '
|
| 229 |
+
'Because we only support single GPU mode in '
|
| 230 |
+
'non-distributed training. Use the first GPU '
|
| 231 |
+
'in `gpu_ids` now.')
|
| 232 |
+
if args.gpus is None and args.gpu_ids is None:
|
| 233 |
+
cfg.gpu_ids = [args.gpu_id]
|
| 234 |
+
|
| 235 |
+
# init distributed env first, since logger depends on the dist info.
|
| 236 |
+
if args.launcher == 'none':
|
| 237 |
+
distributed = False
|
| 238 |
+
print("NOT DISTRIBUTED LAUNCH")
|
| 239 |
+
else:
|
| 240 |
+
print("DIST LAUNCH")
|
| 241 |
+
distributed = True
|
| 242 |
+
init_dist(args.launcher, **cfg.dist_params)
|
| 243 |
+
# re-set gpu_ids with distributed training mode
|
| 244 |
+
_, world_size = get_dist_info()
|
| 245 |
+
cfg.gpu_ids = range(world_size)
|
| 246 |
+
print ("GPU IDS")
|
| 247 |
+
print(cfg.gpu_ids)
|
| 248 |
+
|
| 249 |
+
# create work_dir
|
| 250 |
+
mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir))
|
| 251 |
+
# dump config
|
| 252 |
+
cfg.dump(osp.join(cfg.work_dir, osp.basename(args.config)))
|
| 253 |
+
# init the logger before other steps
|
| 254 |
+
timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime())
|
| 255 |
+
log_file = osp.join(cfg.work_dir, f'{timestamp}.log')
|
| 256 |
+
logger = get_root_logger(log_file=log_file, log_level=cfg.log_level)
|
| 257 |
+
|
| 258 |
+
# init the meta dict to record some important information such as
|
| 259 |
+
# environment info and seed, which will be logged
|
| 260 |
+
meta = dict()
|
| 261 |
+
# log env info
|
| 262 |
+
env_info_dict = collect_env()
|
| 263 |
+
env_info = '\n'.join([(f'{k}: {v}') for k, v in env_info_dict.items()])
|
| 264 |
+
dash_line = '-' * 60 + '\n'
|
| 265 |
+
logger.info('Environment info:\n' + dash_line + env_info + '\n' +
|
| 266 |
+
dash_line)
|
| 267 |
+
meta['env_info'] = env_info
|
| 268 |
+
meta['config'] = cfg.pretty_text
|
| 269 |
+
# log some basic info
|
| 270 |
+
logger.info(f'Distributed training: {distributed}')
|
| 271 |
+
logger.info(f'Config:\n{cfg.pretty_text}')
|
| 272 |
+
|
| 273 |
+
cfg.device = get_device()
|
| 274 |
+
# set random seeds
|
| 275 |
+
seed = init_random_seed(args.seed, device=cfg.device)
|
| 276 |
+
seed = seed + dist.get_rank() if args.diff_seed else seed
|
| 277 |
+
logger.info(f'Set random seed to {seed}, '
|
| 278 |
+
f'deterministic: {args.deterministic}')
|
| 279 |
+
set_random_seed(seed, deterministic=args.deterministic)
|
| 280 |
+
cfg.seed = seed
|
| 281 |
+
meta['seed'] = seed
|
| 282 |
+
meta['exp_name'] = osp.basename(args.config)
|
| 283 |
+
|
| 284 |
+
model = build_detector(
|
| 285 |
+
cfg.model,
|
| 286 |
+
train_cfg=cfg.get('train_cfg'),
|
| 287 |
+
test_cfg=cfg.get('test_cfg'))
|
| 288 |
+
# drop_connect_rate=0.1,
|
| 289 |
+
# drop_path_rate=0.1,
|
| 290 |
+
# drop_block_rate=0.1)
|
| 291 |
+
model.init_weights()
|
| 292 |
+
|
| 293 |
+
print("\n\n\nCONFIG OF MODEL: \n" + str(cfg.model) + "\n\n\n")
|
| 294 |
+
|
| 295 |
+
datasets = [build_dataset(cfg.data.train)]
|
| 296 |
+
if len(cfg.workflow) == 2:
|
| 297 |
+
val_dataset = copy.deepcopy(cfg.data.val)
|
| 298 |
+
val_dataset.pipeline = cfg.data.train.pipeline
|
| 299 |
+
datasets.append(build_dataset(val_dataset))
|
| 300 |
+
if cfg.checkpoint_config is not None:
|
| 301 |
+
# save mmdet version, config file content and class names in
|
| 302 |
+
# checkpoints as meta data
|
| 303 |
+
cfg.checkpoint_config.meta = dict(
|
| 304 |
+
mmdet_version=__version__ + get_git_hash()[:7],
|
| 305 |
+
CLASSES=datasets[0].CLASSES)
|
| 306 |
+
# add an attribute for visualization convenience
|
| 307 |
+
model.CLASSES = datasets[0].CLASSES
|
| 308 |
+
|
| 309 |
+
print("\n\n\nTRAIN DETECTOR CONFIG OF MODEL: \n" + str(cfg) + "\n\n\n")
|
| 310 |
+
|
| 311 |
+
print("\n\n\TRAIN DETECTOR MODEL: \n" + str(model) + "\n\n\n")
|
| 312 |
+
|
| 313 |
+
train_detector(
|
| 314 |
+
model,
|
| 315 |
+
datasets,
|
| 316 |
+
cfg,
|
| 317 |
+
distributed=distributed,
|
| 318 |
+
validate=(not args.no_validate),
|
| 319 |
+
timestamp=timestamp,
|
| 320 |
+
meta=meta)
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
if __name__ == '__main__':
|
| 324 |
+
main()
|
examples/000000032081.jpg
ADDED
|
Git LFS Details
|
examples/000000045472.jpg
ADDED
|
Git LFS Details
|
examples/0c2550a23b8a0f29a7575de8c61690d3c31bc897dd5ba66caec201d201a278c2.png
ADDED
|
Git LFS Details
|
examples/cat.png
ADDED
|
Git LFS Details
|
examples/cju5y84q3mdv50817eyp82xf3.jpg
ADDED
|
Git LFS Details
|
examples/n03394916_14162.JPEG
ADDED
|
|
Git LFS Details
|
examples/n03417042_2960.JPEG
ADDED
|
|
Git LFS Details
|
geovig.py
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import nn
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
from torch.nn import Sequential as Seq
|
| 5 |
+
import logging
|
| 6 |
+
|
| 7 |
+
from timm.data import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD
|
| 8 |
+
try:
|
| 9 |
+
from timm.layers import DropPath # timm >= 0.9
|
| 10 |
+
except ImportError:
|
| 11 |
+
from timm.models.layers import DropPath # timm 0.6.x
|
| 12 |
+
from timm.models import register_model
|
| 13 |
+
|
| 14 |
+
'''
|
| 15 |
+
@article{han2022vision,
|
| 16 |
+
title={Vision GNN: An Image is Worth Graph of Nodes},
|
| 17 |
+
author={Han, Kai and Wang, Yunhe and Guo, Jianyuan and Tang, Yehui and Wu, Enhua},
|
| 18 |
+
journal={arXiv preprint arXiv:2206.00272},
|
| 19 |
+
year={2022}
|
| 20 |
+
}
|
| 21 |
+
'''
|
| 22 |
+
|
| 23 |
+
def _cfg(url='', **kwargs):
|
| 24 |
+
return {
|
| 25 |
+
'url': url,
|
| 26 |
+
'num_classes': 1000, 'input_size': (3, 224, 224), 'pool_size': None,
|
| 27 |
+
'crop_pct': .9, 'interpolation': 'bicubic',
|
| 28 |
+
'mean': IMAGENET_DEFAULT_MEAN, 'std': IMAGENET_DEFAULT_STD,
|
| 29 |
+
'classifier': 'head',
|
| 30 |
+
**kwargs
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
default_cfgs = {
|
| 35 |
+
'mobilevig': _cfg(crop_pct=0.9, mean=IMAGENET_DEFAULT_MEAN, std=IMAGENET_DEFAULT_STD)
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class Stem(nn.Module):
|
| 40 |
+
def __init__(self, input_dim, output_dim, activation=nn.GELU):
|
| 41 |
+
super(Stem, self).__init__()
|
| 42 |
+
self.stem = nn.Sequential(
|
| 43 |
+
nn.Conv2d(input_dim, output_dim // 2, kernel_size=3, stride=2, padding=1),
|
| 44 |
+
nn.BatchNorm2d(output_dim // 2),
|
| 45 |
+
nn.GELU(),
|
| 46 |
+
nn.Conv2d(output_dim // 2, output_dim, kernel_size=3, stride=2, padding=1),
|
| 47 |
+
nn.BatchNorm2d(output_dim),
|
| 48 |
+
nn.GELU()
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
def forward(self, x):
|
| 52 |
+
return self.stem(x)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
class MLP(nn.Module):
|
| 56 |
+
"""
|
| 57 |
+
Implementation of MLP with 1*1 convolutions.
|
| 58 |
+
Input: tensor with shape [B, C, H, W]
|
| 59 |
+
"""
|
| 60 |
+
|
| 61 |
+
def __init__(self, in_features, hidden_features=None,
|
| 62 |
+
out_features=None, drop=0., mid_conv=False):
|
| 63 |
+
super().__init__()
|
| 64 |
+
out_features = out_features or in_features
|
| 65 |
+
hidden_features = hidden_features or in_features
|
| 66 |
+
self.mid_conv = mid_conv
|
| 67 |
+
self.fc1 = nn.Conv2d(in_features, hidden_features, 1)
|
| 68 |
+
self.act = nn.GELU()
|
| 69 |
+
self.fc2 = nn.Conv2d(hidden_features, out_features, 1)
|
| 70 |
+
self.drop = nn.Dropout(drop)
|
| 71 |
+
|
| 72 |
+
if self.mid_conv:
|
| 73 |
+
self.mid = nn.Conv2d(hidden_features, hidden_features, kernel_size=3, stride=1, padding=1,
|
| 74 |
+
groups=hidden_features)
|
| 75 |
+
self.mid_norm = nn.BatchNorm2d(hidden_features)
|
| 76 |
+
|
| 77 |
+
self.norm1 = nn.BatchNorm2d(hidden_features)
|
| 78 |
+
self.norm2 = nn.BatchNorm2d(out_features)
|
| 79 |
+
|
| 80 |
+
def forward(self, x):
|
| 81 |
+
x = self.fc1(x)
|
| 82 |
+
x = self.norm1(x)
|
| 83 |
+
x = self.act(x)
|
| 84 |
+
|
| 85 |
+
if self.mid_conv:
|
| 86 |
+
x_mid = self.mid(x)
|
| 87 |
+
x_mid = self.mid_norm(x_mid)
|
| 88 |
+
x = self.act(x_mid)
|
| 89 |
+
x = self.drop(x)
|
| 90 |
+
|
| 91 |
+
x = self.fc2(x)
|
| 92 |
+
x = self.norm2(x)
|
| 93 |
+
|
| 94 |
+
x = self.drop(x)
|
| 95 |
+
return x
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
class InvertedResidual(nn.Module):
|
| 99 |
+
def __init__(self, dim, mlp_ratio=4., drop=0., drop_path=0., use_layer_scale=True, layer_scale_init_value=1e-5):
|
| 100 |
+
super().__init__()
|
| 101 |
+
|
| 102 |
+
mlp_hidden_dim = int(dim * mlp_ratio)
|
| 103 |
+
self.mlp = MLP(in_features=dim, hidden_features=mlp_hidden_dim, drop=drop, mid_conv=True)
|
| 104 |
+
|
| 105 |
+
self.drop_path = DropPath(drop_path) if drop_path > 0. \
|
| 106 |
+
else nn.Identity()
|
| 107 |
+
self.use_layer_scale = use_layer_scale
|
| 108 |
+
if use_layer_scale:
|
| 109 |
+
self.layer_scale_2 = nn.Parameter(
|
| 110 |
+
layer_scale_init_value * torch.ones(dim).unsqueeze(-1).unsqueeze(-1), requires_grad=True)
|
| 111 |
+
|
| 112 |
+
def forward(self, x):
|
| 113 |
+
if self.use_layer_scale:
|
| 114 |
+
x = x + self.drop_path(self.layer_scale_2 * self.mlp(x))
|
| 115 |
+
else:
|
| 116 |
+
x = x + self.drop_path(self.mlp(x))
|
| 117 |
+
return x
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
# -------------------- New graph utilities and GeoViG additions --------------------
|
| 121 |
+
|
| 122 |
+
def img_to_graph(x):
|
| 123 |
+
"""
|
| 124 |
+
Converts (B, C, H, W) -> (B, N, C) and generates a grid adjacency (8-neighbor-ish).
|
| 125 |
+
For efficiency this returns a single edge_index shared for the batch.
|
| 126 |
+
"""
|
| 127 |
+
B, C, H, W = x.shape
|
| 128 |
+
device = x.device
|
| 129 |
+
N = H * W
|
| 130 |
+
|
| 131 |
+
# Flatten features: (B, C, H, W) -> (B, N, C)
|
| 132 |
+
x_flat = x.flatten(2).transpose(1, 2) # (B, N, C)
|
| 133 |
+
|
| 134 |
+
# Generate grid edges (simplified 8-neighbor connectivity)
|
| 135 |
+
idx = torch.arange(N, device=device).view(H, W)
|
| 136 |
+
edges = []
|
| 137 |
+
|
| 138 |
+
# Right neighbors
|
| 139 |
+
if W > 1:
|
| 140 |
+
edges.append(torch.stack([idx[:, :-1].flatten(), idx[:, 1:].flatten()], dim=0))
|
| 141 |
+
# Left (will be mirrored later)
|
| 142 |
+
# Down neighbors
|
| 143 |
+
if H > 1:
|
| 144 |
+
edges.append(torch.stack([idx[:-1, :].flatten(), idx[1:, :].flatten()], dim=0))
|
| 145 |
+
# Diagonals
|
| 146 |
+
if H > 1 and W > 1:
|
| 147 |
+
edges.append(torch.stack([idx[:-1, :-1].flatten(), idx[1:, 1:].flatten()], dim=0))
|
| 148 |
+
edges.append(torch.stack([idx[1:, :-1].flatten(), idx[:-1, 1:].flatten()], dim=0))
|
| 149 |
+
|
| 150 |
+
if len(edges) == 0:
|
| 151 |
+
edge_index = torch.zeros((2, 0), dtype=torch.long, device=device)
|
| 152 |
+
else:
|
| 153 |
+
edge_index = torch.cat(edges, dim=1)
|
| 154 |
+
# Make undirected by mirroring
|
| 155 |
+
edge_index = torch.cat([edge_index, edge_index.flip(0)], dim=1)
|
| 156 |
+
|
| 157 |
+
return x_flat, edge_index
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
class GraphMRConv(nn.Module):
|
| 161 |
+
"""
|
| 162 |
+
Graph version of Max-Relative Graph Convolution.
|
| 163 |
+
Operates on (B, N, C) and edge_index (2, E).
|
| 164 |
+
Uses PyTorch's index_reduce_ with 'amax' for true max aggregation.
|
| 165 |
+
"""
|
| 166 |
+
def __init__(self, in_channels, out_channels, concat=False):
|
| 167 |
+
super().__init__()
|
| 168 |
+
self.in_channels = in_channels
|
| 169 |
+
self.out_channels = out_channels
|
| 170 |
+
self.concat = concat
|
| 171 |
+
linear_in = 2 * in_channels if concat else in_channels
|
| 172 |
+
self.nn = nn.Sequential(
|
| 173 |
+
nn.Linear(linear_in, out_channels),
|
| 174 |
+
nn.GELU()
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
def forward(self, x, edge_index):
|
| 178 |
+
# x: (B, N, C)
|
| 179 |
+
B, N, C = x.shape
|
| 180 |
+
device = x.device
|
| 181 |
+
|
| 182 |
+
# Reshape to (B*N, C) for batched processing
|
| 183 |
+
x_flat = x.view(-1, C)
|
| 184 |
+
|
| 185 |
+
if edge_index.numel() == 0:
|
| 186 |
+
# No edges: fallback to concatenation with zeros
|
| 187 |
+
# If no edges, no aggregation, so the update is zero.
|
| 188 |
+
# We apply the non-linearity to a zero tensor to maintain shape and type.
|
| 189 |
+
return self.nn(torch.zeros_like(x_flat)).view(B, N, -1)
|
| 190 |
+
|
| 191 |
+
row, col = edge_index # (E,), (E,)
|
| 192 |
+
num_edges = row.size(0)
|
| 193 |
+
|
| 194 |
+
# Create batched edge index: (2, B*E)
|
| 195 |
+
batch_offsets = torch.arange(B, device=device) * N
|
| 196 |
+
edge_offsets = batch_offsets.view(-1, 1).repeat(1, num_edges).view(-1)
|
| 197 |
+
|
| 198 |
+
row_batch = row.repeat(B) + edge_offsets
|
| 199 |
+
col_batch = col.repeat(B) + edge_offsets
|
| 200 |
+
|
| 201 |
+
# Gather features
|
| 202 |
+
# Gather features
|
| 203 |
+
x_flat_c = x_flat.contiguous() # Ensure memory layout for scatter
|
| 204 |
+
# Optimization: Don't compute x_j - x_i explicitly (huge tensor).
|
| 205 |
+
# max(x_j - x_i) = max(x_j) - x_i
|
| 206 |
+
x_j = x_flat_c[col_batch] # (B*E, C)
|
| 207 |
+
|
| 208 |
+
# Initialize aggregation buffer
|
| 209 |
+
aggr = torch.full_like(x_flat, -1e9) # (B*N, C)
|
| 210 |
+
|
| 211 |
+
# Vectorized Max Aggregation of Neighbors
|
| 212 |
+
index_expanded = row_batch.view(-1, 1).expand(-1, C)
|
| 213 |
+
aggr.scatter_reduce_(0, index_expanded, x_j, reduce='amax', include_self=True)
|
| 214 |
+
|
| 215 |
+
# Replace -1e9 with 0 (although strictly if we subtract x_i later, we need to handle no-neighbor case carefully)
|
| 216 |
+
# If include_self=True, then x_i is included in x_j? No.
|
| 217 |
+
# include_self=True in scatter_reduce_ means "reduction includes the initial value of self (aggr)".
|
| 218 |
+
# Our initial value is -1e9. So it works as a safe initializer.
|
| 219 |
+
|
| 220 |
+
aggr = torch.where(aggr == -1e9, torch.zeros_like(aggr), aggr)
|
| 221 |
+
|
| 222 |
+
if self.concat:
|
| 223 |
+
rel = aggr - x_flat
|
| 224 |
+
out = torch.cat([x_flat, rel], dim=-1) # (B*N, 2C)
|
| 225 |
+
else:
|
| 226 |
+
out = aggr - x_flat # (B*N, C)
|
| 227 |
+
|
| 228 |
+
return self.nn(out).view(B, N, -1)
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
class GraphMobileViGBlock(nn.Module):
|
| 232 |
+
"""
|
| 233 |
+
Graph MobileViG block: GraphMRConv + FFN for (B,N,C) representations.
|
| 234 |
+
"""
|
| 235 |
+
def __init__(self, dim, drop=0.0, concat=False):
|
| 236 |
+
super().__init__()
|
| 237 |
+
self.norm1 = nn.LayerNorm(dim)
|
| 238 |
+
self.graph_conv = GraphMRConv(dim, dim, concat=concat)
|
| 239 |
+
self.norm2 = nn.LayerNorm(dim)
|
| 240 |
+
self.ffn = nn.Sequential(
|
| 241 |
+
nn.Linear(dim, dim * 4),
|
| 242 |
+
nn.GELU(),
|
| 243 |
+
nn.Linear(dim * 4, dim),
|
| 244 |
+
nn.Dropout(drop)
|
| 245 |
+
)
|
| 246 |
+
|
| 247 |
+
def forward(self, x, edge_index):
|
| 248 |
+
# x: (B, N, C)
|
| 249 |
+
shortcut = x
|
| 250 |
+
x = self.norm1(x)
|
| 251 |
+
x = self.graph_conv(x, edge_index)
|
| 252 |
+
x = x + shortcut
|
| 253 |
+
|
| 254 |
+
shortcut = x
|
| 255 |
+
x = self.norm2(x)
|
| 256 |
+
x = self.ffn(x)
|
| 257 |
+
x = x + shortcut
|
| 258 |
+
return x
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
class SpreadEdgePool(nn.Module):
|
| 262 |
+
"""
|
| 263 |
+
Geometry-Aware SpreadEdgePool: uses edge scores (diffusion distance proxy) as
|
| 264 |
+
attention weights to guide feature pooling. Ensures nodes with high structural
|
| 265 |
+
diversity contribute more to the pooled representation.
|
| 266 |
+
"""
|
| 267 |
+
def __init__(self, in_channels, ratio=0.5):
|
| 268 |
+
super().__init__()
|
| 269 |
+
self.ratio = ratio
|
| 270 |
+
self.in_channels = in_channels
|
| 271 |
+
|
| 272 |
+
def forward(self, x, edge_index):
|
| 273 |
+
"""
|
| 274 |
+
x: (B, N, C)
|
| 275 |
+
edge_index: (2, E)
|
| 276 |
+
Returns:
|
| 277 |
+
x_pooled: (B, N_new, C) with geometry-aware weighting
|
| 278 |
+
new_edge_index: (2, E_new)
|
| 279 |
+
"""
|
| 280 |
+
B, N, C = x.shape
|
| 281 |
+
device = x.device
|
| 282 |
+
num_keep = max(1, int(N * self.ratio))
|
| 283 |
+
|
| 284 |
+
# Fallback if no edges
|
| 285 |
+
if edge_index.numel() == 0:
|
| 286 |
+
x_pooled = F.adaptive_avg_pool1d(x.transpose(1, 2), num_keep).transpose(1, 2)
|
| 287 |
+
idx = torch.arange(num_keep, device=device)
|
| 288 |
+
new_edge_index = torch.stack([idx[:-1], idx[1:]], dim=0) if num_keep > 1 else torch.zeros((2, 0), dtype=torch.long, device=device)
|
| 289 |
+
return x_pooled, new_edge_index
|
| 290 |
+
|
| 291 |
+
row, col = edge_index # (E,), (E,)
|
| 292 |
+
|
| 293 |
+
# Geometry-Aware Scoring: Diffusion distance proxy via feature space
|
| 294 |
+
# Optimization: Calculate distance WITHOUT materializing (B, E, C) difference tensor.
|
| 295 |
+
# |A - B|^2 = |A|^2 + |B|^2 - 2(A . B)
|
| 296 |
+
|
| 297 |
+
# Precompute norms: (B, N, 1)
|
| 298 |
+
x_sq = (x ** 2).sum(dim=-1, keepdim=True)
|
| 299 |
+
|
| 300 |
+
# Gather norms: (B, E, 1)
|
| 301 |
+
sq_i = x_sq[:, row, :]
|
| 302 |
+
sq_j = x_sq[:, col, :]
|
| 303 |
+
|
| 304 |
+
# Gather features just for dot (this still uses memory but dot reduces it immediately)
|
| 305 |
+
x_row = x[:, row, :] # (B, E, C)
|
| 306 |
+
x_col = x[:, col, :] # (B, E, C)
|
| 307 |
+
dot = (x_row * x_col).sum(dim=-1, keepdim=True) # (B, E, 1)
|
| 308 |
+
|
| 309 |
+
# Combine
|
| 310 |
+
dist_sq = sq_i + sq_j - 2 * dot
|
| 311 |
+
dist = torch.sqrt(F.relu(dist_sq) + 1e-6).squeeze(-1) # (B, E)
|
| 312 |
+
|
| 313 |
+
# Spread score: high score = high distance = high structural diversity
|
| 314 |
+
# Original: "similarity = torch.exp(-dist)", "edge_scores = 1.0 / (similarity + 1e-6)"
|
| 315 |
+
# This means edge_scores = exp(dist). So higher distance means higher score.
|
| 316 |
+
# Using distance directly achieves this.
|
| 317 |
+
edge_scores = dist # Linear distance directly.
|
| 318 |
+
|
| 319 |
+
# Map edge scores to node importance: sum scores of incident edges
|
| 320 |
+
# Average over batch for global topology understanding
|
| 321 |
+
avg_edge_scores = edge_scores.mean(dim=0) # (E,)
|
| 322 |
+
|
| 323 |
+
# Aggregate edge importance to nodes via index_add_
|
| 324 |
+
node_importance = torch.zeros(N, device=device)
|
| 325 |
+
node_importance.index_add_(0, row, avg_edge_scores) # Sum incoming edge scores per node
|
| 326 |
+
|
| 327 |
+
# Normalize importance scores to [0, 1] via sigmoid
|
| 328 |
+
node_weights = torch.sigmoid(node_importance).view(1, N, 1) # (1, N, 1) for broadcasting
|
| 329 |
+
|
| 330 |
+
# Geometry-Aware Pooling: Weight features by their geometric importance before aggregation
|
| 331 |
+
x_weighted = x * node_weights # (B, N, C), broadcast multiply
|
| 332 |
+
|
| 333 |
+
# Adaptive pooling now prioritizes geometrically important features
|
| 334 |
+
x_pooled = F.adaptive_avg_pool1d(x_weighted.transpose(1, 2), num_keep).transpose(1, 2) # (B, num_keep, C)
|
| 335 |
+
|
| 336 |
+
# Build heuristic new adjacency: simple chain / local ring
|
| 337 |
+
idx = torch.arange(num_keep, device=device)
|
| 338 |
+
if num_keep > 1:
|
| 339 |
+
left = idx[:-1]
|
| 340 |
+
right = idx[1:]
|
| 341 |
+
new_edge_index = torch.cat([torch.stack([left, right], dim=0), torch.stack([right, left], dim=0)], dim=1)
|
| 342 |
+
else:
|
| 343 |
+
new_edge_index = torch.zeros((2, 0), dtype=torch.long, device=device)
|
| 344 |
+
|
| 345 |
+
return x_pooled, new_edge_index
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
class Downsample(nn.Module):
|
| 349 |
+
"""
|
| 350 |
+
Convolution-based downsample
|
| 351 |
+
"""
|
| 352 |
+
def __init__(self, in_dim, out_dim):
|
| 353 |
+
super().__init__()
|
| 354 |
+
self.conv = nn.Sequential(
|
| 355 |
+
nn.Conv2d(in_dim, out_dim, 3, stride=2, padding=1),
|
| 356 |
+
nn.BatchNorm2d(out_dim),
|
| 357 |
+
)
|
| 358 |
+
|
| 359 |
+
def forward(self, x):
|
| 360 |
+
x = self.conv(x)
|
| 361 |
+
return x
|
| 362 |
+
|
| 363 |
+
class GeoViG(nn.Module):
|
| 364 |
+
"""
|
| 365 |
+
Hybrid Grid->Graph GeoViG model. Can be used for classification or as a backbone for detection.
|
| 366 |
+
"""
|
| 367 |
+
def __init__(self,
|
| 368 |
+
local_blocks=[2, 2],
|
| 369 |
+
local_channels=[42, 84],
|
| 370 |
+
graph_blocks=[2],
|
| 371 |
+
graph_channels=[168],
|
| 372 |
+
num_classes=1000,
|
| 373 |
+
pool_ratio=0.5,
|
| 374 |
+
distillation=False,
|
| 375 |
+
features_only=False,
|
| 376 |
+
out_indices=None,
|
| 377 |
+
pretrained=None,
|
| 378 |
+
use_concat=False,
|
| 379 |
+
**kwargs):
|
| 380 |
+
super().__init__()
|
| 381 |
+
|
| 382 |
+
self.features_only = features_only
|
| 383 |
+
self.out_indices = out_indices
|
| 384 |
+
self.distillation = distillation
|
| 385 |
+
self.pretrained = pretrained
|
| 386 |
+
self.use_concat = use_concat
|
| 387 |
+
|
| 388 |
+
# Stage 0: stem
|
| 389 |
+
self.stem = Stem(input_dim=3, output_dim=local_channels[0])
|
| 390 |
+
|
| 391 |
+
# Grid stages
|
| 392 |
+
self.grid_stages = nn.ModuleList()
|
| 393 |
+
in_c = local_channels[0]
|
| 394 |
+
|
| 395 |
+
self.feature_info = [dict(num_chs=in_c, reduction=2, module=f'stem')]
|
| 396 |
+
|
| 397 |
+
for i, out_c in enumerate(local_channels):
|
| 398 |
+
layers = []
|
| 399 |
+
if i > 0:
|
| 400 |
+
layers.append(Downsample(in_c, out_c))
|
| 401 |
+
in_c = out_c
|
| 402 |
+
for _ in range(local_blocks[i]):
|
| 403 |
+
layers.append(InvertedResidual(dim=out_c, mlp_ratio=4, drop_path=0.0))
|
| 404 |
+
self.grid_stages.append(nn.Sequential(*layers))
|
| 405 |
+
self.feature_info.append(dict(num_chs=out_c, reduction=2**(i+2), module=f'grid_stages.{i}'))
|
| 406 |
+
|
| 407 |
+
# Transition: grid -> graph
|
| 408 |
+
self.to_graph_pool = SpreadEdgePool(in_channels=in_c, ratio=pool_ratio)
|
| 409 |
+
self.graph_dim = in_c
|
| 410 |
+
|
| 411 |
+
# Graph stages
|
| 412 |
+
self.graph_proj = nn.Linear(self.graph_dim, graph_channels[0]) if graph_channels else nn.Identity()
|
| 413 |
+
self.graph_dim = graph_channels[0] if graph_channels else self.graph_dim
|
| 414 |
+
|
| 415 |
+
self.graph_stages = nn.ModuleList()
|
| 416 |
+
for _ in range(graph_blocks[0] if len(graph_blocks) > 0 else 0):
|
| 417 |
+
self.graph_stages.append(GraphMobileViGBlock(dim=self.graph_dim, concat=use_concat))
|
| 418 |
+
|
| 419 |
+
self.feature_info.append(dict(num_chs=self.graph_dim, reduction=2**(len(local_channels)+1), module='graph_stages'))
|
| 420 |
+
|
| 421 |
+
if not self.features_only:
|
| 422 |
+
# Head for classification
|
| 423 |
+
self.norm = nn.LayerNorm(self.graph_dim)
|
| 424 |
+
self.head = nn.Linear(self.graph_dim, num_classes)
|
| 425 |
+
if self.distillation:
|
| 426 |
+
self.head_dist = nn.Linear(self.graph_dim, num_classes)
|
| 427 |
+
else:
|
| 428 |
+
# For torchvision FPN
|
| 429 |
+
self._out_channels = []
|
| 430 |
+
if self.out_indices:
|
| 431 |
+
for i in self.out_indices:
|
| 432 |
+
# The indices for feature_info are shifted by 1 compared to grid_stages
|
| 433 |
+
self._out_channels.append(self.feature_info[i+1]['num_chs'])
|
| 434 |
+
|
| 435 |
+
if self.pretrained:
|
| 436 |
+
self._load_pretrained_weights()
|
| 437 |
+
|
| 438 |
+
@property
|
| 439 |
+
def out_channels(self):
|
| 440 |
+
if not self.features_only:
|
| 441 |
+
raise ValueError("out_channels is only available in features_only mode.")
|
| 442 |
+
return self._out_channels
|
| 443 |
+
|
| 444 |
+
def _load_pretrained_weights(self):
|
| 445 |
+
"""Load pretrained weights from checkpoint."""
|
| 446 |
+
if self.pretrained and isinstance(self.pretrained, str):
|
| 447 |
+
logging.info(f"Loading pretrained weights from {self.pretrained}")
|
| 448 |
+
state_dict = torch.load(self.pretrained, map_location='cpu', weights_only=False)
|
| 449 |
+
|
| 450 |
+
if 'state_dict' in state_dict:
|
| 451 |
+
state_dict = state_dict['state_dict']
|
| 452 |
+
elif 'model' in state_dict:
|
| 453 |
+
state_dict = state_dict['model']
|
| 454 |
+
|
| 455 |
+
model_dict = self.state_dict()
|
| 456 |
+
|
| 457 |
+
# Filter out keys that are not in the current model, have different shapes, or belong to the classification head
|
| 458 |
+
pretrained_dict = {
|
| 459 |
+
k: v for k, v in state_dict.items()
|
| 460 |
+
if k in model_dict and v.shape == model_dict[k].shape and 'head' not in k
|
| 461 |
+
}
|
| 462 |
+
|
| 463 |
+
model_dict.update(pretrained_dict)
|
| 464 |
+
self.load_state_dict(model_dict, strict=False)
|
| 465 |
+
logging.info(f"Loaded {len(pretrained_dict)} keys from pretrained weights.")
|
| 466 |
+
|
| 467 |
+
def train(self, mode=True):
|
| 468 |
+
super().train(mode)
|
| 469 |
+
# For detection, keep BatchNorm in eval mode
|
| 470 |
+
if self.features_only:
|
| 471 |
+
for m in self.modules():
|
| 472 |
+
if isinstance(m, nn.BatchNorm2d):
|
| 473 |
+
m.eval()
|
| 474 |
+
|
| 475 |
+
def forward(self, x):
|
| 476 |
+
outs = {}
|
| 477 |
+
x = self.stem(x)
|
| 478 |
+
|
| 479 |
+
for i, stage in enumerate(self.grid_stages):
|
| 480 |
+
x = stage(x)
|
| 481 |
+
if self.features_only and self.out_indices and i in self.out_indices:
|
| 482 |
+
outs[str(i)] = x
|
| 483 |
+
|
| 484 |
+
# Graph processing
|
| 485 |
+
B, C, H, W = x.shape
|
| 486 |
+
x_nodes, edge_index = img_to_graph(x)
|
| 487 |
+
x_nodes, edge_index = self.to_graph_pool(x_nodes, edge_index)
|
| 488 |
+
x_nodes = self.graph_proj(x_nodes)
|
| 489 |
+
|
| 490 |
+
for block in self.graph_stages:
|
| 491 |
+
x_nodes = block(x_nodes, edge_index)
|
| 492 |
+
|
| 493 |
+
if self.features_only:
|
| 494 |
+
graph_stage_idx = len(self.grid_stages)
|
| 495 |
+
if self.out_indices and graph_stage_idx in self.out_indices:
|
| 496 |
+
num_nodes = x_nodes.shape[1]
|
| 497 |
+
new_h = int(num_nodes**0.5)
|
| 498 |
+
new_w = int(num_nodes**0.5)
|
| 499 |
+
if new_h * new_w != num_nodes:
|
| 500 |
+
new_h = new_w = int(num_nodes**0.5)
|
| 501 |
+
x_nodes = x_nodes[:, :new_h*new_w, :]
|
| 502 |
+
|
| 503 |
+
x_graph = x_nodes.transpose(1, 2).view(B, self.graph_dim, new_h, new_w)
|
| 504 |
+
outs[str(graph_stage_idx)] = x_graph
|
| 505 |
+
|
| 506 |
+
return outs
|
| 507 |
+
|
| 508 |
+
# Global readout (mean) for classification
|
| 509 |
+
x_global = x_nodes.mean(dim=1)
|
| 510 |
+
x_out = self.norm(x_global)
|
| 511 |
+
|
| 512 |
+
if self.distillation:
|
| 513 |
+
x_main = self.head(x_out)
|
| 514 |
+
x_dist = self.head_dist(x_out)
|
| 515 |
+
if self.training:
|
| 516 |
+
return x_main, x_dist
|
| 517 |
+
else:
|
| 518 |
+
return (x_main + x_dist) / 2
|
| 519 |
+
else:
|
| 520 |
+
return self.head(x_out)
|
| 521 |
+
|
| 522 |
+
|
| 523 |
+
# Register a small GeoViG variant for timm-style usage
|
| 524 |
+
@register_model
|
| 525 |
+
def geovig_ti(pretrained=False, **kwargs):
|
| 526 |
+
model = GeoViG(local_blocks=[3, 3, 3], local_channels=[42, 84, 168],
|
| 527 |
+
graph_blocks=[3], graph_channels=[256],
|
| 528 |
+
pool_ratio=0.5, use_concat=True,
|
| 529 |
+
pretrained=pretrained, **kwargs)
|
| 530 |
+
model.default_cfg = default_cfgs['mobilevig']
|
| 531 |
+
return model
|
| 532 |
+
|
| 533 |
+
@register_model
|
| 534 |
+
def geovig_s(pretrained=False, **kwargs):
|
| 535 |
+
model = GeoViG(local_blocks=[3, 3, 4], local_channels=[56, 112, 224],
|
| 536 |
+
graph_blocks=[3], graph_channels=[288],
|
| 537 |
+
pool_ratio=0.5, use_concat=False,
|
| 538 |
+
pretrained=pretrained, **kwargs)
|
| 539 |
+
model.default_cfg = default_cfgs['mobilevig']
|
| 540 |
+
return model
|
| 541 |
+
|
| 542 |
+
@register_model
|
| 543 |
+
def geovig_m(pretrained=False, **kwargs):
|
| 544 |
+
# Target: ~10M Params (MobileViG-M: 14M)
|
| 545 |
+
model = GeoViG(local_blocks=[3, 3, 9], local_channels=[64, 128, 256],
|
| 546 |
+
graph_blocks=[3], graph_channels=[384],
|
| 547 |
+
pool_ratio=0.5,
|
| 548 |
+
pretrained=pretrained, **kwargs)
|
| 549 |
+
model.default_cfg = default_cfgs['mobilevig']
|
| 550 |
+
return model
|
| 551 |
+
|
| 552 |
+
@register_model
|
| 553 |
+
def geovig_b(pretrained=False, **kwargs):
|
| 554 |
+
# Target: ~19M Params (MobileViG-B: 26.7M)
|
| 555 |
+
model = GeoViG(local_blocks=[5, 5, 9], local_channels=[80, 160, 320],
|
| 556 |
+
graph_blocks=[4], graph_channels=[512],
|
| 557 |
+
pool_ratio=0.5,
|
| 558 |
+
pretrained=pretrained, **kwargs)
|
| 559 |
+
model.default_cfg = default_cfgs['mobilevig']
|
| 560 |
+
return model
|
geovig_backbone.py
ADDED
|
@@ -0,0 +1,667 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import torch
|
| 3 |
+
from torch import nn
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
from torch.nn import Sequential as Seq
|
| 6 |
+
import logging
|
| 7 |
+
import copy
|
| 8 |
+
|
| 9 |
+
from timm.data import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD
|
| 10 |
+
from timm.models.layers import DropPath
|
| 11 |
+
from timm.models.registry import register_model
|
| 12 |
+
|
| 13 |
+
try:
|
| 14 |
+
from mmdet.models.builder import BACKBONES as det_BACKBONES
|
| 15 |
+
from mmdet.utils import get_root_logger
|
| 16 |
+
from mmcv.runner import _load_checkpoint
|
| 17 |
+
has_mmdet = True
|
| 18 |
+
except ImportError:
|
| 19 |
+
print("If for detection, please install mmdetection first")
|
| 20 |
+
has_mmdet = False
|
| 21 |
+
|
| 22 |
+
def _cfg(url='', **kwargs):
|
| 23 |
+
return {
|
| 24 |
+
'url': url,
|
| 25 |
+
'num_classes': 1000, 'input_size': (3, 224, 224), 'pool_size': None,
|
| 26 |
+
'crop_pct': .9, 'interpolation': 'bicubic',
|
| 27 |
+
'mean': IMAGENET_DEFAULT_MEAN, 'std': IMAGENET_DEFAULT_STD,
|
| 28 |
+
'classifier': 'head',
|
| 29 |
+
**kwargs
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
default_cfgs = {
|
| 33 |
+
'mobilevig': _cfg(crop_pct=0.9, mean=IMAGENET_DEFAULT_MEAN, std=IMAGENET_DEFAULT_STD)
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
import logging
|
| 37 |
+
|
| 38 |
+
logging.basicConfig(
|
| 39 |
+
level=logging.WARNING,
|
| 40 |
+
format="%(asctime)s | %(levelname)s | %(name)s | %(message)s"
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
logger = logging.getLogger(__name__)
|
| 44 |
+
|
| 45 |
+
logger = logging.getLogger(__name__)
|
| 46 |
+
|
| 47 |
+
class Stem(nn.Module):
|
| 48 |
+
def __init__(self, input_dim, output_dim, activation=nn.GELU):
|
| 49 |
+
super(Stem, self).__init__()
|
| 50 |
+
self.stem = nn.Sequential(
|
| 51 |
+
nn.Conv2d(input_dim, output_dim // 2, kernel_size=3, stride=2, padding=1),
|
| 52 |
+
nn.BatchNorm2d(output_dim // 2),
|
| 53 |
+
nn.GELU(),
|
| 54 |
+
nn.Conv2d(output_dim // 2, output_dim, kernel_size=3, stride=2, padding=1),
|
| 55 |
+
nn.BatchNorm2d(output_dim),
|
| 56 |
+
nn.GELU()
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
def forward(self, x):
|
| 60 |
+
return self.stem(x)
|
| 61 |
+
|
| 62 |
+
class GraphAdapter(nn.Module):
|
| 63 |
+
"""
|
| 64 |
+
Graph-Aware Adapter: Lightweight 3x3 DW Conv + GroupNorm + 1x1 Conv + GroupNorm.
|
| 65 |
+
Aligns graph features with grid-based detection heads.
|
| 66 |
+
"""
|
| 67 |
+
def __init__(self, channels):
|
| 68 |
+
super().__init__()
|
| 69 |
+
self.adapter = nn.Sequential(
|
| 70 |
+
# Depthwise 3x3
|
| 71 |
+
nn.Conv2d(channels, channels, kernel_size=3, padding=1, groups=channels, bias=False),
|
| 72 |
+
# GroupNorm (num_groups=1 is equivalent to LayerNorm but works with 4D tensors)
|
| 73 |
+
nn.GroupNorm(1, channels),
|
| 74 |
+
# Pointwise 1x1
|
| 75 |
+
nn.Conv2d(channels, channels, kernel_size=1, bias=False),
|
| 76 |
+
nn.GroupNorm(32, channels), # GroupNorm for stability
|
| 77 |
+
nn.GELU()
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
def forward(self, x):
|
| 81 |
+
return self.adapter(x)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class MLP(nn.Module):
|
| 86 |
+
"""
|
| 87 |
+
Implementation of MLP with 1*1 convolutions.
|
| 88 |
+
Input: tensor with shape [B, C, H, W]
|
| 89 |
+
"""
|
| 90 |
+
|
| 91 |
+
def __init__(self, in_features, hidden_features=None,
|
| 92 |
+
out_features=None, drop=0., mid_conv=False):
|
| 93 |
+
super().__init__()
|
| 94 |
+
out_features = out_features or in_features
|
| 95 |
+
hidden_features = hidden_features or in_features
|
| 96 |
+
self.mid_conv = mid_conv
|
| 97 |
+
self.fc1 = nn.Conv2d(in_features, hidden_features, 1)
|
| 98 |
+
self.act = nn.GELU()
|
| 99 |
+
self.fc2 = nn.Conv2d(hidden_features, out_features, 1)
|
| 100 |
+
self.drop = nn.Dropout(drop)
|
| 101 |
+
|
| 102 |
+
if self.mid_conv:
|
| 103 |
+
self.mid = nn.Conv2d(hidden_features, hidden_features, kernel_size=3, stride=1, padding=1,
|
| 104 |
+
groups=hidden_features)
|
| 105 |
+
self.mid_norm = nn.BatchNorm2d(hidden_features)
|
| 106 |
+
|
| 107 |
+
self.norm1 = nn.BatchNorm2d(hidden_features)
|
| 108 |
+
self.norm2 = nn.BatchNorm2d(out_features)
|
| 109 |
+
|
| 110 |
+
def forward(self, x):
|
| 111 |
+
x = self.fc1(x)
|
| 112 |
+
x = self.norm1(x)
|
| 113 |
+
x = self.act(x)
|
| 114 |
+
|
| 115 |
+
if self.mid_conv:
|
| 116 |
+
x_mid = self.mid(x)
|
| 117 |
+
x_mid = self.mid_norm(x_mid)
|
| 118 |
+
x = self.act(x_mid)
|
| 119 |
+
x = self.drop(x)
|
| 120 |
+
|
| 121 |
+
x = self.fc2(x)
|
| 122 |
+
x = self.norm2(x)
|
| 123 |
+
|
| 124 |
+
x = self.drop(x)
|
| 125 |
+
return x
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
class InvertedResidual(nn.Module):
|
| 129 |
+
def __init__(self, dim, mlp_ratio=4., drop=0., drop_path=0., use_layer_scale=True, layer_scale_init_value=1e-5):
|
| 130 |
+
super().__init__()
|
| 131 |
+
|
| 132 |
+
mlp_hidden_dim = int(dim * mlp_ratio)
|
| 133 |
+
self.mlp = MLP(in_features=dim, hidden_features=mlp_hidden_dim, drop=drop, mid_conv=True)
|
| 134 |
+
|
| 135 |
+
self.drop_path = DropPath(drop_path) if drop_path > 0. \
|
| 136 |
+
else nn.Identity()
|
| 137 |
+
self.use_layer_scale = use_layer_scale
|
| 138 |
+
if use_layer_scale:
|
| 139 |
+
self.layer_scale_2 = nn.Parameter(
|
| 140 |
+
layer_scale_init_value * torch.ones(dim).unsqueeze(-1).unsqueeze(-1), requires_grad=True)
|
| 141 |
+
|
| 142 |
+
def forward(self, x):
|
| 143 |
+
if self.use_layer_scale:
|
| 144 |
+
x = x + self.drop_path(self.layer_scale_2 * self.mlp(x))
|
| 145 |
+
else:
|
| 146 |
+
x = x + self.drop_path(self.mlp(x))
|
| 147 |
+
return x
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def img_to_graph(x):
|
| 151 |
+
"""
|
| 152 |
+
Converts (B, C, H, W) -> (B, N, C) and generates a grid adjacency (8-neighbor-ish).
|
| 153 |
+
For efficiency this returns a single edge_index shared for the batch.
|
| 154 |
+
"""
|
| 155 |
+
B, C, H, W = x.shape
|
| 156 |
+
device = x.device
|
| 157 |
+
N = H * W
|
| 158 |
+
|
| 159 |
+
# Flatten features: (B, C, H, W) -> (B, N, C)
|
| 160 |
+
x_flat = x.flatten(2).transpose(1, 2) # (B, N, C)
|
| 161 |
+
|
| 162 |
+
# Generate grid edges (simplified 8-neighbor connectivity)
|
| 163 |
+
idx = torch.arange(N, device=device).view(H, W)
|
| 164 |
+
edges = []
|
| 165 |
+
|
| 166 |
+
# Right neighbors
|
| 167 |
+
if W > 1:
|
| 168 |
+
edges.append(torch.stack([idx[:, :-1].flatten(), idx[:, 1:].flatten()], dim=0))
|
| 169 |
+
# Left (will be mirrored later)
|
| 170 |
+
# Down neighbors
|
| 171 |
+
if H > 1:
|
| 172 |
+
edges.append(torch.stack([idx[:-1, :].flatten(), idx[1:, :].flatten()], dim=0))
|
| 173 |
+
# Diagonals
|
| 174 |
+
if H > 1 and W > 1:
|
| 175 |
+
edges.append(torch.stack([idx[:-1, :-1].flatten(), idx[1:, 1:].flatten()], dim=0))
|
| 176 |
+
edges.append(torch.stack([idx[1:, :-1].flatten(), idx[:-1, 1:].flatten()], dim=0))
|
| 177 |
+
|
| 178 |
+
if len(edges) == 0:
|
| 179 |
+
edge_index = torch.zeros((2, 0), dtype=torch.long, device=device)
|
| 180 |
+
else:
|
| 181 |
+
edge_index = torch.cat(edges, dim=1)
|
| 182 |
+
# Make undirected by mirroring
|
| 183 |
+
edge_index = torch.cat([edge_index, edge_index.flip(0)], dim=1)
|
| 184 |
+
|
| 185 |
+
return x_flat, edge_index
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
class GraphMRConv(nn.Module):
|
| 189 |
+
"""
|
| 190 |
+
Graph version of Max-Relative Graph Convolution.
|
| 191 |
+
Operates on (B, N, C) and edge_index (2, E).
|
| 192 |
+
Uses PyTorch's index_reduce_ with 'amax' for true max aggregation.
|
| 193 |
+
"""
|
| 194 |
+
def __init__(self, in_channels, out_channels):
|
| 195 |
+
super().__init__()
|
| 196 |
+
self.in_channels = in_channels
|
| 197 |
+
self.out_channels = out_channels
|
| 198 |
+
# OPTIMIZATION: Removed concatenation (2*C). Now just Linear(C -> C).
|
| 199 |
+
# This reduces parameters significantly and speeds up the projection.
|
| 200 |
+
self.nn = nn.Sequential(
|
| 201 |
+
nn.Linear(in_channels, out_channels),
|
| 202 |
+
nn.GELU()
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
def forward(self, x, edge_index):
|
| 206 |
+
# x: (B, N, C)
|
| 207 |
+
B, N, C = x.shape
|
| 208 |
+
device = x.device
|
| 209 |
+
|
| 210 |
+
# Reshape to (B*N, C) for batched processing
|
| 211 |
+
x_flat = x.view(-1, C)
|
| 212 |
+
|
| 213 |
+
if edge_index.numel() == 0:
|
| 214 |
+
# No edges: fallback to identity-like behavior with zeros
|
| 215 |
+
# Return processed x_flat reshaped, or zeros if safer
|
| 216 |
+
# Ideally graph conv with no edges is just local projection of 0-vector (since it's rel diff)
|
| 217 |
+
# max(x_j) would be empty -> -inf -> replaced by 0
|
| 218 |
+
# then -x_i
|
| 219 |
+
# so effectively -x_i passed through nn
|
| 220 |
+
return self.nn(-x).view(B, N, -1)
|
| 221 |
+
|
| 222 |
+
row, col = edge_index # (E,), (E,)
|
| 223 |
+
num_edges = row.size(0)
|
| 224 |
+
|
| 225 |
+
# Create batched edge index: (2, B*E)
|
| 226 |
+
batch_offsets = torch.arange(B, device=device) * N
|
| 227 |
+
edge_offsets = batch_offsets.view(-1, 1).repeat(1, num_edges).view(-1)
|
| 228 |
+
|
| 229 |
+
row_batch = row.repeat(B) + edge_offsets
|
| 230 |
+
col_batch = col.repeat(B) + edge_offsets
|
| 231 |
+
|
| 232 |
+
# Gather features
|
| 233 |
+
x_flat_c = x_flat.contiguous()
|
| 234 |
+
# Optimization: Don't compute x_j - x_i explicitly (huge tensor).
|
| 235 |
+
# max(x_j - x_i) = max(x_j) - x_i
|
| 236 |
+
x_j = x_flat_c[col_batch] # (B*E, C)
|
| 237 |
+
|
| 238 |
+
# Initialize aggregation buffer
|
| 239 |
+
aggr = torch.full_like(x_flat, -1e9) # (B*N, C)
|
| 240 |
+
|
| 241 |
+
# Vectorized Max Aggregation of Neighbors
|
| 242 |
+
index_expanded = row_batch.view(-1, 1).expand(-1, C)
|
| 243 |
+
aggr.scatter_reduce_(0, index_expanded, x_j, reduce='amax', include_self=True)
|
| 244 |
+
|
| 245 |
+
# Replace -1e9 with 0
|
| 246 |
+
aggr = torch.where(aggr == -1e9, torch.zeros_like(aggr), aggr)
|
| 247 |
+
|
| 248 |
+
# Now apply the "- x_i" part of "max(x_j) - x_i"
|
| 249 |
+
aggr = aggr - x_flat
|
| 250 |
+
|
| 251 |
+
# Output only the aggregated update
|
| 252 |
+
return self.nn(aggr).view(B, N, -1)
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
class GraphMobileViGBlock(nn.Module):
|
| 256 |
+
"""
|
| 257 |
+
Graph MobileViG block: GraphMRConv + FFN for (B,N,C) representations.
|
| 258 |
+
"""
|
| 259 |
+
def __init__(self, dim, drop=0.0):
|
| 260 |
+
super().__init__()
|
| 261 |
+
self.norm1 = nn.LayerNorm(dim)
|
| 262 |
+
self.graph_conv = GraphMRConv(dim, dim)
|
| 263 |
+
self.norm2 = nn.LayerNorm(dim)
|
| 264 |
+
self.ffn = nn.Sequential(
|
| 265 |
+
nn.Linear(dim, dim * 4),
|
| 266 |
+
nn.GELU(),
|
| 267 |
+
nn.Linear(dim * 4, dim),
|
| 268 |
+
nn.Dropout(drop)
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
def forward(self, x, edge_index):
|
| 272 |
+
# x: (B, N, C)
|
| 273 |
+
shortcut = x
|
| 274 |
+
x = self.norm1(x)
|
| 275 |
+
x = self.graph_conv(x, edge_index)
|
| 276 |
+
x = x + shortcut
|
| 277 |
+
|
| 278 |
+
shortcut = x
|
| 279 |
+
x = self.norm2(x)
|
| 280 |
+
x = self.ffn(x)
|
| 281 |
+
x = x + shortcut
|
| 282 |
+
return x
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
class SpreadEdgePool(nn.Module):
|
| 286 |
+
"""
|
| 287 |
+
Geometry-Aware SpreadEdgePool
|
| 288 |
+
"""
|
| 289 |
+
def __init__(self, in_channels, ratio=0.5):
|
| 290 |
+
super().__init__()
|
| 291 |
+
self.ratio = ratio
|
| 292 |
+
self.in_channels = in_channels
|
| 293 |
+
|
| 294 |
+
def forward(self, x, edge_index, shape=None):
|
| 295 |
+
B, N, C = x.shape
|
| 296 |
+
device = x.device
|
| 297 |
+
|
| 298 |
+
# 2D Scaling Path (For Detection) - STABLE MODE
|
| 299 |
+
# Skips distance-based weighting to avoid instability in detection fine-tuning
|
| 300 |
+
if shape is not None:
|
| 301 |
+
H, W = shape
|
| 302 |
+
if H * W == N:
|
| 303 |
+
x_2d = x.transpose(1, 2).view(B, C, H, W)
|
| 304 |
+
|
| 305 |
+
# Force Stride 2 for Detection (Target Stride 32 from Stride 16 input)
|
| 306 |
+
# ratio=0.25 implies 1/4 area -> stride 2
|
| 307 |
+
stride = 2 if self.ratio <= 0.25 else 1
|
| 308 |
+
|
| 309 |
+
x_pooled_2d = F.avg_pool2d(x_2d, kernel_size=stride, stride=stride)
|
| 310 |
+
|
| 311 |
+
# Regenerate Grid Graph
|
| 312 |
+
x_pooled, new_edge_index = img_to_graph(x_pooled_2d)
|
| 313 |
+
return x_pooled, new_edge_index
|
| 314 |
+
|
| 315 |
+
# Default 1D Graph Pooling (original classification logic)
|
| 316 |
+
num_keep = max(1, int(N * self.ratio))
|
| 317 |
+
|
| 318 |
+
# Only compute weights if strictly needed (1D path)
|
| 319 |
+
if edge_index.numel() > 0:
|
| 320 |
+
row, col = edge_index
|
| 321 |
+
x_sq = (x ** 2).sum(dim=-1, keepdim=True)
|
| 322 |
+
sq_i = x_sq[:, row, :]
|
| 323 |
+
sq_j = x_sq[:, col, :]
|
| 324 |
+
x_row = x[:, row, :]
|
| 325 |
+
x_col = x[:, col, :]
|
| 326 |
+
dot = (x_row * x_col).sum(dim=-1, keepdim=True)
|
| 327 |
+
dist_sq = sq_i + sq_j - 2 * dot
|
| 328 |
+
dist = torch.sqrt(F.relu(dist_sq) + 1e-6).squeeze(-1)
|
| 329 |
+
avg_edge_scores = dist.mean(dim=0)
|
| 330 |
+
node_importance = torch.zeros(N, device=device)
|
| 331 |
+
node_importance.index_add_(0, row, avg_edge_scores)
|
| 332 |
+
node_weights = torch.sigmoid(node_importance).view(1, N, 1)
|
| 333 |
+
else:
|
| 334 |
+
node_weights = torch.ones((1, N, 1), device=device)
|
| 335 |
+
|
| 336 |
+
x_weighted = x * node_weights
|
| 337 |
+
num_keep = max(1, int(N * self.ratio))
|
| 338 |
+
|
| 339 |
+
x_pooled = F.adaptive_avg_pool1d(x_weighted.transpose(1, 2), num_keep).transpose(1, 2)
|
| 340 |
+
|
| 341 |
+
if edge_index.numel() == 0:
|
| 342 |
+
idx = torch.arange(num_keep, device=device)
|
| 343 |
+
new_edge_index = torch.stack([idx[:-1], idx[1:]], dim=0) if num_keep > 1 else torch.zeros((2, 0), dtype=torch.long, device=device)
|
| 344 |
+
return x_pooled, new_edge_index
|
| 345 |
+
|
| 346 |
+
idx = torch.arange(num_keep, device=device)
|
| 347 |
+
if num_keep > 1:
|
| 348 |
+
left = idx[:-1]
|
| 349 |
+
right = idx[1:]
|
| 350 |
+
new_edge_index = torch.cat([torch.stack([left, right], dim=0), torch.stack([right, left], dim=0)], dim=1)
|
| 351 |
+
else:
|
| 352 |
+
new_edge_index = torch.zeros((2, 0), dtype=torch.long, device=device)
|
| 353 |
+
|
| 354 |
+
return x_pooled, new_edge_index
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
class Downsample(nn.Module):
|
| 358 |
+
"""
|
| 359 |
+
Convolution-based downsample
|
| 360 |
+
"""
|
| 361 |
+
def __init__(self, in_dim, out_dim):
|
| 362 |
+
super().__init__()
|
| 363 |
+
self.conv = nn.Sequential(
|
| 364 |
+
nn.Conv2d(in_dim, out_dim, 3, stride=2, padding=1),
|
| 365 |
+
nn.BatchNorm2d(out_dim),
|
| 366 |
+
)
|
| 367 |
+
|
| 368 |
+
def forward(self, x):
|
| 369 |
+
x = self.conv(x)
|
| 370 |
+
return x
|
| 371 |
+
|
| 372 |
+
class GeoViG(nn.Module):
|
| 373 |
+
"""
|
| 374 |
+
Hybrid Grid->Graph GeoViG model. Can be used for classification or as a backbone for detection.
|
| 375 |
+
"""
|
| 376 |
+
def __init__(self,
|
| 377 |
+
local_blocks=[2, 2],
|
| 378 |
+
local_channels=[42, 84],
|
| 379 |
+
graph_blocks=[2],
|
| 380 |
+
graph_channels=[168],
|
| 381 |
+
num_classes=1000,
|
| 382 |
+
pool_ratio=0.5,
|
| 383 |
+
distillation=False,
|
| 384 |
+
features_only=False,
|
| 385 |
+
out_indices=None,
|
| 386 |
+
pretrained=None,
|
| 387 |
+
use_detect_adapter=False,
|
| 388 |
+
**kwargs):
|
| 389 |
+
super().__init__()
|
| 390 |
+
|
| 391 |
+
self.features_only = features_only
|
| 392 |
+
self.pool_ratio = pool_ratio
|
| 393 |
+
self.out_indices = out_indices
|
| 394 |
+
self.distillation = distillation
|
| 395 |
+
self.pretrained = pretrained
|
| 396 |
+
self.use_detect_adapter = use_detect_adapter
|
| 397 |
+
|
| 398 |
+
# Stage 0: stem
|
| 399 |
+
self.stem = Stem(input_dim=3, output_dim=local_channels[0])
|
| 400 |
+
|
| 401 |
+
# Grid stages
|
| 402 |
+
self.grid_stages = nn.ModuleList()
|
| 403 |
+
in_c = local_channels[0]
|
| 404 |
+
|
| 405 |
+
self.feature_info = [dict(num_chs=in_c, reduction=2, module=f'stem')]
|
| 406 |
+
|
| 407 |
+
for i, out_c in enumerate(local_channels):
|
| 408 |
+
layers = []
|
| 409 |
+
if i > 0:
|
| 410 |
+
layers.append(Downsample(in_c, out_c))
|
| 411 |
+
in_c = out_c
|
| 412 |
+
for _ in range(local_blocks[i]):
|
| 413 |
+
layers.append(InvertedResidual(dim=out_c, mlp_ratio=4, drop_path=0.0))
|
| 414 |
+
self.grid_stages.append(nn.Sequential(*layers))
|
| 415 |
+
self.feature_info.append(dict(num_chs=out_c, reduction=2**(i+2), module=f'grid_stages.{i}'))
|
| 416 |
+
|
| 417 |
+
# Transition: grid -> graph
|
| 418 |
+
self.to_graph_pool = SpreadEdgePool(in_channels=in_c, ratio=pool_ratio)
|
| 419 |
+
self.graph_dim = in_c
|
| 420 |
+
|
| 421 |
+
# Graph stages
|
| 422 |
+
self.graph_proj = nn.Linear(self.graph_dim, graph_channels[0]) if graph_channels else nn.Identity()
|
| 423 |
+
self.graph_dim = graph_channels[0] if graph_channels else self.graph_dim
|
| 424 |
+
|
| 425 |
+
self.graph_stages = nn.ModuleList()
|
| 426 |
+
for _ in range(graph_blocks[0] if len(graph_blocks) > 0 else 0):
|
| 427 |
+
self.graph_stages.append(GraphMobileViGBlock(dim=self.graph_dim))
|
| 428 |
+
|
| 429 |
+
self.feature_info.append(dict(num_chs=self.graph_dim, reduction=2**(len(local_channels)+1), module='graph_stages'))
|
| 430 |
+
|
| 431 |
+
if not self.features_only:
|
| 432 |
+
# Head for classification
|
| 433 |
+
self.norm = nn.LayerNorm(self.graph_dim)
|
| 434 |
+
self.head = nn.Linear(self.graph_dim, num_classes)
|
| 435 |
+
if self.distillation:
|
| 436 |
+
self.head_dist = nn.Linear(self.graph_dim, num_classes)
|
| 437 |
+
else:
|
| 438 |
+
# For torchvision FPN
|
| 439 |
+
self._out_channels = []
|
| 440 |
+
if self.out_indices:
|
| 441 |
+
for i in self.out_indices:
|
| 442 |
+
# The indices for feature_info are shifted by 1 compared to grid_stages
|
| 443 |
+
# features_info: [stem(idx0), grid0(idx1), grid1(idx2), grid2(idx3), graph(idx4)]
|
| 444 |
+
# grid_stages: [grid0, grid1, grid2] (indices 0, 1, 2)
|
| 445 |
+
pass
|
| 446 |
+
|
| 447 |
+
# Detection Adapter Initialization
|
| 448 |
+
if self.use_detect_adapter and self.features_only:
|
| 449 |
+
# Determine channels automatically
|
| 450 |
+
# Stem: local_channels[0] (but usually we tap grid stages)
|
| 451 |
+
# Grid Stages output: local_channels
|
| 452 |
+
# Graph Stage output: graph_channels[0]
|
| 453 |
+
|
| 454 |
+
# Map indices to channels
|
| 455 |
+
# We assume out_indices=[0, 1, 2, 3] corresponds to [grid0, grid1, grid2, graph]
|
| 456 |
+
# Verify this mapping convention from forward loop
|
| 457 |
+
self.detect_adapters = nn.ModuleList()
|
| 458 |
+
|
| 459 |
+
# This is a bit dynamic because out_indices can vary, but assuming standard GeoViG-M/B
|
| 460 |
+
# and standard out_indices=[0, 1, 2, 3]
|
| 461 |
+
|
| 462 |
+
current_channels = []
|
| 463 |
+
# Indices refer to stages
|
| 464 |
+
# 0 -> grid_stages[0] (local_channels[0])
|
| 465 |
+
# 1 -> grid_stages[1] (local_channels[1])
|
| 466 |
+
# 2 -> grid_stages[2] (local_channels[2])
|
| 467 |
+
# 3 -> graph_stage (graph_channels[0]) (if present)
|
| 468 |
+
|
| 469 |
+
all_channels = local_channels[:]
|
| 470 |
+
if graph_channels:
|
| 471 |
+
all_channels.append(graph_channels[0])
|
| 472 |
+
|
| 473 |
+
for idx in (self.out_indices or []):
|
| 474 |
+
if idx < len(all_channels):
|
| 475 |
+
self.detect_adapters.append(GraphAdapter(all_channels[idx]))
|
| 476 |
+
else:
|
| 477 |
+
# Fallback or error
|
| 478 |
+
pass
|
| 479 |
+
|
| 480 |
+
self.init_weights()
|
| 481 |
+
if pretrained:
|
| 482 |
+
self._load_pretrained_weights()
|
| 483 |
+
|
| 484 |
+
# self = torch.nn.SyncBatchNorm.convert_sync_batchnorm(self)
|
| 485 |
+
|
| 486 |
+
def init_weights(self):
|
| 487 |
+
if self.pretrained and isinstance(self.pretrained, str):
|
| 488 |
+
logger = get_root_logger()
|
| 489 |
+
print(f"Pretrained weights being being loaded from {self.pretrained}")
|
| 490 |
+
logger.warn(f'Pretrained weights being loaded from {self.pretrained}')
|
| 491 |
+
self._load_pretrained_weights()
|
| 492 |
+
else:
|
| 493 |
+
# Init random weights if needed or rely on default init
|
| 494 |
+
pass
|
| 495 |
+
|
| 496 |
+
def _load_pretrained_weights(self):
|
| 497 |
+
"""Load pretrained weights from checkpoint."""
|
| 498 |
+
if self.pretrained and isinstance(self.pretrained, str):
|
| 499 |
+
logging.info(f"Loading pretrained weights from {self.pretrained}")
|
| 500 |
+
try:
|
| 501 |
+
# Use mmcv loader if available
|
| 502 |
+
ckpt = _load_checkpoint(self.pretrained, logger=logging.getLogger(), map_location='cpu')
|
| 503 |
+
except:
|
| 504 |
+
ckpt = torch.load(self.pretrained, map_location='cpu')
|
| 505 |
+
|
| 506 |
+
print("ckpt keys: ", ckpt.keys())
|
| 507 |
+
if 'state_dict_ema' in ckpt:
|
| 508 |
+
state_dict = ckpt['state_dict_ema']
|
| 509 |
+
elif 'state_dict' in ckpt:
|
| 510 |
+
state_dict = ckpt['state_dict']
|
| 511 |
+
elif 'model' in ckpt:
|
| 512 |
+
state_dict = ckpt['model']
|
| 513 |
+
else:
|
| 514 |
+
state_dict = ckpt
|
| 515 |
+
|
| 516 |
+
model_dict = self.state_dict()
|
| 517 |
+
|
| 518 |
+
# Simple permissive loading
|
| 519 |
+
pretrained_dict = {}
|
| 520 |
+
for k, v in state_dict.items():
|
| 521 |
+
if k in model_dict:
|
| 522 |
+
if v.shape == model_dict[k].shape:
|
| 523 |
+
# Exclude classification head logic
|
| 524 |
+
if 'head' not in k:
|
| 525 |
+
pretrained_dict[k] = v
|
| 526 |
+
else:
|
| 527 |
+
print(f"Shape mismatch for {k}: {v.shape} vs {model_dict[k].shape}")
|
| 528 |
+
|
| 529 |
+
missing, unexpected = self.load_state_dict(pretrained_dict, strict=False)
|
| 530 |
+
print("missing_keys: ", missing)
|
| 531 |
+
print("unexpected_keys: ", unexpected)
|
| 532 |
+
|
| 533 |
+
@torch.no_grad()
|
| 534 |
+
def train(self, mode=True):
|
| 535 |
+
super().train(mode)
|
| 536 |
+
# For detection, keep BatchNorm in eval mode
|
| 537 |
+
for m in self.modules():
|
| 538 |
+
if isinstance(m, nn.BatchNorm2d):
|
| 539 |
+
m.eval()
|
| 540 |
+
|
| 541 |
+
def forward(self, x):
|
| 542 |
+
outs = []
|
| 543 |
+
x = self.stem(x)
|
| 544 |
+
|
| 545 |
+
for i, stage in enumerate(self.grid_stages):
|
| 546 |
+
x = stage(x)
|
| 547 |
+
if self.features_only and self.out_indices and i in self.out_indices:
|
| 548 |
+
outs.append(x)
|
| 549 |
+
|
| 550 |
+
# Graph processing
|
| 551 |
+
B, C, H, W = x.shape
|
| 552 |
+
x_nodes, edge_index = img_to_graph(x)
|
| 553 |
+
# We need to know the stride used by SpreadEdgePool to reconstruct the shape later
|
| 554 |
+
# Logic matches SpreadEdgePool: stride 2 if ratio <= 0.25 else 1
|
| 555 |
+
pool_stride = 2 if self.pool_ratio <= 0.25 else 1
|
| 556 |
+
|
| 557 |
+
x_nodes, edge_index = self.to_graph_pool(x_nodes, edge_index, shape=(H, W))
|
| 558 |
+
x_nodes = self.graph_proj(x_nodes)
|
| 559 |
+
|
| 560 |
+
for block in self.graph_stages:
|
| 561 |
+
x_nodes = block(x_nodes, edge_index)
|
| 562 |
+
|
| 563 |
+
if self.features_only:
|
| 564 |
+
graph_stage_idx = len(self.grid_stages)
|
| 565 |
+
if self.out_indices and graph_stage_idx in self.out_indices:
|
| 566 |
+
# Calculate correct new dimensions
|
| 567 |
+
new_h = H // pool_stride
|
| 568 |
+
new_w = W // pool_stride
|
| 569 |
+
|
| 570 |
+
# Verify shape matches
|
| 571 |
+
if new_h * new_w != x_nodes.shape[1]:
|
| 572 |
+
logger.warning(
|
| 573 |
+
"Shape mismatch in Graph Stage reshape. Expected %dx%d=%d, got %d",
|
| 574 |
+
new_h, new_w, new_h * new_w, x_nodes.shape[1]
|
| 575 |
+
)
|
| 576 |
+
# Fallback to square if totally broken (should not happen with stable pooling)
|
| 577 |
+
num_nodes = x_nodes.shape[1]
|
| 578 |
+
new_h = int(num_nodes**0.5)
|
| 579 |
+
new_w = int(num_nodes**0.5)
|
| 580 |
+
if new_h * new_w != num_nodes:
|
| 581 |
+
x_nodes = x_nodes[:, :new_h*new_w, :]
|
| 582 |
+
|
| 583 |
+
x_graph = x_nodes.transpose(1, 2).view(B, self.graph_dim, new_h, new_w)
|
| 584 |
+
outs.append(x_graph)
|
| 585 |
+
|
| 586 |
+
# Apply Adapters and Clamping
|
| 587 |
+
final_outs = []
|
| 588 |
+
for i, feat in enumerate(outs):
|
| 589 |
+
# Stability Fix: Clamp
|
| 590 |
+
feat = torch.clamp(feat, min=-10.0, max=10.0)
|
| 591 |
+
|
| 592 |
+
# Adaptation: Apply GraphAdapter if enabled
|
| 593 |
+
if self.use_detect_adapter and hasattr(self, 'detect_adapters') and i < len(self.detect_adapters):
|
| 594 |
+
feat = self.detect_adapters[i](feat)
|
| 595 |
+
|
| 596 |
+
final_outs.append(feat)
|
| 597 |
+
|
| 598 |
+
return tuple(final_outs)
|
| 599 |
+
|
| 600 |
+
# Global readout (mean) for classification
|
| 601 |
+
x_global = x_nodes.mean(dim=1)
|
| 602 |
+
x_out = self.norm(x_global)
|
| 603 |
+
|
| 604 |
+
if self.distillation:
|
| 605 |
+
x_main = self.head(x_out)
|
| 606 |
+
x_dist = self.head_dist(x_out)
|
| 607 |
+
if self.training:
|
| 608 |
+
return x_main, x_dist
|
| 609 |
+
else:
|
| 610 |
+
return (x_main + x_dist) / 2
|
| 611 |
+
else:
|
| 612 |
+
return self.head(x_out)
|
| 613 |
+
|
| 614 |
+
|
| 615 |
+
if has_mmdet:
|
| 616 |
+
@det_BACKBONES.register_module()
|
| 617 |
+
def geovig_m_feat(pretrained=False, **kwargs):
|
| 618 |
+
if pretrained == 'torchvision://resnet50':
|
| 619 |
+
pretrained = True
|
| 620 |
+
|
| 621 |
+
# Absolute path relative to this file — works from any CWD
|
| 622 |
+
_here = os.path.dirname(os.path.abspath(__file__))
|
| 623 |
+
real_pretrained = os.path.normpath(
|
| 624 |
+
os.path.join(_here, '../final_model_ipa_pth/pth/geovig_m_5e4_8G_300_80_70/checkpoint.pth')
|
| 625 |
+
)
|
| 626 |
+
# Skip backbone pre-loading if path missing; init_detector loads the full ckpt anyway
|
| 627 |
+
if pretrained is True:
|
| 628 |
+
load_path = real_pretrained if os.path.exists(real_pretrained) else None
|
| 629 |
+
elif isinstance(pretrained, str) and os.path.exists(pretrained):
|
| 630 |
+
load_path = pretrained
|
| 631 |
+
else:
|
| 632 |
+
load_path = None
|
| 633 |
+
|
| 634 |
+
model = GeoViG(local_blocks=[3, 3, 9], local_channels=[64, 128, 256],
|
| 635 |
+
graph_blocks=[3], graph_channels=[384],
|
| 636 |
+
pool_ratio=0.25,
|
| 637 |
+
features_only=True,
|
| 638 |
+
out_indices=[0, 1, 2, 3],
|
| 639 |
+
pretrained=load_path)
|
| 640 |
+
model.default_cfg = default_cfgs['mobilevig']
|
| 641 |
+
return model
|
| 642 |
+
|
| 643 |
+
@det_BACKBONES.register_module()
|
| 644 |
+
def geovig_b_feat(pretrained=False, **kwargs):
|
| 645 |
+
if pretrained == 'torchvision://resnet50':
|
| 646 |
+
pretrained = True
|
| 647 |
+
|
| 648 |
+
_here = os.path.dirname(os.path.abspath(__file__))
|
| 649 |
+
real_pretrained = os.path.normpath(
|
| 650 |
+
os.path.join(_here, '../final_model_ipa_pth/pth/geovig_b_5e4_8G_300_82_38/checkpoint.pth')
|
| 651 |
+
)
|
| 652 |
+
if pretrained is True:
|
| 653 |
+
load_path = real_pretrained if os.path.exists(real_pretrained) else None
|
| 654 |
+
elif isinstance(pretrained, str) and os.path.exists(pretrained):
|
| 655 |
+
load_path = pretrained
|
| 656 |
+
else:
|
| 657 |
+
load_path = None
|
| 658 |
+
|
| 659 |
+
model = GeoViG(local_blocks=[5, 5, 9], local_channels=[80, 160, 320],
|
| 660 |
+
graph_blocks=[4], graph_channels=[512],
|
| 661 |
+
pool_ratio=0.25,
|
| 662 |
+
features_only=True,
|
| 663 |
+
out_indices=[0, 1, 2, 3],
|
| 664 |
+
pretrained=load_path)
|
| 665 |
+
model.default_cfg = default_cfgs['mobilevig']
|
| 666 |
+
return model
|
| 667 |
+
|
geovig_det_backbone.py
ADDED
|
@@ -0,0 +1,676 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import torch
|
| 3 |
+
from torch import nn
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
from torch.nn import Sequential as Seq
|
| 6 |
+
import logging
|
| 7 |
+
import copy
|
| 8 |
+
|
| 9 |
+
from timm.data import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD
|
| 10 |
+
from timm.models.layers import DropPath
|
| 11 |
+
from timm.models.registry import register_model
|
| 12 |
+
|
| 13 |
+
try:
|
| 14 |
+
from mmdet.models.builder import BACKBONES as det_BACKBONES
|
| 15 |
+
from mmdet.utils import get_root_logger
|
| 16 |
+
from mmcv.runner import _load_checkpoint
|
| 17 |
+
has_mmdet = True
|
| 18 |
+
|
| 19 |
+
# Register dummy medical datasets for configuration compatibility
|
| 20 |
+
from mmdet.datasets.builder import DATASETS
|
| 21 |
+
from mmdet.datasets.custom import CustomDataset
|
| 22 |
+
|
| 23 |
+
@DATASETS.register_module(force=True)
|
| 24 |
+
class KvasirDataset(CustomDataset):
|
| 25 |
+
CLASSES = ('polyp',)
|
| 26 |
+
|
| 27 |
+
@DATASETS.register_module(force=True)
|
| 28 |
+
class DSBDataset(CustomDataset):
|
| 29 |
+
CLASSES = ('nucleus',)
|
| 30 |
+
except ImportError:
|
| 31 |
+
print("If for detection, please install mmdetection first")
|
| 32 |
+
has_mmdet = False
|
| 33 |
+
|
| 34 |
+
def _cfg(url='', **kwargs):
|
| 35 |
+
return {
|
| 36 |
+
'url': url,
|
| 37 |
+
'num_classes': 1000, 'input_size': (3, 224, 224), 'pool_size': None,
|
| 38 |
+
'crop_pct': .9, 'interpolation': 'bicubic',
|
| 39 |
+
'mean': IMAGENET_DEFAULT_MEAN, 'std': IMAGENET_DEFAULT_STD,
|
| 40 |
+
'classifier': 'head',
|
| 41 |
+
**kwargs
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
default_cfgs = {
|
| 45 |
+
'mobilevig': _cfg(crop_pct=0.9, mean=IMAGENET_DEFAULT_MEAN, std=IMAGENET_DEFAULT_STD)
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
import logging
|
| 49 |
+
|
| 50 |
+
logging.basicConfig(
|
| 51 |
+
level=logging.WARNING,
|
| 52 |
+
format="%(asctime)s | %(levelname)s | %(name)s | %(message)s"
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
logger = logging.getLogger(__name__)
|
| 56 |
+
|
| 57 |
+
logger = logging.getLogger(__name__)
|
| 58 |
+
|
| 59 |
+
class Stem(nn.Module):
|
| 60 |
+
def __init__(self, input_dim, output_dim, activation=nn.GELU):
|
| 61 |
+
super(Stem, self).__init__()
|
| 62 |
+
self.stem = nn.Sequential(
|
| 63 |
+
nn.Conv2d(input_dim, output_dim // 2, kernel_size=3, stride=2, padding=1),
|
| 64 |
+
nn.BatchNorm2d(output_dim // 2),
|
| 65 |
+
nn.GELU(),
|
| 66 |
+
nn.Conv2d(output_dim // 2, output_dim, kernel_size=3, stride=2, padding=1),
|
| 67 |
+
nn.BatchNorm2d(output_dim),
|
| 68 |
+
nn.GELU()
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
def forward(self, x):
|
| 72 |
+
return self.stem(x)
|
| 73 |
+
|
| 74 |
+
class GraphAdapter(nn.Module):
|
| 75 |
+
"""
|
| 76 |
+
Graph-Aware Adapter: Lightweight 3x3 DW Conv + GroupNorm + 1x1 Conv + GroupNorm.
|
| 77 |
+
Aligns graph features with grid-based detection heads.
|
| 78 |
+
"""
|
| 79 |
+
def __init__(self, channels):
|
| 80 |
+
super().__init__()
|
| 81 |
+
self.adapter = nn.Sequential(
|
| 82 |
+
# Depthwise 3x3
|
| 83 |
+
nn.Conv2d(channels, channels, kernel_size=3, padding=1, groups=channels, bias=False),
|
| 84 |
+
# GroupNorm (num_groups=1 is equivalent to LayerNorm but works with 4D tensors)
|
| 85 |
+
nn.GroupNorm(1, channels),
|
| 86 |
+
# Pointwise 1x1
|
| 87 |
+
nn.Conv2d(channels, channels, kernel_size=1, bias=False),
|
| 88 |
+
nn.GroupNorm(32, channels), # GroupNorm for stability
|
| 89 |
+
nn.GELU()
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
def forward(self, x):
|
| 93 |
+
return self.adapter(x)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
class MLP(nn.Module):
|
| 98 |
+
"""
|
| 99 |
+
Implementation of MLP with 1*1 convolutions.
|
| 100 |
+
Input: tensor with shape [B, C, H, W]
|
| 101 |
+
"""
|
| 102 |
+
|
| 103 |
+
def __init__(self, in_features, hidden_features=None,
|
| 104 |
+
out_features=None, drop=0., mid_conv=False):
|
| 105 |
+
super().__init__()
|
| 106 |
+
out_features = out_features or in_features
|
| 107 |
+
hidden_features = hidden_features or in_features
|
| 108 |
+
self.mid_conv = mid_conv
|
| 109 |
+
self.fc1 = nn.Conv2d(in_features, hidden_features, 1)
|
| 110 |
+
self.act = nn.GELU()
|
| 111 |
+
self.fc2 = nn.Conv2d(hidden_features, out_features, 1)
|
| 112 |
+
self.drop = nn.Dropout(drop)
|
| 113 |
+
|
| 114 |
+
if self.mid_conv:
|
| 115 |
+
self.mid = nn.Conv2d(hidden_features, hidden_features, kernel_size=3, stride=1, padding=1,
|
| 116 |
+
groups=hidden_features)
|
| 117 |
+
self.mid_norm = nn.BatchNorm2d(hidden_features)
|
| 118 |
+
|
| 119 |
+
self.norm1 = nn.BatchNorm2d(hidden_features)
|
| 120 |
+
self.norm2 = nn.BatchNorm2d(out_features)
|
| 121 |
+
|
| 122 |
+
def forward(self, x):
|
| 123 |
+
x = self.fc1(x)
|
| 124 |
+
x = self.norm1(x)
|
| 125 |
+
x = self.act(x)
|
| 126 |
+
|
| 127 |
+
if self.mid_conv:
|
| 128 |
+
x_mid = self.mid(x)
|
| 129 |
+
x_mid = self.mid_norm(x_mid)
|
| 130 |
+
x = self.act(x_mid)
|
| 131 |
+
x = self.drop(x)
|
| 132 |
+
|
| 133 |
+
x = self.fc2(x)
|
| 134 |
+
x = self.norm2(x)
|
| 135 |
+
|
| 136 |
+
x = self.drop(x)
|
| 137 |
+
return x
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
class InvertedResidual(nn.Module):
|
| 141 |
+
def __init__(self, dim, mlp_ratio=4., drop=0., drop_path=0., use_layer_scale=True, layer_scale_init_value=1e-5):
|
| 142 |
+
super().__init__()
|
| 143 |
+
|
| 144 |
+
mlp_hidden_dim = int(dim * mlp_ratio)
|
| 145 |
+
self.mlp = MLP(in_features=dim, hidden_features=mlp_hidden_dim, drop=drop, mid_conv=True)
|
| 146 |
+
|
| 147 |
+
self.drop_path = DropPath(drop_path) if drop_path > 0. \
|
| 148 |
+
else nn.Identity()
|
| 149 |
+
self.use_layer_scale = use_layer_scale
|
| 150 |
+
if use_layer_scale:
|
| 151 |
+
self.layer_scale_2 = nn.Parameter(
|
| 152 |
+
layer_scale_init_value * torch.ones(dim).unsqueeze(-1).unsqueeze(-1), requires_grad=True)
|
| 153 |
+
|
| 154 |
+
def forward(self, x):
|
| 155 |
+
if self.use_layer_scale:
|
| 156 |
+
x = x + self.drop_path(self.layer_scale_2 * self.mlp(x))
|
| 157 |
+
else:
|
| 158 |
+
x = x + self.drop_path(self.mlp(x))
|
| 159 |
+
return x
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def img_to_graph(x):
|
| 163 |
+
"""
|
| 164 |
+
Converts (B, C, H, W) -> (B, N, C) and generates a grid adjacency (8-neighbor-ish).
|
| 165 |
+
For efficiency this returns a single edge_index shared for the batch.
|
| 166 |
+
"""
|
| 167 |
+
B, C, H, W = x.shape
|
| 168 |
+
device = x.device
|
| 169 |
+
N = H * W
|
| 170 |
+
|
| 171 |
+
# Flatten features: (B, C, H, W) -> (B, N, C)
|
| 172 |
+
x_flat = x.flatten(2).transpose(1, 2) # (B, N, C)
|
| 173 |
+
|
| 174 |
+
# Generate grid edges (simplified 8-neighbor connectivity)
|
| 175 |
+
idx = torch.arange(N, device=device).view(H, W)
|
| 176 |
+
edges = []
|
| 177 |
+
|
| 178 |
+
# Right neighbors
|
| 179 |
+
if W > 1:
|
| 180 |
+
edges.append(torch.stack([idx[:, :-1].flatten(), idx[:, 1:].flatten()], dim=0))
|
| 181 |
+
# Left (will be mirrored later)
|
| 182 |
+
# Down neighbors
|
| 183 |
+
if H > 1:
|
| 184 |
+
edges.append(torch.stack([idx[:-1, :].flatten(), idx[1:, :].flatten()], dim=0))
|
| 185 |
+
# Diagonals
|
| 186 |
+
if H > 1 and W > 1:
|
| 187 |
+
edges.append(torch.stack([idx[:-1, :-1].flatten(), idx[1:, 1:].flatten()], dim=0))
|
| 188 |
+
edges.append(torch.stack([idx[1:, :-1].flatten(), idx[:-1, 1:].flatten()], dim=0))
|
| 189 |
+
|
| 190 |
+
if len(edges) == 0:
|
| 191 |
+
edge_index = torch.zeros((2, 0), dtype=torch.long, device=device)
|
| 192 |
+
else:
|
| 193 |
+
edge_index = torch.cat(edges, dim=1)
|
| 194 |
+
# Make undirected by mirroring
|
| 195 |
+
edge_index = torch.cat([edge_index, edge_index.flip(0)], dim=1)
|
| 196 |
+
|
| 197 |
+
return x_flat, edge_index
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
class GraphMRConv(nn.Module):
|
| 201 |
+
"""
|
| 202 |
+
Graph version of Max-Relative Graph Convolution.
|
| 203 |
+
Operates on (B, N, C) and edge_index (2, E).
|
| 204 |
+
Uses PyTorch's index_reduce_ with 'amax' for true max aggregation.
|
| 205 |
+
"""
|
| 206 |
+
def __init__(self, in_channels, out_channels):
|
| 207 |
+
super().__init__()
|
| 208 |
+
self.in_channels = in_channels
|
| 209 |
+
self.out_channels = out_channels
|
| 210 |
+
# OPTIMIZATION: Removed concatenation (2*C). Now just Linear(C -> C).
|
| 211 |
+
# This reduces parameters significantly and speeds up the projection.
|
| 212 |
+
self.nn = nn.Sequential(
|
| 213 |
+
nn.Linear(in_channels, out_channels),
|
| 214 |
+
nn.GELU()
|
| 215 |
+
)
|
| 216 |
+
|
| 217 |
+
def forward(self, x, edge_index):
|
| 218 |
+
# x: (B, N, C)
|
| 219 |
+
B, N, C = x.shape
|
| 220 |
+
device = x.device
|
| 221 |
+
|
| 222 |
+
# Reshape to (B*N, C) for batched processing
|
| 223 |
+
x_flat = x.view(-1, C)
|
| 224 |
+
|
| 225 |
+
if edge_index.numel() == 0:
|
| 226 |
+
# No edges: fallback to identity-like behavior with zeros
|
| 227 |
+
# Return processed x_flat reshaped, or zeros if safer
|
| 228 |
+
# Ideally graph conv with no edges is just local projection of 0-vector (since it's rel diff)
|
| 229 |
+
# max(x_j) would be empty -> -inf -> replaced by 0
|
| 230 |
+
# then -x_i
|
| 231 |
+
# so effectively -x_i passed through nn
|
| 232 |
+
return self.nn(-x).view(B, N, -1)
|
| 233 |
+
|
| 234 |
+
row, col = edge_index # (E,), (E,)
|
| 235 |
+
num_edges = row.size(0)
|
| 236 |
+
|
| 237 |
+
# Create batched edge index: (2, B*E)
|
| 238 |
+
batch_offsets = torch.arange(B, device=device) * N
|
| 239 |
+
edge_offsets = batch_offsets.view(-1, 1).repeat(1, num_edges).view(-1)
|
| 240 |
+
|
| 241 |
+
row_batch = row.repeat(B) + edge_offsets
|
| 242 |
+
col_batch = col.repeat(B) + edge_offsets
|
| 243 |
+
|
| 244 |
+
# Gather features
|
| 245 |
+
x_flat_c = x_flat.contiguous()
|
| 246 |
+
# Optimization: Don't compute x_j - x_i explicitly (huge tensor).
|
| 247 |
+
# max(x_j - x_i) = max(x_j) - x_i
|
| 248 |
+
x_j = x_flat_c[col_batch] # (B*E, C)
|
| 249 |
+
|
| 250 |
+
# Initialize aggregation buffer
|
| 251 |
+
aggr = torch.full_like(x_flat, -1e9) # (B*N, C)
|
| 252 |
+
|
| 253 |
+
# Vectorized Max Aggregation of Neighbors
|
| 254 |
+
index_expanded = row_batch.view(-1, 1).expand(-1, C)
|
| 255 |
+
aggr.scatter_reduce_(0, index_expanded, x_j, reduce='amax', include_self=True)
|
| 256 |
+
|
| 257 |
+
# Replace -1e9 with 0
|
| 258 |
+
aggr = torch.where(aggr == -1e9, torch.zeros_like(aggr), aggr)
|
| 259 |
+
|
| 260 |
+
# Now apply the "- x_i" part of "max(x_j) - x_i"
|
| 261 |
+
aggr = aggr - x_flat
|
| 262 |
+
|
| 263 |
+
# Output only the aggregated update
|
| 264 |
+
return self.nn(aggr).view(B, N, -1)
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
class GraphMobileViGBlock(nn.Module):
|
| 268 |
+
"""
|
| 269 |
+
Graph MobileViG block: GraphMRConv + FFN for (B,N,C) representations.
|
| 270 |
+
"""
|
| 271 |
+
def __init__(self, dim, drop=0.0):
|
| 272 |
+
super().__init__()
|
| 273 |
+
self.norm1 = nn.LayerNorm(dim)
|
| 274 |
+
self.graph_conv = GraphMRConv(dim, dim)
|
| 275 |
+
self.norm2 = nn.LayerNorm(dim)
|
| 276 |
+
self.ffn = nn.Sequential(
|
| 277 |
+
nn.Linear(dim, dim * 4),
|
| 278 |
+
nn.GELU(),
|
| 279 |
+
nn.Linear(dim * 4, dim),
|
| 280 |
+
nn.Dropout(drop)
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
def forward(self, x, edge_index):
|
| 284 |
+
# x: (B, N, C)
|
| 285 |
+
shortcut = x
|
| 286 |
+
x = self.norm1(x)
|
| 287 |
+
x = self.graph_conv(x, edge_index)
|
| 288 |
+
x = x + shortcut
|
| 289 |
+
|
| 290 |
+
shortcut = x
|
| 291 |
+
x = self.norm2(x)
|
| 292 |
+
x = self.ffn(x)
|
| 293 |
+
x = x + shortcut
|
| 294 |
+
return x
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
class SpreadEdgePool(nn.Module):
|
| 298 |
+
"""
|
| 299 |
+
Geometry-Aware SpreadEdgePool
|
| 300 |
+
"""
|
| 301 |
+
def __init__(self, in_channels, ratio=0.5):
|
| 302 |
+
super().__init__()
|
| 303 |
+
self.ratio = ratio
|
| 304 |
+
self.in_channels = in_channels
|
| 305 |
+
|
| 306 |
+
def forward(self, x, edge_index, shape=None):
|
| 307 |
+
B, N, C = x.shape
|
| 308 |
+
device = x.device
|
| 309 |
+
|
| 310 |
+
# 2D Scaling Path (For Detection) - STABLE MODE
|
| 311 |
+
# Skips distance-based weighting to avoid instability in detection fine-tuning
|
| 312 |
+
if shape is not None:
|
| 313 |
+
H, W = shape
|
| 314 |
+
if H * W == N:
|
| 315 |
+
x_2d = x.transpose(1, 2).view(B, C, H, W)
|
| 316 |
+
|
| 317 |
+
# Force Stride 2 for Detection (Target Stride 32 from Stride 16 input)
|
| 318 |
+
# ratio=0.25 implies 1/4 area -> stride 2
|
| 319 |
+
stride = 2 if self.ratio <= 0.25 else 1
|
| 320 |
+
|
| 321 |
+
x_pooled_2d = F.avg_pool2d(x_2d, kernel_size=stride, stride=stride)
|
| 322 |
+
|
| 323 |
+
# Regenerate Grid Graph
|
| 324 |
+
x_pooled, new_edge_index = img_to_graph(x_pooled_2d)
|
| 325 |
+
return x_pooled, new_edge_index
|
| 326 |
+
|
| 327 |
+
# Default 1D Graph Pooling (original classification logic)
|
| 328 |
+
num_keep = max(1, int(N * self.ratio))
|
| 329 |
+
|
| 330 |
+
# Only compute weights if strictly needed (1D path)
|
| 331 |
+
if edge_index.numel() > 0:
|
| 332 |
+
row, col = edge_index
|
| 333 |
+
x_sq = (x ** 2).sum(dim=-1, keepdim=True)
|
| 334 |
+
sq_i = x_sq[:, row, :]
|
| 335 |
+
sq_j = x_sq[:, col, :]
|
| 336 |
+
x_row = x[:, row, :]
|
| 337 |
+
x_col = x[:, col, :]
|
| 338 |
+
dot = (x_row * x_col).sum(dim=-1, keepdim=True)
|
| 339 |
+
dist_sq = sq_i + sq_j - 2 * dot
|
| 340 |
+
dist = torch.sqrt(F.relu(dist_sq) + 1e-6).squeeze(-1)
|
| 341 |
+
avg_edge_scores = dist.mean(dim=0)
|
| 342 |
+
node_importance = torch.zeros(N, device=device)
|
| 343 |
+
node_importance.index_add_(0, row, avg_edge_scores)
|
| 344 |
+
node_weights = torch.sigmoid(node_importance).view(1, N, 1)
|
| 345 |
+
else:
|
| 346 |
+
node_weights = torch.ones((1, N, 1), device=device)
|
| 347 |
+
|
| 348 |
+
x_weighted = x * node_weights
|
| 349 |
+
num_keep = max(1, int(N * self.ratio))
|
| 350 |
+
|
| 351 |
+
x_pooled = F.adaptive_avg_pool1d(x_weighted.transpose(1, 2), num_keep).transpose(1, 2)
|
| 352 |
+
|
| 353 |
+
if edge_index.numel() == 0:
|
| 354 |
+
idx = torch.arange(num_keep, device=device)
|
| 355 |
+
new_edge_index = torch.stack([idx[:-1], idx[1:]], dim=0) if num_keep > 1 else torch.zeros((2, 0), dtype=torch.long, device=device)
|
| 356 |
+
return x_pooled, new_edge_index
|
| 357 |
+
|
| 358 |
+
idx = torch.arange(num_keep, device=device)
|
| 359 |
+
if num_keep > 1:
|
| 360 |
+
left = idx[:-1]
|
| 361 |
+
right = idx[1:]
|
| 362 |
+
new_edge_index = torch.cat([torch.stack([left, right], dim=0), torch.stack([right, left], dim=0)], dim=1)
|
| 363 |
+
else:
|
| 364 |
+
new_edge_index = torch.zeros((2, 0), dtype=torch.long, device=device)
|
| 365 |
+
|
| 366 |
+
return x_pooled, new_edge_index
|
| 367 |
+
|
| 368 |
+
|
| 369 |
+
class Downsample(nn.Module):
|
| 370 |
+
"""
|
| 371 |
+
Convolution-based downsample
|
| 372 |
+
"""
|
| 373 |
+
def __init__(self, in_dim, out_dim):
|
| 374 |
+
super().__init__()
|
| 375 |
+
self.conv = nn.Sequential(
|
| 376 |
+
nn.Conv2d(in_dim, out_dim, 3, stride=2, padding=1),
|
| 377 |
+
nn.BatchNorm2d(out_dim),
|
| 378 |
+
)
|
| 379 |
+
|
| 380 |
+
def forward(self, x):
|
| 381 |
+
x = self.conv(x)
|
| 382 |
+
return x
|
| 383 |
+
|
| 384 |
+
class GeoViG(nn.Module):
|
| 385 |
+
"""
|
| 386 |
+
Hybrid Grid->Graph GeoViG model. Can be used for classification or as a backbone for detection.
|
| 387 |
+
"""
|
| 388 |
+
def __init__(self,
|
| 389 |
+
local_blocks=[2, 2],
|
| 390 |
+
local_channels=[42, 84],
|
| 391 |
+
graph_blocks=[2],
|
| 392 |
+
graph_channels=[168],
|
| 393 |
+
num_classes=1000,
|
| 394 |
+
pool_ratio=0.5,
|
| 395 |
+
distillation=False,
|
| 396 |
+
features_only=False,
|
| 397 |
+
out_indices=None,
|
| 398 |
+
pretrained=None,
|
| 399 |
+
use_detect_adapter=False,
|
| 400 |
+
**kwargs):
|
| 401 |
+
super().__init__()
|
| 402 |
+
|
| 403 |
+
self.features_only = features_only
|
| 404 |
+
self.pool_ratio = pool_ratio
|
| 405 |
+
self.out_indices = out_indices
|
| 406 |
+
self.distillation = distillation
|
| 407 |
+
self.pretrained = pretrained
|
| 408 |
+
self.use_detect_adapter = use_detect_adapter
|
| 409 |
+
|
| 410 |
+
# Stage 0: stem
|
| 411 |
+
self.stem = Stem(input_dim=3, output_dim=local_channels[0])
|
| 412 |
+
|
| 413 |
+
# Grid stages
|
| 414 |
+
self.grid_stages = nn.ModuleList()
|
| 415 |
+
in_c = local_channels[0]
|
| 416 |
+
|
| 417 |
+
self.feature_info = [dict(num_chs=in_c, reduction=2, module=f'stem')]
|
| 418 |
+
|
| 419 |
+
for i, out_c in enumerate(local_channels):
|
| 420 |
+
layers = []
|
| 421 |
+
if i > 0:
|
| 422 |
+
layers.append(Downsample(in_c, out_c))
|
| 423 |
+
in_c = out_c
|
| 424 |
+
for _ in range(local_blocks[i]):
|
| 425 |
+
layers.append(InvertedResidual(dim=out_c, mlp_ratio=4, drop_path=0.0))
|
| 426 |
+
self.grid_stages.append(nn.Sequential(*layers))
|
| 427 |
+
self.feature_info.append(dict(num_chs=out_c, reduction=2**(i+2), module=f'grid_stages.{i}'))
|
| 428 |
+
|
| 429 |
+
# Transition: grid -> graph
|
| 430 |
+
self.to_graph_pool = SpreadEdgePool(in_channels=in_c, ratio=pool_ratio)
|
| 431 |
+
self.graph_dim = in_c
|
| 432 |
+
|
| 433 |
+
# Graph stages
|
| 434 |
+
self.graph_proj = nn.Linear(self.graph_dim, graph_channels[0]) if graph_channels else nn.Identity()
|
| 435 |
+
self.graph_dim = graph_channels[0] if graph_channels else self.graph_dim
|
| 436 |
+
|
| 437 |
+
self.graph_stages = nn.ModuleList()
|
| 438 |
+
for _ in range(graph_blocks[0] if len(graph_blocks) > 0 else 0):
|
| 439 |
+
self.graph_stages.append(GraphMobileViGBlock(dim=self.graph_dim))
|
| 440 |
+
|
| 441 |
+
self.feature_info.append(dict(num_chs=self.graph_dim, reduction=2**(len(local_channels)+1), module='graph_stages'))
|
| 442 |
+
|
| 443 |
+
if not self.features_only:
|
| 444 |
+
# Head for classification
|
| 445 |
+
self.norm = nn.LayerNorm(self.graph_dim)
|
| 446 |
+
self.head = nn.Linear(self.graph_dim, num_classes)
|
| 447 |
+
if self.distillation:
|
| 448 |
+
self.head_dist = nn.Linear(self.graph_dim, num_classes)
|
| 449 |
+
else:
|
| 450 |
+
# For torchvision FPN
|
| 451 |
+
self._out_channels = []
|
| 452 |
+
if self.out_indices:
|
| 453 |
+
for i in self.out_indices:
|
| 454 |
+
# The indices for feature_info are shifted by 1 compared to grid_stages
|
| 455 |
+
# features_info: [stem(idx0), grid0(idx1), grid1(idx2), grid2(idx3), graph(idx4)]
|
| 456 |
+
# grid_stages: [grid0, grid1, grid2] (indices 0, 1, 2)
|
| 457 |
+
pass
|
| 458 |
+
|
| 459 |
+
# Detection Adapter Initialization
|
| 460 |
+
if self.use_detect_adapter and self.features_only:
|
| 461 |
+
# Determine channels automatically
|
| 462 |
+
# Stem: local_channels[0] (but usually we tap grid stages)
|
| 463 |
+
# Grid Stages output: local_channels
|
| 464 |
+
# Graph Stage output: graph_channels[0]
|
| 465 |
+
|
| 466 |
+
# Map indices to channels
|
| 467 |
+
# We assume out_indices=[0, 1, 2, 3] corresponds to [grid0, grid1, grid2, graph]
|
| 468 |
+
# Verify this mapping convention from forward loop
|
| 469 |
+
self.detect_adapters = nn.ModuleList()
|
| 470 |
+
|
| 471 |
+
# This is a bit dynamic because out_indices can vary, but assuming standard GeoViG-M/B
|
| 472 |
+
# and standard out_indices=[0, 1, 2, 3]
|
| 473 |
+
|
| 474 |
+
current_channels = []
|
| 475 |
+
# Indices refer to stages
|
| 476 |
+
# 0 -> grid_stages[0] (local_channels[0])
|
| 477 |
+
# 1 -> grid_stages[1] (local_channels[1])
|
| 478 |
+
# 2 -> grid_stages[2] (local_channels[2])
|
| 479 |
+
# 3 -> graph_stage (graph_channels[0]) (if present)
|
| 480 |
+
|
| 481 |
+
all_channels = local_channels[:]
|
| 482 |
+
if graph_channels:
|
| 483 |
+
all_channels.append(graph_channels[0])
|
| 484 |
+
|
| 485 |
+
for idx in (self.out_indices or []):
|
| 486 |
+
if idx < len(all_channels):
|
| 487 |
+
self.detect_adapters.append(GraphAdapter(all_channels[idx]))
|
| 488 |
+
else:
|
| 489 |
+
# Fallback or error
|
| 490 |
+
pass
|
| 491 |
+
|
| 492 |
+
self.init_weights()
|
| 493 |
+
if pretrained:
|
| 494 |
+
self._load_pretrained_weights()
|
| 495 |
+
|
| 496 |
+
# self = torch.nn.SyncBatchNorm.convert_sync_batchnorm(self)
|
| 497 |
+
|
| 498 |
+
def init_weights(self):
|
| 499 |
+
if self.pretrained and isinstance(self.pretrained, str):
|
| 500 |
+
logger = get_root_logger()
|
| 501 |
+
print(f"Pretrained weights being being loaded from {self.pretrained}")
|
| 502 |
+
logger.warn(f'Pretrained weights being loaded from {self.pretrained}')
|
| 503 |
+
self._load_pretrained_weights()
|
| 504 |
+
else:
|
| 505 |
+
# Init random weights if needed or rely on default init
|
| 506 |
+
pass
|
| 507 |
+
|
| 508 |
+
def _load_pretrained_weights(self):
|
| 509 |
+
"""Load pretrained weights from checkpoint."""
|
| 510 |
+
if self.pretrained and isinstance(self.pretrained, str):
|
| 511 |
+
logging.info(f"Loading pretrained weights from {self.pretrained}")
|
| 512 |
+
try:
|
| 513 |
+
# Use mmcv loader if available
|
| 514 |
+
ckpt = _load_checkpoint(self.pretrained, logger=logging.getLogger(), map_location='cpu')
|
| 515 |
+
except:
|
| 516 |
+
ckpt = torch.load(self.pretrained, map_location='cpu')
|
| 517 |
+
|
| 518 |
+
print("ckpt keys: ", ckpt.keys())
|
| 519 |
+
if 'state_dict_ema' in ckpt:
|
| 520 |
+
state_dict = ckpt['state_dict_ema']
|
| 521 |
+
elif 'state_dict' in ckpt:
|
| 522 |
+
state_dict = ckpt['state_dict']
|
| 523 |
+
elif 'model' in ckpt:
|
| 524 |
+
state_dict = ckpt['model']
|
| 525 |
+
else:
|
| 526 |
+
state_dict = ckpt
|
| 527 |
+
|
| 528 |
+
model_dict = self.state_dict()
|
| 529 |
+
|
| 530 |
+
# Simple permissive loading
|
| 531 |
+
pretrained_dict = {}
|
| 532 |
+
for k, v in state_dict.items():
|
| 533 |
+
if k in model_dict:
|
| 534 |
+
if v.shape == model_dict[k].shape:
|
| 535 |
+
# Exclude classification head logic
|
| 536 |
+
if 'head' not in k:
|
| 537 |
+
pretrained_dict[k] = v
|
| 538 |
+
else:
|
| 539 |
+
print(f"Shape mismatch for {k}: {v.shape} vs {model_dict[k].shape}")
|
| 540 |
+
|
| 541 |
+
missing, unexpected = self.load_state_dict(pretrained_dict, strict=False)
|
| 542 |
+
print("missing_keys: ", missing)
|
| 543 |
+
print("unexpected_keys: ", unexpected)
|
| 544 |
+
|
| 545 |
+
@torch.no_grad()
|
| 546 |
+
def train(self, mode=True):
|
| 547 |
+
super().train(mode)
|
| 548 |
+
# For detection, keep BatchNorm in eval mode
|
| 549 |
+
for m in self.modules():
|
| 550 |
+
if isinstance(m, nn.BatchNorm2d):
|
| 551 |
+
m.eval()
|
| 552 |
+
|
| 553 |
+
def forward(self, x):
|
| 554 |
+
outs = []
|
| 555 |
+
x = self.stem(x)
|
| 556 |
+
|
| 557 |
+
for i, stage in enumerate(self.grid_stages):
|
| 558 |
+
x = stage(x)
|
| 559 |
+
if self.features_only and self.out_indices and i in self.out_indices:
|
| 560 |
+
outs.append(x)
|
| 561 |
+
|
| 562 |
+
# Graph processing
|
| 563 |
+
B, C, H, W = x.shape
|
| 564 |
+
x_nodes, edge_index = img_to_graph(x)
|
| 565 |
+
# We need to know the stride used by SpreadEdgePool to reconstruct the shape later
|
| 566 |
+
# Logic matches SpreadEdgePool: stride 2 if ratio <= 0.25 else 1
|
| 567 |
+
pool_stride = 2 if self.pool_ratio <= 0.25 else 1
|
| 568 |
+
|
| 569 |
+
x_nodes, edge_index = self.to_graph_pool(x_nodes, edge_index, shape=(H, W))
|
| 570 |
+
x_nodes = self.graph_proj(x_nodes)
|
| 571 |
+
|
| 572 |
+
for block in self.graph_stages:
|
| 573 |
+
x_nodes = block(x_nodes, edge_index)
|
| 574 |
+
|
| 575 |
+
if self.features_only:
|
| 576 |
+
graph_stage_idx = len(self.grid_stages)
|
| 577 |
+
if self.out_indices and graph_stage_idx in self.out_indices:
|
| 578 |
+
# Calculate correct new dimensions
|
| 579 |
+
new_h = H // pool_stride
|
| 580 |
+
new_w = W // pool_stride
|
| 581 |
+
|
| 582 |
+
# Verify shape matches
|
| 583 |
+
if new_h * new_w != x_nodes.shape[1]:
|
| 584 |
+
logger.warning(
|
| 585 |
+
"Shape mismatch in Graph Stage reshape. Expected %dx%d=%d, got %d",
|
| 586 |
+
new_h, new_w, new_h * new_w, x_nodes.shape[1]
|
| 587 |
+
)
|
| 588 |
+
# Fallback to square if totally broken (should not happen with stable pooling)
|
| 589 |
+
num_nodes = x_nodes.shape[1]
|
| 590 |
+
new_h = int(num_nodes**0.5)
|
| 591 |
+
new_w = int(num_nodes**0.5)
|
| 592 |
+
if new_h * new_w != num_nodes:
|
| 593 |
+
x_nodes = x_nodes[:, :new_h*new_w, :]
|
| 594 |
+
|
| 595 |
+
x_graph = x_nodes.transpose(1, 2).view(B, self.graph_dim, new_h, new_w)
|
| 596 |
+
outs.append(x_graph)
|
| 597 |
+
|
| 598 |
+
# Apply Adapters
|
| 599 |
+
final_outs = []
|
| 600 |
+
for i, feat in enumerate(outs):
|
| 601 |
+
# Adaptation: Apply GraphAdapter if enabled
|
| 602 |
+
if self.use_detect_adapter and hasattr(self, 'detect_adapters') and i < len(self.detect_adapters):
|
| 603 |
+
feat = self.detect_adapters[i](feat)
|
| 604 |
+
|
| 605 |
+
final_outs.append(feat)
|
| 606 |
+
|
| 607 |
+
return tuple(final_outs)
|
| 608 |
+
|
| 609 |
+
# Global readout (mean) for classification
|
| 610 |
+
x_global = x_nodes.mean(dim=1)
|
| 611 |
+
x_out = self.norm(x_global)
|
| 612 |
+
|
| 613 |
+
if self.distillation:
|
| 614 |
+
x_main = self.head(x_out)
|
| 615 |
+
x_dist = self.head_dist(x_out)
|
| 616 |
+
if self.training:
|
| 617 |
+
return x_main, x_dist
|
| 618 |
+
else:
|
| 619 |
+
return (x_main + x_dist) / 2
|
| 620 |
+
else:
|
| 621 |
+
return self.head(x_out)
|
| 622 |
+
|
| 623 |
+
|
| 624 |
+
if has_mmdet:
|
| 625 |
+
@det_BACKBONES.register_module()
|
| 626 |
+
def geovig_m_feat(pretrained=False, **kwargs):
|
| 627 |
+
if pretrained == 'torchvision://resnet50':
|
| 628 |
+
pretrained = True
|
| 629 |
+
|
| 630 |
+
# Absolute path relative to this file — works from any CWD
|
| 631 |
+
_here = os.path.dirname(os.path.abspath(__file__))
|
| 632 |
+
real_pretrained = os.path.normpath(
|
| 633 |
+
os.path.join(_here, '../final_model_ipa_pth/pth/geovig_m_5e4_8G_300_80_70/checkpoint.pth')
|
| 634 |
+
)
|
| 635 |
+
# Skip backbone pre-loading if path missing; init_detector loads the full ckpt anyway
|
| 636 |
+
if pretrained is True:
|
| 637 |
+
load_path = real_pretrained if os.path.exists(real_pretrained) else None
|
| 638 |
+
elif isinstance(pretrained, str) and os.path.exists(pretrained):
|
| 639 |
+
load_path = pretrained
|
| 640 |
+
else:
|
| 641 |
+
load_path = None
|
| 642 |
+
|
| 643 |
+
model = GeoViG(local_blocks=[3, 3, 9], local_channels=[64, 128, 256],
|
| 644 |
+
graph_blocks=[3], graph_channels=[384],
|
| 645 |
+
pool_ratio=0.25,
|
| 646 |
+
features_only=True,
|
| 647 |
+
out_indices=[0, 1, 2, 3],
|
| 648 |
+
pretrained=load_path)
|
| 649 |
+
model.default_cfg = default_cfgs['mobilevig']
|
| 650 |
+
return model
|
| 651 |
+
|
| 652 |
+
@det_BACKBONES.register_module()
|
| 653 |
+
def geovig_b_feat(pretrained=False, **kwargs):
|
| 654 |
+
if pretrained == 'torchvision://resnet50':
|
| 655 |
+
pretrained = True
|
| 656 |
+
|
| 657 |
+
_here = os.path.dirname(os.path.abspath(__file__))
|
| 658 |
+
real_pretrained = os.path.normpath(
|
| 659 |
+
os.path.join(_here, '../final_model_ipa_pth/pth/geovig_b_5e4_8G_300_82_38/checkpoint.pth')
|
| 660 |
+
)
|
| 661 |
+
if pretrained is True:
|
| 662 |
+
load_path = real_pretrained if os.path.exists(real_pretrained) else None
|
| 663 |
+
elif isinstance(pretrained, str) and os.path.exists(pretrained):
|
| 664 |
+
load_path = pretrained
|
| 665 |
+
else:
|
| 666 |
+
load_path = None
|
| 667 |
+
|
| 668 |
+
model = GeoViG(local_blocks=[5, 5, 9], local_channels=[80, 160, 320],
|
| 669 |
+
graph_blocks=[4], graph_channels=[512],
|
| 670 |
+
pool_ratio=0.25,
|
| 671 |
+
features_only=True,
|
| 672 |
+
out_indices=[0, 1, 2, 3],
|
| 673 |
+
pretrained=load_path)
|
| 674 |
+
model.default_cfg = default_cfgs['mobilevig']
|
| 675 |
+
return model
|
| 676 |
+
|
imagenet_labels.json
ADDED
|
@@ -0,0 +1,1000 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
["tench",
|
| 2 |
+
"goldfish",
|
| 3 |
+
"great white shark",
|
| 4 |
+
"tiger shark",
|
| 5 |
+
"hammerhead shark",
|
| 6 |
+
"electric ray",
|
| 7 |
+
"stingray",
|
| 8 |
+
"cock",
|
| 9 |
+
"hen",
|
| 10 |
+
"ostrich",
|
| 11 |
+
"brambling",
|
| 12 |
+
"goldfinch",
|
| 13 |
+
"house finch",
|
| 14 |
+
"junco",
|
| 15 |
+
"indigo bunting",
|
| 16 |
+
"American robin",
|
| 17 |
+
"bulbul",
|
| 18 |
+
"jay",
|
| 19 |
+
"magpie",
|
| 20 |
+
"chickadee",
|
| 21 |
+
"American dipper",
|
| 22 |
+
"kite",
|
| 23 |
+
"bald eagle",
|
| 24 |
+
"vulture",
|
| 25 |
+
"great grey owl",
|
| 26 |
+
"fire salamander",
|
| 27 |
+
"smooth newt",
|
| 28 |
+
"newt",
|
| 29 |
+
"spotted salamander",
|
| 30 |
+
"axolotl",
|
| 31 |
+
"American bullfrog",
|
| 32 |
+
"tree frog",
|
| 33 |
+
"tailed frog",
|
| 34 |
+
"loggerhead sea turtle",
|
| 35 |
+
"leatherback sea turtle",
|
| 36 |
+
"mud turtle",
|
| 37 |
+
"terrapin",
|
| 38 |
+
"box turtle",
|
| 39 |
+
"banded gecko",
|
| 40 |
+
"green iguana",
|
| 41 |
+
"Carolina anole",
|
| 42 |
+
"desert grassland whiptail lizard",
|
| 43 |
+
"agama",
|
| 44 |
+
"frilled-necked lizard",
|
| 45 |
+
"alligator lizard",
|
| 46 |
+
"Gila monster",
|
| 47 |
+
"European green lizard",
|
| 48 |
+
"chameleon",
|
| 49 |
+
"Komodo dragon",
|
| 50 |
+
"Nile crocodile",
|
| 51 |
+
"American alligator",
|
| 52 |
+
"triceratops",
|
| 53 |
+
"worm snake",
|
| 54 |
+
"ring-necked snake",
|
| 55 |
+
"eastern hog-nosed snake",
|
| 56 |
+
"smooth green snake",
|
| 57 |
+
"kingsnake",
|
| 58 |
+
"garter snake",
|
| 59 |
+
"water snake",
|
| 60 |
+
"vine snake",
|
| 61 |
+
"night snake",
|
| 62 |
+
"boa constrictor",
|
| 63 |
+
"African rock python",
|
| 64 |
+
"Indian cobra",
|
| 65 |
+
"green mamba",
|
| 66 |
+
"sea snake",
|
| 67 |
+
"Saharan horned viper",
|
| 68 |
+
"eastern diamondback rattlesnake",
|
| 69 |
+
"sidewinder",
|
| 70 |
+
"trilobite",
|
| 71 |
+
"harvestman",
|
| 72 |
+
"scorpion",
|
| 73 |
+
"yellow garden spider",
|
| 74 |
+
"barn spider",
|
| 75 |
+
"European garden spider",
|
| 76 |
+
"southern black widow",
|
| 77 |
+
"tarantula",
|
| 78 |
+
"wolf spider",
|
| 79 |
+
"tick",
|
| 80 |
+
"centipede",
|
| 81 |
+
"black grouse",
|
| 82 |
+
"ptarmigan",
|
| 83 |
+
"ruffed grouse",
|
| 84 |
+
"prairie grouse",
|
| 85 |
+
"peacock",
|
| 86 |
+
"quail",
|
| 87 |
+
"partridge",
|
| 88 |
+
"grey parrot",
|
| 89 |
+
"macaw",
|
| 90 |
+
"sulphur-crested cockatoo",
|
| 91 |
+
"lorikeet",
|
| 92 |
+
"coucal",
|
| 93 |
+
"bee eater",
|
| 94 |
+
"hornbill",
|
| 95 |
+
"hummingbird",
|
| 96 |
+
"jacamar",
|
| 97 |
+
"toucan",
|
| 98 |
+
"duck",
|
| 99 |
+
"red-breasted merganser",
|
| 100 |
+
"goose",
|
| 101 |
+
"black swan",
|
| 102 |
+
"tusker",
|
| 103 |
+
"echidna",
|
| 104 |
+
"platypus",
|
| 105 |
+
"wallaby",
|
| 106 |
+
"koala",
|
| 107 |
+
"wombat",
|
| 108 |
+
"jellyfish",
|
| 109 |
+
"sea anemone",
|
| 110 |
+
"brain coral",
|
| 111 |
+
"flatworm",
|
| 112 |
+
"nematode",
|
| 113 |
+
"conch",
|
| 114 |
+
"snail",
|
| 115 |
+
"slug",
|
| 116 |
+
"sea slug",
|
| 117 |
+
"chiton",
|
| 118 |
+
"chambered nautilus",
|
| 119 |
+
"Dungeness crab",
|
| 120 |
+
"rock crab",
|
| 121 |
+
"fiddler crab",
|
| 122 |
+
"red king crab",
|
| 123 |
+
"American lobster",
|
| 124 |
+
"spiny lobster",
|
| 125 |
+
"crayfish",
|
| 126 |
+
"hermit crab",
|
| 127 |
+
"isopod",
|
| 128 |
+
"white stork",
|
| 129 |
+
"black stork",
|
| 130 |
+
"spoonbill",
|
| 131 |
+
"flamingo",
|
| 132 |
+
"little blue heron",
|
| 133 |
+
"great egret",
|
| 134 |
+
"bittern",
|
| 135 |
+
"crane (bird)",
|
| 136 |
+
"limpkin",
|
| 137 |
+
"common gallinule",
|
| 138 |
+
"American coot",
|
| 139 |
+
"bustard",
|
| 140 |
+
"ruddy turnstone",
|
| 141 |
+
"dunlin",
|
| 142 |
+
"common redshank",
|
| 143 |
+
"dowitcher",
|
| 144 |
+
"oystercatcher",
|
| 145 |
+
"pelican",
|
| 146 |
+
"king penguin",
|
| 147 |
+
"albatross",
|
| 148 |
+
"grey whale",
|
| 149 |
+
"killer whale",
|
| 150 |
+
"dugong",
|
| 151 |
+
"sea lion",
|
| 152 |
+
"Chihuahua",
|
| 153 |
+
"Japanese Chin",
|
| 154 |
+
"Maltese",
|
| 155 |
+
"Pekingese",
|
| 156 |
+
"Shih Tzu",
|
| 157 |
+
"King Charles Spaniel",
|
| 158 |
+
"Papillon",
|
| 159 |
+
"toy terrier",
|
| 160 |
+
"Rhodesian Ridgeback",
|
| 161 |
+
"Afghan Hound",
|
| 162 |
+
"Basset Hound",
|
| 163 |
+
"Beagle",
|
| 164 |
+
"Bloodhound",
|
| 165 |
+
"Bluetick Coonhound",
|
| 166 |
+
"Black and Tan Coonhound",
|
| 167 |
+
"Treeing Walker Coonhound",
|
| 168 |
+
"English foxhound",
|
| 169 |
+
"Redbone Coonhound",
|
| 170 |
+
"borzoi",
|
| 171 |
+
"Irish Wolfhound",
|
| 172 |
+
"Italian Greyhound",
|
| 173 |
+
"Whippet",
|
| 174 |
+
"Ibizan Hound",
|
| 175 |
+
"Norwegian Elkhound",
|
| 176 |
+
"Otterhound",
|
| 177 |
+
"Saluki",
|
| 178 |
+
"Scottish Deerhound",
|
| 179 |
+
"Weimaraner",
|
| 180 |
+
"Staffordshire Bull Terrier",
|
| 181 |
+
"American Staffordshire Terrier",
|
| 182 |
+
"Bedlington Terrier",
|
| 183 |
+
"Border Terrier",
|
| 184 |
+
"Kerry Blue Terrier",
|
| 185 |
+
"Irish Terrier",
|
| 186 |
+
"Norfolk Terrier",
|
| 187 |
+
"Norwich Terrier",
|
| 188 |
+
"Yorkshire Terrier",
|
| 189 |
+
"Wire Fox Terrier",
|
| 190 |
+
"Lakeland Terrier",
|
| 191 |
+
"Sealyham Terrier",
|
| 192 |
+
"Airedale Terrier",
|
| 193 |
+
"Cairn Terrier",
|
| 194 |
+
"Australian Terrier",
|
| 195 |
+
"Dandie Dinmont Terrier",
|
| 196 |
+
"Boston Terrier",
|
| 197 |
+
"Miniature Schnauzer",
|
| 198 |
+
"Giant Schnauzer",
|
| 199 |
+
"Standard Schnauzer",
|
| 200 |
+
"Scottish Terrier",
|
| 201 |
+
"Tibetan Terrier",
|
| 202 |
+
"Australian Silky Terrier",
|
| 203 |
+
"Soft-coated Wheaten Terrier",
|
| 204 |
+
"West Highland White Terrier",
|
| 205 |
+
"Lhasa Apso",
|
| 206 |
+
"Flat-Coated Retriever",
|
| 207 |
+
"Curly-coated Retriever",
|
| 208 |
+
"Golden Retriever",
|
| 209 |
+
"Labrador Retriever",
|
| 210 |
+
"Chesapeake Bay Retriever",
|
| 211 |
+
"German Shorthaired Pointer",
|
| 212 |
+
"Vizsla",
|
| 213 |
+
"English Setter",
|
| 214 |
+
"Irish Setter",
|
| 215 |
+
"Gordon Setter",
|
| 216 |
+
"Brittany Spaniel",
|
| 217 |
+
"Clumber Spaniel",
|
| 218 |
+
"English Springer Spaniel",
|
| 219 |
+
"Welsh Springer Spaniel",
|
| 220 |
+
"Cocker Spaniels",
|
| 221 |
+
"Sussex Spaniel",
|
| 222 |
+
"Irish Water Spaniel",
|
| 223 |
+
"Kuvasz",
|
| 224 |
+
"Schipperke",
|
| 225 |
+
"Groenendael",
|
| 226 |
+
"Malinois",
|
| 227 |
+
"Briard",
|
| 228 |
+
"Australian Kelpie",
|
| 229 |
+
"Komondor",
|
| 230 |
+
"Old English Sheepdog",
|
| 231 |
+
"Shetland Sheepdog",
|
| 232 |
+
"collie",
|
| 233 |
+
"Border Collie",
|
| 234 |
+
"Bouvier des Flandres",
|
| 235 |
+
"Rottweiler",
|
| 236 |
+
"German Shepherd Dog",
|
| 237 |
+
"Dobermann",
|
| 238 |
+
"Miniature Pinscher",
|
| 239 |
+
"Greater Swiss Mountain Dog",
|
| 240 |
+
"Bernese Mountain Dog",
|
| 241 |
+
"Appenzeller Sennenhund",
|
| 242 |
+
"Entlebucher Sennenhund",
|
| 243 |
+
"Boxer",
|
| 244 |
+
"Bullmastiff",
|
| 245 |
+
"Tibetan Mastiff",
|
| 246 |
+
"French Bulldog",
|
| 247 |
+
"Great Dane",
|
| 248 |
+
"St. Bernard",
|
| 249 |
+
"husky",
|
| 250 |
+
"Alaskan Malamute",
|
| 251 |
+
"Siberian Husky",
|
| 252 |
+
"Dalmatian",
|
| 253 |
+
"Affenpinscher",
|
| 254 |
+
"Basenji",
|
| 255 |
+
"pug",
|
| 256 |
+
"Leonberger",
|
| 257 |
+
"Newfoundland",
|
| 258 |
+
"Pyrenean Mountain Dog",
|
| 259 |
+
"Samoyed",
|
| 260 |
+
"Pomeranian",
|
| 261 |
+
"Chow Chow",
|
| 262 |
+
"Keeshond",
|
| 263 |
+
"Griffon Bruxellois",
|
| 264 |
+
"Pembroke Welsh Corgi",
|
| 265 |
+
"Cardigan Welsh Corgi",
|
| 266 |
+
"Toy Poodle",
|
| 267 |
+
"Miniature Poodle",
|
| 268 |
+
"Standard Poodle",
|
| 269 |
+
"Mexican hairless dog",
|
| 270 |
+
"grey wolf",
|
| 271 |
+
"Alaskan tundra wolf",
|
| 272 |
+
"red wolf",
|
| 273 |
+
"coyote",
|
| 274 |
+
"dingo",
|
| 275 |
+
"dhole",
|
| 276 |
+
"African wild dog",
|
| 277 |
+
"hyena",
|
| 278 |
+
"red fox",
|
| 279 |
+
"kit fox",
|
| 280 |
+
"Arctic fox",
|
| 281 |
+
"grey fox",
|
| 282 |
+
"tabby cat",
|
| 283 |
+
"tiger cat",
|
| 284 |
+
"Persian cat",
|
| 285 |
+
"Siamese cat",
|
| 286 |
+
"Egyptian Mau",
|
| 287 |
+
"cougar",
|
| 288 |
+
"lynx",
|
| 289 |
+
"leopard",
|
| 290 |
+
"snow leopard",
|
| 291 |
+
"jaguar",
|
| 292 |
+
"lion",
|
| 293 |
+
"tiger",
|
| 294 |
+
"cheetah",
|
| 295 |
+
"brown bear",
|
| 296 |
+
"American black bear",
|
| 297 |
+
"polar bear",
|
| 298 |
+
"sloth bear",
|
| 299 |
+
"mongoose",
|
| 300 |
+
"meerkat",
|
| 301 |
+
"tiger beetle",
|
| 302 |
+
"ladybug",
|
| 303 |
+
"ground beetle",
|
| 304 |
+
"longhorn beetle",
|
| 305 |
+
"leaf beetle",
|
| 306 |
+
"dung beetle",
|
| 307 |
+
"rhinoceros beetle",
|
| 308 |
+
"weevil",
|
| 309 |
+
"fly",
|
| 310 |
+
"bee",
|
| 311 |
+
"ant",
|
| 312 |
+
"grasshopper",
|
| 313 |
+
"cricket",
|
| 314 |
+
"stick insect",
|
| 315 |
+
"cockroach",
|
| 316 |
+
"mantis",
|
| 317 |
+
"cicada",
|
| 318 |
+
"leafhopper",
|
| 319 |
+
"lacewing",
|
| 320 |
+
"dragonfly",
|
| 321 |
+
"damselfly",
|
| 322 |
+
"red admiral",
|
| 323 |
+
"ringlet",
|
| 324 |
+
"monarch butterfly",
|
| 325 |
+
"small white",
|
| 326 |
+
"sulphur butterfly",
|
| 327 |
+
"gossamer-winged butterfly",
|
| 328 |
+
"starfish",
|
| 329 |
+
"sea urchin",
|
| 330 |
+
"sea cucumber",
|
| 331 |
+
"cottontail rabbit",
|
| 332 |
+
"hare",
|
| 333 |
+
"Angora rabbit",
|
| 334 |
+
"hamster",
|
| 335 |
+
"porcupine",
|
| 336 |
+
"fox squirrel",
|
| 337 |
+
"marmot",
|
| 338 |
+
"beaver",
|
| 339 |
+
"guinea pig",
|
| 340 |
+
"common sorrel",
|
| 341 |
+
"zebra",
|
| 342 |
+
"pig",
|
| 343 |
+
"wild boar",
|
| 344 |
+
"warthog",
|
| 345 |
+
"hippopotamus",
|
| 346 |
+
"ox",
|
| 347 |
+
"water buffalo",
|
| 348 |
+
"bison",
|
| 349 |
+
"ram",
|
| 350 |
+
"bighorn sheep",
|
| 351 |
+
"Alpine ibex",
|
| 352 |
+
"hartebeest",
|
| 353 |
+
"impala",
|
| 354 |
+
"gazelle",
|
| 355 |
+
"dromedary",
|
| 356 |
+
"llama",
|
| 357 |
+
"weasel",
|
| 358 |
+
"mink",
|
| 359 |
+
"European polecat",
|
| 360 |
+
"black-footed ferret",
|
| 361 |
+
"otter",
|
| 362 |
+
"skunk",
|
| 363 |
+
"badger",
|
| 364 |
+
"armadillo",
|
| 365 |
+
"three-toed sloth",
|
| 366 |
+
"orangutan",
|
| 367 |
+
"gorilla",
|
| 368 |
+
"chimpanzee",
|
| 369 |
+
"gibbon",
|
| 370 |
+
"siamang",
|
| 371 |
+
"guenon",
|
| 372 |
+
"patas monkey",
|
| 373 |
+
"baboon",
|
| 374 |
+
"macaque",
|
| 375 |
+
"langur",
|
| 376 |
+
"black-and-white colobus",
|
| 377 |
+
"proboscis monkey",
|
| 378 |
+
"marmoset",
|
| 379 |
+
"white-headed capuchin",
|
| 380 |
+
"howler monkey",
|
| 381 |
+
"titi",
|
| 382 |
+
"Geoffroy's spider monkey",
|
| 383 |
+
"common squirrel monkey",
|
| 384 |
+
"ring-tailed lemur",
|
| 385 |
+
"indri",
|
| 386 |
+
"Asian elephant",
|
| 387 |
+
"African bush elephant",
|
| 388 |
+
"red panda",
|
| 389 |
+
"giant panda",
|
| 390 |
+
"snoek",
|
| 391 |
+
"eel",
|
| 392 |
+
"coho salmon",
|
| 393 |
+
"rock beauty",
|
| 394 |
+
"clownfish",
|
| 395 |
+
"sturgeon",
|
| 396 |
+
"garfish",
|
| 397 |
+
"lionfish",
|
| 398 |
+
"pufferfish",
|
| 399 |
+
"abacus",
|
| 400 |
+
"abaya",
|
| 401 |
+
"academic gown",
|
| 402 |
+
"accordion",
|
| 403 |
+
"acoustic guitar",
|
| 404 |
+
"aircraft carrier",
|
| 405 |
+
"airliner",
|
| 406 |
+
"airship",
|
| 407 |
+
"altar",
|
| 408 |
+
"ambulance",
|
| 409 |
+
"amphibious vehicle",
|
| 410 |
+
"analog clock",
|
| 411 |
+
"apiary",
|
| 412 |
+
"apron",
|
| 413 |
+
"waste container",
|
| 414 |
+
"assault rifle",
|
| 415 |
+
"backpack",
|
| 416 |
+
"bakery",
|
| 417 |
+
"balance beam",
|
| 418 |
+
"balloon",
|
| 419 |
+
"ballpoint pen",
|
| 420 |
+
"Band-Aid",
|
| 421 |
+
"banjo",
|
| 422 |
+
"baluster",
|
| 423 |
+
"barbell",
|
| 424 |
+
"barber chair",
|
| 425 |
+
"barbershop",
|
| 426 |
+
"barn",
|
| 427 |
+
"barometer",
|
| 428 |
+
"barrel",
|
| 429 |
+
"wheelbarrow",
|
| 430 |
+
"baseball",
|
| 431 |
+
"basketball",
|
| 432 |
+
"bassinet",
|
| 433 |
+
"bassoon",
|
| 434 |
+
"swimming cap",
|
| 435 |
+
"bath towel",
|
| 436 |
+
"bathtub",
|
| 437 |
+
"station wagon",
|
| 438 |
+
"lighthouse",
|
| 439 |
+
"beaker",
|
| 440 |
+
"military cap",
|
| 441 |
+
"beer bottle",
|
| 442 |
+
"beer glass",
|
| 443 |
+
"bell-cot",
|
| 444 |
+
"bib",
|
| 445 |
+
"tandem bicycle",
|
| 446 |
+
"bikini",
|
| 447 |
+
"ring binder",
|
| 448 |
+
"binoculars",
|
| 449 |
+
"birdhouse",
|
| 450 |
+
"boathouse",
|
| 451 |
+
"bobsleigh",
|
| 452 |
+
"bolo tie",
|
| 453 |
+
"poke bonnet",
|
| 454 |
+
"bookcase",
|
| 455 |
+
"bookstore",
|
| 456 |
+
"bottle cap",
|
| 457 |
+
"bow",
|
| 458 |
+
"bow tie",
|
| 459 |
+
"brass",
|
| 460 |
+
"bra",
|
| 461 |
+
"breakwater",
|
| 462 |
+
"breastplate",
|
| 463 |
+
"broom",
|
| 464 |
+
"bucket",
|
| 465 |
+
"buckle",
|
| 466 |
+
"bulletproof vest",
|
| 467 |
+
"high-speed train",
|
| 468 |
+
"butcher shop",
|
| 469 |
+
"taxicab",
|
| 470 |
+
"cauldron",
|
| 471 |
+
"candle",
|
| 472 |
+
"cannon",
|
| 473 |
+
"canoe",
|
| 474 |
+
"can opener",
|
| 475 |
+
"cardigan",
|
| 476 |
+
"car mirror",
|
| 477 |
+
"carousel",
|
| 478 |
+
"tool kit",
|
| 479 |
+
"carton",
|
| 480 |
+
"car wheel",
|
| 481 |
+
"automated teller machine",
|
| 482 |
+
"cassette",
|
| 483 |
+
"cassette player",
|
| 484 |
+
"castle",
|
| 485 |
+
"catamaran",
|
| 486 |
+
"CD player",
|
| 487 |
+
"cello",
|
| 488 |
+
"mobile phone",
|
| 489 |
+
"chain",
|
| 490 |
+
"chain-link fence",
|
| 491 |
+
"chain mail",
|
| 492 |
+
"chainsaw",
|
| 493 |
+
"chest",
|
| 494 |
+
"chiffonier",
|
| 495 |
+
"chime",
|
| 496 |
+
"china cabinet",
|
| 497 |
+
"Christmas stocking",
|
| 498 |
+
"church",
|
| 499 |
+
"movie theater",
|
| 500 |
+
"cleaver",
|
| 501 |
+
"cliff dwelling",
|
| 502 |
+
"cloak",
|
| 503 |
+
"clogs",
|
| 504 |
+
"cocktail shaker",
|
| 505 |
+
"coffee mug",
|
| 506 |
+
"coffeemaker",
|
| 507 |
+
"coil",
|
| 508 |
+
"combination lock",
|
| 509 |
+
"computer keyboard",
|
| 510 |
+
"confectionery store",
|
| 511 |
+
"container ship",
|
| 512 |
+
"convertible",
|
| 513 |
+
"corkscrew",
|
| 514 |
+
"cornet",
|
| 515 |
+
"cowboy boot",
|
| 516 |
+
"cowboy hat",
|
| 517 |
+
"cradle",
|
| 518 |
+
"crane (machine)",
|
| 519 |
+
"crash helmet",
|
| 520 |
+
"crate",
|
| 521 |
+
"infant bed",
|
| 522 |
+
"Crock Pot",
|
| 523 |
+
"croquet ball",
|
| 524 |
+
"crutch",
|
| 525 |
+
"cuirass",
|
| 526 |
+
"dam",
|
| 527 |
+
"desk",
|
| 528 |
+
"desktop computer",
|
| 529 |
+
"rotary dial telephone",
|
| 530 |
+
"diaper",
|
| 531 |
+
"digital clock",
|
| 532 |
+
"digital watch",
|
| 533 |
+
"dining table",
|
| 534 |
+
"dishcloth",
|
| 535 |
+
"dishwasher",
|
| 536 |
+
"disc brake",
|
| 537 |
+
"dock",
|
| 538 |
+
"dog sled",
|
| 539 |
+
"dome",
|
| 540 |
+
"doormat",
|
| 541 |
+
"drilling rig",
|
| 542 |
+
"drum",
|
| 543 |
+
"drumstick",
|
| 544 |
+
"dumbbell",
|
| 545 |
+
"Dutch oven",
|
| 546 |
+
"electric fan",
|
| 547 |
+
"electric guitar",
|
| 548 |
+
"electric locomotive",
|
| 549 |
+
"entertainment center",
|
| 550 |
+
"envelope",
|
| 551 |
+
"espresso machine",
|
| 552 |
+
"face powder",
|
| 553 |
+
"feather boa",
|
| 554 |
+
"filing cabinet",
|
| 555 |
+
"fireboat",
|
| 556 |
+
"fire engine",
|
| 557 |
+
"fire screen sheet",
|
| 558 |
+
"flagpole",
|
| 559 |
+
"flute",
|
| 560 |
+
"folding chair",
|
| 561 |
+
"football helmet",
|
| 562 |
+
"forklift",
|
| 563 |
+
"fountain",
|
| 564 |
+
"fountain pen",
|
| 565 |
+
"four-poster bed",
|
| 566 |
+
"freight car",
|
| 567 |
+
"French horn",
|
| 568 |
+
"frying pan",
|
| 569 |
+
"fur coat",
|
| 570 |
+
"garbage truck",
|
| 571 |
+
"gas mask",
|
| 572 |
+
"gas pump",
|
| 573 |
+
"goblet",
|
| 574 |
+
"go-kart",
|
| 575 |
+
"golf ball",
|
| 576 |
+
"golf cart",
|
| 577 |
+
"gondola",
|
| 578 |
+
"gong",
|
| 579 |
+
"gown",
|
| 580 |
+
"grand piano",
|
| 581 |
+
"greenhouse",
|
| 582 |
+
"grille",
|
| 583 |
+
"grocery store",
|
| 584 |
+
"guillotine",
|
| 585 |
+
"barrette",
|
| 586 |
+
"hair spray",
|
| 587 |
+
"half-track",
|
| 588 |
+
"hammer",
|
| 589 |
+
"hamper",
|
| 590 |
+
"hair dryer",
|
| 591 |
+
"hand-held computer",
|
| 592 |
+
"handkerchief",
|
| 593 |
+
"hard disk drive",
|
| 594 |
+
"harmonica",
|
| 595 |
+
"harp",
|
| 596 |
+
"harvester",
|
| 597 |
+
"hatchet",
|
| 598 |
+
"holster",
|
| 599 |
+
"home theater",
|
| 600 |
+
"honeycomb",
|
| 601 |
+
"hook",
|
| 602 |
+
"hoop skirt",
|
| 603 |
+
"horizontal bar",
|
| 604 |
+
"horse-drawn vehicle",
|
| 605 |
+
"hourglass",
|
| 606 |
+
"iPod",
|
| 607 |
+
"clothes iron",
|
| 608 |
+
"jack-o'-lantern",
|
| 609 |
+
"jeans",
|
| 610 |
+
"jeep",
|
| 611 |
+
"T-shirt",
|
| 612 |
+
"jigsaw puzzle",
|
| 613 |
+
"pulled rickshaw",
|
| 614 |
+
"joystick",
|
| 615 |
+
"kimono",
|
| 616 |
+
"knee pad",
|
| 617 |
+
"knot",
|
| 618 |
+
"lab coat",
|
| 619 |
+
"ladle",
|
| 620 |
+
"lampshade",
|
| 621 |
+
"laptop computer",
|
| 622 |
+
"lawn mower",
|
| 623 |
+
"lens cap",
|
| 624 |
+
"paper knife",
|
| 625 |
+
"library",
|
| 626 |
+
"lifeboat",
|
| 627 |
+
"lighter",
|
| 628 |
+
"limousine",
|
| 629 |
+
"ocean liner",
|
| 630 |
+
"lipstick",
|
| 631 |
+
"slip-on shoe",
|
| 632 |
+
"lotion",
|
| 633 |
+
"speaker",
|
| 634 |
+
"loupe",
|
| 635 |
+
"sawmill",
|
| 636 |
+
"magnetic compass",
|
| 637 |
+
"mail bag",
|
| 638 |
+
"mailbox",
|
| 639 |
+
"tights",
|
| 640 |
+
"tank suit",
|
| 641 |
+
"manhole cover",
|
| 642 |
+
"maraca",
|
| 643 |
+
"marimba",
|
| 644 |
+
"mask",
|
| 645 |
+
"match",
|
| 646 |
+
"maypole",
|
| 647 |
+
"maze",
|
| 648 |
+
"measuring cup",
|
| 649 |
+
"medicine chest",
|
| 650 |
+
"megalith",
|
| 651 |
+
"microphone",
|
| 652 |
+
"microwave oven",
|
| 653 |
+
"military uniform",
|
| 654 |
+
"milk can",
|
| 655 |
+
"minibus",
|
| 656 |
+
"miniskirt",
|
| 657 |
+
"minivan",
|
| 658 |
+
"missile",
|
| 659 |
+
"mitten",
|
| 660 |
+
"mixing bowl",
|
| 661 |
+
"mobile home",
|
| 662 |
+
"Model T",
|
| 663 |
+
"modem",
|
| 664 |
+
"monastery",
|
| 665 |
+
"monitor",
|
| 666 |
+
"moped",
|
| 667 |
+
"mortar",
|
| 668 |
+
"square academic cap",
|
| 669 |
+
"mosque",
|
| 670 |
+
"mosquito net",
|
| 671 |
+
"scooter",
|
| 672 |
+
"mountain bike",
|
| 673 |
+
"tent",
|
| 674 |
+
"computer mouse",
|
| 675 |
+
"mousetrap",
|
| 676 |
+
"moving van",
|
| 677 |
+
"muzzle",
|
| 678 |
+
"nail",
|
| 679 |
+
"neck brace",
|
| 680 |
+
"necklace",
|
| 681 |
+
"nipple",
|
| 682 |
+
"notebook computer",
|
| 683 |
+
"obelisk",
|
| 684 |
+
"oboe",
|
| 685 |
+
"ocarina",
|
| 686 |
+
"odometer",
|
| 687 |
+
"oil filter",
|
| 688 |
+
"organ",
|
| 689 |
+
"oscilloscope",
|
| 690 |
+
"overskirt",
|
| 691 |
+
"bullock cart",
|
| 692 |
+
"oxygen mask",
|
| 693 |
+
"packet",
|
| 694 |
+
"paddle",
|
| 695 |
+
"paddle wheel",
|
| 696 |
+
"padlock",
|
| 697 |
+
"paintbrush",
|
| 698 |
+
"pajamas",
|
| 699 |
+
"palace",
|
| 700 |
+
"pan flute",
|
| 701 |
+
"paper towel",
|
| 702 |
+
"parachute",
|
| 703 |
+
"parallel bars",
|
| 704 |
+
"park bench",
|
| 705 |
+
"parking meter",
|
| 706 |
+
"passenger car",
|
| 707 |
+
"patio",
|
| 708 |
+
"payphone",
|
| 709 |
+
"pedestal",
|
| 710 |
+
"pencil case",
|
| 711 |
+
"pencil sharpener",
|
| 712 |
+
"perfume",
|
| 713 |
+
"Petri dish",
|
| 714 |
+
"photocopier",
|
| 715 |
+
"plectrum",
|
| 716 |
+
"Pickelhaube",
|
| 717 |
+
"picket fence",
|
| 718 |
+
"pickup truck",
|
| 719 |
+
"pier",
|
| 720 |
+
"piggy bank",
|
| 721 |
+
"pill bottle",
|
| 722 |
+
"pillow",
|
| 723 |
+
"ping-pong ball",
|
| 724 |
+
"pinwheel",
|
| 725 |
+
"pirate ship",
|
| 726 |
+
"pitcher",
|
| 727 |
+
"hand plane",
|
| 728 |
+
"planetarium",
|
| 729 |
+
"plastic bag",
|
| 730 |
+
"plate rack",
|
| 731 |
+
"plow",
|
| 732 |
+
"plunger",
|
| 733 |
+
"Polaroid camera",
|
| 734 |
+
"pole",
|
| 735 |
+
"police van",
|
| 736 |
+
"poncho",
|
| 737 |
+
"billiard table",
|
| 738 |
+
"soda bottle",
|
| 739 |
+
"pot",
|
| 740 |
+
"potter's wheel",
|
| 741 |
+
"power drill",
|
| 742 |
+
"prayer rug",
|
| 743 |
+
"printer",
|
| 744 |
+
"prison",
|
| 745 |
+
"projectile",
|
| 746 |
+
"projector",
|
| 747 |
+
"hockey puck",
|
| 748 |
+
"punching bag",
|
| 749 |
+
"purse",
|
| 750 |
+
"quill",
|
| 751 |
+
"quilt",
|
| 752 |
+
"race car",
|
| 753 |
+
"racket",
|
| 754 |
+
"radiator",
|
| 755 |
+
"radio",
|
| 756 |
+
"radio telescope",
|
| 757 |
+
"rain barrel",
|
| 758 |
+
"recreational vehicle",
|
| 759 |
+
"reel",
|
| 760 |
+
"reflex camera",
|
| 761 |
+
"refrigerator",
|
| 762 |
+
"remote control",
|
| 763 |
+
"restaurant",
|
| 764 |
+
"revolver",
|
| 765 |
+
"rifle",
|
| 766 |
+
"rocking chair",
|
| 767 |
+
"rotisserie",
|
| 768 |
+
"eraser",
|
| 769 |
+
"rugby ball",
|
| 770 |
+
"ruler",
|
| 771 |
+
"running shoe",
|
| 772 |
+
"safe",
|
| 773 |
+
"safety pin",
|
| 774 |
+
"salt shaker",
|
| 775 |
+
"sandal",
|
| 776 |
+
"sarong",
|
| 777 |
+
"saxophone",
|
| 778 |
+
"scabbard",
|
| 779 |
+
"weighing scale",
|
| 780 |
+
"school bus",
|
| 781 |
+
"schooner",
|
| 782 |
+
"scoreboard",
|
| 783 |
+
"CRT screen",
|
| 784 |
+
"screw",
|
| 785 |
+
"screwdriver",
|
| 786 |
+
"seat belt",
|
| 787 |
+
"sewing machine",
|
| 788 |
+
"shield",
|
| 789 |
+
"shoe store",
|
| 790 |
+
"shoji",
|
| 791 |
+
"shopping basket",
|
| 792 |
+
"shopping cart",
|
| 793 |
+
"shovel",
|
| 794 |
+
"shower cap",
|
| 795 |
+
"shower curtain",
|
| 796 |
+
"ski",
|
| 797 |
+
"ski mask",
|
| 798 |
+
"sleeping bag",
|
| 799 |
+
"slide rule",
|
| 800 |
+
"sliding door",
|
| 801 |
+
"slot machine",
|
| 802 |
+
"snorkel",
|
| 803 |
+
"snowmobile",
|
| 804 |
+
"snowplow",
|
| 805 |
+
"soap dispenser",
|
| 806 |
+
"soccer ball",
|
| 807 |
+
"sock",
|
| 808 |
+
"solar thermal collector",
|
| 809 |
+
"sombrero",
|
| 810 |
+
"soup bowl",
|
| 811 |
+
"space bar",
|
| 812 |
+
"space heater",
|
| 813 |
+
"space shuttle",
|
| 814 |
+
"spatula",
|
| 815 |
+
"motorboat",
|
| 816 |
+
"spider web",
|
| 817 |
+
"spindle",
|
| 818 |
+
"sports car",
|
| 819 |
+
"spotlight",
|
| 820 |
+
"stage",
|
| 821 |
+
"steam locomotive",
|
| 822 |
+
"through arch bridge",
|
| 823 |
+
"steel drum",
|
| 824 |
+
"stethoscope",
|
| 825 |
+
"scarf",
|
| 826 |
+
"stone wall",
|
| 827 |
+
"stopwatch",
|
| 828 |
+
"stove",
|
| 829 |
+
"strainer",
|
| 830 |
+
"tram",
|
| 831 |
+
"stretcher",
|
| 832 |
+
"couch",
|
| 833 |
+
"stupa",
|
| 834 |
+
"submarine",
|
| 835 |
+
"suit",
|
| 836 |
+
"sundial",
|
| 837 |
+
"sunglass",
|
| 838 |
+
"sunglasses",
|
| 839 |
+
"sunscreen",
|
| 840 |
+
"suspension bridge",
|
| 841 |
+
"mop",
|
| 842 |
+
"sweatshirt",
|
| 843 |
+
"swimsuit",
|
| 844 |
+
"swing",
|
| 845 |
+
"switch",
|
| 846 |
+
"syringe",
|
| 847 |
+
"table lamp",
|
| 848 |
+
"tank",
|
| 849 |
+
"tape player",
|
| 850 |
+
"teapot",
|
| 851 |
+
"teddy bear",
|
| 852 |
+
"television",
|
| 853 |
+
"tennis ball",
|
| 854 |
+
"thatched roof",
|
| 855 |
+
"front curtain",
|
| 856 |
+
"thimble",
|
| 857 |
+
"threshing machine",
|
| 858 |
+
"throne",
|
| 859 |
+
"tile roof",
|
| 860 |
+
"toaster",
|
| 861 |
+
"tobacco shop",
|
| 862 |
+
"toilet seat",
|
| 863 |
+
"torch",
|
| 864 |
+
"totem pole",
|
| 865 |
+
"tow truck",
|
| 866 |
+
"toy store",
|
| 867 |
+
"tractor",
|
| 868 |
+
"semi-trailer truck",
|
| 869 |
+
"tray",
|
| 870 |
+
"trench coat",
|
| 871 |
+
"tricycle",
|
| 872 |
+
"trimaran",
|
| 873 |
+
"tripod",
|
| 874 |
+
"triumphal arch",
|
| 875 |
+
"trolleybus",
|
| 876 |
+
"trombone",
|
| 877 |
+
"tub",
|
| 878 |
+
"turnstile",
|
| 879 |
+
"typewriter keyboard",
|
| 880 |
+
"umbrella",
|
| 881 |
+
"unicycle",
|
| 882 |
+
"upright piano",
|
| 883 |
+
"vacuum cleaner",
|
| 884 |
+
"vase",
|
| 885 |
+
"vault",
|
| 886 |
+
"velvet",
|
| 887 |
+
"vending machine",
|
| 888 |
+
"vestment",
|
| 889 |
+
"viaduct",
|
| 890 |
+
"violin",
|
| 891 |
+
"volleyball",
|
| 892 |
+
"waffle iron",
|
| 893 |
+
"wall clock",
|
| 894 |
+
"wallet",
|
| 895 |
+
"wardrobe",
|
| 896 |
+
"military aircraft",
|
| 897 |
+
"sink",
|
| 898 |
+
"washing machine",
|
| 899 |
+
"water bottle",
|
| 900 |
+
"water jug",
|
| 901 |
+
"water tower",
|
| 902 |
+
"whiskey jug",
|
| 903 |
+
"whistle",
|
| 904 |
+
"wig",
|
| 905 |
+
"window screen",
|
| 906 |
+
"window shade",
|
| 907 |
+
"Windsor tie",
|
| 908 |
+
"wine bottle",
|
| 909 |
+
"wing",
|
| 910 |
+
"wok",
|
| 911 |
+
"wooden spoon",
|
| 912 |
+
"wool",
|
| 913 |
+
"split-rail fence",
|
| 914 |
+
"shipwreck",
|
| 915 |
+
"yawl",
|
| 916 |
+
"yurt",
|
| 917 |
+
"website",
|
| 918 |
+
"comic book",
|
| 919 |
+
"crossword",
|
| 920 |
+
"traffic sign",
|
| 921 |
+
"traffic light",
|
| 922 |
+
"dust jacket",
|
| 923 |
+
"menu",
|
| 924 |
+
"plate",
|
| 925 |
+
"guacamole",
|
| 926 |
+
"consomme",
|
| 927 |
+
"hot pot",
|
| 928 |
+
"trifle",
|
| 929 |
+
"ice cream",
|
| 930 |
+
"ice pop",
|
| 931 |
+
"baguette",
|
| 932 |
+
"bagel",
|
| 933 |
+
"pretzel",
|
| 934 |
+
"cheeseburger",
|
| 935 |
+
"hot dog",
|
| 936 |
+
"mashed potato",
|
| 937 |
+
"cabbage",
|
| 938 |
+
"broccoli",
|
| 939 |
+
"cauliflower",
|
| 940 |
+
"zucchini",
|
| 941 |
+
"spaghetti squash",
|
| 942 |
+
"acorn squash",
|
| 943 |
+
"butternut squash",
|
| 944 |
+
"cucumber",
|
| 945 |
+
"artichoke",
|
| 946 |
+
"bell pepper",
|
| 947 |
+
"cardoon",
|
| 948 |
+
"mushroom",
|
| 949 |
+
"Granny Smith",
|
| 950 |
+
"strawberry",
|
| 951 |
+
"orange",
|
| 952 |
+
"lemon",
|
| 953 |
+
"fig",
|
| 954 |
+
"pineapple",
|
| 955 |
+
"banana",
|
| 956 |
+
"jackfruit",
|
| 957 |
+
"custard apple",
|
| 958 |
+
"pomegranate",
|
| 959 |
+
"hay",
|
| 960 |
+
"carbonara",
|
| 961 |
+
"chocolate syrup",
|
| 962 |
+
"dough",
|
| 963 |
+
"meatloaf",
|
| 964 |
+
"pizza",
|
| 965 |
+
"pot pie",
|
| 966 |
+
"burrito",
|
| 967 |
+
"red wine",
|
| 968 |
+
"espresso",
|
| 969 |
+
"cup",
|
| 970 |
+
"eggnog",
|
| 971 |
+
"alp",
|
| 972 |
+
"bubble",
|
| 973 |
+
"cliff",
|
| 974 |
+
"coral reef",
|
| 975 |
+
"geyser",
|
| 976 |
+
"lakeshore",
|
| 977 |
+
"promontory",
|
| 978 |
+
"shoal",
|
| 979 |
+
"seashore",
|
| 980 |
+
"valley",
|
| 981 |
+
"volcano",
|
| 982 |
+
"baseball player",
|
| 983 |
+
"bridegroom",
|
| 984 |
+
"scuba diver",
|
| 985 |
+
"rapeseed",
|
| 986 |
+
"daisy",
|
| 987 |
+
"yellow lady's slipper",
|
| 988 |
+
"corn",
|
| 989 |
+
"acorn",
|
| 990 |
+
"rose hip",
|
| 991 |
+
"horse chestnut seed",
|
| 992 |
+
"coral fungus",
|
| 993 |
+
"agaric",
|
| 994 |
+
"gyromitra",
|
| 995 |
+
"stinkhorn mushroom",
|
| 996 |
+
"earth star",
|
| 997 |
+
"hen-of-the-woods",
|
| 998 |
+
"bolete",
|
| 999 |
+
"ear of corn",
|
| 1000 |
+
"toilet paper"]
|
pre_run.sh
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# HF Spaces pre-run hook — installs mmcv-full with the correct CUDA/torch wheel
|
| 3 |
+
set -e
|
| 4 |
+
echo "Installing mmcv-full via openmim..."
|
| 5 |
+
mim install mmcv-full==1.7.2
|
| 6 |
+
echo "mmcv-full installed successfully."
|
requirements.txt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Core — classification tab (always works)
|
| 2 |
+
torch>=1.12.0
|
| 3 |
+
torchvision>=0.13.0
|
| 4 |
+
timm==0.6.12
|
| 5 |
+
gradio>=4.0.0
|
| 6 |
+
huggingface_hub>=0.20.0
|
| 7 |
+
Pillow>=10.0.0
|
| 8 |
+
numpy>=1.23.0
|
| 9 |
+
scipy
|
| 10 |
+
|
| 11 |
+
# Detection & Medical tabs
|
| 12 |
+
openmim
|
| 13 |
+
mmdet==2.28.0
|
| 14 |
+
pycocotools
|
| 15 |
+
opencv-python-headless
|