Skip to content

Instantly share code, notes, and snippets.

@dkittell
Last active April 17, 2024 22:31
Show Gist options
  • Star 16 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"
)
}
@gorillanajs
Copy link

Hi!
Love the script! I would like to ask you for one thing though if you have time. Would it be possible to add filesize to the end of the filename in MB? I have toons of photos that Google messed up creating duplicates in different sizes and I am trying to sort out the ones with the lowest resolution. I thought it would be easy (silly of me) and just add $ImgData.Size.ToString() but apparently this value is the dimensions of the image.

@dkittell
Copy link
Author

I have a bash version of this script that will do the file size.
I can look at some point this week to spend time on this as it would be a nice addition.

@gorillanajs
Copy link

Hi, might come out as a bit of a bastard now but I solved it myself and made a fork of your code :-)

@dkittell
Copy link
Author

No worries here, glad you got the code you needed :)

@gorillanajs
Copy link

Thanks again. Did a fork with my addition.

@mauriciogracia
Copy link

mauriciogracia commented Sep 25, 2023

I have just tried to use this script and run into a couple of issues (is Win 11 supported?)

  1. When calling it on the folder where the images are located I get this error
    image
Get-ChildItem : Cannot retrieve the dynamic parameters for the cmdlet. Cannot process argument because
the value of argument "path" is not valid. Change the value of the "path" argument and run the operation
again.
  1. When calling it like file.ps1 -Path mypath it does not work
  2. When I call it with file.ps1 . to process the local files, it works with manay exceptions, all similar to these
20230924 22:00:35	ERROR	Did not change name for 010.jpg. Reason: Exception calling "GetPropertyItem" with "1" argument(s): "Property cannot be found." Cannot retrieve the dynamic parameters for the cmdlet. Cannot process argument because the value of argument "path" is not valid. Change the value of the "path" argument and run the operation again. Cannot find path 'C:\DATOS\VILLA_LEYVA\-Path' because it does not exist. File C:\DATOS\VILLA_LEYVA\timeLine.ps1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
20230924 22:00:36	ERROR	Did not change name for 160.jpg. Reason: Exception calling "GetPropertyItem" with "1" argument(s): "Property cannot be found."
20230924 22:00:36	ERROR	Did not change name for 165.jpg. Reason: Exception calling "GetPropertyItem" with "1" argument(s): "Property cannot be found."
20230924 22:00:36	ERROR	Did not change name for 170.jpg. Reason: Exception calling "GetPropertyItem" with "1" argument(s): "Property cannot be found."

@dkittell
Copy link
Author

Haven't had any problems on my end with Windows 11 but it is likely needing some tweaks as this was made before 11

@keitetran
Copy link

Window 11 not working now

`PS D:\NewFolder\iphone-backup> powershell -ExecutionPolicy Bypass -File .\PictureRename.ps1

GAC Version Location


True v4.0.30319 C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\Syste...
Get-ChildItem : Cannot retrieve the dynamic parameters for the cmdlet. Cannot process argument because the value of
argument "path" is not valid. Change the value of the "path" argument and run the operation again.
At D:\NewFolder\iphone-backup\PictureRename.ps1:53 char:26

  • ... ImgsFound = Get-ChildItem ($Path + '\*') -Include *.jpeg, *.png, *.g ...
  •             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
    • FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.GetChildItemCommand`

@dkittell
Copy link
Author

I can look at in a few hours
Should be minimal updates

@dkittell
Copy link
Author

dkittell commented Dec 17, 2023

Finally had a moment to get back to this.

The script now works in Windows 11. The main issue was that since Windows 11 the concatenation of path with the file names process changed I had to change the code to accommodate the needed change

@mauriciogracia
Copy link

Thanks for that

@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

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