Ejemplo n.º 1
0
    def __init__(self, nodeUrl, rolling_shutter=False, rerun=False):
        self.node = Node.from_url(nodeUrl)
        self.params = {
            'tasks': [],
            'threads': [],
            'rolling_shutter': rolling_shutter,
            'rerun': rerun
        }
        self.node_online = True

        log.ODM_INFO("LRE: Initializing using cluster node %s:%s" %
                     (self.node.host, self.node.port))
        try:
            info = self.node.info()
            log.ODM_INFO("LRE: Node is online and running %s version %s" %
                         (info.engine, info.engine_version))
        except exceptions.NodeConnectionError:
            log.ODM_WARNING(
                "LRE: The node seems to be offline! We'll still process the dataset, but it's going to run entirely locally."
            )
            self.node_online = False
        except Exception as e:
            raise system.ExitException(
                "LRE: An unexpected problem happened while opening the node connection: %s"
                % str(e))
Ejemplo n.º 2
0
def start_mapping():
    global kondisi
    kondisi = 'start'
    if kondisi == 'start':
        node = Node("localhost", 3000)
        try:
            # Start a task
            print("Uploading images...")
            task = node.create_task(open_file, {
                'dsm': True,
                'orthophoto-resolution': 3
            })
            print(task.info())

            try:
                # This will block until the task is finished
                # or will raise an exception
                task.wait_for_completion()

                print("Task completed, downloading results...")

                # Retrieve results
                task.download_assets("results")

                print("Assets saved in ./results (%s)" % os.listdir("results"))

                # Restart task and this time compute dtm
                task.restart({'dtm': True})
                task.wait_for_completion()

                print("Task completed, downloading results...")

                task.download_assets("./results_with_dtm")

                print("Assets saved in ./results_with_dtm (%s)" %
                      os.listdir("./results_with_dtm"))
            except exceptions.TaskFailedError as e:
                print("\n".join(task.output()))

        except exceptions.NodeConnectionError as e:
            print("Cannot connect: %s" % e)
        except exceptions.NodeResponseError as e:
            print("Error: %s" % e)
Ejemplo n.º 3
0
    def __init__(self, nodeUrl):
        self.node = Node.from_url(nodeUrl)
        self.params = {
            'tasks': [],
            'threads': []
        }
        self.node_online = True

        log.ODM_INFO("LRE: Initializing using cluster node %s:%s" % (self.node.host, self.node.port))
        try:
            odm_version = self.node.info().odm_version
            log.ODM_INFO("LRE: Node is online and running ODM version: %s"  % odm_version)
        except exceptions.NodeConnectionError:
            log.ODM_WARNING("LRE: The node seems to be offline! We'll still process the dataset, but it's going to run entirely locally.")
            self.node_online = False
        except Exception as e:
            log.ODM_ERROR("LRE: An unexpected problem happened while opening the node connection: %s" % str(e))
            exit(1)
Ejemplo n.º 4
0
 def setUp(self):
     self.api_client = Node("localhost", 11223)
Ejemplo n.º 5
0
    def test_token_auth(self):
        def wait_for_status(api,
                            uuid,
                            status,
                            num_retries=10,
                            error_description="Failed to wait for status"):
            retries = 0
            while True:
                try:
                    task_info = api.get_task(uuid).info()
                    if task_info.status.value == status:
                        return True
                except (NodeResponseError, NodeServerError):
                    pass

                time.sleep(0.5)
                retries += 1
                if retries >= num_retries:
                    self.assertTrue(False, error_description)
                    return False

        with start_processing_node(['--token', 'test_token']):
            api = Node("localhost", 11223, "test_token")
            online_node = ProcessingNode.objects.get(pk=3)

            self.assertTrue(online_node.update_node_info(),
                            "Could update info")

            # Cannot call info(), options()  without tokens
            api.token = "invalid"
            self.assertRaises(NodeResponseError, api.info)
            self.assertRaises(NodeResponseError, api.options)

            # Cannot call create_task() without token
            import glob
            self.assertRaises(NodeResponseError, api.create_task,
                              glob.glob("nodeodm/fixtures/test_images/*.JPG"))

            # Can call create_task() with token
            api.token = "test_token"
            res = api.create_task(
                glob.glob("nodeodm/fixtures/test_images/*.JPG"))
            uuid = res.uuid
            self.assertTrue(uuid != None)

            # Can call task_info() with token
            task_info = api.get_task(uuid).info()
            self.assertTrue(isinstance(task_info.date_created, datetime))

            # Cannot call task_info() without token
            api.token = "invalid"
            try:
                api.get_task(uuid).info()
            except NodeResponseError as e:
                self.assertTrue('token does not match' in str(e))

            # Here we are waiting for the task to be completed
            api.token = "test_token"
            wait_for_status(api, uuid, status_codes.COMPLETED, 10,
                            "Could not download assets")

            # Cannot download assets without token
            api.token = "invalid"
            task = api.get_task(uuid)
            self.assertRaises(NodeResponseError, task.download_assets,
                              settings.MEDIA_TMP)

            api.token = "test_token"
            asset_archive = task.download_zip(settings.MEDIA_TMP)
            self.assertTrue(os.path.exists(asset_archive))
            os.unlink(asset_archive)

            # Cannot get task output without token
            api.token = "invalid"
            self.assertRaises(NodeResponseError, task.output, 0)

            api.token = "test_token"
            res = task.output()
            self.assertTrue(isinstance(res, list))

            # Cannot restart task without token
            online_node.token = "invalid"
            self.assertRaises(NodeResponseError, online_node.restart_task,
                              uuid)

            online_node.token = "test_token"
            self.assertTrue(online_node.restart_task(uuid))

            # Cannot cancel task without token
            online_node.token = "invalid"
            self.assertRaises(NodeResponseError, online_node.cancel_task, uuid)
            online_node.token = "test_token"
            self.assertTrue(online_node.cancel_task(uuid))

            # Wait for task to be canceled
            wait_for_status(api, uuid, status_codes.CANCELED, 5,
                            "Could not cancel task")

            # Cannot delete task without token
            online_node.token = "invalid"
            self.assertRaises(NodeResponseError, online_node.remove_task,
                              "invalid token")
            online_node.token = "test_token"
            self.assertTrue(online_node.remove_task(uuid))
Ejemplo n.º 6
0
class TestClientApi(TestCase):
    fixtures = [
        'test_processingnodes',
    ]

    @classmethod
    def setUpClass(cls):
        super(TestClientApi, cls).setUpClass()

    @classmethod
    def tearDownClass(cls):
        super(TestClientApi, cls).tearDownClass()

    def setUp(self):
        self.api_client = Node("localhost", 11223)

    def tearDown(self):
        pass

    def test_offline_api(self):
        api = Node("offline-host", 3000)
        self.assertRaises(NodeConnectionError, api.info)
        self.assertRaises(NodeConnectionError, api.options)

    def test_info(self):
        with start_processing_node():
            info = self.api_client.info()
            self.assertTrue(isinstance(info.version, six.string_types),
                            "Found version string")
            self.assertTrue(isinstance(info.task_queue_count, int),
                            "Found task queue count")
            self.assertTrue(info.max_images is None, "Found task max images")

    def test_options(self):
        with start_processing_node():
            options = self.api_client.options()
            self.assertTrue(len(options) > 0, "Found options")

    def test_online_processing_node(self):
        with start_processing_node():
            online_node = ProcessingNode.objects.get(pk=1)
            self.assertTrue(
                str(online_node) == "localhost:11223",
                "Formatting string works")
            self.assertTrue(online_node.last_refreshed == None,
                            "Last refreshed not yet set")
            self.assertTrue(
                len(online_node.available_options) == 0,
                "Available options not yet set")
            self.assertTrue(online_node.api_version == "",
                            "API version is not set")

            self.assertTrue(online_node.update_node_info(),
                            "Could update info")
            self.assertTrue(online_node.last_refreshed is not None,
                            "Last refreshed is set")
            self.assertTrue(
                len(online_node.available_options) > 0,
                "Available options are set")
            self.assertTrue(online_node.api_version != "",
                            "API version is set")
            self.assertTrue(online_node.max_images is None,
                            "No max images limit is set")

            self.assertTrue(
                isinstance(online_node.get_available_options_json(),
                           six.string_types), "Available options json works")
            self.assertTrue(
                isinstance(online_node.get_available_options_json(pretty=True),
                           six.string_types),
                "Available options json works with pretty")

    def test_offline_processing_node(self):
        offline_node = ProcessingNode.objects.get(pk=2)
        self.assertFalse(offline_node.update_node_info(),
                         "Could not update info (offline)")
        self.assertTrue(offline_node.api_version == "",
                        "API version is not set")

    def test_auto_update_node_info(self):
        with start_processing_node():
            online_node = ProcessingNode.objects.create(hostname="localhost",
                                                        port=11223)
            self.assertTrue(
                online_node.last_refreshed != None,
                "Last refreshed info is here (update_node_info() was called)")

    def test_client_api_and_task_methods(self):
        def wait_for_status(api,
                            uuid,
                            status,
                            num_retries=10,
                            error_description="Failed to wait for status"):
            retries = 0
            while True:
                try:
                    task_info = api.get_task(uuid).info()
                    if task_info.status.value == status:
                        return True
                except (NodeServerError, NodeResponseError):
                    pass

                time.sleep(0.5)
                retries += 1
                if retries >= num_retries:
                    self.assertTrue(False, error_description)
                    return False

        with start_processing_node():
            api = Node("localhost", 11223)
            online_node = ProcessingNode.objects.get(pk=1)

            # Can call info(), options()
            self.assertTrue(type(api.info().version) == str)
            self.assertTrue(len(api.options()) > 0)

            # Can call new_task()
            import glob
            res = api.create_task(
                glob.glob("nodeodm/fixtures/test_images/*.JPG"),
                {'force-ccd': 6.16}, "test")
            uuid = res.uuid
            self.assertTrue(uuid != None)

            # Can call task_info()
            task = api.get_task(uuid)
            task_info = task.info()
            self.assertTrue(isinstance(task_info.date_created, datetime))
            self.assertTrue(isinstance(task_info.uuid, str))

            # Can download assets?
            # Here we are waiting for the task to be completed
            wait_for_status(api, uuid, status_codes.COMPLETED, 10,
                            "Could not download assets")
            asset = api.get_task(uuid).download_zip(settings.MEDIA_TMP)
            self.assertTrue(os.path.exists(asset))

            # task_output
            self.assertTrue(isinstance(api.get_task(uuid).output(0), list))
            self.assertTrue(
                isinstance(online_node.get_task_console_output(uuid, 0), list))

            self.assertRaises(NodeResponseError,
                              online_node.get_task_console_output,
                              "wrong-uuid", 0)

            # Can restart task
            self.assertTrue(online_node.restart_task(uuid))
            self.assertRaises(NodeResponseError, online_node.restart_task,
                              "wrong-uuid")

            wait_for_status(api, uuid, status_codes.COMPLETED, 10,
                            "Could not restart task")

            # Can restart task by passing options
            self.assertTrue(
                online_node.restart_task(uuid, [{
                    'name': 'mesh-size',
                    'value': 12345
                }, {
                    'name': 'invalid',
                    'value': True
                }]))
            wait_for_status(api, uuid, status_codes.COMPLETED, 10,
                            "Could not restart task with options")

            # Verify that options have been updated after restarting the task
            task_info = api.get_task(uuid).info()
            self.assertTrue(len(
                task_info.options) == 2)  # pc-ept has been added
            self.assertTrue(task_info.options[0]['name'] == 'mesh-size')
            self.assertTrue(task_info.options[0]['value'] == 12345)

            # Can cancel task (should work even if we completed the task)
            self.assertTrue(online_node.cancel_task(uuid))
            self.assertRaises(NodeResponseError, online_node.cancel_task,
                              "wrong-uuid")

            # Wait for task to be canceled
            wait_for_status(api, uuid, status_codes.CANCELED, 5,
                            "Could not remove task")
            self.assertTrue(online_node.remove_task(uuid))
            self.assertRaises(NodeResponseError, online_node.remove_task,
                              "wrong-uuid")

            # Cannot delete task again
            self.assertRaises(NodeResponseError, online_node.remove_task, uuid)

            # Task has been deleted
            self.assertRaises(NodeResponseError, online_node.get_task_info,
                              uuid)

            # Test URL building for HTTPS
            sslApi = Node("localhost", 443, 'abc')
            self.assertEqual(sslApi.url('/info'),
                             'https://localhost/info?token=abc')

    def test_find_best_available_node_and_is_online(self):
        with start_processing_node():
            # Fixtures are all offline
            self.assertTrue(ProcessingNode.find_best_available_node() is None)

            # Bring one online
            pnode = ProcessingNode.objects.get(pk=1)
            self.assertFalse(pnode.is_online())

            pnode.last_refreshed = timezone.now()
            pnode.queue_count = 2
            pnode.save()

            self.assertTrue(pnode.is_online())
            self.assertTrue(
                ProcessingNode.find_best_available_node().id == pnode.id)

            # Bring another online with lower queue count
            another_pnode = ProcessingNode.objects.get(pk=2)
            another_pnode.last_refreshed = pnode.last_refreshed
            another_pnode.queue_count = 1
            another_pnode.save()

            self.assertTrue(ProcessingNode.find_best_available_node().id ==
                            another_pnode.id)

            # Bring it offline
            another_pnode.last_refreshed -= timedelta(
                minutes=settings.NODE_OFFLINE_MINUTES)
            another_pnode.save()
            self.assertFalse(another_pnode.is_online())

            # Best choice now is original processing node
            self.assertTrue(
                ProcessingNode.find_best_available_node().id == pnode.id)

    def test_token_auth(self):
        def wait_for_status(api,
                            uuid,
                            status,
                            num_retries=10,
                            error_description="Failed to wait for status"):
            retries = 0
            while True:
                try:
                    task_info = api.get_task(uuid).info()
                    if task_info.status.value == status:
                        return True
                except (NodeResponseError, NodeServerError):
                    pass

                time.sleep(0.5)
                retries += 1
                if retries >= num_retries:
                    self.assertTrue(False, error_description)
                    return False

        with start_processing_node(['--token', 'test_token']):
            api = Node("localhost", 11223, "test_token")
            online_node = ProcessingNode.objects.get(pk=3)

            self.assertTrue(online_node.update_node_info(),
                            "Could update info")

            # Cannot call info(), options()  without tokens
            api.token = "invalid"
            self.assertRaises(NodeResponseError, api.info)
            self.assertRaises(NodeResponseError, api.options)

            # Cannot call create_task() without token
            import glob
            self.assertRaises(NodeResponseError, api.create_task,
                              glob.glob("nodeodm/fixtures/test_images/*.JPG"))

            # Can call create_task() with token
            api.token = "test_token"
            res = api.create_task(
                glob.glob("nodeodm/fixtures/test_images/*.JPG"))
            uuid = res.uuid
            self.assertTrue(uuid != None)

            # Can call task_info() with token
            task_info = api.get_task(uuid).info()
            self.assertTrue(isinstance(task_info.date_created, datetime))

            # Cannot call task_info() without token
            api.token = "invalid"
            try:
                api.get_task(uuid).info()
            except NodeResponseError as e:
                self.assertTrue('token does not match' in str(e))

            # Here we are waiting for the task to be completed
            api.token = "test_token"
            wait_for_status(api, uuid, status_codes.COMPLETED, 10,
                            "Could not download assets")

            # Cannot download assets without token
            api.token = "invalid"
            task = api.get_task(uuid)
            self.assertRaises(NodeResponseError, task.download_assets,
                              settings.MEDIA_TMP)

            api.token = "test_token"
            asset_archive = task.download_zip(settings.MEDIA_TMP)
            self.assertTrue(os.path.exists(asset_archive))
            os.unlink(asset_archive)

            # Cannot get task output without token
            api.token = "invalid"
            self.assertRaises(NodeResponseError, task.output, 0)

            api.token = "test_token"
            res = task.output()
            self.assertTrue(isinstance(res, list))

            # Cannot restart task without token
            online_node.token = "invalid"
            self.assertRaises(NodeResponseError, online_node.restart_task,
                              uuid)

            online_node.token = "test_token"
            self.assertTrue(online_node.restart_task(uuid))

            # Cannot cancel task without token
            online_node.token = "invalid"
            self.assertRaises(NodeResponseError, online_node.cancel_task, uuid)
            online_node.token = "test_token"
            self.assertTrue(online_node.cancel_task(uuid))

            # Wait for task to be canceled
            wait_for_status(api, uuid, status_codes.CANCELED, 5,
                            "Could not cancel task")

            # Cannot delete task without token
            online_node.token = "invalid"
            self.assertRaises(NodeResponseError, online_node.remove_task,
                              "invalid token")
            online_node.token = "test_token"
            self.assertTrue(online_node.remove_task(uuid))
Ejemplo n.º 7
0
    def test_client_api_and_task_methods(self):
        def wait_for_status(api,
                            uuid,
                            status,
                            num_retries=10,
                            error_description="Failed to wait for status"):
            retries = 0
            while True:
                try:
                    task_info = api.get_task(uuid).info()
                    if task_info.status.value == status:
                        return True
                except (NodeServerError, NodeResponseError):
                    pass

                time.sleep(0.5)
                retries += 1
                if retries >= num_retries:
                    self.assertTrue(False, error_description)
                    return False

        with start_processing_node():
            api = Node("localhost", 11223)
            online_node = ProcessingNode.objects.get(pk=1)

            # Can call info(), options()
            self.assertTrue(type(api.info().version) == str)
            self.assertTrue(len(api.options()) > 0)

            # Can call new_task()
            import glob
            res = api.create_task(
                glob.glob("nodeodm/fixtures/test_images/*.JPG"),
                {'force-ccd': 6.16}, "test")
            uuid = res.uuid
            self.assertTrue(uuid != None)

            # Can call task_info()
            task = api.get_task(uuid)
            task_info = task.info()
            self.assertTrue(isinstance(task_info.date_created, datetime))
            self.assertTrue(isinstance(task_info.uuid, str))

            # Can download assets?
            # Here we are waiting for the task to be completed
            wait_for_status(api, uuid, status_codes.COMPLETED, 10,
                            "Could not download assets")
            asset = api.get_task(uuid).download_zip(settings.MEDIA_TMP)
            self.assertTrue(os.path.exists(asset))

            # task_output
            self.assertTrue(isinstance(api.get_task(uuid).output(0), list))
            self.assertTrue(
                isinstance(online_node.get_task_console_output(uuid, 0), list))

            self.assertRaises(NodeResponseError,
                              online_node.get_task_console_output,
                              "wrong-uuid", 0)

            # Can restart task
            self.assertTrue(online_node.restart_task(uuid))
            self.assertRaises(NodeResponseError, online_node.restart_task,
                              "wrong-uuid")

            wait_for_status(api, uuid, status_codes.COMPLETED, 10,
                            "Could not restart task")

            # Can restart task by passing options
            self.assertTrue(
                online_node.restart_task(uuid, [{
                    'name': 'mesh-size',
                    'value': 12345
                }, {
                    'name': 'invalid',
                    'value': True
                }]))
            wait_for_status(api, uuid, status_codes.COMPLETED, 10,
                            "Could not restart task with options")

            # Verify that options have been updated after restarting the task
            task_info = api.get_task(uuid).info()
            self.assertTrue(len(
                task_info.options) == 2)  # pc-ept has been added
            self.assertTrue(task_info.options[0]['name'] == 'mesh-size')
            self.assertTrue(task_info.options[0]['value'] == 12345)

            # Can cancel task (should work even if we completed the task)
            self.assertTrue(online_node.cancel_task(uuid))
            self.assertRaises(NodeResponseError, online_node.cancel_task,
                              "wrong-uuid")

            # Wait for task to be canceled
            wait_for_status(api, uuid, status_codes.CANCELED, 5,
                            "Could not remove task")
            self.assertTrue(online_node.remove_task(uuid))
            self.assertRaises(NodeResponseError, online_node.remove_task,
                              "wrong-uuid")

            # Cannot delete task again
            self.assertRaises(NodeResponseError, online_node.remove_task, uuid)

            # Task has been deleted
            self.assertRaises(NodeResponseError, online_node.get_task_info,
                              uuid)

            # Test URL building for HTTPS
            sslApi = Node("localhost", 443, 'abc')
            self.assertEqual(sslApi.url('/info'),
                             'https://localhost/info?token=abc')
Ejemplo n.º 8
0
 def api_client(self, timeout=30):
     return Node(self.hostname, self.port, self.token, timeout)
Ejemplo n.º 9
0
import os
import shutil
import sys

from pyodm import Node
import cv2

from pure_object_recog.pure_object import Por
from pure_object_recog.parameter import Popr

por = Por()
# Collect photo data
# TODO: make sure cam_list is up to date
cam_list = [0, 1, 2, 3]
por.camera_input(cam_list)

# Create node connection
nd = Node("localhost", 3000)
por.create_mesh(nd)
Ejemplo n.º 10
0
def config(argv=None, parser=None):
    global args

    if args is not None and argv is None:
        return args

    if sys.platform == 'win32':
        usage_bin = 'run'
    else:
        usage_bin = 'run.sh'

    if parser is None:
        parser = SettingsParser(
            description=
            'ODM is a command line toolkit to generate maps, point clouds, 3D models and DEMs from drone, balloon or kite images.',
            usage='%s [options] <dataset name>' % usage_bin,
            yaml_file=open(context.settings_path))

    parser.add_argument(
        '--project-path',
        metavar='<path>',
        action=StoreValue,
        help=
        'Path to the project folder. Your project folder should contain subfolders for each dataset. Each dataset should have an "images" folder.'
    )
    parser.add_argument(
        'name',
        metavar='<dataset name>',
        action=StoreValue,
        type=str,
        default='code',
        nargs='?',
        help=
        'Name of dataset (i.e subfolder name within project folder). Default: %(default)s'
    )

    parser.add_argument(
        '--resize-to',
        metavar='<integer>',
        action=StoreValue,
        default=2048,
        type=int,
        help=
        'Legacy option (use --feature-quality instead). Resizes images by the largest side for feature extraction purposes only. '
        'Set to -1 to disable. This does not affect the final orthophoto '
        'resolution quality and will not resize the original images. Default: %(default)s'
    )

    parser.add_argument(
        '--end-with',
        '-e',
        metavar='<string>',
        action=StoreValue,
        default='odm_postprocess',
        choices=processopts,
        help=
        'End processing at this stage. Can be one of: %(choices)s. Default: %(default)s'
    )

    rerun = parser.add_mutually_exclusive_group()

    rerun.add_argument(
        '--rerun',
        '-r',
        metavar='<string>',
        action=StoreValue,
        choices=processopts,
        help=
        ('Rerun this stage only and stop. Can be one of: %(choices)s. Default: %(default)s'
         ))

    rerun.add_argument(
        '--rerun-all',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Permanently delete all previous results and rerun the processing pipeline.'
    )

    rerun.add_argument(
        '--rerun-from',
        action=RerunFrom,
        metavar='<string>',
        choices=processopts,
        help=
        ('Rerun processing from this stage. Can be one of: %(choices)s. Default: %(default)s'
         ))

    parser.add_argument(
        '--min-num-features',
        metavar='<integer>',
        action=StoreValue,
        default=10000,
        type=int,
        help=
        ('Minimum number of features to extract per image. '
         'More features can be useful for finding more matches between images, '
         'potentially allowing the reconstruction of areas with little overlap or insufficient features. '
         'More features also slow down processing. Default: %(default)s'))

    parser.add_argument(
        '--feature-type',
        metavar='<string>',
        action=StoreValue,
        default='sift',
        choices=['akaze', 'hahog', 'orb', 'sift'],
        help=
        ('Choose the algorithm for extracting keypoints and computing descriptors. '
         'Can be one of: %(choices)s. Default: '
         '%(default)s'))

    parser.add_argument(
        '--feature-quality',
        metavar='<string>',
        action=StoreValue,
        default='high',
        choices=['ultra', 'high', 'medium', 'low', 'lowest'],
        help=
        ('Set feature extraction quality. Higher quality generates better features, but requires more memory and takes longer. '
         'Can be one of: %(choices)s. Default: '
         '%(default)s'))

    parser.add_argument(
        '--matcher-type',
        metavar='<string>',
        action=StoreValue,
        default='flann',
        choices=['bow', 'bruteforce', 'flann'],
        help=
        ('Matcher algorithm, Fast Library for Approximate Nearest Neighbors or Bag of Words. FLANN is slower, but more stable. BOW is faster, but can sometimes miss valid matches. BRUTEFORCE is very slow but robust.'
         'Can be one of: %(choices)s. Default: '
         '%(default)s'))

    parser.add_argument(
        '--matcher-neighbors',
        metavar='<positive integer>',
        action=StoreValue,
        default=0,
        type=int,
        help=
        'Perform image matching with the nearest images based on GPS exif data. Set to 0 to match by triangulation. Default: %(default)s'
    )

    parser.add_argument(
        '--use-fixed-camera-params',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Turn off camera parameter optimization during bundle adjustment. This can be sometimes useful for improving results that exhibit doming/bowling or when images are taken with a rolling shutter camera. Default: %(default)s'
    )

    parser.add_argument(
        '--cameras',
        default='',
        metavar='<json>',
        action=StoreValue,
        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(
        '--camera-lens',
        metavar='<string>',
        action=StoreValue,
        default='auto',
        choices=[
            'auto', 'perspective', 'brown', 'fisheye', 'spherical',
            'equirectangular', 'dual'
        ],
        help=
        ('Set a camera projection type. Manually setting a value '
         'can help improve geometric undistortion. By default the application '
         'tries to determine a lens type from the images metadata. Can be one of: %(choices)s. Default: '
         '%(default)s'))

    parser.add_argument(
        '--radiometric-calibration',
        metavar='<string>',
        action=StoreValue,
        default='none',
        choices=['none', 'camera', 'camera+sun'],
        help=
        ('Set the radiometric calibration to perform on images. '
         'When processing multispectral and thermal images you should set this option '
         'to obtain reflectance/temperature values (otherwise you will get digital number values). '
         '[camera] applies black level, vignetting, row gradient gain/exposure compensation (if appropriate EXIF tags are found) and computes absolute temperature values. '
         '[camera+sun] is experimental, applies all the corrections of [camera], plus compensates for spectral radiance registered via a downwelling light sensor (DLS) taking in consideration the angle of the sun. '
         'Can be one of: %(choices)s. Default: '
         '%(default)s'))

    parser.add_argument(
        '--max-concurrency',
        metavar='<positive integer>',
        action=StoreValue,
        default=context.num_cores,
        type=int,
        help=('The maximum number of processes to use in various '
              'processes. Peak memory requirement is ~1GB per '
              'thread and 2 megapixel image resolution. Default: %(default)s'))

    parser.add_argument(
        '--depthmap-resolution',
        metavar='<positive float>',
        action=StoreValue,
        type=float,
        default=640,
        help=
        ('Controls the density of the point cloud by setting the resolution of the depthmap images. Higher values take longer to compute '
         'but produce denser point clouds. Overrides the value calculated by --pc-quality.'
         'Default: %(default)s'))

    parser.add_argument(
        '--use-hybrid-bundle-adjustment',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Run local bundle adjustment for every image added to the reconstruction and a global '
        'adjustment every 100 images. Speeds up reconstruction for very large datasets. Default: %(default)s'
    )

    parser.add_argument(
        '--sfm-algorithm',
        metavar='<string>',
        action=StoreValue,
        default='incremental',
        choices=['incremental', 'triangulation', 'planar'],
        help=
        ('Choose the structure from motion algorithm. For aerial datasets, if camera GPS positions and angles are available, triangulation can generate better results. For planar scenes captured at fixed altitude with nadir-only images, planar can be much faster. '
         'Can be one of: %(choices)s. Default: '
         '%(default)s'))

    parser.add_argument(
        '--use-3dmesh',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Use a full 3D mesh to compute the orthophoto instead of a 2.5D mesh. This option is a bit faster and provides similar results in planar areas. Default: %(default)s'
    )

    parser.add_argument(
        '--skip-3dmodel',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Skip generation of a full 3D model. This can save time if you only need 2D results such as orthophotos and DEMs. Default: %(default)s'
    )

    parser.add_argument(
        '--skip-report',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Skip generation of PDF report. This can save time if you don\'t need a report. Default: %(default)s'
    )

    parser.add_argument(
        '--skip-orthophoto',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Skip generation of the orthophoto. This can save time if you only need 3D results or DEMs. Default: %(default)s'
    )

    parser.add_argument(
        '--ignore-gsd',
        action=StoreTrue,
        nargs=0,
        default=False,
        help='Ignore Ground Sampling Distance (GSD). GSD '
        'caps the maximum resolution of image outputs and '
        'resizes images when necessary, resulting in faster processing and '
        'lower memory usage. Since GSD is an estimate, sometimes ignoring it can result in slightly better image output quality. Default: %(default)s'
    )

    parser.add_argument(
        '--no-gpu',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Do not use GPU acceleration, even if it\'s available. Default: %(default)s'
    )

    parser.add_argument('--mesh-size',
                        metavar='<positive integer>',
                        action=StoreValue,
                        default=200000,
                        type=int,
                        help=('The maximum vertex count of the output mesh. '
                              'Default: %(default)s'))

    parser.add_argument('--mesh-octree-depth',
                        metavar='<integer: 1 <= x <= 14>',
                        action=StoreValue,
                        default=11,
                        type=int,
                        help=('Octree depth used in the mesh reconstruction, '
                              'increase to get more vertices, recommended '
                              'values are 8-12. Default: %(default)s'))

    parser.add_argument(
        '--fast-orthophoto',
        action=StoreTrue,
        nargs=0,
        default=False,
        help='Skips dense reconstruction and 3D model generation. '
        'It generates an orthophoto directly from the sparse reconstruction. '
        'If you just need an orthophoto and do not need a full 3D model, turn on this option. Default: %(default)s'
    )

    parser.add_argument(
        '--crop',
        metavar='<positive float>',
        action=StoreValue,
        default=3,
        type=float,
        help=('Automatically crop image outputs by creating a smooth buffer '
              'around the dataset boundaries, shrunk by N meters. '
              'Use 0 to disable cropping. '
              'Default: %(default)s'))

    parser.add_argument(
        '--boundary',
        default='',
        metavar='<json>',
        action=StoreValue,
        type=path_or_json_string,
        help='GeoJSON polygon limiting the area of the reconstruction. '
        'Can be specified either as path to a GeoJSON file or as a '
        'JSON string representing the contents of a '
        'GeoJSON file. Default: %(default)s')

    parser.add_argument(
        '--auto-boundary',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Automatically set a boundary using camera shot locations to limit the area of the reconstruction. '
        'This can help remove far away background artifacts (sky, background landscapes, etc.). See also --boundary. '
        'Default: %(default)s')

    parser.add_argument(
        '--pc-quality',
        metavar='<string>',
        action=StoreValue,
        default='medium',
        choices=['ultra', 'high', 'medium', 'low', 'lowest'],
        help=
        ('Set point cloud quality. Higher quality generates better, denser point clouds, but requires more memory and takes longer. Each step up in quality increases processing time roughly by a factor of 4x.'
         'Can be one of: %(choices)s. Default: '
         '%(default)s'))

    parser.add_argument(
        '--pc-classify',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Classify the point cloud outputs using a Simple Morphological Filter. '
        'You can control the behavior of this option by tweaking the --dem-* parameters. '
        'Default: '
        '%(default)s')

    parser.add_argument(
        '--pc-csv',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Export the georeferenced point cloud in CSV format. Default: %(default)s'
    )

    parser.add_argument(
        '--pc-las',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Export the georeferenced point cloud in LAS format. Default: %(default)s'
    )

    parser.add_argument(
        '--pc-ept',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Export the georeferenced point cloud in Entwine Point Tile (EPT) format. Default: %(default)s'
    )

    parser.add_argument(
        '--pc-copc',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Save the georeferenced point cloud in Cloud Optimized Point Cloud (COPC) format. Default: %(default)s'
    )

    parser.add_argument(
        '--pc-filter',
        metavar='<positive float>',
        action=StoreValue,
        type=float,
        default=2.5,
        help=
        'Filters the point cloud by removing points that deviate more than N standard deviations from the local mean. Set to 0 to disable filtering. '
        'Default: %(default)s')

    parser.add_argument(
        '--pc-sample',
        metavar='<positive float>',
        action=StoreValue,
        type=float,
        default=0,
        help=
        'Filters the point cloud by keeping only a single point around a radius N (in meters). This can be useful to limit the output resolution of the point cloud and remove duplicate points. Set to 0 to disable sampling. '
        'Default: %(default)s')

    parser.add_argument(
        '--pc-tile',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Reduce the memory usage needed for depthmap fusion by splitting large scenes into tiles. Turn this on if your machine doesn\'t have much RAM and/or you\'ve set --pc-quality to high or ultra. Experimental. '
        'Default: %(default)s')

    parser.add_argument(
        '--pc-geometric',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Improve the accuracy of the point cloud by computing geometrically consistent depthmaps. This increases processing time, but can improve results in urban scenes. '
        'Default: %(default)s')

    parser.add_argument(
        '--smrf-scalar',
        metavar='<positive float>',
        action=StoreValue,
        type=float,
        default=1.25,
        help='Simple Morphological Filter elevation scalar parameter. '
        'Default: %(default)s')

    parser.add_argument(
        '--smrf-slope',
        metavar='<positive float>',
        action=StoreValue,
        type=float,
        default=0.15,
        help='Simple Morphological Filter slope parameter (rise over run). '
        'Default: %(default)s')

    parser.add_argument(
        '--smrf-threshold',
        metavar='<positive float>',
        action=StoreValue,
        type=float,
        default=0.5,
        help=
        'Simple Morphological Filter elevation threshold parameter (meters). '
        'Default: %(default)s')

    parser.add_argument(
        '--smrf-window',
        metavar='<positive float>',
        action=StoreValue,
        type=float,
        default=18.0,
        help='Simple Morphological Filter window radius parameter (meters). '
        'Default: %(default)s')

    parser.add_argument(
        '--texturing-data-term',
        metavar='<string>',
        action=StoreValue,
        default='gmi',
        choices=['gmi', 'area'],
        help=
        ('When texturing the 3D mesh, for each triangle, choose to prioritize images with sharp features (gmi) or those that cover the largest area (area). Default: %(default)s'
         ))

    parser.add_argument(
        '--texturing-outlier-removal-type',
        metavar='<string>',
        action=StoreValue,
        default='gauss_clamping',
        choices=['none', 'gauss_clamping', 'gauss_damping'],
        help=
        ('Type of photometric outlier removal method. Can be one of: %(choices)s. Default: %(default)s'
         ))

    parser.add_argument(
        '--texturing-skip-global-seam-leveling',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        ('Skip normalization of colors across all images. Useful when processing radiometric data. Default: %(default)s'
         ))

    parser.add_argument(
        '--texturing-skip-local-seam-leveling',
        action=StoreTrue,
        nargs=0,
        default=False,
        help='Skip the blending of colors near seams. Default: %(default)s')

    parser.add_argument(
        '--texturing-keep-unseen-faces',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=('Keep faces in the mesh that are not seen in any camera. '
              'Default:  %(default)s'))

    parser.add_argument('--texturing-tone-mapping',
                        metavar='<string>',
                        action=StoreValue,
                        choices=['none', 'gamma'],
                        default='none',
                        help='Turn on gamma tone mapping or none for no tone '
                        'mapping. Can be one of %(choices)s. '
                        'Default: %(default)s ')

    parser.add_argument(
        '--gcp',
        metavar='<path string>',
        action=StoreValue,
        default=None,
        help=
        ('Path to the file containing the ground control '
         'points used for georeferencing. '
         'The file needs to '
         'use the following format: \n'
         'EPSG:<code> or <+proj definition>\n'
         'geo_x geo_y geo_z im_x im_y image_name [gcp_name] [extra1] [extra2]\n'
         'Default: %(default)s'))

    parser.add_argument(
        '--geo',
        metavar='<path string>',
        action=StoreValue,
        default=None,
        help=
        ('Path to the image geolocation file containing the camera center coordinates used for georeferencing. '
         'Note that omega/phi/kappa are currently not supported (you can set them to 0). '
         'The file needs to '
         'use the following format: \n'
         'EPSG:<code> or <+proj definition>\n'
         'image_name geo_x geo_y geo_z [omega (degrees)] [phi (degrees)] [kappa (degrees)] [horz accuracy (meters)] [vert accuracy (meters)]\n'
         'Default: %(default)s'))

    parser.add_argument(
        '--use-exif',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        ('Use this tag if you have a GCP File but '
         'want to use the EXIF information for georeferencing instead. Default: %(default)s'
         ))

    parser.add_argument(
        '--dtm',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Use this tag to build a DTM (Digital Terrain Model, ground only) using a simple '
        'morphological filter. Check the --dem* and --smrf* parameters for finer tuning. Default: %(default)s'
    )

    parser.add_argument(
        '--dsm',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Use this tag to build a DSM (Digital Surface Model, ground + objects) using a progressive '
        'morphological filter. Check the --dem* parameters for finer tuning. Default: %(default)s'
    )

    parser.add_argument(
        '--dem-gapfill-steps',
        metavar='<positive integer>',
        action=StoreValue,
        default=3,
        type=int,
        help=
        'Number of steps used to fill areas with gaps. Set to 0 to disable gap filling. '
        'Starting with a radius equal to the output resolution, N different DEMs are generated with '
        'progressively bigger radius using the inverse distance weighted (IDW) algorithm '
        'and merged together. Remaining gaps are then merged using nearest neighbor interpolation. '
        'Default: %(default)s')

    parser.add_argument(
        '--dem-resolution',
        metavar='<float>',
        action=StoreValue,
        type=float,
        default=5,
        help=
        'DSM/DTM resolution in cm / pixel. Note that this value is capped to 2x the ground sampling distance (GSD) estimate. To remove the cap, check --ignore-gsd also.'
        ' Default: %(default)s')

    parser.add_argument(
        '--dem-decimation',
        metavar='<positive integer>',
        action=StoreValue,
        default=1,
        type=int,
        help=
        'Decimate the points before generating the DEM. 1 is no decimation (full quality). '
        '100 decimates ~99%% of the points. Useful for speeding up generation of DEM results in very large datasets. Default: %(default)s'
    )

    parser.add_argument(
        '--dem-euclidean-map',
        action=StoreTrue,
        nargs=0,
        default=False,
        help='Computes an euclidean raster map for each DEM. '
        'The map reports the distance from each cell to the nearest '
        'NODATA value (before any hole filling takes place). '
        'This can be useful to isolate the areas that have been filled. '
        'Default: '
        '%(default)s')

    parser.add_argument(
        '--orthophoto-resolution',
        metavar='<float > 0.0>',
        action=StoreValue,
        default=5,
        type=float,
        help=
        ('Orthophoto resolution in cm / pixel. Note that this value is capped by a ground sampling distance (GSD) estimate. To remove the cap, check --ignore-gsd also. '
         'Default: %(default)s'))

    parser.add_argument(
        '--orthophoto-no-tiled',
        action=StoreTrue,
        nargs=0,
        default=False,
        help='Set this parameter if you want a striped GeoTIFF. '
        'Default: %(default)s')

    parser.add_argument(
        '--orthophoto-png',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Set this parameter if you want to generate a PNG rendering of the orthophoto. '
        'Default: %(default)s')

    parser.add_argument(
        '--orthophoto-kmz',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Set this parameter if you want to generate a Google Earth (KMZ) rendering of the orthophoto. '
        'Default: %(default)s')

    parser.add_argument(
        '--orthophoto-compression',
        metavar='<string>',
        action=StoreValue,
        type=str,
        choices=['JPEG', 'LZW', 'PACKBITS', 'DEFLATE', 'LZMA', 'NONE'],
        default='DEFLATE',
        help=
        'Set the compression to use for orthophotos. Can be one of: %(choices)s. Default: %(default)s'
    )

    parser.add_argument(
        '--orthophoto-cutline',
        action=StoreTrue,
        nargs=0,
        default=False,
        help='Generates a polygon around the cropping area '
        'that cuts the orthophoto around the edges of features. This polygon '
        'can be useful for stitching seamless mosaics with multiple overlapping orthophotos. '
        'Default: '
        '%(default)s')

    parser.add_argument(
        '--tiles',
        action=StoreTrue,
        nargs=0,
        default=False,
        help='Generate static tiles for orthophotos and DEMs that are '
        'suitable for viewers like Leaflet or OpenLayers. '
        'Default: %(default)s')

    parser.add_argument(
        '--3d-tiles',
        action=StoreTrue,
        nargs=0,
        default=False,
        help='Generate OGC 3D Tiles outputs. Default: %(default)s')

    parser.add_argument(
        '--rolling-shutter',
        action=StoreTrue,
        nargs=0,
        default=False,
        help='Turn on rolling shutter correction. If the camera '
        'has a rolling shutter and the images were taken in motion, you can turn on this option '
        'to improve the accuracy of the results. See also --rolling-shutter-readout. '
        'Default: %(default)s')

    parser.add_argument(
        '--rolling-shutter-readout',
        type=float,
        action=StoreValue,
        metavar='<positive integer>',
        default=0,
        help=
        'Override the rolling shutter readout time for your camera sensor (in milliseconds), instead of using the rolling shutter readout database. '
        'Note that not all cameras are present in the database. Set to 0 to use the database value. '
        'Default: %(default)s')

    parser.add_argument(
        '--build-overviews',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Build orthophoto overviews for faster display in programs such as QGIS. Default: %(default)s'
    )

    parser.add_argument(
        '--cog',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        'Create Cloud-Optimized GeoTIFFs instead of normal GeoTIFFs. Default: %(default)s'
    )

    parser.add_argument('--verbose',
                        '-v',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Print additional messages to the console. '
                        'Default: %(default)s')

    parser.add_argument(
        '--copy-to',
        metavar='<path>',
        action=StoreValue,
        help='Copy output results to this folder after processing.')

    parser.add_argument('--time',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Generates a benchmark file with runtime info. '
                        'Default: %(default)s')

    parser.add_argument('--debug',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Print debug messages. Default: %(default)s')

    parser.add_argument('--version',
                        action='version',
                        version='ODM {0}'.format(__version__),
                        help='Displays version number and exits. ')

    parser.add_argument(
        '--split',
        type=int,
        action=StoreValue,
        default=999999,
        metavar='<positive integer>',
        help='Average number of images per submodel. When '
        'splitting a large dataset into smaller '
        'submodels, images are grouped into clusters. '
        'This value regulates the number of images that '
        'each cluster should have on average. Default: %(default)s')

    parser.add_argument(
        '--split-overlap',
        type=float,
        action=StoreValue,
        metavar='<positive integer>',
        default=150,
        help='Radius of the overlap between submodels. '
        'After grouping images into clusters, images '
        'that are closer than this radius to a cluster '
        'are added to the cluster. This is done to ensure '
        'that neighboring submodels overlap. Default: %(default)s')

    parser.add_argument(
        '--split-image-groups',
        metavar='<path string>',
        action=StoreValue,
        default=None,
        help=
        ('Path to the image groups file that controls how images should be split into groups. '
         'The file needs to use the following format: \n'
         'image_name group_name\n'
         'Default: %(default)s'))
    # parser.add_argument('--split-multitracks',
    #                    action=StoreTrue,
    #                    nargs=0,
    #                    default=False,
    #                    help='Split multi-track reconstructions.')

    parser.add_argument('--sm-cluster',
                        metavar='<string>',
                        action=StoreValue,
                        type=url_string,
                        default=None,
                        help='URL to a ClusterODM instance '
                        'for distributing a split-merge workflow on '
                        'multiple nodes in parallel. '
                        'Default: %(default)s')

    parser.add_argument(
        '--merge',
        metavar='<string>',
        action=StoreValue,
        default='all',
        choices=['all', 'pointcloud', 'orthophoto', 'dem'],
        help=('Choose what to merge in the merge step in a split dataset. '
              'By default all available outputs are merged. '
              'Options: %(choices)s. Default: '
              '%(default)s'))

    parser.add_argument(
        '--force-gps',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        ('Use images\' GPS exif data for reconstruction, even if there are GCPs present.'
         'This flag is useful if you have high precision GPS measurements. '
         'If there are no GCPs, this flag does nothing. Default: %(default)s'))

    parser.add_argument(
        '--gps-accuracy',
        type=float,
        action=StoreValue,
        metavar='<positive float>',
        default=10,
        help='Set a value in meters for the GPS Dilution of Precision (DOP) '
        'information for all images. If your images are tagged '
        'with high precision GPS information (RTK), this value will be automatically '
        'set accordingly. You can use this option to manually set it in case the reconstruction '
        'fails. Lowering this option can sometimes help control bowling-effects over large areas. Default: %(default)s'
    )

    parser.add_argument(
        '--optimize-disk-space',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        ('Delete heavy intermediate files to optimize disk space usage. This '
         'affects the ability to restart the pipeline from an intermediate stage, '
         'but allows datasets to be processed on machines that don\'t have sufficient '
         'disk space available. Default: %(default)s'))

    parser.add_argument(
        '--pc-rectify',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        ('Perform ground rectification on the point cloud. This means that wrongly classified ground '
         'points will be re-classified and gaps will be filled. Useful for generating DTMs. '
         'Default: %(default)s'))

    parser.add_argument(
        '--primary-band',
        metavar='<string>',
        action=StoreValue,
        default="auto",
        type=str,
        help=
        ('When processing multispectral datasets, you can specify the name of the primary band that will be used for reconstruction. '
         'It\'s recommended to choose a band which has sharp details and is in focus. '
         'Default: %(default)s'))

    parser.add_argument(
        '--skip-band-alignment',
        action=StoreTrue,
        nargs=0,
        default=False,
        help=
        ('When processing multispectral datasets, ODM will automatically align the images for each band. '
         'If the images have been postprocessed and are already aligned, use this option. '
         'Default: %(default)s'))

    args = parser.parse_args(argv)

    # check that the project path setting has been set properly
    if not args.project_path:
        log.ODM_ERROR('You need to set the project path in the '
                      'settings.yaml file before you can run ODM, '
                      'or use `--project-path <path>`. Run `python3 '
                      'run.py --help` for more information. ')
        sys.exit(1)

    if args.fast_orthophoto:
        log.ODM_INFO(
            'Fast orthophoto is turned on, automatically setting --skip-3dmodel'
        )
        args.skip_3dmodel = True

    if args.pc_rectify and not args.pc_classify:
        log.ODM_INFO(
            "Ground rectify is turned on, automatically turning on point cloud classification"
        )
        args.pc_classify = True

    if args.dtm and not args.pc_classify:
        log.ODM_INFO(
            "DTM is turned on, automatically turning on point cloud classification"
        )
        args.pc_classify = True

    if args.skip_3dmodel and args.use_3dmesh:
        log.ODM_WARNING(
            '--skip-3dmodel is set, but so is --use-3dmesh. --skip-3dmodel will be ignored.'
        )
        args.skip_3dmodel = False

    if args.orthophoto_cutline and not args.crop:
        log.ODM_WARNING(
            "--orthophoto-cutline is set, but --crop is not. --crop will be set to 0.01"
        )
        args.crop = 0.01

    if args.sm_cluster:
        try:
            Node.from_url(args.sm_cluster).info()
        except exceptions.NodeConnectionError as e:
            log.ODM_ERROR("Cluster node seems to be offline: %s" % str(e))
            sys.exit(1)

    return args
Ejemplo n.º 11
0
import glob
from pyodm import Node, exceptions

node = Node("localhost", 3000)

try:
    # Get all JPG files in directory
    images = glob.glob("*.JPG") + glob.glob("*.jpg") + glob.glob(
        "*.JPEG") + glob.glob("*.jpeg")

    print("Uploading images...")
    task = node.create_task(images, {'dsm': True, 'orthophoto-resolution': 2})
    print(task.info())

    try:

        def print_status(task_info):
            msecs = task_info.processing_time
            seconds = int((msecs / 1000) % 60)
            minutes = int((msecs / (1000 * 60)) % 60)
            hours = int((msecs / (1000 * 60 * 60)) % 24)
            print("Task is running: %02d:%02d:%02d" %
                  (hours, minutes, seconds),
                  end="\r")

        task.wait_for_completion(status_callback=print_status)

        print("Task completed, downloading results...")

        # Retrieve results
        def print_download(progress):
import os
import sys
sys.path.append('..')

from pyodm import Node, exceptions
from tkinter import *
from tkinter.ttk import Progressbar
from tkinter import ttk, filedialog
from ttkthemes import ThemedStyle

node = Node("192.168.99.100", 3000)

value = 0


def open_file():
    global files
    files = filedialog.askopenfilenames(initialdir="Dataset")
    print(files)
    return files


def create_task():
    try:
        #root.config(cursor='wait red red')
        # Start a task
        print("Uploading images...")
        task = node.create_task(files, {
            'use-opensfm-dense': True,
            'orthophoto-resolution': 4,
        })
Ejemplo n.º 13
0
import sys

sys.path.append('..')

from pyodm import Node, exceptions

node = Node.from_url("http://localhost:3000?token=abc")

try:
    print(node.info())
except exceptions.NodeConnectionError as e:
    print("Cannot connect: " + str(e))
Ejemplo n.º 14
0
import os
import sys
sys.path.append('..')

from pyodm import Node, exceptions

node = Node("localhost", 3000)

try:
    # Start a task
    print("Uploading images...")
    task = node.create_task(['images/image_1.jpg', 'images/image_2.jpg'],
                            {'dsm': True, 'orthophoto-resolution': 4})
    print(task.info())

    try:
        # This will block until the task is finished
        # or will raise an exception
        task.wait_for_completion()

        print("Task completed, downloading results...")

        # Retrieve results
        task.download_assets("./results")

        print("Assets saved in ./results (%s)" % os.listdir("./results"))

        # Restart task and this time compute dtm
        task.restart({'dtm': True})
        task.wait_for_completion()
Ejemplo n.º 15
0
from pyodm import Node, exceptions

node = Node('localhost', 3000)
try:
    print(node.info())
except exceptions.NodeConnectionError as e:
    print("Cannot connect: " + str(e))
Ejemplo n.º 16
0
 def test_offline_api(self):
     api = Node("offline-host", 3000)
     self.assertRaises(NodeConnectionError, api.info)
     self.assertRaises(NodeConnectionError, api.options)
Ejemplo n.º 17
0
def config(argv=None, parser=None):
    global args

    if args is not None and argv is None:
        return args

    if parser is None:
        parser = SettingsParser(description='ODM',
                            usage='%(prog)s [options] <project name>',
                            yaml_file=open(context.settings_path))
    
    parser.add_argument('--project-path',
                        metavar='<path>',
                        action=StoreValue,
                        help='Path to the project folder')

    parser.add_argument('name',
                        metavar='<project name>',
                        action=StoreValue,
                        type=alphanumeric_string,
                        default='code',
                        nargs='?',
                        help='Name of Project (i.e subdirectory of projects folder)')

    parser.add_argument('--resize-to',
                        metavar='<integer>',
                        action=StoreValue,
                        default=2048,
                        type=int,
                        help='Legacy option (use --feature-quality instead). Resizes images by the largest side for feature extraction purposes only. '
                             'Set to -1 to disable. This does not affect the final orthophoto '
                             'resolution quality and will not resize the original images. Default:  %(default)s')

    parser.add_argument('--end-with', '-e',
                        metavar='<string>',
                        action=StoreValue,
                        default='odm_report',
                        choices=processopts,
                        help=('Can be one of:' + ' | '.join(processopts)))

    rerun = parser.add_mutually_exclusive_group()

    rerun.add_argument('--rerun', '-r',
                       metavar='<string>',
                       action=StoreValue,
                       choices=processopts,
                       help=('Can be one of:' + ' | '.join(processopts)))

    rerun.add_argument('--rerun-all',
                       action=StoreTrue,
                       nargs=0,
                       default=False,
                       help='force rerun of all tasks')

    rerun.add_argument('--rerun-from',
                       action=RerunFrom,
                       metavar='<string>',
                       choices=processopts,
                       help=('Can be one of:' + ' | '.join(processopts)))

    # parser.add_argument('--video',
    #                     metavar='<string>',
    #                     help='Path to the video file to process')

    # parser.add_argument('--slam-config',
    #                     metavar='<string>',
    #                     help='Path to config file for orb-slam')

    parser.add_argument('--min-num-features',
                        metavar='<integer>',
                        action=StoreValue,
                        default=8000,
                        type=int,
                        help=('Minimum number of features to extract per image. '
                              'More features leads to better results but slower '
                              'execution. Default: %(default)s'))
    
    parser.add_argument('--feature-type',
                        metavar='<string>',
                        action=StoreValue,
                        default='sift',
                        choices=['sift', 'hahog'],
                        help=('Choose the algorithm for extracting keypoints and computing descriptors. '
                            'Can be one of: %(choices)s. Default: '
                            '%(default)s'))
    
    parser.add_argument('--feature-quality',
                        metavar='<string>',
                        action=StoreValue,
                        default='high',
                        choices=['ultra', 'high', 'medium', 'low', 'lowest'],
                        help=('Set feature extraction quality. Higher quality generates better features, but requires more memory and takes longer. '
                            'Can be one of: %(choices)s. Default: '
                            '%(default)s'))
    
    parser.add_argument('--matcher-type',
                        metavar='<string>',
                        action=StoreValue,
                        default='flann',
                        choices=['flann', 'bow'],
                        help=('Matcher algorithm, Fast Library for Approximate Nearest Neighbors or Bag of Words. FLANN is slower, but more stable. BOW is faster, but can sometimes miss valid matches. '
                            'Can be one of: %(choices)s. Default: '
                            '%(default)s'))

    parser.add_argument('--matcher-neighbors',
                        metavar='<integer>',
                        action=StoreValue,
                        default=8,
                        type=int,
                        help='Number of nearest images to pre-match based on GPS '
                             'exif data. Set to 0 to skip pre-matching. '
                             'Neighbors works together with Distance parameter, '
                             'set both to 0 to not use pre-matching. OpenSFM '
                             'uses both parameters at the same time, Bundler '
                             'uses only one which has value, prefering the '
                             'Neighbors parameter. Default: %(default)s')

    parser.add_argument('--matcher-distance',
                        metavar='<integer>',
                        action=StoreValue,
                        default=0,
                        type=int,
                        help='Distance threshold in meters to find pre-matching '
                             'images based on GPS exif data. Set both '
                             'matcher-neighbors and this to 0 to skip '
                             'pre-matching. Default: %(default)s')

    parser.add_argument('--use-fixed-camera-params',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Turn off camera parameter optimization during bundler')

    parser.add_argument('--cameras',
                        default='',
                        metavar='<json>',
                        action=StoreValue,
                        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('--camera-lens',
            metavar='<string>',
            action=StoreValue,
            default='auto',
            choices=['auto', 'perspective', 'brown', 'fisheye', 'spherical'],
            help=('Set a camera projection type. Manually setting a value '
                'can help improve geometric undistortion. By default the application '
                'tries to determine a lens type from the images metadata. Can be '
                'set to one of: %(choices)s. Default: '
                '%(default)s'))

    parser.add_argument('--radiometric-calibration',
            metavar='<string>',
            action=StoreValue,
            default='none',
            choices=['none', 'camera', 'camera+sun'],
            help=('Set the radiometric calibration to perform on images. '
                'When processing multispectral images you should set this option '
                'to obtain reflectance values (otherwise you will get digital number values). '
                '[camera] applies black level, vignetting, row gradient gain/exposure compensation (if appropriate EXIF tags are found). '
                '[camera+sun] is experimental, applies all the corrections of [camera], plus compensates for spectral radiance registered via a downwelling light sensor (DLS) taking in consideration the angle of the sun. '
                'Can be set to one of: %(choices)s. Default: '
                '%(default)s'))

    parser.add_argument('--max-concurrency',
                        metavar='<positive integer>',
                        action=StoreValue,
                        default=context.num_cores,
                        type=int,
                        help=('The maximum number of processes to use in various '
                              'processes. Peak memory requirement is ~1GB per '
                              'thread and 2 megapixel image resolution. Default: %(default)s'))

    parser.add_argument('--depthmap-resolution',
                        metavar='<positive float>',
                        action=StoreValue,
                        type=float,
                        default=640,
                        help=('Legacy option (use --pc-quality instead). Controls the density of the point cloud by setting the resolution of the depthmap images. Higher values take longer to compute '
                              'but produce denser point clouds. '
                              'Default: %(default)s'))

    parser.add_argument('--opensfm-depthmap-min-consistent-views',
                      metavar='<integer: 2 <= x <= 9>',
                      action=StoreValue,
                      type=int,
                      default=3,
                      help=('Minimum number of views that should reconstruct a point for it to be valid. Use lower values '
                            'if your images have less overlap. Lower values result in denser point clouds '
                            'but with more noise. '
                            'Default: %(default)s'))

    parser.add_argument('--opensfm-depthmap-method',
                      metavar='<string>',
                      action=StoreValue,
                      default='PATCH_MATCH',
                      choices=['PATCH_MATCH', 'BRUTE_FORCE', 'PATCH_MATCH_SAMPLE'],
                      help=('Raw depthmap computation algorithm. '
                            'PATCH_MATCH and PATCH_MATCH_SAMPLE are faster, but might miss some valid points. '
                            'BRUTE_FORCE takes longer but produces denser reconstructions. '
                            'Default: %(default)s'))

    parser.add_argument('--opensfm-depthmap-min-patch-sd',
                      metavar='<positive float>',
                      action=StoreValue,
                      type=float,
                      default=1,
                      help=('When using PATCH_MATCH or PATCH_MATCH_SAMPLE, controls the standard deviation threshold to include patches. '
                            'Patches with lower standard deviation are ignored. '
                            'Default: %(default)s'))

    parser.add_argument('--use-hybrid-bundle-adjustment',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Run local bundle adjustment for every image added to the reconstruction and a global '
                             'adjustment every 100 images. Speeds up reconstruction for very large datasets.')

    parser.add_argument('--use-3dmesh',
                    action=StoreTrue,
                    nargs=0,
                    default=False,
                    help='Use a full 3D mesh to compute the orthophoto instead of a 2.5D mesh. This option is a bit faster and provides similar results in planar areas.')

    parser.add_argument('--skip-3dmodel',
                    action=StoreTrue,
                    nargs=0,
                    default=False,
                    help='Skip generation of a full 3D model. This can save time if you only need 2D results such as orthophotos and DEMs.')

    parser.add_argument('--use-opensfm-dense',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Use opensfm to compute dense point cloud alternatively')

    parser.add_argument('--ignore-gsd',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Ignore Ground Sampling Distance (GSD). GSD '
                        'caps the maximum resolution of image outputs and '
                        'resizes images when necessary, resulting in faster processing and '
                        'lower memory usage. Since GSD is an estimate, sometimes ignoring it can result in slightly better image output quality.')

    parser.add_argument('--mesh-size',
                        metavar='<positive integer>',
                        action=StoreValue,
                        default=200000,
                        type=int,
                        help=('The maximum vertex count of the output mesh. '
                              'Default: %(default)s'))

    parser.add_argument('--mesh-octree-depth',
                        metavar='<positive integer>',
                        action=StoreValue,
                        default=10,
                        type=int,
                        help=('Oct-tree depth used in the mesh reconstruction, '
                              'increase to get more vertices, recommended '
                              'values are 8-12. Default: %(default)s'))

    parser.add_argument('--mesh-samples',
                        metavar='<float >= 1.0>',
                        action=StoreValue,
                        default=1.0,
                        type=float,
                        help=('Number of points per octree node, recommended '
                              'and default value: %(default)s'))

    parser.add_argument('--mesh-point-weight',
                        metavar='<positive float>',
                        action=StoreValue,
                        default=4,
                        type=float,
                        help=('This floating point value specifies the importance'
                        ' that interpolation of the point samples is given in the '
                        'formulation of the screened Poisson equation. The results '
                        'of the original (unscreened) Poisson Reconstruction can '
                        'be obtained by setting this value to 0.'
                        'Default= %(default)s'))

    parser.add_argument('--fast-orthophoto',
                action=StoreTrue,
                nargs=0,
                default=False,
                help='Skips dense reconstruction and 3D model generation. '
                'It generates an orthophoto directly from the sparse reconstruction. '
                'If you just need an orthophoto and do not need a full 3D model, turn on this option.')

    parser.add_argument('--crop',
                    metavar='<positive float>',
                    action=StoreValue,
                    default=3,
                    type=float,
                    help=('Automatically crop image outputs by creating a smooth buffer '
                          'around the dataset boundaries, shrinked by N meters. '
                          'Use 0 to disable cropping. '
                          'Default: %(default)s'))

    parser.add_argument('--pc-quality',
                    metavar='<string>',
                    action=StoreValue,
                    default='medium',
                    choices=['ultra', 'high', 'medium', 'low', 'lowest'],
                    help=('Set point cloud quality. Higher quality generates better, denser point clouds, but requires more memory and takes longer. Each step up in quality increases processing time roughly by a factor of 4x.'
                        'Can be one of: %(choices)s. Default: '
                        '%(default)s'))

    parser.add_argument('--pc-classify',
            action=StoreTrue,
            nargs=0,
            default=False,
            help='Classify the point cloud outputs using a Simple Morphological Filter. '
            'You can control the behavior of this option by tweaking the --dem-* parameters. '
            'Default: '
            '%(default)s')

    parser.add_argument('--pc-csv',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Export the georeferenced point cloud in CSV format. Default:  %(default)s')
    
    parser.add_argument('--pc-las',
                action=StoreTrue,
                nargs=0,
                default=False,
                help='Export the georeferenced point cloud in LAS format. Default:  %(default)s')

    parser.add_argument('--pc-ept',
                action=StoreTrue,
                nargs=0,
                default=False,
                help='Export the georeferenced point cloud in Entwine Point Tile (EPT) format. Default:  %(default)s')

    parser.add_argument('--pc-filter',
                        metavar='<positive float>',
                        action=StoreValue,
                        type=float,
                        default=2.5,
                        help='Filters the point cloud by removing points that deviate more than N standard deviations from the local mean. Set to 0 to disable filtering.'
                             '\nDefault: '
                             '%(default)s')
    
    parser.add_argument('--pc-sample',
                        metavar='<positive float>',
                        action=StoreValue,
                        type=float,
                        default=0,
                        help='Filters the point cloud by keeping only a single point around a radius N (in meters). This can be useful to limit the output resolution of the point cloud. Set to 0 to disable sampling.'
                             '\nDefault: '
                             '%(default)s')

    parser.add_argument('--smrf-scalar',
                        metavar='<positive float>',
                        action=StoreValue,
                        type=float,
                        default=1.25,
                        help='Simple Morphological Filter elevation scalar parameter. '
                             '\nDefault: '
                             '%(default)s')

    parser.add_argument('--smrf-slope',
        metavar='<positive float>',
        action=StoreValue,
        type=float,
        default=0.15,
        help='Simple Morphological Filter slope parameter (rise over run). '
                '\nDefault: '
                '%(default)s')
    
    parser.add_argument('--smrf-threshold',
        metavar='<positive float>',
        action=StoreValue,
        type=float,
        default=0.5,
        help='Simple Morphological Filter elevation threshold parameter (meters). '
                '\nDefault: '
                '%(default)s')
    
    parser.add_argument('--smrf-window',
        metavar='<positive float>',
        action=StoreValue,
        type=float,
        default=18.0,
        help='Simple Morphological Filter window radius parameter (meters). '
                '\nDefault: '
                '%(default)s')

    parser.add_argument('--texturing-data-term',
                        metavar='<string>',
                        action=StoreValue,
                        default='gmi',
                        choices=['gmi', 'area'],
                        help=('Data term: [area, gmi]. Default: '
                              '%(default)s'))

    parser.add_argument('--texturing-outlier-removal-type',
                        metavar='<string>',
                        action=StoreValue,
                        default='gauss_clamping',
                        choices=['none', 'gauss_clamping', 'gauss_damping'],
                        help=('Type of photometric outlier removal method: '
                              '[none, gauss_damping, gauss_clamping]. Default: '
                              '%(default)s'))

    parser.add_argument('--texturing-skip-global-seam-leveling',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help=('Skip global seam leveling. Useful for IR data.'
                              'Default: %(default)s'))

    parser.add_argument('--texturing-skip-local-seam-leveling',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Skip local seam blending. Default:  %(default)s')

    parser.add_argument('--texturing-tone-mapping',
                        metavar='<string>',
                        action=StoreValue,
                        choices=['none', 'gamma'],
                        default='none',
                        help='Turn on gamma tone mapping or none for no tone '
                             'mapping. Choices are  \'gamma\' or \'none\'. '
                             'Default: %(default)s ')

    parser.add_argument('--gcp',
                        metavar='<path string>',
                        action=StoreValue,
                        default=None,
                        help=('Path to the file containing the ground control '
                              'points used for georeferencing.  Default: '
                              '%(default)s. The file needs to '
                              'use the following format: \n'
                              'EPSG:<code> or <+proj definition>\n'
                              'geo_x geo_y geo_z im_x im_y image_name [gcp_name] [extra1] [extra2]'))

    parser.add_argument('--geo',
                        metavar='<path string>',
                        action=StoreValue,
                        default=None,
                        help=('Path to the image geolocation file containing the camera center coordinates used for georeferencing. '
                              'Note that omega/phi/kappa are currently not supported (you can set them to 0). '
                              'Default: '
                              '%(default)s. The file needs to '
                              'use the following format: \n'
                              'EPSG:<code> or <+proj definition>\n'
                              'image_name geo_x geo_y geo_z [omega (degrees)] [phi (degrees)] [kappa (degrees)] [horz accuracy (meters)] [vert accuracy (meters)]'
                              ''))

    parser.add_argument('--use-exif',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help=('Use this tag if you have a gcp_list.txt but '
                              'want to use the exif geotags instead'))

    parser.add_argument('--dtm',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Use this tag to build a DTM (Digital Terrain Model, ground only) using a simple '
                             'morphological filter. Check the --dem* and --smrf* parameters for finer tuning.')

    parser.add_argument('--dsm',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Use this tag to build a DSM (Digital Surface Model, ground + objects) using a progressive '
                             'morphological filter. Check the --dem* parameters for finer tuning.')

    parser.add_argument('--dem-gapfill-steps',
                        metavar='<positive integer>',
                        action=StoreValue,
                        default=3,
                        type=int,
                        help='Number of steps used to fill areas with gaps. Set to 0 to disable gap filling. '
                             'Starting with a radius equal to the output resolution, N different DEMs are generated with '
                             'progressively bigger radius using the inverse distance weighted (IDW) algorithm '
                             'and merged together. Remaining gaps are then merged using nearest neighbor interpolation. '
                             '\nDefault=%(default)s')

    parser.add_argument('--dem-resolution',
                        metavar='<float>',
                        action=StoreValue,
                        type=float,
                        default=5,
                        help='DSM/DTM resolution in cm / pixel. Note that this value is capped by a ground sampling distance (GSD) estimate. To remove the cap, check --ignore-gsd also.'
                             '\nDefault: %(default)s')

    parser.add_argument('--dem-decimation',
                        metavar='<positive integer>',
                        action=StoreValue,
                        default=1,
                        type=int,
                        help='Decimate the points before generating the DEM. 1 is no decimation (full quality). '
                             '100 decimates ~99%% of the points. Useful for speeding up '
                             'generation.\nDefault=%(default)s')
    
    parser.add_argument('--dem-euclidean-map',
            action=StoreTrue,
            nargs=0,
            default=False,
            help='Computes an euclidean raster map for each DEM. '
            'The map reports the distance from each cell to the nearest '
            'NODATA value (before any hole filling takes place). '
            'This can be useful to isolate the areas that have been filled. '
            'Default: '
            '%(default)s')

    parser.add_argument('--orthophoto-resolution',
                        metavar='<float > 0.0>',
                        action=StoreValue,
                        default=5,
                        type=float,
                        help=('Orthophoto resolution in cm / pixel. Note that this value is capped by a ground sampling distance (GSD) estimate. To remove the cap, check --ignore-gsd also.\n'
                              'Default: %(default)s'))

    parser.add_argument('--orthophoto-no-tiled',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Set this parameter if you want a stripped geoTIFF.\n'
                             'Default: %(default)s')
    
    parser.add_argument('--orthophoto-png',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Set this parameter if you want to generate a PNG rendering of the orthophoto.\n'
                             'Default: %(default)s')

    parser.add_argument('--orthophoto-compression',
                        metavar='<string>',
                        action=StoreValue,
                        type=str,
                        choices=['JPEG', 'LZW', 'PACKBITS', 'DEFLATE', 'LZMA', 'NONE'],
                        default='DEFLATE',
                        help='Set the compression to use for orthophotos. Options: %(choices)s.\nDefault: %(default)s')
    
    parser.add_argument('--orthophoto-cutline',
            action=StoreTrue,
            nargs=0,
            default=False,
            help='Generates a polygon around the cropping area '
            'that cuts the orthophoto around the edges of features. This polygon '
            'can be useful for stitching seamless mosaics with multiple overlapping orthophotos. '
            'Default: '
            '%(default)s')

    parser.add_argument('--tiles',
                    action=StoreTrue,
                    nargs=0,
                    default=False,
                    help='Generate static tiles for orthophotos and DEMs that are '
                         'suitable for viewers like Leaflet or OpenLayers. '
                         'Default: %(default)s')

    parser.add_argument('--build-overviews',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Build orthophoto overviews using gdaladdo.')

    parser.add_argument('--verbose', '-v',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Print additional messages to the console\n'
                             'Default: %(default)s')

    parser.add_argument('--time',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Generates a benchmark file with runtime info\n'
                             'Default: %(default)s')
    
    parser.add_argument('--debug',
                        action=StoreTrue,
                        nargs=0,
                        default=False,
                        help='Print debug messages\n'
                             'Default: %(default)s')

    parser.add_argument('--version',
                        action='version',
                        version='ODM {0}'.format(__version__),
                        help='Displays version number and exits. ')

    parser.add_argument('--split',
                        type=int,
                        action=StoreValue,
                        default=999999,
                        metavar='<positive integer>',
                        help='Average number of images per submodel. When '
                                'splitting a large dataset into smaller '
                                'submodels, images are grouped into clusters. '
                                'This value regulates the number of images that '
                                'each cluster should have on average.')

    parser.add_argument('--split-overlap',
                        type=float,
                        action=StoreValue,
                        metavar='<positive integer>',
                        default=150,
                        help='Radius of the overlap between submodels. '
                        'After grouping images into clusters, images '
                        'that are closer than this radius to a cluster '
                        'are added to the cluster. This is done to ensure '
                        'that neighboring submodels overlap.')

    parser.add_argument('--split-multitracks',
                       action=StoreTrue,
                       nargs=0,
                       default=False,
                       help='Split multi-track reconstructions.')

    parser.add_argument('--sm-cluster',
                        metavar='<string>',
                        action=StoreValue,
                        type=url_string,
                        default=None,
                        help='URL to a ClusterODM instance '
                            'for distributing a split-merge workflow on '
                            'multiple nodes in parallel. '
                            'Default: %(default)s')

    parser.add_argument('--merge',
                    metavar='<string>',
                    action=StoreValue,
                    default='all',
                    choices=['all', 'pointcloud', 'orthophoto', 'dem'],
                    help=('Choose what to merge in the merge step in a split dataset. '
                          'By default all available outputs are merged. '
                          'Options: %(choices)s. Default: '
                            '%(default)s'))

    parser.add_argument('--force-gps',
                    action=StoreTrue,
                    nargs=0,
                    default=False,
                    help=('Use images\' GPS exif data for reconstruction, even if there are GCPs present.'
                          'This flag is useful if you have high precision GPS measurements. '
                          'If there are no GCPs, this flag does nothing. Default: %(default)s'))
    
    parser.add_argument('--gps-accuracy',
                        type=float,
                        action=StoreValue,
                        metavar='<positive float>',
                        default=10,
                        help='Set a value in meters for the GPS Dilution of Precision (DOP) '
                        'information for all images. If your images are tagged '
                        'with high precision GPS information (RTK), this value will be automatically '
                        'set accordingly. You can use this option to manually set it in case the reconstruction '
                        'fails. Lowering this option can sometimes help control bowling-effects over large areas. Default: %(default)s')

    parser.add_argument('--optimize-disk-space',
                action=StoreTrue,
                nargs=0,
                default=False,
                help=('Delete heavy intermediate files to optimize disk space usage. This '
                      'affects the ability to restart the pipeline from an intermediate stage, '
                      'but allows datasets to be processed on machines that don\'t have sufficient '
                      'disk space available. Default: %(default)s'))

    parser.add_argument('--pc-rectify',
                    action=StoreTrue,
                    nargs=0,
                    default=False,
                    help=('Perform ground rectification on the point cloud. This means that wrongly classified ground '
                          'points will be re-classified and gaps will be filled. Useful for generating DTMs. '
                          'Default: %(default)s'))

    parser.add_argument('--primary-band',
                        metavar='<string>',
                        action=StoreValue,
                        default="auto",
                        type=str,
                        help=('When processing multispectral datasets, you can specify the name of the primary band that will be used for reconstruction. '
                              'It\'s recommended to choose a band which has sharp details and is in focus. ' 
                              'Default: %(default)s'))

    args = parser.parse_args(argv)

    # check that the project path setting has been set properly
    if not args.project_path:
        log.ODM_ERROR('You need to set the project path in the '
                      'settings.yaml file before you can run ODM, '
                      'or use `--project-path <path>`. Run `python3 '
                      'run.py --help` for more information. ')
        sys.exit(1)

    if args.fast_orthophoto:
      log.ODM_INFO('Fast orthophoto is turned on, automatically setting --skip-3dmodel')
      args.skip_3dmodel = True

    if args.pc_rectify and not args.pc_classify:
      log.ODM_INFO("Ground rectify is turned on, automatically turning on point cloud classification")
      args.pc_classify = True

    if args.dtm and not args.pc_classify:
      log.ODM_INFO("DTM is turned on, automatically turning on point cloud classification")
      args.pc_classify = True

    if args.skip_3dmodel and args.use_3dmesh:
      log.ODM_WARNING('--skip-3dmodel is set, but so is --use-3dmesh. --skip-3dmodel will be ignored.')
      args.skip_3dmodel = False

    if args.orthophoto_cutline and not args.crop:
      log.ODM_WARNING("--orthophoto-cutline is set, but --crop is not. --crop will be set to 0.01")
      args.crop = 0.01

    if args.sm_cluster:
        try:
            Node.from_url(args.sm_cluster).info()
        except exceptions.NodeConnectionError as e:
            log.ODM_ERROR("Cluster node seems to be offline: %s"  % str(e))
            sys.exit(1)
    
    # if args.radiometric_calibration != "none" and not args.texturing_skip_global_seam_leveling:
    #     log.ODM_WARNING("radiometric-calibration is turned on, automatically setting --texturing-skip-global-seam-leveling")
    #     args.texturing_skip_global_seam_leveling = True

    return args
Ejemplo n.º 18
0
import os
from pyodm import Node

print("Creating Node connection")
nd = Node("localhost", 3000)

print("Creating ODM task...")
path = os.path.realpath("images")
image_list = [
    path + "/1-1.jpg", path + "/1-2.jpg", path + "/1-3.jpg", path + "/1-4.jpg",
    path + "/1-5.jpg", path + "/1-6.jpg", path + "/1-7.jpg", path + "/1-8.jpg",
    path + "/1-9.jpg", path + "/1-10.jpg", path + "/1-11.jpg"
]

image_list_1 = [path + "/1-1.jpg", path + "/1-2.jpg", path + "/1-3.jpg"]

task = nd.create_task(image_list_1)

print("Running task...")
task.wait_for_completion()

print("Completed")
os.listdir(task.download_assets("results"))
Ejemplo n.º 19
0
def config():
    parser.add_argument('--project-path',
                        metavar='<path>',
                        help='Path to the project folder')

    parser.add_argument('name',
                        metavar='<project name>',
                        type=alphanumeric_string,
                        default='code',
                        nargs='?',
                        help='Name of Project (i.e subdirectory of projects folder)')

    parser.add_argument('--resize-to',
                        metavar='<integer>',
                        default=2048,
                        type=int,
                        help='Resizes images by the largest side for feature extraction purposes only. '
                             'Set to -1 to disable. This does not affect the final orthophoto '
                             ' resolution quality and will not resize the original images. Default:  %(default)s')

    parser.add_argument('--end-with', '-e',
                        metavar='<string>',
                        default='odm_orthophoto',
                        choices=processopts,
                        help=('Can be one of:' + ' | '.join(processopts)))

    rerun = parser.add_mutually_exclusive_group()

    rerun.add_argument('--rerun', '-r',
                       metavar='<string>',
                       choices=processopts,
                       help=('Can be one of:' + ' | '.join(processopts)))

    rerun.add_argument('--rerun-all',
                       action='store_true',
                       default=False,
                       help='force rerun of all tasks')

    rerun.add_argument('--rerun-from',
                       action=RerunFrom,
                       metavar='<string>',
                       choices=processopts,
                       help=('Can be one of:' + ' | '.join(processopts)))

    # parser.add_argument('--video',
    #                     metavar='<string>',
    #                     help='Path to the video file to process')

    # parser.add_argument('--slam-config',
    #                     metavar='<string>',
    #                     help='Path to config file for orb-slam')

    parser.add_argument('--min-num-features',
                        metavar='<integer>',
                        default=8000,
                        type=int,
                        help=('Minimum number of features to extract per image. '
                              'More features leads to better results but slower '
                              'execution. Default: %(default)s'))

    parser.add_argument('--matcher-neighbors',
                        type=int,
                        metavar='<integer>',
                        default=8,
                        help='Number of nearest images to pre-match based on GPS '
                             'exif data. Set to 0 to skip pre-matching. '
                             'Neighbors works together with Distance parameter, '
                             'set both to 0 to not use pre-matching. OpenSFM '
                             'uses both parameters at the same time, Bundler '
                             'uses only one which has value, prefering the '
                             'Neighbors parameter. Default: %(default)s')

    parser.add_argument('--matcher-distance',
                        metavar='<integer>',
                        default=0,
                        type=int,
                        help='Distance threshold in meters to find pre-matching '
                             'images based on GPS exif data. Set both '
                             'matcher-neighbors and this to 0 to skip '
                             'pre-matching. Default: %(default)s')

    parser.add_argument('--use-fixed-camera-params',
                        action='store_true',
                        default=False,
                        help='Turn off camera parameter optimization during bundler')

    parser.add_argument('--cameras',
                        default='',
                        metavar='<json>',
                        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('--camera-lens',
            metavar='<string>',
            default='auto',
            choices=['auto', 'perspective', 'brown', 'fisheye', 'spherical'],
            help=('Set a camera projection type. Manually setting a value '
                'can help improve geometric undistortion. By default the application '
                'tries to determine a lens type from the images metadata. Can be '
                'set to one of: [auto, perspective, brown, fisheye, spherical]. Default: '
                '%(default)s'))

    parser.add_argument('--max-concurrency',
                        metavar='<positive integer>',
                        default=context.num_cores,
                        type=int,
                        help=('The maximum number of processes to use in various '
                              'processes. Peak memory requirement is ~1GB per '
                              'thread and 2 megapixel image resolution. Default: %(default)s'))

    parser.add_argument('--depthmap-resolution',
                        metavar='<positive float>',
                        type=float,
                        default=640,
                        help=('Controls the density of the point cloud by setting the resolution of the depthmap images. Higher values take longer to compute '
                              'but produce denser point clouds. '
                              'Default: %(default)s'))

    parser.add_argument('--opensfm-depthmap-min-consistent-views',
                      metavar='<integer: 2 <= x <= 9>',
                      type=int,
                      default=3,
                      help=('Minimum number of views that should reconstruct a point for it to be valid. Use lower values '
                            'if your images have less overlap. Lower values result in denser point clouds '
                            'but with more noise. '
                            'Default: %(default)s'))

    parser.add_argument('--opensfm-depthmap-method',
                      metavar='<string>',
                      default='PATCH_MATCH',
                      choices=['PATCH_MATCH', 'BRUTE_FORCE', 'PATCH_MATCH_SAMPLE'],
                      help=('Raw depthmap computation algorithm. '
                            'PATCH_MATCH and PATCH_MATCH_SAMPLE are faster, but might miss some valid points. '
                            'BRUTE_FORCE takes longer but produces denser reconstructions. '
                            'Default: %(default)s'))

    parser.add_argument('--opensfm-depthmap-min-patch-sd',
                      metavar='<positive float>',
                      type=float,
                      default=1,
                      help=('When using PATCH_MATCH or PATCH_MATCH_SAMPLE, controls the standard deviation threshold to include patches. '
                            'Patches with lower standard deviation are ignored. '
                            'Default: %(default)s'))

    parser.add_argument('--use-hybrid-bundle-adjustment',
                        action='store_true',
                        default=False,
                        help='Run local bundle adjustment for every image added to the reconstruction and a global '
                             'adjustment every 100 images. Speeds up reconstruction for very large datasets.')

    parser.add_argument('--mve-confidence',
                        metavar='<float: 0 <= x <= 1>',
                        type=float,
                        default=0.60,
                        help=('Discard points that have less than a certain confidence threshold. '
                              'This only affects dense reconstructions performed with MVE. '
                              'Higher values discard more points. '
                              'Default: %(default)s'))

    parser.add_argument('--use-3dmesh',
                    action='store_true',
                    default=False,
                    help='Use a full 3D mesh to compute the orthophoto instead of a 2.5D mesh. This option is a bit faster and provides similar results in planar areas.')

    parser.add_argument('--skip-3dmodel',
                    action='store_true',
                    default=False,
                    help='Skip generation of a full 3D model. This can save time if you only need 2D results such as orthophotos and DEMs.')

    parser.add_argument('--use-opensfm-dense',
                        action='store_true',
                        default=False,
                        help='Use opensfm to compute dense point cloud alternatively')

    parser.add_argument('--ignore-gsd',
                        action='store_true',
                        default=False,
                        help='Ignore Ground Sampling Distance (GSD). GSD '
                        'caps the maximum resolution of image outputs and '
                        'resizes images when necessary, resulting in faster processing and '
                        'lower memory usage. Since GSD is an estimate, sometimes ignoring it can result in slightly better image output quality.')

    parser.add_argument('--mesh-size',
                        metavar='<positive integer>',
                        default=100000,
                        type=int,
                        help=('The maximum vertex count of the output mesh. '
                              'Default: %(default)s'))

    parser.add_argument('--mesh-octree-depth',
                        metavar='<positive integer>',
                        default=9,
                        type=int,
                        help=('Oct-tree depth used in the mesh reconstruction, '
                              'increase to get more vertices, recommended '
                              'values are 8-12. Default: %(default)s'))

    parser.add_argument('--mesh-samples',
                        metavar='<float >= 1.0>',
                        default=1.0,
                        type=float,
                        help=('Number of points per octree node, recommended '
                              'and default value: %(default)s'))

    parser.add_argument('--mesh-point-weight',
                        metavar='<positive float>',
                        default=4,
                        type=float,
                        help=('This floating point value specifies the importance'
                        ' that interpolation of the point samples is given in the '
                        'formulation of the screened Poisson equation. The results '
                        'of the original (unscreened) Poisson Reconstruction can '
                        'be obtained by setting this value to 0.'
                        'Default= %(default)s'))

    parser.add_argument('--fast-orthophoto',
                action='store_true',
                default=False,
                help='Skips dense reconstruction and 3D model generation. '
                'It generates an orthophoto directly from the sparse reconstruction. '
                'If you just need an orthophoto and do not need a full 3D model, turn on this option. '
                'Experimental.')

    parser.add_argument('--crop',
                    metavar='<positive float>',
                    default=3,
                    type=float,
                    help=('Automatically crop image outputs by creating a smooth buffer '
                          'around the dataset boundaries, shrinked by N meters. '
                          'Use 0 to disable cropping. '
                          'Default: %(default)s'))

    parser.add_argument('--pc-classify',
            action='store_true',
            default=False,
            help='Classify the point cloud outputs using a Simple Morphological Filter. '
            'You can control the behavior of this option by tweaking the --dem-* parameters. '
            'Default: '
            '%(default)s')

    parser.add_argument('--pc-csv',
                        action='store_true',
                        default=False,
                        help='Export the georeferenced point cloud in CSV format. Default:  %(default)s')
    
    parser.add_argument('--pc-las',
                action='store_true',
                default=False,
                help='Export the georeferenced point cloud in LAS format. Default:  %(default)s')

    parser.add_argument('--pc-ept',
                action='store_true',
                default=False,
                help='Export the georeferenced point cloud in Entwine Point Tile (EPT) format. Default:  %(default)s')

    parser.add_argument('--pc-filter',
                        metavar='<positive float>',
                        type=float,
                        default=2.5,
                        help='Filters the point cloud by removing points that deviate more than N standard deviations from the local mean. Set to 0 to disable filtering.'
                             '\nDefault: '
                             '%(default)s')
    
    parser.add_argument('--pc-sample',
                        metavar='<positive float>',
                        type=float,
                        default=0,
                        help='Filters the point cloud by keeping only a single point around a radius N (in meters). This can be useful to limit the output resolution of the point cloud. Set to 0 to disable sampling.'
                             '\nDefault: '
                             '%(default)s')

    parser.add_argument('--smrf-scalar',
                        metavar='<positive float>',
                        type=float,
                        default=1.25,
                        help='Simple Morphological Filter elevation scalar parameter. '
                             '\nDefault: '
                             '%(default)s')

    parser.add_argument('--smrf-slope',
        metavar='<positive float>',
        type=float,
        default=0.15,
        help='Simple Morphological Filter slope parameter (rise over run). '
                '\nDefault: '
                '%(default)s')
    
    parser.add_argument('--smrf-threshold',
        metavar='<positive float>',
        type=float,
        default=0.5,
        help='Simple Morphological Filter elevation threshold parameter (meters). '
                '\nDefault: '
                '%(default)s')
    
    parser.add_argument('--smrf-window',
        metavar='<positive float>',
        type=float,
        default=18.0,
        help='Simple Morphological Filter window radius parameter (meters). '
                '\nDefault: '
                '%(default)s')

    parser.add_argument('--texturing-data-term',
                        metavar='<string>',
                        default='gmi',
                        choices=['gmi', 'area'],
                        help=('Data term: [area, gmi]. Default: '
                              '%(default)s'))

    parser.add_argument('--texturing-nadir-weight',
                        metavar='<integer: 0 <= x <= 32>',
                        default=16,
                        type=int,
                        help=('Affects orthophotos only. '
                              'Higher values result in sharper corners, but can affect color distribution and blurriness. '
                              'Use lower values for planar areas and higher values for urban areas. '
                              'The default value works well for most scenarios. Default: '
                              '%(default)s'))

    parser.add_argument('--texturing-outlier-removal-type',
                        metavar='<string>',
                        default='gauss_clamping',
                        choices=['none', 'gauss_clamping', 'gauss_damping'],
                        help=('Type of photometric outlier removal method: '
                              '[none, gauss_damping, gauss_clamping]. Default: '
                              '%(default)s'))

    parser.add_argument('--texturing-skip-visibility-test',
                        action='store_true',
                        default=False,
                        help=('Skip geometric visibility test. Default: '
                              ' %(default)s'))

    parser.add_argument('--texturing-skip-global-seam-leveling',
                        action='store_true',
                        default=False,
                        help=('Skip global seam leveling. Useful for IR data.'
                              'Default: %(default)s'))

    parser.add_argument('--texturing-skip-local-seam-leveling',
                        action='store_true',
                        default=False,
                        help='Skip local seam blending. Default:  %(default)s')

    parser.add_argument('--texturing-skip-hole-filling',
                        action='store_true',
                        default=False,
                        help=('Skip filling of holes in the mesh. Default: '
                              ' %(default)s'))

    parser.add_argument('--texturing-keep-unseen-faces',
                        action='store_true',
                        default=False,
                        help=('Keep faces in the mesh that are not seen in any camera. '
                              'Default:  %(default)s'))

    parser.add_argument('--texturing-tone-mapping',
                        metavar='<string>',
                        choices=['none', 'gamma'],
                        default='none',
                        help='Turn on gamma tone mapping or none for no tone '
                             'mapping. Choices are  \'gamma\' or \'none\'. '
                             'Default: %(default)s ')

    parser.add_argument('--gcp',
                        metavar='<path string>',
                        default=None,
                        help=('path to the file containing the ground control '
                              'points used for georeferencing.  Default: '
                              '%(default)s. The file needs to '
                              'be on the following line format: \neasting '
                              'northing height pixelrow pixelcol imagename'))

    parser.add_argument('--use-exif',
                        action='store_true',
                        default=False,
                        help=('Use this tag if you have a gcp_list.txt but '
                              'want to use the exif geotags instead'))

    parser.add_argument('--dtm',
                        action='store_true',
                        default=False,
                        help='Use this tag to build a DTM (Digital Terrain Model, ground only) using a simple '
                             'morphological filter. Check the --dem* and --smrf* parameters for finer tuning.')

    parser.add_argument('--dsm',
                        action='store_true',
                        default=False,
                        help='Use this tag to build a DSM (Digital Surface Model, ground + objects) using a progressive '
                             'morphological filter. Check the --dem* parameters for finer tuning.')

    parser.add_argument('--dem-gapfill-steps',
                        metavar='<positive integer>',
                        default=3,
                        type=int,
                        help='Number of steps used to fill areas with gaps. Set to 0 to disable gap filling. '
                             'Starting with a radius equal to the output resolution, N different DEMs are generated with '
                             'progressively bigger radius using the inverse distance weighted (IDW) algorithm '
                             'and merged together. Remaining gaps are then merged using nearest neighbor interpolation. '
                             '\nDefault=%(default)s')

    parser.add_argument('--dem-resolution',
                        metavar='<float>',
                        type=float,
                        default=5,
                        help='DSM/DTM resolution in cm / pixel.'
                             '\nDefault: %(default)s')

    parser.add_argument('--dem-decimation',
                        metavar='<positive integer>',
                        default=1,
                        type=int,
                        help='Decimate the points before generating the DEM. 1 is no decimation (full quality). '
                             '100 decimates ~99%% of the points. Useful for speeding up '
                             'generation.\nDefault=%(default)s')
    
    parser.add_argument('--dem-euclidean-map',
            action='store_true',
            default=False,
            help='Computes an euclidean raster map for each DEM. '
            'The map reports the distance from each cell to the nearest '
            'NODATA value (before any hole filling takes place). '
            'This can be useful to isolate the areas that have been filled. '
            'Default: '
            '%(default)s')

    parser.add_argument('--orthophoto-resolution',
                        metavar='<float > 0.0>',
                        default=5,
                        type=float,
                        help=('Orthophoto resolution in cm / pixel.\n'
                              'Default: %(default)s'))

    parser.add_argument('--orthophoto-no-tiled',
                        action='store_true',
                        default=False,
                        help='Set this parameter if you want a stripped geoTIFF.\n'
                             'Default: %(default)s')

    parser.add_argument('--orthophoto-compression',
                        metavar='<string>',
                        type=str,
                        choices=['JPEG', 'LZW', 'PACKBITS', 'DEFLATE', 'LZMA', 'NONE'],
                        default='DEFLATE',
                        help='Set the compression to use. Note that this could '
                             'break gdal_translate if you don\'t know what you '
                             'are doing. Options: %(choices)s.\nDefault: %(default)s')
    
    parser.add_argument('--orthophoto-cutline',
            action='store_true',
            default=False,
            help='Generates a polygon around the cropping area '
            'that cuts the orthophoto around the edges of features. This polygon '
            'can be useful for stitching seamless mosaics with multiple overlapping orthophotos. '
            'Default: '
            '%(default)s')

    parser.add_argument('--build-overviews',
                        action='store_true',
                        default=False,
                        help='Build orthophoto overviews using gdaladdo.')

    parser.add_argument('--verbose', '-v',
                        action='store_true',
                        default=False,
                        help='Print additional messages to the console\n'
                             'Default: %(default)s')

    parser.add_argument('--time',
                        action='store_true',
                        default=False,
                        help='Generates a benchmark file with runtime info\n'
                             'Default: %(default)s')
    
    parser.add_argument('--debug',
                        action='store_true',
                        default=False,
                        help='Print debug messages\n'
                             'Default: %(default)s')

    parser.add_argument('--version',
                        action='version',
                        version='OpenDroneMap {0}'.format(__version__),
                        help='Displays version number and exits. ')

    parser.add_argument('--split',
                        type=int,
                        default=999999,
                        metavar='<positive integer>',
                        help='Average number of images per submodel. When '
                                'splitting a large dataset into smaller '
                                'submodels, images are grouped into clusters. '
                                'This value regulates the number of images that '
                                'each cluster should have on average.')

    parser.add_argument('--split-overlap',
                        type=float,
                        metavar='<positive integer>',
                        default=150,
                        help='Radius of the overlap between submodels. '
                        'After grouping images into clusters, images '
                        'that are closer than this radius to a cluster '
                        'are added to the cluster. This is done to ensure '
                        'that neighboring submodels overlap.')

    parser.add_argument('--sm-cluster',
                        metavar='<string>',
                        type=url_string,
                        default=None,
                        help='URL to a ClusterODM instance '
                            'for distributing a split-merge workflow on '
                            'multiple nodes in parallel. '
                            'Default: %(default)s')

    parser.add_argument('--merge',
                    metavar='<string>',
                    default='all',
                    choices=['all', 'pointcloud', 'orthophoto', 'dem'],
                    help=('Choose what to merge in the merge step in a split dataset. '
                          'By default all available outputs are merged. '
                          'Options: %(choices)s. Default: '
                            '%(default)s'))

    parser.add_argument('--force-gps',
                    action='store_true',
                    default=False,
                    help=('Use images\' GPS exif data for reconstruction, even if there are GCPs present.'
                          'This flag is useful if you have high precision GPS measurements. '
                          'If there are no GCPs, this flag does nothing. Default: %(default)s'))

    args = parser.parse_args()

    # check that the project path setting has been set properly
    if not args.project_path:
        log.ODM_ERROR('You need to set the project path in the '
                      'settings.yaml file before you can run ODM, '
                      'or use `--project-path <path>`. Run `python '
                      'run.py --help` for more information. ')
        sys.exit(1)

    if args.fast_orthophoto:
      log.ODM_INFO('Fast orthophoto is turned on, automatically setting --skip-3dmodel')
      args.skip_3dmodel = True

    if args.dtm and not args.pc_classify:
      log.ODM_INFO("DTM is turned on, automatically turning on point cloud classification")
      args.pc_classify = True

    if args.skip_3dmodel and args.use_3dmesh:
      log.ODM_WARNING('--skip-3dmodel is set, but so is --use-3dmesh. --skip-3dmodel will be ignored.')
      args.skip_3dmodel = False

    if args.orthophoto_cutline and not args.crop:
      log.ODM_WARNING("--orthophoto-cutline is set, but --crop is not. --crop will be set to 0.01")
      args.crop = 0.01

    if args.sm_cluster:
        try:
            Node.from_url(args.sm_cluster).info()
        except exceptions.NodeConnectionError as e:
            log.ODM_ERROR("Cluster node seems to be offline: %s"  % str(e))
            sys.exit(1)

    return args
Ejemplo n.º 20
0
import os
from pyodm import Node
import time
from tqdm import tqdm

image_dir = 'C:\\Users\\YFZX\\Desktop\\pinjie\\images\\'
result_path = image_dir.split('/')[0] + '_results'
ip = '192.168.99.100'  # 改为自己的ip
port = 3000

start = time.time()
images_name = os.listdir(image_dir)
images_name = [image_dir + image_name for image_name in images_name]
print(images_name)

n = Node(ip, port)
print("Node连接成功,{}张图开始处理".format(len(images_name)))

task = n.create_task(
    images_name, {
        "dsm": False,
        'orthophoto-resolution': 0.0274,
        "ignore-gsd": True,
        "min-num-features": 350
    })
#task = n.create_task(images_name, {'orthophoto-resolution': 0.0274,"min-num-features":3500})
print("任务创建完成")
pbar = tqdm(total=100)
processing = 0
while True:
    info = task.info()