Skip to content

Commit ffaa58b

Browse files
committed
Merge remote-tracking branch 'refs/remotes/upstream/SRIF' into SRIF
2 parents 5ea270e + f7dc811 commit ffaa58b

6 files changed

Lines changed: 72 additions & 33 deletions

File tree

scripts/keypoint_density_evaluation.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ def ProjectionsOfTriangulatedTiePoints(
1818

1919
image_projections = {}
2020
reconstruction = pycolmap.Reconstruction(model_path)
21+
average_tie_points = []
22+
average_tie_points_also_not_triangulated = []
2123

2224
for image in tqdm(reconstruction.images.values(), desc="Find projections that have a corresponding 3D tie points"):
2325
camera_id = image.camera_id
@@ -26,19 +28,27 @@ def ProjectionsOfTriangulatedTiePoints(
2628
height = camera.height
2729

2830
projections = []
31+
tot_tie_points = 0
32+
tot_tie_points_also_not_triangulated = 0
2933
for feature_idx, point2D in enumerate(image.points2D):
3034
#if point2D.has_point3D:
3135
if point2D.point3D_id < 1000000:
3236
#print(point2D.point3D_id);quit()
3337
#print(point2D.has_point3D.__getattribute__)
3438
#print(type(point2D.has_point3D))
3539
#quit()
40+
tot_tie_points += 1
41+
tot_tie_points_also_not_triangulated += 1
3642
x, y = point2D.xy
3743
x, y = x, (height-y)
3844
projections.append((image.image_id, (x,y), width, height))
45+
else:
46+
tot_tie_points_also_not_triangulated += 1
3947
image_projections[image] = projections
48+
average_tie_points.append(tot_tie_points)
49+
average_tie_points_also_not_triangulated.append(tot_tie_points_also_not_triangulated)
4050

41-
return image_projections
51+
return image_projections, np.mean(average_tie_points), np.mean(average_tie_points_also_not_triangulated)
4252

4353
def ComupteMetrics(
4454
output_dir: Path,
@@ -75,11 +85,11 @@ def ComupteMetrics(
7585
transform = from_origin(xmin, ymax, resolution, resolution)
7686

7787
zero_cells = np.count_nonzero(density_grid == 0)
78-
print(f"Image: {image_name}")
79-
print(f"Number of cells with zero density: {zero_cells}")
80-
print(f"Total number of cells: {rows * cols}")
88+
#print(f"Image: {image_name}")
89+
#print(f"Number of cells with zero density: {zero_cells}")
90+
#print(f"Total number of cells: {rows * cols}")
8191
covered_area = ((rows * cols)-zero_cells) / (rows * cols) * 100
82-
print(f"Percentage of cells with non zero density: {covered_area}%")
92+
#print(f"Percentage of cells with non zero density: {covered_area}%")
8393

8494
density_grid_normalized = zoom(
8595
density_grid_normalized,
@@ -107,20 +117,25 @@ def ComupteMetrics(
107117
parser = argparse.ArgumentParser(
108118
description="Export pairs in a txt file from a COLMAP database."
109119
)
110-
parser.add_argument("-d", "--database", type=Path, required=True, help="Path to COLMAP database.")
111-
parser.add_argument("-m", "--min_n_matches", type=int, required=True, help="Min number of matches that a pair should have after geometric verification.")
112-
parser.add_argument("-o", "--output", type=Path, required=True, help="Path to output folder.")
120+
#parser.add_argument("-d", "--database", type=Path, required=True, help="Path to COLMAP database.")
121+
#parser.add_argument("-m", "--min_n_matches", type=int, required=True, help="Min number of matches that a pair should have after geometric verification.")
122+
#parser.add_argument("-o", "--output", type=Path, required=True, help="Path to output folder.")
113123
parser.add_argument("-l", "--model", type=Path, required=True, help="Path to model folder.")
114124
args = parser.parse_args()
115125

116-
image_projections = ProjectionsOfTriangulatedTiePoints(model_path=args.model)
126+
image_projections, mean_tie_points, mean_tie_points_also_not_triangulated = ProjectionsOfTriangulatedTiePoints(model_path=args.model)
127+
print('Mean number of tie points per image:', mean_tie_points)
128+
print('Mean number of tie points per image also not triangulated:', mean_tie_points_also_not_triangulated)
129+
print(f"Inlier ratio: {mean_tie_points / mean_tie_points_also_not_triangulated}")
117130

118131
results = {}
132+
output_dir = Path(args.model) / "tie_points_density"
133+
output_dir.mkdir(parents=True, exist_ok=True)
119134

120135
for image in tqdm(image_projections, desc="Compute metrics"):
121136
#print(f"Image: {image.name} - Number of projections: {len(image_projections[image])}")
122137
image_name, covered_area = ComupteMetrics(
123-
output_dir=Path(args.output),
138+
output_dir=output_dir,
124139
image_name=image.name,
125140
projections=image_projections[image],
126141
scale_max_value=1,

scripts/mount_video.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# Function to create video from images in a folder
55
def images_to_video(image_folder, output_video_path, frame_duration=0.5):
66
images = [img for img in os.listdir(image_folder) if img.endswith(".png")]
7+
#images.sort(key=lambda x: int(x.split('_')[0]))
78
frame = cv2.imread(os.path.join(image_folder, images[0]))
89

910
# Get image dimensions

scripts/resize_images.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def resize_images(input_folder, output_folder, new_resolution):
1010

1111
# Loop through all files in the input folder
1212
for filename in os.listdir(input_folder):
13-
if filename.endswith((".jpg", ".jpeg", ".png", ".gif")):
13+
if filename.endswith((".jpg", ".jpeg", ".png", ".gif", ".tif")):
1414
input_path = os.path.join(input_folder, filename)
1515
output_path = os.path.join(output_folder, filename)
1616

scripts/show_matches.py

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,11 @@ def LoadDatabase(self):
105105
(int(pair_id[0]), int(pair_id[1]))
106106
] = blob_to_array(data, np.uint32, (-1, 2))
107107

108-
def ShowMatches(self):
108+
def ShowMatches(self, plot_config: dict):
109109
if self.db_type == "colmap":
110-
self.ShowColmapMatches()
110+
self.ShowColmapMatches(plot_config)
111111

112-
def ShowColmapMatches(self):
112+
def ShowColmapMatches(self, plot_config: dict):
113113
print("Showing matches..")
114114
if self.imgs_dict["type"] == "ids":
115115
id0 = int(self.imgs_dict["data"][0])
@@ -133,7 +133,7 @@ def ShowColmapMatches(self):
133133
try:
134134
print("verified matches shape", np.shape(self.two_views_matches[(id0, id1)]))
135135
except:
136-
pass
136+
self.two_views_matches[(id0, id1)] = []
137137

138138
img0_path = self.imgs_dir / self.imgs[id0]
139139
img1_path = self.imgs_dir / self.imgs[id1]
@@ -146,6 +146,7 @@ def ShowColmapMatches(self):
146146
keypoints0,
147147
keypoints1,
148148
self.two_views_matches[(id0, id1)],
149+
plot_config,
149150
)
150151

151152
def GeneratePlot(
@@ -155,7 +156,14 @@ def GeneratePlot(
155156
kpts0: np.ndarray,
156157
kpts1: np.ndarray,
157158
matches: np.ndarray,
159+
plot_config: dict,
158160
):
161+
162+
show_keypoints = plot_config["show_keypoints"]
163+
radius = plot_config["radius"]
164+
thickness = plot_config["thickness"]
165+
space_between_images = plot_config["space_between_images"]
166+
159167
# Load images
160168
img0 = cv2.imread(str(img0_path))
161169
img1 = cv2.imread(str(img1_path))
@@ -166,32 +174,34 @@ def GeneratePlot(
166174

167175
# Create a new image to draw matches
168176
img_matches = np.zeros(
169-
(max(img0.shape[0], img1.shape[0]), img0.shape[1] + img1.shape[1], 3),
177+
(max(img0.shape[0], img1.shape[0]), img0.shape[1] + img1.shape[1] + space_between_images, 3),
170178
dtype=np.uint8,
171179
)
172180
img_matches[: img0.shape[0], : img0.shape[1]] = img0
173-
img_matches[: img1.shape[0], img0.shape[1] :] = img1
181+
img_matches[: img1.shape[0], img0.shape[1]+space_between_images :] = img1
182+
img_matches[: img1.shape[0], img0.shape[1] : img0.shape[1]+space_between_images] = (255,255,255)
174183

175-
# Show keypoints
176-
for kpt in kpts0_int:
177-
kpt = tuple(kpt)
178-
cv2.circle(img_matches, kpt, 3, (0, 0, 255), -1)
184+
if show_keypoints:
185+
# Show keypoints
186+
for kpt in kpts0_int:
187+
kpt = tuple(kpt)
188+
cv2.circle(img_matches, kpt, radius, (0, 0, 255), thickness)
179189

180-
for kpt in kpts1_int:
181-
kpt = tuple(kpt + np.array([img0.shape[1], 0]))
182-
cv2.circle(img_matches, kpt, 3, (0, 0, 255), -1)
190+
for kpt in kpts1_int:
191+
kpt = tuple(kpt + np.array([img0.shape[1], 0]))
192+
cv2.circle(img_matches, kpt, radius, (0, 0, 255), thickness)
183193

184194
# Draw lines and circles for matches
185195
for match in matches:
186196
pt1 = tuple(kpts0_int[match[0]])
187197
pt2 = tuple(np.array(kpts1_int[match[1]]) + np.array([img0.shape[1], 0]))
188198

189199
# Draw a line connecting the keypoints
190-
cv2.line(img_matches, pt1, pt2, (0, 255, 0), 1)
200+
cv2.line(img_matches, pt1, pt2, (0, 255, 0), thickness)
191201

192202
# Draw circles around keypoints
193-
cv2.circle(img_matches, pt1, 3, (255, 0, 0), -1)
194-
cv2.circle(img_matches, pt2, 3, (255, 0, 0), -1)
203+
cv2.circle(img_matches, pt1, radius, (255, 0, 0), thickness)
204+
cv2.circle(img_matches, pt2, radius, (255, 0, 0), thickness)
195205

196206
img_matches_resized = self.resize_image(img_matches, self.max_out_img_size)
197207

@@ -256,6 +266,13 @@ def parse_args():
256266

257267

258268
def main():
269+
plot_config = {
270+
"show_keypoints": True,
271+
"radius": 5,
272+
"thickness": 2,
273+
"space_between_images": 0,
274+
}
275+
259276
args = parse_args()
260277
database_path = Path(args.database)
261278
out_dir = Path(args.output)
@@ -283,7 +300,7 @@ def main():
283300
)
284301

285302
show_pair_matches.LoadDatabase()
286-
show_pair_matches.ShowMatches()
303+
show_pair_matches.ShowMatches(plot_config)
287304

288305
else:
289306
pairs = generate_pairs(imgs_dir, method="bruteforce", database_path=database_path)
@@ -306,9 +323,10 @@ def main():
306323
)
307324

308325
show_pair_matches.LoadDatabase()
326+
#show_pair_matches.ShowMatches(plot_config);quit()
309327

310328
try:
311-
show_pair_matches.ShowMatches()
329+
show_pair_matches.ShowMatches(plot_config)
312330
except:
313331
print("No verified matches found")
314332

src/deep_image_matching/config.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@
4545
# GeometricVerification.MAGSAC (use opencv MAGSAC),
4646
# Other methods: RANSAC, LMEDS, RHO, USAC_DEFAULT, USAC_PARALLEL, USAC_FM_8PTS, USAC_FAST, USAC_ACCURATE, USAC_PROSAC, USAC_MAGSAC
4747
"geom_verification": GeometricVerification.MAGSAC,
48-
"gv_threshold": 1,
48+
"gv_threshold": 4,
4949
"gv_confidence": 0.99999,
5050
# Minimum number of inliers matches and minumum inlier ratio per pair
5151
"min_inliers_per_pair": 15,
52-
"min_inlier_ratio_per_pair": 0.25,
52+
"min_inlier_ratio_per_pair": 0.15,
5353
# Even if the features are extracted by tiles, you can try to match the features of the entire image first (if the number of features is not too high and they can fit into memory). Default is False.
5454
"try_match_full_images": False,
5555
"preselection_pipeline": "superpoint+lightglue",
@@ -66,7 +66,7 @@
6666
"name": "superpoint",
6767
"nms_radius": 3,
6868
"keypoint_threshold": 0.0005,
69-
"max_keypoints": 4096,
69+
"max_keypoints": 2048,
7070
},
7171
"matcher": {
7272
# Refer to https://github.com/cvg/LightGlue/tree/main for the meaning of the parameters
@@ -156,6 +156,11 @@
156156
"sift+kornia_matcher": {
157157
"extractor": {
158158
"name": "sift",
159+
"n_features": 2048,
160+
"nOctaveLayers": 3,
161+
"contrastThreshold": 0.0004,
162+
"edgeThreshold": 10,
163+
"sigma": 1.6,
159164
},
160165
"matcher": {"name": "kornia_matcher", "match_mode": "smnn", "th": 0.85},
161166
},

src/deep_image_matching/matchers/srif.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def _match_pairs(
124124
F, inlMask = pydegensac.findFundamentalMatrix(
125125
mkpts0,
126126
mkpts1,
127-
px_th=1.0,
127+
px_th=4.0,
128128
conf=0.99,
129129
max_iters=10000,
130130
)

0 commit comments

Comments
 (0)