Skip to content

Instantly share code, notes, and snippets.

@rinme
Created November 3, 2025 18:46
Show Gist options
  • Select an option

  • Save rinme/db6001673e32fbf70338803bd711e39c to your computer and use it in GitHub Desktop.

Select an option

Save rinme/db6001673e32fbf70338803bd711e39c to your computer and use it in GitHub Desktop.
AutoTracker workflow using GLOMAP by polyfjord Ported to PowerShell
<#
================================================================
PowerShell SCRIPT FOR AUTOMATED PHOTOGRAMMETRY TRACKING WORKFLOW
By polyfjord - https://youtube.com/polyfjord
Ported to PowerShell by Rinme with some help from ChatGPT
Tested with Windows PowerShell 7 on Windows 11
GLOMAP mapping (faster), COLMAP for features/matching + TXT export
================================================================
#>
param()
$ErrorActionPreference = 'Stop'
# ---------- Resolve top-level folder (one up from this .ps1) -----
$TOP = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
# ---------- Key paths -------------------------------------------
$SFM_DIR = Join-Path $TOP '01 GLOMAP'
$VIDEOS_DIR = Join-Path $TOP '02 VIDEOS'
$FFMPEG_DIR = Join-Path $TOP '03 FFMPEG'
$SCENES_DIR = Join-Path $TOP '04 SCENES'
# ---------- Locate ffmpeg.exe -----------------------------------
$FFMPEG = $null
if (Test-Path (Join-Path $FFMPEG_DIR 'ffmpeg.exe')) {
$FFMPEG = Join-Path $FFMPEG_DIR 'ffmpeg.exe'
} elseif (Test-Path (Join-Path $FFMPEG_DIR 'bin/ffmpeg.exe')) {
$FFMPEG = Join-Path $FFMPEG_DIR 'bin/ffmpeg.exe'
} else {
Write-Host "[ERROR] ffmpeg.exe not found inside '$FFMPEG_DIR'." -ForegroundColor Red
Read-Host 'Press Enter to exit'
exit 1
}
# ---------- Locate glomap.exe -----------------------------------
$GLOMAP = $null
if (Test-Path (Join-Path $SFM_DIR 'glomap.exe')) {
$GLOMAP = Join-Path $SFM_DIR 'glomap.exe'
} elseif (Test-Path (Join-Path $SFM_DIR 'bin/glomap.exe')) {
$GLOMAP = Join-Path $SFM_DIR 'bin/glomap.exe'
} else {
Write-Host "[ERROR] glomap.exe not found inside '$SFM_DIR'." -ForegroundColor Red
Read-Host 'Press Enter to exit'
exit 1
}
# ---------- Locate colmap.exe (DB + TXT export) ------------------
$COLMAP = $null
if (Test-Path (Join-Path $SFM_DIR 'colmap.exe')) {
$COLMAP = Join-Path $SFM_DIR 'colmap.exe'
} elseif (Test-Path (Join-Path $SFM_DIR 'bin/colmap.exe')) {
$COLMAP = Join-Path $SFM_DIR 'bin/colmap.exe'
} else {
Write-Host "[ERROR] colmap.exe not found inside '$SFM_DIR'." -ForegroundColor Red
Read-Host 'Press Enter to exit'
exit 1
}
# ---------- Put binaries on PATH --------------------------------
$env:PATH = (Join-Path $SFM_DIR '.') + ";" + (Join-Path $SFM_DIR 'bin') + ";" + $env:PATH
# ---------- Ensure required folders exist ------------------------
if (-not (Test-Path $VIDEOS_DIR)) {
Write-Host "[ERROR] Input folder '$VIDEOS_DIR' missing." -ForegroundColor Red
Read-Host 'Press Enter to exit'
exit 1
}
if (-not (Test-Path $SCENES_DIR)) { New-Item -ItemType Directory -Path $SCENES_DIR | Out-Null }
# ---------- Count videos for progress bar ------------------------
$videos = Get-ChildItem -Path $VIDEOS_DIR -File | Sort-Object Name
$TOTAL = ($videos | Measure-Object).Count
if ($TOTAL -eq 0) {
Write-Host "[INFO] No video files found in '$VIDEOS_DIR'." -ForegroundColor Yellow
Read-Host 'Press Enter to exit'
exit 0
}
Write-Host ('=' * 61)
Write-Host " Starting GLOMAP pipeline on $TOTAL video(s) …"
Write-Host ('=' * 61)
function Invoke-Checked {
param(
[Parameter(Mandatory)] [string] $Exe,
[Parameter(Mandatory)] [string[]] $Args,
[string] $FailMessage
)
& $Exe @Args
if ($LASTEXITCODE -ne 0) {
if ($FailMessage) { Write-Host " × $FailMessage" -ForegroundColor Red }
throw "Command failed: $Exe"
}
}
function Process-Video {
param(
[Parameter(Mandatory)] [string] $VideoPath,
[Parameter(Mandatory)] [int] $Index,
[Parameter(Mandatory)] [int] $Total
)
$BASE = [System.IO.Path]::GetFileNameWithoutExtension($VideoPath)
$EXT = [System.IO.Path]::GetExtension($VideoPath)
Write-Host "`n[$Index/$Total] === Processing '$BASE$EXT' ==="
# -------- Directory layout for this scene --------------------
$SCENE = Join-Path $SCENES_DIR $BASE
$IMG_DIR = Join-Path $SCENE 'images'
$SPARSE_DIR = Join-Path $SCENE 'sparse'
# -------- Skip if already reconstructed ----------------------
if (Test-Path $SCENE) {
Write-Host " • Skipping '$BASE' – already reconstructed."
return
}
# Clean slate -------------------------------------------------
New-Item -ItemType Directory -Path $IMG_DIR -Force | Out-Null
New-Item -ItemType Directory -Path $SPARSE_DIR -Force | Out-Null
# -------- 1) Extract every frame -----------------------------
Write-Host " [1/4] Extracting frames …"
try {
Invoke-Checked -Exe $FFMPEG -Args @('-loglevel','error','-stats','-i', $VideoPath, '-qscale:v','2', (Join-Path $IMG_DIR 'frame_%06d.jpg')) -FailMessage "FFmpeg failed – skipping '$BASE'."
} catch { return }
if (-not (Get-ChildItem -Path (Join-Path $IMG_DIR '*.jpg') -ErrorAction SilentlyContinue)) {
Write-Host " × No frames extracted – skipping '$BASE'." -ForegroundColor Red
return
}
# -------- 2) Feature extraction (COLMAP) ---------------------
Write-Host " [2/4] COLMAP feature_extractor …"
try {
Invoke-Checked -Exe $COLMAP -Args @('feature_extractor',
'--database_path', (Join-Path $SCENE 'database.db'),
'--image_path', $IMG_DIR,
'--ImageReader.single_camera','1',
'--SiftExtraction.use_gpu','1',
'--SiftExtraction.max_image_size','4096'
) -FailMessage 'feature_extractor failed – skipping.'
} catch { return }
# -------- 3) Sequential matching (COLMAP) --------------------
Write-Host " [3/4] COLMAP sequential_matcher …"
try {
Invoke-Checked -Exe $COLMAP -Args @('sequential_matcher',
'--database_path', (Join-Path $SCENE 'database.db'),
'--SequentialMatching.overlap','15'
) -FailMessage 'sequential_matcher failed – skipping.'
} catch { return }
# -------- 4) Sparse reconstruction (GLOMAP) ------------------
Write-Host " [4/4] GLOMAP mapper …"
try {
Invoke-Checked -Exe $GLOMAP -Args @('mapper',
'--database_path', (Join-Path $SCENE 'database.db'),
'--image_path', $IMG_DIR,
'--output_path', $SPARSE_DIR
) -FailMessage 'glomap mapper failed – skipping.'
} catch { return }
# -------- Export TXT **inside the model folder** -------------
$model0 = Join-Path $SPARSE_DIR '0'
if (Test-Path $model0) {
& $COLMAP 'model_converter' '--input_path' $model0 '--output_path' $model0 '--output_type' 'TXT' *> $null
}
# -------- Export TXT to parent sparse\ (for Blender autodetect)
if (Test-Path $model0) {
& $COLMAP 'model_converter' '--input_path' $model0 '--output_path' $SPARSE_DIR '--output_type' 'TXT' *> $null
}
Write-Host " ✓ Finished '$BASE' ($Index/$Total)" -ForegroundColor Green
}
# ---------- Main loop -------------------------------------------
$idx = 0
foreach ($v in $videos) {
$idx++
Write-Progress -Activity 'GLOMAP pipeline' -Status "$idx / $TOTAL : $($v.Name)" -PercentComplete ([int](100*$idx/$TOTAL))
Process-Video -VideoPath $v.FullName -Index $idx -Total $TOTAL
}
Write-Progress -Activity 'GLOMAP pipeline' -Completed
Write-Host ('-' * 62)
Write-Host " All jobs finished – results are in '$SCENES_DIR'."
Write-Host ('-' * 62)
Read-Host 'Press Enter to exit'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment