__version__ = "2.0" import json import os from meshroom.core import desc class StructureFromMotion(desc.CommandLineNode): commandLine = 'aliceVision_incrementalSfM {allParams}' size = desc.DynamicNodeSize('input') inputs = [ desc.File( name='input', label='Input', description='SfMData file.', value='', uid=[0], ), desc.ListAttribute( elementDesc=desc.File( name="featuresFolder", label="Features Folder", description="", value="", uid=[0], ), name="featuresFolders", label="Features Folders", description="Folder(s) containing the extracted features and descriptors." ), desc.ListAttribute( elementDesc=desc.File( name="matchesFolder", label="Matches Folder", description="", value="", uid=[0], ), name="matchesFolders", label="Matches Folders", description="Folder(s) in which computed matches are stored." ), desc.ChoiceParam( name='describerTypes', label='Describer Types', description='Describer types used to describe an image.', value=['sift'], values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'], exclusive=False, uid=[0], joinChar=',', ), desc.ChoiceParam( name='localizerEstimator', label='Localizer Estimator', description='Estimator type used to localize cameras (acransac, ransac, lsmeds, loransac, maxconsensus).', value='acransac', values=['acransac', 'ransac', 'lsmeds', 'loransac', 'maxconsensus'], exclusive=True, uid=[0], advanced=True, ), desc.IntParam( name='localizerEstimatorMaxIterations', label='Localizer Max Ransac Iterations', description='Maximum number of iterations allowed in ransac step.', value=4096, range=(1, 20000, 1), uid=[0], advanced=True, ), desc.FloatParam( name='localizerEstimatorError', label='Localizer Max Ransac Error', description='Maximum error (in pixels) allowed for camera localization (resectioning).\n' 'If set to 0, it will select a threshold according to the localizer estimator used\n' '(if ACRansac, it will analyze the input data to select the optimal value).', value=0.0, range=(0.0, 100.0, 0.1), uid=[0], advanced=True, ), desc.BoolParam( name='lockScenePreviouslyReconstructed', label='Lock Scene Previously Reconstructed', description='This option is useful for SfM augmentation. Lock previously reconstructed poses and intrinsics.', value=False, uid=[0], ), desc.BoolParam( name='useLocalBA', label='Local Bundle Adjustment', description='It reduces the reconstruction time, especially for large datasets (500+ images),\n' 'by avoiding computation of the Bundle Adjustment on areas that are not changing.', value=True, uid=[0], ), desc.IntParam( name='localBAGraphDistance', label='LocalBA Graph Distance', description='Graph-distance limit to define the Active region in the Local Bundle Adjustment strategy.', value=1, range=(2, 10, 1), uid=[0], advanced=True, ), desc.IntParam( name='maxNumberOfMatches', label='Maximum Number of Matches', description='Maximum number of matches per image pair (and per feature type). \n' 'This can be useful to have a quick reconstruction overview. \n' '0 means no limit.', value=0, range=(0, 50000, 1), uid=[0], ), desc.IntParam( name='minInputTrackLength', label='Min Input Track Length', description='Minimum track length in input of SfM', value=2, range=(2, 10, 1), uid=[0], ), desc.IntParam( name='minNumberOfObservationsForTriangulation', label='Min Observation For Triangulation', description='Minimum number of observations to triangulate a point.\n' 'Set it to 3 (or more) reduces drastically the noise in the point cloud,\n' 'but the number of final poses is a little bit reduced\n' '(from 1.5% to 11% on the tested datasets).', value=2, range=(2, 10, 1), uid=[0], advanced=True, ), desc.FloatParam( name='minAngleForTriangulation', label='Min Angle For Triangulation', description='Minimum angle for triangulation.', value=3.0, range=(0.1, 10, 0.1), uid=[0], advanced=True, ), desc.FloatParam( name='minAngleForLandmark', label='Min Angle For Landmark', description='Minimum angle for landmark.', value=2.0, range=(0.1, 10, 0.1), uid=[0], advanced=True, ), desc.FloatParam( name='maxReprojectionError', label='Max Reprojection Error', description='Maximum reprojection error.', value=4.0, range=(0.1, 10, 0.1), uid=[0], advanced=True, ), desc.FloatParam( name='minAngleInitialPair', label='Min Angle Initial Pair', description='Minimum angle for the initial pair.', value=5.0, range=(0.1, 10, 0.1), uid=[0], advanced=True, ), desc.FloatParam( name='maxAngleInitialPair', label='Max Angle Initial Pair', description='Maximum angle for the initial pair.', value=40.0, range=(0.1, 60, 0.1), uid=[0], advanced=True, ), desc.BoolParam( name='useOnlyMatchesFromInputFolder', label='Use Only Matches From Input Folder', description='Use only matches from the input matchesFolder parameter.\n' 'Matches folders previously added to the SfMData file will be ignored.', value=False, uid=[], advanced=True, ), desc.BoolParam( name='useRigConstraint', label='Use Rig Constraint', description='Enable/Disable rig constraint.', value=True, uid=[0], advanced=True, ), desc.File( name='initialPairA', label='Initial Pair A', description='Filename of the first image (without path).', value='', uid=[0], ), desc.File( name='initialPairB', label='Initial Pair B', description='Filename of the second image (without path).', value='', uid=[0], ), desc.ChoiceParam( name='interFileExtension', label='Inter File Extension', description='Extension of the intermediate file export.', value='.abc', values=('.abc', '.ply'), exclusive=True, uid=[], advanced=True, ), desc.ChoiceParam( name='verboseLevel', label='Verbose Level', description='Verbosity level (fatal, error, warning, info, debug, trace).', value='info', values=['fatal', 'error', 'warning', 'info', 'debug', 'trace'], exclusive=True, uid=[], ) ] outputs = [ desc.File( name='output', label='Output SfMData File', description='Path to the output sfmdata file', value=desc.Node.internalFolder + 'sfm.abc', uid=[], ), desc.File( name='outputViewsAndPoses', label='Output SfMData File', description='''Path to the output sfmdata file with cameras (views and poses).''', value=desc.Node.internalFolder + 'cameras.sfm', uid=[], ), desc.File( name='extraInfoFolder', label='Output Folder', description='Folder for intermediate reconstruction files and additional reconstruction information files.', value=desc.Node.internalFolder, uid=[], ), ] @staticmethod def getViewsAndPoses(node): """ Parse SfM result and return views and poses as two dict with viewId and poseId as keys. """ reportFile = node.outputViewsAndPoses.value if not os.path.exists(reportFile): return {}, {} with open(reportFile) as jsonFile: report = json.load(jsonFile) views = dict() poses = dict() for view in report['views']: views[view['viewId']] = view for pose in report['poses']: poses[pose['poseId']] = pose['pose'] return views, poses