Commit ac6f363f authored by Clément Pinard's avatar Clément Pinard
Browse files

add new images ground truth generation (needs doc now)

parent 5c977416
......@@ -37,14 +37,13 @@ def preprocess_metadata(metadata, proj):
metadata["indoor"] = False
if 0 in metadata["location_valid"].unique():
location_validity = metadata["location_valid"].diff()
invalidity_start = location_validity.index[location_validity == -1].tolist()
validity_start = location_validity.index[location_validity == 1].tolist()
if metadata["location_valid"].iloc[0]:
if not metadata["location_valid"].iloc[0]:
end = validity_start.pop(0)
positions[:end] = extrapolate_position(speed[:end], timestamps[:end], None, positions[end])
if metadata["location_valid"].iloc[-1]:
if not metadata["location_valid"].iloc[-1]:
start = invalidity_start.pop(-1) - 1
positions[start:] = extrapolate_position(speed[start:], timestamps[start:], positions[start], None)
......
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from path import Path
import numpy as np
def set_argparser():
parser = ArgumentParser(description='Main pipeline, from LIDAR pictures and videos to GT depth enabled videos',
formatter_class=ArgumentDefaultsHelpFormatter)
def add_main_options(parser):
main_parser = parser.add_argument_group("Main options")
main_parser.add_argument('--input_folder', metavar='PATH', default=Path("."), type=Path,
help="Folder with LAS point cloud, videos, and images")
......@@ -21,7 +19,9 @@ def set_argparser():
main_parser.add_argument('--begin_step', metavar="N", type=int, default=None)
main_parser.add_argument('--show_steps', action="store_true")
main_parser.add_argument('--add_new_videos', action="store_true",
help="If selected, will skit first 6 steps to directly register videos without mapping")
help="If selected, will skip first 6 steps to directly register videos without mapping")
main_parser.add_argument('--generate_groundtruth_for_individual_images', action="store_true",
help="If selected, will generate Ground truth for individual images as well as videos")
main_parser.add_argument('--save_space', action="store_true")
main_parser.add_argument('-v', '--verbose', action="count", default=0)
main_parser.add_argument('--resume_work', action="store_true",
......@@ -36,7 +36,9 @@ def set_argparser():
main_parser.add_argument('--raw_ext', nargs='+', default=[".ARW", ".NEF", ".DNG"],
help='Raw Image extensions to scrape from input folder')
pcp_parser = parser.add_argument_group("PointCLoud preparation")
def add_pcp_options(parser):
pcp_parser = parser.add_argument_group("PointCloud preparation")
pcp_parser.add_argument("--pointcloud_resolution", default=None, type=float,
help='If set, will subsample the Lidar point clouds at the chosen resolution')
pcp_parser.add_argument("--SOR", default=[10, 6], nargs=2, type=float,
......@@ -44,6 +46,8 @@ def set_argparser():
pcp_parser.add_argument('--registration_method', choices=["simple", "eth3d", "interactive"], default="simple",
help='Method used for point cloud registration. See README, Manual step by step : step 11')
def add_ve_options(parser):
ve_parser = parser.add_argument_group("Video extractor")
ve_parser.add_argument('--total_frames', default=500, type=int)
ve_parser.add_argument('--orientation_weight', default=1, type=float,
......@@ -66,7 +70,12 @@ def set_argparser():
ve_parser.add_argument('--generic_model', default='OPENCV',
help='COLMAP model for generic videos. Same zoom level assumed throughout the whole video. '
'See https://colmap.github.io/cameras.html')
ve_parser.add_argument('--full_metadata', default=None,
help='where to save all concatenated metadata in a file that will be used to add new videos afterward. '
'If not set, will save at the root of workspace')
def add_exec_options(parser):
exec_parser = parser.add_argument_group("Executable files")
exec_parser.add_argument('--log', default=None, type=Path)
exec_parser.add_argument('--nw', default="native-wrapper.sh", type=Path,
......@@ -78,6 +87,8 @@ def set_argparser():
exec_parser.add_argument("--ffmpeg", default="ffmpeg", type=Path)
exec_parser.add_argument("--pcl_util", default="pcl_util/build", type=Path)
def add_pm_options(parser):
pm_parser = parser.add_argument_group("Photogrammetry")
pm_parser.add_argument('--max_num_matches', default=32768, type=int, help="max number of matches, lower it if you get GPU memory error")
pm_parser.add_argument('--vocab_tree', type=Path, default="vocab_tree_flickr100K_words256K.bin")
......@@ -94,6 +105,8 @@ def set_argparser():
pm_parser.add_argument('--stereo_min_depth', type=float, default=0.1, help="Min depth for PatchMatch Stereo")
pm_parser.add_argument('--stereo_max_depth', type=float, default=100, help="Max depth for PatchMatch Stereo")
def add_om_options(parser):
om_parser = parser.add_argument_group("Occlusion Mesh")
om_parser.add_argument('--normals_method', default="radius", choices=["radius", "neighbours"],
help='Method used for normal computation between radius and nearest neighbours')
......@@ -112,11 +125,69 @@ def set_argparser():
help='Splat size is defined by mean istance from its neighbours. You can define a max splat size for '
'isolated points which otherwise would make a very large useless splat. '
'If not set, will be `2.5*splat_threshold`.')
def add_gt_options(parser):
gt_parser = parser.add_argument_group("Ground Truth Creator")
gt_parser.add_argument('--max_occlusion_depth', default=250, type=float,
help='max depth for occlusion. Everything further will not be considered at infinity')
gt_parser.add_argument('--eth3d_splat_radius', default=0.01, type=float,
help='see splat radius for ETH3D')
def set_full_argparser():
parser = ArgumentParser(description='Main pipeline, from LIDAR pictures and videos to GT depth enabled videos',
formatter_class=ArgumentDefaultsHelpFormatter)
add_main_options(parser)
add_pcp_options(parser)
add_ve_options(parser)
add_exec_options(parser)
add_pm_options(parser)
add_om_options(parser)
add_gt_options(parser)
return parser
def set_full_argparser_no_lidar():
parser = ArgumentParser(description='Main pipeline, from pictures and videos to GT depth enabled videos, '
'using only COLMAP (No lidar)',
formatter_class=ArgumentDefaultsHelpFormatter)
add_main_options(parser)
add_ve_options(parser)
add_exec_options(parser)
add_pm_options(parser)
add_om_options(parser)
add_gt_options(parser)
parser.add_argument("--SOR", default=[10, 6], nargs=2, type=float,
help="Satistical Outlier Removal parameters : Number of nearest neighbours, "
"max relative distance to standard deviation. "
"This will be used for filtering dense reconstruction")
return parser
def set_new_images_arparser():
parser = ArgumentParser(description='Additional pipeline, to add new pictures to an already existing workspace, '
'The main pipeline must be finished before launching this script, '
' at least until reconstruction alignment',
formatter_class=ArgumentDefaultsHelpFormatter)
add_main_options(parser)
add_exec_options(parser)
add_pm_options(parser)
add_om_options(parser)
add_gt_options(parser)
parser.add_argument("--map_new_images", action="store_true",
help="if selected, will replace the 'omage_registrator' step with a full mapping step")
parser.add_argument("--bundle_adjustor_steps", default=100, type=int,
help="number of iteration for bundle adjustor after image registration")
parser.add_argument("--rebuild_occlusion_mesh", action="store_true",
help="If selected, will rebuild a new dense point cloud and deauney mesh. "
"Useful when new images see new parts of the model")
parser.add_argument('--generic_model', default='OPENCV',
help='COLMAP model for generic videos. Same zoom level assumed throughout the whole video. '
'See https://colmap.github.io/cameras.html')
return parser
......@@ -159,3 +230,21 @@ def print_workflow():
print("\tPer video:")
for i, s in enumerate(per_vid_steps_2):
print("\t{}) {}".format(i+1, s))
def get_matrix(path):
if path.isfile():
'''Note : We use the inverse matrix here, because in general, it's easier to register the reconstructed model into the lidar one,
as the reconstructed will have less points, but in the end we need the matrix to apply to the lidar point to be aligned
with the camera positions (ie the inverse)'''
return np.linalg.inv(np.fromfile(path, sep=" ").reshape(4, 4))
else:
print("Error, no registration matrix can be found")
print("Ensure that your registration matrix was saved under the name {}".format(path))
decision = None
while decision not in ["y", "n", ""]:
decision = input("retry ? [Y/n]")
if decision.lower() in ["y", ""]:
return get_matrix(path)
elif decision.lower() == "n":
return np.eye(4)
......@@ -11,11 +11,11 @@ from tqdm import tqdm
from wrappers import FFMpeg
import gzip
from pebble import ProcessPool
import pandas as pd
import yaml
def save_intrinsics(cameras, images, output_dir, downscale=1):
def construct_intrinsics(cam):
def save_intrinsics(cameras, images, output_dir, output_width=None, downscale=None):
def construct_intrinsics(cam, downscale):
# assert('PINHOLE' in cam.model)
if 'SIMPLE' in cam.model:
fx, cx, cy, *_ = cam.params
......@@ -27,16 +27,32 @@ def save_intrinsics(cameras, images, output_dir, downscale=1):
[0, fy / downscale, cy / downscale],
[0, 0, 1]])
def save_cam(cam, intrinsics_path, yaml_path):
if downscale is None:
current_downscale = output_width / cam.width
else:
current_downscale = downscale
intrinsics = construct_intrinsics(cam, current_downscale)
np.savetxt(intrinsics_path, intrinsics)
with open(yaml_path, 'w') as f:
camera_dict = {"model": cam.model,
"params": cam.params,
"width": cam.width / current_downscale,
"height": cam.height / current_downscale}
yaml.dump(camera_dict, f, default_flow_style=False)
if len(cameras) == 1:
print("bonjour")
cam = cameras[list(cameras.keys())[0]]
intrinsics = construct_intrinsics(cam)
np.savetxt(output_dir / 'intrinsics.txt', intrinsics)
save_cam(cam, output_dir / "intrinsics.txt", output_dir / "camera.yaml")
else:
print("au revoir")
for _, img in images.items():
cam = cameras[img.camera_id]
intrinsics = construct_intrinsics(cam)
intrinsics_name = output_dir / Path(img.name).stem + "_intrinsics.txt"
np.savetxt(intrinsics_name, intrinsics)
save_cam(cam, output_dir / Path(img.name).stem + "_intrinsics.txt",
output_dir / Path(img.name).stem + "_camera.yaml")
def to_transform_matrix(q, t):
......@@ -179,27 +195,37 @@ parser.add_argument('--verbose', '-v', action='count', default=0)
def convert_dataset(final_model, depth_dir, images_root_folder, occ_dir,
dataset_output_dir, video_output_dir, metadata_path, interpolated_frames,
ffmpeg, threads=8, downscale=None, compressed=True,
dataset_output_dir, video_output_dir, ffmpeg,
interpolated_frames=[], metadata=None, images_list=None,
threads=8, downscale=None, compressed=True,
width=None, visualization=False, video=False, verbose=0, **env):
dataset_output_dir.makedirs_p()
video_output_dir.makedirs_p()
if video:
visualization = True
cameras, images, _ = rm.read_model(final_model, '.txt')
metadata = pd.read_csv(metadata_path).set_index("db_id", drop=False).sort_values("time")
framerate = metadata["framerate"].values[0]
if downscale is None:
assert(width is not None)
# image_df = pd.DataFrame.from_dict(images, orient="index").set_index("id")
if metadata is not None:
metadata = metadata.set_index("db_id", drop=False).sort_values("time")
framerate = metadata["framerate"].values[0]
# image_df = image_df.reindex(metadata.index)
images_list = metadata["image_path"]
else:
assert images_list is not None
framerate = None
video = False
input_width = metadata["width"].values[0]
downscale = width / input_width
# Discard images and cameras that are not represented by the image list
images = {k: i for k, i in images.items() if i.name in images_list}
cameras_ids = set([i.camera_id for i in images.values()])
cameras = {k: cameras[k] for k in cameras_ids}
save_intrinsics(cameras, images, dataset_output_dir, downscale)
if downscale is None:
assert width is not None
save_intrinsics(cameras, images, dataset_output_dir, width, downscale)
save_positions(images, dataset_output_dir)
image_df = pd.DataFrame.from_dict(images, orient="index").set_index("id")
image_df = image_df.reindex(metadata.index)
depth_maps = []
occ_maps = []
interpolated = []
......@@ -207,7 +233,7 @@ def convert_dataset(final_model, depth_dir, images_root_folder, occ_dir,
cameras = []
not_registered = 0
for i in metadata["image_path"]:
for i in images_list:
img_path = images_root_folder / i
imgs.append(img_path)
......@@ -236,8 +262,8 @@ def convert_dataset(final_model, depth_dir, images_root_folder, occ_dir,
depth_maps.append(None)
occ_maps.append(None)
interpolated.append(False)
print('{}/{} Frames not registered ({:.2f}%)'.format(not_registered, len(metadata), 100*not_registered/len(metadata)))
print('{}/{} Frames interpolated ({:.2f}%)'.format(sum(interpolated), len(metadata), 100*sum(interpolated)/len(metadata)))
print('{}/{} Frames not registered ({:.2f}%)'.format(not_registered, len(images_list), 100*not_registered/len(images_list)))
print('{}/{} Frames interpolated ({:.2f}%)'.format(sum(interpolated), len(images_list), 100*sum(interpolated)/len(images_list)))
if threads == 1:
for i, d, o, n in tqdm(zip(imgs, depth_maps, occ_maps, interpolated), total=len(imgs)):
process_one_frame(i, d, o, dataset_output_dir, video_output_dir, downscale, n, visualization, viz_width=1920)
......
......@@ -11,21 +11,39 @@ parser.add_argument('--input_model', metavar='DIR', type=Path, required=True,
parser.add_argument('--output_model', metavar='DIR', type=Path, required=True,
help='Output folder where the modified COLMAP model will be saved')
parser.add_argument('--output_format', choices=['.txt', '.bin'], default='.txt')
parser.add_argument('--metadata_path', metavar="CSV", type=Path, required=True,
help='Path to metadata CSV file of the desired video. '
'Usually in /pictures/Videos/<size>/<video_name>/metadata.csv')
group = parser.add_mutually_exclusive_group()
group.add_argument('--metadata_path', metavar="CSV", type=Path, default=None,
help='Path to metadata CSV file of the desired video. '
'Usually in /pictures/Videos/<size>/<video_name>/metadata.csv')
group.add_argument('--picture_list_path', type=Path, default=None,
help='Path to list of picture to extract from model. '
'Picture paths must be relatvie to colmap root')
def extract_video(input, output, video_metadata_path, output_format='.bin'):
def extract_pictures(input, output, picture_list, output_format='.bin'):
cameras = rm.read_cameras_binary(input / "cameras.bin")
images = rm.read_images_binary(input / "images.bin")
images_per_name = {}
video_metadata = pd.read_csv(video_metadata_path)
image_names = video_metadata["image_path"].values
camera_ids = []
def add_image(image):
images_per_name[image.name] = image._replace(xys=[], point3D_ids=[])
cam_id = image.camera_id
if cam_id not in camera_ids:
camera_ids.append(cam_id)
for id, image in images.items():
if image.name in image_names:
images_per_name[image.name] = image._replace(xys=[], point3D_ids=[])
camera_ids = video_metadata["camera_id"].unique()
if image.name in picture_list:
add_image(image)
if len(images_per_name) == 1:
# Add also first picture so that we have multiple pictures.
# Otherwise, GourndTruth Creator will error
for id, image in images.items():
if image.name not in picture_list:
add_image(image)
break
output_cameras = {cid: cameras[cid] for cid in camera_ids if cid in cameras.keys()}
rm.write_model(output_cameras, images_per_name, {}, output, output_format)
......@@ -35,7 +53,12 @@ def extract_video(input, output, video_metadata_path, output_format='.bin'):
def main():
args = parser.parse_args()
extract_video(args.input_model, args.output_model, args.metadata_path, args.output_format)
if args.metadata_path is not None:
picture_list = pd.read_csv(args.metadata_path)["image_path"].values
elif args.picture_list_path is not None:
with open(args.picture_list_path, 'r') as f:
picture_list = [line[:-1] for line in f.readlines()]
extract_pictures(args.input_model, args.output_model, picture_list, args.output_format)
return
......
......@@ -58,13 +58,13 @@ def extract_sky_mask(network, image_paths, mask_folder):
imageio.imwrite(mask_folder/(path.basename() + '.png'), (f*255).astype(np.uint8))
def process_folder(folder_to_process, image_path, mask_path, pic_ext, verbose=False, batchsize=8, **env):
def process_folder(folder_to_process, colmap_img_root, mask_path, pic_ext, verbose=False, batchsize=8, **env):
network = prepare_network()
folders = [folder_to_process] + list(folder_to_process.walkdirs())
for folder in folders:
mask_folder = mask_path/image_path.relpathto(folder)
mask_folder = mask_path/colmap_img_root.relpathto(folder)
mask_folder.makedirs_p()
images = sum((folder.files('*{}'.format(ext)) for ext in pic_ext), [])
if images:
......
......@@ -23,7 +23,6 @@ def load_and_convert(input_file, output_folder, verbose=False):
file_type = input_file.ext[1:].upper()
if file_type == "LAS":
offset = np.array(laspy.file.File(input_file, mode="r").header.offset)
print(offset)
else:
offset = np.zeros(3)
cloud = PyntCloud.from_file(input_file)
......
import las2ply
import numpy as np
from wrappers import Colmap, FFMpeg, PDraw, ETH3D, PCLUtil
from cli_utils import set_argparser, print_step, print_workflow
from video_localization import localize_video, generate_GT
from cli_utils import set_full_argparser, print_step, print_workflow, get_matrix
from video_localization import localize_video, generate_GT, generate_GT_individual_pictures
import meshlab_xml_writer as mxw
import prepare_images as pi
import prepare_workspace as pw
......@@ -37,20 +37,21 @@ def prepare_point_clouds(pointclouds, lidar_path, verbose, eth3d, pcl_util, SOR,
def main():
args = set_argparser().parse_args()
args = set_full_argparser().parse_args()
env = vars(args)
if args.show_steps:
print_workflow()
return
if args.add_new_videos:
args.skip_step += [1, 2, 4, 5, 6]
env["resume_work"] = True
args.skip_step = [1, 2, 4, 5, 8]
if args.begin_step is not None:
args.skip_step += list(range(args.begin_step))
pw.check_input_folder(args.input_folder)
args.workspace = args.workspace.abspath()
pw.prepare_workspace(args.workspace, env)
colmap = Colmap(db=env["thorough_db"],
image_path=env["image_path"],
image_path=env["colmap_img_root"],
mask_path=env["mask_path"],
dense_workspace=env["dense_workspace"],
binary=args.colmap,
......@@ -90,21 +91,24 @@ def main():
i += 1
if i not in args.skip_step:
print_step(i, "Pictures preparation")
env["existing_pictures"] = pi.extract_pictures_to_workspace(**env)
env["individual_pictures"] = pi.extract_pictures_to_workspace(**env)
else:
env["existing_pictures"] = sum((list(env["image_path"].walkfiles('*{}'.format(ext))) for ext in env["pic_ext"]), [])
full_paths = sum((list(env["individual_pictures_path"].walkfiles('*{}'.format(ext))) for ext in env["pic_ext"]), [])
env["individual_pictures"] = [path.relpath(env["colmap_img_root"]) for path in full_paths]
i += 1
# Get already existing_videos
env["videos_frames_folders"] = {}
by_name = {v.stem: v for v in env["videos_list"]}
for folder in env["video_path"].walkdirs():
video_name = folder.basename()
if video_name in by_name.keys():
env["videos_frames_folders"][by_name[video_name]] = folder
if i not in args.skip_step:
print_step(i, "Extracting Videos and selecting optimal frames for a thorough scan")
env["videos_frames_folders"] = pi.extract_videos_to_workspace(fps=args.lowfps, **env)
else:
env["videos_frames_folders"] = {}
by_name = {v.stem: v for v in env["videos_list"]}
for folder in env["video_path"].walkdirs():
video_name = folder.basename()
if video_name in by_name.keys():
env["videos_frames_folders"][by_name[video_name]] = folder
new_video_frame_folders = pi.extract_videos_to_workspace(fps=args.lowfps, **env)
# Concatenate both already treated videos and newly detected videos
env["videos_frames_folders"] = {**env["videos_frames_folders"], **new_video_frame_folders}
env["videos_workspaces"] = {}
for v, frames_folder in env["videos_frames_folders"].items():
env["videos_workspaces"][v] = pw.prepare_video_workspace(v, frames_folder, **env)
......@@ -127,6 +131,7 @@ def main():
if i not in args.skip_step:
print_step(i, "Alignment of photogrammetric reconstruction with GPS")
env["georef_recon"].makedirs_p()
env["georef_full_recon"].makedirs_p()
colmap.align_model(output=env["georef_recon"],
input=thorough_model,
ref_images=env["georef_frames_list"])
......@@ -136,6 +141,9 @@ def main():
thorough_model.merge_tree(env["georef_recon"])
env["georef_recon"].merge_tree(env["georef_full_recon"])
if args.inspect_dataset:
print("FIRST DATASET INSPECTION")
print("Inspection of localisalization of frames used in thorough mapping "
"w.r.t Sparse reconstruction")
colmap.export_model(output=env["georef_recon"] / "georef_sparse.ply",
input=env["georef_recon"])
georef_mlp = env["georef_recon"]/"georef_recon.mlp"
......@@ -145,7 +153,7 @@ def main():
output_type="TXT")
eth3d.inspect_dataset(scan_meshlab=georef_mlp,
colmap_model=env["georef_recon"],
image_path=env["image_path"])
image_path=env["colmap_img_root"])
i += 1
if i not in args.skip_step:
......@@ -163,30 +171,13 @@ def main():
i += 1
if i not in args.skip_step:
print_step(i, "Full reconstruction point cloud densificitation")
env["georef_full_recon"].makedirs_p()
colmap.undistort(input=env["georef_full_recon"])
colmap.dense_stereo(min_depth=env["stereo_min_depth"], max_depth=env["stereo_max_depth"])
colmap.stereo_fusion(output=env["georefrecon_ply"])
def get_matrix(path):
if path.isfile():
'''Note : We use the inverse matrix here, because in general, it's easier to register the reconstructed model into the lidar one,
as the reconstructed will have less points, but in the end we need the matrix to apply to the lidar point to be aligned
with the camera positions (ie the inverse)'''
return np.linalg.inv(np.fromfile(path, sep=" ").reshape(4, 4))
else:
print("Error, no registration matrix can be found")
print("Ensure that your registration matrix was saved under the name {}".format(path))
decision = None
while decision not in ["y", "n", ""]:
decision = input("retry ? [Y/n]")
if decision.lower() in ["y", ""]:
return get_matrix(path)
elif decision.lower() == "n":
return np.eye(4)
i += 1
if i not in args.skip_step:
print_step(i, "Registration of photogrammetric reconstruction with respect to Lidar Point Cloud")
print_step(i, "Alignment of photogrammetric reconstruction with respect to Lidar Point Cloud")
if args.registration_method == "eth3d":
# Note : ETH3D doesn't register with scale, this might not be suitable for very large areas
mxw.add_meshes_to_project(env["lidar_mlp"], env["aligned_mlp"], [env["georefrecon_ply"]], start_index=0)
......@@ -248,22 +239,27 @@ def main():
if args.inspect_dataset:
# First inspection : Check registration of the Lidar pointcloud wrt to COLMAP model but without the occlusion mesh
# Second inspection : Check the occlusion mesh and the splats
colmap.export_model(output=env["georef_full_recon"] / "georef_sparse.ply",
input=env["georef_full_recon"])
georef_mlp = env["georef_recon"]/"georef_recon.mlp"
mxw.create_project(georef_mlp, [env["georefrecon_ply"]])
colmap.export_model(output=env["georef_full_recon"],
input=env["georef_full_recon"],
output_type="TXT")
print("SECOND DATASET INSPECTION")
print("Inspection of localisalization of frames used in thorough mapping "
"w.r.t Dense reconstruction")
eth3d.inspect_dataset(scan_meshlab=georef_mlp,
colmap_model=env["georef_full_recon"],
image_path=env["image_path"])
image_path=env["colmap_img_root"])
print("Inspection of localisalization of frames used in thorough mapping "
"w.r.t Aligned Lidar Point Cloud")
eth3d.inspect_dataset(scan_meshlab=env["aligned_mlp"],
colmap_model=env["georef_full_recon"],
image_path=env["image_path"])
image_path=env["colmap_img_root"])
print("Inspection of localisalization of frames used in thorough mapping "
"w.r.t Aligned Lidar Point Cloud and Occlusion Meshes")
eth3d.inspect_dataset(scan_meshlab=env["aligned_mlp"],
colmap_model=env["georef_full_recon"],
image_path=env["image_path"],
image_path=env["colmap_img_root"],
occlusions=env["occlusion_ply"],
splats=env["splats_ply"])
......@@ -279,6 +275,9 @@ def main():
num_videos=len(env["videos_to_localize"]),
metadata=video_env["metadata"],
**video_env["output_env"], **env)
if env["generate_groundtruth_for_individual_images"]:
generate_GT_individual_pictures(input_colmap_model=env["georef_full_recon"],
step_index=i, **env)
if __name__ == '__main__':
......
import numpy as np
from wrappers import Colmap, FFMpeg, PDraw, ETH3D, PCLUtil
from cli_utils import set_argparser, print_step, print_workflow
from cli_utils import set_full_argparser, print_step, print_workflow
from video_localization import localize_video, generate_GT
import meshlab_xml_writer as mxw
import prepare_images as pi
......@@ -8,7 +8,7 @@ import prepare_workspace as pw
def main():
args = set_argparser().parse_args()
args = set_full_argparser().parse_args()
env = vars(args)
if args.show_steps:
print_workflow()
......@@ -21,7 +21,7 @@ def main():
args.workspace = args.workspace.abspath()
pw.prepare_workspace(args.workspace, env, with_lidar=False)
colmap = Colmap(db=env["thorough_db"],
image_path=env["image_path"],
image_path=env["colmap_img_root"],
mask_path=env["mask_path"],
dense_workspace=env["dense_workspace"],
binary=args.colmap,
......@@ -49,7 +49,7 @@ def main():
print_step(i, "Pictures preparation")
env["existing_pictures"] = pi.extract_pictures_to_workspace(**env)
else:
env["existing_pictures"] = sum((list(env["image_path"].walkfiles('*{}'.format(ext))) for ext in env["pic_ext"]), [])
env["existing_pictures"] = sum((list(env["colmap_img_root"].walkfiles('*{}'.format(ext))) for ext in env["pic_ext"]), [])
i += 1
if i not in args.skip_step:
......@@ -103,7 +103,7 @@ def main():
output_type="TXT")
eth3d.inspect_dataset(scan_meshlab=georef_mlp,
colmap_model=env["georef_recon"],
image_path=env["image_path"])