-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import argparse | ||
import os | ||
import cv2 | ||
|
||
def copy_images_no_exif(input_folder, output_folder): | ||
if not os.path.exists(output_folder): | ||
os.makedirs(output_folder) | ||
|
||
for filename in os.listdir(input_folder): | ||
if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff')): | ||
img_path = os.path.join(input_folder, filename) | ||
img = cv2.imread(img_path) | ||
if img is not None: | ||
output_path = os.path.join(output_folder, filename) | ||
cv2.imwrite(output_path, img) | ||
print(f"Copied {filename} to {output_folder}") | ||
|
||
def main(): | ||
parser = argparse.ArgumentParser(description="Copy images without EXIF data") | ||
parser.add_argument('input_folder', type=str, help="Folder with input images") | ||
parser.add_argument('output_folder', type=str, help="Folder to save output images") | ||
args = parser.parse_args() | ||
|
||
copy_images_no_exif(args.input_folder, args.output_folder) | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import sqlite3 | ||
import argparse | ||
from pathlib import Path | ||
|
||
def pair_id_to_image_ids(pair_id): | ||
image_id2 = pair_id % 2147483647 | ||
image_id1 = (pair_id - image_id2) / 2147483647 | ||
return image_id1, image_id2 | ||
|
||
def ExportMatches( | ||
database_path: Path, | ||
min_num_matches: int, | ||
output_path: Path, | ||
width: int, | ||
height: int, | ||
) -> None: | ||
|
||
if not output_path.exists(): | ||
print("Output folder does not exist") | ||
quit() | ||
|
||
connection = sqlite3.connect(database_path) | ||
cursor = connection.cursor() | ||
|
||
images = {} | ||
pairs = [] | ||
|
||
cursor.execute("SELECT image_id, name FROM images") | ||
for row in cursor: | ||
image_id = row[0] | ||
name = row[1] | ||
images[image_id] = name | ||
|
||
cursor.execute("SELECT pair_id, rows FROM two_view_geometries") | ||
for row in cursor: | ||
pair_id = row[0] | ||
n_matches = row[1] | ||
id_img1, id_img2 = pair_id_to_image_ids(pair_id) | ||
id_img1, id_img2 = int(id_img1), int(id_img2) | ||
img1 = images[id_img1] | ||
img2 = images[id_img2] | ||
|
||
if n_matches >= min_num_matches: | ||
pairs.append((img1, img2)) | ||
|
||
with open(output_path / "pairs.txt", "w") as f: | ||
n_pairs = len(pairs) | ||
n_brute = 0 | ||
N = len(images.keys()) | ||
for x in range(1, N): | ||
n_brute += N-x | ||
print(f"n_pairs/n_brute: {n_pairs} / {n_brute}") | ||
|
||
for pair in pairs: | ||
f.write(f"{pair[0]} {pair[1]}\n") | ||
|
||
connection.close() | ||
|
||
|
||
if __name__ == "__main__": | ||
parser = argparse.ArgumentParser( | ||
description="Export pairs in a txt file from a COLMAP database." | ||
) | ||
parser.add_argument("-d", "--database", type=Path, required=True, help="Path to COLMAP database.") | ||
parser.add_argument("-m", "--min_n_matches", type=int, required=True, help="Min number of matches that a pair should have after geometric verification.") | ||
parser.add_argument("-w", "--width", type=int, required=True, help="Image width.") | ||
parser.add_argument("-e", "--height", type=int, required=True, help="Image height.") | ||
parser.add_argument("-o", "--output", type=Path, required=True, help="Path to output folder.") | ||
args = parser.parse_args() | ||
|
||
ExportMatches( | ||
database_path=args.database, | ||
min_num_matches=args.min_n_matches, | ||
output_path=args.output, | ||
width=args.width, | ||
height=args.height, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import tqdm | ||
import random | ||
import argparse | ||
import pycolmap | ||
import open3d as o3d | ||
import numpy as np | ||
from scipy.spatial import cKDTree | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser(description='Process point cloud and colmap model folder') | ||
parser.add_argument('point_cloud', type=str, help='Path to the point cloud file') | ||
parser.add_argument('colmap_folder', type=str, help='Path to the folder containing the colmap model') | ||
parser.add_argument('output_folder', type=str, help='Path to the output folder') | ||
parser.add_argument('camera_pos', type=str, help='Path to camera positions') | ||
|
||
args = parser.parse_args() | ||
point_cloud_path = args.point_cloud | ||
colmap_folder_path = args.colmap_folder | ||
output_folder = args.output_folder | ||
camera_pos_path = args.camera_pos | ||
|
||
constraints = {} | ||
reconstruction = pycolmap.Reconstruction(colmap_folder_path) | ||
point_cloud = o3d.io.read_point_cloud(point_cloud_path) | ||
points_ply = np.asarray(point_cloud.points) | ||
kdtree = cKDTree(points_ply) | ||
|
||
#camera_pos = {} | ||
#with open(camera_pos_path, 'r') as f: | ||
# lines = f.readlines() | ||
# for line in lines: | ||
# line = line.strip() | ||
# image, x, y, z, _ = line.split('\t', 4) | ||
# for img_id, img in reconstruction.images.items(): | ||
# if img.name == image: | ||
# camera_pos[img_id] = np.array([float(x), float(y), float(z)]) | ||
# break | ||
# | ||
#scale, rotation, translation = pycolmap.align_reconstruction(reconstruction, camera_pos);quit() | ||
|
||
point3Ds = [] | ||
for point3D_id in reconstruction.points3D: | ||
point3Ds.append(point3D_id) | ||
|
||
random_point3D_ids = random.sample(point3Ds, 200) | ||
|
||
for point3D_id in random_point3D_ids: | ||
point3D = reconstruction.points3D[point3D_id] | ||
#print(f"3D Point {point3D_id} coordinates: {point3D.xyz}") | ||
|
||
projections = [] | ||
for image in reconstruction.images.values(): | ||
# For each image, check if it observes the 3D point | ||
for feature_idx, point2D in enumerate(image.points2D): | ||
if point2D.has_point3D and point2D.point3D_id == point3D_id: | ||
# Add the projection information: image ID and 2D point coordinates | ||
projections.append((image.image_id, point2D.xy)) | ||
#print(f"Image {image.image_id}: Projected 2D point (x, y) = {point2D.xy}") | ||
|
||
constraints[point3D_id] = { | ||
'3D_tiepoint': point3D.xyz, | ||
'projections': projections, | ||
'lidar_point': None, | ||
} | ||
|
||
# Export to txt file | ||
with open(f'{output_folder}/constraints.txt', 'w') as f: | ||
for point3D_id in random_point3D_ids: | ||
for image_id, point2D in constraints[point3D_id]['projections']: | ||
image = reconstruction.images[image_id] | ||
f.write(f"{point3D_id},{image.name[:-4]},{point2D[0]},{point2D[1]}\n") | ||
|
||
|
||
query_points = np.empty((0, 3)) | ||
ids = [] | ||
for point3D_id in random_point3D_ids: | ||
x = constraints[point3D_id]['3D_tiepoint'][0] | ||
y = constraints[point3D_id]['3D_tiepoint'][1] | ||
z = constraints[point3D_id]['3D_tiepoint'][2] | ||
query_points = np.vstack((query_points, np.array([float(x), float(y), float(z)]))) | ||
ids.append(point3D_id) | ||
|
||
distances, indices = kdtree.query(query_points) | ||
|
||
for i, (point3D_id, index) in enumerate(zip(ids, indices)): | ||
nearest_point = points_ply[index] | ||
constraints[point3D_id]['lidar_point'] = nearest_point | ||
|
||
with open(f'{output_folder}/points.txt', 'w') as f: | ||
for point3D_id in random_point3D_ids: | ||
x_photogram = float(constraints[point3D_id]['3D_tiepoint'][0]) | ||
y_photogram = float(constraints[point3D_id]['3D_tiepoint'][1]) | ||
z_photogram = float(constraints[point3D_id]['3D_tiepoint'][2]) | ||
x_lidar = float(constraints[point3D_id]['lidar_point'][0]) | ||
y_lidar = float(constraints[point3D_id]['lidar_point'][1]) | ||
z_lidar = float(constraints[point3D_id]['lidar_point'][2]) | ||
norm = ((x_photogram-x_lidar)**2+(y_photogram-y_lidar)**2+(z_photogram-z_lidar)**2)**0.5 | ||
print(norm) | ||
if norm < 0.20: | ||
f.write(f"{point3D_id},{x_lidar},{y_lidar},{z_lidar}\n") | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |