Created
November 3, 2025 18:46
-
-
Save rinme/db6001673e32fbf70338803bd711e39c to your computer and use it in GitHub Desktop.
AutoTracker workflow using GLOMAP by polyfjord Ported to PowerShell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| ================================================================ | |
| 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