Realcat commited on
Commit
3c77caa
1 Parent(s): b5957bd

add: dkm & ense matcher max_keypoints

Browse files
common/app_class.py CHANGED
@@ -162,7 +162,7 @@ class ImageMatchingApp:
162
 
163
  with gr.Accordion("Geometry Setting", open=False):
164
  with gr.Row(equal_height=False):
165
- choice_estimate_geom = gr.Radio(
166
  ["Fundamental", "Homography"],
167
  label="Reconstruct Geometry",
168
  value=self.cfg["defaults"][
@@ -182,7 +182,7 @@ class ImageMatchingApp:
182
  ransac_reproj_threshold,
183
  ransac_confidence,
184
  ransac_max_iter,
185
- choice_estimate_geom,
186
  gr.State(self.matcher_zoo),
187
  ]
188
 
@@ -282,20 +282,20 @@ class ImageMatchingApp:
282
  ransac_reproj_threshold,
283
  ransac_confidence,
284
  ransac_max_iter,
285
- choice_estimate_geom,
286
  ]
287
  button_reset.click(
288
  fn=self.ui_reset_state, inputs=None, outputs=reset_outputs
289
  )
290
 
291
  # estimate geo
292
- choice_estimate_geom.change(
293
  fn=change_estimate_geom,
294
  inputs=[
295
  input_image0,
296
  input_image1,
297
  geometry_result,
298
- choice_estimate_geom,
299
  ],
300
  outputs=[output_wrapped, geometry_result],
301
  )
@@ -441,12 +441,12 @@ class ImageMatchingApp:
441
  v["info"]["name"],
442
  v["info"]["source"],
443
  v["info"]["github"],
444
- v["info"]["project"],
445
  v["info"]["paper"],
 
446
  ]
447
  )
448
  tab = gr.Dataframe(
449
- headers=["Algo.", "Conference", "Code", "Project", "Paper"],
450
  datatype=["str", "str", "str", "str", "str"],
451
  col_count=(5, "fixed"),
452
  value=data,
 
162
 
163
  with gr.Accordion("Geometry Setting", open=False):
164
  with gr.Row(equal_height=False):
165
+ choice_geometry_type = gr.Radio(
166
  ["Fundamental", "Homography"],
167
  label="Reconstruct Geometry",
168
  value=self.cfg["defaults"][
 
182
  ransac_reproj_threshold,
183
  ransac_confidence,
184
  ransac_max_iter,
185
+ choice_geometry_type,
186
  gr.State(self.matcher_zoo),
187
  ]
188
 
 
282
  ransac_reproj_threshold,
283
  ransac_confidence,
284
  ransac_max_iter,
285
+ choice_geometry_type,
286
  ]
287
  button_reset.click(
288
  fn=self.ui_reset_state, inputs=None, outputs=reset_outputs
289
  )
290
 
291
  # estimate geo
292
+ choice_geometry_type.change(
293
  fn=change_estimate_geom,
294
  inputs=[
295
  input_image0,
296
  input_image1,
297
  geometry_result,
298
+ choice_geometry_type,
299
  ],
300
  outputs=[output_wrapped, geometry_result],
301
  )
 
441
  v["info"]["name"],
442
  v["info"]["source"],
443
  v["info"]["github"],
 
444
  v["info"]["paper"],
445
+ v["info"]["project"],
446
  ]
447
  )
448
  tab = gr.Dataframe(
449
+ headers=["Algo.", "Conference", "Code", "Paper", "Project"],
450
  datatype=["str", "str", "str", "str", "str"],
451
  col_count=(5, "fixed"),
452
  value=data,
common/config.yaml CHANGED
@@ -1,6 +1,6 @@
1
  server:
2
  name: "0.0.0.0"
3
- port: 7860
4
 
5
  defaults:
6
  setting_threshold: 0.1
@@ -26,6 +26,16 @@ matcher_zoo:
26
  paper: https://arxiv.org/abs/2305.15404
27
  project: https://parskatt.github.io/RoMa
28
  display: true
 
 
 
 
 
 
 
 
 
 
29
  loftr:
30
  matcher: loftr
31
  dense: true
 
1
  server:
2
  name: "0.0.0.0"
3
+ port: 7861
4
 
5
  defaults:
6
  setting_threshold: 0.1
 
26
  paper: https://arxiv.org/abs/2305.15404
27
  project: https://parskatt.github.io/RoMa
28
  display: true
29
+ dkm:
30
+ matcher: dkm
31
+ dense: true
32
+ info:
33
+ name: DKM #dispaly name
34
+ source: "CVPR 2023"
35
+ github: https://github.com/Parskatt/DKM
36
+ paper: https://arxiv.org/abs/2202.00667
37
+ project: https://parskatt.github.io/DKM
38
+ display: true
39
  loftr:
40
  matcher: loftr
41
  dense: true
common/utils.py CHANGED
@@ -21,6 +21,7 @@ from .viz import (
21
  import time
22
  import matplotlib.pyplot as plt
23
  import warnings
 
24
  warnings.simplefilter("ignore")
25
  device = "cuda" if torch.cuda.is_available() else "cpu"
26
 
@@ -41,6 +42,7 @@ GRADIO_VERSION = gr.__version__.split(".")[0]
41
  MATCHER_ZOO = None
42
  models_already_loaded = {}
43
 
 
44
  def load_config(config_name: str) -> Dict[str, Any]:
45
  """
46
  Load a YAML configuration file.
@@ -417,7 +419,7 @@ def run_matching(
417
  ransac_reproj_threshold: int = DEFAULT_RANSAC_REPROJ_THRESHOLD,
418
  ransac_confidence: float = DEFAULT_RANSAC_CONFIDENCE,
419
  ransac_max_iter: int = DEFAULT_RANSAC_MAX_ITER,
420
- choice_estimate_geom: str = DEFAULT_SETTING_GEOMETRY,
421
  matcher_zoo: Dict[str, Any] = None,
422
  ) -> Tuple[
423
  np.ndarray,
@@ -441,7 +443,7 @@ def run_matching(
441
  ransac_reproj_threshold (int, optional): RANSAC reprojection threshold.
442
  ransac_confidence (float, optional): RANSAC confidence level.
443
  ransac_max_iter (int, optional): RANSAC maximum number of iterations.
444
- choice_estimate_geom (str, optional): setting of geometry estimation.
445
 
446
  Returns:
447
  tuple:
@@ -476,8 +478,8 @@ def run_matching(
476
  cache_key = match_conf["model"]["name"]
477
  if cache_key in models_already_loaded:
478
  matcher = models_already_loaded[cache_key]
479
- matcher.conf['max_keypoints'] = extract_max_keypoints
480
- matcher.conf['match_threshold'] = match_threshold
481
  logger.info(f"Loaded cached model {cache_key}")
482
  else:
483
  matcher = get_model(match_conf)
@@ -485,7 +487,7 @@ def run_matching(
485
  gr.Info(f"Loading model using: {time.time()-t0:.3f}s")
486
  logger.info(f"Loading model using: {time.time()-t0:.3f}s")
487
  t1 = time.time()
488
-
489
  if model["dense"]:
490
  pred = match_dense.match_images(
491
  matcher, image0, image1, match_conf["preprocessing"], device=device
@@ -500,8 +502,8 @@ def run_matching(
500
  cache_key = extract_conf["model"]["name"]
501
  if cache_key in models_already_loaded:
502
  extractor = models_already_loaded[cache_key]
503
- extractor.conf['max_keypoints'] = extract_max_keypoints
504
- extractor.conf['keypoint_threshold'] = keypoint_threshold
505
  logger.info(f"Loaded cached model {cache_key}")
506
  else:
507
  extractor = get_feature_model(extract_conf)
@@ -570,10 +572,8 @@ def run_matching(
570
  pred["image0_orig"],
571
  pred["image1_orig"],
572
  {"geom_info": geom_info},
573
- choice_estimate_geom,
574
  )
575
- gr.Info(f"Compute geometry done using: {time.time()-t1:.3f}s")
576
- logger.info(f"Compute geometry done using: {time.time()-t1:.3f}s")
577
  plt.close("all")
578
  del pred
579
  logger.info(f"TOTAL time: {time.time()-t0:.3f}s")
 
21
  import time
22
  import matplotlib.pyplot as plt
23
  import warnings
24
+
25
  warnings.simplefilter("ignore")
26
  device = "cuda" if torch.cuda.is_available() else "cpu"
27
 
 
42
  MATCHER_ZOO = None
43
  models_already_loaded = {}
44
 
45
+
46
  def load_config(config_name: str) -> Dict[str, Any]:
47
  """
48
  Load a YAML configuration file.
 
419
  ransac_reproj_threshold: int = DEFAULT_RANSAC_REPROJ_THRESHOLD,
420
  ransac_confidence: float = DEFAULT_RANSAC_CONFIDENCE,
421
  ransac_max_iter: int = DEFAULT_RANSAC_MAX_ITER,
422
+ choice_geometry_type: str = DEFAULT_SETTING_GEOMETRY,
423
  matcher_zoo: Dict[str, Any] = None,
424
  ) -> Tuple[
425
  np.ndarray,
 
443
  ransac_reproj_threshold (int, optional): RANSAC reprojection threshold.
444
  ransac_confidence (float, optional): RANSAC confidence level.
445
  ransac_max_iter (int, optional): RANSAC maximum number of iterations.
446
+ choice_geometry_type (str, optional): setting of geometry estimation.
447
 
448
  Returns:
449
  tuple:
 
478
  cache_key = match_conf["model"]["name"]
479
  if cache_key in models_already_loaded:
480
  matcher = models_already_loaded[cache_key]
481
+ matcher.conf["max_keypoints"] = extract_max_keypoints
482
+ matcher.conf["match_threshold"] = match_threshold
483
  logger.info(f"Loaded cached model {cache_key}")
484
  else:
485
  matcher = get_model(match_conf)
 
487
  gr.Info(f"Loading model using: {time.time()-t0:.3f}s")
488
  logger.info(f"Loading model using: {time.time()-t0:.3f}s")
489
  t1 = time.time()
490
+
491
  if model["dense"]:
492
  pred = match_dense.match_images(
493
  matcher, image0, image1, match_conf["preprocessing"], device=device
 
502
  cache_key = extract_conf["model"]["name"]
503
  if cache_key in models_already_loaded:
504
  extractor = models_already_loaded[cache_key]
505
+ extractor.conf["max_keypoints"] = extract_max_keypoints
506
+ extractor.conf["keypoint_threshold"] = keypoint_threshold
507
  logger.info(f"Loaded cached model {cache_key}")
508
  else:
509
  extractor = get_feature_model(extract_conf)
 
572
  pred["image0_orig"],
573
  pred["image1_orig"],
574
  {"geom_info": geom_info},
575
+ choice_geometry_type,
576
  )
 
 
577
  plt.close("all")
578
  del pred
579
  logger.info(f"TOTAL time: {time.time()-t0:.3f}s")
hloc/match_dense.py CHANGED
@@ -368,7 +368,7 @@ def match_images(model, image_0, image_1, conf, device="cpu"):
368
  }
369
  if "mconf" in pred.keys():
370
  ret["mconf"] = pred["mconf"].cpu().numpy()
371
- elif "scores" in pred.keys(): #adapting loftr
372
  ret["mconf"] = pred["scores"].cpu().numpy()
373
  else:
374
  ret["mconf"] = np.ones_like(kpts0.cpu().numpy()[:, 0])
 
368
  }
369
  if "mconf" in pred.keys():
370
  ret["mconf"] = pred["mconf"].cpu().numpy()
371
+ elif "scores" in pred.keys(): # adapting loftr
372
  ret["mconf"] = pred["scores"].cpu().numpy()
373
  else:
374
  ret["mconf"] = np.ones_like(kpts0.cpu().numpy()[:, 0])
hloc/matchers/aspanformer.py CHANGED
@@ -69,7 +69,6 @@ class ASpanFormer(BaseModel):
69
 
70
  do_system(f"cd {str(aspanformer_path)} & tar -xvf {str(tar_path)}")
71
 
72
-
73
  config = get_cfg_defaults()
74
  config.merge_from_file(conf["config_path"])
75
  _config = lower_config(config)
@@ -99,4 +98,14 @@ class ASpanFormer(BaseModel):
99
  "keypoints1": data_["mkpts1_f"],
100
  "mconf": data_["mconf"],
101
  }
 
 
 
 
 
 
 
 
 
 
102
  return pred
 
69
 
70
  do_system(f"cd {str(aspanformer_path)} & tar -xvf {str(tar_path)}")
71
 
 
72
  config = get_cfg_defaults()
73
  config.merge_from_file(conf["config_path"])
74
  _config = lower_config(config)
 
98
  "keypoints1": data_["mkpts1_f"],
99
  "mconf": data_["mconf"],
100
  }
101
+ scores = data_["mconf"]
102
+ top_k = self.conf["max_keypoints"]
103
+ if top_k is not None and len(scores) > top_k:
104
+ keep = torch.argsort(scores, descending=True)[:top_k]
105
+ scores = scores[keep]
106
+ pred["keypoints0"], pred["keypoints1"], pred["mconf"] = (
107
+ pred["keypoints0"][keep],
108
+ pred["keypoints1"][keep],
109
+ scores,
110
+ )
111
  return pred
hloc/matchers/dkm.py CHANGED
@@ -12,11 +12,13 @@ from DKM.dkm import DKMv3_outdoor
12
  dkm_path = Path(__file__).parent / "../../third_party/DKM"
13
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
14
 
 
15
  class DKMv3(BaseModel):
16
  default_conf = {
17
  "model_name": "DKMv3_outdoor.pth",
18
  "match_threshold": 0.2,
19
  "checkpoint_dir": dkm_path / "pretrained",
 
20
  }
21
  required_inputs = [
22
  "image0",
@@ -38,8 +40,8 @@ class DKMv3(BaseModel):
38
  cmd = ["wget", link, "-O", str(model_path)]
39
  logger.info(f"Downloading the DKMv3 model with `{cmd}`.")
40
  subprocess.run(cmd, check=True)
41
- logger.info(f"Loading DKMv3 model...")
42
  self.net = DKMv3_outdoor(path_to_weights=str(model_path), device=device)
 
43
 
44
  def _forward(self, data):
45
  img0 = data["image0"].cpu().numpy().squeeze() * 255
@@ -52,10 +54,16 @@ class DKMv3(BaseModel):
52
  W_B, H_B = img1.size
53
 
54
  warp, certainty = self.net.match(img0, img1, device=device)
55
- matches, certainty = self.net.sample(warp, certainty)
 
 
56
  kpts1, kpts2 = self.net.to_pixel_coordinates(
57
  matches, H_A, W_A, H_B, W_B
58
  )
59
- pred = {}
60
- pred["keypoints0"], pred["keypoints1"] = kpts1, kpts2
 
 
 
 
61
  return pred
 
12
  dkm_path = Path(__file__).parent / "../../third_party/DKM"
13
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
14
 
15
+
16
  class DKMv3(BaseModel):
17
  default_conf = {
18
  "model_name": "DKMv3_outdoor.pth",
19
  "match_threshold": 0.2,
20
  "checkpoint_dir": dkm_path / "pretrained",
21
+ "max_keypoints": -1,
22
  }
23
  required_inputs = [
24
  "image0",
 
40
  cmd = ["wget", link, "-O", str(model_path)]
41
  logger.info(f"Downloading the DKMv3 model with `{cmd}`.")
42
  subprocess.run(cmd, check=True)
 
43
  self.net = DKMv3_outdoor(path_to_weights=str(model_path), device=device)
44
+ logger.info(f"Loading DKMv3 model done")
45
 
46
  def _forward(self, data):
47
  img0 = data["image0"].cpu().numpy().squeeze() * 255
 
54
  W_B, H_B = img1.size
55
 
56
  warp, certainty = self.net.match(img0, img1, device=device)
57
+ matches, certainty = self.net.sample(
58
+ warp, certainty, num=self.conf["max_keypoints"]
59
+ )
60
  kpts1, kpts2 = self.net.to_pixel_coordinates(
61
  matches, H_A, W_A, H_B, W_B
62
  )
63
+ pred = {
64
+ "keypoints0": kpts1,
65
+ "keypoints1": kpts2,
66
+ "mconf": certainty,
67
+ }
68
+ breakpoint()
69
  return pred
hloc/matchers/loftr.py CHANGED
@@ -10,15 +10,18 @@ class LoFTR(BaseModel):
10
  default_conf = {
11
  "weights": "outdoor",
12
  "match_threshold": 0.2,
13
- "max_keypoints": None,
 
14
  }
15
  required_inputs = ["image0", "image1"]
16
 
17
  def _init(self, conf):
18
  cfg = default_cfg
19
  cfg["match_coarse"]["thr"] = conf["match_threshold"]
 
20
  self.net = LoFTR_(pretrained=conf["weights"], config=cfg)
21
  logger.info(f"Loaded LoFTR with weights {conf['weights']}")
 
22
  def _forward(self, data):
23
  # For consistency with hloc pairs, we refine kpts in image0!
24
  rename = {
 
10
  default_conf = {
11
  "weights": "outdoor",
12
  "match_threshold": 0.2,
13
+ "sinkhorn_iterations": 20,
14
+ "max_keypoints": -1,
15
  }
16
  required_inputs = ["image0", "image1"]
17
 
18
  def _init(self, conf):
19
  cfg = default_cfg
20
  cfg["match_coarse"]["thr"] = conf["match_threshold"]
21
+ cfg["match_coarse"]["skh_iters"] = conf["sinkhorn_iterations"]
22
  self.net = LoFTR_(pretrained=conf["weights"], config=cfg)
23
  logger.info(f"Loaded LoFTR with weights {conf['weights']}")
24
+
25
  def _forward(self, data):
26
  # For consistency with hloc pairs, we refine kpts in image0!
27
  rename = {
hloc/matchers/topicfm.py CHANGED
@@ -16,6 +16,7 @@ class TopicFM(BaseModel):
16
  "weights": "outdoor",
17
  "match_threshold": 0.2,
18
  "n_sampling_topics": 4,
 
19
  }
20
  required_inputs = ["image0", "image1"]
21
 
@@ -39,4 +40,14 @@ class TopicFM(BaseModel):
39
  "keypoints1": data_["mkpts1_f"],
40
  "mconf": data_["mconf"],
41
  }
 
 
 
 
 
 
 
 
 
 
42
  return pred
 
16
  "weights": "outdoor",
17
  "match_threshold": 0.2,
18
  "n_sampling_topics": 4,
19
+ "max_keypoints": -1,
20
  }
21
  required_inputs = ["image0", "image1"]
22
 
 
40
  "keypoints1": data_["mkpts1_f"],
41
  "mconf": data_["mconf"],
42
  }
43
+ scores = data_["mconf"]
44
+ top_k = self.conf["max_keypoints"]
45
+ if top_k is not None and len(scores) > top_k:
46
+ keep = torch.argsort(scores, descending=True)[:top_k]
47
+ scores = scores[keep]
48
+ pred["keypoints0"], pred["keypoints1"], pred["mconf"] = (
49
+ pred["keypoints0"][keep],
50
+ pred["keypoints1"][keep],
51
+ scores,
52
+ )
53
  return pred