Skip to content

Instantly share code, notes, and snippets.

@foobarbecue
Last active May 26, 2020 00:35
Show Gist options
  • Save foobarbecue/0f1b3706738d5069c724648e0897a32b to your computer and use it in GitHub Desktop.
Save foobarbecue/0f1b3706738d5069c724648e0897a32b to your computer and use it in GitHub Desktop.
# NACpipeline Argo workflow
# Aaron Curtis
# Concurrency rules: Write files as temporary, then move to intended dest. Check for existing output before starting.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: nac-stereo-
spec:
entrypoint: nac-mosaic
volumes:
- name: nac-volume
hostPath:
path: /c/nacpldata4
type: DirectoryOrCreate
arguments:
parameters:
- name: west
- name: east
- name: south
- name: north
templates:
# Dummy template with reusable parts for references in stereo templates. Not to be run.
- name: dummy-stereo-template
inputs: &right-left-inps
parameters:
- name: right-prod-id
- name: left-prod-id
container: &nacpl-container
image: lmmp-local/lmmp-container
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nac-volume
mountPath: /data/nac
- name: dummy-stereo-dag-template
inputs: *right-left-inps
dag:
tasks:
- name: dummy-task
template: dummy-stereo-template
arguments: &left-right-arguments-from-inputs
parameters:
- name: right-prod-id
value: "{{inputs.parameters.right-prod-id}}"
- name: left-prod-id
value: "{{inputs.parameters.left-prod-id}}"
# Dummy template with reusable parts for references in mosaic templates. Not to be run.
- name: dummy-mosaic-template
inputs: &bounding-box-arguments-from-inputs
parameters:
- name: west
value: "{{workflow.parameters.west}}"
- name: east
value: "{{workflow.parameters.east}}"
- name: south
value: "{{workflow.parameters.south}}"
- name: north
value: "{{workflow.parameters.north}}"
container: *nacpl-container
# Given a bounding box {{west}}, {{east}}, {{south}}, {{north}} find appropriate NAC stereo pairs and create:
# - stereo DEM mosaic covering the box at /data/nac/{{west}}_{{east}}_{{south}}_{{north}}DEM-mosaic.tif
# - orthophoto mosaic covering the box at /data/nac/{{west}}_{{east}}_{{south}}_{{north}}DRG-mosaic.tif
# - list of NAC pairs to use at /data/nac/{{west}}_{{east}}_{{south}}_{{north}}pairs.txt
- name: nac-mosaic
inputs: *bounding-box-arguments-from-inputs
parallelism: 3
steps:
- - name: find-pairs
template: find-pairs
arguments: *bounding-box-arguments-from-inputs
- - name: nac-stereo
template: nac-stereo
arguments:
parameters:
- name: left-prod-id
value: "{{item.left}}"
- name: right-prod-id
value: "{{item.right}}"
withParam: "{{steps.find-pairs.outputs.result}}"
continueOn:
failed: true
- - name: merge-dems
template: merge-dems
arguments:
parameters:
- name: pair-ids
value: "{{steps.find-pairs.outputs.result}}"
- - name: color-hillshade
template: color-hillshade
arguments:
artifacts:
- name: dem
from: "{{steps.merge-dems.outputs.artifacts.dem}}"
- name: merge-orthoimages # TODO don't need to wait for hillshade or dem
template: merge-orthoimages
arguments:
parameters:
- name: pair-ids
value: "{{steps.find-pairs.outputs.result}}"
- name: find-pairs
inputs: *bounding-box-arguments-from-inputs
script:
<<: *nacpl-container
command: [bash]
source: |
. activate NAC_pl_env_py3
python /nacpl/find_stereo_pairs.py \
--west={{inputs.parameters.west}} \
--east={{inputs.parameters.east}} \
--south={{inputs.parameters.south}} \
--north={{inputs.parameters.north}} \
--find-covering=True
# Given two nac product ids, right-prod-id and left-prod-id, initiate workflow to create:
# - stereo DEM at /data/nac/{{left-prod-id}}xx{{right-prod-id}}-median-DEM.tif
# - orthophoto at /data/nac/{{left-prod-id}}xx{{right-prod-id}}-median-DRG.tif
# Also create some useful extra products:
# - Downloaded .IMG files in /data/nac/
# - ISIS-ingested .cub files in /data/nac/
# - Intermediate data products in /data/nac/{{left-prod-id}}xx{{right-prod-id}}
- name: nac-stereo
parallelism: 3
inputs: *left-right-arguments-from-inputs
dag:
tasks:
- name: dl-ingest-right
template: dl-ingest-nac
arguments:
parameters: [{name: prod-id, value: "{{inputs.parameters.right-prod-id}}"}]
- name: dl-ingest-left
template: dl-ingest-nac
arguments:
parameters: [{name: prod-id, value: "{{inputs.parameters.left-prod-id}}"}]
- name: map-project
template: map-project
arguments: *left-right-arguments-from-inputs
dependencies: [dl-ingest-right, dl-ingest-left]
- name: stereo
template: stereo
arguments: *left-right-arguments-from-inputs
dependencies: [map-project]
- name: download-lola
template: download-lola
arguments: *left-right-arguments-from-inputs
dependencies: [map-project]
- name: align
template: align
arguments: *left-right-arguments-from-inputs
dependencies: [stereo, download-lola]
# Before this point, intermediate data products are considered re-usable and should be stored in a common location to
# prevent re-processing in future mosaics
- name: generate-DEM-and-orthoimages
template: generate-DEM-and-orthoimages
arguments: *left-right-arguments-from-inputs
dependencies: [align]
# Given a nac product id prod-id, initiate workflow download and ingest NAC into ISIS, creating:
# - Downloaded .IMG file in /data/nac/{{prod-id}}
# - ISIS-ingested .cub file in /data/nac/{{prod-id}}
- name: dl-ingest-nac
inputs:
parameters:
- name: prod-id
dag:
tasks:
- name: download-nac
template: download-nac
arguments:
parameters:
- name: prod-id
value: "{{inputs.parameters.prod-id}}"
- name: img2cub
template: img2cub
arguments:
parameters:
- name: prod-id
value: "{{inputs.parameters.prod-id}}"
dependencies: [download-nac]
- name: calibrate
template: calibrate
arguments:
parameters:
- name: prod-id
value: "{{inputs.parameters.prod-id}}"
dependencies: [img2cub]
# Download a NAC image if doesn't exist and store to /data/nac on NFS
# Script downloads to a temporary file and then moves it into place
- name: download-nac
inputs:
parameters:
- name: prod-id
retryStrategy:
limit: 3
script:
<<: *nacpl-container
command: [bash]
source: |
. activate NAC_pl_env_py3
if ! test -f /data/nac/{{inputs.parameters.prod-id}}.IMG
then
python /nacpl/download_NAC.py {{inputs.parameters.prod-id}} /data/nac
else
echo "Skipping step because output file already exists"
fi
# Download LOLA data for NAC image if it hasn't already been downloaded
# Saves image at /data/nac/[right-prod-id]xx[left-prod-id]_lola.csv
# TODO see if can use abs paths and avoid cd
# NOTE breaks concurrency rule but write time for file is short enough that collision unlikely
- name: download-lola
inputs: *right-left-inps
script:
<<: *nacpl-container
command: [bash]
source: |
if ! test -f /data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/*_lola.csv
then
. activate NAC_pl_env_py3
cd /data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}
python /nacpl/download_LOLA.py \
map-{{inputs.parameters.left-prod-id}}.map.cub map-{{inputs.parameters.right-prod-id}}.map.cub \
/data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}
else
echo "Skipping step because output file already exists"
fi
# Convert IMG to ISIS cube if the cube doesn't already exist
- name: img2cub
inputs:
parameters:
- name: prod-id
retryStrategy:
limit: 3
script:
<<: *nacpl-container
command: [bash]
source: |
if ! test -f /data/nac/{{inputs.parameters.prod-id}}.cub
then
mkdir -p /data/nac/tmp/
echo "Running lronac2isis FROM=/data/nac/{{inputs.parameters.prod-id}}.IMG TO=/data/nac/tmp/{{inputs.parameters.prod-id}}.cub"
lronac2isis -v FROM=/data/nac/{{inputs.parameters.prod-id}}.IMG TO=/data/nac/tmp/{{inputs.parameters.prod-id}}.cub
mv /data/nac/tmp/{{inputs.parameters.prod-id}}.cub /data/nac/
else
echo "Skipping step because output file already exists"
fi
# Run spiceinit, lronaccal, and lronacecho and produce a .cal.cub if one doesn't already exist
- name: calibrate
inputs:
parameters:
- name: prod-id
retryStrategy:
limit: 3
script:
<<: *nacpl-container
command: [bash]
source: |
if ! test -f /data/nac/{{inputs.parameters.prod-id}}.cal.cub
then
spiceinit FROM=/data/nac/{{inputs.parameters.prod-id}}.cub web=yes
lronaccal FROM=/data/nac/{{inputs.parameters.prod-id}}.cub TO=/tmp/{{inputs.parameters.prod-id}}.cal.cub
lronacecho FROM=/tmp/{{inputs.parameters.prod-id}}.cal.cub TO=/data/nac/tmp/{{inputs.parameters.prod-id}}.cal.cub
mv /data/nac/tmp/{{inputs.parameters.prod-id}}.cal.cub /data/nac/{{inputs.parameters.prod-id}}.cal.cub
else
echo "Skipping step because output file already exists"
fi
# Project the images to the best common resolution
- name: map-project
inputs: *right-left-inps
retryStrategy:
limit: 3
script:
<<: *nacpl-container
command: [bash]
source: |
if (! test -f /data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/map-{{inputs.parameters.left-prod-id}}.map.cub && \
! test -f /data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/map-{{inputs.parameters.right-prod-id}}.map.cub)
then
. activate NAC_pl_env_py3
cd /StereoPipeline-2.6.2-2019-06-17-x86_64-Linux/bin
cam2map4stereo.py \
--prefix=/data/nac/tmp/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/map \
/data/nac/{{inputs.parameters.left-prod-id}}.cal.cub \
/data/nac/{{inputs.parameters.right-prod-id}}.cal.cub
mkdir -p /data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/
mv /data/nac/tmp/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/map*map.cub \
/data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/
else
echo "Skipping step because output file already exists"
fi
# Do stereo reconstruction between the pairs if a stereo point cloud doesn't exist yet for them
- name: stereo
inputs: *right-left-inps
script:
<<: *nacpl-container
command: [bash]
source: |
if ! test -f /data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/stereo-PC.tif
then
parallel_stereo \
--processes=10 \
--threads-singleprocess=10 \
--alignment-method=none \
--individually-normalize \
/data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/map-{{inputs.parameters.right-prod-id}}.map.cub \
/data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/map-{{inputs.parameters.left-prod-id}}.map.cub \
/data/nac/tmp/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/stereo
mv /data/nac/tmp/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/* \
/data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/
else
echo "Skipping step because output file already exists"
fi
# Align the point cloud to LOLA
- name: align
inputs: *right-left-inps
script:
<<: *nacpl-container
command: [bash]
source: |
if ! test -f /data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/pc_align-trans_reference.tif
then
pc_align \
--max-displacement 1200 \
--csv-format '1:lon 2:lat 3:height_above_datum' \
--save-inv-transformed-reference-points \
--threads 10 \
/data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/stereo-PC.tif \
/data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/map-{{inputs.parameters.left-prod-id}}xxmap-{{inputs.parameters.right-prod-id}}_lola.csv \
-o /data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/tmp/pc_align
mv /data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/tmp/pc_align*.tif \
/data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/
else
echo "Skipping step because output file already exists"
fi
# Convert the point cloud to a DEM and an orthoimage
- name: generate-DEM-and-orthoimages
inputs: *right-left-inps
retryStrategy:
limit: 3
script:
<<: *nacpl-container
command: [bash]
source: |
if (! test -f /data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}*DEM.tif && \
! test -f /data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}*DRG.tif)
then
point2dem \
-o /data/nac/tmp/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}} \
--t_srs http://spatialreference.org/ref/iau2000/30100/ \
--nodata -9999 \
--filter median \
--median-filter-params 40 20 \
--tif-compress LZW \
--threads 10 \
--orthoimage \
/data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/stereo-L.tif \
/data/nac/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}/pc_align-trans_reference.tif
mv /data/nac/tmp/{{inputs.parameters.left-prod-id}}xx{{inputs.parameters.right-prod-id}}*.tif \
/data/nac/
else
echo "Skipping step because output file already exists"
fi
# Template to merge DEMs
# mosaic_merge.py calls ASP dem_mosaic
- name: merge-dems
inputs:
parameters:
- name: pair-ids
script:
<<: *nacpl-container
command: [bash]
#TODO tidy up following section, maybe get rid of bash substitution
source: |
. activate NAC_pl_env_py3
mkdir -p /data/nac/merged
python /nacpl/mosaic_merge.py $'{{inputs.parameters.pair-ids}}' DEM /data/nac /tmp/
# Copy to shared storage, ending the DEM with DEM.tif instead of tile-0.tif
DEM_FILENAME=( /tmp/*tile-0.tif )
DEM_FILENAME_CORRECTED=${DEM_FILENAME/tile-0/DEM}
cp /tmp/*tile-0.tif /data/nac/merged/${DEM_FILENAME_CORRECTED/\/tmp/}
# Move the file so it can be output as an artifact
mv /tmp/*tile-0.tif /tmp/merged_DEM.tif
outputs:
artifacts:
- name: dem
path: /tmp/merged_DEM.tif
# Template to render a color hillshade
- name: color-hillshade
inputs:
artifacts:
- name: dem
path: /tmp/merged_DEM.tif
script:
<<: *nacpl-container
command: [bash]
source: |
. activate NAC_pl_env_py3
gdalwarp -s_srs "EPSG:104903" \
-t_srs "+proj=eqc +lat_ts={{workflow.parameters.north}} +lat_0={{workflow.parameters.north}} +lon_0={{workflow.parameters.west}} +x_0=0 +y_0=0 +a=1737400 +b=1737400 +units=m +no_defs" \
/tmp/merged_DEM.tif /tmp/merged_DEM_eqc.tif
gdaldem hillshade /tmp/merged_DEM_eqc.tif /tmp/merged_hillshade.tif
gdaldem color-relief /tmp/merged_DEM_eqc.tif /nacpl/elevation_colors.txt /tmp/merged_color-relief.tif
hsv_merge -o /tmp/color_hillshade.tif /tmp/merged_color-relief.tif /tmp/merged_hillshade.tif
outputs:
artifacts:
- name: color-hillshade
path: /tmp/ #TODO save in volume?
# Template to merge orthophotos (digital raster graphics, DRGs)
# mosaic_merge.py calls Orfeo Toolbox Mosaic (otbcli_Mosaic)
- name: merge-orthoimages
inputs:
parameters:
- name: pair-ids
script:
<<: *nacpl-container
command: [bash]
source: |
. activate NAC_pl_env_py3
mkdir -p /data/nac/merged
export OTB_MAX_RAM_HINT=1024
python /nacpl/mosaic_merge.py $'{{inputs.parameters.pair-ids}}' DRG /data/nac /tmp
outputs:
artifacts:
- name: orthoimage
path: /tmp/ #TODO save in volume?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment