Monday, August 27, 2007

Getting EXIF Information

So you are sitting there at your desk Monday morning, trying to organize some photos. Your file dates are a mess, but - hey - you know that the photos contains the EXIF date picture taken value. Writing a small script to sort things out should be a small problem.

But how do I get to the data? Searching for Powershell exif data picture taken soon got me to the James ONeill's blog. But it was quite complicated and then I remembered that I did something similar from VBScript a while back. I looked in the script and realized, that the Shell.Application object could retrieve the information in an easier way.

I started off writing Get-ExifDatePictureTaken, refined it to Get-ExifProperty with a $property parameter with "date picture taken" as default, but realized that the right solution was to use the extensible type system (ETS). ETS makes Powershell so stong!

I created ExifDatePictureTaken.ps1xml -
<Types>
<Type>
<Name>System.IO.FileInfo</Name>
<Members>
<ScriptProperty>
<Name>DatePictureTaken</Name>
<GetScriptBlock>
trap {write-debug $error[0]}
$null=[reflection.assembly]::loadfile("$env:windir\Microsoft.Net\framework\v2.0.50727\System.Drawing.Dll")
$shell=new-object -com Shell.Application
$folder=$shell.Namespace((split-path $this))
foreach($i in 1..100) {
if ($folder.GetDetailsOf("",$i) -eq "date picture taken") {write-debug $i;break}
}
if ($i -lt 100) {
$folder.items() | ? { $_.name -eq $this.name } | % {
$date=$folder.GetDetailsOf($_,$i)
if ($date) {
# Dates are text and DD-MM-YYYY cannot be converted to datetime
# Convert DD-MM-YYYY to YYYY-MM-DD
$date=$date -replace "^(\d{2})\-(\d{2})\-(\d{4})",'$3-$2-$1'
# Cast to a datetime
[datetime] $date
}
}
}
</GetScriptBlock>
</ScriptProperty>
</Members>
</Type>
</Types>


and loaded it with Update-TypeData.

Hint: You cannot Update-TypeData on the same files twice (in the same Powershell sessions). Consequenly, I use something like this when testing -
PS> powershell { update-typedata Exifdatepicturetaken.ps1xml; $file=get-item 'S:\Billeder\2007\2007-03 Diverse\P1010169.JPG'; $debugpreference="continue"; $file.datepicturetaken }

Now I can simply get the DatePictureTaken property of any System.IO.File object.

The downside to this approach is speed. It is slow. I could be improved in several ways -
  • If the index is static (I do not know whether that is the case or not), it did not have to be found everytime
  • The folder object could be cached in a global variable, so multiple consequtive lookups in the same folder was faster

If you make any improvements, please post them as comments.

No comments: