11#!/usr/bin/env python
22import argparse
3- import os
4- import sys
53import distutils .util
64import json
5+ import logging
6+ import os
77import re
8+ import sys
89
10+ import meshroom .core .graph
911from meshroom import setupEnvironment
1012
1113setupEnvironment ()
1214
13- import meshroom .core .graph
14- import logging
15-
1615meshroom .core .initPipelines ()
1716
1817parser = argparse .ArgumentParser (
@@ -56,19 +55,25 @@ general_group.add_argument(
5655 metavar = 'FILE.mg / PIPELINE' ,
5756 type = str ,
5857 default = os .environ .get ('MESHROOM_DEFAULT_PIPELINE' , 'photogrammetry' ),
59- help = 'Template pipeline among those listed or a Meshroom file containing a custom pipeline to run on input images:\n '
60- + '\n ' .join ([' - ' + p for p in meshroom .core .pipelineTemplates ])
61- + '\n Requirements: the graph must contain one CameraInit node, and one Publish node if --output is set.' ,
58+ help = 'Template pipeline among those listed or a Meshroom file containing a custom pipeline '
59+ 'to run on input images:\n ' +
60+ '\n ' .join ([' - ' + p for p in meshroom .core .pipelineTemplates ]) +
61+ '\n Requirements: the graph must contain at least one CameraInit node, and at least '
62+ 'one Publish node if --output is set.' ,
6263 )
6364
6465general_group .add_argument (
65- '-o' , '--output' , metavar = 'FOLDER' , type = str , required = False ,
66+ '-o' , '--output' , metavar = 'FOLDER PUBLISH_INSTANCE=FOLDER' ,
67+ type = str , required = False , nargs = '*' ,
6668 help = 'Output folder where results should be copied to. '
69+ 'If the output folder is provided without specifiying the instance of a Publish node, '
70+ 'all the Publish nodes in the scene will be set with the same ouput folder value. '
6771 'If not set, results will have to be retrieved directly from the cache folder.' )
6872
6973general_group .add_argument (
7074 '-s' , '--save' , metavar = 'FILE' , type = str , required = False ,
71- help = 'Save the configured Meshroom graph to a project file. It will setup the cache folder accordingly if not explicitly changed by --cache.' )
75+ help = 'Save the configured Meshroom graph to a project file. It will setup the cache folder '
76+ 'accordingly if not explicitly changed by --cache.' )
7277
7378general_group .add_argument (
7479 '--submit' , help = 'Submit on renderfarm instead of local computation.' ,
@@ -130,7 +135,6 @@ advanced_group.add_argument(
130135
131136args = parser .parse_args ()
132137
133-
134138logStringToPython = {
135139 'fatal' : logging .FATAL ,
136140 'error' : logging .ERROR ,
@@ -154,11 +158,12 @@ with meshroom.core.graph.GraphModification(graph):
154158 # initialize template pipeline
155159 loweredPipelineTemplates = {k .lower (): v for k , v in meshroom .core .pipelineTemplates .items ()}
156160 if args .pipeline .lower () in loweredPipelineTemplates :
157- graph .initFromTemplate (loweredPipelineTemplates [args .pipeline .lower ()], publishOutputs = True if args .output else False )
161+ graph .initFromTemplate (loweredPipelineTemplates [args .pipeline .lower ()],
162+ publishOutputs = True if args .output else False )
158163 else :
159164 # custom pipeline
160165 graph .initFromTemplate (args .pipeline , publishOutputs = True if args .output else False )
161-
166+
162167 def parseInputs (inputs , uniqueInitNode ):
163168 """Utility method for parsing the input and inputRecursive arguments."""
164169 mapInputs = {}
@@ -193,14 +198,14 @@ with meshroom.core.graph.GraphModification(graph):
193198 nodeInputs = inputGroup [- 1 ].split (';' )
194199 mapInputs [nodeName ] = [os .path .abspath (path ) for path in nodeInputs ]
195200 return mapInputs
196-
201+
197202 # get init nodes
198203 initNodes = graph .findInitNodes ()
199204 uniqueInitNode = initNodes [0 ] if (len (initNodes ) == 1 ) else None
200205
201206 # parse inputs for each init node
202207 mapInput = parseInputs (args .input , uniqueInitNode )
203-
208+
204209 # parse recursive inputs for each init node
205210 mapInputRecursive = parseInputs (args .inputRecursive , uniqueInitNode )
206211
@@ -221,15 +226,48 @@ with meshroom.core.graph.GraphModification(graph):
221226 graph .setVerbose (args .verbose )
222227
223228 if args .output :
224- # if there is more than 1 Publish node, they will all be set to the same output;
225- # depending on what they are connected to, some input files might be overwritten in the output folder
226- # (e.g. if two Publish nodes are connected to two Texturing nodes)
229+ # The output folders for Publish nodes can be set as follows:
230+ # - for each node, the output folder is specified following the
231+ # "Publish_name=/output/folder/path" convention.
232+ # - a path is provided without specifying which Publish node should be set with it:
233+ # all the Publish nodes will be set with it.
234+ # - some Publish nodes have their path specified, and another path is provided
235+ # without specifying a node: all Publish nodes with dedicated will have their own
236+ # output folders set, and those which have not been specified will be set with the
237+ # other path.
238+ # - some Publish nodes have their output folder specified while others do not: all
239+ # the nodes with specified folders will use the provided values, and those without
240+ # any will be set with the output folder of the first specified Publish node.
241+ # - several output folders are provided without specifying any node: the last one will
242+ # be used to set all the Publish nodes' output folders.
243+
244+ # Check that there is at least one Publish node
227245 publishNodes = graph .nodesOfType ('Publish' )
228- if len (publishNodes ) > 0 :
229- for node in publishNodes :
230- node .output .value = os .path .abspath (args .output )
231- else :
232- raise RuntimeError ("meshroom_batch requires a pipeline graph with at least one Publish node, none found." )
246+ if len (publishNodes ) == 0 :
247+ raise RuntimeError ('meshroom_batch requires a pipeline graph with at least ' +
248+ 'one Publish node, none found.' )
249+
250+ reExtract = re .compile (r'(\w+)=(.*)' ) # NodeName=value
251+ globalPublishPath = ""
252+ for p in args .output :
253+ result = reExtract .match (p )
254+ if not result : # If the argument is only a path, set it for the global path
255+ globalPublishPath = p
256+ continue
257+
258+ node , value = result .groups ()
259+ for i , n in enumerate (publishNodes ): # Find the correct Publish node in the list
260+ if n .name == node : # If found, set the value, and remove it from the list
261+ n .output .value = value
262+ publishNodes .pop (i )
263+ if globalPublishPath == "" : # Fallback in case some nodes would have no path
264+ globalPublishPath = value
265+ break
266+
267+ for n in publishNodes : # Set the remaining Publish nodes with the global path
268+ n .output .value = globalPublishPath
269+ else :
270+ print (f'No output set, results will be available in the cache folder: "{ graph .cacheDir } "' )
233271
234272 if args .overrides :
235273 with open (args .overrides , encoding = 'utf-8' , errors = 'ignore' ) as f :
@@ -267,9 +305,6 @@ if args.save:
267305 graph .save (args .save , setupProjectFile = not bool (args .cache ))
268306 print (f'File successfully saved: "{ args .save } "' )
269307
270- if not args .output :
271- print (f'No output set, results will be available in the cache folder: "{ graph .cacheDir } "' )
272-
273308# find end nodes (None will compute all graph)
274309toNodes = graph .findNodes (args .toNode ) if args .toNode else None
275310
@@ -278,10 +313,11 @@ if args.submit:
278313 if not args .save :
279314 raise ValueError ('Need to save the project to file to submit on renderfarm.' )
280315 # submit on renderfarm
281- meshroom .core .graph .submit (args .save , args .submitter , toNode = args .toNode , submitLabel = args .submitLabel )
316+ meshroom .core .graph .submit (args .save , args .submitter , toNode = args .toNode ,
317+ submitLabel = args .submitLabel )
282318elif args .compute :
283319 # find end nodes (None will compute all graph)
284320 toNodes = graph .findNodes (args .toNode ) if args .toNode else None
285321 # start computation
286- meshroom .core .graph .executeGraph (graph , toNodes = toNodes , forceCompute = args .forceCompute , forceStatus = args . forceStatus )
287-
322+ meshroom .core .graph .executeGraph (graph , toNodes = toNodes , forceCompute = args .forceCompute ,
323+ forceStatus = args . forceStatus )
0 commit comments