def export_openmvg_command_line(): """ Do the kapture to openmvg export using the parameters given on the command line. """ parser = argparse.ArgumentParser( description='Exports from kapture format to openMVG JSON file.') parser_verbosity = parser.add_mutually_exclusive_group() parser_verbosity.add_argument( '-v', '--verbose', nargs='?', default=logging.WARNING, const=logging.INFO, action=kapture.utils.logging.VerbosityParser, help= 'verbosity level (debug, info, warning, critical, ... or int value) [warning]' ) parser_verbosity.add_argument('-q', '--silent', '--quiet', action='store_const', dest='verbose', const=logging.CRITICAL) parser.add_argument('-f', '-y', '--force', action='store_true', default=False, help='Force delete output file if already exists.') # create the parser for the export command ######################################################################### parser.add_argument('-k', '--kapture', required=True, help='input path to kapture data root directory') parser.add_argument('-o', '--openmvg', help='output openMVG JSON file path.') parser.add_argument('--image_action', default='root_link', type=TransferAction, help=f'''what to do with images: {TransferAction.root_link.name}: link to the root of the images directory (default) ; {TransferAction.link_absolute.name}: absolute individual file link ; {TransferAction.link_relative.name}: relative individual file link ; {TransferAction.copy.name}: copy file instead of creating link ; {TransferAction.move.name}: move file instead of creating link ; {TransferAction.skip.name}: do not create links ''') args = parser.parse_args() logger.setLevel(args.verbose) if args.verbose <= logging.DEBUG: # for debug, let kapture express itself. kapture.utils.logging.getLogger().setLevel(args.verbose) logger.debug(f'{sys.argv[0]} \\\n' + ' \\\n'.join('--{:20} {:100}'.format(k, str(v)) for k, v in vars(args).items())) # Check that we will not try to do symbolics links on Windows # because this is not possible today if the user is not admin if sys.platform.startswith("win") and \ (args.image_link == TransferAction.link_relative or args.image_link == TransferAction.link_absolute or args.image_link == TransferAction.root_link): logger.fatal( "It is currently impossible to link files on a Windows platform") logger.fatal( f"Please try another option for the image: either do nothing ({TransferAction.skip})," f" or {TransferAction.copy}") raise OSError("Image file linking not possible on Windows") else: export_openmvg(args.kapture, args.openmvg, args.image_action, args.force)
def test_kapture_to_openmvg(self) -> None: """ Test the kapture_to_openmvg export function on a small kapture dataset """ self.assertTrue(path.exists(self._kapture_sample_path), "Kapture directory exists") json_file = path.join(self._tempdir.name, 'sfm_export.json') export_openmvg(self._kapture_sample_path, json_file, TransferAction.copy, force=True) self.assertTrue(path.isfile(json_file), "Openmvg JSON file created") with open(json_file, 'r') as f: sfm_data = json.load(f) self.assertEqual(SFM_DATA_VERSION_NUMBER, sfm_data.get(SFM_DATA_VERSION), "Sfm data version number") root_path = sfm_data.get(ROOT_PATH) self.assertIsNotNone(root_path, "Root path exported") self.assertEqual(self._tempdir.name, root_path, "Root path correct") intrinsics = sfm_data.get(INTRINSICS) self.assertIsNotNone(intrinsics, "Intrinsics") self.assertEqual(9, len(intrinsics), "Cameras") camera_ids = {} # Search for camera 22970285 and lidar1 basler = None lidar = None for intrinsic in intrinsics: camera_id = intrinsic.get(KEY) camera_ids[camera_id] = camera_id if camera_id == FIRST_BASLER_ID: basler = intrinsic.get(VALUE) elif camera_id == LIDAR1: lidar = intrinsic.get(VALUE) self.assertEqual(9, len(camera_ids), "All camera identifiers are different") self.assertIsNotNone(basler, "First basler camera") self.assertEqual(CameraModel.pinhole_brown_t2.name, basler.get(POLYMORPHIC_NAME), "Polymorphic name") camera_params = basler.get(PTR_WRAPPER).get(DATA).get(VALUE0) self.assertEqual(2048, camera_params.get(WIDTH), "Camera width") self.assertEqual(1536, camera_params.get(HEIGHT), "Camera height") self.assertAlmostEqual(1725.842032333, camera_params.get(FOCAL_LENGTH), msg="Focal length") self.assertEqual(1024, camera_params.get(PRINCIPAL_POINT)[0], "Principal point X") self.assertEqual(768, camera_params.get(PRINCIPAL_POINT)[1], "Principal point Y") self.assertIsNotNone( basler.get(PTR_WRAPPER).get(DATA).get(DISTO_T2), "Disto_t2 defined") # Make sure lidar has not been exported to openMVG self.assertIsNone(lidar, "Lidar 1") # Recorded images: views in openmvg format views = sfm_data.get(VIEWS) self.assertIsNotNone(views, "Views") self.assertEqual(18, len(views), "Recorded images") image_record = None for view in views: if view.get(KEY) == 3: image_record = view.get(VALUE).get(PTR_WRAPPER).get(DATA) break self.assertIsNotNone(image_record, "4th image record") local_path = image_record.get(LOCAL_PATH) self.assertEqual(FIRST_BASLER_ID, local_path, "Local path is the camera id") self.assertEqual(FIRST_BASLER_ID, image_record.get(ID_INTRINSIC), "id_intrinsic is the camera id") self.assertEqual(camera_params.get(WIDTH), image_record.get(WIDTH), "Image has camera width") self.assertEqual(camera_params.get(HEIGHT), image_record.get(HEIGHT), "Image has camera height") filename = image_record.get(FILENAME) copied_image_path = path.join(root_path, local_path, filename) self.assertTrue(path.isfile(copied_image_path), "Image copied") self.assertIsNotNone(filename, "Filename is defined") pose_id = image_record.get(ID_POSE) self.assertIsNotNone(pose_id, "Pose id") self.assertTrue(image_record.get(USE_POSE_ROTATION_PRIOR), "Use pose rotation prior is true") view_center = image_record.get(CENTER) view_rotation = image_record.get(ROTATION) self.assertIsNotNone(view_center, "Center of image") self.assertIsNotNone(view_rotation, "Rotation of image") # poses: extrinsics in openmvg format extrinsics = sfm_data.get(EXTRINSICS) self.assertIsNotNone(extrinsics, "Extrinsics") self.assertEqual(10, len(extrinsics), "Trajectory points") # Search for the pose of the above image pose = None for extrinsic in extrinsics: if extrinsic.get(KEY) == pose_id: pose = extrinsic.get(VALUE) break self.assertIsNotNone(pose, "Pose for image") pose_center = pose.get(CENTER) pose_rotation = pose.get(ROTATION) self.assertEqual(view_center, pose_center, "Center are equal") self.assertEqual(view_rotation, pose_rotation, "Rotations are equal")
def export_openmvg_command_line(): """ Do the kapture to openmvg export using the parameters given on the command line. """ parser = argparse.ArgumentParser( description='Exports from kapture format to openMVG JSON file.') parser_verbosity = parser.add_mutually_exclusive_group() parser_verbosity.add_argument( '-v', '--verbose', nargs='?', default=logging.WARNING, const=logging.INFO, action=kapture.utils.logging.VerbosityParser, help= 'verbosity level (debug, info, warning, critical, ... or int value) [warning]' ) parser_verbosity.add_argument('-q', '--silent', '--quiet', action='store_const', dest='verbose', const=logging.CRITICAL) parser.add_argument('-f', '-y', '--force', action='store_true', default=False, help='Force delete output file if already exists.') # create the parser for the export command ######################################################################### parser.add_argument('-i', '-k', '--kapture', required=True, help='path to input kapture data root directory') parser.add_argument( '-o', '--openmvg', help='root path to openmvg data structure (subpath are guessed).') parser.add_argument('-s', '--sfm_data', help='path to output openMVG sfm_data JSON file.') parser.add_argument('-im', '--images', help='path to output openMVG image directory.') parser.add_argument( '-r', '--regions', help= 'path to output openMVG regions directory (for features and descriptors).' ) parser.add_argument('-m', '--matches', help='path to output openMVG matches file.') parser.add_argument('--image_action', default='skip', type=TransferAction, help=f'''what to do with images: {TransferAction.root_link.name}: link to the root of the images directory (default) ; {TransferAction.link_absolute.name}: absolute individual file link ; {TransferAction.link_relative.name}: relative individual file link ; {TransferAction.copy.name}: copy file instead of creating link ; {TransferAction.move.name}: move file instead of creating link ; {TransferAction.skip.name}: do not create links ''') parser.add_argument( '--image_path_flatten', action='store_true', help= 'flatten image subpath, to make sure there is no collision in image names.' ) args = parser.parse_args() logger.setLevel(args.verbose) if args.openmvg is None and args.sfm_data is None: raise ValueError('at least one output path should be given.') if args.verbose <= logging.DEBUG: # for debug, let kapture express itself. kapture.utils.logging.getLogger().setLevel(args.verbose) logger.debug(f'{sys.argv[0]} \\\n' + ' \\\n'.join('--{:20} {:100}'.format(k, str(v)) for k, v in vars(args).items())) # auto complete using args.openmvg if args.openmvg and args.sfm_data is None: args.sfm_data = path.join(args.openmvg, 'sfm_data.json') logger.debug(f'guessing output sfm_data is {args.sfm_data}') if args.openmvg and args.images is None: args.images = path.join(args.openmvg, 'images') logger.debug(f'guessing output images is {args.images}') if args.openmvg and args.regions is None: args.regions = path.join(args.openmvg, 'matches') logger.debug(f'guessing output regions is {args.regions}') if args.openmvg and args.matches is None: args.matches = path.join(args.openmvg, 'matches', 'matches.txt') logger.debug(f'guessing output matches is {args.matches}') # no image dir == sip transfer if args.images is None: args.image_action = TransferAction.skip if args.image_action == TransferAction.skip: args.images = None if not args.image_action == TransferAction.skip: if args.images is None: raise ValueError( f'You must specify an image root path for this transfer ({args.image_action})' ) export_openmvg(kapture_path=args.kapture, openmvg_sfm_data_file_path=args.sfm_data, openmvg_image_root_path=args.images, openmvg_regions_dir_path=args.regions, openmvg_matches_file_path=args.matches, image_action=args.image_action, image_path_flatten=args.image_path_flatten, force=args.force)