Skip to content

Instantly share code, notes, and snippets.

@dkittell
Last active May 2, 2024 15:54
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save dkittell/746e69967a7f4d0f0c52 to your computer and use it in GitHub Desktop.
Save dkittell/746e69967a7f4d0f0c52 to your computer and use it in GitHub Desktop.
PowerShell – Rename Pictures to Image Taken Date/Time with Dimensions
<#
.SYNOPSIS
Renames pictures.
.DESCRIPTION
The Rename-Pictures cmdlet to rename pictures to a format where the file creation time is first
in the name in this format: . The idea is that
.PARAMETER Path
Specifies the path to the folder where image files are located. Default is current location (Get-Location).
.EXAMPLE
PS C:\> Rename-Pictures
Description:
Renames all the pictures in folder you are in.
.EXAMPLE
PS C:\> Rename-Pictures -Path C:\Folder\Pics\
Description:
Renames all the pictures in the given folder path.
.NOTES
Author: Magnus Ringkjøb
E-mail: magnus.ringkjob@gmail.com
Image Dimensions Added By David Kittell - Kittell.net
#>
Param(
[string]$Path
)
if ([string]::IsNullOrWhiteSpace($Path)) {
$Path = (Get-Location)
}
$BackupFileName = '_backupdata.csv'
$ErrorFileName = '_errors.csv'
[reflection.assembly]::LoadFile("C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Drawing.dll")
$Script:ErrorLogMsg = $Null
$Script:CorrectPath = $Null
$Path
$ImgsFound = (dir $Path -Include ('*.jpeg', '*.png', '*.gif', '*.jpg', '*.bmp', '*.png') -Recurse | Select-Object -Property FullName, Name, BaseName, Extension)
# If any file was found
# Array that takes in the old- and the new filename. This is used for saving a backup to .csv
$BackupData = @()
# Loops through the images found
foreach ($Img in $ImgsFound) {
# Gets image data
$ImgData = New-Object System.Drawing.Bitmap($Img.FullName)
$ImgDimensions = $ImgData.Width.ToString() + $("x") + $ImgData.Height.ToString()
try {
# Gets 'Date Taken' in bytes
[byte[]]$ImgBytes = $ImgData.GetPropertyItem(36867).Value
}
catch [System.Exception], [System.IO.IOException] {
[string]$ErrorMessage = (
(Get-Date).ToString('yyyyMMdd HH:mm:ss') + "`tERROR`tDid not change name for " + $Img.Name + ". Reason: " + $Error
)
$Script:ErrorLogMsg += $ErrorMessage + "`r`n"
Write-Host -ForegroundColor Red -Object $ErrorMessage
# Clears any error messages
$Error.Clear()
# No reason to continue. Move on to the next file
continue
}
# Gets the date and time from bytes
[string]$dateString = [System.Text.Encoding]::ASCII.GetString($ImgBytes)
# Formats the date to the desired format
[string]$dateTaken = [datetime]::ParseExact($dateString, "yyyy:MM:dd HH:mm:ss`0", $Null).ToString('yyyy-MM-dd_HH.mm.ss.ms')
# The new file name for the image
# [string]$NewFileName = $dateTaken + '-' + $Img.Name
[string]$NewFileName = $dateTaken + "_" + $ImgDimensions + [System.IO.Path]::GetExtension($Img.Name)
$ImgData.Dispose()
try {
Rename-Item -NewName $NewFileName -Path $Img.FullName -ErrorAction Stop
Write-Host -Object ("Renamed " + $Img.Name + " to " + $NewFileName)
}
catch {
[string]$ErrorMessage = (
(Get-Date).ToString('yyyyMMdd HH:mm:ss') + "`tERROR`tDid not change name for " + $Img.Name + ". Reason: " + $Error
)
$Script:ErrorLogMsg += $ErrorMessage + "`r`n"
Write-Host -ForegroundColor Red -Object $ErrorMessage
# Clears any previous error messages
$Error.Clear()
# No reason to continue. Move on to the next file
continue
}
# Collect data to be added to the backup file
$BUData = New-Object -TypeName System.Object
$BUData | Add-Member -MemberType NoteProperty -Name "OldName" -Value $Img.Name
$BUData | Add-Member -MemberType NoteProperty -Name "NewName" -Value $NewFileName
# Add data to backup collection
$BackupData += $BUData
try {
$BackupData | Export-Csv -NoTypeInformation -Path "$Path\\$BackupFileName"
}
catch [System.Exception] {
[string]$ErrorMessage = "((Get-Date).ToString('yyyyMMdd HH:mm:ss'))`tERROR`tCould not create $Path $BackupFileName Reason: " + $Error
$Script:ErrorLogMsg += $ErrorMessage + "`r`n"
# Clears any error messages
$Error.Clear()
}
}
# If there was a problem during the run:
# Print to file, and let user know
if ($Script:ErrorLogMsg -ne $Null) {
$ErrorLogMsg | Export-Csv -NoTypeInformation -Path "$Path\\$ErrorFileName"
Write-Host -ForegroundColor Red -Object (
"Errors were found. Please check " + $Path + "_errors.log"
)
}
@dkittell
Copy link
Author

No problem

@umassfan29
Copy link

how would I be able to get this to work for MP4s as well in the same folder? I tried adding that in the ImgsFound to include '*.mp4' but did not work

@dkittell
Copy link
Author

dkittell commented Feb 5, 2024

Typically video files in general do not have the needed EXIF information.

@umassfan29
Copy link

Appreciate the quick response. Do you have any suggestions on how I could do this with photos and videos inside? Seems powershell can't handle it if I want the date created

@umassfan29
Copy link

ah it looks like videos use "media created" and not "date taken"

@dkittell
Copy link
Author

dkittell commented Feb 5, 2024

Even that "media created" can be misleading depending on the device used.

If you look at the date created value you can get from get-content that may help.

I had a script that would pull that and rename the videos but similar to this powershell script when Windows 11 came out the process changed just enough that it doesn't work.

Get-ChildItem -Path video.mp4 | select Name,CreationTime

If you do a loop within the directory of your video files this could work.

@dkittell
Copy link
Author

dkittell commented Feb 5, 2024

By all means try out each field and see what works best for you

@sondreb
Copy link

sondreb commented May 1, 2024

The $ImgData.Dispose() happens outside of the error handler, which performs continue if there is an error. This puts a lock on the files. Should perhaps be refactored to dispose before continue on next file.

Thanks for this sample, I'm working on an updated script that includes the ability to read LastWriteTime if there are no EXIF data available.

@dkittell
Copy link
Author

dkittell commented May 1, 2024

The $ImgData.Dispose() happens outside of the error handler, which performs continue if there is an error. This puts a lock on the files. Should perhaps be refactored to dispose before continue on next file.

Thanks for this sample, I'm working on an updated script that includes the ability to read LastWriteTime if there are no EXIF data available.

Good catch, I mostly use a bash/shell version of this script now but would love to see your update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment