ODM Tutorial and How To

Hi all,
Because I already had to look at some ODM code and modify it I can tell that it is not easy to jump in. At least it was not easy for me, especially because I am not python fluent. (This is even more true for javascript and as such WebODM is even more cryptic for me).

That is why I would suggest some easy guides/micro-tutorials in order to help new people wanting to contribute. I would be nice to develop the main classes of the code and their structure / how they work (ODM_Stage, ODM_Tree, etc.).

Here is my few findings regarding parameters and stages:

ODM Development

ODM Development

Add a parameter

In order to add a parameter, you have to look in file opendm/config.py, in function config. Make sure to add the new parameter after the check if parser is None:.

New parameter code is the following:

parser.add_argument('--parameter-name',
                    metavar='<path string>',
                    action=StoreValue,
                    type=str,
                    default=None,
                    help='Description of the paremeter. Default: %(default)s')

Parameter Name

The name of the parameter is the first argument here : --parameter-name, when using it inside the code later, the value is accessible through args.parameter_name

Metavar

No idea (Use for human reading in the documentation ?)

Action

Possible values are StoreTrue, StoreValue and RerunFrom.
No sure of the use-case. Boolean parameters seems all to have the StoreTrue value.
All other parameters except --rerun-from use the StoreValue.

Type

Type of the parameter, str, int, float, etc.

Default

Default value of the parameter. Can be any acceptable value of the parameter or None.

Help

The help message describing usefulness of the parameter, how it impacts the computing and the default value if it is relevant.

Possible values

It is possible to give a list of all acceptable values for the parameter, passing a choices parameter array.

rerun.add_argument('--parameter-list',
                       action=RerunFrom,
                       metavar='<string>',
                       choices=['option1','optionD','dummyOption'],
                       default='dummyOption',
                       help=('Rerun processing from this stage. Can be one of: %(choices)s. Default: %(default)s'))

Exclusive parameter group

It is possible to add multiple mutually exclusive parameters, i.e. parameters that can not be passed together to ODM, like the rerun, rerun-all and rerun-from arguments.

To do so, first create the group of parameters:
my_parameter_group = parser.add_mutually_exclusive_group()

Then add the argument to this group instead of directly to the parser:

my_parameter_group.add_argument('--parameter-name',
                    metavar='<path string>',
                    action=StoreValue,
                    type=str,
                    default=None,
                    help='Description of the paremeter. Default: %(default)s')

Add a stage

Stage definition

In order to add a new stage to the pipeline, you should first create a corresponding python file in the stages folder. Let’s say we will make a new stage called “My Stage”.

We will create the file stages/odm_mystage.py.
In this file we will put what is necessary to define a stage:

#definition of the class name, deriving from the ODM_Stage class.
class ODMMyStage(types.ODM_Stage):
    def process(self, args, outputs):
        # Always usefull, retrieve the output values of previous stages
        tree = outputs['tree']

        # We will put here the code of what the stage must do

Stage creation

Now that we made an empty stage, we need to add it to the pipeline stage. Open the stages/odm_app.py and go to the def __init__(self, args): line.

Then we can add the creation of the stage near the others:

mystage = ODMMyStage('my_stage', args, progress=10.0)
  • 'my_stage' string is the name of our new stage
  • args is the list of all arguments passed down to ODM
  • progress is the pourcentage of progress of the task that should be assign at the end of the stage.

Stage pipelining

Now that we defined our stage we must defined its place inside the pipeline. To do so, we will use the connectfunction. Still in the stages/odm_app.py we need to jump to the lines where self.first_stage is used.

If the new stage must became the first stage (not very likely), edit this line and make it:
self.first_stage = mystage.

In must cases we will insert the stage between two existing stages.

Lets take the example of the first part of the pipeline:

dataset.connect(split) \
       .connect(merge) \
       .connect(find) \
       .connect(opensfm)

Here the dataset stage is followed by split, followed by merge, etc.
If we want to insert it between find and opensfm it would become:

dataset.connect(split) \
       .connect(merge) \
       .connect(find) \
       .connect(mystage) \
       .connect(opensfm)

If you want to insert it conditionally look at the use of the fast_orthophoto parameters to insert or not the openmvs stage.

Would be happy to received comments on my current document and perhaps other tips and tricks regarding ODM development.

3 Likes

There’s some notes here: ODM Architecture Overview · OpenDroneMap/ODM Wiki · GitHub

But we’d welcome updates and improvements! :pray:

1 Like

Tank you for this link, I will have a look and try to enrich it the more I understand the global structure of ODM.

2 Likes

@pierotofy, thank you for the pointer. I’ve taken a look at the document and find that it is probably a bit out of date. I don’t understand the code well enough to make any changes, so I’ll just ask a question. The third step in the Task Order is “MVE or SMVS”, which in the “Cell” table should run code smvs.py or mve.py. I cannot find either of these files. Can you point me to the correct files?

Thank you.

1 Like

It’s out of date, MVE/SMVS are no longer in ODM (we now use OpenMVS).

1 Like