├── .github
└── workflows
│ ├── black.yml
│ ├── isort.yml
│ └── publish-image.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── R
├── normalize_image_intensity.R
└── prep_gcps.R
├── README.md
├── automate-metashape.Rproj
├── calibration
├── RP02-1618153-SC.csv
├── RP02-1622152-SC.csv
├── RP04-1806002-SC.csv
└── RP04-1923118-OB.csv
├── config
└── config-base.yml
├── prior-versions
└── metashape_v1.6-1.8
│ ├── R
│ ├── prep_configs.R
│ └── prep_gcps.R
│ ├── config
│ ├── base.yml
│ ├── derived.yml
│ └── example.yml
│ └── python
│ ├── metashape_workflow.py
│ ├── metashape_workflow_functions.py
│ └── read_yaml.py
├── python
├── metashape_workflow.py
└── metashape_workflow_functions.py
└── settings.json
/.github/workflows/black.yml:
--------------------------------------------------------------------------------
1 | name: Black Linter
2 |
3 | # This allows Black be called from the isort workflow
4 | on:
5 | workflow_call:
6 |
7 | jobs:
8 | black-linter:
9 | name: Run Black
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Determine Branch Name
14 | run: |
15 | if [ "${{ github.event_name }}" = "pull_request" ]; then
16 | echo "BRANCH_NAME=${{ github.head_ref }}" >> $GITHUB_ENV
17 | else
18 | BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})
19 | echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
20 | fi
21 |
22 | - name: Checkout code
23 | uses: actions/checkout@v3
24 | with:
25 | ref: ${{ env.BRANCH_NAME }}
26 |
27 | - name: Pull latest changes
28 | run: git pull origin ${{ env.BRANCH_NAME }}
29 |
30 | - name: Run Black formatter
31 | uses: psf/black@stable
32 | with:
33 | options: "--verbose"
34 | src: "."
35 | jupyter: true
36 |
37 | - name: Push changes
38 | uses: stefanzweifel/git-auto-commit-action@v4
39 | with:
40 | commit_message: Apply black formatting changes
--------------------------------------------------------------------------------
/.github/workflows/isort.yml:
--------------------------------------------------------------------------------
1 | name: Code Formatter
2 |
3 | # Add 'pull_request:' beneath 'on:' to apply on active PRs
4 | on:
5 | push:
6 | branches:
7 | - main
8 |
9 | jobs:
10 | isort:
11 | name: Run Isort
12 | runs-on: ubuntu-latest
13 |
14 | # Redundant to determine branch name since workflow only runs on main, but will be useful if we also want the workflow to run PRs
15 | steps:
16 | - name: Determine Branch Name
17 | run: |
18 | if [ "${{ github.event_name }}" = "pull_request" ]; then
19 | echo "BRANCH_NAME=${{ github.head_ref }}" >> $GITHUB_ENV
20 | else
21 | BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})
22 | echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
23 | fi
24 |
25 | - name: Checkout code
26 | uses: actions/checkout@v3
27 | with:
28 | ref: ${{ env.BRANCH_NAME }}
29 |
30 | - name: Set up Python
31 | uses: actions/setup-python@v2
32 | with:
33 | python-version: 3.9
34 |
35 | - name: Run Isort
36 | run: pip install isort==5.13.2 && isort .
37 |
38 | - name: Push changes
39 | uses: stefanzweifel/git-auto-commit-action@v5
40 | with:
41 | commit_message: Apply isort formatting changes
42 |
43 | # Ensure Black runs after Isort has completed
44 | black:
45 | needs: isort
46 | uses: ./.github/workflows/black.yml
47 |
48 | # Ensure publish-image runs after Black and Isort have completed
49 | publish-image:
50 | needs: [isort, black]
51 | uses: ./.github/workflows/publish-image.yml
--------------------------------------------------------------------------------
/.github/workflows/publish-image.yml:
--------------------------------------------------------------------------------
1 | name: Publish image to Github Packages
2 |
3 | on:
4 | push:
5 | branches-ignore:
6 | # Do not run on pushes to main, because this is separately triggred on pushes to main by the
7 | # isort workflow, and we want to make sure it runs after isort, and not twice
8 | - main
9 | workflow_call:
10 |
11 | env:
12 | REGISTRY: ghcr.io
13 | IMAGE_NAME: ${{ github.repository }}
14 |
15 | jobs:
16 | build-and-push-image:
17 | runs-on: ubuntu-latest
18 |
19 | permissions:
20 | contents: read
21 | packages: write
22 | attestations: write
23 | id-token: write
24 |
25 | steps:
26 | - name: Checkout repository
27 | uses: actions/checkout@v4
28 |
29 | - name: Log in to the Container registry
30 | uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
31 | with:
32 | registry: ${{ env.REGISTRY }}
33 | username: ${{ github.actor }}
34 | password: ${{ secrets.GITHUB_TOKEN }}
35 |
36 | - name: Extract metadata (tags, labels) for Docker
37 | id: meta
38 | uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
39 | with:
40 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
41 | tags: |
42 | # The first 4 lines are default; have to include them in order to specify the
43 | # 5th one (apply latest tag to main branch)
44 | type=schedule
45 | type=ref,event=branch
46 | type=ref,event=tag
47 | type=ref,event=pr
48 | type=raw,value=latest,enable={{is_default_branch}}
49 |
50 | - name: Build and push Docker image
51 | id: push
52 | uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
53 | with:
54 | context: .
55 | push: true
56 | tags: ${{ steps.meta.outputs.tags }}
57 | labels: ${{ steps.meta.outputs.labels }}
58 |
59 | # Diabling attestation for now because it is creating two images in the registry for every
60 | # push and making it look messy
61 |
62 | # - name: Generate artifact attestation
63 | # uses: actions/attest-build-provenance@v1
64 | # with:
65 | # subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
66 | # subject-digest: ${{ steps.push.outputs.digest }}
67 | # push-to-registry: true
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .Rproj.user
2 | .Rhistory
3 | .RData
4 | .Ruserdata
5 | metashape.lic
6 |
7 |
8 | # Created by https://www.gitignore.io/api/python
9 | # Edit at https://www.gitignore.io/?templates=python
10 |
11 | ### Python ###
12 | # Byte-compiled / optimized / DLL files
13 | __pycache__/
14 | *.py[cod]
15 | *$py.class
16 |
17 | # C extensions
18 | *.so
19 |
20 | # Distribution / packaging
21 | .Python
22 | build/
23 | develop-eggs/
24 | dist/
25 | downloads/
26 | eggs/
27 | .eggs/
28 | lib/
29 | lib64/
30 | parts/
31 | sdist/
32 | var/
33 | wheels/
34 | pip-wheel-metadata/
35 | share/python-wheels/
36 | *.egg-info/
37 | .installed.cfg
38 | *.egg
39 | MANIFEST
40 |
41 | # PyInstaller
42 | # Usually these files are written by a python script from a template
43 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
44 | *.manifest
45 | *.spec
46 |
47 | # Installer logs
48 | pip-log.txt
49 | pip-delete-this-directory.txt
50 |
51 | # Unit test / coverage reports
52 | htmlcov/
53 | .tox/
54 | .nox/
55 | .coverage
56 | .coverage.*
57 | .cache
58 | nosetests.xml
59 | coverage.xml
60 | *.cover
61 | .hypothesis/
62 | .pytest_cache/
63 |
64 | # Translations
65 | *.mo
66 | *.pot
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # pyenv
78 | .python-version
79 |
80 | # pipenv
81 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
82 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
83 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
84 | # install all needed dependencies.
85 | #Pipfile.lock
86 |
87 | # celery beat schedule file
88 | celerybeat-schedule
89 |
90 | # SageMath parsed files
91 | *.sage.py
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # Mr Developer
101 | .mr.developer.cfg
102 | .project
103 | .pydevproject
104 |
105 | # mkdocs documentation
106 | /site
107 |
108 | # mypy
109 | .mypy_cache/
110 | .dmypy.json
111 | dmypy.json
112 |
113 | # Pyre type checker
114 | .pyre/
115 |
116 | # End of https://www.gitignore.io/api/python
117 |
118 |
119 | ## apparently added by pycharm
120 | .idea/
121 |
122 | ## dev folder for temp configs etc
123 | dev/
124 |
125 | ## Mac system file
126 | .DS_Store
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use a GPU-enabled base image for Ubuntu 24.04
2 | FROM nvcr.io/nvidia/cuda:12.8.1-runtime-ubuntu24.04
3 |
4 | USER root
5 |
6 | # Adapted from https://github.com/jeffgillan/agisoft_metashape/blob/main/Dockerfile
7 | LABEL authors="David Russell"
8 | LABEL maintainer="djrussell@ucdavis"
9 |
10 | # Create user account with password-less sudo abilities
11 | RUN useradd -s /bin/bash -g 100 -G sudo -m user
12 | RUN /usr/bin/printf '%s\n%s\n' 'password' 'password'| passwd user
13 | RUN echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
14 |
15 | ENV DEBIAN_FRONTEND=noninteractive
16 |
17 | # Install basic dependencies
18 | RUN apt-get update && \
19 | apt-get install -y libglib2.0-dev libglib2.0-0 libgl1 libglu1-mesa libcurl4 wget python3-venv python3-full libgomp1 && \
20 | rm -rf /var/lib/apt/lists/*
21 |
22 | # Download the Metashape .whl file
23 | RUN cd /opt && wget https://download.agisoft.com/Metashape-2.2.0-cp37.cp38.cp39.cp310.cp311-abi3-linux_x86_64.whl
24 |
25 | # Create a virtual environment for Metashape
26 | RUN python3 -m venv /opt/venv_metashape
27 |
28 | # Activate the virtual environment and install Metashape and PyYAML
29 | RUN /opt/venv_metashape/bin/pip install --upgrade pip
30 | RUN /opt/venv_metashape/bin/pip install /opt/Metashape-2.2.0-cp37.cp38.cp39.cp310.cp311-abi3-linux_x86_64.whl
31 | RUN /opt/venv_metashape/bin/pip install PyYAML
32 |
33 | # Remove the downloaded wheel file
34 | RUN rm /opt/*.whl
35 |
36 | # Set the container workdir
37 | WORKDIR /app
38 | # Copy files from current directory into /app
39 | COPY . /app
40 |
41 | # Set the default command and default arguments
42 | ENV PATH="/opt/venv_metashape/bin:${PATH}"
43 | ENTRYPOINT ["python3", "/app/python/metashape_workflow.py"]
44 | CMD ["/data/config.yml"]
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2022, UC Davis
4 | Authors: Derek Young, Alex Mandel, Mallika Nocco, Robert Hijmans, and contributors
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 |
10 | 1. Redistributions of source code must retain the above copyright notice, this
11 | list of conditions and the following disclaimer.
12 |
13 | 2. Redistributions in binary form must reproduce the above copyright notice,
14 | this list of conditions and the following disclaimer in the documentation
15 | and/or other materials provided with the distribution.
16 |
17 | 3. Neither the name of the copyright holder nor the names of its
18 | contributors may be used to endorse or promote products derived from
19 | this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/R/normalize_image_intensity.R:
--------------------------------------------------------------------------------
1 | # Function to take a directory of images and normalize their intensities, saving the normalized images in a new directory named {input_dir}_normalized
2 |
3 | library(magick)
4 | library(stringr)
5 | library(furrr)
6 |
7 | # Function for a single image, to parallelize across within a directory of images
8 | normalize_one_img = function(img_file, img_dir, out_dir) {
9 |
10 | # get the relative path below the specified folder (for saving in same folder structure within the "..._normalized" folder)
11 | rel_path = str_replace(img_file, pattern = fixed(img_dir), replacement = "")
12 |
13 | out_file = file.path(out_dir, rel_path)
14 |
15 | # if already computed for this image, skip
16 | if(file.exists(out_file)) return(FALSE)
17 |
18 | img = image_read(img_file)
19 |
20 | img_norm = image_normalize(img)
21 |
22 | # create dir if doesn't exist
23 | out_dir_img = dirname(out_file)
24 | if(!dir.exists(out_dir_img)) dir.create(out_dir_img, recursive = TRUE)
25 |
26 | image_write(img_norm, out_file)
27 |
28 | gc()
29 |
30 | }
31 |
32 |
33 | # Function to parallelize across images in a directory
34 | normalize_images_in_dir = function(img_dir) {
35 |
36 | # What string to append to the input directory name to store the normalized images
37 | out_dir = paste0(img_dir, "_normalized")
38 | if(!dir.exists(out_dir)) dir.create(out_dir, recursive=TRUE)
39 |
40 | img_files = list.files(img_dir, pattern = "(JPG|jpg|tif|TIF)$", recursive = TRUE, full.names = TRUE)
41 |
42 | gc()
43 |
44 | plan(multisession)
45 |
46 | future_walk(img_files, normalize_one_img, img_dir = img_dir, out_dir = out_dir)
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/R/prep_gcps.R:
--------------------------------------------------------------------------------
1 | ### Author: Derek Young, UC Davis
2 |
3 | ### This script does the following:
4 | ### - Loads a user-created data file of the image files (and coordinates in each image) where GCPs are located
5 | ### - Loads a geospatial file containing the locations of the GCPs
6 | ### - Loads a 10 m DEM and extracts elevation values at each GCP
7 | ### - Compiles all of the above into a file needed by Metashape for its GCP workflow
8 | ### - Produces a PDF that shows each GCP image and the location of the GCP for QAQC
9 |
10 |
11 | ### This script requires the following folders/files in the main mission imagery directory (the one containing 100MEDIA etc):
12 | ### - gcps/raw/gcps.gpkg : geospatial data file with each gcp id in the column "gcp_id". Must be in the same projection as the whole Metashape project, and must be in meters xy (usually a UTM zone).
13 | ### - gcps/raw/gcp_imagecoords.csv : data file listing the images and coordinates in each where a GCP is visible (created manually by technician via imagery inspection)
14 | ### - dem_usgs/dem_usgs.tif : 10 m USGS dem extending well beyond the project flight area
15 |
16 | ### This script assumes all images to be linked to GCPs have standard DJI naming and directory structure ("100MEDIA/DJI_xxxx.JPG").
17 | ### In input data file gcp_imagecoords.csv, images are specified without "DJI_", leading zeros, and ".JPG". The image directory is specified without "MEDIA"
18 |
19 |
20 | #### Load packages ####
21 |
22 | library(sf)
23 | library(raster)
24 | library(dplyr)
25 | library(stringr)
26 | library(magick)
27 | library(ggplot2)
28 |
29 | #### User-defined vars (only used when running interactivesly) ####
30 |
31 | dir_manual = "/home/derek/Downloads/crater_gcps"
32 |
33 |
34 |
35 | #### Load data ####
36 |
37 | ### All relevant GCP data should be in the top-level mission imagery folder
38 | ### Load folder from the command line argument
39 |
40 |
41 | dir = commandArgs(trailingOnly=TRUE)
42 |
43 | if(length(dir) == 0) {
44 | dir = dir_manual
45 | }
46 |
47 | gcps = read_sf(paste0(dir,"/gcps/raw/gcps.geojson"))
48 | imagecoords = read.csv(paste0(dir,"/gcps/raw/gcp_imagecoords.csv"),header=TRUE,stringsAsFactors=FALSE)
49 | dem_usgs = raster(paste0(dir,"/dem_usgs/dem_usgs.tif"))
50 |
51 | # remove blank lines from image coords file
52 | imagecoords = imagecoords %>%
53 | filter(!is.na(x))
54 |
55 |
56 | #### Make prepared data directory if it doesn't ecist ####
57 | dir.create(paste0(dir,"/gcps/prepared"),showWarnings=FALSE)
58 |
59 |
60 |
61 | #### Create GCP table in the format required by metashape_control and metashape_functions ####
62 |
63 | # Extract elev
64 | gcp_table = gcps
65 | gcp_table$elev = suppressWarnings(extract(dem_usgs,gcp_table,method="bilinear"))
66 |
67 | # Extract coords
68 | coords = st_coordinates(gcp_table)
69 | gcp_table = cbind(gcp_table,coords)
70 |
71 | # Remove geospatial info
72 | st_geometry(gcp_table) = NULL
73 |
74 | # Reorder columns, add "point" prefix to GCP names
75 | gcp_table = gcp_table %>%
76 | dplyr::select(gcp_id,x=X,y=Y,elev) %>%
77 | dplyr::mutate(gcp_id = paste0("point",gcp_id))
78 |
79 | write.table(gcp_table,paste0(dir,"/gcps/prepared/gcp_table.csv"),row.names=FALSE,col.names=FALSE,sep=",")
80 |
81 |
82 | #### Create image coordinate-to-gcp table in the format required by metashape_control and metashape_functions ####
83 |
84 | imagecoords_table = imagecoords %>%
85 | mutate(gcp_id = paste0("point",gcp)) %>%
86 | mutate(image_text = paste0("DJI_",str_pad(image_file,4,pad="0"),".JPG")) %>%
87 | mutate(part_text = paste0("PART_",str_pad(part_folder,2,pad="0"))) %>%
88 | mutate(folder_text = paste0(media_folder,"MEDIA")) %>%
89 | mutate(image_path = paste0(part_text,"/",folder_text,"/",image_text)) %>%
90 | dplyr::select(gcp_id,image_path,x,y) %>%
91 | arrange(gcp_id,image_path)
92 |
93 | # remove blank lines from image coords file
94 | imagecoords = imagecoords %>%
95 | filter(!is.na(gcp))
96 |
97 |
98 | write.table(imagecoords_table,paste0(dir,"/gcps/prepared/gcp_imagecoords_table.csv"),row.names=FALSE,col.names=FALSE,sep=",")
99 |
100 |
101 | #### Export a PDF of images with the GCP circled on each ####
102 |
103 |
104 | pdf(paste0(dir,"/gcps/prepared/gcp_qaqc.pdf"))
105 |
106 | for(i in 1:nrow(imagecoords_table)) {
107 |
108 | imagecoords_row = imagecoords_table[i,]
109 |
110 | img = image_read(paste0(dir,"/",imagecoords_row$image_path))
111 | img = image_scale(img,"10%")
112 | img = image_flip(img)
113 |
114 | img_x = imagecoords_row$x/10
115 | img_y = imagecoords_row$y/10
116 | img_gcp = imagecoords_row$gcp_id
117 | img_path = imagecoords_row$image_path
118 |
119 | map = image_ggplot(img) +
120 | geom_point(x=img_x,y=img_y,size=20,pch=1,fill=NA,color="red",stroke=1) +
121 | geom_point(x=img_x,y=img_y,size=20,pch=3,fill=NA,color="red",stroke=0.5) +
122 | labs(title=paste0(img_gcp,"\n",img_path))
123 |
124 | print(map)
125 |
126 | cat("Completed GCP ", i, " of ",nrow(imagecoords_table),"\r")
127 |
128 | }
129 |
130 | garbage = dev.off()
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Easy, reproducible Metashape workflows
2 |
3 | A simple command line tool to automate end-to-end photogrammetry workflows using [Agisoft Metashape](https://www.agisoft.com/). Metashape is proprietary structure-from-motion photogrammetry software that creates 3D point clouds, digital elevation models, mesh models, and orthomosaics from overlapping aerial imagery. Our no-code automation increases the speed of image product creation and makes your workflows fully reproducible and documented. Run parameters are controlled through a single, intuitive configuration file. We offer a [native installation workflow](#native-installation-workflow) as well as a [docker workflow](#docker-workflow). You need to provide **1.** a Metashape license, **2.** your aerial images, and optionally **3.** [ground control points](#preparing-ground-control-points-gcps).
4 |
5 |
6 |
7 |
8 |
9 |
10 | ## Native Installation Workflow
11 |
12 | ### Download and Install Software
13 | **Python:** You need Python 3.7-3.11. We recommend the [Anaconda distribution](https://www.anaconda.com/distribution/) because it includes all the required libraries. When installing, if asked whether the installer should initialize Anaconda3, say "yes". Anaconda must be initialized upon install such that `python` can be called from the command line. A way to check is to simply enter `python` at your command prompt and see if the resulting header info includes Anaconda and Python 3. If it doesn't, you may still need to initialize your Conda install. **Alternative option:** If you want a minimal python installation (such as if you're installing on a computing cluster), you can install [miniconda](https://docs.conda.io/en/latest/miniconda.html) instead. After intalling miniconda, you will need to install additional packages required by our scripts (currently only `PyYAML`) using `pip install {package_name}`.
14 |
15 | **Reproducible workflow scripts (python):** Simply clone this repository to your machine! `git clone https://github.com/open-forest-observatory/automate-metashape.git`
16 |
17 | **Metashape:** You must install the Metashape Python 3 module (Metashape version 2.0). Download the [current .whl file](https://www.agisoft.com/downloads/installer/) and install it following [these instructions](https://agisoft.freshdesk.com/support/solutions/articles/31000148930-how-to-install-metashape-stand-alone-python-module) (using the name of the .whl file that you downloaded). NOTE: If you wish to use an older version of Metashape (v1.6-1.8), the primary scripts here (for v2.0) are not backwards-compatible, but scripts for older versions (with somewhat more limited configuration options) are archived in `prior-versions/`. For the Metashape v1.6-1.8-compatible scripts, you need Python 3.5-3.7.
18 |
19 | **Metashape license:** You need a license (and associated license file) for Metashape. The easiest way to get the license file (assuming you own a license) is by installing the [Metashape Professional Edition GUI software](https://www.agisoft.com/downloads/installer/) (distinct from the Python module) and registering it following the prompts in the software (note you need to purchase a license first). UC Davis users, inquire over the geospatial listserv or the #spatial Slack channel for information on joining a floating license pool. Once you have a license file (whether a node-locked or floating license), you need to set the `agisoft_LICENSE` environment variable (search onilne for instructions for your OS; look for how to *permanently* set it) to the path to the folder containing the license file (`metashape.lic`). On many Linux systems, assuming the Metashape GUI is installed in `/opt/metashape-pro/`, you can set the environment variable with `export agisoft_LICENSE=/opt/metashape-pro/`, though if run directly in a bash terminal it will only be effective during that bash session.
20 |
21 |
22 |
23 | ** Internal OFO developers only: Python and the Metashape python module are pre-installed and ready for use on 'ofo-dev' instances launched from Exosphere and as well as 'Open-Forest-Observatory' template launched from CACAO. The software is installed in a conda environment. `conda activate meta`
24 |
25 |
26 |
27 | ### Organizing raw imagery (and associated files) for processing
28 |
29 | Images should be organized such that there is one root level that contains all the photos from the flight mission to be processed (these photos may optionally be organized within sub-folders), and no other missions. If your workflow is to include the **optional** inputs of _spectral calibration_, [ground control points (GCPs)](#preparing-ground-control-points-gcps), and/or a _USGS DEM_, this root-level folder *must* also contain a corresponding folder for each. For example:
30 |
31 | ```
32 | mission001_photos
33 | ├───100MEDIA
34 | | DJI_0001.JPG
35 | | DJI_0002.JPG
36 | | ...
37 | ├───101MEDIA
38 | | DJI_0001.JPG
39 | | DJI_0002.JPG
40 | | ...
41 | ├───102MEDIA
42 | | DJI_0001.JPG
43 | | DJI_0002.JPG
44 | | ...
45 | ├───gcps
46 | | ...
47 | ├───dem_usgs
48 | | dem_usgs.tif
49 | └───calibration
50 | RP04-1923118-OB.csv
51 | ```
52 |
53 | The namings for the ancillary data folders (`gcps`, `dem_usgs`, and `calibration`) must exactly match these if they are to be a part of the workflow.
54 |
55 | A **sample RGB photo dataset** (which includes GCPs and a USGS DEM) may be [downloaded here](https://ucdavis.box.com/s/hv8m8fibe164pjj0mssdx1mj8qb996k8) (1.5 GB). Note this dataset has sparse photos (low overlap), so photogrammetry results are unimpressive.
56 |
57 |
58 |
59 | ### Workflow configuration
60 |
61 | All of the parameters defining the Metashape workflow are specified in the configuration file (a [YAML-format](https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html) file). This includes directories of input and output files, workflow steps to include, quality settings, and many other parameters.
62 |
63 | An example configuration file is provided in this repo at [`config/config-base.yml`](/config/config-base.yml). Edit the parameter values to meet your specifications. The file contains comments explaining the purpose of each customizable parameter. You can directly edit the `config-base.yml` or save a new copy somewhere on the your local computer. You will specify the path of this config.yml in the [python run command](#running-the-workflow).
64 |
65 | Note: Please do not remove or add parameters to the configuration file; adding will have no effect unless the Python code is changed along with the addition, and removing will produce errors.
66 |
67 |
68 |
69 | ### Running the Workflow
70 |
71 | The general command line call to run the workflow has two required components:
72 | 1. Call to Python
73 | 2. Path to metashape workflow Python script (`metashape_workflow.py`)
74 |
75 | For example:
76 |
77 | `python {repo_path}/python/metashape_workflow.py`
78 |
79 |
80 |
81 | With this minimalist run command, the script assumes your config.yml file is located in the repo at `{repo_path}/config/config-base.yml`
82 |
83 |
84 |
85 | If your config file is located in a different directory, use the optional flag `--config_file`
86 |
87 | For example:
88 |
89 | `python {repo_path}/python/metashape_workflow.py --config_file {config_path}/{config_file}.yml`
90 |
91 |
92 |
93 |
94 | **Additional run command flags**. Using these flags will override parameters specified in the config.yml file.
95 |
96 | `--photo-path` Path to the directory that contains the aerial images (usually jpegs) to be processed
97 |
98 | `--photo-path-secondary` Path to the directory that contains aerial images which are aligned only after all other processing is done. Not commonly used.
99 |
100 | `--project-path` Path where the metashape project file (.psx) will be written
101 |
102 | `--output-path` Path where all imagery products (orthomosaic, point cloud, DEMs, mesh model, reports) will be written
103 |
104 | `--run-name` The identifier for the run. Will be used in naming output files
105 |
106 | `--project-crs` Coordinate reference system EPSG code for outputs. Eg. _EPSG:26910_
107 |
108 |
109 |
110 |
111 |
112 |
113 | #### Running workflow batches
114 |
115 | Running workflows in batch (i.e., multiple workflows in series) on a single computer is as simple as creating configuration file for each workflow run and calling the Python workflow script once for each. The calls can be combined into a shell script. Here is a quick workflow of how to do this:
116 |
117 | 1. Create an empty shell script `touch metashape.sh`
118 | 2. Populate the script with run commands on different lines. Note: the only thing that changes is the name of the config file.
119 |
120 |
121 | ```
122 | python ~/repos/automate-metashape/python/metashape_workflow.py --config_file ~/projects/forest_structure/metashape_configs/config001.yml
123 | python ~/repos/automate-metashape/python/metashape_workflow.py --config_file ~/projects/forest_structure/metashape_configs/config002.yml
124 | python ~/repos/automate-metashape/python/metashape_workflow.py --config_file ~/projects/forest_structure/metashape_configs/config003.yml
125 | ```
126 | 3. Give the shell script file executable permissions ` chmod +x metashape.sh`
127 | 4. Make sure you are located in the directory that contains the shell script. Run the shell script `./metashape.sh`
128 |
129 |
130 |
131 |
132 | ### Workflow outputs
133 |
134 | The outputs of the workflow are the following:
135 | - **Photogrammetry outputs** (e.g., dense point cloud, orthomosaic, digital surface model, and Metashape processing report)
136 | - **A Metashape project file** (for additional future processing or for inspecting the data via the Metashape GUI)
137 | - **A processing log** (which records the processing time for each step and the full set of configuration parameters, for reproducibility)
138 |
139 | The outputs for a given workflow run are named using the following convention: `{run_name}_abc.xyz`. For example: `set14-highQuality_ortho.tif`. The run name and output directories are specified in the configuration file.
140 |
141 |
142 |
143 |
144 |
145 | ---
146 |
147 |
148 |
149 | ## Docker Workflow
150 |
151 | Docker, a type of software containerization, is an alternative way to run software where you don't need to install software in the traditional sense. Docker packages up the code and all its environment dependencies so the application runs reliably from one computer to another. Background information on docker and software containers can be found [here](https://foss.cyverse.org/07_reproducibility_II/).
152 |
153 | To run a docker container on your local machine, you do need to install `docker`. You can install and run docker as a command line tool for [linux distributions](https://docs.docker.com/engine/install/) or as a graphical program (i.e, Docker Desktop) for [windows](https://docs.docker.com/desktop/setup/install/windows-install/), [macOS](https://docs.docker.com/desktop/setup/install/mac-install/), or [linux](https://docs.docker.com/desktop/setup/install/linux/). We recommend running docker commands at the terminal. If you are using Docker Desktop, you can still write commands at the terminal while Docker Desktop is open and running.
154 |
155 | The `automate-metashape` docker image contains the python libraries needed to run the workflow, while you (the user) need to provide at minimum the **1.** aerial images; **2** a configuration file specifying your choices for processing; **3.** a license to use Metashape; and optionally **4.** [ground control points (GCPs)](#preparing-ground-control-points-gcps).
156 |
157 |
158 |
159 | ### User inputs to docker workflow
160 |
161 | To provide the input data to Metashape, you need to specify a folder from your computer to be mirrored ("mounted") within the Docker container. The files needed to run Metashape (the folder of aerial images, the configuration file, and optionally the GCPs file) must all be in the folder that you mount. The files can be located in any arbitrary directory beneath this folder. For example, if the folder you mount is `~/drone_data` on your local computer, the images could be located at `~/drone_data/images/` and the config file could be located at `~/drone_data/config.yml` The folder from your local machine is mounted into the Docker container at the path `/data/`, so for the example above, the images would be found inside the docker container at the path `/data/images/`.
162 |
163 |
164 |
165 | #### Image directory
166 |
167 | The images to be processed should all be in one parent folder (and optionally organized into subfolders beneath this folder) somewhere within the data folder you will be mounting. If including GCPs, spectral calibratation, and/or the USGS DEM, follow the organization shown [here](#organizing-raw-imagery-and-associated-files-for-processing).
168 |
169 |
170 |
171 | #### Workflow configuration file
172 |
173 | An example configuration file is provided in this repository at [`config/config-base.yml`](/config/config-base.yml). Please download this file to your local machine and rename it `config.yml`. By default the container expects the config YAML file describing the Metashape workflow parameters to be located at `/data/config.yaml`. So in a case where the local folder to be mounted is `~/drone_data/`, then for the config file to be mounted in the Docker container at `/data/config.yaml`, it must be located on the local computer at `~/drone_data/config.yaml`. However, the config file location can be overridden by passing a different location following an optional command line argument `--config_file` of the `docker run` command. For more information click [here](#custom-location-of-the-metashape-configuration-file).
174 |
175 | Within the `config.yml` you will need to edit some of the project level parameters to specify where to find input images and where to put output products within the container. Within this config file, all paths will be relative to the file structure of the docker container (beginning with `/data/`). In the config.yaml, at a minimum the following entries should be updated:
176 |
177 | * The value for 'photo_path' should be updated to `/data/{path_to_images_folder_within_mounted_folder}`
178 | * The value for 'output_path' should be updated to `/data/{path_to_desired_ouputs_folder_within_mounted_folder}` (can be any location you want; will be created if it does not exist)
179 | * The value for 'project_path' should be updated similarly as for 'output_path'.
180 |
181 |
182 |
183 | ### Metashape license
184 | Users need to provide a license to use Metashape. Currently, this docker method only supports a floating license server using the format `:`. Within a terminal, users can declare the floating license as an environment variable using the command:
185 |
186 | `export AGISOFT_FLS=:`
187 |
188 | Keep in mind that environment variables will not persist across different terminal sessions.
189 |
190 |
191 |
192 | ### Enable GPUs for accelerated processing
193 |
194 | The use of graphical processing units (GPUs) can greatly increase the speed of photogrammetry processing. If your machine has GPU hardware, you will need extra software so docker can find and use your GPUs. Linux users simply need to install nvidia-container-toolkit via `sudo apt install nvidia-container-toolkit`. For Windows users please see [this documentation](https://docs.docker.com/desktop/features/gpu/). For macOS user, it may not be possible to use your local GPU (Apple Silicon) through Docker.
195 |
196 |
197 |
198 | ### Run the docker container
199 | From a terminal, run this command:
200 |
201 | `docker run -v :/data -e AGISOFT_FLS=$AGISOFT_FLS --gpus all ghcr.io/open-forest-observatory/automate-metashape`
202 |
203 | Here is a breakdown of the command:
204 |
205 | `docker run` is the command to run a docker image
206 |
207 | `-v :/data` is mounting a volume from your local computer into the container. We are mounting your directory that has the imagery and config file () into the container at the path "/data".
208 |
209 | `-e AGISOFT_FLS=$AGISOFT_FLS` is declaring your floating license to use Metashape. We set the license info as an environmental variable earlier in these instructions (i.e., `export AGISOFT_FLS=:`)
210 |
211 | `--gpus all` If the container has access to your local GPUs, use this flag to enable it.
212 |
213 | `ghcr.io/open-forest-observatory/automate-metashape` This is the docker image that has the software to run the `automate-metashape` script. It is located in the Github container registry. When you execute the `docker run...` command, it will download the container image to your local machine and start the script to process imagery using Metashape.
214 |
215 |
216 |
217 | #### Custom location of the Metashape configuration file
218 |
219 | If your config.yaml is located anywhere other than `/data/config.yaml` (or the file is named differently), you can specify its location following one additional command line argument `--config_file` at the end of the `docker run` command. For example, if it is located in the container at `/data/configs/project_10/config.yml` (meanining, in the example above, it is located on your local computer at `~/drone_data/configs/project_10/config.yml`), just append `--config_file /data/configs/project_10/config.yml` to the `docker run` command above. So the command above would look like:
220 |
221 | `docker run -v :/data -e AGISOFT_FLS=$AGISOFT_FLS --gpus all ghcr.io/open-forest-observatory/automate-metashape --config_file /data/configs/project_10/config.yml`
222 |
223 |
224 |
225 | ### Outputs
226 |
227 | As the processing runs, the completed imagery products will be saved into the folder you specified for the `output_path` parameter in the config.yaml. In the example above, if your config.yaml specifies the `output_path` as `/data/{path_to_desired_ouputs_folder_within_mounted_folder}`, the outputs will be saved on your local computer at `~/drone_data/{path_to_desired_ouputs_folder_within_mounted_folder}`.
228 |
229 |
230 |
231 | ### Permissions on Linux
232 |
233 | If running Docker on Linux without `sudo` (as in this example), your user will need to be in the `docker` group. This can be achieved with `sudo usermod -a -G docker $USER` and then logging out and in, as explained [here](https://docs.docker.com/engine/install/linux-postinstall/).
234 |
235 | Note that the owner of the output data will be the `root` user. To set the ownership to your user account, you can run `sudo chown : ` or `sudo chown : -R `.
236 |
237 |
238 |
239 |
240 |
241 | ---
242 |
243 |
244 |
245 | ## Preparing ground-control points (GCPs)
246 |
247 | Because the workflow implemented here is completely GUI-free, it is necessary to prepare GCPs in advance. The process of preparing GCPs involves recording (a) the geospatial location of the GCPs on the earth and (b) the location of the GCPs within the photos in which they appear.
248 |
249 | Metashape requires this information in a very specific format, so this repository includes an R script to assist in producing the necessary files based on more human-readable input. The helper script is `R/prep_gcps.R`.
250 |
251 | **GCP processing input files.** Example GCP input files are included in the [example RGB photo dataset](https://ucdavis.box.com/s/hv8m8fibe164pjj0mssdx1mj8qb996k8) under `gcps/raw/`. The files are the following:
252 | - **gcps.gpkg**: A geopackage (shapefile-like GIS format) containing the locations of each GCP on the earth. Must include an integer column called `gcp_id` that gives each GCP a unique integer ID number.
253 | - **gcp_imagecoords.csv**: A CSV table identifying the locations of the GCPs within raw drone images. Each GCP should be located in at least 5 images (ideally more). The tabls must contain the following columns:
254 | - `gcp`: the integer ID number of the GCP (to match the ID number in `gcps.gpkg`)
255 | - `folder`: the *integer* number of the subfolder in which the raw drone image is located. For example, if the image is in `100MEDIA`, the value that should be recorded is `100`.
256 | - `image`: the *ingeter* number of the image in which the GCP is to be identified. For example, if the image is named `DJI_0077.JPG`, the value that should be recorded is `77`.
257 | - `x` and `y`: the coordinates of the pixel in the image where the GCP is located. `x` and `y` are in units of pixels right and down (respectively) from the upper-left corner.
258 |
259 | These two files must be in `gcps/raw/` at the top level of the flight mission directory (where the subfolders of images also reside). Identification of the image pixel coordinates where the GCPs are located is easy using the info tool in QGIS.
260 |
261 | **Running the script.** You must have R and the following packages installed: sf, raster, dplyr, stringr, magick, ggplot2. The R `bin` directory must be in your system path, or you'll need to use the full path to R. You run the script from the command line by calling `Rscript --vanilla` with the helper script and passing the location of the top-level mission imagery folder (which contains the `gcp` folder) as an argument. For example, on Windows:
262 |
263 | ```
264 | Rscript --vanilla {path_to_repo}/R/prep_gcps.R {path_to_imagery_storage}/sample_rgb_photoset
265 | ```
266 |
267 | **Outputs.** The script will create a `prepared` directory within the `gcps` folder containing the two files used by Metashape: `gcp_table.csv`, which contains the geospatial coordinates of the GCPs on the earth, and `gcp_imagecoords_table.csv`, which contains the pixel coordinates of the GCPs within each image. It also outputs a PDF called `gcp_qaqc.pdf`, which shows the specified location of each GCP in each image in order to quality-control the location data. If left in this folder structure (`gcps/prepared`), the Metashape workflow script will be able to find and incorporate the GCP data if GCPs are enabled in the configuration file.
268 |
269 |
270 |
271 |
272 |
273 |
--------------------------------------------------------------------------------
/automate-metashape.Rproj:
--------------------------------------------------------------------------------
1 | Version: 1.0
2 |
3 | RestoreWorkspace: Default
4 | SaveWorkspace: Default
5 | AlwaysSaveHistory: Default
6 |
7 | EnableCodeIndexing: Yes
8 | UseSpacesForTab: Yes
9 | NumSpacesForTab: 2
10 | Encoding: UTF-8
11 |
12 | RnwWeave: Sweave
13 | LaTeX: pdfLaTeX
14 |
--------------------------------------------------------------------------------
/calibration/RP02-1618153-SC.csv:
--------------------------------------------------------------------------------
1 | 250.0,0.0
2 | 251.0,0.0
3 | 252.0,0.0
4 | 253.0,0.0
5 | 254.0,0.0
6 | 255.0,0.0
7 | 256.0,0.0
8 | 257.0,0.0
9 | 258.0,0.0
10 | 259.0,0.0
11 | 260.0,0.0
12 | 261.0,0.0
13 | 262.0,0.0
14 | 263.0,0.0
15 | 264.0,0.0
16 | 265.0,0.0
17 | 266.0,0.0
18 | 267.0,0.0
19 | 268.0,0.0
20 | 269.0,0.0
21 | 270.0,0.0
22 | 271.0,0.0
23 | 272.0,0.0
24 | 273.0,0.0
25 | 274.0,0.0
26 | 275.0,0.0
27 | 276.0,0.0
28 | 277.0,0.0
29 | 278.0,0.0
30 | 279.0,0.0
31 | 280.0,0.0
32 | 281.0,0.0
33 | 282.0,0.0
34 | 283.0,0.0
35 | 284.0,0.0
36 | 285.0,0.0
37 | 286.0,0.0
38 | 287.0,0.0
39 | 288.0,0.0
40 | 289.0,0.0
41 | 290.0,0.0
42 | 291.0,0.0
43 | 292.0,0.0
44 | 293.0,0.0
45 | 294.0,0.0
46 | 295.0,0.0
47 | 296.0,0.0
48 | 297.0,0.0
49 | 298.0,0.0
50 | 299.0,0.0
51 | 300.0,0.0
52 | 301.0,0.0
53 | 302.0,0.0
54 | 303.0,0.0
55 | 304.0,0.0
56 | 305.0,0.0
57 | 306.0,0.0
58 | 307.0,0.0
59 | 308.0,0.0
60 | 309.0,0.0
61 | 310.0,0.0
62 | 311.0,0.0
63 | 312.0,0.0
64 | 313.0,0.0
65 | 314.0,0.0
66 | 315.0,0.0
67 | 316.0,0.0
68 | 317.0,0.0
69 | 318.0,0.0
70 | 319.0,0.0
71 | 320.0,0.0
72 | 321.0,0.0
73 | 322.0,0.0
74 | 323.0,0.0
75 | 324.0,0.0
76 | 325.0,0.0
77 | 326.0,0.0
78 | 327.0,0.0
79 | 328.0,0.0
80 | 329.0,0.0
81 | 330.0,0.0
82 | 331.0,0.0
83 | 332.0,0.0
84 | 333.0,0.0
85 | 334.0,0.0
86 | 335.0,0.0
87 | 336.0,0.0
88 | 337.0,0.0
89 | 338.0,0.0
90 | 339.0,0.0
91 | 340.0,0.0
92 | 341.0,0.0
93 | 342.0,0.0
94 | 343.0,0.0
95 | 344.0,0.0
96 | 345.0,0.0
97 | 346.0,0.0
98 | 347.0,0.0
99 | 348.0,0.0
100 | 349.0,0.0
101 | 350.0,0.0
102 | 351.0,0.0
103 | 352.0,0.0
104 | 353.0,0.0
105 | 354.0,0.0
106 | 355.0,0.0
107 | 356.0,0.0
108 | 357.0,0.0
109 | 358.0,0.0237
110 | 359.0,0.183
111 | 360.0,0.4767
112 | 361.0,0.6355
113 | 362.0,0.6658
114 | 363.0,0.6776
115 | 364.0,0.689
116 | 365.0,0.6958
117 | 366.0,0.7012
118 | 367.0,0.6951
119 | 368.0,0.6859
120 | 369.0,0.6856
121 | 370.0,0.6872
122 | 371.0,0.6884
123 | 372.0,0.6887
124 | 373.0,0.6902
125 | 374.0,0.6914
126 | 375.0,0.6883
127 | 376.0,0.686
128 | 377.0,0.6823
129 | 378.0,0.6773
130 | 379.0,0.6801
131 | 380.0,0.6875
132 | 381.0,0.6845
133 | 382.0,0.6792
134 | 383.0,0.6802
135 | 384.0,0.6812
136 | 385.0,0.6812
137 | 386.0,0.6814
138 | 387.0,0.6812
139 | 388.0,0.6817
140 | 389.0,0.6846
141 | 390.0,0.6843
142 | 391.0,0.6798
143 | 392.0,0.6767
144 | 393.0,0.6765
145 | 394.0,0.6791
146 | 395.0,0.684
147 | 396.0,0.6879
148 | 397.0,0.6885
149 | 398.0,0.6858
150 | 399.0,0.6833
151 | 400.0,0.6833
152 | 401.0,0.6838
153 | 402.0,0.6854
154 | 403.0,0.6866
155 | 404.0,0.6883
156 | 405.0,0.6907
157 | 406.0,0.6925
158 | 407.0,0.6933
159 | 408.0,0.6935
160 | 409.0,0.6942
161 | 410.0,0.6955
162 | 411.0,0.6975
163 | 412.0,0.6982
164 | 413.0,0.6989
165 | 414.0,0.699
166 | 415.0,0.6991
167 | 416.0,0.6999
168 | 417.0,0.7021
169 | 418.0,0.705
170 | 419.0,0.7068
171 | 420.0,0.7065
172 | 421.0,0.7039
173 | 422.0,0.7024
174 | 423.0,0.703
175 | 424.0,0.7035
176 | 425.0,0.7047
177 | 426.0,0.7068
178 | 427.0,0.7078
179 | 428.0,0.7083
180 | 429.0,0.709
181 | 430.0,0.7086
182 | 431.0,0.707
183 | 432.0,0.7049
184 | 433.0,0.7039
185 | 434.0,0.7036
186 | 435.0,0.7037
187 | 436.0,0.7048
188 | 437.0,0.7055
189 | 438.0,0.7044
190 | 439.0,0.7037
191 | 440.0,0.7034
192 | 441.0,0.7028
193 | 442.0,0.7018
194 | 443.0,0.7016
195 | 444.0,0.7033
196 | 445.0,0.7041
197 | 446.0,0.7031
198 | 447.0,0.7023
199 | 448.0,0.7023
200 | 449.0,0.7032
201 | 450.0,0.7033
202 | 451.0,0.7027
203 | 452.0,0.7029
204 | 453.0,0.7033
205 | 454.0,0.7024
206 | 455.0,0.7011
207 | 456.0,0.7012
208 | 457.0,0.702
209 | 458.0,0.7022
210 | 459.0,0.7028
211 | 460.0,0.7031
212 | 461.0,0.7029
213 | 462.0,0.7028
214 | 463.0,0.7029
215 | 464.0,0.7028
216 | 465.0,0.703
217 | 466.0,0.7031
218 | 467.0,0.7029
219 | 468.0,0.703
220 | 469.0,0.703
221 | 470.0,0.7027
222 | 471.0,0.7026
223 | 472.0,0.7024
224 | 473.0,0.7028
225 | 474.0,0.7038
226 | 475.0,0.7042
227 | 476.0,0.7043
228 | 477.0,0.7042
229 | 478.0,0.7038
230 | 479.0,0.7039
231 | 480.0,0.7048
232 | 481.0,0.7053
233 | 482.0,0.7051
234 | 483.0,0.7049
235 | 484.0,0.705
236 | 485.0,0.7054
237 | 486.0,0.7057
238 | 487.0,0.7058
239 | 488.0,0.7059
240 | 489.0,0.7059
241 | 490.0,0.706
242 | 491.0,0.7064
243 | 492.0,0.7073
244 | 493.0,0.7076
245 | 494.0,0.707
246 | 495.0,0.7069
247 | 496.0,0.7072
248 | 497.0,0.7074
249 | 498.0,0.7076
250 | 499.0,0.7073
251 | 500.0,0.707
252 | 501.0,0.7071
253 | 502.0,0.7079
254 | 503.0,0.7086
255 | 504.0,0.7084
256 | 505.0,0.7073
257 | 506.0,0.7067
258 | 507.0,0.7069
259 | 508.0,0.7071
260 | 509.0,0.7077
261 | 510.0,0.7085
262 | 511.0,0.7089
263 | 512.0,0.7097
264 | 513.0,0.7115
265 | 514.0,0.7121
266 | 515.0,0.711
267 | 516.0,0.7104
268 | 517.0,0.7108
269 | 518.0,0.7105
270 | 519.0,0.7107
271 | 520.0,0.712
272 | 521.0,0.7129
273 | 522.0,0.713
274 | 523.0,0.7123
275 | 524.0,0.7114
276 | 525.0,0.7117
277 | 526.0,0.7125
278 | 527.0,0.713
279 | 528.0,0.7132
280 | 529.0,0.7133
281 | 530.0,0.7137
282 | 531.0,0.7138
283 | 532.0,0.7143
284 | 533.0,0.7149
285 | 534.0,0.7152
286 | 535.0,0.7155
287 | 536.0,0.7154
288 | 537.0,0.7154
289 | 538.0,0.7157
290 | 539.0,0.7162
291 | 540.0,0.7158
292 | 541.0,0.7164
293 | 542.0,0.7173
294 | 543.0,0.7172
295 | 544.0,0.7169
296 | 545.0,0.717
297 | 546.0,0.7172
298 | 547.0,0.7165
299 | 548.0,0.7156
300 | 549.0,0.7153
301 | 550.0,0.7155
302 | 551.0,0.7162
303 | 552.0,0.7172
304 | 553.0,0.718
305 | 554.0,0.7178
306 | 555.0,0.7172
307 | 556.0,0.7168
308 | 557.0,0.7172
309 | 558.0,0.7175
310 | 559.0,0.7175
311 | 560.0,0.7175
312 | 561.0,0.7179
313 | 562.0,0.7184
314 | 563.0,0.7181
315 | 564.0,0.7175
316 | 565.0,0.7168
317 | 566.0,0.7172
318 | 567.0,0.7187
319 | 568.0,0.7191
320 | 569.0,0.7182
321 | 570.0,0.7176
322 | 571.0,0.7173
323 | 572.0,0.7171
324 | 573.0,0.7179
325 | 574.0,0.7187
326 | 575.0,0.7192
327 | 576.0,0.7196
328 | 577.0,0.7194
329 | 578.0,0.7188
330 | 579.0,0.719
331 | 580.0,0.72
332 | 581.0,0.7204
333 | 582.0,0.72
334 | 583.0,0.7192
335 | 584.0,0.7189
336 | 585.0,0.7185
337 | 586.0,0.7175
338 | 587.0,0.7173
339 | 588.0,0.7177
340 | 589.0,0.718
341 | 590.0,0.7191
342 | 591.0,0.7201
343 | 592.0,0.7209
344 | 593.0,0.7214
345 | 594.0,0.722
346 | 595.0,0.7216
347 | 596.0,0.7207
348 | 597.0,0.7205
349 | 598.0,0.7202
350 | 599.0,0.7202
351 | 600.0,0.7209
352 | 601.0,0.7213
353 | 602.0,0.7209
354 | 603.0,0.7204
355 | 604.0,0.7205
356 | 605.0,0.7213
357 | 606.0,0.7214
358 | 607.0,0.7203
359 | 608.0,0.7195
360 | 609.0,0.7198
361 | 610.0,0.7208
362 | 611.0,0.7215
363 | 612.0,0.722
364 | 613.0,0.7218
365 | 614.0,0.7213
366 | 615.0,0.7219
367 | 616.0,0.7218
368 | 617.0,0.7205
369 | 618.0,0.7197
370 | 619.0,0.7197
371 | 620.0,0.7204
372 | 621.0,0.7214
373 | 622.0,0.7219
374 | 623.0,0.7221
375 | 624.0,0.722
376 | 625.0,0.7218
377 | 626.0,0.722
378 | 627.0,0.7226
379 | 628.0,0.723
380 | 629.0,0.7226
381 | 630.0,0.7214
382 | 631.0,0.7202
383 | 632.0,0.7206
384 | 633.0,0.7218
385 | 634.0,0.7227
386 | 635.0,0.7229
387 | 636.0,0.7217
388 | 637.0,0.7203
389 | 638.0,0.72
390 | 639.0,0.7204
391 | 640.0,0.7214
392 | 641.0,0.7225
393 | 642.0,0.7224
394 | 643.0,0.721
395 | 644.0,0.72
396 | 645.0,0.7201
397 | 646.0,0.7207
398 | 647.0,0.7211
399 | 648.0,0.7211
400 | 649.0,0.7205
401 | 650.0,0.72
402 | 651.0,0.7202
403 | 652.0,0.7201
404 | 653.0,0.7205
405 | 654.0,0.721
406 | 655.0,0.7207
407 | 656.0,0.7203
408 | 657.0,0.7202
409 | 658.0,0.72
410 | 659.0,0.7198
411 | 660.0,0.7194
412 | 661.0,0.7186
413 | 662.0,0.7175
414 | 663.0,0.7162
415 | 664.0,0.7148
416 | 665.0,0.7148
417 | 666.0,0.7157
418 | 667.0,0.7161
419 | 668.0,0.7154
420 | 669.0,0.7142
421 | 670.0,0.7138
422 | 671.0,0.714
423 | 672.0,0.7133
424 | 673.0,0.7119
425 | 674.0,0.7119
426 | 675.0,0.713
427 | 676.0,0.7136
428 | 677.0,0.7139
429 | 678.0,0.7135
430 | 679.0,0.712
431 | 680.0,0.7106
432 | 681.0,0.71
433 | 682.0,0.7094
434 | 683.0,0.7087
435 | 684.0,0.7087
436 | 685.0,0.7093
437 | 686.0,0.7101
438 | 687.0,0.7103
439 | 688.0,0.7099
440 | 689.0,0.7089
441 | 690.0,0.7078
442 | 691.0,0.708
443 | 692.0,0.7083
444 | 693.0,0.7086
445 | 694.0,0.7091
446 | 695.0,0.7093
447 | 696.0,0.7092
448 | 697.0,0.709
449 | 698.0,0.7091
450 | 699.0,0.7088
451 | 700.0,0.7081
452 | 701.0,0.7083
453 | 702.0,0.7088
454 | 703.0,0.7088
455 | 704.0,0.7078
456 | 705.0,0.7063
457 | 706.0,0.7057
458 | 707.0,0.7059
459 | 708.0,0.7062
460 | 709.0,0.7068
461 | 710.0,0.7074
462 | 711.0,0.7074
463 | 712.0,0.7067
464 | 713.0,0.7063
465 | 714.0,0.7063
466 | 715.0,0.7063
467 | 716.0,0.706
468 | 717.0,0.7057
469 | 718.0,0.7058
470 | 719.0,0.7059
471 | 720.0,0.7057
472 | 721.0,0.7054
473 | 722.0,0.7051
474 | 723.0,0.7048
475 | 724.0,0.7053
476 | 725.0,0.7056
477 | 726.0,0.7049
478 | 727.0,0.7048
479 | 728.0,0.7052
480 | 729.0,0.705
481 | 730.0,0.7049
482 | 731.0,0.7047
483 | 732.0,0.7045
484 | 733.0,0.7042
485 | 734.0,0.7037
486 | 735.0,0.7024
487 | 736.0,0.7016
488 | 737.0,0.7011
489 | 738.0,0.7003
490 | 739.0,0.6994
491 | 740.0,0.6983
492 | 741.0,0.6978
493 | 742.0,0.6985
494 | 743.0,0.6992
495 | 744.0,0.6994
496 | 745.0,0.6996
497 | 746.0,0.6997
498 | 747.0,0.6991
499 | 748.0,0.6982
500 | 749.0,0.6965
501 | 750.0,0.6953
502 | 751.0,0.6955
503 | 752.0,0.6952
504 | 753.0,0.6939
505 | 754.0,0.6934
506 | 755.0,0.6944
507 | 756.0,0.6942
508 | 757.0,0.6934
509 | 758.0,0.6927
510 | 759.0,0.6925
511 | 760.0,0.6925
512 | 761.0,0.692
513 | 762.0,0.691
514 | 763.0,0.6898
515 | 764.0,0.6889
516 | 765.0,0.6879
517 | 766.0,0.6872
518 | 767.0,0.6865
519 | 768.0,0.6859
520 | 769.0,0.6862
521 | 770.0,0.6864
522 | 771.0,0.6861
523 | 772.0,0.6851
524 | 773.0,0.6849
525 | 774.0,0.685
526 | 775.0,0.6844
527 | 776.0,0.6836
528 | 777.0,0.6817
529 | 778.0,0.68
530 | 779.0,0.6799
531 | 780.0,0.6801
532 | 781.0,0.6793
533 | 782.0,0.678
534 | 783.0,0.6774
535 | 784.0,0.6772
536 | 785.0,0.6774
537 | 786.0,0.6777
538 | 787.0,0.6781
539 | 788.0,0.6772
540 | 789.0,0.6765
541 | 790.0,0.6772
542 | 791.0,0.6772
543 | 792.0,0.676
544 | 793.0,0.674
545 | 794.0,0.6734
546 | 795.0,0.6743
547 | 796.0,0.6748
548 | 797.0,0.674
549 | 798.0,0.6723
550 | 799.0,0.6707
551 | 800.0,0.6701
552 | 801.0,0.6697
553 | 802.0,0.6691
554 | 803.0,0.6694
555 | 804.0,0.6704
556 | 805.0,0.6706
557 | 806.0,0.6698
558 | 807.0,0.6692
559 | 808.0,0.6698
560 | 809.0,0.6712
561 | 810.0,0.6712
562 | 811.0,0.6689
563 | 812.0,0.6657
564 | 813.0,0.6645
565 | 814.0,0.6653
566 | 815.0,0.6674
567 | 816.0,0.668
568 | 817.0,0.6665
569 | 818.0,0.6653
570 | 819.0,0.6654
571 | 820.0,0.6655
572 | 821.0,0.6655
573 | 822.0,0.6666
574 | 823.0,0.667
575 | 824.0,0.665
576 | 825.0,0.6634
577 | 826.0,0.6646
578 | 827.0,0.665
579 | 828.0,0.6644
580 | 829.0,0.6645
581 | 830.0,0.6637
582 | 831.0,0.6631
583 | 832.0,0.6645
584 | 833.0,0.6638
585 | 834.0,0.6612
586 | 835.0,0.6611
587 | 836.0,0.662
588 | 837.0,0.6623
589 | 838.0,0.6615
590 | 839.0,0.6612
591 | 840.0,0.6624
592 | 841.0,0.6635
593 | 842.0,0.6618
594 | 843.0,0.6596
595 | 844.0,0.6585
596 | 845.0,0.6589
597 | 846.0,0.6602
598 | 847.0,0.6605
599 | 848.0,0.6577
600 | 849.0,0.6319
601 | 850.0,0.4742
602 | 851.0,0.1817
603 | 852.0,0.0235
604 | 853.0,0.0
605 | 854.0,0.0
606 | 855.0,0.0
607 | 856.0,0.0
608 | 857.0,0.0
609 | 858.0,0.0
610 | 859.0,0.0
611 | 860.0,0.0
612 | 861.0,0.0
613 | 862.0,0.0
614 | 863.0,0.0
615 | 864.0,0.0
616 | 865.0,0.0
617 | 866.0,0.0
618 | 867.0,0.0
619 | 868.0,0.0
620 | 869.0,0.0
621 | 870.0,0.0
622 | 871.0,0.0
623 | 872.0,0.0
624 | 873.0,0.0
625 | 874.0,0.0
626 | 875.0,0.0
627 | 876.0,0.0
628 | 877.0,0.0
629 | 878.0,0.0
630 | 879.0,0.0
631 | 880.0,0.0
632 | 881.0,0.0
633 | 882.0,0.0
634 | 883.0,0.0
635 | 884.0,0.0
636 | 885.0,0.0
637 | 886.0,0.0
638 | 887.0,0.0
639 | 888.0,0.0
640 | 889.0,0.0
641 | 890.0,0.0
642 | 891.0,0.0
643 | 892.0,0.0
644 | 893.0,0.0
645 | 894.0,0.0
646 | 895.0,0.0
647 | 896.0,0.0
648 | 897.0,0.0
649 | 898.0,0.0
650 | 899.0,0.0
651 | 900.0,0.0
652 |
--------------------------------------------------------------------------------
/calibration/RP02-1622152-SC.csv:
--------------------------------------------------------------------------------
1 | 250.0,0.0
2 | 251.0,0.0
3 | 252.0,0.0
4 | 253.0,0.0
5 | 254.0,0.0
6 | 255.0,0.0
7 | 256.0,0.0
8 | 257.0,0.0
9 | 258.0,0.0
10 | 259.0,0.0
11 | 260.0,0.0
12 | 261.0,0.0
13 | 262.0,0.0
14 | 263.0,0.0
15 | 264.0,0.0
16 | 265.0,0.0
17 | 266.0,0.0
18 | 267.0,0.0
19 | 268.0,0.0
20 | 269.0,0.0
21 | 270.0,0.0
22 | 271.0,0.0
23 | 272.0,0.0
24 | 273.0,0.0
25 | 274.0,0.0
26 | 275.0,0.0
27 | 276.0,0.0
28 | 277.0,0.0
29 | 278.0,0.0
30 | 279.0,0.0
31 | 280.0,0.0
32 | 281.0,0.0
33 | 282.0,0.0
34 | 283.0,0.0
35 | 284.0,0.0
36 | 285.0,0.0
37 | 286.0,0.0
38 | 287.0,0.0
39 | 288.0,0.0
40 | 289.0,0.0
41 | 290.0,0.0
42 | 291.0,0.0
43 | 292.0,0.0
44 | 293.0,0.0
45 | 294.0,0.0
46 | 295.0,0.0
47 | 296.0,0.0
48 | 297.0,0.0
49 | 298.0,0.0
50 | 299.0,0.0
51 | 300.0,0.0
52 | 301.0,0.0
53 | 302.0,0.0
54 | 303.0,0.0
55 | 304.0,0.0
56 | 305.0,0.0
57 | 306.0,0.0
58 | 307.0,0.0
59 | 308.0,0.0
60 | 309.0,0.0
61 | 310.0,0.0
62 | 311.0,0.0
63 | 312.0,0.0
64 | 313.0,0.0
65 | 314.0,0.0
66 | 315.0,0.0
67 | 316.0,0.0
68 | 317.0,0.0
69 | 318.0,0.0
70 | 319.0,0.0
71 | 320.0,0.0
72 | 321.0,0.0
73 | 322.0,0.0
74 | 323.0,0.0
75 | 324.0,0.0
76 | 325.0,0.0
77 | 326.0,0.0
78 | 327.0,0.0
79 | 328.0,0.0
80 | 329.0,0.0
81 | 330.0,0.0
82 | 331.0,0.0
83 | 332.0,0.0
84 | 333.0,0.0
85 | 334.0,0.0
86 | 335.0,0.0
87 | 336.0,0.0
88 | 337.0,0.0
89 | 338.0,0.0
90 | 339.0,0.0
91 | 340.0,0.0
92 | 341.0,0.0
93 | 342.0,0.0
94 | 343.0,0.0
95 | 344.0,0.0
96 | 345.0,0.0
97 | 346.0,0.0
98 | 347.0,0.0
99 | 348.0,0.0
100 | 349.0,0.0
101 | 350.0,0.0
102 | 351.0,0.0
103 | 352.0,0.0
104 | 353.0,0.0
105 | 354.0,0.0
106 | 355.0,0.0
107 | 356.0,0.0
108 | 357.0,0.0
109 | 358.0,0.0245
110 | 359.0,0.1892
111 | 360.0,0.4924
112 | 361.0,0.6512
113 | 362.0,0.6705
114 | 363.0,0.6707
115 | 364.0,0.6742
116 | 365.0,0.6784
117 | 366.0,0.6796
118 | 367.0,0.6754
119 | 368.0,0.6685
120 | 369.0,0.6647
121 | 370.0,0.6646
122 | 371.0,0.666
123 | 372.0,0.6677
124 | 373.0,0.6658
125 | 374.0,0.6633
126 | 375.0,0.6645
127 | 376.0,0.6675
128 | 377.0,0.6669
129 | 378.0,0.6642
130 | 379.0,0.6627
131 | 380.0,0.6621
132 | 381.0,0.6644
133 | 382.0,0.6657
134 | 383.0,0.6631
135 | 384.0,0.6588
136 | 385.0,0.6585
137 | 386.0,0.6622
138 | 387.0,0.6634
139 | 388.0,0.6611
140 | 389.0,0.6599
141 | 390.0,0.6596
142 | 391.0,0.6595
143 | 392.0,0.6601
144 | 393.0,0.6621
145 | 394.0,0.6632
146 | 395.0,0.6633
147 | 396.0,0.6644
148 | 397.0,0.6658
149 | 398.0,0.6662
150 | 399.0,0.6657
151 | 400.0,0.6654
152 | 401.0,0.6661
153 | 402.0,0.6658
154 | 403.0,0.6645
155 | 404.0,0.6651
156 | 405.0,0.6674
157 | 406.0,0.6699
158 | 407.0,0.6707
159 | 408.0,0.6704
160 | 409.0,0.6704
161 | 410.0,0.6708
162 | 411.0,0.6716
163 | 412.0,0.6726
164 | 413.0,0.6738
165 | 414.0,0.675
166 | 415.0,0.6759
167 | 416.0,0.6767
168 | 417.0,0.6774
169 | 418.0,0.6776
170 | 419.0,0.6776
171 | 420.0,0.6785
172 | 421.0,0.6788
173 | 422.0,0.6784
174 | 423.0,0.6786
175 | 424.0,0.6786
176 | 425.0,0.6785
177 | 426.0,0.6786
178 | 427.0,0.6778
179 | 428.0,0.6761
180 | 429.0,0.6758
181 | 430.0,0.6761
182 | 431.0,0.6764
183 | 432.0,0.677
184 | 433.0,0.6774
185 | 434.0,0.6778
186 | 435.0,0.678
187 | 436.0,0.678
188 | 437.0,0.6777
189 | 438.0,0.6775
190 | 439.0,0.6775
191 | 440.0,0.6771
192 | 441.0,0.6766
193 | 442.0,0.676
194 | 443.0,0.6754
195 | 444.0,0.675
196 | 445.0,0.6751
197 | 446.0,0.6761
198 | 447.0,0.6773
199 | 448.0,0.6775
200 | 449.0,0.6757
201 | 450.0,0.674
202 | 451.0,0.6739
203 | 452.0,0.6744
204 | 453.0,0.6741
205 | 454.0,0.6736
206 | 455.0,0.6739
207 | 456.0,0.6742
208 | 457.0,0.674
209 | 458.0,0.6737
210 | 459.0,0.674
211 | 460.0,0.6745
212 | 461.0,0.6741
213 | 462.0,0.6735
214 | 463.0,0.6734
215 | 464.0,0.6736
216 | 465.0,0.674
217 | 466.0,0.6736
218 | 467.0,0.6729
219 | 468.0,0.673
220 | 469.0,0.6733
221 | 470.0,0.673
222 | 471.0,0.6725
223 | 472.0,0.6726
224 | 473.0,0.6732
225 | 474.0,0.6737
226 | 475.0,0.6737
227 | 476.0,0.6736
228 | 477.0,0.6732
229 | 478.0,0.6727
230 | 479.0,0.6727
231 | 480.0,0.6731
232 | 481.0,0.6737
233 | 482.0,0.6743
234 | 483.0,0.675
235 | 484.0,0.6747
236 | 485.0,0.6741
237 | 486.0,0.6745
238 | 487.0,0.675
239 | 488.0,0.675
240 | 489.0,0.6751
241 | 490.0,0.6753
242 | 491.0,0.6751
243 | 492.0,0.6754
244 | 493.0,0.6765
245 | 494.0,0.6769
246 | 495.0,0.6765
247 | 496.0,0.676
248 | 497.0,0.6759
249 | 498.0,0.6764
250 | 499.0,0.6768
251 | 500.0,0.6769
252 | 501.0,0.6772
253 | 502.0,0.6779
254 | 503.0,0.6781
255 | 504.0,0.6782
256 | 505.0,0.678
257 | 506.0,0.678
258 | 507.0,0.6787
259 | 508.0,0.6791
260 | 509.0,0.6787
261 | 510.0,0.6785
262 | 511.0,0.6788
263 | 512.0,0.6792
264 | 513.0,0.6797
265 | 514.0,0.6802
266 | 515.0,0.6807
267 | 516.0,0.681
268 | 517.0,0.681
269 | 518.0,0.6808
270 | 519.0,0.6808
271 | 520.0,0.6814
272 | 521.0,0.6821
273 | 522.0,0.6824
274 | 523.0,0.6821
275 | 524.0,0.6815
276 | 525.0,0.681
277 | 526.0,0.6808
278 | 527.0,0.6816
279 | 528.0,0.6826
280 | 529.0,0.6832
281 | 530.0,0.6836
282 | 531.0,0.6839
283 | 532.0,0.6838
284 | 533.0,0.6835
285 | 534.0,0.6832
286 | 535.0,0.683
287 | 536.0,0.6834
288 | 537.0,0.6837
289 | 538.0,0.6838
290 | 539.0,0.6838
291 | 540.0,0.6839
292 | 541.0,0.6841
293 | 542.0,0.6843
294 | 543.0,0.6844
295 | 544.0,0.6844
296 | 545.0,0.6845
297 | 546.0,0.6851
298 | 547.0,0.6856
299 | 548.0,0.6857
300 | 549.0,0.6857
301 | 550.0,0.6858
302 | 551.0,0.6856
303 | 552.0,0.6858
304 | 553.0,0.6865
305 | 554.0,0.6867
306 | 555.0,0.6869
307 | 556.0,0.6872
308 | 557.0,0.6871
309 | 558.0,0.6872
310 | 559.0,0.6874
311 | 560.0,0.6876
312 | 561.0,0.6871
313 | 562.0,0.6862
314 | 563.0,0.6857
315 | 564.0,0.6857
316 | 565.0,0.6856
317 | 566.0,0.686
318 | 567.0,0.6868
319 | 568.0,0.6873
320 | 569.0,0.6874
321 | 570.0,0.6876
322 | 571.0,0.6878
323 | 572.0,0.6881
324 | 573.0,0.6886
325 | 574.0,0.6892
326 | 575.0,0.6895
327 | 576.0,0.6892
328 | 577.0,0.6883
329 | 578.0,0.6877
330 | 579.0,0.688
331 | 580.0,0.6884
332 | 581.0,0.6888
333 | 582.0,0.6891
334 | 583.0,0.6888
335 | 584.0,0.6889
336 | 585.0,0.689
337 | 586.0,0.6888
338 | 587.0,0.6888
339 | 588.0,0.6888
340 | 589.0,0.6888
341 | 590.0,0.6887
342 | 591.0,0.6886
343 | 592.0,0.6888
344 | 593.0,0.689
345 | 594.0,0.6887
346 | 595.0,0.6882
347 | 596.0,0.6884
348 | 597.0,0.689
349 | 598.0,0.6892
350 | 599.0,0.6892
351 | 600.0,0.6893
352 | 601.0,0.6893
353 | 602.0,0.6894
354 | 603.0,0.6896
355 | 604.0,0.6898
356 | 605.0,0.6899
357 | 606.0,0.6897
358 | 607.0,0.6894
359 | 608.0,0.6893
360 | 609.0,0.6897
361 | 610.0,0.6904
362 | 611.0,0.6907
363 | 612.0,0.6903
364 | 613.0,0.6897
365 | 614.0,0.6896
366 | 615.0,0.6906
367 | 616.0,0.6913
368 | 617.0,0.6914
369 | 618.0,0.6914
370 | 619.0,0.6914
371 | 620.0,0.6912
372 | 621.0,0.691
373 | 622.0,0.6911
374 | 623.0,0.6914
375 | 624.0,0.6915
376 | 625.0,0.6911
377 | 626.0,0.6909
378 | 627.0,0.6915
379 | 628.0,0.6921
380 | 629.0,0.6926
381 | 630.0,0.6926
382 | 631.0,0.6917
383 | 632.0,0.6911
384 | 633.0,0.6917
385 | 634.0,0.6924
386 | 635.0,0.6919
387 | 636.0,0.691
388 | 637.0,0.691
389 | 638.0,0.6911
390 | 639.0,0.691
391 | 640.0,0.6911
392 | 641.0,0.6914
393 | 642.0,0.6916
394 | 643.0,0.6916
395 | 644.0,0.6916
396 | 645.0,0.6914
397 | 646.0,0.6907
398 | 647.0,0.6904
399 | 648.0,0.6907
400 | 649.0,0.6907
401 | 650.0,0.6906
402 | 651.0,0.6905
403 | 652.0,0.6902
404 | 653.0,0.6902
405 | 654.0,0.6902
406 | 655.0,0.6898
407 | 656.0,0.6893
408 | 657.0,0.689
409 | 658.0,0.6885
410 | 659.0,0.6884
411 | 660.0,0.6895
412 | 661.0,0.6902
413 | 662.0,0.6904
414 | 663.0,0.6904
415 | 664.0,0.6899
416 | 665.0,0.6891
417 | 666.0,0.6887
418 | 667.0,0.6881
419 | 668.0,0.6876
420 | 669.0,0.6872
421 | 670.0,0.6866
422 | 671.0,0.6858
423 | 672.0,0.6853
424 | 673.0,0.6846
425 | 674.0,0.6847
426 | 675.0,0.6855
427 | 676.0,0.6854
428 | 677.0,0.6841
429 | 678.0,0.683
430 | 679.0,0.6829
431 | 680.0,0.6832
432 | 681.0,0.6837
433 | 682.0,0.6837
434 | 683.0,0.6835
435 | 684.0,0.6829
436 | 685.0,0.6818
437 | 686.0,0.6814
438 | 687.0,0.6823
439 | 688.0,0.6829
440 | 689.0,0.6828
441 | 690.0,0.6824
442 | 691.0,0.6816
443 | 692.0,0.6809
444 | 693.0,0.6812
445 | 694.0,0.6827
446 | 695.0,0.6837
447 | 696.0,0.6831
448 | 697.0,0.6819
449 | 698.0,0.6814
450 | 699.0,0.6814
451 | 700.0,0.6807
452 | 701.0,0.6805
453 | 702.0,0.6807
454 | 703.0,0.6807
455 | 704.0,0.6807
456 | 705.0,0.6806
457 | 706.0,0.6808
458 | 707.0,0.6811
459 | 708.0,0.6807
460 | 709.0,0.6802
461 | 710.0,0.6803
462 | 711.0,0.6804
463 | 712.0,0.6794
464 | 713.0,0.6786
465 | 714.0,0.6785
466 | 715.0,0.6785
467 | 716.0,0.6786
468 | 717.0,0.6792
469 | 718.0,0.6793
470 | 719.0,0.6785
471 | 720.0,0.6779
472 | 721.0,0.6783
473 | 722.0,0.6796
474 | 723.0,0.6799
475 | 724.0,0.6784
476 | 725.0,0.6773
477 | 726.0,0.6776
478 | 727.0,0.6783
479 | 728.0,0.6793
480 | 729.0,0.6799
481 | 730.0,0.6789
482 | 731.0,0.6774
483 | 732.0,0.6772
484 | 733.0,0.6772
485 | 734.0,0.6772
486 | 735.0,0.6769
487 | 736.0,0.6767
488 | 737.0,0.6766
489 | 738.0,0.6763
490 | 739.0,0.6758
491 | 740.0,0.6753
492 | 741.0,0.6752
493 | 742.0,0.6749
494 | 743.0,0.6745
495 | 744.0,0.6744
496 | 745.0,0.6747
497 | 746.0,0.6747
498 | 747.0,0.6732
499 | 748.0,0.6722
500 | 749.0,0.6718
501 | 750.0,0.6713
502 | 751.0,0.6711
503 | 752.0,0.6717
504 | 753.0,0.6721
505 | 754.0,0.6715
506 | 755.0,0.6708
507 | 756.0,0.6707
508 | 757.0,0.6707
509 | 758.0,0.6701
510 | 759.0,0.6697
511 | 760.0,0.6693
512 | 761.0,0.6688
513 | 762.0,0.6684
514 | 763.0,0.6681
515 | 764.0,0.6678
516 | 765.0,0.6678
517 | 766.0,0.6675
518 | 767.0,0.6667
519 | 768.0,0.6662
520 | 769.0,0.6665
521 | 770.0,0.6666
522 | 771.0,0.6663
523 | 772.0,0.6651
524 | 773.0,0.6642
525 | 774.0,0.6639
526 | 775.0,0.6636
527 | 776.0,0.6631
528 | 777.0,0.6625
529 | 778.0,0.6623
530 | 779.0,0.6619
531 | 780.0,0.6614
532 | 781.0,0.661
533 | 782.0,0.6605
534 | 783.0,0.6605
535 | 784.0,0.6606
536 | 785.0,0.6599
537 | 786.0,0.6588
538 | 787.0,0.6585
539 | 788.0,0.6585
540 | 789.0,0.6578
541 | 790.0,0.6575
542 | 791.0,0.6574
543 | 792.0,0.6568
544 | 793.0,0.6562
545 | 794.0,0.6553
546 | 795.0,0.6547
547 | 796.0,0.6549
548 | 797.0,0.655
549 | 798.0,0.6551
550 | 799.0,0.6556
551 | 800.0,0.6555
552 | 801.0,0.6546
553 | 802.0,0.6541
554 | 803.0,0.6541
555 | 804.0,0.6536
556 | 805.0,0.653
557 | 806.0,0.6518
558 | 807.0,0.6517
559 | 808.0,0.6522
560 | 809.0,0.6517
561 | 810.0,0.6504
562 | 811.0,0.6499
563 | 812.0,0.6505
564 | 813.0,0.6514
565 | 814.0,0.6515
566 | 815.0,0.6511
567 | 816.0,0.6503
568 | 817.0,0.6496
569 | 818.0,0.6494
570 | 819.0,0.6497
571 | 820.0,0.6505
572 | 821.0,0.6508
573 | 822.0,0.6497
574 | 823.0,0.648
575 | 824.0,0.647
576 | 825.0,0.6468
577 | 826.0,0.6459
578 | 827.0,0.6454
579 | 828.0,0.6467
580 | 829.0,0.6474
581 | 830.0,0.6473
582 | 831.0,0.6481
583 | 832.0,0.6491
584 | 833.0,0.6486
585 | 834.0,0.6472
586 | 835.0,0.6463
587 | 836.0,0.6447
588 | 837.0,0.6431
589 | 838.0,0.6431
590 | 839.0,0.6443
591 | 840.0,0.6459
592 | 841.0,0.6461
593 | 842.0,0.6448
594 | 843.0,0.6443
595 | 844.0,0.6442
596 | 845.0,0.6437
597 | 846.0,0.6446
598 | 847.0,0.6473
599 | 848.0,0.6486
600 | 849.0,0.6251
601 | 850.0,0.4684
602 | 851.0,0.1791
603 | 852.0,0.0231
604 | 853.0,0.0
605 | 854.0,0.0
606 | 855.0,0.0
607 | 856.0,0.0
608 | 857.0,0.0
609 | 858.0,0.0
610 | 859.0,0.0
611 | 860.0,0.0
612 | 861.0,0.0
613 | 862.0,0.0
614 | 863.0,0.0
615 | 864.0,0.0
616 | 865.0,0.0
617 | 866.0,0.0
618 | 867.0,0.0
619 | 868.0,0.0
620 | 869.0,0.0
621 | 870.0,0.0
622 | 871.0,0.0
623 | 872.0,0.0
624 | 873.0,0.0
625 | 874.0,0.0
626 | 875.0,0.0
627 | 876.0,0.0
628 | 877.0,0.0
629 | 878.0,0.0
630 | 879.0,0.0
631 | 880.0,0.0
632 | 881.0,0.0
633 | 882.0,0.0
634 | 883.0,0.0
635 | 884.0,0.0
636 | 885.0,0.0
637 | 886.0,0.0
638 | 887.0,0.0
639 | 888.0,0.0
640 | 889.0,0.0
641 | 890.0,0.0
642 | 891.0,0.0
643 | 892.0,0.0
644 | 893.0,0.0
645 | 894.0,0.0
646 | 895.0,0.0
647 | 896.0,0.0
648 | 897.0,0.0
649 | 898.0,0.0
650 | 899.0,0.0
651 | 900.0,0.0
652 |
--------------------------------------------------------------------------------
/calibration/RP04-1806002-SC.csv:
--------------------------------------------------------------------------------
1 | Wavelength (nm),Reflectance
2 | 250,49.901862
3 | 251,49.832032
4 | 252,49.766288
5 | 253,49.717399
6 | 254,49.615813
7 | 255,49.527184
8 | 256,49.470175
9 | 257,49.384848
10 | 258,49.324898
11 | 259,49.290495
12 | 260,49.230678
13 | 261,49.150224
14 | 262,49.114494
15 | 263,49.101499
16 | 264,49.045483
17 | 265,49.003076
18 | 266,48.980207
19 | 267,49.011184
20 | 268,48.940621
21 | 269,48.900661
22 | 270,48.898757
23 | 271,48.925217
24 | 272,48.929311
25 | 273,48.89483
26 | 274,48.820243
27 | 275,48.791543
28 | 276,48.769115
29 | 277,48.815296
30 | 278,48.809855
31 | 279,48.766377
32 | 280,48.753984
33 | 281,48.749944
34 | 282,48.72047
35 | 283,48.743811
36 | 284,48.707487
37 | 285,48.696994
38 | 286,48.766163
39 | 287,48.746015
40 | 288,48.748846
41 | 289,48.775368
42 | 290,48.812023
43 | 291,48.771199
44 | 292,48.799693
45 | 293,48.820744
46 | 294,48.751855
47 | 295,48.827653
48 | 296,48.848083
49 | 297,48.845934
50 | 298,48.83592
51 | 299,48.831873
52 | 300,48.815778
53 | 301,48.827736
54 | 302,48.845061
55 | 303,48.813947
56 | 304,48.840886
57 | 305,48.849711
58 | 306,48.809554
59 | 307,48.896175
60 | 308,48.945884
61 | 309,48.902407
62 | 310,48.842354
63 | 311,48.838909
64 | 312,48.786016
65 | 313,48.85805
66 | 314,48.80662
67 | 315,48.872551
68 | 316,48.832587
69 | 317,48.696978
70 | 318,48.695546
71 | 319,48.639999
72 | 320,48.671532
73 | 321,48.779213
74 | 322,48.582865
75 | 323,48.607938
76 | 324,48.666362
77 | 325,48.541926
78 | 326,48.600089
79 | 327,48.574099
80 | 328,48.453977
81 | 329,48.413214
82 | 330,48.452426
83 | 331,48.385727
84 | 332,48.419893
85 | 333,48.428104
86 | 334,48.417844
87 | 335,48.419071
88 | 336,48.430646
89 | 337,48.332083
90 | 338,48.310058
91 | 339,48.279598
92 | 340,48.28381
93 | 341,48.314246
94 | 342,48.357884
95 | 343,48.336439
96 | 344,48.356472
97 | 345,48.326084
98 | 346,48.286254
99 | 347,48.316462
100 | 348,48.29935
101 | 349,48.331439
102 | 350,48.299259
103 | 351,48.371994
104 | 352,48.28669
105 | 353,48.322813
106 | 354,48.318801
107 | 355,48.298261
108 | 356,48.276364
109 | 357,48.277978
110 | 358,48.258998
111 | 359,48.307042
112 | 360,48.277528
113 | 361,48.218594
114 | 362,48.278612
115 | 363,48.277062
116 | 364,48.307968
117 | 365,48.210622
118 | 366,48.287384
119 | 367,48.264492
120 | 368,48.338323
121 | 369,48.314075
122 | 370,48.26416
123 | 371,48.329519
124 | 372,48.191372
125 | 373,48.271729
126 | 374,48.313783
127 | 375,48.204573
128 | 376,48.299615
129 | 377,48.384155
130 | 378,48.358459
131 | 379,48.417144
132 | 380,48.354234
133 | 381,48.358213
134 | 382,48.384212
135 | 383,48.370969
136 | 384,48.381329
137 | 385,48.38559
138 | 386,48.364826
139 | 387,48.34676
140 | 388,48.339269
141 | 389,48.328116
142 | 390,48.334449
143 | 391,48.312791
144 | 392,48.328882
145 | 393,48.340994
146 | 394,48.315117
147 | 395,48.298929
148 | 396,48.308705
149 | 397,48.309548
150 | 398,48.291071
151 | 399,48.313812
152 | 400,48.346554
153 | 401,48.344698
154 | 402,48.340867
155 | 403,48.343119
156 | 404,48.333864
157 | 405,48.279082
158 | 406,48.314276
159 | 407,48.362913
160 | 408,48.322448
161 | 409,48.330502
162 | 410,48.316138
163 | 411,48.323258
164 | 412,48.038124
165 | 413,48.355781
166 | 414,48.286055
167 | 415,48.272801
168 | 416,48.278334
169 | 417,48.322142
170 | 418,48.31726
171 | 419,48.335462
172 | 420,48.326164
173 | 421,48.325002
174 | 422,48.354496
175 | 423,48.310591
176 | 424,48.343642
177 | 425,48.365068
178 | 426,48.374752
179 | 427,48.334929
180 | 428,48.356817
181 | 429,48.381074
182 | 430,48.407889
183 | 431,48.400647
184 | 432,48.388909
185 | 433,48.395997
186 | 434,48.367681
187 | 435,48.395099
188 | 436,48.400717
189 | 437,48.357291
190 | 438,48.359293
191 | 439,48.375422
192 | 440,48.347006
193 | 441,48.036478
194 | 442,48.369696
195 | 443,48.391377
196 | 444,48.409047
197 | 445,48.401593
198 | 446,48.412322
199 | 447,48.421272
200 | 448,48.430114
201 | 449,48.468935
202 | 450,48.500344
203 | 451,48.480246
204 | 452,48.483367
205 | 453,48.486221
206 | 454,48.499744
207 | 455,48.513576
208 | 456,48.506246
209 | 457,48.524053
210 | 458,48.540735
211 | 459,48.526469
212 | 460,48.509991
213 | 461,48.500729
214 | 462,48.488062
215 | 463,48.514728
216 | 464,48.528348
217 | 465,48.523823
218 | 466,48.53376
219 | 467,48.519198
220 | 468,48.50547
221 | 469,48.504732
222 | 470,48.496032
223 | 471,48.506687
224 | 472,48.51877
225 | 473,48.514463
226 | 474,48.509255
227 | 475,48.489268
228 | 476,48.483041
229 | 477,48.478538
230 | 478,48.485284
231 | 479,48.503033
232 | 480,48.507856
233 | 481,48.527548
234 | 482,48.556359
235 | 483,48.542056
236 | 484,48.552749
237 | 485,48.568022
238 | 486,48.581135
239 | 487,48.605629
240 | 488,48.580285
241 | 489,48.576883
242 | 490,48.577357
243 | 491,48.599234
244 | 492,48.630432
245 | 493,48.650965
246 | 494,48.62625
247 | 495,48.60958
248 | 496,48.629314
249 | 497,48.628052
250 | 498,48.636994
251 | 499,48.669465
252 | 500,48.674485
253 | 501,48.673051
254 | 502,48.695508
255 | 503,48.718945
256 | 504,48.677516
257 | 505,48.646079
258 | 506,48.691358
259 | 507,48.670188
260 | 508,48.639847
261 | 509,48.675135
262 | 510,48.685472
263 | 511,48.679828
264 | 512,48.678474
265 | 513,48.655074
266 | 514,48.636762
267 | 515,48.062903
268 | 516,48.636584
269 | 517,48.640861
270 | 518,48.649285
271 | 519,48.664188
272 | 520,48.630941
273 | 521,48.639484
274 | 522,48.665044
275 | 523,48.654198
276 | 524,48.645514
277 | 525,48.655185
278 | 526,48.663269
279 | 527,48.651478
280 | 528,48.681113
281 | 529,48.673884
282 | 530,48.663913
283 | 531,48.668236
284 | 532,48.682404
285 | 533,48.677809
286 | 534,48.685439
287 | 535,48.682547
288 | 536,48.657788
289 | 537,48.692969
290 | 538,48.680596
291 | 539,48.679762
292 | 540,48.685612
293 | 541,48.693875
294 | 542,48.695462
295 | 543,48.709244
296 | 544,48.722597
297 | 545,48.701996
298 | 546,48.691316
299 | 547,48.71125
300 | 548,48.743624
301 | 549,48.685695
302 | 550,48.710354
303 | 551,48.719757
304 | 552,48.738154
305 | 553,48.727637
306 | 554,48.722658
307 | 555,48.720261
308 | 556,48.751001
309 | 557,48.727751
310 | 558,48.714458
311 | 559,48.707999
312 | 560,48.74051
313 | 561,48.743157
314 | 562,48.078161
315 | 563,48.725152
316 | 564,48.726839
317 | 565,48.775858
318 | 566,48.717721
319 | 567,48.703616
320 | 568,48.741869
321 | 569,48.741555
322 | 570,48.755322
323 | 571,48.756143
324 | 572,48.751802
325 | 573,48.73353
326 | 574,48.801166
327 | 575,48.791089
328 | 576,48.736649
329 | 577,48.77727
330 | 578,48.800739
331 | 579,48.817663
332 | 580,48.795422
333 | 581,48.764341
334 | 582,48.792945
335 | 583,48.767497
336 | 584,48.822201
337 | 585,48.849749
338 | 586,48.785576
339 | 587,48.810754
340 | 588,48.819354
341 | 589,48.802453
342 | 590,48.796951
343 | 591,48.792811
344 | 592,48.775917
345 | 593,48.84778
346 | 594,48.798235
347 | 595,48.803502
348 | 596,48.810659
349 | 597,48.798094
350 | 598,48.802474
351 | 599,48.847474
352 | 600,48.81904
353 | 601,48.81001
354 | 602,48.80034
355 | 603,48.791482
356 | 604,48.764584
357 | 605,48.814675
358 | 606,48.772569
359 | 607,48.805918
360 | 608,48.776226
361 | 609,48.775156
362 | 610,48.846107
363 | 611,48.776432
364 | 612,48.75292
365 | 613,48.800598
366 | 614,48.775082
367 | 615,48.813462
368 | 616,48.810548
369 | 617,48.811661
370 | 618,48.828614
371 | 619,48.826144
372 | 620,48.819734
373 | 621,48.815959
374 | 622,48.819444
375 | 623,48.806167
376 | 624,48.828745
377 | 625,48.831237
378 | 626,48.790401
379 | 627,48.761857
380 | 628,48.774955
381 | 629,48.776295
382 | 630,48.839976
383 | 631,48.822788
384 | 632,48.797378
385 | 633,48.809433
386 | 634,48.777428
387 | 635,48.774429
388 | 636,48.80509
389 | 637,48.801427
390 | 638,48.821084
391 | 639,48.823469
392 | 640,48.829767
393 | 641,48.821518
394 | 642,48.801609
395 | 643,48.801509
396 | 644,48.807979
397 | 645,48.816484
398 | 646,48.80929
399 | 647,48.786394
400 | 648,48.77045
401 | 649,48.807452
402 | 650,48.792956
403 | 651,48.757819
404 | 652,48.793262
405 | 653,48.861351
406 | 654,48.847244
407 | 655,48.860408
408 | 656,48.81712
409 | 657,48.808571
410 | 658,48.826882
411 | 659,48.822413
412 | 660,48.815297
413 | 661,48.815241
414 | 662,48.79037
415 | 663,48.790019
416 | 664,48.816807
417 | 665,48.805496
418 | 666,48.832898
419 | 667,48.852245
420 | 668,48.80264
421 | 669,48.784343
422 | 670,48.825531
423 | 671,48.8244
424 | 672,48.81128
425 | 673,48.805488
426 | 674,48.821233
427 | 675,48.7713
428 | 676,48.73763
429 | 677,48.776496
430 | 678,48.799645
431 | 679,48.822359
432 | 680,48.801343
433 | 681,48.78154
434 | 682,48.795082
435 | 683,48.828061
436 | 684,48.806118
437 | 685,48.824881
438 | 686,48.854758
439 | 687,48.805826
440 | 688,48.786083
441 | 689,48.807197
442 | 690,48.828559
443 | 691,48.818046
444 | 692,48.818169
445 | 693,48.788472
446 | 694,48.805344
447 | 695,48.826149
448 | 696,48.822366
449 | 697,48.814115
450 | 698,48.829891
451 | 699,48.825166
452 | 700,48.819816
453 | 701,48.843799
454 | 702,48.818926
455 | 703,48.783245
456 | 704,48.804459
457 | 705,48.822034
458 | 706,48.782178
459 | 707,48.735864
460 | 708,48.798376
461 | 709,48.814644
462 | 710,48.80214
463 | 711,48.805258
464 | 712,48.788
465 | 713,48.77131
466 | 714,48.742568
467 | 715,48.792859
468 | 716,48.820604
469 | 717,48.803291
470 | 718,48.790489
471 | 719,48.781973
472 | 720,48.786833
473 | 721,48.796694
474 | 722,48.786939
475 | 723,48.793219
476 | 724,48.781781
477 | 725,48.726482
478 | 726,48.77407
479 | 727,48.760307
480 | 728,48.761701
481 | 729,48.778904
482 | 730,48.819632
483 | 731,48.81622
484 | 732,48.804228
485 | 733,48.797972
486 | 734,48.75203
487 | 735,48.7079
488 | 736,48.797498
489 | 737,48.783389
490 | 738,48.767258
491 | 739,48.777227
492 | 740,48.718542
493 | 741,48.816492
494 | 742,48.74358
495 | 743,48.702773
496 | 744,48.715847
497 | 745,48.725965
498 | 746,48.680414
499 | 747,48.7403
500 | 748,48.709426
501 | 749,48.71778
502 | 750,48.756355
503 | 751,48.749149
504 | 752,48.742367
505 | 753,48.716819
506 | 754,48.858484
507 | 755,48.735756
508 | 756,48.735173
509 | 757,48.790095
510 | 758,48.762792
511 | 759,48.758794
512 | 760,48.761744
513 | 761,48.854291
514 | 762,48.788249
515 | 763,48.773682
516 | 764,48.698095
517 | 765,48.702501
518 | 766,48.766943
519 | 767,48.888948
520 | 768,48.841963
521 | 769,48.795017
522 | 770,48.76593
523 | 771,48.759821
524 | 772,48.753461
525 | 773,48.776467
526 | 774,48.742771
527 | 775,48.760796
528 | 776,48.674254
529 | 777,48.761153
530 | 778,48.763679
531 | 779,48.79558
532 | 780,48.68229
533 | 781,48.793194
534 | 782,48.794083
535 | 783,48.717196
536 | 784,48.723229
537 | 785,48.773601
538 | 786,48.777928
539 | 787,48.710835
540 | 788,48.740258
541 | 789,48.702232
542 | 790,48.689636
543 | 791,48.71094
544 | 792,48.883474
545 | 793,48.852829
546 | 794,48.831846
547 | 795,48.682758
548 | 796,48.766773
549 | 797,48.807332
550 | 798,48.715845
551 | 799,48.790745
552 | 800,48.752849
553 | 801,48.760534
554 | 802,48.833629
555 | 803,48.76366
556 | 804,48.914618
557 | 805,48.77223
558 | 806,48.765341
559 | 807,48.733947
560 | 808,48.710275
561 | 809,48.85523
562 | 810,48.694376
563 | 811,48.767581
564 | 812,48.640435
565 | 813,48.759008
566 | 814,48.712641
567 | 815,48.772458
568 | 816,48.700501
569 | 817,48.811982
570 | 818,48.756295
571 | 819,48.781304
572 | 820,48.70058
573 | 821,48.696793
574 | 822,48.697703
575 | 823,48.703654
576 | 824,48.680882
577 | 825,48.687001
578 | 826,48.648107
579 | 827,48.689181
580 | 828,48.68419
581 | 829,48.661869
582 | 830,48.679162
583 | 831,48.729507
584 | 832,48.675258
585 | 833,48.686848
586 | 834,48.691247
587 | 835,48.678836
588 | 836,48.672374
589 | 837,48.67277
590 | 838,48.722834
591 | 839,48.732526
592 | 840,48.702952
593 | 841,48.698386
594 | 842,48.713321
595 | 843,48.706019
596 | 844,48.686337
597 | 845,48.663478
598 | 846,48.667986
599 | 847,48.665026
600 | 848,48.695657
601 | 849,48.692007
602 | 850,48.664873
603 | 851,48.648599
604 | 852,48.655843
605 | 853,48.667715
606 | 854,48.674927
607 | 855,48.680186
608 | 856,48.689655
609 | 857,48.689947
610 | 858,48.652976
611 | 859,48.667459
612 | 860,48.670248
613 | 861,48.675221
614 | 862,48.663039
615 | 863,48.655759
616 | 864,48.6637
617 | 865,48.653296
618 | 866,48.671069
619 | 867,48.66131
620 | 868,48.660164
621 | 869,48.644716
622 | 870,48.646048
623 | 871,48.647604
624 | 872,48.634986
625 | 873,48.640604
626 | 874,48.666046
627 | 875,48.626141
628 | 876,48.640122
629 | 877,48.640477
630 | 878,48.647683
631 | 879,48.644104
632 | 880,48.650011
633 | 881,48.65429
634 | 882,48.647466
635 | 883,48.611739
636 | 884,48.617006
637 | 885,48.645316
638 | 886,48.64322
639 | 887,48.657715
640 | 888,48.609973
641 | 889,48.603667
642 | 890,48.059939
643 | 891,48.613968
644 | 892,48.622905
645 | 893,48.644836
646 | 894,48.577469
647 | 895,48.557767
648 | 896,48.601988
649 | 897,48.605107
650 | 898,48.599799
651 | 899,48.602866
652 | 900,48.061022
653 |
--------------------------------------------------------------------------------
/calibration/RP04-1923118-OB.csv:
--------------------------------------------------------------------------------
1 | 250,0.535167685
2 | 251,0.53452483
3 | 252,0.5343019
4 | 253,0.533966455
5 | 254,0.53357559
6 | 255,0.53293146
7 | 256,0.53240557
8 | 257,0.531824095
9 | 258,0.5312529
10 | 259,0.530892775
11 | 260,0.5304574
12 | 261,0.52994818
13 | 262,0.52921809
14 | 263,0.52891028
15 | 264,0.528235495
16 | 265,0.527593155
17 | 266,0.526951025
18 | 267,0.52664793
19 | 268,0.526247075
20 | 269,0.526109155
21 | 270,0.526252575
22 | 271,0.526136245
23 | 272,0.52576835
24 | 273,0.525325205
25 | 274,0.52519193
26 | 275,0.52502539
27 | 276,0.52477239
28 | 277,0.524707975
29 | 278,0.52443889
30 | 279,0.5241249
31 | 280,0.524037115
32 | 281,0.523789715
33 | 282,0.523990845
34 | 283,0.523809885
35 | 284,0.523612985
36 | 285,0.52290262
37 | 286,0.52294067
38 | 287,0.52340302
39 | 288,0.52314584
40 | 289,0.522978625
41 | 290,0.522663875
42 | 291,0.523025005
43 | 292,0.522648435
44 | 293,0.52265417
45 | 294,0.52249575
46 | 295,0.522914275
47 | 296,0.522986385
48 | 297,0.522860175
49 | 298,0.52265903
50 | 299,0.522768625
51 | 300,0.522524385
52 | 301,0.52244084
53 | 302,0.522784505
54 | 303,0.522845295
55 | 304,0.522013785
56 | 305,0.52206547
57 | 306,0.52224455
58 | 307,0.522380735
59 | 308,0.522323775
60 | 309,0.52244347
61 | 310,0.52217347
62 | 311,0.521908725
63 | 312,0.522027475
64 | 313,0.52130765
65 | 314,0.52117231
66 | 315,0.52086072
67 | 316,0.52079087
68 | 317,0.52012746
69 | 318,0.520477745
70 | 319,0.520318555
71 | 320,0.51993609
72 | 321,0.52048082
73 | 322,0.518805175
74 | 323,0.51931888
75 | 324,0.51833801
76 | 325,0.517796495
77 | 326,0.51693495
78 | 327,0.51795147
79 | 328,0.516737685
80 | 329,0.51691914
81 | 330,0.5170833
82 | 331,0.5166048
83 | 332,0.51680222
84 | 333,0.51667296
85 | 334,0.51616523
86 | 335,0.516521655
87 | 336,0.51680724
88 | 337,0.51664931
89 | 338,0.51598996
90 | 339,0.51616529
91 | 340,0.51633528
92 | 341,0.51649069
93 | 342,0.517102155
94 | 343,0.515998255
95 | 344,0.515391235
96 | 345,0.515935635
97 | 346,0.51671749
98 | 347,0.515830165
99 | 348,0.51573144
100 | 349,0.51623872
101 | 350,0.515561375
102 | 351,0.51580132
103 | 352,0.51533121
104 | 353,0.515336645
105 | 354,0.515383835
106 | 355,0.51559338
107 | 356,0.51608675
108 | 357,0.51650068
109 | 358,0.5159401
110 | 359,0.516104175
111 | 360,0.51579234
112 | 361,0.515900055
113 | 362,0.51611426
114 | 363,0.515441495
115 | 364,0.51594912
116 | 365,0.51556528
117 | 366,0.51529954
118 | 367,0.515446705
119 | 368,0.51584185
120 | 369,0.515364035
121 | 370,0.515421295
122 | 371,0.515209465
123 | 372,0.515389855
124 | 373,0.514820135
125 | 374,0.51500396
126 | 375,0.51496904
127 | 376,0.5141078
128 | 377,0.514734445
129 | 378,0.51541812
130 | 379,0.515307925
131 | 380,0.51524868
132 | 381,0.515385145
133 | 382,0.51531708
134 | 383,0.515608515
135 | 384,0.51534993
136 | 385,0.51530677
137 | 386,0.51557698
138 | 387,0.51537896
139 | 388,0.51528367
140 | 389,0.5150503
141 | 390,0.51518486
142 | 391,0.515335805
143 | 392,0.515502035
144 | 393,0.5151493
145 | 394,0.51504164
146 | 395,0.515231355
147 | 396,0.51556815
148 | 397,0.5151158
149 | 398,0.51468831
150 | 399,0.51526068
151 | 400,0.515255705
152 | 401,0.51519312
153 | 402,0.51518973
154 | 403,0.515179415
155 | 404,0.51505956
156 | 405,0.51492619
157 | 406,0.51518255
158 | 407,0.515149605
159 | 408,0.51515633
160 | 409,0.51472888
161 | 410,0.514916135
162 | 411,0.514726915
163 | 412,0.514638215
164 | 413,0.5148837
165 | 414,0.51507724
166 | 415,0.515168355
167 | 416,0.51519544
168 | 417,0.515139295
169 | 418,0.51499267
170 | 419,0.51498911
171 | 420,0.514842615
172 | 421,0.515240165
173 | 422,0.515232985
174 | 423,0.515165565
175 | 424,0.51498553
176 | 425,0.51496034
177 | 426,0.514910445
178 | 427,0.51493306
179 | 428,0.514922575
180 | 429,0.514956005
181 | 430,0.51513383
182 | 431,0.515400215
183 | 432,0.515355505
184 | 433,0.51477437
185 | 434,0.51477072
186 | 435,0.514626195
187 | 436,0.514531505
188 | 437,0.514751885
189 | 438,0.514820775
190 | 439,0.514684955
191 | 440,0.514728565
192 | 441,0.514960825
193 | 442,0.515166735
194 | 443,0.515109505
195 | 444,0.51518153
196 | 445,0.51539494
197 | 446,0.51515323
198 | 447,0.515143335
199 | 448,0.51532102
200 | 449,0.51549558
201 | 450,0.515597595
202 | 451,0.515664895
203 | 452,0.51566427
204 | 453,0.515501455
205 | 454,0.515578685
206 | 455,0.515637085
207 | 456,0.51562906
208 | 457,0.51588465
209 | 458,0.5158362
210 | 459,0.515618895
211 | 460,0.515668385
212 | 461,0.515876635
213 | 462,0.515925925
214 | 463,0.51550964
215 | 464,0.515180855
216 | 465,0.515337685
217 | 466,0.5155426
218 | 467,0.515682285
219 | 468,0.51570792
220 | 469,0.515634855
221 | 470,0.51542337
222 | 471,0.51522832
223 | 472,0.515526255
224 | 473,0.51574646
225 | 474,0.51562567
226 | 475,0.51566216
227 | 476,0.515820835
228 | 477,0.516016895
229 | 478,0.51583094
230 | 479,0.515704795
231 | 480,0.5158298
232 | 481,0.515925745
233 | 482,0.51569865
234 | 483,0.515687625
235 | 484,0.5158341
236 | 485,0.515949405
237 | 486,0.51626935
238 | 487,0.51619145
239 | 488,0.515892035
240 | 489,0.51611791
241 | 490,0.516394375
242 | 491,0.516573385
243 | 492,0.51655298
244 | 493,0.51636203
245 | 494,0.516479825
246 | 495,0.51677009
247 | 496,0.516829975
248 | 497,0.5168363
249 | 498,0.516737395
250 | 499,0.51652405
251 | 500,0.516627615
252 | 501,0.516732135
253 | 502,0.516632905
254 | 503,0.51655746
255 | 504,0.51660972
256 | 505,0.516539005
257 | 506,0.51669438
258 | 507,0.51661585
259 | 508,0.51650514
260 | 509,0.51661908
261 | 510,0.516626275
262 | 511,0.516637895
263 | 512,0.51649327
264 | 513,0.51652303
265 | 514,0.516688795
266 | 515,0.51654557
267 | 516,0.51654916
268 | 517,0.51652429
269 | 518,0.51643898
270 | 519,0.516632705
271 | 520,0.51665743
272 | 521,0.51686091
273 | 522,0.516623475
274 | 523,0.51649351
275 | 524,0.516538655
276 | 525,0.516494835
277 | 526,0.51648086
278 | 527,0.51658713
279 | 528,0.516410005
280 | 529,0.516507885
281 | 530,0.516623465
282 | 531,0.516594585
283 | 532,0.516628255
284 | 533,0.516450085
285 | 534,0.516650415
286 | 535,0.51682738
287 | 536,0.516838005
288 | 537,0.516640715
289 | 538,0.51660307
290 | 539,0.51657299
291 | 540,0.516736065
292 | 541,0.51689559
293 | 542,0.51687192
294 | 543,0.516715495
295 | 544,0.516707405
296 | 545,0.51685442
297 | 546,0.516972265
298 | 547,0.51683514
299 | 548,0.51710126
300 | 549,0.51692002
301 | 550,0.517040045
302 | 551,0.517089065
303 | 552,0.51706753
304 | 553,0.51685855
305 | 554,0.517096675
306 | 555,0.516848125
307 | 556,0.51680511
308 | 557,0.516765555
309 | 558,0.51688874
310 | 559,0.517101705
311 | 560,0.5170944
312 | 561,0.517191515
313 | 562,0.517100555
314 | 563,0.517256035
315 | 564,0.517130835
316 | 565,0.517135485
317 | 566,0.51702515
318 | 567,0.51699859
319 | 568,0.51735174
320 | 569,0.51731715
321 | 570,0.516971
322 | 571,0.517198915
323 | 572,0.517187185
324 | 573,0.5172815
325 | 574,0.5169817
326 | 575,0.517373675
327 | 576,0.517193355
328 | 577,0.516933185
329 | 578,0.516812225
330 | 579,0.51730114
331 | 580,0.517008605
332 | 581,0.51720408
333 | 582,0.51696832
334 | 583,0.5170618
335 | 584,0.51720347
336 | 585,0.517565125
337 | 586,0.517041785
338 | 587,0.516833425
339 | 588,0.51705541
340 | 589,0.517182325
341 | 590,0.517376245
342 | 591,0.517054055
343 | 592,0.51703146
344 | 593,0.51725763
345 | 594,0.517326125
346 | 595,0.517333955
347 | 596,0.517053075
348 | 597,0.516882745
349 | 598,0.517490115
350 | 599,0.517223845
351 | 600,0.51719085
352 | 601,0.516839625
353 | 602,0.51708647
354 | 603,0.51701273
355 | 604,0.516828655
356 | 605,0.517045345
357 | 606,0.516805515
358 | 607,0.5170346
359 | 608,0.51734829
360 | 609,0.51702513
361 | 610,0.517326565
362 | 611,0.516906625
363 | 612,0.51692526
364 | 613,0.517096155
365 | 614,0.517250085
366 | 615,0.51704632
367 | 616,0.517087375
368 | 617,0.51713945
369 | 618,0.516785215
370 | 619,0.517133915
371 | 620,0.517094765
372 | 621,0.516692025
373 | 622,0.516953905
374 | 623,0.51696422
375 | 624,0.516885165
376 | 625,0.516883745
377 | 626,0.516654495
378 | 627,0.51680049
379 | 628,0.516806555
380 | 629,0.51719801
381 | 630,0.51678739
382 | 631,0.516669725
383 | 632,0.517016105
384 | 633,0.517060635
385 | 634,0.51690666
386 | 635,0.51692941
387 | 636,0.51710886
388 | 637,0.51707126
389 | 638,0.51682491
390 | 639,0.516880565
391 | 640,0.51697917
392 | 641,0.516985775
393 | 642,0.517085045
394 | 643,0.51721062
395 | 644,0.517237905
396 | 645,0.517131005
397 | 646,0.517005025
398 | 647,0.516658855
399 | 648,0.51691748
400 | 649,0.51694108
401 | 650,0.516290815
402 | 651,0.51676958
403 | 652,0.516753005
404 | 653,0.51671301
405 | 654,0.516552475
406 | 655,0.516677785
407 | 656,0.51671955
408 | 657,0.51664365
409 | 658,0.51659122
410 | 659,0.516698395
411 | 660,0.51660394
412 | 661,0.5165246
413 | 662,0.516946795
414 | 663,0.516603515
415 | 664,0.51659502
416 | 665,0.51681242
417 | 666,0.51661319
418 | 667,0.516688905
419 | 668,0.516785085
420 | 669,0.516802935
421 | 670,0.51717179
422 | 671,0.51699025
423 | 672,0.516705975
424 | 673,0.516591385
425 | 674,0.516569835
426 | 675,0.516731425
427 | 676,0.516797475
428 | 677,0.516696305
429 | 678,0.516819565
430 | 679,0.516659655
431 | 680,0.51656748
432 | 681,0.516769305
433 | 682,0.516673315
434 | 683,0.516702665
435 | 684,0.51678787
436 | 685,0.51661195
437 | 686,0.516344085
438 | 687,0.516370145
439 | 688,0.516560665
440 | 689,0.516678745
441 | 690,0.51678935
442 | 691,0.51685688
443 | 692,0.51664022
444 | 693,0.516508025
445 | 694,0.516552705
446 | 695,0.516320365
447 | 696,0.51640646
448 | 697,0.51650916
449 | 698,0.51598395
450 | 699,0.51610842
451 | 700,0.51642874
452 | 701,0.51628442
453 | 702,0.5162397
454 | 703,0.516045075
455 | 704,0.51592893
456 | 705,0.51608671
457 | 706,0.516294915
458 | 707,0.516153125
459 | 708,0.51596912
460 | 709,0.515955575
461 | 710,0.51593095
462 | 711,0.51622272
463 | 712,0.51614402
464 | 713,0.515721835
465 | 714,0.515663105
466 | 715,0.515569765
467 | 716,0.515552245
468 | 717,0.51551152
469 | 718,0.51582759
470 | 719,0.516155335
471 | 720,0.516001245
472 | 721,0.515775715
473 | 722,0.51567695
474 | 723,0.51563619
475 | 724,0.51575649
476 | 725,0.516190975
477 | 726,0.5161494
478 | 727,0.51618338
479 | 728,0.51618448
480 | 729,0.516311325
481 | 730,0.516220445
482 | 731,0.51547789
483 | 732,0.51565221
484 | 733,0.51572362
485 | 734,0.51561541
486 | 735,0.51557802
487 | 736,0.515675195
488 | 737,0.515602025
489 | 738,0.51580009
490 | 739,0.515344645
491 | 740,0.51569711
492 | 741,0.5165672
493 | 742,0.51636421
494 | 743,0.515540295
495 | 744,0.51572579
496 | 745,0.515794305
497 | 746,0.51602672
498 | 747,0.515249755
499 | 748,0.51528167
500 | 749,0.5157138
501 | 750,0.51606596
502 | 751,0.51582924
503 | 752,0.51521899
504 | 753,0.514998495
505 | 754,0.51525229
506 | 755,0.515232915
507 | 756,0.514996315
508 | 757,0.515243815
509 | 758,0.515491505
510 | 759,0.516081925
511 | 760,0.515744005
512 | 761,0.515208795
513 | 762,0.514970855
514 | 763,0.514535445
515 | 764,0.51575857
516 | 765,0.515832185
517 | 766,0.51612387
518 | 767,0.51575873
519 | 768,0.51599667
520 | 769,0.51543141
521 | 770,0.5149252
522 | 771,0.51511567
523 | 772,0.51461833
524 | 773,0.51489704
525 | 774,0.51491034
526 | 775,0.514278025
527 | 776,0.515376675
528 | 777,0.51507031
529 | 778,0.51491368
530 | 779,0.514387345
531 | 780,0.5154995
532 | 781,0.5153718
533 | 782,0.515415255
534 | 783,0.51469621
535 | 784,0.51502734
536 | 785,0.51451523
537 | 786,0.514744405
538 | 787,0.514575915
539 | 788,0.51476855
540 | 789,0.515101155
541 | 790,0.515200055
542 | 791,0.514184
543 | 792,0.514403805
544 | 793,0.51461034
545 | 794,0.51385538
546 | 795,0.51510953
547 | 796,0.51485992
548 | 797,0.51433598
549 | 798,0.51508546
550 | 799,0.51445356
551 | 800,0.514922715
552 | 801,0.51545938
553 | 802,0.514896385
554 | 803,0.513880565
555 | 804,0.514905255
556 | 805,0.51427441
557 | 806,0.51416581
558 | 807,0.514222745
559 | 808,0.514894315
560 | 809,0.51476702
561 | 810,0.514730215
562 | 811,0.51437342
563 | 812,0.51443731
564 | 813,0.515037375
565 | 814,0.514065315
566 | 815,0.51435599
567 | 816,0.51398426
568 | 817,0.51390227
569 | 818,0.51488974
570 | 819,0.514667735
571 | 820,0.51426614
572 | 821,0.514256665
573 | 822,0.514115835
574 | 823,0.514276795
575 | 824,0.513997275
576 | 825,0.51415871
577 | 826,0.514197025
578 | 827,0.51401756
579 | 828,0.51420522
580 | 829,0.513877365
581 | 830,0.5139021
582 | 831,0.513972555
583 | 832,0.51381361
584 | 833,0.5141964
585 | 834,0.51430322
586 | 835,0.51429574
587 | 836,0.51428642
588 | 837,0.513979935
589 | 838,0.51413932
590 | 839,0.514073035
591 | 840,0.514264405
592 | 841,0.5141232
593 | 842,0.513880715
594 | 843,0.51405224
595 | 844,0.5138746
596 | 845,0.513950685
597 | 846,0.51368354
598 | 847,0.51374989
599 | 848,0.51360146
600 | 849,0.513672145
601 | 850,0.51354418
602 | 851,0.51363923
603 | 852,0.51358569
604 | 853,0.51363109
605 | 854,0.513453395
606 | 855,0.51342489
607 | 856,0.513481745
608 | 857,0.513443375
609 | 858,0.51358626
610 | 859,0.513173695
611 | 860,0.51310122
612 | 861,0.51324978
613 | 862,0.51329565
614 | 863,0.51334036
615 | 864,0.51314697
616 | 865,0.51299097
617 | 866,0.513131465
618 | 867,0.512968475
619 | 868,0.51332801
620 | 869,0.513111025
621 | 870,0.513152715
622 | 871,0.513044475
623 | 872,0.513121225
624 | 873,0.512907205
625 | 874,0.51297056
626 | 875,0.51296887
627 | 876,0.51292413
628 | 877,0.51266848
629 | 878,0.512845375
630 | 879,0.512755365
631 | 880,0.512787
632 | 881,0.51284581
633 | 882,0.512778435
634 | 883,0.512566835
635 | 884,0.51238274
636 | 885,0.512806055
637 | 886,0.51272044
638 | 887,0.51259587
639 | 888,0.51244687
640 | 889,0.512702695
641 | 890,0.512839015
642 | 891,0.512778295
643 | 892,0.51244354
644 | 893,0.51246906
645 | 894,0.51228598
646 | 895,0.512518255
647 | 896,0.512699755
648 | 897,0.51237151
649 | 898,0.51238678
650 | 899,0.51228898
651 | 900,0.51234108
652 | 901,0.512002805
653 | 902,0.512155365
654 | 903,0.51222658
655 | 904,0.51208221
656 | 905,0.51196674
657 | 906,0.512023115
658 | 907,0.51156843
659 | 908,0.511586895
660 | 909,0.5120468
661 | 910,0.51177663
662 | 911,0.51154949
663 | 912,0.51167964
664 | 913,0.511930695
665 | 914,0.511745285
666 | 915,0.511608495
667 | 916,0.51167313
668 | 917,0.511618125
669 | 918,0.511660845
670 | 919,0.51135416
671 | 920,0.511443295
672 | 921,0.51148291
673 | 922,0.511388265
674 | 923,0.511457685
675 | 924,0.51141813
676 | 925,0.511197125
677 | 926,0.511190745
678 | 927,0.51105918
679 | 928,0.511127855
680 | 929,0.511108005
681 | 930,0.51087727
682 | 931,0.51086988
683 | 932,0.511145235
684 | 933,0.511147695
685 | 934,0.511062875
686 | 935,0.511025385
687 | 936,0.51087904
688 | 937,0.510618705
689 | 938,0.510694835
690 | 939,0.510813965
691 | 940,0.51078748
692 | 941,0.510685715
693 | 942,0.51048159
694 | 943,0.51072852
695 | 944,0.51084656
696 | 945,0.51071908
697 | 946,0.510912995
698 | 947,0.51082743
699 | 948,0.51078216
700 | 949,0.51073716
701 | 950,0.511014645
702 |
--------------------------------------------------------------------------------
/config/config-base.yml:
--------------------------------------------------------------------------------
1 | # This is an example yaml configuration for a metashape run
2 |
3 | #### Project-level parameters:
4 |
5 | # Project to load. If not a blank string, this will open an existing project at the path specified. If a blank string, creates a new empty project.
6 | # Even if opening an existing project, all processing on it is saved as a new project (path and name specified below). The original project file is not modified.
7 | load_project: ""
8 |
9 | # The path to the directory of flight photos
10 | # If there are multiple photo folders, set path to the folder that contains all the photo folders,
11 | # or provide a list of paths via the YAML list syntax (e.g., ["/path/to/photo/folder1", "/path/to/photo/folder2"])
12 | # If there are no photos to add (e.g., this is an existing project that already has photos in it, set to an empty string ("")
13 | photo_path: ""
14 |
15 | # The path to a secondary directory of flight photos, which are only aligned to the project *after*
16 | # all the other processing is done. This is useful if you want to use secondary photos for multiview
17 | # analyses (e.g., species ID) without affecting the photogrammetry. Note that the secondary photos
18 | # are processed in the same way as the primary (e.g., same resolution, same procedure regarding
19 | # separate calibration per path) and that align_photos:reset_alignment must be False and
20 | # align_photos:keep_keypoints must be True. If there are no secondary photos to add, set to an empty
21 | # string ("").
22 | photo_path_secondary: ""
23 |
24 | # Path for exports (e.g., points, DSM, orthomosaic) and processing log. Will be created if does not exist.
25 | output_path: ""
26 |
27 | # Path to save Metashape project file (.psx). Will be created if does not exist
28 | project_path: ""
29 |
30 | # The identifier for the run. Will be used in naming output files. Recommended to include a photoset name and processing parameter set name.
31 | # Optionally, set it to an empty string ("") to use the config file name (minus extension) as the run name
32 | run_name: ""
33 |
34 | # CRS EPSG code that project outputs should be in (projection should be in meter units and intended for the project area)
35 | project_crs: "EPSG::26910" # 26910 is UTM 10N
36 |
37 | # Enable metashape "fine-level task subdivision" which reduces memory use by breaking processing into independent chunks that are run in series.
38 | # Assuming there's enough memory, it seems to run 10-20% faster by disabling subdividing. But large projects can run out memory and fail if subdivide is not enabled.
39 | subdivide_task: True
40 |
41 | # Should CUDA GPU driver be used? Alternative is OpenCL. Metashape uses CUDA by default but we have observed it can cause crashes on HPC infrastructure.
42 | use_cuda: True
43 |
44 | # What value to use for the Metashape tweak "depth_max_gpu_multiplier"? May help to mitigate GPU errors per: https://www.agisoft.com/forum/index.php?topic=11771.0, but doesn't appear to do anything per our testing. Metashape default is 2.
45 | gpu_multiplier: 2
46 |
47 | #### Processing parameters:
48 | ## Steps can be run or skipped using the 'enabled' parameter. If enabled == False, everything else in the step is irrelevant.
49 | ## The metashape functions powering each of these steps are listed in the comments in parentheses.
50 | ## Refer to Metashape documentation for full parameter definitions: https://www.agisoft.com/pdf/metashape_python_api_1_5_0.pdf
51 | ## Parameter names here generally follow the parameter names of the Metashape functions.
52 |
53 | # Should the photos at the path(s) listed above be added to the project? Can disable if, for
54 | # example, you only want to add GCPs (or do additional processing) to an existing project.
55 | addPhotos: # (Metashape: addPhotos)
56 | enabled: True # This applies to the main photos specified in photo_path above. Secondary photos are always added and aligned if a path (or paths) is provided.
57 | separate_calibration_per_path: False # If True, each photo path (i.e. each element in the list supplied to 'photo_path' above) will be calibrated independently. Regardless whether True or False, separate camera *models* are calibrated separately; if True, identical camera *models* are calibrated separately if they are provided as separate paths. This addresses the case where two different instances of the same camera model are used in the same project. Note that when True, the logic for assigning separate calibration to each path assumes that the same camera is used for all photos in the path.
58 | multispectral: False # Is this a multispectral photo set? If RGB, set to False.
59 | use_rtk: False # Whether to use image EXIF RTK flags to make image geospatial accuracy more precise. If enabled but photos don't have RTK data, will treat them as regular photos and use the nofix accuracy.
60 | fix_accuracy: 3 # Accuracy to set for photos that have a RTK fix, in units of the CRS
61 | nofix_accuracy: 25 # Accuracy to set for photos that have no fix, in units of the CRS
62 | # Choose what type of camera you are using:
63 | # Frame: default, uses radial distortion to handle lens distortion
64 | # Spherical: expects image data to come in in the equirectangular format
65 | sensor_type: Metashape.Sensor.Type.Frame # Sets the camera type. Tested choices: Metashape.Sensor.Type.Frame, Metashape.Sensor.Type.Spherical
66 |
67 | calibrateReflectance: # (Metahsape: calibrateReflectance)
68 | enabled: False
69 | panel_filename: "RP04-1923118-OB.csv" # The calibration file must be in the "calibration" folder in the top-level project photos directory. See example panel calibration file in the calibration directory of project repo.
70 | use_reflectance_panels: True
71 | use_sun_sensor: True
72 |
73 | alignPhotos: # (Metashape: matchPhotos, alignCameras)
74 | enabled: True
75 | downscale: 2 # How much to coarsen the photos when searching for tie points. Higher number for blurrier photos or when there are small surfaces that may move between photos (such as leaves). Accepts numbers 2^x (and zero) (https://www.agisoft.com/forum/index.php?topic=11697.0).
76 | adaptive_fitting: True # Should the camera lens model be fit at the same time as aligning photos?
77 | keep_keypoints: True # Should keypoints from matching photos be stored in the project? Required if you later want to add more photos and align them to the previously aligned photos without redoing the original alignment.
78 | reset_alignment: False # When running an alignment, if any of the photos were already aligned, should we keep that alignment? Or reset it so we align everything anew?
79 | generic_preselection: True # When matching photos, use a much-coarsened version of each photo to narrow down the potential neighbors to pair? Works well if the photos have high altitude above the surface and high overlap (e.g. a 120m nadir 90/90 overlap mission), but doesn't work well for low-altitude and/or highly oblique photos (e.g. a 80m 25deg pitch 80/80 overlap mission)
80 | reference_preselection: True # When matching photos, use the camera location data to narrow down the potential neighbors to pair?
81 | reference_preselection_mode: Metashape.ReferencePreselectionSource # When matching photos, use the camera location data to narrow down the potential neighbors to pair?
82 |
83 | # To use GCPs, a 'gcps' folder must exist in the root of the photo folder provided in photo_path
84 | # above (or the first folder, if a list is passed). The contents of the 'gcps' folder are created by
85 | # the prep_gcps.R script. See readme: https://github.com/ucdavis/metashape
86 | addGCPs:
87 | enabled: False
88 | gcp_crs: "EPSG::26910" # CRS EPSG code of GCP coordinates. 26910 (UTM 10 N) is the CRS of the sample RGB photoset.
89 | marker_location_accuracy: 0.1 # Accuracy of GCPs real-world coordinates, in meters.
90 | marker_projection_accuracy: 8 # Accuracy of the identified locations of the GCPs within the images, in pixels.
91 | optimize_w_gcps_only: True # Optimize alignment using GCPs only: required for GCP locations to take precedence over photo GPS data. Disabling it makes GCPs essentially irrelevant.
92 |
93 | filterPointsUSGS:
94 | enabled: False
95 | rec_thresh_percent: 20
96 | rec_thresh_absolute: 15
97 | proj_thresh_percent: 30
98 | proj_thresh_absolute: 2
99 | reproj_thresh_percent: 5
100 | reproj_thresh_absolute: 0.3
101 |
102 | optimizeCameras: # (Metashape: optimizeCameras)
103 | enabled: True
104 | adaptive_fitting: True # Should the camera lens model be fit at the same time as optimizing photos?
105 | export: True # Export the camera locations, now updated from the initial alignment
106 |
107 | # Should an xml file specifying estimated camera locations (transform matrices) be exported? If
108 | # enabled, it is exported once after all alignment-related steps (e.g., align, fliter points,
109 | # optimize cameras) -- even if these steps are disabled -- and then again after aligning the
110 | # secondary set of locations (if performed), overwriting the first file
111 | exportCameras: # (Metashape: exportCameras)
112 | enabled: True
113 |
114 | buildDepthMaps: # (Metashape: buildDepthMaps)
115 | enabled: True
116 | downscale: 4 # How much to coarsen the photos when searching for matches to build the point cloud. For large photosets, values < 4 likely take prohibitively long. Accepts numbers 2^x (https://www.agisoft.com/forum/index.php?topic=11697.0).
117 | filter_mode: Metashape.ModerateFiltering # How to filter the depth map. Options are NoFiltering, MildFiltering, ModerateFiltering, AggressiveFiltering. Aggressive filtering removes detail and makes worse DEMs (at least for forest). NoFiltering takes very long. In trials, it never completed.
118 | reuse_depth: False # Purpose unknown.
119 | max_neighbors: 60 # Maximum number of neighboring photos to use for estimating depth map. Higher numbers may increase accuracy but dramatically increase processing time.
120 |
121 | buildPointCloud: # (Metashape: buildPointCloud, (optionally) classifyGroundPoints, and exportPoints)
122 | enabled: True
123 | keep_depth: True # If False, removes depth maps from project data after building point cloud
124 | max_neighbors: 60 # Maximum number of neighboring photos to use for estimating point cloud. Higher numbers may increase accuracy but dramatically increase processing time.
125 | classify_ground_points: True # Should ground points be classified as a part of this step? Must be enabled (either here or in buildDem, below) if a digital terrain model (DTM) is needed either for orthomosaic or DTM export. Enabling here is an alternative to enabling as a component of buildDem (below). It depends on which stage you want the classification to be done at. If you already have a point cloud but it's unclassified, then don't do it as part of this stage as it would require computing the point cloud again.
126 | export: False # Whether to export point cloud file.
127 | export_format: Metashape.PointCloudFormatCOPC # Export format. Options: Metashape.PointCloudFormatCOPC, Metashape.PointCloudFormatLAZ, Metashape.PointCloudFormatLAS, or other options indicated in the Metashape Python module documentation. We have observed that COPC export takes about 6x longer than LAZ or LAS export on a 32-core machine, but this is still much faster than using Untwine when the files are not stored on a local volume. PDAL is proably faster but requires a lot of memory. COPC is cloud-optimized point cloud (a subset of LAZ format) and is recommended for cloud native visualization and analysis.
128 | classes: "ALL" # Point classes to export. Must be a list. Or can set to "ALL" to use all points. An example of a specific class is: Metashape.PointClass.Ground
129 | remove_after_export: False # Remove point cloud from project after export of all dependencies (DEMs) to reduce the metashape project file size
130 |
131 | classifyGroundPoints: # (Metashape: classifyGroundPoints) # classify points, IF SPECIFIED as a component of buildPointCloud (above) or buildDem (below). Must be enabled (in either location) if a digital terrain model (DTM) is needed either for orthomosaic or DTM export. Definitions here: https://www.agisoft.com/forum/index.php?topic=9328.0
132 | max_angle: 15.0
133 | max_distance: 1.0
134 | cell_size: 50.0
135 |
136 | buildMesh:
137 | enabled: True
138 | face_count: "Metashape.MediumFaceCount" # How many faces to use, Metashape.LowFaceCount, MediumFaceCount, HighFaceCount, CustomFaceCount
139 | face_count_custom: 100000 # Only used if custom number of faces set (above).
140 | export: True # Export the georeferenced mesh.
141 | export_extension: "ply" # Can be any supported 3D mesh extension
142 | # If True, add in a shift to the CRS so the mesh origin is at the camera EXIF average location and points are reported in a local frame - shifted from the normal CRS origin.
143 | # This is necessary when fine mesh detail is needed, since the natural export format of Metashape is float32 for meshes. In most CRS, that results in point quantization in the 5cm-20cm range
144 | # In order to save finer detail, set shift_crs_to_cameras to True, and then apply the inverse shift to the mesh model to recreate the true CRS values
145 | # The shift used is reported using the save_metadata_xml mechanism of exportModel
146 | shift_crs_to_cameras: False
147 |
148 | buildDem: # (Metashape: buildDem, (optionally) classifyGroundPoints, exportRaster)
149 | enabled: True
150 | classify_ground_points: False # Should ground points be classified as part of this step? Note that an alternative is to calculate them as a part of buildPointCloud (above)
151 | surface: ["DTM-ptcloud", "DSM-ptcloud", "DSM-mesh"] # Options: "DTM-ptcloud", "DSM-ptcloud", and/or "DSM-mesh". Type of DEM to export and data to build it from (digital terrain model or digital surface model, and from point cloud or mesh)
152 | resolution: 0 # DSM resolution. Only affects DSM built using the mesh (other DSM types are set by Metashape and not customizable). Note that this also sets the resolution of the orthomosaic built from this DSM, which is 1/4 of the DSM resolution. If using a mesh-derived DSM, and you also desire an orthomosaic with maximal detail, set the DEM resolution to 4x your GSD. Set to 0 to use Metashape-determined default.
153 | export: True # Whether to export DEM(s)
154 | tiff_big: True # Use BigTIFF format? Required for larger projects with large DEMs
155 | tiff_tiled: False # Use tiled TIFF? This is related to internal file architecture.
156 | nodata: -32767 # Value used to represent nodata.
157 | tiff_overviews: True # Include coarse-scale raster data in file for quick display in GIS.
158 |
159 | buildOrthomosaic: # (Metashape: buildOrthomosaic, exportRaster)
160 | enabled: True
161 | surface: ["DTM-ptcloud", "DSM-ptcloud", "DSM-mesh", "Mesh"] # Options: "DTM-ptcloud", "DSM-ptcloud", "DSM-mesh", and/or "Mesh". The surface to build the orthomosaic onto. DTM and DSM refer to elevation models built by Metashape and must be configured to be computed via buildDem, above. Mesh refers to using the mesh model directly rather than first computing a DEM.
162 | blending: Metashape.MosaicBlending # Photo blending mode. Options include AverageBlending, MosaicBlending, MinBlending, MaxBlending, DisabledBlending
163 | fill_holes: True # Fill holes in orthomosaic where no photo data exist by interpolating?
164 | refine_seamlines: True # Use smart algorithm to identify photo seamlines where they will least distort.
165 | export: True # Whether to export orthomosaic(s)
166 | tiff_big: True # Use BigTIFF format? Required for larger projects with large DEMs
167 | tiff_tiled: True # Use tiled TIFF? This is related to internal file architecture. Tiled may be (semi-)equivalent to COG.
168 | nodata: -32767 # Value used to represent nodata.
169 | tiff_overviews: True # Include coarse-scale raster data in file for quick display in GIS.
170 | remove_after_export: True # Remove orthomosaic from project after export to reduce the metashape project file size
171 |
--------------------------------------------------------------------------------
/prior-versions/metashape_v1.6-1.8/R/prep_configs.R:
--------------------------------------------------------------------------------
1 | ### Author: Derek Young, UC Davis
2 |
3 | ### This script does the following:
4 | ### - Takes a base config YAML template and a set of alternate (derived) parameter values (partial YAMLs that only specify the parameters to change) and composes a set of config files to run in the metashape workflow)
5 |
6 | library(yaml)
7 | library(readr)
8 | library(stringr)
9 |
10 | #### Determine paths and read YAML files ####
11 |
12 | # If running manually, specify path to base and derived YAML templates
13 | manual_yaml_path = "/storage/forestuav/configs/set26"
14 | # also the path to metashape repo (this is used only in building the batch job script -- for the call to metashape)
15 | manual_metashape_path = "~/Documents/projects/metashape/python/metashape_workflow.py"
16 |
17 | ## read paths from command line argument (otherwise use the hard-coded defaults above)
18 | command_args = commandArgs(trailingOnly=TRUE)
19 |
20 | if(length(command_args) == 0) {
21 | yaml_path = manual_yaml_path
22 | metashape_path = manual_metashape_path
23 | } else if (length(command_args) == 1) {
24 | yaml_path = command_args[1]
25 | } else {
26 | metashape_path = command_args[2]
27 | yaml_path = command_args[1]
28 | }
29 |
30 | ## Read YAML files
31 | base_yaml_path = paste0(yaml_path,"/","base.yml")
32 | derived_yaml_path = paste0(yaml_path,"/","derived.yml")
33 |
34 | base = read_yaml(base_yaml_path)
35 |
36 | # read derived config lines as vector
37 | derived_data = read_lines(derived_yaml_path, skip_empty_rows = TRUE)
38 |
39 |
40 |
41 | #### Store each derived set as a separate R object (interpreted from YAML) ####
42 |
43 | # remove newlines at start of each line
44 | derived_data = str_replace(derived_data,"^\n","")
45 |
46 | # search for vector elements (lines) that indicate the start of a derived parameter set
47 | start_rows = grep("^####CONFIG", derived_data)
48 |
49 | # Vector to store config file locations to create a shell script
50 | config_files = NULL
51 |
52 | # For each derived parameter set, replace the base parameters with the provided derived parameters, and write a config file
53 | for(i in 1:length(start_rows)) {
54 |
55 | # get first line of the current derived parameter set
56 | first_line = start_rows[i] + 1
57 |
58 | # get last line of the current derived parameter set
59 | if(i != length(start_rows)) {
60 | last_line = start_rows[i+1] - 1
61 | } else {
62 | last_line = length(derived_data)
63 | }
64 |
65 |
66 | ## save as R object
67 | yaml_string = paste(derived_data[first_line:last_line],collapse="\n")
68 | derived_focal = yaml.load(yaml_string)
69 |
70 | ## take the template and replace each element specified in the derived with the value specified in the derived
71 | base_derived = modifyList(base,derived_focal)
72 |
73 |
74 | ## get the number (ID) of the derived set (just use the run name from the YAML)
75 | id = base_derived$run_name
76 |
77 | ## write the derived set with its ID number
78 | filename = paste0(yaml_path,"/cfg_",id,".yml")
79 |
80 | write_yaml(base_derived,filename)
81 |
82 | config_files = c(config_files,filename)
83 |
84 |
85 | }
86 |
87 |
88 | ## make a shell script to run all the config files (assume WD is the metashape repo)
89 | shell_lines = paste0("python ", metashape_path, " ", config_files)
90 |
91 | writeLines(shell_lines,
92 | con = paste0(yaml_path,"/config_batch.sh"), sep="\n")
93 |
--------------------------------------------------------------------------------
/prior-versions/metashape_v1.6-1.8/R/prep_gcps.R:
--------------------------------------------------------------------------------
1 | ### Author: Derek Young, UC Davis
2 |
3 | ### This script does the following:
4 | ### - Loads a user-created data file of the image files (and coordinates in each image) where GCPs are located
5 | ### - Loads a geospatial file containing the locations of the GCPs
6 | ### - Loads a 10 m DEM and extracts elevation values at each GCP
7 | ### - Compiles all of the above into a file needed by Metashape for its GCP workflow
8 | ### - Produces a PDF that shows each GCP image and the location of the GCP for QAQC
9 |
10 |
11 | ### This script requires the following folders/files in the main mission imagery directory (the one containing 100MEDIA etc):
12 | ### - gcps/raw/gcps.gpkg : geospatial data file with each gcp id in the column "gcp_id". Must be in the same projection as the whole Metashape project, and must be in meters xy (usually a UTM zone).
13 | ### - gcps/raw/gcp_imagecoords.csv : data file listing the images and coordinates in each where a GCP is visible (created manually by technician via imagery inspection)
14 | ### - dem_usgs/dem_usgs.tif : 10 m USGS dem extending well beyond the project flight area
15 |
16 | ### This script assumes all images to be linked to GCPs have standard DJI naming and directory structure ("100MEDIA/DJI_xxxx.JPG").
17 | ### In input data file gcp_imagecoords.csv, images are specified without "DJI_", leading zeros, and ".JPG". The image directory is specified without "MEDIA"
18 |
19 |
20 | #### Load packages ####
21 |
22 | library(sf)
23 | library(raster)
24 | library(dplyr)
25 | library(stringr)
26 | library(magick)
27 | library(ggplot2)
28 |
29 | #### User-defined vars (only used when running interactivesly) ####
30 |
31 | dir_manual = "/home/derek/Downloads/crater_gcps"
32 |
33 |
34 |
35 | #### Load data ####
36 |
37 | ### All relevant GCP data should be in the top-level mission imagery folder
38 | ### Load folder from the command line argument
39 |
40 |
41 | dir = commandArgs(trailingOnly=TRUE)
42 |
43 | if(length(dir) == 0) {
44 | dir = dir_manual
45 | }
46 |
47 | gcps = read_sf(paste0(dir,"/gcps/raw/gcps.geojson"))
48 | imagecoords = read.csv(paste0(dir,"/gcps/raw/gcp_imagecoords.csv"),header=TRUE,stringsAsFactors=FALSE)
49 | dem_usgs = raster(paste0(dir,"/dem_usgs/dem_usgs.tif"))
50 |
51 | # remove blank lines from image coords file
52 | imagecoords = imagecoords %>%
53 | filter(!is.na(x))
54 |
55 |
56 | #### Make prepared data directory if it doesn't ecist ####
57 | dir.create(paste0(dir,"/gcps/prepared"),showWarnings=FALSE)
58 |
59 |
60 |
61 | #### Create GCP table in the format required by metashape_control and metashape_functions ####
62 |
63 | # Extract elev
64 | gcp_table = gcps
65 | gcp_table$elev = suppressWarnings(extract(dem_usgs,gcp_table,method="bilinear"))
66 |
67 | # Extract coords
68 | coords = st_coordinates(gcp_table)
69 | gcp_table = cbind(gcp_table,coords)
70 |
71 | # Remove geospatial info
72 | st_geometry(gcp_table) = NULL
73 |
74 | # Reorder columns, add "point" prefix to GCP names
75 | gcp_table = gcp_table %>%
76 | dplyr::select(gcp_id,x=X,y=Y,elev) %>%
77 | dplyr::mutate(gcp_id = paste0("point",gcp_id))
78 |
79 | write.table(gcp_table,paste0(dir,"/gcps/prepared/gcp_table.csv"),row.names=FALSE,col.names=FALSE,sep=",")
80 |
81 |
82 | #### Create image coordinate-to-gcp table in the format required by metashape_control and metashape_functions ####
83 |
84 | imagecoords_table = imagecoords %>%
85 | mutate(gcp_id = paste0("point",gcp)) %>%
86 | mutate(image_text = paste0("DJI_",str_pad(image_file,4,pad="0"),".JPG")) %>%
87 | mutate(part_text = paste0("PART_",str_pad(part_folder,2,pad="0"))) %>%
88 | mutate(folder_text = paste0(media_folder,"MEDIA")) %>%
89 | mutate(image_path = paste0(part_text,"/",folder_text,"/",image_text)) %>%
90 | dplyr::select(gcp_id,image_path,x,y) %>%
91 | arrange(gcp_id,image_path)
92 |
93 | # remove blank lines from image coords file
94 | imagecoords = imagecoords %>%
95 | filter(!is.na(gcp))
96 |
97 |
98 | write.table(imagecoords_table,paste0(dir,"/gcps/prepared/gcp_imagecoords_table.csv"),row.names=FALSE,col.names=FALSE,sep=",")
99 |
100 |
101 | #### Export a PDF of images with the GCP circled on each ####
102 |
103 |
104 | pdf(paste0(dir,"/gcps/prepared/gcp_qaqc.pdf"))
105 |
106 | for(i in 1:nrow(imagecoords_table)) {
107 |
108 | imagecoords_row = imagecoords_table[i,]
109 |
110 | img = image_read(paste0(dir,"/",imagecoords_row$image_path))
111 | img = image_scale(img,"10%")
112 | img = image_flip(img)
113 |
114 | img_x = imagecoords_row$x/10
115 | img_y = imagecoords_row$y/10
116 | img_gcp = imagecoords_row$gcp_id
117 | img_path = imagecoords_row$image_path
118 |
119 | map = image_ggplot(img) +
120 | geom_point(x=img_x,y=img_y,size=20,pch=1,fill=NA,color="red",stroke=1) +
121 | geom_point(x=img_x,y=img_y,size=20,pch=3,fill=NA,color="red",stroke=0.5) +
122 | labs(title=paste0(img_gcp,"\n",img_path))
123 |
124 | print(map)
125 |
126 | cat("Completed GCP ", i, " of ",nrow(imagecoords_table),"\r")
127 |
128 | }
129 |
130 | garbage = dev.off()
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/prior-versions/metashape_v1.6-1.8/config/base.yml:
--------------------------------------------------------------------------------
1 | # This is an example yaml configuration for a metashape run
2 |
3 | #### Project-level parameters:
4 |
5 | # Project to load. If not a blank string, this will open an existing project at the path specified. If a blank string, creates a new empty project.
6 | # Even if opening an existing project, all processing on it is saved as a new project (path and name specified below). The original project file is not modified.
7 | load_project: ""
8 |
9 | # The path to the directory of flight photos
10 | # If there are multiple photo folders, set path to the folder that contains all the photo folders
11 | # If there are no photos to add (e.g., this is an existing project that already has photos in it, set to an empty string ("")
12 | photo_path: "/storage/uav/photosets/sample_rgb_photoset"
13 | multispectral: False # Is this a multispectral photo set? If RGB, set to False.
14 |
15 | # Path for exports (e.g., points, DSM, orthomosaic) and processing log. Will be created if does not exist.
16 | output_path: "/storage/uav/metashape_outputs/sample"
17 |
18 | # Path to save Metashape project file (.psx). Will be created if does not exist
19 | project_path: "/storage/uav/metashape_projects/sample"
20 |
21 | # The identifier for the run. Will be used in naming output files. Recommended to include a photoset name and processing parameter set name.
22 | run_name: "sample_rgb_photoset_run001"
23 |
24 | # CRS EPSG code that project outputs should be in (projection should be in meter units and intended for the project area)
25 | project_crs: "EPSG::26910" # 26910 is UTM 10N
26 |
27 | # Enable metashape "fine-level task subdivision" which reduces memory use by breaking processing into independent chunks that are run in series.
28 | # Assuming there's enough memory, it seems to run 10-20% faster by disabling subdividing. But large projects can run out memory and fail if subdivide is not enabled.
29 | subdivide_task: True
30 |
31 | # Should CUDA GPU driver be used? Alternative is OpenCL. Metashape uses CUDA by default but we have observed it can cause crashes on HPC infrastructure
32 | use_cuda: True # Recommended: True (Metashape default)
33 |
34 | # What value to use for the Metashape tweak "depth_max_gpu_multiplier"? May help to mitigate GPU errors per: https://www.agisoft.com/forum/index.php?topic=11771.0
35 | gpu_multiplier: 2 # Recommended: 2 (Metashape default)
36 |
37 | #### Processing parameters:
38 | ## Steps can be run or skipped using the 'enabled' parameter. If enabled == False, everything else in the step is irrelevant.
39 | ## The metashape functions powering each of these steps are listed in the comments in parentheses.
40 | ## Refer to Metashape documentation for full parameter definitions: https://www.agisoft.com/pdf/metashape_python_api_1_5_0.pdf
41 | ## Parameter names here generally follow the parameter names of the Metashape functions.
42 |
43 | ### Whether to use image EXIF RTK flags to make image geospatial accuracy more precise
44 | use_rtk: True # Recommended: True
45 | fix_accuracy: 3
46 | nofix_accuracy: 25
47 |
48 | # To use GCPs, a 'gcps' folder must exist in the top level photos folder. The contents of the 'gcps' folder are created by the prep_gcps.R script. See readme: https://github.com/ucdavis/metashape
49 | addGCPs:
50 | enabled: True
51 | gcp_crs: "EPSG::26910" # CRS EPSG code of GCP coordinates. 26910 (UTM 10 N) is the CRS of the sample RGB photoset.
52 | marker_location_accuracy: 0.1 # Recommended: 0.1. Accuracy of GCPs real-world coordinates, in meters.
53 | marker_projection_accuracy: 8 # Recommended: 8. Accuracy of the identified locations of the GCPs within the images, in pixels.
54 | optimize_w_gcps_only: True # Optimize alignment using GCPs only: required for GCP locations to take precedence over photo GPS data. Disabling it makes GCPs essentially irrelevant.
55 |
56 | calibrateReflectance: # (Metahsape: calibrateReflectance)
57 | enabled: True
58 | panel_filename: "RP04-1923118-OB.csv" # The calibration file must be in the "calibration" folder in the top-level project photos directory. See example panel calibration file in the calibration directory of project repo.
59 | use_reflectance_panels: True
60 | use_sun_sensor: True
61 |
62 | alignPhotos: # (Metashape: alignPhotos)
63 | enabled: True
64 | downscale: 2 # Recommended: 2. How much to coarsen the photos when searching for tie points. Higher number for blurrier photos or when there are small surfces that may move between photos (such as leaves). Accepts numbers 2^x (and zero) (https://www.agisoft.com/forum/index.php?topic=11697.0).
65 | adaptive_fitting: True # Recommended: True. Should the camera lens model be fit at the same time as aligning photos?
66 | keep_keypoints: True # Recommended: True. Should keypoints from matching photos be stored in the project? Required if you later want to add more photos and align them to the previously aligned photos without redoing the original alignment.
67 | reset_alignment: False # Recommended: False. When running an alignment, if any of the photos were already aligned, should we keep that alignment? Or reset it so we align everything anew?
68 |
69 | filterPointsUSGS:
70 | enabled: False
71 | rec_thresh_percent: 20
72 | rec_thresh_absolute: 15
73 | proj_thresh_percent: 30
74 | proj_thresh_absolute: 2
75 | reproj_thresh_percent: 5
76 | reproj_thresh_absolute: 0.3
77 |
78 | optimizeCameras: # (Metashape: optimizeCameras)
79 | enabled: True
80 | adaptive_fitting: True # Recommended: True. Should the camera lens model be fit at the same time as optinizing photos?
81 |
82 | buildDenseCloud: # (Metashape: buildDepthMaps, buildDenseCloud, (optionally) classifyGroundPoints, and exportPoints)
83 | enabled: True
84 | ## For depth maps (buldDepthMaps)
85 | downscale: 2 # Recommended: 2. How much to coarsen the photos when searching for matches to build the dense cloud. For large photosets, values < 4 likely take prohibitively long. Accepts numbers 2^x (https://www.agisoft.com/forum/index.php?topic=11697.0).
86 | filter_mode: Metashape.MildFiltering # Recommended: Metashape.MildFiltering. How to filter the point cloud. Options are NoFiltering, MildFiltering, ModerateFiltering, AggressiveFiltering. Aggressive filtering removes detail and makes worse DEMs (at least for forest). NoFiltering takes very long. In trials, it never completed.
87 | reuse_depth: False # Recommended: False. Purpose unknown.
88 | ## For dense cloud (buildDenseCloud)
89 | keep_depth: False # Recommended: False. Purpose unknown.
90 | ## For both
91 | max_neighbors: 100 # Recommended: 100. Maximum number of neighboring photos to use for estimating point cloud. Higher numbers may increase accuracy but dramatically increase processing time.
92 | ## For ground point classification (classifyGroundPoints). Definitions here: https://www.agisoft.com/forum/index.php?topic=9328.0
93 | classify_ground_points: False # Should ground points be classified as a part of this step? Must be enabled (either here or in buldDem, below) if a digital terrain model (DTM) is needed either for orthomosaic or DTM export. Enabling here is an alternative to enabling as a component of buildDem (below). It depends on which stage you want the classification to be done at. If you already have a point cloud but it's unclassified, then don't do it as part of this stage as it would require computing the point cloud again.
94 | ## For dense cloud export (exportPoints)
95 | export: True # Whether to export dense cloud file.
96 | format: Metashape.PointsFormatLAS # Recommended: PointsFormatLAS. The file format to export points in.
97 | classes: "ALL" # Recommended: "ALL". Point classes to export. Must be a list. Or can set to "ALL" to use all points. An example of a specific class is: Metashape.PointClass.Ground
98 |
99 | classifyGroundPoints: # (Meatshape: classifyGroundPoints) # classify points, IF SPECIFIED as a component of buildDenseCloud (above) or buldDem (below). Must be enabled (either here or in buldDem, below) if a digital terrain model (DTM) is needed either for orthomosaic or DTM export.
100 | max_angle: 15.0 # Recommended: 15.0
101 | max_distance: 1.0 # Recommended: 1.0
102 | cell_size: 50.0 # Recommended: 50.0
103 |
104 | buildDem: # (Metashape: buildDem, (optionally) classifyGroundPoints, exportRaster)
105 | enabled: True
106 | classify_ground_points: True # Should ground points be classified as part of this step? Note that an alternative is to calculate them as a part of buildDenseCloud (above)
107 | ## For building DEM (buildDem)
108 | type: "both" # Recommended: "both". Options: "DSM" or "DTM" or "both". Type of DEM to exporot (digital surface model, digital terrain model, or both).
109 | ## For exporting DEM (exportRaster)
110 | export: True # Whether to export DEM(s)
111 | tiff_big: True # Recommended: True. Use BigTIFF format? Required for larger projects with large DEMs
112 | tiff_tiled: False # Recommended: False. Use tiled TIFF? This is related to internal file architecture.
113 | nodata: -32767 # Recommended: -32767. Value used to represent nodata.
114 | tiff_overviews: True # Recommended: True. Include coarse-scale raster data in file for quick display in GIS.
115 |
116 | buildOrthomosaic: # (Metashape: buildOrthomosaic, exportRaster)
117 | enabled: True
118 | ## For building orthomosaic (buildOrthomosaic)
119 | surface: "USGS" # Recommended: "USGS" (assuming a USGS DEM is available and GCPs with accurate elvevation data are being used). The surface to build the orthomosaic onto. "DTM", "DSM", "USGS", or "DTMandDSM. DTM and DSM refer to elevation models built by Metashape (buildDem step above) and stored in the project. If USGS, you must use GCPs with accurate elevations (ideally extracted from the USGS DEM).
120 | usgs_dem_path: "dem_usgs/dem_usgs.tif" # Path to USGS DEM for the project area. Needed if surface (parameter above) is "USGS".
121 | usgs_dem_crs: "EPSG::4269" # CRS of the USGS DEM. Needed if surface (parameter above) is "USGS". For sample RGB photoset, crs is 4269 (Geographic NAD83)
122 | blending: Metashape.MosaicBlending # Recommended: Metashape.MosaicBlending. Photo blending mode. Options include AverageBlending, MosaicBlending, MinBlending, MaxBlending, DisabledBlending
123 | fill_holes: True # Recommended: True. Fill holes in orthomosaic where no photo data exist by interpolating?
124 | refine_seamlines: True # Recommended: True. Use smart algorithm to identify photo seamlines where they will least distort.
125 | ## For exporting orthomosaic (exportRaster)
126 | export: True # Whether to export orthomosaic
127 | tiff_big: True # Recommended: True. Use BigTIFF format? Required for larger projects with large DEMs
128 | tiff_tiled: False # Recommended: False. Use tiled TIFF? This is related to internal file architecture.
129 | nodata: -32767 # Recommended: -32767. Value used to represent nodata.
130 | tiff_overviews: True # Recommended: True. Include coarse-scale raster data in file for quick display in GIS.
131 |
--------------------------------------------------------------------------------
/prior-versions/metashape_v1.6-1.8/config/derived.yml:
--------------------------------------------------------------------------------
1 | ####CONFIG_0001####
2 | photo_path: "/storage/uav/photosets/sample_rgb_photoset_01"
3 | multispectral: False
4 | buildDenseCloud: # (Metashape: buildDepthMaps, buildDenseCloud, classifyGroundPoints, and exportPoints)
5 | enabled: False
6 |
7 | ####CONFIG_0002####
8 | photo_path: "/storage/uav/photosets/sample_rgb_photoset_02"
9 | multispectral: True
10 |
11 | ####CONFIG_0003####
12 | photo_path: "/storage/uav/photosets/sample_rgb_photoset_03"
13 | multispectral: False
14 |
--------------------------------------------------------------------------------
/prior-versions/metashape_v1.6-1.8/config/example.yml:
--------------------------------------------------------------------------------
1 | # This is an example yaml configuration for a metashape run
2 |
3 | #### Project-level parameters:
4 |
5 | # Project to load. If not a blank string, this will open an existing project at the path specified. If a blank string, creates a new empty project.
6 | # Even if opening an existing project, all processing on it is saved as a new project (path and name specified below). The original project file is not modified.
7 | load_project: ""
8 |
9 | # The path to the directory of flight photos
10 | # If there are multiple photo folders, set path to the folder that contains all the photo folders
11 | # If there are no photos to add (e.g., this is an existing project that already has photos in it, set to an empty string ("")
12 | photo_path: "/storage/uav/photosets/sample_rgb_photoset"
13 | multispectral: False # Is this a multispectral photo set? If RGB, set to False.
14 |
15 | # Path for exports (e.g., points, DSM, orthomosaic) and processing log. Will be created if does not exist.
16 | output_path: "/storage/uav/metashape_outputs/sample"
17 |
18 | # Path to save Metashape project file (.psx). Will be created if does not exist
19 | project_path: "/storage/uav/metashape_projects/sample"
20 |
21 | # The identifier for the run. Will be used in naming output files. Recommended to include a photoset name and processing parameter set name.
22 | run_name: "sample_rgb_photoset_run001"
23 |
24 | # CRS EPSG code that project outputs should be in (projection should be in meter units and intended for the project area)
25 | project_crs: "EPSG::26910" # 26910 is UTM 10N
26 |
27 | # Enable metashape "fine-level task subdivision" which reduces memory use by breaking processing into independent chunks that are run in series.
28 | # Assuming there's enough memory, it seems to run 10-20% faster by disabling subdividing. But large projects can run out memory and fail if subdivide is not enabled.
29 | subdivide_task: True
30 |
31 | # Should CUDA GPU driver be used? Alternative is OpenCL. Metashape uses CUDA by default but we have observed it can cause crashes on HPC infrastructure
32 | use_cuda: True # Recommended: True (Metashape default)
33 |
34 | # What value to use for the Metashape tweak "depth_max_gpu_multiplier"? May help to mitigate GPU errors per: https://www.agisoft.com/forum/index.php?topic=11771.0
35 | gpu_multiplier: 2 # Recommended: 2 (Metashape default)
36 |
37 | #### Processing parameters:
38 | ## Steps can be run or skipped using the 'enabled' parameter. If enabled == False, everything else in the step is irrelevant.
39 | ## The metashape functions powering each of these steps are listed in the comments in parentheses.
40 | ## Refer to Metashape documentation for full parameter definitions: https://www.agisoft.com/pdf/metashape_python_api_1_5_0.pdf
41 | ## Parameter names here generally follow the parameter names of the Metashape functions.
42 |
43 | ### Whether to use image EXIF RTK flags to make image geospatial accuracy more precise
44 | use_rtk: True # Recommended: True
45 | fix_accuracy: 3
46 | nofix_accuracy: 25
47 |
48 | # To use GCPs, a 'gcps' folder must exist in the top level photos folder. The contents of the 'gcps' folder are created by the prep_gcps.R script. See readme: https://github.com/ucdavis/metashape
49 | addGCPs:
50 | enabled: True
51 | gcp_crs: "EPSG::26910" # CRS EPSG code of GCP coordinates. 26910 (UTM 10 N) is the CRS of the sample RGB photoset.
52 | marker_location_accuracy: 0.1 # Recommended: 0.1. Accuracy of GCPs real-world coordinates, in meters.
53 | marker_projection_accuracy: 8 # Recommended: 8. Accuracy of the identified locations of the GCPs within the images, in pixels.
54 | optimize_w_gcps_only: True # Optimize alignment using GCPs only: required for GCP locations to take precedence over photo GPS data. Disabling it makes GCPs essentially irrelevant.
55 |
56 | calibrateReflectance: # (Metahsape: calibrateReflectance)
57 | enabled: True
58 | panel_filename: "RP04-1923118-OB.csv" # The calibration file must be in the "calibration" folder in the top-level project photos directory. See example panel calibration file in the calibration directory of project repo.
59 | use_reflectance_panels: True
60 | use_sun_sensor: True
61 |
62 | alignPhotos: # (Metashape: alignPhotos)
63 | enabled: True
64 | downscale: 2 # Recommended: 2. How much to coarsen the photos when searching for tie points. Higher number for blurrier photos or when there are small surfces that may move between photos (such as leaves). Accepts numbers 2^x (and zero) (https://www.agisoft.com/forum/index.php?topic=11697.0).
65 | adaptive_fitting: True # Recommended: True. Should the camera lens model be fit at the same time as aligning photos?
66 | keep_keypoints: True # Recommended: True. Should keypoints from matching photos be stored in the project? Required if you later want to add more photos and align them to the previously aligned photos without redoing the original alignment.
67 | reset_alignment: False # Recommended: False. When running an alignment, if any of the photos were already aligned, should we keep that alignment? Or reset it so we align everything anew?
68 |
69 | filterPointsUSGS:
70 | enabled: False
71 | rec_thresh_percent: 20
72 | rec_thresh_absolute: 15
73 | proj_thresh_percent: 30
74 | proj_thresh_absolute: 2
75 | reproj_thresh_percent: 5
76 | reproj_thresh_absolute: 0.3
77 |
78 | optimizeCameras: # (Metashape: optimizeCameras)
79 | enabled: True
80 | adaptive_fitting: True # Recommended: True. Should the camera lens model be fit at the same time as optinizing photos?
81 |
82 | buildDenseCloud: # (Metashape: buildDepthMaps, buildDenseCloud, (optionally) classifyGroundPoints, and exportPoints)
83 | enabled: True
84 | ## For depth maps (buldDepthMaps)
85 | downscale: 2 # Recommended: 2. How much to coarsen the photos when searching for matches to build the dense cloud. For large photosets, values < 4 likely take prohibitively long. Accepts numbers 2^x (https://www.agisoft.com/forum/index.php?topic=11697.0).
86 | filter_mode: Metashape.MildFiltering # Recommended: Metashape.MildFiltering. How to filter the point cloud. Options are NoFiltering, MildFiltering, ModerateFiltering, AggressiveFiltering. Aggressive filtering removes detail and makes worse DEMs (at least for forest). NoFiltering takes very long. In trials, it never completed.
87 | reuse_depth: False # Recommended: False. Purpose unknown.
88 | ## For dense cloud (buildDenseCloud)
89 | keep_depth: False # Recommended: False. Purpose unknown.
90 | ## For both
91 | max_neighbors: 100 # Recommended: 100. Maximum number of neighboring photos to use for estimating point cloud. Higher numbers may increase accuracy but dramatically increase processing time.
92 | ## For ground point classification (classifyGroundPoints). Definitions here: https://www.agisoft.com/forum/index.php?topic=9328.0
93 | classify_ground_points: False # Should ground points be classified as a part of this step? Must be enabled (either here or in buldDem, below) if a digital terrain model (DTM) is needed either for orthomosaic or DTM export. Enabling here is an alternative to enabling as a component of buildDem (below). It depends on which stage you want the classification to be done at. If you already have a point cloud but it's unclassified, then don't do it as part of this stage as it would require computing the point cloud again.
94 | ## For dense cloud export (exportPoints)
95 | export: True # Whether to export dense cloud file.
96 | format: Metashape.PointsFormatLAS # Recommended: PointsFormatLAS. The file format to export points in.
97 | classes: "ALL" # Recommended: "ALL". Point classes to export. Must be a list. Or can set to "ALL" to use all points. An example of a specific class is: Metashape.PointClass.Ground
98 |
99 | classifyGroundPoints: # (Meatshape: classifyGroundPoints) # classify points, IF SPECIFIED as a component of buildDenseCloud (above) or buldDem (below). Must be enabled (either here or in buldDem, below) if a digital terrain model (DTM) is needed either for orthomosaic or DTM export.
100 | max_angle: 15.0 # Recommended: 15.0
101 | max_distance: 1.0 # Recommended: 1.0
102 | cell_size: 50.0 # Recommended: 50.0
103 |
104 | buildDem: # (Metashape: buildDem, (optionally) classifyGroundPoints, exportRaster)
105 | enabled: True
106 | classify_ground_points: True # Should ground points be classified as part of this step? Note that an alternative is to calculate them as a part of buildDenseCloud (above)
107 | ## For building DEM (buildDem)
108 | type: "both" # Recommended: "both". Options: "DSM" or "DTM" or "both". Type of DEM to exporot (digital surface model, digital terrain model, or both).
109 | ## For exporting DEM (exportRaster)
110 | export: True # Whether to export DEM(s)
111 | tiff_big: True # Recommended: True. Use BigTIFF format? Required for larger projects with large DEMs
112 | tiff_tiled: False # Recommended: False. Use tiled TIFF? This is related to internal file architecture.
113 | nodata: -32767 # Recommended: -32767. Value used to represent nodata.
114 | tiff_overviews: True # Recommended: True. Include coarse-scale raster data in file for quick display in GIS.
115 |
116 | buildOrthomosaic: # (Metashape: buildOrthomosaic, exportRaster)
117 | enabled: True
118 | ## For building orthomosaic (buildOrthomosaic)
119 | surface: "USGS" # Recommended: "USGS" (assuming a USGS DEM is available and GCPs with accurate elvevation data are being used). The surface to build the orthomosaic onto. "DTM", "DSM", "USGS", or "DTMandDSM. DTM and DSM refer to elevation models built by Metashape (buildDem step above) and stored in the project. If USGS, you must use GCPs with accurate elevations (ideally extracted from the USGS DEM).
120 | usgs_dem_path: "dem_usgs/dem_usgs.tif" # Path to USGS DEM for the project area. Needed if surface (parameter above) is "USGS".
121 | usgs_dem_crs: "EPSG::4269" # CRS of the USGS DEM. Needed if surface (parameter above) is "USGS". For sample RGB photoset, crs is 4269 (Geographic NAD83)
122 | blending: Metashape.MosaicBlending # Recommended: Metashape.MosaicBlending. Photo blending mode. Options include AverageBlending, MosaicBlending, MinBlending, MaxBlending, DisabledBlending
123 | fill_holes: True # Recommended: True. Fill holes in orthomosaic where no photo data exist by interpolating?
124 | refine_seamlines: True # Recommended: True. Use smart algorithm to identify photo seamlines where they will least distort.
125 | ## For exporting orthomosaic (exportRaster)
126 | export: True # Whether to export orthomosaic
127 | tiff_big: True # Recommended: True. Use BigTIFF format? Required for larger projects with large DEMs
128 | tiff_tiled: False # Recommended: False. Use tiled TIFF? This is related to internal file architecture.
129 | nodata: -32767 # Recommended: -32767. Value used to represent nodata.
130 | tiff_overviews: True # Recommended: True. Include coarse-scale raster data in file for quick display in GIS.
131 |
--------------------------------------------------------------------------------
/prior-versions/metashape_v1.6-1.8/python/metashape_workflow.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # File for running a metashape workflow
3 |
4 | # Derek Young and Alex Mandel
5 | # University of California, Davis
6 | # 2021
7 |
8 | import sys
9 |
10 | # ---- If this is a first run from the standalone python module, need to copy the license file from the full metashape install: from python import metashape_license_setup
11 |
12 | ## Define where to get the config file (only used if running interactively)
13 | manual_config_file = "config/example_dev.yml"
14 | # ---- If not running interactively, the config file should be supplied as the command-line argument after the python script, e.g.: python metashape_workflow.py config.yml
15 |
16 |
17 | ## Load custom modules and config file: slightly different depending whether running interactively or via command line
18 | try: # running interactively (in linux) or command line (windows)
19 | from python import metashape_workflow_functions as meta
20 | from python import read_yaml
21 | except: # running from command line (in linux) or interactively (windows)
22 | import metashape_workflow_functions as meta
23 | import read_yaml
24 |
25 | if sys.stdin.isatty():
26 | config_file = sys.argv[1]
27 | else:
28 | config_file = manual_config_file
29 |
30 | ## Parse the config file
31 | cfg = read_yaml.read_yaml(config_file)
32 |
33 | ### Run the Metashape workflow
34 |
35 | doc, log, run_id = meta.project_setup(cfg)
36 |
37 | meta.enable_and_log_gpu(log, cfg)
38 |
39 | if cfg["photo_path"] != "": # only add photos if there is a photo directory listed
40 | meta.add_photos(doc, cfg)
41 |
42 | if cfg["calibrateReflectance"]["enabled"]:
43 | meta.calibrate_reflectance(doc, cfg)
44 |
45 | if cfg["alignPhotos"]["enabled"]:
46 | meta.align_photos(doc, log, cfg)
47 | meta.reset_region(doc)
48 |
49 | if cfg["filterPointsUSGS"]["enabled"]:
50 | meta.filter_points_usgs_part1(doc, cfg)
51 | meta.reset_region(doc)
52 |
53 | if cfg["addGCPs"]["enabled"]:
54 | meta.add_gcps(doc, cfg)
55 | meta.reset_region(doc)
56 |
57 | if cfg["optimizeCameras"]["enabled"]:
58 | meta.optimize_cameras(doc, cfg)
59 | meta.reset_region(doc)
60 |
61 | if cfg["filterPointsUSGS"]["enabled"]:
62 | meta.filter_points_usgs_part2(doc, cfg)
63 | meta.reset_region(doc)
64 |
65 | if cfg["buildDenseCloud"]["enabled"]:
66 | meta.build_dense_cloud(doc, log, run_id, cfg)
67 |
68 | if cfg["buildDem"]["enabled"]:
69 | meta.build_dem(doc, log, run_id, cfg)
70 |
71 | if cfg["buildOrthomosaic"]["enabled"]:
72 | meta.build_orthomosaics(doc, log, run_id, cfg)
73 |
74 | meta.export_report(doc, run_id, cfg)
75 |
76 | meta.finish_run(log, config_file)
77 |
--------------------------------------------------------------------------------
/prior-versions/metashape_v1.6-1.8/python/metashape_workflow_functions.py:
--------------------------------------------------------------------------------
1 | # Derek Young and Alex Mandel
2 | # University of California, Davis
3 | # 2021
4 |
5 | #### Import libraries
6 |
7 | import datetime
8 | import glob
9 | import os
10 | import platform
11 | import re
12 |
13 | # import the fuctionality we need to make time stamps to measure performance
14 | import time
15 |
16 | ### import the Metashape functionality
17 | import Metashape
18 | import yaml
19 |
20 | #### Helper functions and globals
21 |
22 | # Set the log file name-value separator
23 | # Chose ; as : is in timestamps
24 | # TODO: Consider moving log to json/yaml formatting using a dict
25 | sep = "; "
26 |
27 |
28 | def stamp_time():
29 | """
30 | Format the timestamps as needed
31 | """
32 | stamp = datetime.datetime.now().strftime("%Y%m%dT%H%M")
33 | return stamp
34 |
35 |
36 | def diff_time(t2, t1):
37 | """
38 | Give a end and start time, subtract, and round
39 | """
40 | total = str(round(t2 - t1, 1))
41 | return total
42 |
43 |
44 | # Used by add_gcps function
45 | def get_marker(chunk, label):
46 | for marker in chunk.markers:
47 | if marker.label == label:
48 | return marker
49 | return None
50 |
51 |
52 | # Used by add_gcps function
53 | def get_camera(chunk, label):
54 | for camera in chunk.cameras:
55 | if camera.label.lower() == label.lower():
56 | return camera
57 | return None
58 |
59 |
60 | #### Functions for each major step in Metashape
61 |
62 |
63 | def project_setup(cfg):
64 | """
65 | Create output and project paths, if they don't exist
66 | Define a project ID based on photoset name and timestamp
67 | Define a project filename and a log filename
68 | Create the project
69 | Start a log file
70 | """
71 |
72 | # Make project directories (necessary even if loading an existing project because this workflow saves a new project based on the old one, leaving the old one intact
73 | if not os.path.exists(cfg["output_path"]):
74 | os.makedirs(cfg["output_path"])
75 | if not os.path.exists(cfg["project_path"]):
76 | os.makedirs(cfg["project_path"])
77 |
78 | ### Set a filename template for project files and output files
79 | ## Get the first parts of the filename (the photoset ID and location string)
80 |
81 | run_name = cfg["run_name"]
82 |
83 | ## Project file example to make: "projectID_YYYYMMDDtHHMM-jobID.psx"
84 | timestamp = stamp_time()
85 | run_id = "_".join([run_name, timestamp])
86 | # TODO: If there is a slurm JobID, append to time (separated with "-", not "_"). This will keep jobs initiated in the same minute distinct
87 |
88 | project_file = os.path.join(cfg["project_path"], ".".join([run_id, "psx"]))
89 | log_file = os.path.join(cfg["output_path"], ".".join([run_id + "_log", "txt"]))
90 |
91 | """
92 | Create a doc and a chunk
93 | """
94 |
95 | # create a handle to the Metashape object
96 | doc = (
97 | Metashape.Document()
98 | ) # When running via Metashape, can use: doc = Metashape.app.document
99 |
100 | # If specified, open existing project
101 | if cfg["load_project"] != "":
102 | doc.open(cfg["load_project"])
103 | else:
104 | # Initialize a chunk, set its CRS as specified
105 | chunk = doc.addChunk()
106 | chunk.crs = Metashape.CoordinateSystem(cfg["project_crs"])
107 | chunk.marker_crs = Metashape.CoordinateSystem(cfg["addGCPs"]["gcp_crs"])
108 |
109 | # Save doc doc as new project (even if we opened an existing project, save as a separate one so the existing project remains accessible in its original state)
110 | doc.save(project_file)
111 |
112 | """
113 | Log specs except for GPU
114 | """
115 |
116 | # log Metashape version, CPU specs, time, and project location to results file
117 | # open the results file
118 | # TODO: records the Slurm values for actual cpus and ram allocated
119 | # https://slurm.schedmd.com/sbatch.html#lbAI
120 | with open(log_file, "a") as file:
121 |
122 | # write a line with the Metashape version
123 | file.write(sep.join(["Project", run_id]) + "\n")
124 | file.write(
125 | sep.join(["Agisoft Metashape Professional Version", Metashape.app.version])
126 | + "\n"
127 | )
128 | # write a line with the date and time
129 | file.write(sep.join(["Processing started", stamp_time()]) + "\n")
130 | # write a line with CPU info - if possible, improve the way the CPU info is found / recorded
131 | file.write(sep.join(["Node", platform.node()]) + "\n")
132 | file.write(sep.join(["CPU", platform.processor()]) + "\n")
133 | # write two lines with GPU info: count and model names - this takes multiple steps to make it look clean in the end
134 |
135 | return doc, log_file, run_id
136 |
137 |
138 | def enable_and_log_gpu(log_file, cfg):
139 | """
140 | Enables GPU and logs GPU specs
141 | """
142 |
143 | gpustringraw = str(Metashape.app.enumGPUDevices())
144 | gpucount = gpustringraw.count("name': '")
145 | gpustring = ""
146 | currentgpu = 1
147 | while gpucount >= currentgpu:
148 | if gpustring != "":
149 | gpustring = gpustring + ", "
150 | gpustring = (
151 | gpustring + gpustringraw.split("name': '")[currentgpu].split("',")[0]
152 | )
153 | currentgpu = currentgpu + 1
154 | # gpustring = gpustringraw.split("name': '")[1].split("',")[0]
155 | gpu_mask = Metashape.app.gpu_mask
156 |
157 | with open(log_file, "a") as file:
158 | file.write(sep.join(["Number of GPUs Found", str(gpucount)]) + "\n")
159 | file.write(sep.join(["GPU Model", gpustring]) + "\n")
160 | file.write(sep.join(["GPU Mask", str(gpu_mask)]) + "\n")
161 |
162 | # If a GPU exists but is not enabled, enable the 1st one
163 | if (gpucount > 0) and (gpu_mask == 0):
164 | Metashape.app.gpu_mask = 1
165 | gpu_mask = Metashape.app.gpu_mask
166 | file.write(sep.join(["GPU Mask Enabled", str(gpu_mask)]) + "\n")
167 |
168 | # This writes down all the GPU devices available
169 | # file.write('GPU(s): '+str(Metashape.app.enumGPUDevices())+'\n')
170 |
171 | # set Metashape to *not* use the CPU during GPU steps (appears to be standard wisdom)
172 | Metashape.app.cpu_enable = False
173 |
174 | # Disable CUDA if specified
175 | if not cfg["use_cuda"]:
176 | Metashape.app.settings.setValue("main/gpu_enable_cuda", "0")
177 |
178 | # Set GPU multiplier to value specified (2 is default)
179 | Metashape.app.settings.setValue(
180 | "main/depth_max_gpu_multiplier", cfg["gpu_multiplier"]
181 | )
182 |
183 | return True
184 |
185 |
186 | def add_photos(doc, cfg):
187 | """
188 | Add photos to project and change their labels to include their containing folder
189 | """
190 |
191 | ## Get paths to all the project photos
192 | a = glob.iglob(
193 | os.path.join(cfg["photo_path"], "**", "*.*"), recursive=True
194 | ) # (([jJ][pP][gG])|([tT][iI][fF]))
195 | b = [path for path in a]
196 | photo_files = [
197 | x
198 | for x in b
199 | if (
200 | re.search("(.tif$)|(.jpg$)|(.TIF$)|(.JPG$)", x)
201 | and (not re.search("dem_usgs.tif", x))
202 | )
203 | ]
204 |
205 | ## Add them
206 | if cfg["multispectral"]:
207 | doc.chunk.addPhotos(photo_files, layout=Metashape.MultiplaneLayout)
208 | else:
209 | doc.chunk.addPhotos(photo_files)
210 |
211 | ## Need to change the label on each camera so that it includes the containing folder(S)
212 | for camera in doc.chunk.cameras:
213 | path = camera.photo.path
214 | # remove the base imagery dir from this string
215 | rel_path = path.replace(cfg["photo_path"], "")
216 | # if it starts with a '/', remove it
217 | newlabel = re.sub("^/", "", rel_path)
218 | camera.label = newlabel
219 |
220 | ## If specified, change the accuracy of the cameras to match the RTK flag (RTK fix if flag = 50, otherwise no fix
221 | if cfg["use_rtk"]:
222 | for cam in doc.chunk.cameras:
223 | rtkflag = cam.photo.meta["DJI/RtkFlag"]
224 | if rtkflag == "50":
225 | cam.reference.location_accuracy = Metashape.Vector(
226 | [cfg["fix_accuracy"], cfg["fix_accuracy"], cfg["fix_accuracy"]]
227 | )
228 | cam.reference.accuracy = Metashape.Vector(
229 | [cfg["fix_accuracy"], cfg["fix_accuracy"], cfg["fix_accuracy"]]
230 | )
231 | else:
232 | cam.reference.location_accuracy = Metashape.Vector(
233 | [
234 | cfg["nofix_accuracy"],
235 | cfg["nofix_accuracy"],
236 | cfg["nofix_accuracy"],
237 | ]
238 | )
239 | cam.reference.accuracy = Metashape.Vector(
240 | [
241 | cfg["nofix_accuracy"],
242 | cfg["nofix_accuracy"],
243 | cfg["nofix_accuracy"],
244 | ]
245 | )
246 |
247 | doc.save()
248 |
249 | return True
250 |
251 |
252 | def calibrate_reflectance(doc, cfg):
253 | # TODO: Handle failure to find panels, or mulitple panel images by returning error to user.
254 | doc.chunk.locateReflectancePanels()
255 | doc.chunk.loadReflectancePanelCalibration(
256 | os.path.join(
257 | cfg["photo_path"],
258 | "calibration",
259 | cfg["calibrateReflectance"]["panel_filename"],
260 | )
261 | )
262 | # doc.chunk.calibrateReflectance(use_reflectance_panels=True,use_sun_sensor=True)
263 | doc.chunk.calibrateReflectance(
264 | use_reflectance_panels=cfg["calibrateReflectance"]["use_reflectance_panels"],
265 | use_sun_sensor=cfg["calibrateReflectance"]["use_sun_sensor"],
266 | )
267 | doc.save()
268 |
269 | return True
270 |
271 |
272 | def add_gcps(doc, cfg):
273 | """
274 | Add GCPs (GCP coordinates and the locations of GCPs in individual photos.
275 | See the helper script (and the comments therein) for details on how to prepare the data needed by this function: R/prep_gcps.R
276 | """
277 |
278 | ## Tag specific pixels in specific images where GCPs are located
279 | path = os.path.join(
280 | cfg["photo_path"], "gcps", "prepared", "gcp_imagecoords_table.csv"
281 | )
282 | file = open(path)
283 | content = file.read().splitlines()
284 |
285 | for line in content:
286 | marker_label, camera_label, x_proj, y_proj = line.split(",")
287 | if (
288 | marker_label[0] == '"'
289 | ): # if it's in quotes (from saving CSV in Excel), remove quotes
290 | marker_label = marker_label[
291 | 1:-1
292 | ] # need to get it out of the two pairs of quotes
293 | if (
294 | camera_label[0] == '"'
295 | ): # if it's in quotes (from saving CSV in Excel), remove quotes
296 | camera_label = camera_label[1:-1]
297 |
298 | marker = get_marker(doc.chunk, marker_label)
299 | if not marker:
300 | marker = doc.chunk.addMarker()
301 | marker.label = marker_label
302 |
303 | camera = get_camera(doc.chunk, camera_label)
304 | if not camera:
305 | print(camera_label + " camera not found in project")
306 | continue
307 |
308 | marker.projections[camera] = Metashape.Marker.Projection(
309 | (float(x_proj), float(y_proj)), True
310 | )
311 |
312 | ## Assign real-world coordinates to each GCP
313 | path = os.path.join(cfg["photo_path"], "gcps", "prepared", "gcp_table.csv")
314 |
315 | file = open(path)
316 | content = file.read().splitlines()
317 |
318 | for line in content:
319 | marker_label, world_x, world_y, world_z = line.split(",")
320 | if (
321 | marker_label[0] == '"'
322 | ): # if it's in quotes (from saving CSV in Excel), remove quotes
323 | marker_label = marker_label[
324 | 1:-1
325 | ] # need to get it out of the two pairs of quotes
326 |
327 | marker = get_marker(doc.chunk, marker_label)
328 | if not marker:
329 | marker = doc.chunk.addMarker()
330 | marker.label = marker_label
331 |
332 | marker.reference.location = (float(world_x), float(world_y), float(world_z))
333 | marker.reference.accuracy = (
334 | cfg["addGCPs"]["marker_location_accuracy"],
335 | cfg["addGCPs"]["marker_location_accuracy"],
336 | cfg["addGCPs"]["marker_location_accuracy"],
337 | )
338 |
339 | doc.chunk.marker_location_accuracy = (
340 | cfg["addGCPs"]["marker_location_accuracy"],
341 | cfg["addGCPs"]["marker_location_accuracy"],
342 | cfg["addGCPs"]["marker_location_accuracy"],
343 | )
344 | doc.chunk.marker_projection_accuracy = cfg["addGCPs"]["marker_projection_accuracy"]
345 |
346 | doc.save()
347 |
348 | return True
349 |
350 |
351 | def align_photos(doc, log_file, cfg):
352 | """
353 | Match photos, align cameras, optimize cameras
354 | """
355 |
356 | #### Align photos
357 |
358 | # get a beginning time stamp
359 | timer1a = time.time()
360 |
361 | # Align cameras
362 | doc.chunk.matchPhotos(
363 | downscale=cfg["alignPhotos"]["downscale"],
364 | subdivide_task=cfg["subdivide_task"],
365 | keep_keypoints=cfg["alignPhotos"]["keep_keypoints"],
366 | )
367 | doc.chunk.alignCameras(
368 | adaptive_fitting=cfg["alignPhotos"]["adaptive_fitting"],
369 | subdivide_task=cfg["subdivide_task"],
370 | reset_alignment=cfg["alignPhotos"]["reset_alignment"],
371 | )
372 | doc.save()
373 |
374 | # get an ending time stamp
375 | timer1b = time.time()
376 |
377 | # calculate difference between end and start time to 1 decimal place
378 | time1 = diff_time(timer1b, timer1a)
379 |
380 | # record results to file
381 | with open(log_file, "a") as file:
382 | file.write(sep.join(["Align Photos", time1]) + "\n")
383 |
384 | return True
385 |
386 |
387 | def reset_region(doc):
388 | """
389 | Reset the region and make it much larger than the points; necessary because if points go outside the region, they get clipped when saving
390 | """
391 |
392 | doc.chunk.resetRegion()
393 | region_dims = doc.chunk.region.size
394 | region_dims[2] *= 3
395 | doc.chunk.region.size = region_dims
396 |
397 | return True
398 |
399 |
400 | def optimize_cameras(doc, cfg):
401 | """
402 | Optimize cameras
403 | """
404 |
405 | # Disable camera locations as reference if specified in YML
406 | if cfg["addGCPs"]["enabled"] and cfg["addGCPs"]["optimize_w_gcps_only"]:
407 | n_cameras = len(doc.chunk.cameras)
408 | for i in range(0, n_cameras):
409 | doc.chunk.cameras[i].reference.enabled = False
410 |
411 | # Currently only optimizes the default parameters, which is not all possible parameters
412 | doc.chunk.optimizeCameras(
413 | adaptive_fitting=cfg["optimizeCameras"]["adaptive_fitting"]
414 | )
415 | doc.save()
416 |
417 | return True
418 |
419 |
420 | def filter_points_usgs_part1(doc, cfg):
421 |
422 | doc.chunk.optimizeCameras(
423 | adaptive_fitting=cfg["optimizeCameras"]["adaptive_fitting"]
424 | )
425 |
426 | rec_thresh_percent = cfg["filterPointsUSGS"]["rec_thresh_percent"]
427 | rec_thresh_absolute = cfg["filterPointsUSGS"]["rec_thresh_absolute"]
428 | proj_thresh_percent = cfg["filterPointsUSGS"]["proj_thresh_percent"]
429 | proj_thresh_absolute = cfg["filterPointsUSGS"]["proj_thresh_absolute"]
430 | reproj_thresh_percent = cfg["filterPointsUSGS"]["reproj_thresh_percent"]
431 | reproj_thresh_absolute = cfg["filterPointsUSGS"]["reproj_thresh_absolute"]
432 |
433 | fltr = Metashape.PointCloud.Filter()
434 | fltr.init(doc.chunk, Metashape.PointCloud.Filter.ReconstructionUncertainty)
435 | values = fltr.values.copy()
436 | values.sort()
437 | thresh = values[int(len(values) * (1 - rec_thresh_percent / 100))]
438 | if thresh < rec_thresh_absolute:
439 | thresh = (
440 | rec_thresh_absolute # don't throw away too many points if they're all good
441 | )
442 | fltr.removePoints(thresh)
443 |
444 | doc.chunk.optimizeCameras(
445 | adaptive_fitting=cfg["optimizeCameras"]["adaptive_fitting"]
446 | )
447 |
448 | fltr = Metashape.PointCloud.Filter()
449 | fltr.init(doc.chunk, Metashape.PointCloud.Filter.ProjectionAccuracy)
450 | values = fltr.values.copy()
451 | values.sort()
452 | thresh = values[int(len(values) * (1 - proj_thresh_percent / 100))]
453 | if thresh < proj_thresh_absolute:
454 | thresh = (
455 | proj_thresh_absolute # don't throw away too many points if they're all good
456 | )
457 | fltr.removePoints(thresh)
458 |
459 | doc.chunk.optimizeCameras(
460 | adaptive_fitting=cfg["optimizeCameras"]["adaptive_fitting"]
461 | )
462 |
463 | fltr = Metashape.PointCloud.Filter()
464 | fltr.init(doc.chunk, Metashape.PointCloud.Filter.ReprojectionError)
465 | values = fltr.values.copy()
466 | values.sort()
467 | thresh = values[int(len(values) * (1 - reproj_thresh_percent / 100))]
468 | if thresh < reproj_thresh_absolute:
469 | thresh = reproj_thresh_absolute # don't throw away too many points if they're all good
470 | fltr.removePoints(thresh)
471 |
472 | doc.chunk.optimizeCameras(
473 | adaptive_fitting=cfg["optimizeCameras"]["adaptive_fitting"]
474 | )
475 |
476 | doc.save()
477 |
478 |
479 | def filter_points_usgs_part2(doc, cfg):
480 |
481 | doc.chunk.optimizeCameras(
482 | adaptive_fitting=cfg["optimizeCameras"]["adaptive_fitting"]
483 | )
484 |
485 | reproj_thresh_percent = cfg["filterPointsUSGS"]["reproj_thresh_percent"]
486 | reproj_thresh_absolute = cfg["filterPointsUSGS"]["reproj_thresh_absolute"]
487 |
488 | fltr = Metashape.PointCloud.Filter()
489 | fltr.init(doc.chunk, Metashape.PointCloud.Filter.ReprojectionError)
490 | values = fltr.values.copy()
491 | values.sort()
492 | thresh = values[int(len(values) * (1 - reproj_thresh_percent / 100))]
493 | if thresh < reproj_thresh_absolute:
494 | thresh = reproj_thresh_absolute # don't throw away too many points if they're all good
495 | fltr.removePoints(thresh)
496 |
497 | doc.chunk.optimizeCameras(
498 | adaptive_fitting=cfg["optimizeCameras"]["adaptive_fitting"]
499 | )
500 |
501 | doc.save()
502 |
503 |
504 | def classify_ground_points(doc, log_file, run_id, cfg):
505 |
506 | # get a beginning time stamp for the next step
507 | timer_a = time.time()
508 |
509 | doc.chunk.dense_cloud.classifyGroundPoints(
510 | max_angle=cfg["classifyGroundPoints"]["max_angle"],
511 | max_distance=cfg["classifyGroundPoints"]["max_distance"],
512 | cell_size=cfg["classifyGroundPoints"]["cell_size"],
513 | )
514 | doc.save()
515 |
516 | # get an ending time stamp for the previous step
517 | timer_b = time.time()
518 |
519 | # calculate difference between end and start time to 1 decimal place
520 | time_tot = diff_time(timer_b, timer_a)
521 |
522 | # record results to file
523 | with open(log_file, "a") as file:
524 | file.write(sep.join(["Classify Ground Points", time_tot]) + "\n")
525 |
526 |
527 | def build_dense_cloud(doc, log_file, run_id, cfg):
528 | """
529 | Build depth maps and dense cloud
530 | """
531 |
532 | ### Build depth maps
533 |
534 | # get a beginning time stamp for the next step
535 | timer2a = time.time()
536 |
537 | # build depth maps only instead of also building the dense cloud ##?? what does
538 | doc.chunk.buildDepthMaps(
539 | downscale=cfg["buildDenseCloud"]["downscale"],
540 | filter_mode=cfg["buildDenseCloud"]["filter_mode"],
541 | reuse_depth=cfg["buildDenseCloud"]["reuse_depth"],
542 | max_neighbors=cfg["buildDenseCloud"]["max_neighbors"],
543 | subdivide_task=cfg["subdivide_task"],
544 | )
545 | doc.save()
546 |
547 | # get an ending time stamp for the previous step
548 | timer2b = time.time()
549 |
550 | # calculate difference between end and start time to 1 decimal place
551 | time2 = diff_time(timer2b, timer2a)
552 |
553 | # record results to file
554 | with open(log_file, "a") as file:
555 | file.write(sep.join(["Build Depth Maps", time2]) + "\n")
556 |
557 | ### Build dense cloud
558 |
559 | # get a beginning time stamp for the next step
560 | timer3a = time.time()
561 |
562 | # build dense cloud
563 | doc.chunk.buildDenseCloud(
564 | max_neighbors=cfg["buildDenseCloud"]["max_neighbors"],
565 | keep_depth=cfg["buildDenseCloud"]["keep_depth"],
566 | subdivide_task=cfg["subdivide_task"],
567 | point_colors=True,
568 | )
569 | doc.save()
570 |
571 | # classify ground points if specified
572 | if cfg["buildDenseCloud"]["classify_ground_points"]:
573 | classify_ground_points(doc, log_file, run_id, cfg)
574 |
575 | ### Export points
576 |
577 | if cfg["buildDenseCloud"]["export"]:
578 |
579 | output_file = os.path.join(cfg["output_path"], run_id + "_points.las")
580 |
581 | if cfg["buildDenseCloud"]["classes"] == "ALL":
582 | # call without classes argument (Metashape then defaults to all classes)
583 | doc.chunk.exportPoints(
584 | path=output_file,
585 | source_data=Metashape.DenseCloudData,
586 | format=Metashape.PointsFormatLAS,
587 | crs=Metashape.CoordinateSystem(cfg["project_crs"]),
588 | subdivide_task=cfg["subdivide_task"],
589 | )
590 | else:
591 | # call with classes argument
592 | doc.chunk.exportPoints(
593 | path=output_file,
594 | source_data=Metashape.DenseCloudData,
595 | format=Metashape.PointsFormatLAS,
596 | crs=Metashape.CoordinateSystem(cfg["project_crs"]),
597 | clases=cfg["buildDenseCloud"]["classes"],
598 | subdivide_task=cfg["subdivide_task"],
599 | )
600 |
601 | # get an ending time stamp for the previous step
602 | timer3b = time.time()
603 |
604 | # calculate difference between end and start time to 1 decimal place
605 | time3 = diff_time(timer3b, timer3a)
606 |
607 | # record results to file
608 | with open(log_file, "a") as file:
609 | file.write(sep.join(["Build Dense Cloud", time3]) + "\n")
610 |
611 | return True
612 |
613 |
614 | def build_dem(doc, log_file, run_id, cfg):
615 | """
616 | Build end export DEM
617 | """
618 |
619 | # classify ground points if specified
620 | if cfg["buildDem"]["classify_ground_points"]:
621 | classify_ground_points(doc, log_file, run_id, cfg)
622 |
623 | # get a beginning time stamp for the next step
624 | timer5a = time.time()
625 |
626 | # prepping params for buildDem
627 | projection = Metashape.OrthoProjection()
628 | projection.crs = Metashape.CoordinateSystem(cfg["project_crs"])
629 |
630 | # prepping params for export
631 | compression = Metashape.ImageCompression()
632 | compression.tiff_big = cfg["buildDem"]["tiff_big"]
633 | compression.tiff_tiled = cfg["buildDem"]["tiff_tiled"]
634 | compression.tiff_overviews = cfg["buildDem"]["tiff_overviews"]
635 |
636 | if (cfg["buildDem"]["type"] == "DSM") | (cfg["buildDem"]["type"] == "both"):
637 | # call without classes argument (Metashape then defaults to all classes)
638 | doc.chunk.buildDem(
639 | source_data=Metashape.DenseCloudData,
640 | subdivide_task=cfg["subdivide_task"],
641 | projection=projection,
642 | )
643 | output_file = os.path.join(cfg["output_path"], run_id + "_dsm.tif")
644 | if cfg["buildDem"]["export"]:
645 | doc.chunk.exportRaster(
646 | path=output_file,
647 | projection=projection,
648 | nodata_value=cfg["buildDem"]["nodata"],
649 | source_data=Metashape.ElevationData,
650 | image_compression=compression,
651 | )
652 | if (cfg["buildDem"]["type"] == "DTM") | (cfg["buildDem"]["type"] == "both"):
653 | # call with classes argument
654 | doc.chunk.buildDem(
655 | source_data=Metashape.DenseCloudData,
656 | classes=Metashape.PointClass.Ground,
657 | subdivide_task=cfg["subdivide_task"],
658 | projection=projection,
659 | )
660 | output_file = os.path.join(cfg["output_path"], run_id + "_dtm.tif")
661 | if cfg["buildDem"]["export"]:
662 | doc.chunk.exportRaster(
663 | path=output_file,
664 | projection=projection,
665 | nodata_value=cfg["buildDem"]["nodata"],
666 | source_data=Metashape.ElevationData,
667 | image_compression=compression,
668 | )
669 | if (
670 | (cfg["buildDem"]["type"] != "DTM")
671 | & (cfg["buildDem"]["type"] == "both")
672 | & (cfg["buildDem"]["type"] == "DSM")
673 | ):
674 | raise ValueError("DEM type must be either 'DSM' or 'DTM' or 'both'")
675 |
676 | doc.save()
677 |
678 | # get an ending time stamp for the previous step
679 | timer5b = time.time()
680 |
681 | # calculate difference between end and start time to 1 decimal place
682 | time5 = diff_time(timer5b, timer5a)
683 |
684 | # record results to file
685 | with open(log_file, "a") as file:
686 | file.write(sep.join(["Build DEM", time5]) + "\n")
687 |
688 | return True
689 |
690 |
691 | def build_export_orthomosaic(doc, log_file, run_id, cfg, file_ending):
692 | """
693 | Helper function called by build_orthomosaics. build_export_orthomosaic builds and exports an ortho based on the current elevation data.
694 | build_orthomosaics sets the current elevation data and calls build_export_orthomosaic (one or more times depending on how many orthomosaics requested)
695 | """
696 |
697 | # get a beginning time stamp for the next step
698 | timer6a = time.time()
699 |
700 | # prepping params for buildDem
701 | projection = Metashape.OrthoProjection()
702 | projection.crs = Metashape.CoordinateSystem(cfg["project_crs"])
703 |
704 | doc.chunk.buildOrthomosaic(
705 | surface_data=Metashape.ElevationData,
706 | blending_mode=cfg["buildOrthomosaic"]["blending"],
707 | fill_holes=cfg["buildOrthomosaic"]["fill_holes"],
708 | refine_seamlines=cfg["buildOrthomosaic"]["refine_seamlines"],
709 | subdivide_task=cfg["subdivide_task"],
710 | projection=projection,
711 | )
712 |
713 | doc.save()
714 |
715 | ## Export orthomosaic
716 | if cfg["buildOrthomosaic"]["export"]:
717 | output_file = os.path.join(
718 | cfg["output_path"], run_id + "_ortho_" + file_ending + ".tif"
719 | )
720 |
721 | compression = Metashape.ImageCompression()
722 | compression.tiff_big = cfg["buildOrthomosaic"]["tiff_big"]
723 | compression.tiff_tiled = cfg["buildOrthomosaic"]["tiff_tiled"]
724 | compression.tiff_overviews = cfg["buildOrthomosaic"]["tiff_overviews"]
725 |
726 | projection = Metashape.OrthoProjection()
727 | projection.crs = Metashape.CoordinateSystem(cfg["project_crs"])
728 |
729 | doc.chunk.exportRaster(
730 | path=output_file,
731 | projection=projection,
732 | nodata_value=cfg["buildOrthomosaic"]["nodata"],
733 | source_data=Metashape.OrthomosaicData,
734 | image_compression=compression,
735 | )
736 |
737 | # get an ending time stamp for the previous step
738 | timer6b = time.time()
739 |
740 | # calculate difference between end and start time to 1 decimal place
741 | time6 = diff_time(timer6b, timer6a)
742 |
743 | # record results to file
744 | with open(log_file, "a") as file:
745 | file.write(sep.join(["Build Orthomosaic", time6]) + "\n")
746 |
747 | return True
748 |
749 |
750 | def build_orthomosaics(doc, log_file, run_id, cfg):
751 | """
752 | Build orthomosaic. This function just calculates the needed elevation data(s) and then calls build_export_orthomosaic to do the actual building and exporting. It does this multiple times if orthos based on multiple surfaces were requsted
753 | """
754 |
755 | # prep projection for export step below (in case export is enabled)
756 | projection = Metashape.OrthoProjection()
757 | projection.crs = Metashape.CoordinateSystem(cfg["project_crs"])
758 |
759 | # get a beginning time stamp for the next step
760 | timer6a = time.time()
761 |
762 | # what should the orthomosaic filename end in? e.g., DSM, DTM, USGS to indicate the surface it was built on
763 | file_ending = cfg["buildOrthomosaic"]["surface"]
764 |
765 | # Import USGS DEM as surface for orthomosaic if specified
766 | if cfg["buildOrthomosaic"]["surface"] == "USGS":
767 | path = os.path.join(cfg["photo_path"], cfg["buildOrthomosaic"]["usgs_dem_path"])
768 | crs = Metashape.CoordinateSystem(cfg["buildOrthomosaic"]["usgs_dem_crs"])
769 | doc.chunk.importRaster(path=path, crs=crs, raster_type=Metashape.ElevationData)
770 | build_export_orthomosaic(doc, log_file, run_id, cfg, file_ending="USGS")
771 | # Otherwise use Metashape point cloud to build elevation model
772 | # DTM: use ground points only
773 | if (cfg["buildOrthomosaic"]["surface"] == "DTM") | (
774 | cfg["buildOrthomosaic"]["surface"] == "DTMandDSM"
775 | ):
776 | doc.chunk.buildDem(
777 | source_data=Metashape.DenseCloudData,
778 | classes=Metashape.PointClass.Ground,
779 | subdivide_task=cfg["subdivide_task"],
780 | projection=projection,
781 | )
782 | build_export_orthomosaic(doc, log_file, run_id, cfg, file_ending="dtm")
783 | # DSM: use all point classes
784 | if (cfg["buildOrthomosaic"]["surface"] == "DSM") | (
785 | cfg["buildOrthomosaic"]["surface"] == "DTMandDSM"
786 | ):
787 | doc.chunk.buildDem(
788 | source_data=Metashape.DenseCloudData,
789 | subdivide_task=cfg["subdivide_task"],
790 | projection=projection,
791 | )
792 | build_export_orthomosaic(doc, log_file, run_id, cfg, file_ending="dsm")
793 |
794 | return True
795 |
796 |
797 | def export_report(doc, run_id, cfg):
798 | """
799 | Export report
800 | """
801 |
802 | output_file = os.path.join(cfg["output_path"], run_id + "_report.pdf")
803 |
804 | doc.chunk.exportReport(path=output_file)
805 |
806 | return True
807 |
808 |
809 | def finish_run(log_file, config_file):
810 | """
811 | Finish run (i.e., write completed time to log)
812 | """
813 |
814 | # finish local results log and close it for the last time
815 | with open(log_file, "a") as file:
816 | file.write(sep.join(["Run Completed", stamp_time()]) + "\n")
817 |
818 | # open run configuration again. We can't just use the existing cfg file because its objects had already been converted to Metashape objects (they don't write well)
819 | with open(config_file) as file:
820 | config_full = yaml.safe_load(file)
821 |
822 | # write the run configuration to the log file
823 | with open(log_file, "a") as file:
824 | file.write("\n\n### CONFIGURATION ###\n")
825 | documents = yaml.dump(config_full, file, default_flow_style=False)
826 | file.write("### END CONFIGURATION ###\n")
827 |
828 | return True
829 |
--------------------------------------------------------------------------------
/prior-versions/metashape_v1.6-1.8/python/read_yaml.py:
--------------------------------------------------------------------------------
1 | """
2 | Created on Mon Oct 21 13:45:15 2019
3 |
4 | @author: Alex Mandel
5 | """
6 |
7 | import Metashape
8 | import yaml
9 |
10 |
11 | def convert_objects(a_dict):
12 | """
13 | Based on
14 | https://stackoverflow.com/a/25896596/237354
15 | """
16 | for k, v in a_dict.items():
17 | if not isinstance(v, dict):
18 | if isinstance(v, str):
19 | # TODO look for Metashape.
20 | if (
21 | v
22 | and "Metashape" in v
23 | and not ("path" in k)
24 | and not ("project" in k)
25 | ): # allow "path" and "project" keys from YAML to include "Metashape" (e.g., Metashape in the filename)
26 | a_dict[k] = eval(v)
27 | elif isinstance(v, list):
28 | # TODO skip if no item have metashape
29 | a_dict[k] = [eval(item) for item in v if ("Metashape" in item)]
30 | else:
31 | convert_objects(v)
32 |
33 |
34 | def read_yaml(yml_path):
35 | with open(yml_path, "r") as ymlfile:
36 | cfg = yaml.load(ymlfile, Loader=yaml.SafeLoader)
37 |
38 | # TODO: wrap in a Try to catch errors
39 | convert_objects(cfg)
40 |
41 | return cfg
42 |
43 |
44 | if __name__ == "__main__":
45 |
46 | yml_path = "config/example.yml"
47 | cfg = read_yaml(yml_path)
48 | # with open("config/example.yml",'r') as ymlfile:
49 | # cfg = yaml.load(ymlfile)
50 |
51 | # catch = convert_objects(cfg)
52 |
53 | # Get a String value
54 | Photo_path = cfg["Photo_path"]
55 |
56 | # Get a True/False
57 | GPU_use = cfg["GPU"]["GPU_use"]
58 |
59 | # Get a Num
60 | GPU_num = cfg["GPU"]["GPU_num"]
61 |
62 | # Convert a to a Metashape Object
63 | accuracy = eval(cfg["matchPhotos"]["accuracy"])
64 |
--------------------------------------------------------------------------------
/python/metashape_workflow.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # File for running a metashape workflow
3 |
4 | import argparse
5 | import contextlib
6 | import sys
7 | from pathlib import Path
8 |
9 | # ---- If this is a first run from the standalone python module, need to copy the license file from the full metashape install: from python import metashape_license_setup
10 |
11 | ## Define where to get the config file (only used if running interactively)
12 | # manual_config_file = "config/config-base.yml"
13 | manual_config_file = Path(
14 | Path(__file__).parent, "..", "config", "config-base.yml"
15 | ).resolve()
16 | # ---- If not running interactively, the config file should be supplied as the command-line argument after the python script, e.g.: python metashape_workflow.py config.yml
17 |
18 |
19 | # Load custom modules and config file: slightly different depending whether running interactively or via command line
20 | try: # running interactively (in linux) or command line (windows)
21 | from python.metashape_workflow_functions import MetashapeWorkflow
22 | except: # running from command line (in linux) or interactively (windows)
23 | from metashape_workflow_functions import MetashapeWorkflow
24 |
25 |
26 | def parse_args():
27 | parser = argparse.ArgumentParser(
28 | description="The first required argument is the path to the config file. "
29 | + "All other arguments are optional overrides to the corresponding entry in that config"
30 | )
31 | parser.add_argument(
32 | "--config_file",
33 | default=manual_config_file,
34 | help="A path to a yaml config file.",
35 | )
36 | parser.add_argument(
37 | "--photo-path",
38 | nargs="+",
39 | help="One or more absolute paths to load photos from, separated by spaces.",
40 | )
41 | parser.add_argument(
42 | "--photo-path-secondary",
43 | help="A path to a folder of images to add after alignment. "
44 | + "For more information, see the description in the example config file.",
45 | )
46 | parser.add_argument(
47 | "--project-path",
48 | help="Path to save Metashape project file (.psx). Will be created if does not exist",
49 | )
50 | parser.add_argument(
51 | "--output-path",
52 | help="Path for exports (e.g., points, DSM, orthomosaic) and processing log. "
53 | + "Will be created if does not exist.",
54 | )
55 | parser.add_argument(
56 | "--run-name",
57 | help="The identifier for the run. Will be used in naming output files.",
58 | )
59 | parser.add_argument(
60 | "--project-crs",
61 | help="CRS EPSG code that project outputs should be in "
62 | + "(projection should be in meter units and intended for the project area). "
63 | + "It should be specified in the following format: 'EPSG::'.",
64 | )
65 |
66 | args = parser.parse_args()
67 | return args
68 |
69 |
70 | if __name__ == "__main__":
71 | args = parse_args()
72 |
73 | # Get the non-None overrides provided on the command line
74 | override_dict = {k: v for k, v in args.__dict__.items() if v is not None}
75 |
76 | # Initialize the workflow instance with the configuration file and the dictionary representation of
77 | # CLI overrides
78 | meta = MetashapeWorkflow(config_file=args.config_file, override_dict=override_dict)
79 |
80 | ### Run the Metashape workflow
81 | # The argo workflow requires that all stdout is json formatted. Since this isn't the case for the
82 | # metashape logs, we redirect to standard error.
83 | with contextlib.redirect_stdout(sys.stderr):
84 | # Actually run the processing step
85 | try:
86 | meta.run()
87 | except Exception as e:
88 | # TODO make this error message more descriptive
89 | print(
90 | "Metashape errored while processing, the completed paths will still be reported. "
91 | + "The error was: \n"
92 | + e.__str__()
93 | )
94 |
95 | # Log where the data files were written as json dict
96 | print(meta.get_written_paths(as_json=True))
97 |
--------------------------------------------------------------------------------
/settings.json:
--------------------------------------------------------------------------------
1 | {
2 |
3 | "editor.rulers": [
4 |
5 | 100
6 | ],
7 | "rewrap.autoWrap.enabled": false
8 |
9 | }
--------------------------------------------------------------------------------