Skip to content

Instantly share code, notes, and snippets.

@svc-user
Last active May 13, 2024 08:10
Show Gist options
  • Save svc-user/3a75647a7eec6e470c98cdec2ef64469 to your computer and use it in GitHub Desktop.
Save svc-user/3a75647a7eec6e470c98cdec2ef64469 to your computer and use it in GitHub Desktop.
# Script for extracting focus bracketing images taken in quick succession and sort into individual folders for stacking.
function Get-Stacks {
[CmdletBinding()]
param (
# Input dir containing the image stacks
[Parameter(Mandatory = $true, HelpMessage = "The directory to extract images from.")]
[string]
$InputDir,
# Output dir to put extracted files. Must be empty
[Parameter(Mandatory = $true, HelpMessage = "The output dir in which subfolders for each set of images are put.")]
[string]
$OutputDir,
# Threshold, in ms, between same frames
[Parameter(Mandatory = $false, HelpMessage = "Maximum time, in miliseconds, that an image can be older than the previous image to be considered as part of the same set.")]
[UInt32]
$StackFrameMaxOffset = 2000,
# Recurse InputDir for files
[Parameter(Mandatory = $false, HelpMessage = "Look in the input dir recursively.")]
[Switch]
$Recurse,
[Parameter(Mandatory = $false, HelpMessage = "Move (cut) files out of the input dir rather than copying.")]
[switch]
$Carve,
[Parameter(Mandatory = $false, HelpMessage = "Determine how old files to import. Format is 1d2h3m where 1, 2 and 3 are variable. Any time indicator can be omitted.")]
[string]
$Since = "2d"
)
function Get-DateTimeOffset($timestr) {
$days_m = [System.Text.RegularExpressions.Regex]::Match($timestr, "([\d]+)d")
$hours_m = [System.Text.RegularExpressions.Regex]::Match($timestr, "([\d]+)h")
$minutes_m = [System.Text.RegularExpressions.Regex]::Match($timestr, "([\d]+)m")
$days = 0
$hours = 0
$minutes = 0
if ($days_m.Success) {
$days = [UInt16]::Parse($days_m.Groups[1].Value)
}
if ($hours_m.Success) {
$hours = [UInt16]::Parse($hours_m.Groups[1].Value)
}
if ($minutes_m.Success) {
$minutes = [UInt16]::Parse($minutes_m.Groups[1].Value)
}
$offsetDate = (Get-Date -AsUTC).AddDays(-$days)
$offsetDate = $offsetDate.AddHours(-$hours)
$offsetDate = $offsetDate.AddMinutes(-$minutes)
return $offsetDate
}
function Copy-FilesToSet($set) {
if ($set.Length -lt 3) {
Write-Warning "Stack contains only $($set.Length) image(s). Skipping.."
return $false
}
$set = $set | Sort-Object -Property Name
$md = Get-FileMetaData -File $set[0].FullName
$setDate = $set[0].CreationTime.ToString("yyyyMMdd_HHmmss")
$setFolder = Join-Path -Path $OutputDir -ChildPath "Set_${setCounter}_$setDate\"
$remap = @{}
if ($md."Camera maker" -like "olympus*" -and $md."Camera model" -like "tg-*") {
Write-Warning "Olympus TG detected, reordering stack."
$newFilePath = $null
if($set.Length % 2 -eq 0) {
$half = $set.Length / 2
$newName = (Get-Item -Path $set[$half-1]).BaseName
$ext = (Get-Item -Path $set[0]).Extension
$newFilePath = Join-Path -Path $setFolder -ChildPath "${newName}_2$ext"
$remap = @{ $set[0].FullName = $newFilePath}
} else {
Write-Warning "Uneven stack size. No reordering done."
}
}
New-Item -ItemType Directory -Path $setFolder
$set | ForEach-Object {
$op = "Copy-Item"
if($Carve) {
$op = "Move-Item"
}
if($null -ne $remap[$_.FullName]) {
&$op -Path $_ -Destination $remap[$_.FullName]
} else {
&$op -Path $_ -Destination $setFolder
}
}
return $true
}
# resolve paths to be absolute
$cwd = Get-Location
if (-not (Split-Path -IsAbsolute -Path $InputDir)) {
$InputDir = Join-Path -Resolve -Path $cwd -ChildPath $InputDir
}
if (-not (Split-Path -IsAbsolute -Path $OutputDir)) {
$OutputDir = Join-Path -Resolve -Path $cwd -ChildPath $OutputDir
}
# create output dir if it does not exist
if (-not (Test-Path -PathType Container -Path $OutputDir)) {
Write-Warning "Output directory does not exist. Creating directory."
New-Item -ItemType Directory -Path $OutputDir
}
# check output dir is empty
$childCount = Get-ChildItem -ErrorAction Stop -Recurse $OutputDir | Measure-Object | Select-Object -ExpandProperty Count
if ($childCount -gt 0) {
Write-Error "Output directory is not empty."
return
}
# Obtain files
if ($Recurse) {
$files = Get-ChildItem -Path $InputDir -Include "*.jpg", "*.jpeg", "*.tif", "*.tiff" -File -Recurse
}
else {
$files = Get-ChildItem -Path "$InputDir\*" -Include "*.jpg", "*.jpeg", "*.tif", "*.tiff" -File
}
# Get max file age
$dtOffset = Get-DateTimeOffset -timestr $since
$files = $files | Where-Object -Property CreationTimeUtc -GE $dtOffset
if ($files.Length -eq 0) {
Write-Error "No input files found."
return
}
# sort on CreateTime for grouping stacks
$files = $files | Sort-Object -Property CreationTimeUtc
# build stacking sets
$setCounter = 1;
$set = @()
$set += $files[0] # a set always contains at least one file.
$i = 1
while ($i -lt $files.Length) {
$nextFile = $files[$i]
# if the next file is too old to be related to the previous file...
if ($nextFile.CreationTimeUtc.AddMilliseconds(-$StackFrameMaxOffset) -gt $files[$i - 1].CreationTimeUtc) {
if( Copy-FilesToSet($set) ) {
$setCounter += 1;
}
$set = @()
}
$set += $nextFile
$i += 1
}
# write the remaining files in the set
if ($set.Length -gt 0) {
Copy-FilesToSet($set)
}
Write-Output "Found $($files.Length) files in $InputDir"
Write-Output "Saved sets to $OutputDir"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment