Skip to content

Commit 923159a

Browse files
committed
fix bugs in COLMAP images text loading
Signed-off-by: NotMorven <[email protected]>
1 parent 9c45032 commit 923159a

File tree

1 file changed

+107
-58
lines changed

1 file changed

+107
-58
lines changed

fvdb_reality_capture/sfm_scene/_colmap_utils/scene_manager.py

Lines changed: 107 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ def load_colmap_project_file(self, project_file=None, image_path=None):
7474
pass
7575

7676
if self.image_path is None:
77-
logger = logging.getLogger(f"{self.__class__.__module__}.{self.__class__.__name__}")
77+
logger = logging.getLogger(
78+
f"{self.__class__.__module__}.{self.__class__.__name__}"
79+
)
7880
logger.debug("Warning: image_path not found for reconstruction")
7981
elif not self.image_path.endswith("/"):
8082
self.image_path += "/"
@@ -123,7 +125,9 @@ def _load_cameras_txt(self, input_file):
123125

124126
data = line.split()
125127
camera_id = int(data[0])
126-
self.cameras[camera_id] = Camera(data[1], int(data[2]), int(data[3]), map(float, data[4:]))
128+
self.cameras[camera_id] = Camera(
129+
data[1], int(data[2]), int(data[3]), map(float, data[4:])
130+
)
127131
self.last_camera_id = max(self.last_camera_id, camera_id)
128132

129133
# ---------------------------------------------------------------------------
@@ -169,7 +173,9 @@ def _load_images_bin(self, input_file):
169173

170174
ids_array = array.array("Q")
171175
ids_array.frombytes(points_elements[:, 2].tobytes())
172-
image.point3D_ids = np.array(ids_array, dtype=np.uint64).reshape((num_points2D,))
176+
image.point3D_ids = np.array(ids_array, dtype=np.uint64).reshape(
177+
(num_points2D,)
178+
)
173179

174180
# automatically remove points without an associated 3D point
175181
# mask = (image.point3D_ids != SceneManager.INVALID_POINT3D)
@@ -184,42 +190,29 @@ def _load_images_bin(self, input_file):
184190
def _load_images_txt(self, input_file):
185191
self.images = OrderedDict()
186192

187-
with open(input_file, "r") as f:
188-
is_camera_description_line = False
189-
190-
for line in iter(lambda: f.readline().strip(), ""):
191-
if not line or line.startswith("#"):
192-
continue
193-
194-
is_camera_description_line = not is_camera_description_line
195-
196-
data = line.split()
197-
198-
if is_camera_description_line:
199-
read_quat = np.array(list(map(float, data[1:5])))
200-
read_pos = np.array(list(map(float, data[5:8])))
201-
image_id = int(data[0])
202-
image = Image(
203-
data[-1],
204-
int(data[-2]),
205-
Quaternion(read_quat),
206-
read_pos,
193+
with open(input_file, "r") as fid:
194+
while True:
195+
line = fid.readline()
196+
if not line:
197+
break
198+
line = line.strip()
199+
if len(line) > 0 and line[0] != "#":
200+
elems = line.split()
201+
image_id = int(elems[0])
202+
qvec = np.array(list(map(float, elems[1:5])))
203+
tvec = np.array(list(map(float, elems[5:8])))
204+
camera_id = int(elems[8])
205+
image_name = elems[9]
206+
elems = fid.readline().split()
207+
xys = np.column_stack(
208+
[tuple(map(float, elems[0::3])), tuple(map(float, elems[1::3]))]
207209
)
208-
else:
209-
points_2d_x = [float(x) for x in data[::3]]
210-
points_2d_y = [float(y) for y in data[1::3]]
211-
point3d_ids = [np.uint64(pid) for pid in data[2::3]]
212-
image.points2D = np.array([points_2d_x, points_2d_y]).T
213-
image.point3D_ids = np.array(point3d_ids, dtype=np.uint64)
214-
215-
# automatically remove points without an associated 3D point
216-
# mask = (image.point3D_ids != SceneManager.INVALID_POINT3D)
217-
# image.points2D = image.points2D[mask]
218-
# image.point3D_ids = image.point3D_ids[mask]
219-
220-
self.images[image_id] = image
221-
self.name_to_image_id[image.name] = image_id
222-
210+
point3D_ids = np.array(tuple(map(int, elems[2::3])))
211+
self.images[image_id] = Image(
212+
image_name, camera_id, Quaternion(qvec), tvec
213+
)
214+
self.images[image_id].points2D = xys
215+
self.images[image_id].point3D_ids = point3D_ids
223216
self.last_image_id = max(self.last_image_id, image_id)
224217

225218
# ---------------------------------------------------------------------------
@@ -261,7 +254,9 @@ def _load_points3D_bin(self, input_file):
261254

262255
data = struct.unpack(f"{2*track_len}I", f.read(2 * track_len * 4))
263256

264-
self.point3D_id_to_images[self.point3D_ids[i]] = np.array(data, dtype=np.uint32).reshape(track_len, 2)
257+
self.point3D_id_to_images[self.point3D_ids[i]] = np.array(
258+
data, dtype=np.uint32
259+
).reshape(track_len, 2)
265260

266261
def _load_points3D_txt(self, input_file):
267262
self.points3D = []
@@ -328,7 +323,11 @@ def _save_cameras_bin(self, output_file):
328323
camera_struct = struct.Struct("IiLL")
329324

330325
for camera_id, camera in sorted(self.cameras.items()):
331-
fid.write(camera_struct.pack(camera_id, camera.camera_type, camera.width, camera.height))
326+
fid.write(
327+
camera_struct.pack(
328+
camera_id, camera.camera_type, camera.width, camera.height
329+
)
330+
)
332331
# TODO (True): should move this into the Camera class
333332
fid.write(camera.get_params().tobytes())
334333

@@ -368,7 +367,9 @@ def _save_images_bin(self, output_file):
368367
fid.write(struct.pack("I", image.camera_id))
369368
fid.write((image.name + "\0").encode("utf-8"))
370369
fid.write(struct.pack("L", len(image.points2D)))
371-
data = np.rec.fromarrays((image.points2D[:, 0], image.points2D[:, 1], image.point3D_ids))
370+
data = np.rec.fromarrays(
371+
(image.points2D[:, 0], image.points2D[:, 1], image.point3D_ids)
372+
)
372373
fid.write(data.tobytes())
373374

374375
def _save_images_txt(self, output_file):
@@ -386,7 +387,11 @@ def _save_images_txt(self, output_file):
386387
print >> fid, image.camera_id, image.name
387388

388389
data = np.rec.fromarrays(
389-
(image.points2D[:, 0], image.points2D[:, 1], image.point3D_ids.astype(np.int64))
390+
(
391+
image.points2D[:, 0],
392+
image.points2D[:, 1],
393+
image.point3D_ids.astype(np.int64),
394+
)
390395
)
391396
if len(data) > 0:
392397
np.savetxt(fid, data, "%.2f %.2f %d", newline=" ")
@@ -411,7 +416,9 @@ def save_points3D(self, output_folder, output_file=None, binary=True):
411416

412417
def _save_points3D_bin(self, output_file):
413418
num_valid_points3D = sum(
414-
1 for point3D_idx in self.point3D_id_to_point3D_idx.values() if point3D_idx != SceneManager.INVALID_POINT3D
419+
1
420+
for point3D_idx in self.point3D_id_to_point3D_idx.values()
421+
if point3D_idx != SceneManager.INVALID_POINT3D
415422
)
416423

417424
iter_point3D_id_to_point3D_idx = self.point3D_id_to_point3D_idx.items()
@@ -432,7 +439,9 @@ def _save_points3D_bin(self, output_file):
432439

433440
def _save_points3D_txt(self, output_file):
434441
num_valid_points3D = sum(
435-
1 for point3D_idx in self.point3D_id_to_point3D_idx.values() if point3D_idx != SceneManager.INVALID_POINT3D
442+
1
443+
for point3D_idx in self.point3D_id_to_point3D_idx.values()
444+
if point3D_idx != SceneManager.INVALID_POINT3D
436445
)
437446

438447
array_to_string = lambda arr: " ".join(str(x) for x in arr)
@@ -454,7 +463,9 @@ def _save_points3D_txt(self, output_file):
454463
print >> fid, array_to_string(self.points3D[point3D_idx]),
455464
print >> fid, array_to_string(self.point3D_colors[point3D_idx]),
456465
print >> fid, self.point3D_errors[point3D_idx],
457-
print >> fid, array_to_string(self.point3D_id_to_images[point3D_id].flat)
466+
print >> fid, array_to_string(
467+
self.point3D_id_to_images[point3D_id].flat
468+
)
458469

459470
# ---------------------------------------------------------------------------
460471

@@ -475,7 +486,12 @@ def get_points3D(self, image_id, return_points2D=True, return_colors=False):
475486

476487
mask = image.point3D_ids != SceneManager.INVALID_POINT3D
477488

478-
point3D_idxs = np.array([self.point3D_id_to_point3D_idx[point3D_id] for point3D_id in image.point3D_ids[mask]])
489+
point3D_idxs = np.array(
490+
[
491+
self.point3D_id_to_point3D_idx[point3D_id]
492+
for point3D_id in image.point3D_ids[mask]
493+
]
494+
)
479495
# detect filtered points
480496
filter_mask = point3D_idxs != SceneManager.INVALID_POINT3D
481497
point3D_idxs = point3D_idxs[filter_mask]
@@ -492,12 +508,18 @@ def get_points3D(self, image_id, return_points2D=True, return_colors=False):
492508
# ---------------------------------------------------------------------------
493509

494510
def point3D_valid(self, point3D_id):
495-
return self.point3D_id_to_point3D_idx[point3D_id] != SceneManager.INVALID_POINT3D
511+
return (
512+
self.point3D_id_to_point3D_idx[point3D_id] != SceneManager.INVALID_POINT3D
513+
)
496514

497515
# ---------------------------------------------------------------------------
498516

499517
def get_filtered_points3D(self, return_colors=False):
500-
point3D_idxs = [idx for idx in self.point3D_id_to_point3D_idx.values() if idx != SceneManager.INVALID_POINT3D]
518+
point3D_idxs = [
519+
idx
520+
for idx in self.point3D_id_to_point3D_idx.values()
521+
if idx != SceneManager.INVALID_POINT3D
522+
]
501523
result = [self.points3D[point3D_idxs, :]]
502524

503525
if return_colors:
@@ -509,10 +531,14 @@ def get_filtered_points3D(self, return_colors=False):
509531

510532
# return 3D points shared by two images
511533
def get_shared_points3D(self, image_id1, image_id2):
512-
point3D_ids = set(self.images[image_id1].point3D_ids) & set(self.images[image_id2].point3D_ids)
534+
point3D_ids = set(self.images[image_id1].point3D_ids) & set(
535+
self.images[image_id2].point3D_ids
536+
)
513537
point3D_ids.discard(SceneManager.INVALID_POINT3D)
514538

515-
point3D_idxs = np.array([self.point3D_id_to_point3D_idx[point3D_id] for point3D_id in point3D_ids])
539+
point3D_idxs = np.array(
540+
[self.point3D_id_to_point3D_idx[point3D_id] for point3D_id in point3D_ids]
541+
)
516542

517543
return self.points3D[point3D_idxs, :]
518544

@@ -581,17 +607,33 @@ def delete_images(self, image_list):
581607
if point3D_idx == SceneManager.INVALID_POINT3D:
582608
continue
583609

584-
mask = np.array([image_id in keep_set for image_id in self.point3D_id_to_images[point3D_id][:, 0]])
610+
mask = np.array(
611+
[
612+
image_id in keep_set
613+
for image_id in self.point3D_id_to_images[point3D_id][:, 0]
614+
]
615+
)
585616
if np.any(mask):
586-
self.point3D_id_to_images[point3D_id] = self.point3D_id_to_images[point3D_id][mask]
617+
self.point3D_id_to_images[point3D_id] = self.point3D_id_to_images[
618+
point3D_id
619+
][mask]
587620
else:
588-
self.point3D_id_to_point3D_idx[point3D_id] = SceneManager.INVALID_POINT3D
621+
self.point3D_id_to_point3D_idx[point3D_id] = (
622+
SceneManager.INVALID_POINT3D
623+
)
589624

590625
# ---------------------------------------------------------------------------
591626

592627
# camera_list: set of cameras whose points we'd like to keep
593628
# min/max triangulation angle: in degrees
594-
def filter_points3D(self, min_track_len=0, max_error=np.inf, min_tri_angle=0, max_tri_angle=180, image_set=set()):
629+
def filter_points3D(
630+
self,
631+
min_track_len=0,
632+
max_error=np.inf,
633+
min_tri_angle=0,
634+
max_tri_angle=180,
635+
image_set=set(),
636+
):
595637

596638
image_set = set(image_set)
597639

@@ -619,12 +661,16 @@ def filter_points3D(self, min_track_len=0, max_error=np.inf, min_tri_angle=0, ma
619661
or image_set
620662
and image_set.isdisjoint(image_ids)
621663
):
622-
self.point3D_id_to_point3D_idx[point3D_id] = SceneManager.INVALID_POINT3D
664+
self.point3D_id_to_point3D_idx[point3D_id] = (
665+
SceneManager.INVALID_POINT3D
666+
)
623667

624668
# find dot product between all camera viewing rays
625669
elif check_triangulation_angles:
626670
xyz = self.points3D[point3D_idx, :]
627-
tvecs = np.array([(self.images[image_id].tvec - xyz) for image_id in image_ids])
671+
tvecs = np.array(
672+
[(self.images[image_id].tvec - xyz) for image_id in image_ids]
673+
)
628674
tvecs /= np.linalg.norm(tvecs, axis=-1)[:, np.newaxis]
629675

630676
cos_theta = np.array([u.dot(v) for u, v in combinations(tvecs, 2)])
@@ -633,13 +679,16 @@ def filter_points3D(self, min_track_len=0, max_error=np.inf, min_tri_angle=0, ma
633679
# if maximum viewing angle is too small or too large,
634680
# don't add this point
635681
if np.min(cos_theta) > max_tri_prod or np.max(cos_theta) < min_tri_prod:
636-
self.point3D_id_to_point3D_idx[point3D_id] = SceneManager.INVALID_POINT3D
682+
self.point3D_id_to_point3D_idx[point3D_id] = (
683+
SceneManager.INVALID_POINT3D
684+
)
637685

638686
# apply the filters to the image point3D_ids
639687
for image in self.images.itervalues():
640688
mask = np.array(
641689
[
642-
self.point3D_id_to_point3D_idx.get(point3D_id, 0) == SceneManager.INVALID_POINT3D
690+
self.point3D_id_to_point3D_idx.get(point3D_id, 0)
691+
== SceneManager.INVALID_POINT3D
643692
for point3D_id in image.point3D_ids
644693
]
645694
)

0 commit comments

Comments
 (0)