|
import sys |
|
from pathlib import Path |
|
import subprocess |
|
import logging |
|
import torch |
|
from PIL import Image |
|
from ..utils.base_model import BaseModel |
|
import torchvision.transforms as transforms |
|
|
|
dedode_path = Path(__file__).parent / "../../third_party/DeDoDe" |
|
sys.path.append(str(dedode_path)) |
|
|
|
from DeDoDe import dedode_detector_L, dedode_descriptor_B |
|
from DeDoDe.utils import to_pixel_coords |
|
|
|
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
class DeDoDe(BaseModel): |
|
default_conf = { |
|
"name": "dedode", |
|
"model_detector_name": "dedode_detector_L.pth", |
|
"model_descriptor_name": "dedode_descriptor_B.pth", |
|
"max_keypoints": 2000, |
|
"match_threshold": 0.2, |
|
"dense": False, |
|
} |
|
required_inputs = [ |
|
"image", |
|
] |
|
weight_urls = { |
|
"dedode_detector_L.pth": "https://github.com/Parskatt/DeDoDe/releases/download/dedode_pretrained_models/dedode_detector_L.pth", |
|
"dedode_descriptor_B.pth": "https://github.com/Parskatt/DeDoDe/releases/download/dedode_pretrained_models/dedode_descriptor_B.pth", |
|
} |
|
|
|
|
|
def _init(self, conf): |
|
model_detector_path = dedode_path / "pretrained" / conf["model_detector_name"] |
|
model_descriptor_path = ( |
|
dedode_path / "pretrained" / conf["model_descriptor_name"] |
|
) |
|
|
|
self.normalizer = transforms.Normalize( |
|
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] |
|
) |
|
|
|
if not model_detector_path.exists(): |
|
model_detector_path.parent.mkdir(exist_ok=True) |
|
link = self.weight_urls[conf["model_detector_name"]] |
|
cmd = ["wget", link, "-O", str(model_detector_path)] |
|
logger.info(f"Downloading the DeDoDe detector model with `{cmd}`.") |
|
subprocess.run(cmd, check=True) |
|
|
|
if not model_descriptor_path.exists(): |
|
model_descriptor_path.parent.mkdir(exist_ok=True) |
|
link = self.weight_urls[conf["model_descriptor_name"]] |
|
cmd = ["wget", link, "-O", str(model_descriptor_path)] |
|
logger.info(f"Downloading the DeDoDe descriptor model with `{cmd}`.") |
|
subprocess.run(cmd, check=True) |
|
|
|
logger.info(f"Loading DeDoDe model...") |
|
|
|
|
|
weights_detector = torch.load(model_detector_path, map_location="cpu") |
|
weights_descriptor = torch.load(model_descriptor_path, map_location="cpu") |
|
self.detector = dedode_detector_L(weights=weights_detector, device = device) |
|
self.descriptor = dedode_descriptor_B(weights=weights_descriptor, device = device) |
|
logger.info(f"Load DeDoDe model done.") |
|
|
|
def _forward(self, data): |
|
""" |
|
data: dict, keys: {'image0','image1'} |
|
image shape: N x C x H x W |
|
color mode: RGB |
|
""" |
|
img0 = self.normalizer(data["image"].squeeze()).float()[None] |
|
H_A, W_A = img0.shape[2:] |
|
|
|
|
|
detections_A = None |
|
batch_A = {"image": img0} |
|
if self.conf["dense"]: |
|
detections_A = self.detector.detect_dense(batch_A) |
|
else: |
|
detections_A = self.detector.detect( |
|
batch_A, num_keypoints=self.conf["max_keypoints"] |
|
) |
|
keypoints_A, P_A = detections_A["keypoints"], detections_A["confidence"] |
|
|
|
|
|
|
|
description_A = self.descriptor.describe_keypoints(batch_A, keypoints_A)[ |
|
"descriptions" |
|
] |
|
keypoints_A = to_pixel_coords(keypoints_A, H_A, W_A) |
|
|
|
return { |
|
"keypoints": keypoints_A, |
|
"descriptors": description_A.permute(0, 2, 1), |
|
"scores": P_A, |
|
} |
|
|