open-forest-observatory / automate-metashape-2

Easy, reproducible Metashape photogrammetry workflows
Other
74 stars 26 forks source link

Implement YAML configuration files #2

Closed youngdjn closed 4 years ago

youngdjn commented 4 years ago

This is to allow easy specification of alternate documented configurations for Metashape runs

wildintellect commented 4 years ago

Should include Projection handling. Either Setting in YAML or guessing the appropriate UTM zone when needed. Should the default be EPSG:4326 just to make sure it works everywhere?

youngdjn commented 4 years ago

I think setting projection in YAML is acceptable. I don't think guessing is necessary--that would also make it harder to document/log since the YAML-reading script is not be aware of the project's geospatial data. Defaulting to EPSG:4326 is an acceptable last resort. Most software I'm aware of for working with point cloud data expects X, Y, and Z coordinates in meters -- meaning the user would need to specify a coordinate system such as UTM to achieve this.

wildintellect commented 4 years ago

Usage instructions should guide users on how to find their UTM zone, which should be possible via the EXIF tags from the images (that's actually what I meant by "guessing"). This could be a separate helper script at some point.

youngdjn commented 4 years ago

Currently used functions and the parameters we are currently using (or are likely to use) for them. Also shown are example values (either what we are currently using, or the default if we are not currently using the parameter).

chunk.matchPhotos(accuracy=Metashape.HighAccuracy, preselection=Metashape.GenericPreselection)

    accuracy = Metashape.HighAccuracy

chunk.alignCameras(adaptive_fitting=True)

    adaptive_fitting = True

chunk.optimizeCameras(fit_f=True, fit_cx=True, fit_cy=True, fit_b1=True, fit_b2=True, fit_k1=True, fit_k2=True, fit_k3=True, fit_k4=True, fit_p1=True, fit_p2=True, fit_p3=True, fit_p4=True, adaptive_fitting=True)

    adaptive_fitting = True

chunk.buildDepthMaps(quality=Metashape.MediumQuality, filter=Metashape.MildFiltering)

    quality = Metashape.MediumQuality
    filter = Metashape.MildFiltering
    reuse_depth = True
    max_neighbors = 100

chunk.buildDenseCloud(max_neighbors=60)

    keep_depth = False
    max_neighbors = 100

chunk.dense_cloud.classifyGroundPoints()

    max_angle = 15.0
    max_distance = 1.0
    cell_size = 50.0
    source = None # not sure if this is an acceptable defult meaning "don't interpret this parameter"

chunk.buildDem(projection = project_crs, classes=[Metashape.PointClass.Ground])

    source = Metashape.DenseCloudData
    projection = Metashape.CoordinateSystem("EPSG::32610") # UTM 10N / WGS84. Must be double-quotes
    classes = [Metashape.PointClass.Ground]

chunk.buildOrthomosaic(projection = project_crs, refine_seamlines = True)

    surface = Metashape.ElevationData
    blending = Metashape.MosaicBlending
    fill_holes = True
    refine_seamlines = True
    projection = Metashape.CoordinateSystem("EPSG::32610")

chunk.exportDem(os.path.join(output_path, 'dem_'+project_id+'.tif'), tiff_big = True, tiff_tiled = False, projection = project_crs)

    path = #string
    tiff_big = True
    tiff_tiled = False
    image_format = Metashape.ImageFormatTIFF
    projection = Metashape.CoordinateSystem("EPSG::32610")
    nodata = -32767
    tiff_overviews = True

chunk.exportOrthomosaic(os.path.join(output_path, 'ortho_'+project_id+'.tif'), tiff_big = True, tiff_tiled = False, projection = project_crs)

    path = #string
    tiff_big = True
    tiff_tiled = False
    image_format = Metashape.ImageFormatTIFF
    projection = Metashape.CoordinateSystem("EPSG::32610")
    tiff_overviews = True

chunk.exportPoints(os.path.join(output_path, 'points_'+project_id+'.las'), format = Metashape.PointsFormatLAS, projection = project_crs)

    path = #string
    source = Metashape.DenseCloudData
    precision = 6
    format = Metashape.PointsFormatLAS
    projection = Metashape.CoordinateSystem("EPSG::32610")
    classes = None # Must also accept a list of PointClasstClass

chunk.exportReport(os.path.join(output_path, 'report_'+project_id+'.pdf'))

    path = #string
wildintellect commented 4 years ago

Function read_yaml in read_yaml.py works, but has some bugs around the word Metashape that need to be fixed. See code TODO comments.

youngdjn commented 4 years ago

If the cfg object is referenced for an index that doesn't exist, is there a way to make it return None? That way, we can supply, for example, cfg["alignphotos"]["quality"] to the Metashape function, and if the value is not defined in the configuration YAML, Metashape would use its default (assuming None works in this way in python).

EDIT: No, this is not possible in python. Setting a parameter to None is not equivalent to not passing the parameter at all. For this functionality it is necessary to specify alternative function calls: one with a given parameter and one without, and select which based on the parameter value as specified in the configuration YAML

youngdjn commented 4 years ago

When trying to run cfg = read_yaml.read_yaml("config/example.yml") from the control script

I get: NameError: name 'Metashape' is not defined

youngdjn commented 4 years ago

The NameError is resolved by pull request #11

youngdjn commented 4 years ago

This is completed via #12 except for a bug around supplying a list of non-Metashape object parameters. Creating a separate issue for that one bug.