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 connect
function. 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.