Renaming photographs from your digital camera
I bought my first digital camera a few years back, and used the setting to create sequenced numbering of the files in the format PICTxxxx, where xxxx would increment regardless of the date. At the time I was using the following directory structure:
DCIM 2006 200609 20060919 PICT9974.jpg PICT9975.jpg
After about two years the counter had reached 9999 and reset itself to 0000. I decided soon after to set the camera to reset the counter on each new day, which was not a problem as a new folder was created for each day. Now I had the following situation:
DCIM 2007 200704 20070407 PICT0001.jpg PICT0002.jpg 20070408 PICT0001.jpg PICT0002.jpg
When putting together albums and CD's I now faced a photo identifying dilemma: how could I uniquely refer to a picture in my collection? If I wanted to put the pictures from both 20070407 and 20070408 together I would need to rename them upon copying, and this wasn't always desired.
I wanted a way to name and find each file by its unique name, not by using paths and so forth. I decided that I wanted the files to have the following naming convention:
GG-yyyyMMdd-xxxx.*
where GG are my initials, yyyyMMdd the day the picture was taken, and xxxx was the day counter. Using this convention I could shoot 10.000 pictures a day before needing to take further action to uniquely identify the files.
I considered writing a VBScript to do this, but couldn't think of how to do it exactly. The renaming would have to be done based on the file's LastWriteTime property, as some of the parent directories weren't reliable sources for the yyyyMMdd part of the filename. I had sometimes added extra directories under the day level, to separate periods during a day-long wedding shoot for instance. The xxxx part could not be random or generated but had to be the same as the file originally had.
I then turned to PowerShell, and after a bit of testing, I had come up with the following one-liner to get my files that were to be renamed listed and tested:
Get-ChildItem -include pict*.* -Recurse |
>> foreach-object { $_.directoryname + "" + $_.name +
>> " = "+ ($_.Name -replace "pict",
>> ("GG-"+$_.lastwritetime.toString('yyyyMMdd')+"-"))}
The first part (up to the | pipe sign) gets all files matching the pattern pict*.* in all underlying directories as objects, with all their properties, attributes and methods available for use in the second part. The second part takes each object, lists the path (including the current filename) where it was found, and shows how the new name will look like.
Here I used the LastWriteTime property of my files, as I always have either my photo organizer ThumbsPlus or the exiffile tool from the EXIFUtils toolkit set the file modified time to the EXIF Date & Time Taken value from the picture.
This way I can edit or rotate a picture and still keep the Modified date & time correct to the actual date and time the picture was taken. I then used the toString method of the LastWriteTime property to extract / format only the date in a yyyyMMdd format, ie. 20070407.
By replacing the "pict" with "GG-20070407-" I now had GG-20070407-0001.jpg as the new filename, just as I wanted.
To ease my checking of the list I altered it to output the list as a CSV file:
Get-ChildItem -include pict*.* -Recurse |
>> foreach-object { $_.directoryname + "" + $_.name +
>> ","+ ($_.Name -replace "pict",
>> ("GG-"+$_.lastwritetime.toString('yyyyMMdd')+"-"))} > allfiles2Brenamed.csv
Thorough checking revealed that some pictures had the wrong date. How was this happening? Apparently the pictures had been taken close to the midnight hour, and had a different timestamp than I expected. This was due to either the camera or the system where I first transferred the files from the card to a PC marking not only the time, but also the UTC time. With the recent change to regular (non-DST) time, the date-time properties were being adjusted on the fly by the system, and the pictures were being show to be an hour earlier on the previous day. This was only a problem with a few pictures, and once I had identified them I could easily fix them afterwards. In the future I'll have a think about how I want to fix this so my pictures / renaming code will not be affected by the timezone I happen to be in.
Now that I was satisfied that all my own photographs were included in the selection list it was time for the actual rename. I rewrote the command to include the Rename-Item cmdlet as follows:
Get-ChildItem -include pict*.* -Recurse |
>> foreach-object { rename-item $_ -newname ($_.Name -replace "pict",
>> ("GG-"+$_.lastwritetime.toString('yyyyMMdd')+"-"))}
I first tested it on a copy of a subdirectory just to be sure, and it all worked perfectly! I then proceeded to the root of my photo hierarchy, DCIM, and ran the command again. Within mere seconds, all of my photographs were renamed, including RAW and TIFF versions I had of some of them.
To use these scripts with a different camera all that's needed is to identify the common part, like PICT in this case, or DSC or DCS in others. Instead of keeping the sequential number the actual date and time could also be used as follows:
Get-ChildItem -include pict*.* -Recurse |
>> foreach-object { rename-item $_ -newname
>> ("GG-"+$_.lastwritetime.toString('yyyyMMdd-hhmmss')}
Now all I had to do was update the ThumbsPlus SQL database to reflect these changes prior to starting the program up, so that it would not see the files as new ones and the data in the database as belonging to orphaned (removed from the filesystem but still in the database) thumbnails and decide to clean the database up. This way all my custom userdata, galleries, and tracking info would remain intact. A few test runs and SQL scripts later I had it done and started ThumbsPlus up, and got the expected and desired results.
Thanks PowerShell!
P.S.: My camera also has the tendency to name the folders it creates by using a combination of a sequential number and the date, in the following way:
DCIM 10170407 PICT0001.jpg PICT0002.jpg 10270408 PICT0001.jpg PICT0002.jpg
Here the first 3 numbers of the directories are sequential, and the last 5 are yMMdd. When copying to my photorepository on my PC I like to have them renamed to yyyyMMdd instead of ###yMMdd, so I used to do that manually. I now also have a PowerShell script to do this for me:
Get-Childitem |
>> where {($_.attributes -like "dir*") -and ($_.name -notlike "100KM*")} |
>> foreach-object { rename-item $_ -newname $_.lastwritetime.toString('yyyyMMdd')}
The Where operator allows me to only rename directories that aren't the 100KM* settings directory. Other cameras use a different name for their settings directory, so this can be changed if needed. I could have just replaced the first 3 characters with 200 instead of using the datestamp, but in about 3 years this would not work anymore, and I'm not going make the Y2K assumptions and errors all over again 😉