#!/bin/sh

# colmap_openmvs_pipeline.sh
# 2026-02-21
# by Gernot Walzl

# This script runs the COLMAP and OpenMVS pipeline.
# COLMAP generates a sparse point cloud. OpenMVS reconstructs a textured mesh.
# Based on:
# COLMAP usage: https://colmap.github.io/cli.html#example
# OpenMVS usage: https://github.com/cdcseacave/openMVS/wiki/Usage

export PATH="/usr/bin/OpenMVS:$PATH"
SMOOTH=${SMOOTH:-'5'}  # The default value of OpenMVS is 2.
PROJECT="$1"

set -e

colmap_sparse () {
  # Perform feature extraction
  # Camera modes are defined in
  # https://github.com/colmap/colmap/blob/main/src/colmap/exe/feature.h#L72
  CAMERA_MODE=2
  # Mask path is declared here
  # https://github.com/colmap/colmap/blob/main/src/colmap/controllers/image_reader.h#L53
  if [ -d masks ]; then
    colmap feature_extractor \
      --database_path database.db \
      --image_path images \
      --camera_mode "$CAMERA_MODE" \
      --ImageReader.mask_path masks
  else
    colmap feature_extractor \
      --database_path database.db \
      --image_path images \
      --camera_mode "$CAMERA_MODE"
  fi

  # Perform feature matching
  colmap exhaustive_matcher \
    --database_path database.db

  # Sparse 3D reconstruction
  mkdir sparse
  colmap mapper \
    --database_path database.db \
    --image_path images \
    --output_path sparse

  # Find largest sub-model
  MODEL=$(du sparse/* | sort -nr | head -n1 | awk '{print $2}')

  # Undistort images and export them for dense reconstruction
  mkdir dense
  colmap image_undistorter \
    --image_path images \
    --input_path "$MODEL" \
    --output_path dense \
    --output_type COLMAP
}

openmvs_texture () {
  # Import from COLMAP
  InterfaceCOLMAP -i dense -o scene.mvs --image-folder dense/images

  # Dense Point Cloud Reconstruction
  DensifyPointCloud scene.mvs

  # Mesh Reconstruction
  # To halve the number of faces: --decimate 0.5
  # To smooth the mesh with 5 iterations: --smooth 5
  ReconstructMesh scene_dense.mvs -p scene_dense.ply --smooth "$SMOOTH"

  # Mesh Refinement (optional)
  # It will create a more regular mesh with similarly sized faces.
  # To specify the size of the faces: --max-face-area 256
  # In case the refinement runs out of memory: --resolution-level 1
  RefineMesh scene_dense.mvs -m scene_dense_mesh.ply --resolution-level 1

  # Mesh Texturing
  # To halve the resolution of the texture: --resolution-level 1
  # Set the color of unseen faces to white: --empty-color 16777215 # = 0xFFFFFF
  TextureMesh scene_dense.mvs -m scene_dense_refine.ply --empty-color 16777215
}

if [ -z "$PROJECT" ]; then
  echo "ERROR: Project directory missing."
  echo "Usage: $0 PROJECT"
  exit 1
fi
if [ ! -d "$PROJECT/images" ]; then
  echo "ERROR: Project '$PROJECT' does not contain 'images'."
  exit 1
fi
cd "$PROJECT" || exit 1
if [ ! -d 'dense' ]; then
  colmap_sparse
fi
openmvs_texture