Last active
May 13, 2024 08:10
-
-
Save svc-user/3a75647a7eec6e470c98cdec2ef64469 to your computer and use it in GitHub Desktop.
This file contains 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
# 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