Skip to content

Commit 1a7e57f

Browse files
authored
Merge pull request #3 from meyerls/dev
Dev
2 parents f850eb1 + 7d05a6b commit 1a7e57f

File tree

5 files changed

+120
-64
lines changed

5 files changed

+120
-64
lines changed

colmap_wrapper/colmap/bin.py

Lines changed: 90 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111

1212
# Built-in/Generic Imports
1313
import struct
14+
import pathlib as path
1415

1516
# Libs
1617
import numpy as np
17-
import pathlib as path
1818

1919
# Own
2020
from colmap_wrapper.colmap.camera import (CAMERA_MODEL_IDS, Camera, ImageInformation, Point3D)
@@ -33,32 +33,6 @@ def read_next_bytes(fid, num_bytes, format_char_sequence, endian_character="<"):
3333
return struct.unpack(endian_character + format_char_sequence, data)
3434

3535

36-
def write_cameras_binary(a):
37-
"""
38-
void Reconstruction::WriteCamerasBinary(const std::string& path) const {
39-
std::ofstream file(path, std::ios::trunc | std::ios::binary);
40-
CHECK(file.is_open()) << path;
41-
42-
WriteBinaryLittleEndian<uint64_t>(&file, cameras_.size());
43-
44-
for (const auto& camera : cameras_) {
45-
WriteBinaryLittleEndian<camera_t>(&file, camera.first);
46-
WriteBinaryLittleEndian<int>(&file, camera.second.ModelId());
47-
WriteBinaryLittleEndian<uint64_t>(&file, camera.second.Width());
48-
WriteBinaryLittleEndian<uint64_t>(&file, camera.second.Height());
49-
for (const double param : camera.second.Params()) {
50-
WriteBinaryLittleEndian<double>(&file, param);
51-
}
52-
}
53-
}
54-
55-
@param a:
56-
@return:
57-
"""
58-
pass
59-
return
60-
61-
6236
def read_cameras_binary(path_to_model_file):
6337
"""
6438
Original C++ Code can be found here: https://github.com/colmap/colmap/blob/dev/src/base/reconstruction.cc
@@ -69,7 +43,7 @@ def read_cameras_binary(path_to_model_file):
6943
with open(path_to_model_file, "rb") as fid:
7044
# First 8 bits contain information about the quantity of different camera models
7145
num_cameras = read_next_bytes(fid, 8, "Q")[0]
72-
for _ in range(num_cameras): # camera_line_index
46+
for _ in range(num_cameras): # camera_line_index
7347
# Afterwards the 64 bits contain information about a specific camera
7448
camera_properties = read_next_bytes(fid, num_bytes=24, format_char_sequence="iiQQ")
7549
camera_id = camera_properties[0]
@@ -100,7 +74,7 @@ def read_images_binary(path_to_model_file):
10074
with open(path_to_model_file, "rb") as fid:
10175
# First 8 bits contain information about the quantity of different registrated camera models
10276
num_reg_images = read_next_bytes(fid, 8, "Q")[0]
103-
for _ in range(num_reg_images): # image_index
77+
for _ in range(num_reg_images): # image_index
10478
# Image properties: (image_id, qvec, tvec, camera_id)
10579
binary_image_properties = read_next_bytes(
10680
fid, num_bytes=64, format_char_sequence="idddddddi")
@@ -138,13 +112,13 @@ def read_images_binary(path_to_model_file):
138112
pt3did_to_kpidx[ptid] = kpidx
139113

140114
images[image_id] = ImageInformation(image_id=image_id,
141-
qvec=qvec,
142-
tvec=tvec,
143-
camera_id=camera_id,
144-
image_name=image_name,
145-
xys=xys,
146-
point3D_ids=point3D_ids,
147-
point3DiD_to_kpidx=pt3did_to_kpidx)
115+
qvec=qvec,
116+
tvec=tvec,
117+
camera_id=camera_id,
118+
image_name=image_name,
119+
xys=xys,
120+
point3D_ids=point3D_ids,
121+
point3DiD_to_kpidx=pt3did_to_kpidx)
148122
return images
149123

150124

@@ -183,13 +157,13 @@ def read_images_text(path):
183157
pt3did_to_kpidx[ptid] = kpidx
184158

185159
images[image_id] = ImageInformation(image_id=image_id,
186-
qvec=qvec,
187-
tvec=tvec,
188-
camera_id=camera_id,
189-
image_name=image_name,
190-
xys=xys,
191-
point3D_ids=point3D_ids,
192-
point3DiD_to_kpidx=pt3did_to_kpidx)
160+
qvec=qvec,
161+
tvec=tvec,
162+
camera_id=camera_id,
163+
image_name=image_name,
164+
xys=xys,
165+
point3D_ids=point3D_ids,
166+
point3DiD_to_kpidx=pt3did_to_kpidx)
193167
return images
194168

195169

@@ -203,7 +177,7 @@ def read_points3d_binary(path_to_model_file):
203177
with open(path_to_model_file, "rb") as fid:
204178
# Number of points in sparse model
205179
num_points = read_next_bytes(fid, 8, "Q")[0]
206-
for _ in range(num_points): # point_line_index
180+
for _ in range(num_points): # point_line_index
207181
binary_point_line_properties = read_next_bytes(
208182
fid, num_bytes=43, format_char_sequence="QdddBBBd")
209183
# Point ID
@@ -311,6 +285,7 @@ def read_cameras_text(path, int_id=True):
311285
params=params)
312286
return cameras
313287

288+
314289
def read_array(path):
315290
with open(path, "rb") as fid:
316291
width, height, channels = np.genfromtxt(fid, delimiter="&", max_rows=1,
@@ -326,4 +301,74 @@ def read_array(path):
326301
byte = fid.read(1)
327302
array = np.fromfile(fid, np.float32)
328303
array = array.reshape((width, height, channels), order="F")
329-
return np.transpose(array, (1, 0, 2)).squeeze()
304+
return np.transpose(array, (1, 0, 2)).squeeze()
305+
306+
307+
def write_cameras_text(cameras, path):
308+
"""
309+
see: src/base/reconstruction.cc
310+
void Reconstruction::WriteCamerasText(const std::string& path)
311+
void Reconstruction::ReadCamerasText(const std::string& path)
312+
"""
313+
HEADER = "# Camera list with one line of data per camera:\n" + \
314+
"# CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[]\n" + \
315+
"# Number of cameras: {}\n".format(len(cameras))
316+
with open(path, "w") as fid:
317+
fid.write(HEADER)
318+
for _, cam in cameras.items():
319+
to_write = [cam.id, cam.model, cam.width, cam.height, *cam.params]
320+
line = " ".join([str(elem) for elem in to_write])
321+
fid.write(line + "\n")
322+
323+
324+
def write_images_text(images, path):
325+
"""
326+
see: src/base/reconstruction.cc
327+
void Reconstruction::ReadImagesText(const std::string& path)
328+
void Reconstruction::WriteImagesText(const std::string& path)
329+
"""
330+
if len(images) == 0:
331+
mean_observations = 0
332+
else:
333+
mean_observations = sum((len(img.point3D_ids) for _, img in images.items())) / len(images)
334+
HEADER = "# Image list with two lines of data per image:\n" + \
335+
"# IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME\n" + \
336+
"# POINTS2D[] as (X, Y, POINT3D_ID)\n" + \
337+
"# Number of images: {}, mean observations per image: {}\n".format(len(images), mean_observations)
338+
339+
with open(path, "w") as fid:
340+
fid.write(HEADER)
341+
for _, img in images.items():
342+
image_header = [img.id, *img.qvec, *img.tvec, img.camera_id, img.name]
343+
first_line = " ".join(map(str, image_header))
344+
fid.write(first_line + "\n")
345+
346+
points_strings = []
347+
for xy, point3D_id in zip(img.xys, img.point3D_ids):
348+
points_strings.append(" ".join(map(str, [*xy, point3D_id])))
349+
fid.write(" ".join(points_strings) + "\n")
350+
351+
352+
def write_points3D_text(points3D, path):
353+
"""
354+
see: src/base/reconstruction.cc
355+
void Reconstruction::ReadPoints3DText(const std::string& path)
356+
void Reconstruction::WritePoints3DText(const std::string& path)
357+
"""
358+
if len(points3D) == 0:
359+
mean_track_length = 0
360+
else:
361+
mean_track_length = sum((len(pt.image_ids) for _, pt in points3D.items())) / len(points3D)
362+
HEADER = "# 3D point list with one line of data per point:\n" + \
363+
"# POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX)\n" + \
364+
"# Number of points: {}, mean track length: {}\n".format(len(points3D), mean_track_length)
365+
366+
with open(path, "w") as fid:
367+
fid.write(HEADER)
368+
for _, pt in points3D.items():
369+
point_header = [pt.id, *pt.xyz, *pt.rgb, pt.error]
370+
fid.write(" ".join(map(str, point_header)) + " ")
371+
track_strings = []
372+
for image_id, point2D in zip(pt.image_ids, pt.point2D_idxs):
373+
track_strings.append(" ".join(map(str, [image_id, point2D])))
374+
fid.write(" ".join(track_strings) + "\n")

colmap_wrapper/colmap/camera.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def getData(self, downsample: float = 1.0) -> np.ndarray:
116116
# M[:3, :3] = Rwc
117117
# M[:3, 3] = twc
118118

119-
#return M
119+
# return M
120120

121121
def set_extrinsics(self, T: [None, np.ndarray] = None):
122122
if isinstance(T, type(None)):

colmap_wrapper/colmap/colmap.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ def __init__(self, project_path: str,
2424
load_images: bool = True,
2525
load_depth: bool = False,
2626
image_resize: float = 1.,
27-
bg_color: np.ndarray = np.asarray([1, 1, 1])):
27+
bg_color: np.ndarray = np.asarray([1, 1, 1]),
28+
exif_read=False):
2829

30+
self.exif_read = exif_read
2931
self.vis_bg_color = bg_color
3032
self._project_path: Path = Path(project_path)
3133

@@ -63,11 +65,12 @@ def __init__(self, project_path: str,
6365
self.model_ids.append(project_index)
6466

6567
project = COLMAPProject(project_path=project_structure[project_index],
66-
dense_pc='fused.ply',
68+
dense_pc=dense_pc,
6769
load_images=load_images,
6870
load_depth=load_depth,
6971
image_resize=0.4,
70-
bg_color=bg_color)
72+
bg_color=bg_color,
73+
exif_read=self.exif_read)
7174

7275
self.project_list.append(project)
7376

colmap_wrapper/colmap/colmap_project.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ def __init__(self, project_path: [dict, str],
4343
load_images: bool = True,
4444
load_depth: bool = False,
4545
image_resize: float = 1.,
46-
bg_color: np.ndarray = np.asarray([1, 1, 1])):
46+
bg_color: np.ndarray = np.asarray([1, 1, 1]),
47+
exif_read=False):
4748
"""
4849
This is a simple COLMAP project wrapper to simplify the readout of a COLMAP project.
4950
THE COLMAP project is assumed to be in the following workspace folder structure as suggested in the COLMAP
@@ -88,6 +89,9 @@ def __init__(self, project_path: [dict, str],
8889

8990
PhotogrammetrySoftware.__init__(self, project_path=project_path)
9091

92+
# Flag to read exif data (takes long for large image sets)
93+
self.exif_read = exif_read
94+
9195
# Search and Set Paths
9296
if isinstance(project_path, str):
9397
self._project_path: Path = Path(project_path)
@@ -109,7 +113,10 @@ def __init__(self, project_path: [dict, str],
109113
self._dense_base_path: Path = self._project_path
110114
elif isinstance(project_path, dict):
111115
self._project_path: Path = project_path['project_path']
112-
self._dense_base_path: Path = project_path['dense']
116+
if not project_path['dense'].exists():
117+
self._dense_base_path: Path = self._project_path
118+
else:
119+
self._dense_base_path: Path = project_path['dense']
113120
self._sparse_base_path: Path = project_path['sparse']
114121
else:
115122
raise ValueError("{}".format(self._project_path))
@@ -177,18 +184,19 @@ def __read_project_init_file(self):
177184
return {}
178185

179186
def __read_exif_data(self):
180-
if self.__project_ini_path.exists():
181-
try:
182-
for image_idx in self.images.keys():
183-
self.images[image_idx].original_filename: Path = Path(self.project_ini['Basic']['image_path']) / \
184-
self.images[
185-
image_idx].name
186-
with exiftool.ExifToolHelper() as et:
187-
metadata = et.get_metadata(self.images[image_idx].original_filename.__str__())
188-
self.images[image_idx].exifdata = metadata[0]
189-
except exiftool.exceptions.ExifToolExecuteError as error:
190-
# traceback.print_exc()
191-
warnings.warn("Exif Data could not be read.")
187+
if self.exif_read:
188+
if self.__project_ini_path.exists():
189+
try:
190+
for image_idx in self.images.keys():
191+
self.images[image_idx].original_filename: Path = Path(self.project_ini['Basic']['image_path']) / \
192+
self.images[
193+
image_idx].name
194+
with exiftool.ExifToolHelper() as et:
195+
metadata = et.get_metadata(self.images[image_idx].original_filename.__str__())
196+
self.images[image_idx].exifdata = metadata[0]
197+
except exiftool.exceptions.ExifToolExecuteError as error:
198+
# traceback.print_exc()
199+
warnings.warn("Exif Data could not be read.")
192200

193201
def __add_infos(self):
194202
"""

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
setuptools.setup(
1818
name='colmap_wrapper',
19-
version='1.1.4',
19+
version='1.1.5',
2020
description='COLMAP Wrapper',
2121
license="MIT",
2222
long_description=long_description,

0 commit comments

Comments
 (0)