a `§»eí~ã@sddZddlZddlZddlZddlZddlZddlZddlZddlZ ddl Z ddl m Z ddl m Z ddl mZddl mZddl mZddl mZdd l mZdd lmZmZdd lmZdd lmZdd lmZddlmZddlmZddlm Z ddl!m"Z"ddl#m$Z$m%Z%ddlm&Z&m'Z'ddl(m)Z)Gdd„dƒZ*ddd„Z+dd„Z,dd„Z-dd„Z.dS) z OpenSfM related utils éN)ÚCRS)Úio)Úlog)Úsystem)Úcontext)Úcamera)Úlocation)Úfind_largest_photo_dimsÚfind_largest_photo)Ú metadataset)Útools)Ú undistort)ÚDataSet)ÚReconstruction)Úreport)Úget_photos_by_band)Ú"has_popsift_and_can_handle_texsizeÚhas_gpu)Ú multiviewÚexif)Ú _transformc@seZdZdd„Zdd„Zdd„Zd@dd „ZdAd d „Zd d „Zgdfdd„Z dd„Z dd„Z dBdd„Z dCdd„Z dd„ZdDdd„ZdEdd„Zdd„Zd d!„Zd"d#„ZdFd$d%„ZdGd(d)„Zd*d+„Zd,d-„Zd.d/„Zd0d1„Zd2d3„Zd4d5„ZdHd6d7„ZdId8d9„Zd:d;„Zdd?„Z d&S)JÚ OSFMContextcCs ||_dS©N)Úopensfm_project_path)Úselfr©rú/code/opendm/osfm.pyÚ__init__szOSFMContext.__init__cCs,tj tjdd¡}t d|||jf¡dS)NÚbinÚopensfmz "%s" %s "%s")ÚosÚpathÚjoinrÚ opensfm_pathrÚrunr)rÚcommandZosfm_binrrrr$ s ÿzOSFMContext.runcCs4tj |jd¡}tj |jd¡}t |¡o2t |¡S)Nú tracks.csvúreconstruction.json)r r!r"rrÚ file_exists)rÚ tracks_fileÚreconstruction_filerrrÚis_reconstruction_done%sz"OSFMContext.is_reconstruction_doneFcCsFtj |jd¡}| d¡}t |¡r(|r4| d¡nt d|¡dS)Nr&ú rs_done.txtÚ create_tracksz(Found a valid OpenSfM tracks file in: %s) r r!r"rrr(r$rÚ ODM_WARNING)rÚrerunr)Úrs_filerrrr-+s   zOSFMContext.create_trackscCsÄtj |jd¡}t |¡r|r6| d¡|rD| ¡nt  d|¡|  ¡sVt   d¡‚|rÀ| d¡}t |¡rr|r¶| d¡t  d¡| d¡| d¡|jd |dd | |¡n t  d ¡dS) Nr'Ú reconstructz0Found a valid OpenSfM reconstruction file in: %szÚThe program could not process this dataset using the current settings. Check that the images have enough overlap, that there are enough recognizable features and that the images are in focus. The program will now exit.r,Z rs_correctz&Re-running the reconstruction pipelineTF)Úrolling_shutter_correctÚ merge_partialr/z*Rolling shutter correction already applied)r r!r"rrr(r$Ú#check_merge_partial_reconstructionsrr.Ú reconstructedrÚ ExitExceptionÚODM_INFOÚmatch_featuresr-r1Útouch)rr2r3r/r*r0rrrr14s$         zOSFMContext.reconstructc Cs¾| ¡rºt|jƒ}| ¡}| ¡}t|ƒdkrºt dt|ƒ¡t d¡t ƒ}|  |dj ¡t |ƒD]>\}}|j |j kr„qlt d|¡|j  ¡D]}| |¡qœ|j ¡D]l}z| |j|j¡} |j| _Wq¶ty } z0t d| jt| ƒf¡WYd} ~ q¶WYd} ~ q¶d} ~ 00q¶|j ¡D]|} | | ¡z| | j¡} Wn*tyvt d| j¡Yq.Yn0|  ¡D]&\} }| |jvr€| | j| |¡q€q.ql| |g¡dS)NézrMultiple reconstructions detected (%s), this might be an indicator that some areas did not have sufficient overlapzAttempting mergerzMerging reconstruction %szCannot merge shot id %s (%s)z'Shot id %s missing from tracks_manager!)r5rrZload_reconstructionZload_tracks_managerÚlenrr.r7rÚ set_referenceÚ referenceÚ enumerateÚcamerasÚvaluesÚ add_cameraÚpointsÚ create_pointÚidÚ coordinatesÚcolorÚ RuntimeErrorÚstrÚshotsÚadd_shotÚget_shot_observationsÚitemsÚadd_observationZsave_reconstruction)rÚdataÚreconstructionsÚtracks_managerÚmergedZix_rÚrecrÚpointZ new_pointÚeÚshotZobsdictÚtrack_idÚobsrrrr4UsB      $   z/OSFMContext.check_merge_partial_reconstructionsc)Cs|rt |j¡rt |j¡t |j¡s4t |j¡tj  |jd¡}t  |¡rT|rö|j r¤t |j |j ƒ}t|ƒdkr†td|j  ¡ƒ‚t dt|ƒ|j  ¡f¡n|j}d}d} d} t|dƒr} |D]\} | jd urÚd} n| jdkrì|d7}| jd ur| jd urd} |  d tj  || j¡¡qÆWd ƒn1s:0Y| rj|t|ƒd krjt d ¡d} tj  |jd ¡} d|vr’tj |j¡} t  | ¡rÌtj  |jd ¡}t | |¡t d| |f¡|jrpzbt  !|j¡}ttj  |jd¡dƒ }| t" #|¡¡Wd ƒn1s 0Yt d¡Wn8tyn}zt dt$|ƒ¡WYd }~n d }~00g}|D].}|j%d urx| &|jtj  ||j%¡f¡qx|rt dt|ƒ¡ttj  |jd¡dƒ2}|D]\}}| d '||¡¡qÜWd ƒn1s0Yd}ddddddœ}t(|ƒ}|d urÖ|\}}t)||ƒ}t dt$|ƒ¡d}d}||d}d}|d krŒd }n|d!kršd}t*d||j+|ƒ} t*|t)|t,|| ƒƒƒ}t d"|¡n t d#¡|j-dkrød}!|j-}"nd$}!d}"t|ƒd%krd}!d%}"d&d'd(|d)|j.d*|j/d+|"d,d-|!d.|j0rLd/nd0d1|j1d2d3d4d5d6d7g}#|j2dkrš| 3¡s|# &d8|j2¡n t d9¡|j4d:krº|# &d;|j4 5¡¡|j6}$|j7 5¡}%dd?œ}&| sôd@|vrôt dA¡dB}$|$dBkr|%dCkrt dD¡dC}%|# &dE|&|$¡t8|ƒrª|d urª|\}}||krht,|||ƒ}t,|ƒ}nt,|||ƒ}t,|ƒ}t9||ƒrª|%dFkrªt dG¡dH}%d|_:|# &dI|%¡| rÒt dJ¡|# &dK¡|j;j<}'| sæ|'rò|# &dL¡n |# &dM¡|j=r.t dN¡|# &dO¡|# &dP¡|# &dQ¡n |# &dR¡|'rx|# &dS¡|j>s\|# &dT¡n |# &dU¡t |'| dV¡¡|#|}#t |#¡| ?¡}(t|(dƒ } |  dW  |#¡¡Wd ƒn1sÄ0Y| 3¡r| @|jAjB|jAjC|jA D¡¡nt dX|¡d S)Yz) Setup a OpenSfM project zimage_list.txtr:z%Not enough images in selected band %sz.Reconstruction will use %s images from %s bandrTFÚwNz%s gš™™™™™©?zrMore than 5% of images have zero altitude, this might be an indicator that the images have no altitude informationzimage_groups.txtZsplit_image_groups_is_setzCopied %s to %szcamera_models_overrides.jsonz7Wrote camera_models_overrides.json to OpenSfM directoryz+Cannot set camera_models_overrides.json: %szFound %s image masksz mask_list.txtz{} {} igà?gÐ?gÀ?gHáz®G±?)ÚultraÚhighÚmediumÚlowÚlowestzMaximum photo dimensions: %spxi@i€g€„.Aéé*z-Photo dimensions for feature extraction: %ipxz8Cannot compute max image dimensions, going with defaultsé2ézuse_exif_size: nozflann_algorithm: KDTREEzfeature_process_size: %szfeature_min_frames: %sz processes: %szmatching_gps_neighbors: %szmatching_gps_distance: 0zmatching_graph_rounds: %szoptimize_camera_parameters: %sÚnoÚyeszreconstruction_algorithm: %szundistorted_image_format: tifz#bundle_outlier_filtering_type: AUTOzsift_peak_threshold: 0.066z!align_orientation_prior: verticalztriangulation_type: ROBUSTzretriangulation_ratio: 2zmatching_order_neighbors: %sz6Georeferenced reconstruction, ignoring --matcher-orderÚautozcamera_projection_type: %sZWORDSZFLANNZ BRUTEFORCE)ÚbowÚflannÚ bruteforceZmatcher_type_is_setznNo GPS information, using BOW matching by default (you can override this by setting --matcher-type explicitly)reÚHAHOGz9Using BOW matching, will use HAHOG feature type, not SIFTzmatcher_type: %sÚSIFTz&Using GPU for extracting SIFT featuresÚSIFT_GPUzfeature_type: %sz5Altitude data detected, enabling it for GPS alignmentzuse_altitude_tag: yeszalign_method: autozalign_method: orientation_priorz!Enabling hybrid bundle adjustmentzbundle_interval: 100zbundle_new_points_ratio: 1.2zlocal_bundle_radius: 1zlocal_bundle_radius: 0zbundle_use_gcp: yeszbundle_use_gps: nozbundle_compensate_gps_bias: yesz gcp_list.txtÚ z.%s already exists, not rerunning OpenSfM setup)ErÚ dir_existsrÚshutilÚrmtreerÚmkdir_pr r!r"r(Ú multi_camerarÚ primary_bandr;Ú ExceptionÚlowerrr7ÚphotosÚopenÚaltitudeÚlatitudeÚ longitudeÚwriteÚfilenamer.Ú project_pathÚabspathÚsplit_image_groupsÚcopyr?rZget_opensfm_camera_modelsÚjsonÚdumpsrHÚmaskÚappendÚformatr ÚmaxÚminÚfeature_qualityÚintÚmatcher_neighborsÚmin_num_featuresÚmax_concurrencyÚuse_fixed_camera_paramsÚ sfm_algorithmÚ matcher_orderÚis_georeferencedÚ camera_lensÚupperÚ matcher_typeÚ feature_typerrÚgpu_sift_feature_extractionÚgcpÚgcp_pathÚuse_hybrid_bundle_adjustmentÚ force_gpsÚget_config_file_pathÚwrite_reference_llaÚgeorefÚutm_east_offsetÚutm_north_offsetÚproj4))rÚargsÚ images_pathÚreconstructionZ append_configr/Z list_pathrtZ num_zero_altZhas_altÚhas_gpsÚfoutÚphotoZimage_groups_fileZdst_groups_fileZcamera_overridesÚfrTÚmasksÚpÚfnamerÚfeature_process_sizeZfeature_quality_scaleÚmax_dimsrXÚhÚmax_dimÚ lower_limitÚ upper_limitÚ megapixelsÚ multiplierÚfactorZmatcher_graph_roundsrˆÚconfigr‘r’Z osfm_matchersr•Zconfig_filenamerrrÚsetups>       <     0(   6û       ð     ý                      0 zOSFMContext.setupcCstj |jd¡S)Nz config.yaml©r r!r"r©rrrrr˜dsz OSFMContext.get_config_file_pathcCsXt | d¡¡sdSt| d¡dƒ }| ¡ ¡dkWdƒS1sJ0YdS)Nr'FÚrz[])rr(r!ruÚreadlineÚstrip)rr¤rrrr5gszOSFMContext.reconstructedcCs&| d¡}t |¡r|r"| d¡dS)NrÚextract_metadata)r!rrlr$)rr/Ú metadata_dirrrrr¸ns zOSFMContext.extract_metadatac CsR| d¡}t |¡r*|s*t d|¡dSt |¡r>t |¡tj|ddi}t |j ƒ}|D]‚}|  ||¡} t tj  |d|j¡dƒ$} |  tj| dd¡Wdƒn1s²0Y| ¡} | |vr^t | |¡} | || <q^| ¡rD| ¡} d | vr(|D]"}t | d ¡||<|||_qn|  ¡D]\}}|||<q0| |¡dS) Nrz2%s already exists, not rerunning photo to metadataT)Úexist_okz%s.exifrXé©ÚindentÚall)r!rrlrr.rmrnr ÚmakedirsrrÚto_opensfm_exifrur"rzryrr€Ú camera_idrZcamera_from_exif_metadataZcamera_models_overrides_existsZload_camera_models_overridesr~rDrLZsave_camera_models)rrtÚrolling_shutterÚrolling_shutter_readoutr/r¹Z camera_modelsrNr¦Údr¤rÁrÚ overridesÚkeyÚvaluerrrÚphotos_to_metadatass4     2     zOSFMContext.photos_to_metadatacCs(| d¡}| d¡}t |¡o&t |¡S)NÚfeaturesÚmatches)r!rrl)rÚ features_dirÚ matches_dirrrrÚis_feature_matching_done˜s  z$OSFMContext.is_feature_matching_donec Cs´| d¡}t |¡r|r˜z| d¡Wq¦tjy”}zTt|dƒr|t d¡|  ddi¡t j  |¡rpt   |¡| d¡n|‚WYd}~q¦d}~00nt d|¡| |¡dS)NrÉZdetect_featuresr“z`GPU SIFT extraction failed, maybe the graphics card is not supported? Attempting fallback to CPUr’riz'Detect features already done: %s exists)r!rrlr$rÚSubprocessExceptionÚhasattrrr.Ú update_configr Úexistsrmrnr8)rr/rËrTrrrÚfeature_matchingžs      zOSFMContext.feature_matchingcCs6| d¡}t |¡r|r$| d¡nt d|¡dS)NrÊr8z&Match features already done: %s exists)r!rrlr$rr.)rr/rÌrrrr8µs  zOSFMContext.match_featurescCsp| d¡}t |¡r|r^t d¡t |j¡}t  |¡}t  |tj d¡}t  |¡|  |¡nt d|¡dS)Nzalignment_done.txtzAligning submodels...Fz+Found a alignment done progress file in: %s)r!rr(rr7r Z MetaDataSetrr Zload_reconstruction_shotsÚalign_reconstructionsZpartial_reconstruction_nameZapply_transformationsr9r.)rr/Zalignment_fileZ meta_dataZreconstruction_shotsZtransformationsrrrrÓ¼s    þ  z!OSFMContext.align_reconstructionscCs8t|dƒ}| d¡Wdƒn1s*0YdS)NrXzDone! )rury)rÚfiler¢rrrr9Ës zOSFMContext.touchcGstjj|jg|¢RŽSrr³)rÚpathsrrrr!ÏszOSFMContext.pathc Cs®tj |¡r|r zR| d¡}t|dƒ*}| tjt |¡dd¡Wdƒn1sV0YWqªt yœ}z"t   d|t |ƒf¡WYd}~qªd}~00n t   d¡dS)Nr'rXr»r¼z Cannot export cameras to %s. %s.zAlready extracted cameras)r r!rÑruryrr€rZget_cameras_from_opensfmrrrr.rHr7)rÚoutputr/r*r¢rTrrrÚextract_camerasÒs  <.zOSFMContext.extract_camerasNÚnominalcCs|t d|j¡| dd|¡}t |¡r.|rjt|jƒ}|durJ| |¡t  |dddd|¡|  |¡nt  d|¡dS)NzUndistorting %s ...Ú undistortedz %s_done.txtr'rzAlready undistorted (%s)) rr7rr!rr(rZ_set_image_listr Z run_datasetr9r.)rr/Z imageFilterZ image_listÚrunIdZdone_flag_fileÚdsrrrÚconvert_and_undistortÝs  ÿ z!OSFMContext.convert_and_undistortcCsPtj | ¡¡rLtj | ¡¡r.t | ¡¡t | ¡| ¡¡t d¡dS)NzRestored reconstruction.json) r r!rÑÚrecon_backup_fileÚ recon_fileÚremoveÚreplacerr7r´rrrÚrestore_reconstruction_backupîs z)OSFMContext.restore_reconstruction_backupcCs@tj | ¡¡rt | ¡¡t d¡t |  ¡| ¡¡dS)NzBacking up reconstruction) r r!rÑrÝrßrr7rmÚcopyfilerÞr´rrrÚbackup_reconstruction÷s z!OSFMContext.backup_reconstructioncCs | d¡S)Nzreconstruction.backup.json©r!r´rrrrÝþszOSFMContext.recon_backup_filecCs | d¡S)Nr'rär´rrrrÞszOSFMContext.recon_filec CsÚt| ¡ƒ}t | ¡¡}Wdƒn1s00Y|D]X}|d}t|ƒ}|D]>}| |¡}|dur|t d|¡qV|D]} |||| j <q€qVq>t| ¡dƒ }|  t  |¡¡Wdƒn1sÌ0YdS)NrIz#Cannot find secondary photos for %srX) rurÞrÚloadsÚreadÚlistÚgetrr.rzryr€) rÚp2sr¤r ÚreconrIZsidsÚshot_idZsecondary_photosr¦rrrÚadd_shots_to_reconstructions, z'OSFMContext.add_shots_to_reconstructionc Cs| ¡}t d|¡tj |¡rz t|ƒ}t |¡}Wdƒn1sN0Y|  ¡D]"\}}|||<t d||f¡q`t|dƒ$}|  tj |dd¡Wdƒn1s¸0YWn:t yþ}z"t  d|t|ƒf¡WYd}~n d}~00nt  d|¡dS)Nz Updating %sz%s: %srXF)Údefault_flow_stylez'Cannot update configuration file %s: %sz5Tried to update configuration, but %s does not exist.)r˜rr7r r!rÑruÚyamlÚ safe_loadrLryÚdumprrr.rH) rZcfg_dictZcfg_fileÚfinZcfgÚkÚvr¢rTrrrrÐs ( 6.zOSFMContext.update_configcCsDt d¡| dd¡}tj |¡r&|r2| d¡nt d|¡dS)NzExport reconstruction statsÚstatsz stats.jsonz.compute_statistics --diagram_max_points 100000z&Found existing reconstruction stats %s)rr7r!r rÑr$r.)rr/Z stats_pathrrrÚ export_stats+s    zOSFMContext.export_statscCs t d|¡| dd¡}tj |¡r*|rŽt|jƒ}t ||¡}|  ¡|  d¡tj |¡r‚tj |¡rtt  |¡t   ||¡qœt d¡nt d|¡dS)NzExporting report to %srôz report.pdfzReport could not be generatedzReport %s already exported)rr7r!r rÑrrrZReportZgenerate_reportZ save_reportÚunlinkrmÚmover.)rZ report_pathZ odm_statsr/Zosfm_report_pathrNZ pdf_reportrrrÚ export_report3s        zOSFMContext.export_reportc Cs‚| d¡}t d¡}t t |¡|||¡\}}t|dƒ,}| tj ||ddœdd¡Wdƒn1sj0Yt   d¡dS) Nzreference_lla.jsonÚ4326rXg)rwrxrvr»r¼zWrote reference_lla.json) r!rÚ from_epsgrÚ transform2Ú from_proj4ruryrr€rr7) rÚoffset_xÚoffset_yrÚ reference_llaÚlonglatÚlonÚlatr¤rrrr™Fs   ýü&zOSFMContext.write_reference_llac CsÚ| dd¡}t |¡sgSi}z:t|ƒ}t | ¡¡}Wdƒn1sL0YWnt d|¡Yn0|s|gSt |j ƒ}|  ¡}t   |¡}g}|D]4} t| d||ƒ} | | d| d| | dd œ¡q |S) z8 Load ground control point information. rôzground_control_points.jsonNzCannot parse %srErDÚ observationsÚerror)rDrrEr)r!rr(rurrårærr7rrZload_referenceÚpyprojÚProjrr‚) rrZgcp_stats_fileZ gcps_statsr¤rÛr=Ú projectionÚresultr”Z geocoordsrrrÚground_control_pointsUs0   0  ü z!OSFMContext.ground_control_pointscCstj tj | d¡¡¡S)Nz..)r r!Úbasenamer|r´rrrÚnameyszOSFMContext.name)F)FFF)F)F)F)F)F)FNNrØ)F)F)!Ú__name__Ú __module__Ú __qualname__rr$r+r-r1r4r²r˜r5r¸rÈrÍrÒr8rÓr9r!r×rÜrárãrÝrÞrìrÐrõrør™r r rrrrrs> !,d  %      $rc sgd¢}gd¢‰ddg}tj}|d}tjdkrJtj |¡}tj |d¡}|g}t|ƒ ¡} dd „|   ¡Dƒ} d | vr†| d =|   d ¡d | vrž| d =|   d ¡‡fd d „| Dƒ} |D]} | | vr´|   | ¡d | | <q´|D]} | | vrØzHt | | t ƒrt | | ¡| | <t | | tƒr*t | | ¡| | <WqØtyf} z t d t | ƒ¡¡WYd} ~ qØd} ~ 00qØd| vr–t| dƒ} | dkrŽd} | | d<| D]P} |  d|  dd¡¡t | | tƒrÖ| | d krÖqš|  t | | ƒ¡qš|r|  d¡|  |¡|r|  |¡|S)af Gets argv for a submodel starting from the args passed to the application startup. Additionally, if project_name, submodels_path and submodel_name are passed, the function handles the value and --project-path detection / override. When all arguments are set to None, --project-path and project name are always removed. :return the same as argv, but removing references to --split, setting/replacing --project-path and name removing --rerun-from, --rerun, --rerun-all, --sm-cluster removing --pc-las, --pc-csv, --pc-ept, --tiles flags (processing these is wasteful) adding --orthophoto-cutline adding --dem-euclidean-map adding --skip-3dmodel (split-merge does not support 3D model merging) tweaking --crop if necessary (DEM merging makes assumption about the area of DEMs and their euclidean maps that require cropping. If cropping is skipped, this leads to errors.) removing --gcp (the GCP path if specified is always "gcp_list.txt") reading the contents of --cameras reading the contents of --boundary )Úorthophoto_cutlineÚdem_euclidean_mapÚ skip_3dmodelÚ skip_report)ÚsplitÚ split_overlapÚ rerun_fromr/r”Úend_withÚ sm_clusterÚ rerun_allÚpc_csvÚpc_lasÚpc_eptÚtileszcopy-toÚcogr?ÚboundaryrÚwin32r$cSs(g|] }| d¡r|dtdƒ …‘qS)Ú_is_setN)Úendswithr;©Ú.0ròrrrÚ  óz%get_submodel_argv..r r{csg|]}|ˆvr|‘qSrrr"©Z remove_alwaysrrr$¬r%TzCannot parse/read JSON: {}NÚcropg?z--%sÚ_Ú-z--project-path)ÚsysÚargvÚplatformr r!Údirnamer"Úvarsr~Úkeysrßr‚Ú isinstancerHrÚpath_or_json_string_to_dictÚdictrr€Ú ValueErrorrr.rƒÚfloatràÚbool)ržÚsubmodels_pathZ submodel_nameZ assure_alwaysZread_json_alwaysr+Zstartup_scriptZstartup_script_dirrÚ args_dictZset_keysròrTZ crop_valuerr&rÚget_submodel_argv|s^       ,      r8cCs°t|ƒ}i}d}|t|ƒkr¬||}|t|ƒdkr8dn ||d}|rˆ| d¡rˆ| d¡rnd||dd…<q¢|||dd…<|d7}n| d¡r¢d||dd…<|d7}q|S)Nrr:z--Tr^)r8r;Ú startswith)ržZ submodel_argvrÚiÚargZnext_argrrrÚget_submodel_args_dictÙs      r<cGsrg}tj |¡s|St |¡D]N}| d¡rtjj||g|¢RŽ}tj |¡rZ| |¡qt d||f¡q|S)z2 :return Existing paths for all submodels ÚsubmodelúMissing %s from submodel %s) r r!rÑÚlistdirr9r"r‚rr.)r6rÕrr¤r¦rrrÚget_submodel_pathsïs    r@csg}tj ˆ¡s|St ˆ¡D]l‰ˆ d¡rd}|D]6}tj ˆˆ|¡}tj |¡s4t d|ˆf¡d}q4|r| ‡‡fdd„|Dƒ¡q|S)aÜ :return Existing, multiple paths for all submodels as a nested list (all or nothing for each submodel) if a single file is missing from the submodule, no files are returned for that submodel. (i.e. get_multi_submodel_paths("path/", "odm_orthophoto.tif", "dem.tif")) --> [["path/submodel_0000/odm_orthophoto.tif", "path/submodel_0000/dem.tif"], ["path/submodel_0001/odm_orthophoto.tif", "path/submodel_0001/dem.tif"]] r=Tr>Fcsg|]}tj ˆˆ|¡‘qSr)r r!r")r#Úap©r¤r6rrr$r%z*get_all_submodel_paths..) r r!rÑr?r9r"rr.r‚)r6Z all_pathsrZ all_foundrAr¦rrBrÚget_all_submodel_pathss    rC)NN)/Ú__doc__r rmr*rÚargparser~rîÚnumpyÚnprrÚopendmrrrrrrÚ opendm.photor r Z opensfm.larger r Zopensfm.actionsr Zopensfm.datasetrZ opensfm.typesrrrZopendm.multispectralrZ opendm.gpurrrrZ opensfm.actions.export_geocoordsrrr8r<r@rCrrrrÚs>0               d ]