kopia lustrzana https://github.com/OpenDroneMap/ODM
rodzic
e6e62aae36
commit
3b4bcb09e4
|
@ -0,0 +1,72 @@
|
||||||
|
import os, json
|
||||||
|
from opendm import log
|
||||||
|
|
||||||
|
def get_cameras_from_opensfm(reconstruction_file):
|
||||||
|
"""
|
||||||
|
Extract the cameras from OpenSfM's reconstruction.json
|
||||||
|
"""
|
||||||
|
if os.path.exists(reconstruction_file):
|
||||||
|
with open(reconstruction_file, 'r') as fin:
|
||||||
|
reconstructions = json.loads(fin.read())
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for recon in reconstructions:
|
||||||
|
if 'cameras' in recon:
|
||||||
|
for camera_id in recon['cameras']:
|
||||||
|
# Strip "v2" from OpenSfM camera IDs
|
||||||
|
new_camera_id = camera_id
|
||||||
|
if new_camera_id.startswith("v2 "):
|
||||||
|
new_camera_id = new_camera_id[3:]
|
||||||
|
|
||||||
|
result[new_camera_id] = recon['cameras'][camera_id]
|
||||||
|
|
||||||
|
# Remove "_prior" keys
|
||||||
|
keys = list(result[new_camera_id].keys())
|
||||||
|
for k in keys:
|
||||||
|
if k.endswith('_prior'):
|
||||||
|
result[new_camera_id].pop(k)
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
raise RuntimeError("%s does not exist." % reconstruction_file)
|
||||||
|
|
||||||
|
|
||||||
|
def get_opensfm_camera_models(cameras):
|
||||||
|
"""
|
||||||
|
Convert cameras to a format OpenSfM can understand
|
||||||
|
(opposite of get_cameras_from_opensfm)
|
||||||
|
"""
|
||||||
|
if isinstance(cameras, dict):
|
||||||
|
result = {}
|
||||||
|
for camera_id in cameras:
|
||||||
|
# Quick check on IDs
|
||||||
|
if len(camera_id.split(" ")) < 6:
|
||||||
|
raise RuntimeError("Invalid cameraID: %s" % camera_id)
|
||||||
|
|
||||||
|
# Add "v2" to camera ID
|
||||||
|
if not camera_id.startswith("v2 "):
|
||||||
|
osfm_camera_id = "v2 " + camera_id
|
||||||
|
else:
|
||||||
|
osfm_camera_id = camera_id
|
||||||
|
|
||||||
|
# Add "_prior" keys
|
||||||
|
camera = cameras[camera_id]
|
||||||
|
prior_fields = ["focal","focal_x","focal_y","c_x","c_y","k1","k2","p1","p2","k3"]
|
||||||
|
valid_fields = ["id","width","height","projection_type"] + prior_fields + [f + "_prior" for f in prior_fields]
|
||||||
|
|
||||||
|
keys = list(camera.keys())
|
||||||
|
for param in keys:
|
||||||
|
param_prior = param + "_prior"
|
||||||
|
if param in prior_fields and not param_prior in camera:
|
||||||
|
camera[param_prior] = camera[param]
|
||||||
|
|
||||||
|
# Remove invalid keys
|
||||||
|
keys = list(camera.keys())
|
||||||
|
for k in keys:
|
||||||
|
if not k in valid_fields:
|
||||||
|
camera.pop(k)
|
||||||
|
log.ODM_WARNING("Invalid camera key ignored: %s" % k)
|
||||||
|
|
||||||
|
result[osfm_camera_id] = camera
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Invalid cameras format: %s. Expected dict." % str(cameras))
|
|
@ -1,4 +1,5 @@
|
||||||
import argparse
|
import argparse
|
||||||
|
import json
|
||||||
from opendm import context
|
from opendm import context
|
||||||
from opendm import io
|
from opendm import io
|
||||||
from opendm import log
|
from opendm import log
|
||||||
|
@ -23,6 +24,25 @@ def alphanumeric_string(string):
|
||||||
raise argparse.ArgumentTypeError(msg)
|
raise argparse.ArgumentTypeError(msg)
|
||||||
return string
|
return string
|
||||||
|
|
||||||
|
def path_or_json_string(string):
|
||||||
|
if string == "":
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if string.startswith("[") or string.startswith("{"):
|
||||||
|
try:
|
||||||
|
return json.loads(string)
|
||||||
|
except:
|
||||||
|
raise argparse.ArgumentTypeError("{0} is not a valid JSON string.".format(string))
|
||||||
|
elif io.file_exists(string):
|
||||||
|
try:
|
||||||
|
with open(string, 'r') as f:
|
||||||
|
return json.loads(f.read())
|
||||||
|
except:
|
||||||
|
raise argparse.ArgumentTypeError("{0} is not a valid JSON file.".format(string))
|
||||||
|
else:
|
||||||
|
raise argparse.ArgumentTypeError("{0} is not a valid JSON file or string.".format(string))
|
||||||
|
|
||||||
|
|
||||||
# Django URL validation regex
|
# Django URL validation regex
|
||||||
def url_string(string):
|
def url_string(string):
|
||||||
import re
|
import re
|
||||||
|
@ -137,6 +157,16 @@ def config():
|
||||||
default=False,
|
default=False,
|
||||||
help='Turn off camera parameter optimization during bundler')
|
help='Turn off camera parameter optimization during bundler')
|
||||||
|
|
||||||
|
parser.add_argument('--cameras',
|
||||||
|
default='',
|
||||||
|
metavar='<string>',
|
||||||
|
type=path_or_json_string,
|
||||||
|
help='Use the camera parameters computed from '
|
||||||
|
'another dataset instead of calculating them. '
|
||||||
|
'Can be specified either as path to a cameras.json file or as a '
|
||||||
|
'JSON string representing the contents of a '
|
||||||
|
'cameras.json file. Default: %(default)s')
|
||||||
|
|
||||||
parser.add_argument('--max-concurrency',
|
parser.add_argument('--max-concurrency',
|
||||||
metavar='<positive integer>',
|
metavar='<positive integer>',
|
||||||
default=context.num_cores,
|
default=context.num_cores,
|
||||||
|
@ -556,6 +586,8 @@ def config():
|
||||||
'%(default)s'))
|
'%(default)s'))
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
print(args.cameras)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
# check that the project path setting has been set properly
|
# check that the project path setting has been set properly
|
||||||
if not args.project_path:
|
if not args.project_path:
|
||||||
|
|
|
@ -8,6 +8,7 @@ from opendm import io
|
||||||
from opendm import log
|
from opendm import log
|
||||||
from opendm import system
|
from opendm import system
|
||||||
from opendm import context
|
from opendm import context
|
||||||
|
from opendm import camera
|
||||||
from opensfm.large import metadataset
|
from opensfm.large import metadataset
|
||||||
from opensfm.large import tools
|
from opensfm.large import tools
|
||||||
|
|
||||||
|
@ -85,13 +86,14 @@ class OSFMContext:
|
||||||
log.ODM_DEBUG("Copied image_groups.txt to OpenSfM directory")
|
log.ODM_DEBUG("Copied image_groups.txt to OpenSfM directory")
|
||||||
io.copy(image_groups_file, os.path.join(self.opensfm_project_path, "image_groups.txt"))
|
io.copy(image_groups_file, os.path.join(self.opensfm_project_path, "image_groups.txt"))
|
||||||
|
|
||||||
# check for cameras.json
|
# check for cameras
|
||||||
# TODO: use config.cameras
|
if args.cameras:
|
||||||
camera_models_file = os.path.join(args.project_path, "cameras.json")
|
try:
|
||||||
has_camera_calibration = io.file_exists(camera_models_file)
|
with open(os.path.join(self.opensfm_project_path, "camera_models_overrides.json"), 'r') as f:
|
||||||
if has_camera_calibration:
|
f.write(json.dumps(camera.get_opensfm_camera_models(args.cameras)))
|
||||||
log.ODM_DEBUG("Copied cameras.json to OpenSfM directory (camera_models_overrides.json)")
|
log.ODM_DEBUG("Wrote camera_models_overrides.json to OpenSfM directory (camera_models_overrides.json)")
|
||||||
io.copy(camera_models_file, os.path.join(self.opensfm_project_path, "camera_models_overrides.json"))
|
except Exception as e:
|
||||||
|
log.ODM_WARNING("Cannot set camera_models_overrides.json: %s" % str(e))
|
||||||
|
|
||||||
# create config file for OpenSfM
|
# create config file for OpenSfM
|
||||||
config = [
|
config = [
|
||||||
|
@ -196,30 +198,11 @@ class OSFMContext:
|
||||||
def extract_cameras(self, output, rerun=False):
|
def extract_cameras(self, output, rerun=False):
|
||||||
reconstruction_file = self.path("reconstruction.json")
|
reconstruction_file = self.path("reconstruction.json")
|
||||||
if not os.path.exists(output) or rerun:
|
if not os.path.exists(output) or rerun:
|
||||||
if os.path.exists(reconstruction_file):
|
try:
|
||||||
result = {}
|
|
||||||
with open(reconstruction_file, 'r') as fin:
|
|
||||||
json_f = json.loads(fin.read())
|
|
||||||
for recon in json_f:
|
|
||||||
if 'cameras' in recon:
|
|
||||||
for camera_id in recon['cameras']:
|
|
||||||
# Strip "v2" from OpenSfM camera IDs
|
|
||||||
new_camera_id = camera_id
|
|
||||||
if new_camera_id.startswith("v2 "):
|
|
||||||
new_camera_id = new_camera_id[3:]
|
|
||||||
|
|
||||||
result[new_camera_id] = recon['cameras'][camera_id]
|
|
||||||
|
|
||||||
# Remove "_prior" keys
|
|
||||||
keys = list(result[new_camera_id].keys())
|
|
||||||
for k in keys:
|
|
||||||
if k.endswith('_prior'):
|
|
||||||
result[new_camera_id].pop(k)
|
|
||||||
|
|
||||||
with open(output, 'w') as fout:
|
with open(output, 'w') as fout:
|
||||||
fout.write(json.dumps(result))
|
fout.write(json.dumps(camera.get_cameras_from_opensfm(reconstruction_file)))
|
||||||
else:
|
except Exception as e:
|
||||||
log.ODM_WARNING("Cannot export cameras to %s. reconstruction.json does not exist." % output)
|
log.ODM_WARNING("Cannot export cameras to %s. %s." % (output, str(e)))
|
||||||
else:
|
else:
|
||||||
log.ODM_INFO("Already extracted cameras")
|
log.ODM_INFO("Already extracted cameras")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"reference_lla": {
|
||||||
|
"latitude": 46.84265666666667,
|
||||||
|
"altitude": 0,
|
||||||
|
"longitude": -91.99400802469134
|
||||||
|
},
|
||||||
|
"cameras": {
|
||||||
|
"v2 dji fc300s 4000 2250 perspective 0.5555": {
|
||||||
|
"focal_prior": 0.5555555555555556,
|
||||||
|
"width": 4000,
|
||||||
|
"k1": 0.0052219633739416784,
|
||||||
|
"k2": 0.015202301516716735,
|
||||||
|
"k1_prior": 0.0,
|
||||||
|
"k2_prior": 0.0,
|
||||||
|
"projection_type": "perspective",
|
||||||
|
"focal": 0.5614307793062748,
|
||||||
|
"height": 2250
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shots": {}
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,41 @@
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from opendm import camera
|
||||||
|
|
||||||
|
|
||||||
|
class TestCamera(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
if os.path.exists("tests/assets/output"):
|
||||||
|
shutil.rmtree("tests/assets/output")
|
||||||
|
os.makedirs("tests/assets/output")
|
||||||
|
|
||||||
|
def test_camera(self):
|
||||||
|
c = camera.get_cameras_from_opensfm("tests/assets/reconstruction.json")
|
||||||
|
self.assertEqual(len(c.keys()), 1)
|
||||||
|
camera_id = c.keys()[0]
|
||||||
|
self.assertTrue('v2 ' not in camera_id)
|
||||||
|
|
||||||
|
self.assertRaises(RuntimeError, camera.get_cameras_from_opensfm, 'tests/assets/nonexistant.json')
|
||||||
|
self.assertRaises(ValueError, camera.get_cameras_from_opensfm, 'tests/assets/gcp_extras.txt')
|
||||||
|
self.assertFalse('k1_prior' in c[camera_id])
|
||||||
|
|
||||||
|
# Add bogus field
|
||||||
|
c[camera_id]['test'] = 0
|
||||||
|
|
||||||
|
osfm_c = camera.get_opensfm_camera_models(c)
|
||||||
|
self.assertEqual(len(osfm_c.keys()), 1)
|
||||||
|
c1 = osfm_c[osfm_c.keys()[0]]
|
||||||
|
self.assertTrue('k1_prior' in c1)
|
||||||
|
self.assertTrue('k2_prior' in c1)
|
||||||
|
self.assertFalse('test' in c1)
|
||||||
|
self.assertEqual(c1['k1'], c1['k1_prior'])
|
||||||
|
self.assertEqual(c1['k2'], c1['k2_prior'])
|
||||||
|
self.assertEqual(c1['focal'], c1['focal_prior'])
|
||||||
|
self.assertTrue('width_prior' not in c1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Ładowanie…
Reference in New Issue